├── src └── org │ └── kwt │ ├── package.html │ ├── ui │ ├── package.html │ ├── KWTCheckboxGroup.java │ ├── KWTScrollPanel.java │ ├── KWTCheckbox.java │ ├── KWTProgressBar.java │ └── KWTSelectableLabel.java │ └── InvalidStyleException.java ├── .gitignore ├── samples ├── KWTCheckbox │ ├── kindlet.properties │ ├── src │ │ └── org │ │ │ └── kwt │ │ │ └── samples │ │ │ └── kwtcheckbox │ │ │ └── Main.java │ └── build.xml ├── KWTProgressBar │ ├── kindlet.properties │ ├── src │ │ └── org │ │ │ └── kwt │ │ │ └── samples │ │ │ └── kwtprogressbar │ │ │ └── Main.java │ └── build.xml ├── KWTScrollPanel │ ├── kindlet.properties │ ├── src │ │ └── org │ │ │ └── kwt │ │ │ └── samples │ │ │ └── kwtscrollpanel │ │ │ └── Main.java │ └── build.xml └── KWTSelectableLabel │ ├── kindlet.properties │ ├── src │ └── org │ │ └── kwt │ │ └── samples │ │ └── kwtselectablelabel │ │ └── Main.java │ └── build.xml ├── README.md └── LICENSE /src/org/kwt/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | General utility classes for KWT. 4 | 5 | -------------------------------------------------------------------------------- /src/org/kwt/ui/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | A collection of general user interface elements for KWT. 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directory ignore 2 | build/ 3 | 4 | # Eclipse-specific ignores 5 | bin/ 6 | .settings/ 7 | .classpath 8 | .project 9 | 10 | -------------------------------------------------------------------------------- /samples/KWTCheckbox/kindlet.properties: -------------------------------------------------------------------------------- 1 | kindlet.title=KWTCheckboxSample 2 | kindlet.url=http\://github.com/AdrianP/Kindle-Widget-Toolkit 3 | kindlet.image= 4 | kindlet.keystore=developer-signing.keystore 5 | kindlet.version=1 6 | kindlet.mainClass=org.kwt.samples.kwtcheckbox.Main 7 | kindlet.networkSupport=false 8 | kindlet.vendor=KWT 9 | kindlet.id= 10 | -------------------------------------------------------------------------------- /samples/KWTProgressBar/kindlet.properties: -------------------------------------------------------------------------------- 1 | kindlet.title=KWTProgressBarSample 2 | kindlet.url=http\://github.com/AdrianP/Kindle-Widget-Toolkit 3 | kindlet.image= 4 | kindlet.keystore=developer-signing.keystore 5 | kindlet.version=1 6 | kindlet.mainClass=org.kwt.samples.kwtprogressbar.Main 7 | kindlet.networkSupport=false 8 | kindlet.vendor=KWT 9 | kindlet.id= 10 | -------------------------------------------------------------------------------- /samples/KWTScrollPanel/kindlet.properties: -------------------------------------------------------------------------------- 1 | kindlet.title=KWTScrollPanelSample 2 | kindlet.url=http\://github.com/AdrianP/Kindle-Widget-Toolkit 3 | kindlet.image= 4 | kindlet.keystore=developer-signing.keystore 5 | kindlet.version=1 6 | kindlet.mainClass=org.kwt.samples.kwtscrollpanel.Main 7 | kindlet.networkSupport=false 8 | kindlet.vendor=KWT 9 | kindlet.id= 10 | -------------------------------------------------------------------------------- /samples/KWTSelectableLabel/kindlet.properties: -------------------------------------------------------------------------------- 1 | kindlet.title=KWTSelectableLabelSample 2 | kindlet.url=http\://github.com/AdrianP/Kindle-Widget-Toolkit 3 | kindlet.image= 4 | kindlet.keystore=developer-signing.keystore 5 | kindlet.version=1 6 | kindlet.mainClass=org.kwt.samples.kwtselectablelabel.Main 7 | kindlet.networkSupport=false 8 | kindlet.vendor=KWT 9 | kindlet.id= 10 | -------------------------------------------------------------------------------- /samples/KWTScrollPanel/src/org/kwt/samples/kwtscrollpanel/Main.java: -------------------------------------------------------------------------------- 1 | package org.kwt.samples.kwtscrollpanel; 2 | 3 | import java.awt.Container; 4 | 5 | import org.kwt.ui.KWTScrollPanel; 6 | import org.kwt.ui.KWTSelectableLabel; 7 | 8 | import com.amazon.kindle.kindlet.AbstractKindlet; 9 | import com.amazon.kindle.kindlet.KindletContext; 10 | import com.amazon.kindle.kindlet.ui.KBoxLayout; 11 | import com.amazon.kindle.kindlet.ui.KLabel; 12 | import com.amazon.kindle.kindlet.ui.KPanel; 13 | 14 | /** 15 | * This sample Kindlet demonstrates the use of the KWTScrollPanel 16 | * 17 | * @author Adrian Petrescu 18 | * 19 | */ 20 | public class Main extends AbstractKindlet { 21 | 22 | public void create(final KindletContext context) { 23 | final Container rootContainer = context.getRootContainer(); 24 | Container testContainer = new KWTScrollPanel(80); 25 | testContainer.setLayout(new KBoxLayout(testContainer, KBoxLayout.Y_AXIS)); 26 | rootContainer.add(testContainer); 27 | 28 | KLabel[] labels = new KLabel[25]; 29 | for (int i = 0; i < labels.length; i++) { 30 | labels[i] = new KLabel("This is Label #" + i); 31 | testContainer.add(labels[i]); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/org/kwt/InvalidStyleException.java: -------------------------------------------------------------------------------- 1 | package org.kwt; 2 | 3 | /** 4 | * An exception thrown by styleable KWT components which indicates that they do not support 5 | * the provided style. This may occur either because an incorrect style from another class was 6 | * provided, or because that particular style applies to some other part of the component. 7 | * 8 | */ 9 | public class InvalidStyleException extends RuntimeException { 10 | private static final long serialVersionUID = -8543895842607438654L; 11 | 12 | private int invalidStyle; 13 | 14 | /** 15 | * Constructs a new exception with the specified detail message and style. 16 | * @param message the detail message 17 | * @param invalidStyle the style which could not be applied 18 | */ 19 | public InvalidStyleException(String message, int invalidStyle) { 20 | super(message); 21 | this.invalidStyle = invalidStyle; 22 | } 23 | 24 | /** 25 | * Returns the style which could not be applied. 26 | * @return the invalid style 27 | */ 28 | public int getInvalidStyle() { 29 | return invalidStyle; 30 | } 31 | 32 | /** 33 | * Returns a detail message specifying why the style could not be applied. 34 | * @return the detail message 35 | */ 36 | public String getMessage() { 37 | return super.getMessage() + " [Invalid Style: " + invalidStyle + "]"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /samples/KWTCheckbox/src/org/kwt/samples/kwtcheckbox/Main.java: -------------------------------------------------------------------------------- 1 | package org.kwt.samples.kwtcheckbox; 2 | 3 | import java.awt.Container; 4 | import java.awt.FlowLayout; 5 | 6 | import org.kwt.ui.KWTCheckbox; 7 | import org.kwt.ui.KWTCheckboxGroup; 8 | 9 | import com.amazon.kindle.kindlet.AbstractKindlet; 10 | import com.amazon.kindle.kindlet.KindletContext; 11 | import com.amazon.kindle.kindlet.ui.KLabel; 12 | 13 | /** 14 | * This sample Kindlet demonstrates the use of the KWTCheckbox. 15 | * 16 | * @author Adrian Petrescu 17 | * 18 | */ 19 | public class Main extends AbstractKindlet { 20 | 21 | public void create(final KindletContext context) { 22 | Container rootContainer = context.getRootContainer(); 23 | rootContainer.setLayout(new FlowLayout()); 24 | 25 | KWTCheckboxGroup group = new KWTCheckboxGroup(); 26 | 27 | rootContainer.add(new KWTCheckbox(group)); 28 | rootContainer.add(new KWTCheckbox(group)); 29 | rootContainer.add(new KWTCheckbox(group)); 30 | rootContainer.add(new KWTCheckbox(group)); 31 | rootContainer.add(new KWTCheckbox(group)); 32 | 33 | group = null; 34 | 35 | rootContainer.add(new KWTCheckbox(group)); 36 | rootContainer.add(new KWTCheckbox(group)); 37 | rootContainer.add(new KWTCheckbox(group)); 38 | rootContainer.add(new KWTCheckbox(group)); 39 | rootContainer.add(new KWTCheckbox(group)); 40 | 41 | KWTCheckbox button = new KWTCheckbox(group); 42 | rootContainer.add(button); 43 | 44 | button.requestFocus(); 45 | rootContainer.add(new KLabel("Click this option!")); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/org/kwt/ui/KWTCheckboxGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Dan Fabulich. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located in the LICENSE file included with this 7 | * distribution. 8 | * 9 | * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | * CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the 12 | * License. 13 | */ 14 | 15 | package org.kwt.ui; 16 | 17 | /** 18 | * This group binds together multiple radio buttons, ensuring that only one 19 | * radio button is selected at a time. 20 | * 21 | * @author Dan Fabulich 22 | * 23 | */ 24 | public class KWTCheckboxGroup { 25 | private KWTCheckbox selected = null; 26 | 27 | /** 28 | * Returns the currently selected radio button. 29 | * 30 | * @return the currently selected radio button 31 | */ 32 | public KWTCheckbox getSelected() { 33 | return selected; 34 | } 35 | 36 | /** 37 | * Selects a radio button, deselecting the previously selected button. 38 | * 39 | * @param box a radio button belonging to this group, or null to deselect 40 | * everything. 41 | */ 42 | public void setSelected(KWTCheckbox box) { 43 | if (box != null && box.group != this) { 44 | return; 45 | } 46 | KWTCheckbox oldChoice = this.selected; 47 | this.selected = box; 48 | if (oldChoice != null && oldChoice != box && oldChoice.group == this) { 49 | oldChoice.setSelected(false); 50 | } 51 | if (box != null && oldChoice != box && !box.isSelected()) { 52 | box.setSelected(true); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /samples/KWTSelectableLabel/src/org/kwt/samples/kwtselectablelabel/Main.java: -------------------------------------------------------------------------------- 1 | package org.kwt.samples.kwtselectablelabel; 2 | 3 | import java.awt.Container; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | 7 | import org.kwt.ui.KWTSelectableLabel; 8 | 9 | import com.amazon.kindle.kindlet.AbstractKindlet; 10 | import com.amazon.kindle.kindlet.KindletContext; 11 | import com.amazon.kindle.kindlet.ui.KBoxLayout; 12 | import com.amazon.kindle.kindlet.ui.KLabel; 13 | 14 | /** 15 | * This sample Kindlet demonstrates the use of the KWTSelectableLabel. 16 | * 17 | * @author Adrian Petrescu 18 | * 19 | */ 20 | public class Main extends AbstractKindlet { 21 | 22 | public void create(final KindletContext context) { 23 | final Container rootContainer = context.getRootContainer(); 24 | rootContainer.setLayout(new KBoxLayout(rootContainer, KBoxLayout.Y_AXIS)); 25 | 26 | final KLabel description = new KLabel("No label has been selected yet."); 27 | 28 | KWTSelectableLabel[] labels = new KWTSelectableLabel[5]; 29 | for (int i = 0; i < labels.length; i++) { 30 | final int selectedLabel = i; 31 | labels[i] = new KWTSelectableLabel("This is Label #" + i); 32 | labels[i].setUnderlineWidth((i+1) * 2); 33 | labels[i].setUnderlineGap(i * 2); 34 | labels[i].setUnderlineStyle((i % 2 == 0) ? 35 | KWTSelectableLabel.STYLE_SOLID 36 | : KWTSelectableLabel.STYLE_DASHED); 37 | labels[i].addActionListener(new ActionListener() { 38 | public void actionPerformed(ActionEvent arg0) { 39 | description.setText("Label #" + selectedLabel + " was selected."); 40 | rootContainer.repaint(); 41 | } 42 | }); 43 | rootContainer.add(labels[i]); 44 | } 45 | labels[0].requestFocus(); 46 | rootContainer.add(description); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /samples/KWTProgressBar/src/org/kwt/samples/kwtprogressbar/Main.java: -------------------------------------------------------------------------------- 1 | package org.kwt.samples.kwtprogressbar; 2 | 3 | import java.awt.Container; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | 7 | import org.kwt.ui.KWTProgressBar; 8 | 9 | import com.amazon.kindle.kindlet.AbstractKindlet; 10 | import com.amazon.kindle.kindlet.KindletContext; 11 | import com.amazon.kindle.kindlet.ui.KButton; 12 | import com.amazon.kindle.kindlet.ui.KPanel; 13 | 14 | public class Main extends AbstractKindlet { 15 | 16 | public void create(final KindletContext context) { 17 | final Container rootContainer = context.getRootContainer(); 18 | final KPanel panel = new KPanel(); 19 | rootContainer.add(panel); 20 | 21 | final KWTProgressBar progressBar1 = new KWTProgressBar(); 22 | progressBar1.setWidth(800); 23 | progressBar1.setLabelStyle(KWTProgressBar.STYLE_NONE); 24 | 25 | final KWTProgressBar progressBar2 = new KWTProgressBar(); 26 | progressBar2.setWidth(800); 27 | progressBar2.setLabelStyle(KWTProgressBar.STYLE_PERCENTAGE); 28 | 29 | final KWTProgressBar progressBar3 = new KWTProgressBar(); 30 | progressBar3.setWidth(800); 31 | progressBar3.setLabelStyle(KWTProgressBar.STYLE_TOTAL); 32 | 33 | KButton increaseTicks = new KButton("Increase ticks"); 34 | KButton decreaseTicks = new KButton("Decrease ticks"); 35 | increaseTicks.addActionListener(new ActionListener() { 36 | public void actionPerformed(ActionEvent e) { 37 | progressBar1.setCurrentTick(progressBar1.getCurrentTick() + 1); 38 | progressBar2.setCurrentTick(progressBar2.getCurrentTick() + 1); 39 | progressBar3.setCurrentTick(progressBar3.getCurrentTick() + 1); 40 | panel.repaint(); 41 | } 42 | }); 43 | decreaseTicks.addActionListener(new ActionListener() { 44 | public void actionPerformed(ActionEvent e) { 45 | progressBar1.setCurrentTick(progressBar1.getCurrentTick() - 1); 46 | progressBar2.setCurrentTick(progressBar2.getCurrentTick() - 1); 47 | progressBar3.setCurrentTick(progressBar3.getCurrentTick() - 1); 48 | panel.repaint(); 49 | } 50 | }); 51 | increaseTicks.requestFocus(); 52 | 53 | panel.add(progressBar1); 54 | panel.add(progressBar2); 55 | panel.add(progressBar3); 56 | panel.add(increaseTicks); 57 | panel.add(decreaseTicks); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kindle Widget Toolkit 2 | ===================== 3 | 4 | The KWT (**Kindle Widget Toolkit**) is a set of KComponents to be used with the Kindle Development Kit to create compelling user interfaces for Active Content. 5 | 6 | The components in KWT attempt to match the style of the KDK APIs and the underlying Java AWT, and should in most cases be a drop-in replacement for the default components in the KDK. 7 | 8 | Features 9 | -------- 10 | 11 | At the moment, KWT supports the following components. 12 | 13 | **KWTCheckbox** - A simple toggle-able checkbox, or a selectable radio button, depending on whether it belongs to a **KWTCheckboxGroup**. 14 | 15 | **KWTDiagram/KWTMutableDiagram** - A diagram, in both mutable and non-mutable versions, which can be added to a container and have text flow around it. 16 | 17 | **KWTProgressBar** - A graphical representation of the progress towards completion of a particular task. 18 | 19 | **KWTScrollPanel** - A KPanel that supports vertical scrolling, instead of the Kindle's usual Paged metaphor. 20 | 21 | **KWTSelectableLabel** - A KLabel that can receive ActionEvents as if it were a KButton. 22 | 23 | Further components are in active development. 24 | 25 | Using KWT 26 | --------- 27 | 28 | Unfortunately, the KDK does not enable app bundles to contain third-party JARs, so using KWT is not as simple as adding it to the classpath. There are two main approaches you can use. 29 | 30 | ### Copying the source 31 | 32 | You can simply copy the widgets you want to use into your own package and compile it together with your application. This is the easiest way to get everything working, but you will have to manually apply updates as KWT adds features and improvements. Alternatively, you can include the KWT package as a git submodule in your repository. 33 | 34 | ### Use the JAR 35 | Although the KDK does not enable its app bundles to contain third-party JARs, you can add a target to your **build.xml** that copies the required Class files into your own JAR. Updating KWT then becomes simply a matter of dropping in the latest release. You can see an example of such a target in the sample project distributed together with KWT. 36 | 37 | Documentation 38 | ------------- 39 | 40 | You can view a full JavaDoc API Reference for KWT [here](http://s3.amazonaws.com/kwt-dev/javadoc/index.html), or you can generate one yourself using the **generate-public-javadoc** build target. 41 | 42 | License 43 | ------- 44 | 45 | KWT is distributed under a permissive Apache 2.0 License (see our LICENSE file). As such, it can safely be used in both proprietary and free Kindle applications. 46 | 47 | Contributing 48 | ------------ 49 | 50 | KWT is still in the early stages of development, and there are many great components still to be added. If you have some bug fixes or new components you would like included in KWT, please feel free to send a pull request. If you have bug reports, ideas for new features, or general feedback, please use GitHub's Issue tracker to let us know. 51 | -------------------------------------------------------------------------------- /src/org/kwt/ui/KWTScrollPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Adrian Petrescu. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located in the LICENSE file included with this 7 | * distribution. 8 | * 9 | * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | * CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the 12 | * License. 13 | */ 14 | 15 | package org.kwt.ui; 16 | 17 | import java.awt.Dimension; 18 | import java.awt.Graphics; 19 | import java.awt.Rectangle; 20 | import java.awt.Shape; 21 | 22 | import com.amazon.kindle.kindlet.ui.KPanel; 23 | 24 | public class KWTScrollPanel extends KPanel { 25 | private static final long serialVersionUID = 323032064088045342L; 26 | 27 | private static final int LEFT_GAP = 10; 28 | private static final int SCROLLBAR_WIDTH = 20; 29 | private static final int SCROLLBAR_LEFT_GAP = 10; 30 | private static final int SCROLLBAR_RIGHT_GAP = 10; 31 | private static final int RIGHT_GAP = SCROLLBAR_LEFT_GAP + SCROLLBAR_WIDTH + SCROLLBAR_RIGHT_GAP; 32 | private static final int TOP_GAP = 10; 33 | private static final int BOTTOM_GAP = 10; 34 | 35 | /** 36 | * The height of the viewport displaying the underlying KPanel. Since KWTScrollPanels only 37 | * support vertical scrolling, only a viewportHeight is needed, and not a viewportWidth. 38 | */ 39 | private int viewportHeight; 40 | 41 | /** 42 | * How far down the component the scroll bar is. 0 represents the very top, 43 | * 1 represents the very bottom. 44 | */ 45 | private double scrollLevel; 46 | 47 | public KWTScrollPanel(int height) { 48 | scrollLevel = 0.0; 49 | viewportHeight = height; 50 | } 51 | 52 | public void setSize(Dimension d) { 53 | setSize(d.width, d.height); 54 | } 55 | 56 | public void setSize(int width, int height) { 57 | viewportHeight = height; 58 | 59 | super.setSize(width - (LEFT_GAP + RIGHT_GAP), super.getSize().height); 60 | } 61 | 62 | public Dimension getSize() { 63 | Dimension d = super.getSize(); 64 | d.width += (LEFT_GAP + RIGHT_GAP); 65 | d.height = viewportHeight + (TOP_GAP + BOTTOM_GAP); 66 | return d; 67 | } 68 | 69 | public Dimension getSize(Dimension rv) { 70 | super.getSize(rv); 71 | rv.width += (LEFT_GAP + RIGHT_GAP); 72 | rv.height = viewportHeight + (TOP_GAP + BOTTOM_GAP); 73 | return rv; 74 | } 75 | 76 | public void paint(Graphics g) { 77 | Shape clip = g.getClip(); 78 | 79 | // Draw the outer border 80 | g.drawRect(0, 0, super.getMinimumSize().width + (LEFT_GAP + RIGHT_GAP), viewportHeight + (TOP_GAP + BOTTOM_GAP)); 81 | 82 | // Clip g to viewport 83 | g.clipRect(LEFT_GAP, TOP_GAP, super.getMinimumSize().width, viewportHeight); 84 | // Translate g to the current vertical scroll level 85 | g.translate(LEFT_GAP, -180); 86 | // Paint the underlying KPanel onto the viewport 87 | super.paint(g); 88 | 89 | // Restore the original clip 90 | Rectangle r = g.getClipBounds(); 91 | g.setClip(clip); 92 | 93 | // Draw the viewport border. 94 | g.drawRect(r.x, r.y, r.width, r.height); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/org/kwt/ui/KWTCheckbox.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Dan Fabulich. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located in the LICENSE file included with this 7 | * distribution. 8 | * 9 | * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | * CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the 12 | * License. 13 | */ 14 | 15 | package org.kwt.ui; 16 | 17 | import java.awt.Color; 18 | import java.awt.Dimension; 19 | import java.awt.Graphics; 20 | import java.awt.event.ActionEvent; 21 | import java.awt.event.ActionListener; 22 | 23 | import com.amazon.kindle.kindlet.ui.KButton; 24 | 25 | /** 26 | * A custom checkbox. If the KWTCheckbox belongs to an instance of a KWTCheckboxGroup, it will behave 27 | * and look like a radio button; otherwise, it will be a simple toggleable checkbox. 28 | * 29 | *

Example render: 30 | * 31 | * 32 | * @author Dan Fabulich 33 | * 34 | */ 35 | public class KWTCheckbox extends KButton implements ActionListener { 36 | private static final long serialVersionUID = 8105922331821759692L; 37 | 38 | private boolean selected = false; 39 | final KWTCheckboxGroup group; 40 | 41 | // TODO: Make these values styleable. 42 | private static final int padding = 1, border = 2; 43 | 44 | private boolean isRadioButton() { 45 | return group != null; 46 | } 47 | 48 | /** 49 | * Returns whether the box is checked. 50 | * 51 | * @return whether the box is checked 52 | */ 53 | public boolean isSelected() { 54 | return selected; 55 | } 56 | 57 | /** Toggle the state of this box. If it belongs to a KWTCheckboxGroup, it will deselect the currently 58 | * selected box in that group. 59 | * 60 | * @param selected whether to select or deselect this box. 61 | */ 62 | public void setSelected(boolean selected) { 63 | if (this.selected == selected) return; 64 | if (selected && group != null) group.setSelected(this); 65 | this.selected = selected; 66 | repaint(); 67 | } 68 | 69 | /** 70 | * Constructs a new checkbox. It will be a toggleable checkbox not belonging to a group. 71 | */ 72 | public KWTCheckbox() { 73 | this(null); 74 | } 75 | 76 | /** Constructs a radio button belonging to a group. 77 | * 78 | * @param group the group this radio button will belong to 79 | */ 80 | public KWTCheckbox(KWTCheckboxGroup group) { 81 | this.group = group; 82 | addActionListener(this); 83 | } 84 | 85 | /** 86 | * {@inheritDoc } 87 | */ 88 | public Dimension getPreferredSize() { 89 | return getMinimumSize(); 90 | } 91 | 92 | /** 93 | * {@inheritDoc } 94 | */ 95 | public Dimension getMinimumSize() { 96 | int d = getFontMetrics(getFont()).getMaxAscent(); 97 | d += (padding + border) * 2; 98 | return new Dimension(d, d); 99 | } 100 | 101 | /** 102 | * {@inheritDoc } 103 | */ 104 | public void paint(Graphics g) { 105 | // Max Ascent ~ maximum height of a letter from the baseline 106 | // We'll use maxAscent to size our checkbox 107 | int maxAscent = g.getFontMetrics().getMaxAscent(); 108 | int diameter = maxAscent - border; 109 | 110 | int x = padding + border; 111 | int y = padding + border; 112 | 113 | g.setColor(Color.black); 114 | if (isRadioButton()) { 115 | if (selected) g.fillOval(x, y, diameter, diameter); 116 | g.drawOval(x, y, diameter, diameter); 117 | } else { 118 | // checkbox 119 | if (selected) { 120 | // draw X 121 | g.drawLine(x, y, x+diameter, y+diameter); 122 | g.drawLine(x, y+diameter, x+diameter, y); 123 | } 124 | g.drawRect(x, y, diameter, diameter); 125 | } 126 | 127 | if (!isFocusOwner()) { 128 | g.setColor(Color.white); 129 | } 130 | for (int i = 0; i < border; i++) { 131 | // Draw the border as a sequence of self-contained rectangles 132 | // The first border is the full size, the next border is 1px smaller, and so on 133 | int rectWidth = diameter + 2 * (padding + border - i); 134 | g.drawRect(i, i, rectWidth, rectWidth); 135 | } 136 | } 137 | 138 | /** 139 | * Called when the checkbox is toggled by the user. This method should never be called by 140 | * client code. 141 | * 142 | * @param event the event that toggled this action 143 | */ 144 | public void actionPerformed(ActionEvent event) { 145 | if (isRadioButton() && selected) return; // can't deselect last radio button 146 | setSelected(!selected); 147 | } 148 | 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/org/kwt/ui/KWTProgressBar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Adrian Petrescu. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located in the LICENSE file included with this 7 | * distribution. 8 | * 9 | * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | * CONDITIONS OF ANY KIND, either express or implied. See the License 11 | * for the specific language governing permissions and limitations under the 12 | * License. 13 | */ 14 | 15 | package org.kwt.ui; 16 | 17 | import java.awt.Color; 18 | import java.awt.Dimension; 19 | import java.awt.Graphics; 20 | 21 | import org.kwt.InvalidStyleException; 22 | 23 | import com.amazon.kindle.kindlet.ui.KComponent; 24 | 25 | /** 26 | * A progress bar. Represents the proportion of progress towards completing a particular task. Meant 27 | * to emulate the book progress bar at the bottom of the regular Kindle reader application. 28 | * 29 | *

Example render: 30 | * 31 | * 32 | * @author Adrian Petrescu 33 | * 34 | */ 35 | public class KWTProgressBar extends KComponent { 36 | private static final long serialVersionUID = 5781953629278873008L; 37 | 38 | private static final int VERTICAL_PADDING = 3; 39 | private static final int HORIZONTAL_PADDING = 10; 40 | private static final int CORNER_ROUNDING = 10; 41 | 42 | /** An empty style. */ 43 | public static final int STYLE_NONE = 0; 44 | /** A percentage description of progress */ 45 | public static final int STYLE_PERCENTAGE = 1; 46 | /** A fractional description of progress */ 47 | public static final int STYLE_TOTAL = 2; 48 | 49 | private int width; 50 | private int labelStyle = STYLE_PERCENTAGE; 51 | private int totalTicks; 52 | private int currentTick; 53 | 54 | /** 55 | * Constructs a new progress bar, with 100 total ticks. The current tick begins at 0. 56 | */ 57 | public KWTProgressBar() { 58 | this(100); 59 | } 60 | 61 | /** 62 | * Constructs a new progress bar. The current tick begins at 0. 63 | * 64 | * @param totalTicks The total number of ticks representing completion of the task. 65 | */ 66 | public KWTProgressBar(int totalTicks) { 67 | this.totalTicks = totalTicks; 68 | this.currentTick = 0; 69 | 70 | this.setFocusable(false); 71 | } 72 | 73 | /** 74 | * Returns the total number of ticks. 75 | * 76 | * @return the total number of ticks. 77 | */ 78 | public int getTotalTicks() { 79 | return totalTicks; 80 | } 81 | 82 | /** 83 | * Sets the total number of ticks. If the current tick is larger than totalTicks, 84 | * then it is set to totalTicks. 85 | * 86 | * @param totalTicks the total number of ticks representing completion of the task. 87 | */ 88 | public void setTotalTicks(int totalTicks) { 89 | this.totalTicks = totalTicks; 90 | 91 | // Make sure current tick is not beyond the maximum value. 92 | setCurrentTick(currentTick); 93 | } 94 | 95 | /** 96 | * Returns the current tick. 97 | * 98 | * @return the current tick. 99 | */ 100 | public int getCurrentTick() { 101 | return currentTick; 102 | } 103 | 104 | /** 105 | * Sets the current tick. If currentTick < 0 or 106 | * currentTick > totalTicks, then currentTick 107 | * will be set to 0 or totalTicks respectively. 108 | * 109 | * @param currentTick the current tick. 110 | */ 111 | public void setCurrentTick(int currentTick) { 112 | this.currentTick = Math.min(totalTicks, Math.max(0, currentTick)); 113 | } 114 | 115 | /** 116 | * Increments the current tick by 1. If the current tick is already at 117 | * the maximum value, this does nothing. 118 | */ 119 | public void incrementTick() { 120 | if (currentTick < totalTicks) currentTick += 1; 121 | } 122 | 123 | /** 124 | * Decrements the current tick by 1. If the current tick is already at 125 | * 0, this does nothing. 126 | */ 127 | public void decrementTick() { 128 | if (currentTick > 0) currentTick -= 1; 129 | } 130 | 131 | /** 132 | * Returns the style of label to be used. Valid options are: 133 | * 138 | * 139 | * @return the style of label to be used. 140 | */ 141 | public int getLabelStyle() { 142 | return labelStyle; 143 | } 144 | 145 | /** 146 | * Sets the style of label to be used. Valid styles are: 147 | * 152 | * 153 | * @param style the style of label to be used. 154 | * @throws InvalidStyleException if style is not one of the allowed values. 155 | */ 156 | public void setLabelStyle(int style) throws InvalidStyleException { 157 | if (style < STYLE_NONE || style > STYLE_TOTAL) 158 | throw new InvalidStyleException(this.getClass().getName() + 159 | " does not support the given style for highlighting.", style); 160 | labelStyle = style; 161 | } 162 | 163 | /** 164 | * {@inheritDoc } 165 | */ 166 | public Dimension getPreferredSize() { 167 | Dimension d = getMinimumSize(); 168 | d.width = Math.max(width, d.width); 169 | return d; 170 | } 171 | 172 | /** 173 | * {@inheritDoc } 174 | */ 175 | public Dimension getMinimumSize() { 176 | return new Dimension(getMinimumWidth() + 2 * HORIZONTAL_PADDING, 177 | getMinimumHeight() + 2 * VERTICAL_PADDING); 178 | } 179 | 180 | /** 181 | * Sets the width (in pixels) of this component. 182 | * @param width the width (in pixels) of this component. 183 | */ 184 | public void setWidth(int width) { 185 | this.width = width; 186 | } 187 | 188 | /** 189 | * {@inheritDoc } 190 | */ 191 | public void paint(Graphics g) { 192 | double progress = (double) currentTick / totalTicks; 193 | g.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, CORNER_ROUNDING, CORNER_ROUNDING); 194 | g.setColor(Color.BLACK); 195 | g.fillRoundRect(0, 0, (int) (progress * getWidth()) - 1, getHeight() - 1, CORNER_ROUNDING, CORNER_ROUNDING); 196 | 197 | if (labelStyle == STYLE_PERCENTAGE || labelStyle == STYLE_TOTAL) { 198 | String progressString = ""; 199 | switch (labelStyle) { 200 | case STYLE_PERCENTAGE: 201 | progressString = ((int) (progress * 100)) + "%"; 202 | break; 203 | case STYLE_TOTAL: 204 | progressString = currentTick + "/" + totalTicks; 205 | } 206 | g.setXORMode(Color.WHITE); 207 | g.drawString(progressString, 208 | ((getWidth() - 1) / 2) - (getFontMetrics(getFont()).stringWidth(progressString) / 2), 209 | getFontMetrics(getFont()).getHeight()); 210 | } 211 | } 212 | 213 | private int getMinimumHeight() { 214 | switch (labelStyle) { 215 | case STYLE_NONE: return 0; 216 | case STYLE_PERCENTAGE: 217 | case STYLE_TOTAL: 218 | default: 219 | return getFontMetrics(getFont()).getHeight(); 220 | } 221 | } 222 | 223 | private int getMinimumWidth() { 224 | switch (labelStyle) { 225 | case STYLE_NONE: return 0; 226 | case STYLE_PERCENTAGE: 227 | return getFontMetrics(getFont()).stringWidth("100%"); 228 | case STYLE_TOTAL: 229 | String longest = String.valueOf(totalTicks); 230 | longest = longest + "/" + longest; 231 | return getFontMetrics(getFont()).stringWidth(longest); 232 | } 233 | 234 | return 0; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | 4 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 5 | 6 | 1. Definitions. 7 | 8 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 9 | 10 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 11 | 12 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 13 | 14 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 15 | 16 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 17 | 18 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 19 | 20 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 21 | 22 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 23 | 24 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 25 | 26 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 27 | 28 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 29 | 30 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 31 | 32 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 33 | 34 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 35 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 36 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 37 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 38 | 39 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 40 | 41 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 42 | 43 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 44 | 45 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 46 | 47 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 48 | 49 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 50 | 51 | END OF TERMS AND CONDITIONS 52 | 53 | Note: Other license terms may apply to certain, identified software files contained within or distributed with the accompanying software if such terms are included in the directory containing the accompanying software. Such other license terms will then apply in lieu of the terms of the software license above. -------------------------------------------------------------------------------- /src/org/kwt/ui/KWTSelectableLabel.java: -------------------------------------------------------------------------------- 1 | package org.kwt.ui; 2 | 3 | import java.awt.AWTEvent; 4 | import java.awt.Color; 5 | import java.awt.Dimension; 6 | import java.awt.Graphics; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | import java.awt.event.KeyEvent; 10 | 11 | import java.util.Iterator; 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | 15 | import org.kwt.InvalidStyleException; 16 | 17 | import com.amazon.kindle.kindlet.ui.KLabel; 18 | 19 | /** 20 | * A selectable KLabel that can be focusable and receive ActionEvents. Intended to be sometimes 21 | * used in lieu of a KButton. 22 | * 23 | *

Example render: 24 | * 25 | * 26 | * @author Adrian Petrescu 27 | * 28 | */ 29 | public class KWTSelectableLabel extends KLabel { 30 | private static final long serialVersionUID = 8118660222383683366L; 31 | 32 | private static final int BUTTON_DOWN_EVENT = 401; 33 | private static final int SELECT_BUTTON_KEY = 61451; 34 | private static final int DEFAULT_UNDERLINE_WIDTH = 5; 35 | private static final int DEFAULT_UNDERLINE_GAP = 2; 36 | 37 | /** An empty style */ 38 | public static final int STYLE_NONE = 0; 39 | /** A solid style in the foreground color */ 40 | public static final int STYLE_SOLID = 1; 41 | /** A dashed line in the foreground color */ 42 | public static final int STYLE_DASHED = 2; 43 | 44 | private List actionListeners; 45 | private int highlightStyle = STYLE_NONE; 46 | private int underlineStyle = STYLE_SOLID; 47 | private int underlineWidth = DEFAULT_UNDERLINE_WIDTH; 48 | private int underlineGap = DEFAULT_UNDERLINE_GAP; 49 | 50 | /* This is a dirty hack to get around the fact that KLabels do not respond to setPosition(). 51 | * Instead, when painting the superclass, we have to trick it into thinking its size is smaller 52 | * than it actually is. However, we don't want this faulty size to be read at any other time. 53 | * Hence this flag for when to spoof the size. 54 | */ 55 | private boolean spoofSize = false; 56 | 57 | /** 58 | * Constructs a new selectable label with no text. 59 | */ 60 | public KWTSelectableLabel() { 61 | this(null); 62 | } 63 | 64 | /** 65 | * Constructs a new selectable label with the given text. The text will be clipped if it extends 66 | * past the label's maximum size. 67 | * 68 | * @param text the label's text 69 | */ 70 | public KWTSelectableLabel(String text) { 71 | super(text); 72 | enableEvents(AWTEvent.KEY_EVENT_MASK); 73 | setFocusable(true); 74 | actionListeners = new LinkedList(); 75 | } 76 | 77 | /** 78 | * Returns the style of highlighting to be used when the label has the focus. Valid results are: 79 | * 83 | * 84 | * @return the style used for the highlighting 85 | */ 86 | public int getHighlightStyle() { 87 | return highlightStyle; 88 | } 89 | 90 | /** 91 | * Sets the style of highlighting to be used when the label has the focus. Valid styles are: 92 | * 96 | * 97 | * @param style the style to use for the highlighting 98 | * @throws InvalidStyleException if style is not one of the allowed values. 99 | */ 100 | public void setHighlightStyle(int style) throws InvalidStyleException { 101 | if (style < STYLE_NONE || style > STYLE_SOLID) 102 | throw new InvalidStyleException(this.getClass().getName() + 103 | " does not support the given style for highlighting.", style); 104 | highlightStyle = style; 105 | } 106 | 107 | /** 108 | * Returns the style of underlining to be used when the label has the focus. Valid results are: 109 | * 114 | * 115 | * @return the style used for the underlining 116 | */ 117 | public int getUnderlineStyle() { 118 | return underlineStyle; 119 | } 120 | 121 | /** 122 | * Sets the style of underlining to be used when the label has the focus. Valid styles are: 123 | * 128 | * 129 | * @param style the style to use for the underlining 130 | */ 131 | public void setUnderlineStyle(int style) throws InvalidStyleException { 132 | if (style < STYLE_NONE || style > STYLE_DASHED) 133 | throw new InvalidStyleException(this.getClass().getName() + 134 | " does not support the given style for underlining.", style); 135 | underlineStyle = style; 136 | } 137 | 138 | /** 139 | * Sets the width of the underline, assuming the style provides one. Note that this may return 140 | * a non-zero value even if the underlining style is set to STYLE_NONE 141 | * 142 | * @return the width of the underline 143 | */ 144 | public int getUnderlineWidth() { 145 | return underlineWidth; 146 | } 147 | 148 | /** 149 | * Sets the width of the underline, assuming the style provides one. 150 | * 151 | * @param width the width of the underline 152 | */ 153 | public void setUnderlineWidth(int width) { 154 | underlineWidth = width; 155 | } 156 | 157 | /** 158 | * Sets the gap between the bottom of the label's text and the top of the underline, assuming the style 159 | * provides one. Note that the gap takes into account the maximum descent of the text's font. 160 | * 161 | * @param gap the gap between the label and the underline 162 | */ 163 | public void setUnderlineGap(int gap) { 164 | underlineGap = gap; 165 | } 166 | 167 | /** 168 | * Returns the gap between the bottom of the label's text and the top of the underline, assuming the style 169 | * provides one. Note that the gap takes into account hte maximum descent of the text's font. 170 | * 171 | * @return the gap between the label and the underline 172 | */ 173 | public int getUnderlineGap() { 174 | return underlineGap; 175 | } 176 | 177 | /** 178 | * {@inheritDoc } 179 | */ 180 | public Dimension getPreferredSize() { 181 | return getMinimumSize(); 182 | } 183 | 184 | /** 185 | * {@inheritDoc } 186 | */ 187 | public Dimension getMinimumSize() { 188 | // Use KLabel's preferred size for its minimum size, to work around a known bug in the KDK. 189 | Dimension d = super.getPreferredSize(); 190 | if (spoofSize) return new Dimension(d.width, d.height - underlineGap - underlineWidth); 191 | return new Dimension(d.width, d.height + underlineGap + underlineWidth + 1); 192 | } 193 | 194 | /** 195 | * {@inheritDoc } 196 | */ 197 | public Dimension getSize() { 198 | Dimension d = super.getSize(); 199 | if (spoofSize) return new Dimension(d.width, d.height - underlineGap - underlineWidth); 200 | return new Dimension(d.width, d.height + underlineGap + underlineWidth + 1); 201 | } 202 | 203 | /** 204 | * {@inheritDoc } 205 | */ 206 | public void paint(Graphics g) { 207 | spoofSize = true; 208 | super.paint(g); 209 | spoofSize = false; 210 | if (this.isFocusOwner()) { 211 | int y = super.getSize().height - (underlineGap + underlineWidth); 212 | g.setColor(Color.BLACK); 213 | switch (underlineStyle) { 214 | case STYLE_SOLID: 215 | g.fillRect(0, y + underlineGap, this.getWidth() - 1, underlineWidth - 1); 216 | break; 217 | case STYLE_DASHED: 218 | for (int i = 0; i <= (this.getWidth() - 1) / (underlineWidth - 1); i += 2) { 219 | g.fillRect(i * (underlineWidth - 1), y + underlineGap, underlineWidth - 1, underlineWidth - 1); 220 | } 221 | } 222 | } 223 | } 224 | 225 | /** 226 | * Registers a listener who wishes to be notified whenever this label is clicked by the user. 227 | * 228 | * @param listener a listener who wishes to be notified 229 | */ 230 | public void addActionListener(ActionListener listener) { 231 | this.actionListeners.add(listener); 232 | } 233 | 234 | /** 235 | * {@inheritDoc } 236 | */ 237 | protected void processEvent(AWTEvent e) { 238 | switch(e.getID()) { 239 | case BUTTON_DOWN_EVENT: 240 | if (((KeyEvent) e).getKeyCode() != SELECT_BUTTON_KEY) break; 241 | Iterator it = actionListeners.iterator(); 242 | while (it.hasNext()) { 243 | ActionListener listener = (ActionListener) it.next(); 244 | listener.actionPerformed(new ActionEvent(this, BUTTON_DOWN_EVENT, null)); 245 | } 246 | break; 247 | default: 248 | break; 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /samples/KWTCheckbox/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /samples/KWTProgressBar/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /samples/KWTScrollPanel/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /samples/KWTSelectableLabel/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | --------------------------------------------------------------------------------