Commit d7aefb4b authored by 楊慶堂's avatar 楊慶堂

init and 翻成繁體

parents
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="1.2" />
<option name="languageVersion" value="1.2" />
</component>
</project>
\ No newline at end of file
<component name="libraryTable">
<library name="KotlinJavaRuntime">
<CLASSES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
</SOURCES>
</library>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Lottery.iml" filepath="$PROJECT_DIR$/Lottery.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>
\ No newline at end of file
/*
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import javax.swing.*;
import java.awt.*;
/**
* A 1.4 file that provides utility methods for
* creating form- or grid-style layouts with SpringLayout.
* These utilities are used by several programs, such as
* SpringBox and SpringCompactGrid.
*/
public class SpringUtilities {
/**
* A debugging utility that prints to stdout the component's
* minimum, preferred, and maximum sizes.
*/
public static void printSizes(Component c) {
System.out.println("minimumSize = " + c.getMinimumSize());
System.out.println("preferredSize = " + c.getPreferredSize());
System.out.println("maximumSize = " + c.getMaximumSize());
}
/**
* Aligns the first <code>rows</code> * <code>cols</code>
* components of <code>parent</code> in
* a grid. Each component is as big as the maximum
* preferred width and height of the components.
* The parent is made just big enough to fit them all.
*
* @param rows number of rows
* @param cols number of columns
* @param initialX x location to start the grid at
* @param initialY y location to start the grid at
* @param xPad x padding between cells
* @param yPad y padding between cells
*/
public static void makeGrid(Container parent,
int rows, int cols,
int initialX, int initialY,
int xPad, int yPad) {
SpringLayout layout;
try {
layout = (SpringLayout) parent.getLayout();
} catch (ClassCastException exc) {
System.err.println("The first argument to makeGrid must use SpringLayout.");
return;
}
Spring xPadSpring = Spring.constant(xPad);
Spring yPadSpring = Spring.constant(yPad);
Spring initialXSpring = Spring.constant(initialX);
Spring initialYSpring = Spring.constant(initialY);
int max = rows * cols;
//Calculate Springs that are the max of the width/height so that all
//cells have the same size.
Spring maxWidthSpring = layout.getConstraints(parent.getComponent(0)).
getWidth();
Spring maxHeightSpring = layout.getConstraints(parent.getComponent(0)).
getWidth();
for (int i = 1; i < max; i++) {
SpringLayout.Constraints cons = layout.getConstraints(
parent.getComponent(i));
maxWidthSpring = Spring.max(maxWidthSpring, cons.getWidth());
maxHeightSpring = Spring.max(maxHeightSpring, cons.getHeight());
}
//Apply the new width/height Spring. This forces all the
//components to have the same size.
for (int i = 0; i < max; i++) {
SpringLayout.Constraints cons = layout.getConstraints(
parent.getComponent(i));
cons.setWidth(maxWidthSpring);
cons.setHeight(maxHeightSpring);
}
//Then adjust the x/y constraints of all the cells so that they
//are aligned in a grid.
SpringLayout.Constraints lastCons = null;
SpringLayout.Constraints lastRowCons = null;
for (int i = 0; i < max; i++) {
SpringLayout.Constraints cons = layout.getConstraints(
parent.getComponent(i));
if (i % cols == 0) { //start of new row
lastRowCons = lastCons;
cons.setX(initialXSpring);
} else { //x position depends on previous component
cons.setX(Spring.sum(lastCons.getConstraint(SpringLayout.EAST),
xPadSpring));
}
if (i / cols == 0) { //first row
cons.setY(initialYSpring);
} else { //y position depends on previous row
cons.setY(Spring.sum(lastRowCons.getConstraint(SpringLayout.SOUTH),
yPadSpring));
}
lastCons = cons;
}
//Set the parent's size.
SpringLayout.Constraints pCons = layout.getConstraints(parent);
pCons.setConstraint(SpringLayout.SOUTH,
Spring.sum(
Spring.constant(yPad),
lastCons.getConstraint(SpringLayout.SOUTH)));
pCons.setConstraint(SpringLayout.EAST,
Spring.sum(
Spring.constant(xPad),
lastCons.getConstraint(SpringLayout.EAST)));
}
/* Used by makeCompactGrid. */
private static SpringLayout.Constraints getConstraintsForCell(
int row, int col,
Container parent,
int cols) {
SpringLayout layout = (SpringLayout) parent.getLayout();
Component c = parent.getComponent(row * cols + col);
return layout.getConstraints(c);
}
/**
* Aligns the first <code>rows</code> * <code>cols</code>
* components of <code>parent</code> in
* a grid. Each component in a column is as wide as the maximum
* preferred width of the components in that column;
* height is similarly determined for each row.
* The parent is made just big enough to fit them all.
*
* @param rows number of rows
* @param cols number of columns
* @param initialX x location to start the grid at
* @param initialY y location to start the grid at
* @param xPad x padding between cells
* @param yPad y padding between cells
*/
public static void makeCompactGrid(Container parent,
int rows, int cols,
int initialX, int initialY,
int xPad, int yPad) {
SpringLayout layout;
try {
layout = (SpringLayout) parent.getLayout();
} catch (ClassCastException exc) {
System.err.println("The first argument to makeCompactGrid must use SpringLayout.");
return;
}
//Align all cells in each column and make them the same width.
Spring x = Spring.constant(initialX);
for (int c = 0; c < cols; c++) {
Spring width = Spring.constant(0);
for (int r = 0; r < rows; r++) {
width = Spring.max(width,
getConstraintsForCell(r, c, parent, cols).
getWidth());
}
for (int r = 0; r < rows; r++) {
SpringLayout.Constraints constraints =
getConstraintsForCell(r, c, parent, cols);
constraints.setX(x);
constraints.setWidth(width);
}
x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad)));
}
//Align all cells in each row and make them the same height.
Spring y = Spring.constant(initialY);
for (int r = 0; r < rows; r++) {
Spring height = Spring.constant(0);
for (int c = 0; c < cols; c++) {
height = Spring.max(height,
getConstraintsForCell(r, c, parent, cols).
getHeight());
}
for (int c = 0; c < cols; c++) {
SpringLayout.Constraints constraints =
getConstraintsForCell(r, c, parent, cols);
constraints.setY(y);
constraints.setHeight(height);
}
y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad)));
}
//Set the parent's size.
SpringLayout.Constraints pCons = layout.getConstraints(parent);
pCons.setConstraint(SpringLayout.SOUTH, y);
pCons.setConstraint(SpringLayout.EAST, x);
}
}
package io.github.apollozhu;
/**
* Still in alpha
*/
public class AZConsole {
public static void clear() {
System.out.print("\033[H\033[2J");
}
public static final String resetStyle = "\033[0m";
public static String bolded(String content) {
return format(content, 1);
}
public static String italiclized(String content) {
return format(content, 3);
}
public static String underlined(String content) {
return format(content, 4);
}
public static String strikedThrough(String content) {
return format(content, 9);
}
private static String format(String content, int code) {
return "\033[" + code + "m" + content;
}
}
package io.github.apollozhu.awt;
import java.awt.*;
public class AZColor {
private static int randomComponent() {
return (int) (Math.random() * 256);
}
private static Color randColorWithAlpha(boolean hasAlpha) {
return new Color(randomComponent(), randomComponent(), randomComponent(), hasAlpha ? randomComponent() : 255);
}
/**
* @return Color with RGB components.
*/
public static Color randomOpaque() {
return randColorWithAlpha(false);
}
/**
* @return Color with RGBA components.
*/
public static Color randomTranslucent() {
return randColorWithAlpha(true);
}
public static boolean isDark(Color color) {
double darkness = 1 - (0.299 * color.getRed() + 0.587 * color.getGreen() + 0.114 * color.getBlue()) / 255.;
return darkness < 0.5;
}
public static boolean isLight(Color color) {
return !isDark(color);
}
}
package io.github.apollozhu.awt;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public interface AZKeyAdapter extends KeyListener {
/**
* Invoked when a key has been typed.
* This event occurs when a key press is followed by a key release.
*/
default void keyTyped(KeyEvent e) { }
/**
* Invoked when a key has been pressed.
*/
default void keyPressed(KeyEvent e) { }
/**
* Invoked when a key has been released.
*/
default void keyReleased(KeyEvent e) { }
}
package io.github.apollozhu.awt;
import java.awt.*;
public class AZRandomFont {
public static Font ofSiZe(float size) {
Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
int idx = (int) (Math.random() * fonts.length);
return fonts[idx].deriveFont(style(), size);
}
public static int style() {
return (int) (Math.random() * 4);
}
}
package io.github.apollozhu.lottery.lottery
import io.github.apollozhu.lottery.settings.LotteryPreferences
import io.github.apollozhu.lottery.utils.PreferenceLoading
import java.awt.Font
import java.util.prefs.PreferenceChangeEvent
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.SwingConstants
open class LotteryCenterPanel : JPanel(), PreferenceLoading {
protected var label = JLabel("", SwingConstants.CENTER)
protected open fun addLabel() {
add(label)
}
init {
addLabel()
LotteryPreferences.addListener { loadPreferences() }
loadPreferences()
}
override fun setVisible(aFlag: Boolean) {
super.setVisible(aFlag)
if (aFlag) loadPreferences()
}
override fun loadPreferences(ignored: PreferenceChangeEvent?) {
label.font = label.font.deriveFont(Font.BOLD, LotteryPreferences.winnerSize)
label.foreground = LotteryPreferences.winnerColor
background = LotteryPreferences.backgroundColor
}
}
package io.github.apollozhu.lottery.lottery
import io.github.apollozhu.lottery.prize.LotteryPrizeDisplayTextOnlyPanel
import io.github.apollozhu.lottery.prize.LotteryPrizeDisplayWithImagePanel
import io.github.apollozhu.lottery.prize.LotteryPrizeModel
import java.awt.CardLayout
import java.awt.event.MouseListener
import javax.swing.JPanel
class LotteryCenterPanelManagerPanel(clickListener: MouseListener) : JPanel() {
private val cardLayout = CardLayout()
private val placeholder = LotteryCenterPanel()
private val roller = LotteryRollingPanel()
private val displayWithImage = LotteryPrizeDisplayWithImagePanel()
private val displayTextOnly = LotteryPrizeDisplayTextOnlyPanel()
init {
layout = cardLayout
add(placeholder, "placeholder")
add(roller, "roller")
add(displayWithImage, "displayWithImage")
add(displayTextOnly, "displayTextOnly")
addMouseListener(clickListener)
}
fun showPlaceHodler() = cardLayout.show(this, "placeholder")
fun showRoller() = cardLayout.show(this, "roller")
fun showFor(winner: String, prizeModel: LotteryPrizeModel) {
if (prizeModel.hasImage) {
displayWithImage.displayFor(winner, prizeModel)
cardLayout.show(this, "displayWithImage")
} else {
displayTextOnly.displayFor(winner, prizeModel)
cardLayout.show(this, "displayTextOnly")
}
}
}
package io.github.apollozhu.lottery.lottery
import io.github.apollozhu.lottery.prize.LotteryPrizeModel
import io.github.apollozhu.lottery.settings.LotteryPreferences
import io.github.apollozhu.lottery.tabbedPane
import io.github.apollozhu.lottery.utils.PreferenceLoading
import io.github.apollozhu.lottery.utils.RandomSelector
import java.awt.BorderLayout
import java.awt.Color
import java.awt.GridLayout
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.util.prefs.PreferenceChangeEvent
import javax.swing.*
class LotteryPanel : JPanel(), PreferenceLoading {
private val title = JLabel("", SwingConstants.CENTER)
private val subtitle = JLabel("", SwingConstants.CENTER)
private val box = object : JComboBox<LotteryPrizeModel>() {
override fun setBackground(bg: Color?) {}
}
private val withdrawButton = JButton("放棄")
private val nextButton = JButton("抽獎")
private val settingsButton = JButton("設置")
private val centerPanels = LotteryCenterPanelManagerPanel(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) = next()
})
init {
layout = BorderLayout()
val nPanel = JPanel()
add(nPanel, BorderLayout.NORTH)
nPanel.layout = GridLayout(3, 1)
nPanel.add(JPanel())
nPanel.add(title)
nPanel.add(subtitle)
add(centerPanels, BorderLayout.CENTER)
val sPanel = JPanel()
add(sPanel, BorderLayout.SOUTH)
box.addItemListener { load(box.getSelectedItem() as LotteryPrizeModel?) }
box.setRenderer { _, model, _, _, _ -> JLabel(if (model == null) "無效獎項" else model.name) }
sPanel.add(box)
withdrawButton.addActionListener { withdraw() }
withdrawButton.isEnabled = false
sPanel.add(withdrawButton)
nextButton.addActionListener { next() }
sPanel.add(nextButton)
settingsButton.addActionListener { tabbedPane.selectedIndex = 1 }
sPanel.add(settingsButton)
LotteryPreferences.addListener { loadPreferences() }
loadPreferences()
RandomSelector.addListener { reloadCandidateList() }
}
override fun loadPreferences(ignored: PreferenceChangeEvent?) {
title.text = LotteryPreferences.title
title.font = title.font.deriveFont(LotteryPreferences.titleSize)
title.foreground = LotteryPreferences.titleColor
subtitle.text = LotteryPreferences.subtitle
subtitle.font = subtitle.font.deriveFont(LotteryPreferences.subtitleSize)
subtitle.foreground = LotteryPreferences.subtitleColor
subtitle.isVisible = subtitle.text.isNotBlank()
nextButton.isEnabled = RandomSelector.hasNext()
centerPanels.background = LotteryPreferences.backgroundColor
box.removeAllItems()
for (model in LotteryPreferences.prizes) {
box.addItem(model)
}
}
private var winner = ""
private var prizeModel: LotteryPrizeModel? = null
private var left: Int = 0
fun load(model: LotteryPrizeModel?) {
if (model == null) return
prizeModel = model
left = prizeModel!!.count
winner = ""
reloadCandidateList()
}
fun reloadCandidateList() = stateDidChange(false)
private var isWaiting: Boolean = false
fun next() {
if (!hasNext()) return
if (isWaiting) {
isWaiting = false
left--
winner = RandomSelector.next()!!
stateDidChange(true)
centerPanels.showFor(winner, prizeModel!!)
} else {
isWaiting = true
withdrawButton.isEnabled = false
centerPanels.showRoller()
}
}
fun withdraw() {
left++
RandomSelector.add(winner)
stateDidChange(false)
centerPanels.showPlaceHodler()
}
operator fun hasNext(): Boolean {
return prizeModel != null && RandomSelector.hasNext() && left > 0
}
private fun stateDidChange(isWithdrawButtonEnabled: Boolean) {
nextButton.isEnabled = hasNext()
withdrawButton.isEnabled = isWithdrawButtonEnabled
}
}
package io.github.apollozhu.lottery.lottery
import io.github.apollozhu.lottery.utils.RandomSelector
import javax.swing.Timer
class LotteryRollingPanel : LotteryCenterPanel() {
private val timer = Timer(30) {
label.text = RandomSelector.list[RandomSelector.randomIndex()]
checkState()
}
fun stop() = timer.stop()
fun start() {
if (RandomSelector.hasNext()) timer.start()
}
private fun checkState() {
if (!RandomSelector.hasNext()) stop()
}
init {
start()
}
override fun setVisible(aFlag: Boolean) {
super.setVisible(aFlag)
if (aFlag) timer.start() else timer.stop()
}
}
package io.github.apollozhu.lottery
import io.github.apollozhu.lottery.lottery.LotteryPanel
import io.github.apollozhu.lottery.settings.LotteryPreferences
import io.github.apollozhu.lottery.settings.LotterySettingsPanel
import io.github.apollozhu.lottery.utils.BackgroundMusicPlayer
import java.awt.Color
import java.awt.Container
import java.awt.GraphicsEnvironment
import java.awt.Image
import javax.swing.ImageIcon
import javax.swing.JButton
import javax.swing.JFrame
import javax.swing.JTabbedPane
private val settingsPanel = LotterySettingsPanel()
val frame = JFrame("抽獎軟件(開源代碼 github.com/ApolloZhu/Lottery)")
val tabbedPane = JTabbedPane()
fun main(args: Array<String>) {
val lotteryPanel = LotteryPanel()
tabbedPane.addTab("抽獎", lotteryPanel)
LotteryPreferences.addListener { setBackgroundRecursively(container = lotteryPanel) }
setBackgroundRecursively(container = lotteryPanel)
tabbedPane.addTab("設置", settingsPanel)
tabbedPane.selectedIndex = 1
frame.contentPane = tabbedPane
frame.bounds = GraphicsEnvironment.getLocalGraphicsEnvironment().maximumWindowBounds
frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
LotteryPreferences.addListener { frame.background = LotteryPreferences.backgroundColor }
frame.background = LotteryPreferences.backgroundColor
frame.isVisible = true
BackgroundMusicPlayer.play(/*"/Users/Apollonian/Music/Music Converter/m.wav"*/)
}
fun setBackgroundRecursively(color: Color = LotteryPreferences.backgroundColor, container: Container) {
for (component in container.components) {
component.background = color
if (component is Container) {
setBackgroundRecursively(color, component)
}
}
}
fun imageAspectFit(image: Image, width: Int, height: Int): Image {
val icon = ImageIcon(image)
val isLongThin = icon.iconWidth < icon.iconHeight
val toWidth = if (isLongThin) -1 else width
val toHeight = if (isLongThin) height else -1
return image.getScaledInstance(toWidth, toHeight, Image.SCALE_DEFAULT)
}
package io.github.apollozhu.lottery.prize
import io.github.apollozhu.lottery.lottery.LotteryCenterPanel
open class LotteryPrizeDisplayTextOnlyPanel : LotteryCenterPanel() {
open fun displayFor(name: String, prizeModel: LotteryPrizeModel) {
label.text = "恭喜 $name 獲得 ${prizeModel.name}"
}
}
package io.github.apollozhu.lottery.prize
import SpringUtilities
import io.github.apollozhu.lottery.frame
import io.github.apollozhu.lottery.imageAspectFit
import javax.swing.ImageIcon
import javax.swing.JLabel
import javax.swing.SpringLayout
import javax.swing.SwingConstants
class LotteryPrizeDisplayWithImagePanel : LotteryPrizeDisplayTextOnlyPanel() {
private var imageLabel: JLabel? = null
override fun addLabel() {
layout = SpringLayout()
imageLabel = JLabel("", SwingConstants.RIGHT)
add(imageLabel!!)
label = JLabel("", SwingConstants.LEFT)
add(label)
SpringUtilities.makeCompactGrid(this, 1, 2, 50, 50, 50, 8)
}
override fun displayFor(name: String, prizeModel: LotteryPrizeModel) {
super.displayFor(name, prizeModel)
if (!prizeModel.hasImage) return
val w = frame.bounds.width / 4
val h = frame.bounds.height / 4
imageLabel!!.icon = ImageIcon(imageAspectFit(prizeModel.image!!, w, h))
}
}
package io.github.apollozhu.lottery.prize
import io.github.apollozhu.lottery.imageAspectFit
import io.github.apollozhu.lottery.utils.AZGridBagConstraints
import io.github.apollozhu.swing.AZJButton
import java.awt.GridBagLayout
import java.awt.Image
import javax.swing.*
import javax.swing.filechooser.FileNameExtensionFilter
data class LotteryPrizeGeneratePanel(val identifier: String) : JPanel() {
private val nameTextField = JTextField()
private val countTextField = JTextField()
private val imagePathButton = AZJButton("選擇圖片")
private var prizeImage: Image? = null
init {
layout = GridBagLayout()
add(JLabel("名稱"), AZGridBagConstraints(0, 0))
add(nameTextField, AZGridBagConstraints(1, 0, weightx = 100.0))
add(JLabel("人數"), AZGridBagConstraints(0, 1))
add(countTextField, AZGridBagConstraints(1, 1, weightx = 100.0))
add(JLabel("圖片"), AZGridBagConstraints(0, 2, gridheight = 2, weighty = 100.0))
add(imagePathButton, AZGridBagConstraints(1, 2, gridheight = 2, weightx = 100.0, weighty = 100.0))
imagePathButton.addActionListener {
val chooser = JFileChooser()
chooser.dialogTitle = "選擇${if (hasPrizeName()) prizeName else "獎品"}圖片"
chooser.dialogType = JFileChooser.FILES_ONLY
chooser.fileFilter = FileNameExtensionFilter("Images (jpg/jpeg/gif/png)", "jpg", "jpeg", "gif", "png")
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
imagePathButton.text = chooser.selectedFile.path
prizeImage = ImageIcon(imagePathButton.text).image
imagePathButton.icon = ImageIcon(imageAspectFit(prizeImage!!, imagePathButton.width - 50, imagePathButton.height - 50))
}
}
}
val prizeName: String
get() = nameTextField.text.trim()
fun hasPrizeName() = prizeName.isNotBlank()
val prizeCount: String
get() = countTextField.text.trim()
val unsafePrizeCount: Int?
get() = prizeCount.toIntOrNull()
fun isDataValid() = hasPrizeName()
&& unsafePrizeCount != null
&& unsafePrizeCount!! > 0
val model: LotteryPrizeModel?
get() = if (isDataValid()) LotteryPrizeModel(prizeName, unsafePrizeCount!!, prizeImage) else null
}
package io.github.apollozhu.lottery.prize
import io.github.apollozhu.lottery.tabbedPane
import java.awt.BorderLayout
import java.awt.CardLayout
import java.awt.Font
import java.util.*
import javax.swing.JButton
import javax.swing.JLabel
import javax.swing.JPanel
class LotteryPrizeGeneratePanelManagerPanel : JPanel() {
private val cardLayout = CardLayout()
private val panelList = ArrayList<LotteryPrizeGeneratePanel>()
private val panelsContainer = JPanel()
private var curIndex = 0
private var nextId = Integer.MIN_VALUE
private val label = JLabel("獎項設置")
private val deleteButton = JButton("刪除")
private val previousButton = JButton("上一個")
private val nextButton = JButton("下一個")
private val doneButton = JButton("完成")
init {
layout = BorderLayout()
add(label, BorderLayout.NORTH)
label.font = label.font.deriveFont(Font.BOLD, 35f)
add(panelsContainer, BorderLayout.CENTER)
panelsContainer.layout = cardLayout
addPrize()
val controls = JPanel()
add(controls, BorderLayout.SOUTH)
controls.add(deleteButton)
deleteButton.addActionListener { removePrize() }
deleteButton.isEnabled = hasRemovablePrize()
val button = JButton("添加")
controls.add(button)
button.addActionListener { addPrize() }
controls.add(previousButton)
previousButton.addActionListener {
curIndex--
indexShifted()
}
controls.add(nextButton)
nextButton.addActionListener {
curIndex++
indexShifted()
}
controls.add(doneButton)
doneButton.addActionListener {
tabbedPane.selectedIndex = 0
}
}
private fun indexShifted() {
if (!hasRemovablePrize()) {
previousButton.isEnabled = false
nextButton.isEnabled = false
deleteButton.isEnabled = false
} else {
previousButton.isEnabled = curIndex - 1 >= 0
nextButton.isEnabled = curIndex + 1 < panelList.size
deleteButton.isEnabled = true
}
cardLayout.show(panelsContainer, panelList[curIndex].identifier)
val prizeName = panelList[curIndex].prizeName
label.text = "獎項設置 ${curIndex + 1}/${panelList.size} ${if (prizeName.isBlank()) "" else " - $prizeName"}"
}
private fun addPrize() {
val newPanel = LotteryPrizeGeneratePanel(nextId++.toString() + "")
panelList.add(curIndex, newPanel)
panelsContainer.add(newPanel, newPanel.identifier, curIndex)
indexShifted()
}
internal fun hasRemovablePrize() = panelList.size > 1
private fun removePrize() {
panelList.removeAt(curIndex)
panelsContainer.remove(curIndex)
indexShifted()
}
val prizes: Array<LotteryPrizeModel>
get() = panelList.map { it.model }
.filter { it != null }
.toTypedArray() as Array<LotteryPrizeModel>
}
package io.github.apollozhu.lottery.prize
import java.awt.Image
data class LotteryPrizeModel(val name: String, val count: Int, val image: Image?) {
val hasImage = image != null
}
package io.github.apollozhu.lottery.settings
import io.github.apollozhu.lottery.prize.LotteryPrizeModel
import io.github.apollozhu.lottery.settings.LotteryPreferences.forEachListener
import io.github.apollozhu.util.AZListenable
import java.awt.Color
import java.util.prefs.PreferenceChangeListener
import java.util.prefs.Preferences
import javax.swing.event.EventListenerList
object LotteryPreferences : AZListenable<PreferenceChangeListener> {
private val preferences = Preferences.userNodeForPackage(javaClass)
private val list = EventListenerList()
override fun getListenerList() = list
internal fun fireUpdate() = forEachListener { it.preferenceChange(null) }
var title: String
get() {
val title = preferences.get("title", "")
return if (title.isBlank()) "抽獎" else title
}
set(value) = preferences.put("title", value)
var titleSize: Float
get() = preferences.getFloat("titleSize", 80f)
set(value) = preferences.putFloat("titleSize", value)
var titleColor: Color
get() = Color(preferences.getInt("titleColor", 0XFFFF00))
set(value) = preferences.putInt("titleColor", value.rgb)
var subtitle: String
get() = preferences.get("subtitle", "")
set(value) = preferences.put("subtitle", value)
var subtitleSize: Float
get() = preferences.getFloat("subtitleSize", 50f)
set(value) = preferences.putFloat("subtitleSize", value)
var subtitleColor: Color
get() = Color(preferences.getInt("subtitleColor", 0))
set(value) = preferences.putInt("subtitleColor", value.rgb)
var backgroundMusicPath: String
get() {
val backgroundMusicPath = preferences.get("backgroundMusicPath", "")
return if (backgroundMusicPath.isBlank()) "選擇背景音樂" else backgroundMusicPath
}
set(value) = preferences.put("backgroundMusicPath", value)
var backgroundImagePath: String
get() {
val backgroundImagePath = preferences.get("backgroundImagePath", "")
return if (backgroundImagePath.isBlank()) "選擇背景圖片" else backgroundImagePath
}
set(value) = preferences.put("backgroundImagePath", value)
var backgroundColor: Color
get() = Color(preferences.getInt("backgroundColor", 0xFF0000))
set(value) = preferences.putInt("backgroundColor", value.rgb)
var listPath: String
get() {
val listPath = preferences.get("listPath", "")
return if (listPath.isBlank()) "選擇可中獎者名單" else listPath
}
set(value) = preferences.put("listPath", value)
var winnerSize: Float
get() = preferences.getFloat("winnerSize", 80f)
set(value) = preferences.putFloat("winnerSize", value)
var winnerColor: Color
get() = Color(preferences.getInt("winnerColor", 0XFFFF00))
set(value) = preferences.putInt("winnerColor", value.rgb)
var prizes: Array<LotteryPrizeModel> = arrayOf()
}
package io.github.apollozhu.lottery.settings
import SpringUtilities
import io.github.apollozhu.lottery.utils.PreferenceLoading
import io.github.apollozhu.swing.AZJButton
import java.awt.BorderLayout
import java.awt.Font
import java.util.prefs.PreferenceChangeEvent
import javax.swing.*
import javax.swing.filechooser.FileNameExtensionFilter
class LotterySettingsBasicSettingsPanel : JPanel(), PreferenceLoading {
private val titleTextField = JTextField()
private val titleSizeTextField = JTextField()
private val titleColorButton = AZJButton()
private val subtitleTextField = JTextField()
private val subtitleSizeTextField = JTextField()
private val subtitleColorButton = AZJButton()
private val backgroundMusicPathButton = AZJButton()
private val backgroundImagePathButton = AZJButton()
private val backgroundColorButton = AZJButton()
private val listButton = AZJButton()
private val winnerSizeTextField = JTextField()
private val winnerColorButton = AZJButton()
init {
layout = BorderLayout()
val promptLabel = JLabel("基本設置")
add(promptLabel, BorderLayout.NORTH)
promptLabel.font = promptLabel.font.deriveFont(Font.BOLD, 35f)
val uiSettingsPanel = JPanel()
add(uiSettingsPanel, BorderLayout.CENTER)
uiSettingsPanel.layout = SpringLayout()
var label = JLabel("標題", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
uiSettingsPanel.add(titleTextField)
label = JLabel("大小", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
uiSettingsPanel.add(titleSizeTextField)
label = JLabel("顏色", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
titleColorButton.addActionListener {
titleColorButton.background = JColorChooser.showDialog(this, "選擇標題的顏色", titleColorButton.background) ?: titleColorButton.background
}
uiSettingsPanel.add(titleColorButton)
label = JLabel("副標題", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
uiSettingsPanel.add(subtitleTextField)
label = JLabel("大小", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
uiSettingsPanel.add(subtitleSizeTextField)
label = JLabel("顏色", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
subtitleColorButton.addActionListener {
subtitleColorButton.background = JColorChooser.showDialog(this, "選擇副標題的顏色", subtitleColorButton.background) ?: subtitleColorButton.background
}
uiSettingsPanel.add(subtitleColorButton)
label = JLabel("背景音樂", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
backgroundMusicPathButton.addActionListener {
val chooser = JFileChooser(backgroundMusicPathButton.text)
chooser.dialogTitle = "選擇背景音樂"
chooser.dialogType = JFileChooser.FILES_ONLY
chooser.fileFilter = FileNameExtensionFilter("Media (aif/aiff/fxm/flv/m3u8/mp3/mp4/m4a/m4v/wav)", "aif", "aiff", "fxm", "flv", "m3u8", "mp3", "mp4", "m4a", "m4v", "wav")
when (chooser.showOpenDialog(this)) {
JFileChooser.APPROVE_OPTION -> backgroundMusicPathButton.text = chooser.selectedFile.path
else -> backgroundMusicPathButton.text = ""
}
}
uiSettingsPanel.add(backgroundMusicPathButton)
label = JLabel("背景圖片", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
backgroundImagePathButton.isEnabled = false
uiSettingsPanel.add(backgroundImagePathButton)
label = JLabel("背景顏色", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
backgroundColorButton.addActionListener {
backgroundColorButton.background = JColorChooser.showDialog(this, "選擇背景顏色", backgroundColorButton.background) ?: backgroundColorButton.background
}
uiSettingsPanel.add(backgroundColorButton)
uiSettingsPanel.layout = SpringLayout()
label = JLabel("可中獎者名單", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
listButton.addActionListener {
val chooser = JFileChooser(listButton.text)
chooser.dialogTitle = "選擇可中獎者名單"
chooser.dialogType = JFileChooser.FILES_ONLY
when (chooser.showOpenDialog(this)) {
JFileChooser.APPROVE_OPTION -> listButton.text = chooser.selectedFile.path
}
}
uiSettingsPanel.add(listButton)
label = JLabel("大小", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
uiSettingsPanel.add(winnerSizeTextField)
label = JLabel("顏色", SwingConstants.RIGHT)
uiSettingsPanel.add(label)
winnerColorButton.addActionListener {
winnerColorButton.background = JColorChooser.showDialog(this, "選擇中獎者的顏色", winnerColorButton.background) ?: winnerColorButton.background
}
uiSettingsPanel.add(winnerColorButton)
SpringUtilities.makeCompactGrid(uiSettingsPanel, 4, 6, 8, 8, 8, 8)
loadPreferences()
}
override fun loadPreferences(ignored: PreferenceChangeEvent?) {
titleTextField.text = LotteryPreferences.title
titleSizeTextField.text = "${LotteryPreferences.titleSize}"
titleColorButton.background = LotteryPreferences.titleColor
subtitleTextField.text = LotteryPreferences.subtitle
subtitleSizeTextField.text = "${LotteryPreferences.subtitleSize}"
subtitleColorButton.background = LotteryPreferences.subtitleColor
backgroundMusicPathButton.text = LotteryPreferences.backgroundMusicPath
backgroundImagePathButton.text = LotteryPreferences.backgroundImagePath
backgroundColorButton.background = LotteryPreferences.backgroundColor
listButton.text = LotteryPreferences.listPath
winnerSizeTextField.text = "${LotteryPreferences.winnerSize}"
winnerColorButton.background = LotteryPreferences.winnerColor
}
fun savePreferences() {
LotteryPreferences.title = titleTextField.text
LotteryPreferences.titleSize = titleSizeTextField.text.toFloatOrNull() ?: LotteryPreferences.titleSize
LotteryPreferences.titleColor = titleColorButton.background
LotteryPreferences.subtitle = subtitleTextField.text
LotteryPreferences.subtitleSize = subtitleSizeTextField.text.toFloatOrNull() ?: LotteryPreferences.subtitleSize
LotteryPreferences.subtitleColor = subtitleColorButton.background
LotteryPreferences.backgroundMusicPath = backgroundMusicPathButton.text
LotteryPreferences.backgroundImagePath = backgroundImagePathButton.text
LotteryPreferences.backgroundColor = backgroundColorButton.background
LotteryPreferences.listPath = listButton.text
LotteryPreferences.winnerSize = winnerSizeTextField.text.toFloatOrNull() ?: LotteryPreferences.winnerSize
LotteryPreferences.winnerColor = winnerColorButton.background
}
}
package io.github.apollozhu.lottery.settings
import io.github.apollozhu.lottery.prize.LotteryPrizeGeneratePanelManagerPanel
import java.awt.BorderLayout
import javax.swing.JPanel
class LotterySettingsPanel : JPanel() {
private val basic = LotterySettingsBasicSettingsPanel()
private val prize = LotteryPrizeGeneratePanelManagerPanel()
init {
layout = BorderLayout()
add(basic, BorderLayout.NORTH)
add(prize, BorderLayout.CENTER)
}
override fun setVisible(aFlag: Boolean) {
super.setVisible(aFlag)
if (aFlag) basic.loadPreferences() else {
basic.savePreferences()
LotteryPreferences.prizes = prize.prizes
LotteryPreferences.fireUpdate()
}
}
}
package io.github.apollozhu.lottery.utils
import java.awt.GridBagConstraints
import java.awt.Insets
class AZGridBagConstraints(gridx: Int = RELATIVE, gridy: Int = RELATIVE, gridwidth: Int = 1, gridheight: Int = 1, weightx: Double = 0.0, weighty: Double = 0.0, anchor: Int = CENTER, fill: Int = BOTH, insets: Insets = Insets(0, 0, 0, 0), ipadx: Int = 0, ipady: Int = 0) : GridBagConstraints(gridx, gridy, gridwidth, gridheight, weightx, weighty, anchor, fill, insets, ipadx, ipady) {}
package io.github.apollozhu.lottery.utils
import io.github.apollozhu.lottery.settings.LotteryPreferences
import javafx.application.Platform
import javafx.embed.swing.JFXPanel
import javafx.scene.media.Media
import javafx.scene.media.MediaPlayer
import java.io.File
object BackgroundMusicPlayer {
// IMPORTANT: Magic, Do NOT Touch
val panel = JFXPanel()
// END-IMPORTANT
init {
LotteryPreferences.addListener { play() }
play()
}
var currentPlayer: MediaPlayer? = null
fun stop() {
currentPlayer?.stop()
}
private var currentPath = ""
fun play(path: String = LotteryPreferences.backgroundMusicPath) {
Platform.runLater {
if (path != currentPath) {
currentPath = path
try {
val media = Media(File(path).toURI().toString())
val player = MediaPlayer(media)
player.cycleCount = MediaPlayer.INDEFINITE
player.play()
stop()
currentPlayer = player
} catch (_: Exception) {
}
}
}
}
}
package io.github.apollozhu.lottery.utils
import java.util.prefs.PreferenceChangeEvent
interface PreferenceLoading {
fun loadPreferences(ignored: PreferenceChangeEvent?)
fun loadPreferences() = loadPreferences(null)
}
package io.github.apollozhu.lottery.utils
import io.github.apollozhu.lottery.settings.LotteryPreferences
import io.github.apollozhu.lottery.utils.RandomSelector.forEachListener
import io.github.apollozhu.util.AZListenable
import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
import java.util.stream.Collectors
import java.util.stream.Stream
import javax.swing.event.ChangeListener
import javax.swing.event.EventListenerList
object RandomSelector : AZListenable<ChangeListener> {
private val changeListenerList = EventListenerList()
override fun getListenerList() = changeListenerList
init {
LotteryPreferences.addListener { loadList() }
loadList()
}
var list: MutableList<String> = ArrayList()
private var currentPath = ""
fun loadList(stringPath: String = LotteryPreferences.listPath, cs: Charset? = null) {
val path = Paths.get(stringPath)
if (stringPath == currentPath || !Files.exists(path)) {
return
}
currentPath = stringPath
var stream: Stream<String>
try {
stream = Files.lines(path, cs ?: Charset.forName("UTF8"))
} catch (ignored: Exception) {
stream = Files.lines(path)
}
list = stream
.map { it.replace("\\s+", "") }
.filter { !it.isBlank() }
.collect(Collectors.toList())
forEachListener { it.stateChanged(null) }
}
fun add(it: String) = list.add(it)
fun hasNext(): Boolean = list.size > 0
fun randomIndex(): Int = (Math.random() * list.size).toInt()
fun next(): String? = if (hasNext()) list.removeAt(randomIndex()) else null
}
/**
* MIT License
* Copyright (c) 2017 Apollo Zhu (朱智语).
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.apollozhu.swing;
import javax.swing.*;
import java.awt.*;
import java.beans.ConstructorProperties;
/**
* A subclass of JButton with {@link #setBackground(Color)} working on macOS/OS X.
*
* @author Apollo Zhu
*
* @version 1.0
*/
@SuppressWarnings("serial")
public class AZJButton extends JButton {
/**
* Creates a button with no set text or icon.
*/
public AZJButton() {
super();
}
/**
* Creates a button where properties are taken from the <code>Action</code>
* supplied.
*
* @param a the <code>Action</code> used to specify the new button
*/
public AZJButton(Action a) {
super(a);
}
/**
* Creates a button with an icon.
*
* @param icon the Icon image to display on the button
*/
public AZJButton(Icon icon) {
super(icon);
}
/**
* Creates a button with text.
*
* @param text the text of the button
*/
@ConstructorProperties({"text"})
public AZJButton(String text) {
super(text);
}
/**
* Creates a button with initial text and an icon.
*
* @param text the text of the button
* @param icon the Icon image to display on the button
*/
public AZJButton(String text, Icon icon) {
super(text, icon);
}
/** Selected menu item color from Apple's Developer Swatch. */
protected static final Color SELECTED_MENU_ITEM_COLOR = new Color(3, 100, 236);
/** Header color from Apple's Developer Swatch. */
protected static final Color HEADER_COLOR = new Color(174, 174, 174);
/** Keyboard focus indicator color from Apple's Developer Swatch. */
protected static final Color KEYBOARD_FOCUS_INDICATOR_COLOR = new Color(76, 149, 255);
/** Selected control color from Apple's Developer Swatch. */
protected static final Color SELECTED_CONTROL_COLOR = new Color(164, 205, 255);
/**
* Check if UIManager is using Apple Aqua Look and Feel.
*
* @return true if current look and feel is aqua.
*/
private boolean isAquaUI() {
try {
return getBorder().getClass().getName().contains("Aqua");
} catch (Exception e) {
return false;
}
}
/**
* Calls the super implementation, unless UIManager is using Apple Aqua Look
* and Feel.
*
* If it is, and if border is required, and has a different background than
* the default one, the button will be drawn differently to match the actual
* look and feel for a button with background color on OS X Yosemite (10.10)
* and other macOSs above.
*
* @param g the <code>Graphics</code> object to protect
*
* @see #setBackground(Color)
*/
@Override
public void paintComponent(Graphics g) {
if (!isAquaUI()) {
super.paintComponent(g);
return;
}
final Color backgroundColor = getBackground(), foregroundColor = getForeground();
final boolean hasCustomBackground = !backgroundColor.equals(UIManager.getColor("Button.background"));
final boolean isBorderPainted = isBorderPainted();
final boolean isOpaque = isOpaque();
if (isBorderPainted && hasCustomBackground) {
final boolean isPressed = getModel().isPressed();
Insets i = getBorder().getBorderInsets(this);
int r = 8, offset = i.top, x = offset, y = offset, w = getWidth() - 2 * offset,
h = getHeight() - offset - i.bottom;
Graphics2D g2 = (Graphics2D) g;
if (isPressed) {
int midX = getWidth() / 2;
g2.setPaint(new GradientPaint(midX, 0, KEYBOARD_FOCUS_INDICATOR_COLOR, midX, getHeight(), SELECTED_MENU_ITEM_COLOR));
setForeground(Color.white);
} else {
g2.setColor(backgroundColor);
}
g2.fillRoundRect(x, y, w, h, r, r);
g2.setColor(HEADER_COLOR);
g2.drawRoundRect(x, y, w, h, r, r);
if (isPressed || isFocusOwner()) {
g2.setColor(SELECTED_CONTROL_COLOR);
g2.setStroke(new BasicStroke(2));
g2.drawRoundRect(x, y, w, h, r, r);
}
super.setBorderPainted(false);
super.setOpaque(false);
}
super.paintComponent(g);
super.setBorderPainted(isBorderPainted);
super.setOpaque(isOpaque);
super.setForeground(foregroundColor);
}
}
package io.github.apollozhu.swing;
import javax.swing.*;
import java.awt.*;
import java.lang.reflect.Method;
import java.util.List;
/**
* A subclass of JFrame with {@link #setIconImage(Image)} and
* {@link #setIconImages(List)} working on macOS/OS X.
*
* @author Apollo Zhu
* @version 1.0
*/
@SuppressWarnings("serial")
public class AZJFrame extends JFrame {
/**
* Constructs a new frame that is initially invisible.
* <p>
* This constructor sets the component's locale property to the value
* returned by <code>JComponent.getDefaultLocale</code>.
*
* @throws HeadlessException if GraphicsEnvironment.isHeadless()
* returns true.
* @see java.awt.GraphicsEnvironment#isHeadless
* @see Component#setSize
* @see Component#setVisible
* @see JComponent#getDefaultLocale
*/
public AZJFrame() throws HeadlessException {
super();
}
/**
* Creates a <code>Frame</code> in the specified
* <code>GraphicsConfiguration</code> of
* a screen device and a blank title.
* <p>
* This constructor sets the component's locale property to the value
* returned by <code>JComponent.getDefaultLocale</code>.
*
* @param gc the <code>GraphicsConfiguration</code> that is used
* to construct the new <code>Frame</code>;
* if <code>gc</code> is <code>null</code>, the system
* default <code>GraphicsConfiguration</code> is assumed
* @throws IllegalArgumentException if <code>gc</code> is not from
* a screen device. This exception is always thrown when
* GraphicsEnvironment.isHeadless() returns true.
* @see java.awt.GraphicsEnvironment#isHeadless
* @see JComponent#getDefaultLocale
* @since 1.3
*/
public AZJFrame(GraphicsConfiguration gc) {
super(gc);
}
/**
* Creates a new, initially invisible <code>Frame</code> with the
* specified title.
* <p>
* This constructor sets the component's locale property to the value
* returned by <code>JComponent.getDefaultLocale</code>.
*
* @param title the title for the frame
* @throws HeadlessException if GraphicsEnvironment.isHeadless()
* returns true.
* @see java.awt.GraphicsEnvironment#isHeadless
* @see Component#setSize
* @see Component#setVisible
* @see JComponent#getDefaultLocale
*/
public AZJFrame(String title) throws HeadlessException {
super(title);
}
/**
* Creates a <code>JFrame</code> with the specified title and the
* specified <code>GraphicsConfiguration</code> of a screen device.
* <p>
* This constructor sets the component's locale property to the value
* returned by <code>JComponent.getDefaultLocale</code>.
*
* @param title the title to be displayed in the
* frame's border. A <code>null</code> value is treated as
* an empty string, "".
* @param gc the <code>GraphicsConfiguration</code> that is used
* to construct the new <code>JFrame</code> with;
* if <code>gc</code> is <code>null</code>, the system
* default <code>GraphicsConfiguration</code> is assumed
* @throws IllegalArgumentException if <code>gc</code> is not from
* a screen device. This exception is always thrown when
* GraphicsEnvironment.isHeadless() returns true.
* @see java.awt.GraphicsEnvironment#isHeadless
* @see JComponent#getDefaultLocale
* @since 1.3
*/
public AZJFrame(String title, GraphicsConfiguration gc) {
super(title, gc);
}
/**
* Sets the sequence of images to be displayed as the icon
* for this window. Subsequent calls to {@code getIconImages} will
* always return a copy of the {@code icons} list.
* <p>
* Depending on the platform capabilities one or several images
* of different dimensions will be used as the window's icon.
* <p>
* The {@code icons} list is scanned for the images of most
* appropriate dimensions from the beginning. If the list contains
* several images of the same size, the first will be used.
* <p>
* Ownerless windows with no icon specified use platfrom-default icon.
* The icon of an owned window may be inherited from the owner
* unless explicitly overridden.
* Setting the icon to {@code null} or empty list restores
* the default behavior.
* <p>
* Note : Native windowing systems may use different images of differing
* dimensions to represent a window, depending on the context (e.g.
* window decoration, window list, taskbar, etc.). They could also use
* just a single image for all contexts or no image at all.
*
* @param icons the list of icon images to be displayed.
* @see #getIconImages()
* @see #setIconImage(Image)
* @since 1.6
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public synchronized void setIconImages(List<? extends Image> icons) {
super.setIconImages(icons);
try {
Class NSApplication = Class.forName("com.apple.eawt.Application");
Method sharedApplication = NSApplication.getMethod("getApplication");
Object shared = sharedApplication.invoke(NSApplication);
Method setApplicationIconImage = NSApplication.getMethod("setDockIconImage", Image.class);
setApplicationIconImage.invoke(shared, getIconImage());
} catch (Exception e) {
}
}
}
/**
* MIT License
* Copyright (c) 2017 Apollo Zhu (朱智语).
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.github.apollozhu.swing;
import io.github.apollozhu.util.AZListenable;
import javax.swing.*;
import javax.swing.event.EventListenerList;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* A subclass of JLabel with {@link #setText(String)} working with \n.
*
* @author Apollo Zhu
*
* @version 1.0
*/
@SuppressWarnings("serial")
public class AZJLabel extends JLabel implements AZListenable<ActionListener> {
/**
* Creates a <code>JLabel</code> instance with the specified
* text, image, and horizontal alignment.
* The label is centered vertically in its display area.
* The text is on the trailing edge of the image.
*
* @param text The text to be displayed by the label.
* @param icon The image to be displayed by the label.
* @param horizontalAlignment One of the following constants
* defined in <code>SwingConstants</code>:
* <code>LEFT</code>,
* <code>CENTER</code>,
* <code>RIGHT</code>,
* <code>LEADING</code> or
* <code>TRAILING</code>.
*/
public AZJLabel(String text, Icon icon, int horizontalAlignment) {
super(text, icon, horizontalAlignment);
}
/**
* Creates a <code>JLabel</code> instance with the specified
* text and horizontal alignment.
* The label is centered vertically in its display area.
*
* @param text The text to be displayed by the label.
* @param horizontalAlignment One of the following constants
* defined in <code>SwingConstants</code>:
* <code>LEFT</code>,
* <code>CENTER</code>,
* <code>RIGHT</code>,
* <code>LEADING</code> or
* <code>TRAILING</code>.
*/
public AZJLabel(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
}
/**
* Creates a <code>JLabel</code> instance with the specified text.
* The label is aligned against the leading edge of its display area,
* and centered vertically.
*
* @param text The text to be displayed by the label.
*/
public AZJLabel(String text) {
super(text);
}
/**
* Creates a <code>JLabel</code> instance with the specified
* image and horizontal alignment.
* The label is centered vertically in its display area.
*
* @param image The image to be displayed by the label.
* @param horizontalAlignment One of the following constants
* defined in <code>SwingConstants</code>:
* <code>LEFT</code>,
* <code>CENTER</code>,
* <code>RIGHT</code>,
* <code>LEADING</code> or
* <code>TRAILING</code>.
*/
public AZJLabel(Icon image, int horizontalAlignment) {
super(image, horizontalAlignment);
}
/**
* Creates a <code>JLabel</code> instance with the specified image.
* The label is centered vertically and horizontally
* in its display area.
*
* @param image The image to be displayed by the label.
*/
public AZJLabel(Icon image) {
super(image);
}
/**
* Creates a <code>JLabel</code> instance with
* no image and with an empty string for the title.
* The label is centered vertically
* in its display area.
* The label's contents, once set, will be displayed on the leading edge
* of the label's display area.
*/
public AZJLabel() {
super();
}
/**
* Defines the single line of text this component will display. If the value
* of text is null or empty string, nothing is displayed. If the text
* contains \n, it is converted to <br />
* tag, and the whole text is wrapped around by \<html\> tag. Then fires an
* action event after text changed.
* <p>
* The default value of this property is null.
* <p>
* This is a JavaBeans bound property.
*
* @see #setVerticalTextPosition
* @see #setHorizontalTextPosition
* @see #setIcon
* @beaninfo
* preferred: true
* bound: true
* attribute: visualUpdate true
* description: Defines the single line of text this component will display.
*/
@Override
public void setText(String text) {
if (text != null && !text.startsWith("<html>") && !text.endsWith("</html>") && text.contains("\n"))
text = "<html>" + text.replaceAll("\n", "<br />") + "</html>";
super.setText(text);
forEachListener(listener -> listener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "setText")));
}
private EventListenerList listenerList = new EventListenerList();
@Override
public EventListenerList getListenerList() {
return listenerList;
}
}
package io.github.apollozhu.util;
import javax.swing.event.EventListenerList;
import java.util.EventListener;
import java.util.function.Consumer;
/**
* A easy way to become listenable. Could be more pleasant if ****ing Java
* supports requiring instance variable in interface.
*
* @author Apollo Zhu
* @version 1.0
*/
@FunctionalInterface
public interface AZListenable<Listener extends EventListener> {
/**
* Suggested implementation:
* <p>
* <pre>
* <code>import javax.swing.event.EventListenerList;
* // ...
* private EventListenerList list = new EventListenerList();
* {@literal @}Override
* public EventListenerList getListenerList() {
* return list;
* }</code>
* </pre>
*
* @return the persisted event listener list.
*/
EventListenerList getListenerList();
@SuppressWarnings("unchecked")
default Class<Listener> getListenerClass() {
return (Class<Listener>) EventListener.class;
}
default void addListener(Listener l) {
getListenerList().add(getListenerClass(), l);
}
default void removeListener(Listener l) {
getListenerList().remove(getListenerClass(), l);
}
default void forEachListener(Consumer<Listener> consumer) {
for (Listener listener : getListenerList().getListeners(getListenerClass()))
if (listener != null)
consumer.accept(listener);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment