diff options
author | Mark Wielaard <mark@klomp.org> | 2004-09-04 17:14:00 +0000 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2004-09-04 17:14:00 +0000 |
commit | 23640e02408b9d5ef14dfb99d3a75b4408b7f9be (patch) | |
tree | 59ee419732dcb4acfbfa972b452d7e8e1f2958ae /javax/swing | |
parent | 26ab6649730d6e8249ef03f8b02c8a146f4a62fa (diff) | |
download | classpath-23640e02408b9d5ef14dfb99d3a75b4408b7f9be.tar.gz |
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/JTextArea.java
(lineWrap): Renamed from "wrapping".
(wrapStyleWord): NEw field.
(getLineWrap): Re-edited javadoc comment.
(setLineWrap): Likewise.
(getWrapStyleWord): New method.
(setWrapStyleWord): Likewise.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicTextAreaUI.java
(create): New method.
* javax/swing/text/DefaultHighlighter.java
(DefaultHighlightPainter.debugRect): Removed.
* javax/swing/text/StyleContext.java
(DEFAULT_STYLE): New field.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicLookAndFeel.java
(initComponentDefaults): Add keybindings for selection.backward and
selection-forward for text components.
* javax/swing/plaf/basic/BasicTextUI.java
(paintSafely): Paint highlight only when something is actually
selected.
* javax/swing/text/DefaultCaret.java
(handleHighlight): New method.
(setSelectionVisible): Don't do anything when nothing changes.
Handle highlight.
(moveDot): Reimplemented. Handle highlight.
(setDot): Set mark too. Handle highlight.
(getSelectionPainter): New method.
* javax/swing/text/DefaultEditorKit.java
(defaultActions): Added new actions for text selection.
* javax/swing/text/DefaultHighlighter.java
(DefaultHighlightPainter): New inner class.
(DefaultPainter): New field.
(paint): Implemented.
* javax/swing/text/PlainView.java
(paint): Don't draw background here again.
* javax/swing/text/Utilities.java
(getTabbedTextWidth): Use width of ' ' instead of 'm' for tabsize.
(drawTabbedText): Likewise.
2004-09-04 Graydon Hoare <graydon@redhat.com>
* java/awt/Component.java
(isFocusTraversable): Predicate on isLightweight()
(setFocusable): Set isFocusTraversableOverridden.
(requestFocus): Predicate peer dispatch on !isLightweight()
(requestFocusInWindow): Likewise.
(dispatchEventImpl): Coordinate with KeyboardFocusManager.
* java/awt/Container.java
(dispatchEventImpl): Predicate on event mask.
(LightweightDispatcher): Remove focus machinery.
* java/awt/DefaultFocusTraversalPolicy.java
(accept): Expand predicate to include isFocusable().
* java/awt/DefaultKeyboardFocusManager.java:
Globally change c.dispatchEvent(e) to redispatchEvent(c,e)
* java/awt/KeyboardFocusManager.java
(redispatchEvent): Synchronize on event to prevent feedback.
* javax/swing/AbstractButton.java
(ButtonFocusListener): Remove class.
(init): Set focusPainted, focusable.
* javax/swing/ActionMap.java (get): Check parent for null.
* javax/swing/InputMap.java (get): Likewise.
* javax/swing/JComponent.java
(inputMap_whenFocused): New InputMap.
(inputMap_whenAncestorOfFocused): Likewise.
(inputMap_whenInFocusedWindow): Likewise.
(getActionForKeyStroke): Rewrite.
(getConditionForKeystroke): Likewise.
(ActionListenerProxy): New private class.
(setInputMap): Implement.
(getInputMap): Likewise.
(setActionMap): Likewise.
(getActionMap): Likewise.
(processComponentKeyEvent): New empty method.
(processKeyEvent): Implement.
(processKeyBinding): Likewise.
(resetKeyboardActions): Rewrite.
* javax/swing/KeyStroke.java: Rewrite.
* javax/swing/SwingUtilities.java
(notifyAction): Implement.
(replaceUIActionMap): Likewise.
(replaceUIInputMap): Likewise.
* javax/swing/plaf/basic/BasicButtonListener.java
(focusGained): Implement.
(focusLost): Repaint if focusPainted().
(installKeyboardActions): Install pressed / released actions.
(uninstallKeyboardActions): Implement.
* javax/swing/plaf/basic/BasicButtonUI.java
(focusColor): New field.
(installDefaults): Load focus color, install input map.
(installKeyboardActions): Implement.
(uninstallKeyboardActions): Likewise.
(paintFocus): Rewrite.
* javax/swing/plaf/basic/BasicLookAndFeel.java
(Button.focus): New default, midPurple.
* javax/swing/plaf/basic/BasicTextUI.java
(kit): Make static.
(installUI): Get doc from kit, load defaults.
(getKeymapName): Implement.
(createKeymap): Likewise.
(installKeyboardActions): Likewise.
(getInputMap): Likewise.
(getActionMap): Likewise.
(createActionMap): Likewise.
* javax/swing/text/AbstractDocument.java
(getStartPosition): Implement.
(getEndPosition): Likewise.
* javax/swing/text/DefaultEditorKit.java
(CopyAction): New class.
(CutAction): Likewise.
(DefaultKeyTypedAction): Likewise.
(InsertBreakAction): Likewise.
(InsertContentAction): Likewise.
(InsertTabAction): Likewise.
(PasteAction): Likewise.
(defaultActions): New static table.
(createCaret): Implement.
(getActions): Likewise.
* javax/swing/text/JTextComponent.java
(KeymapWrapper): New private class.
(KeymapActionMap): Likewise.
(DefaultKeymap): New class.
(keymaps): New static table.
(keymap): New field.
(getKeymap): Implement.
(removeKeymap): Likewise.
(addKeymap): Likewise.
(setKeymap): Likewise.
(loadKeymap): Likewise.
(getActions): Likewise.
(margin): New field.
(JTextComponent): Build and install default keymap.
* javax/swing/text/TextAction.java
(textAction): Call super properly.
(getTextComponent): Implement.
* javax/swing/text/Utilities.java
(drawTabbedText): Adjust position by ascent.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicTextUI.java
(RottView.modelToView): New method.
(UpdateHandler): Renamed from EventHandler.
(updateHandler): Renamed from eventHandler.
(modelToView): Implemented.
* javax/swing/text/AbstractDocument.java
(BranchElement.getElement): Return null for non-existing indeces.
(BranchElement.getElementIndex): Return 0 in some corner cases.
* javax/swing/text/FieldView.java
(modelToView): New method.
* javax/swing/text/PlainView.java
(modelToView): Made public.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/text/AbstractDocument.java
(createBranchElement): Use new constructor of BranchElement.
(createLeafElement): Renamed arguments.
(getRootElements): Implemented.
(BranchElement.start): Removed.
(BranchElement.end): Likewise.
(BranchElement.BranchElement): Fixed arguments.
(BranchElement.getEndOffset): Reimplemented.
(BranchElement.getStartOffset): Likewis.
* javax/swing/text/DefaultCaret.java
(paint): Draw simple vertical line as caret instead of a rectangle.
* javax/swing/text/JTextComponent.java
(setText): Use doc directly.
* javax/swing/text/PlainView.java
(nextTabStop): Implemented.
* javax/swing/text/Utilities.java
(drawTabbedText): nextTabStop() returns an absolute x position.
(getTabbedTextWidth): Likewise.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/text/PlainView.java
(selectedColor): New field.
(unselectedColor): Likewise.
(font): Likewise.
(updateMetrics): New method.
(lineToRect): Likewise.
(modelToView): Likewise.
(drawSelectedText): Use color from JTextComponent ad draw with
Utilities class.
(drawUnselectedText): Likewise.
(paint): Initialize helper fields.
* javax/swing/text/View.java
(getChildAllocation): New method.
(getViewIndex): Likewise.
(getToolTipText): Likewise.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/text/Utilities.java
(drawTabbedText): Reimplemented.
(getTabbedTextWidth): Likewise.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicTextUI.java
(installDefaults): Install caret and highlighter.
(modelToView): Use Bias.Forward when calling sibling.
* javax/swing/text/AbstractDocument.java
(ElementEdit): Implements DocumentEvent.ElementChange.
(ElementEdit.ElementEdit): New method.
(ElementEdit.getChildrenAdded): Likewise.
(ElementEdit.getChildrenRemoved): Likewise.
(ElementEdit.getElement): Likewise.
(ElementEdit.getIndex): Likewise.
* javax/swing/text/DefaultCaret.java
(color): Removed.
(textComponent): Renamed from parent, made private.
(selectionVisible): Renamed from vis_sel, made private.
(blinkRate): Renamed from blink, made private.
(magicCaretPosition): Renamed from magic, made private.
(visible): Renamed from vis, made private.
(dot): Made private.
(mark): Likewise.
(deinstall): Remove as MouseMotionListener.
(install): Initialize textComponent first. Add as
MouseMotionListener.
(paint): Reimplemented.
* javax/swing/text/JTextComponent.java
(setCaret): Deinstall old caret, install new one and fire property
change after setting property.
(setHighlighter): Deinstall old highlighter, install new one and fire
property change after setting property.
(setCaretColor): Fire property change after setting property.
(setDisabledTextColor): Likewise.
(setSelectedTextColor): Likewise.
(setSelectionColor): Likewise.
(modelToView): New method.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/text/AbstractDocument.java
(getText): Simplified.
* javax/swing/text/Segment.java
(current): New field.
(current): Reimplemented.
(first): Likewise.
(getIndex): Likewise.
(last): Likewise.
(next): Likewise.
(previous): Likewise.
(setIndex): Likewise.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicButtonUI.java
(defaultTextIconGap): Made protected.
(defaultTextShiftOffset): Likewise.
(textShiftOffset): New field.
(clearTextShiftOffset): New method.
(getTextShiftOffset): Likewise.
(setTextShiftOffset): Likewise.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicTextUI.java
(installUI): Add eventHandler as property listener.
(uninstallUI): remove eventHandler as propert listener.
(installDefaults): Added comment.
(installListeners): Likewise.
(installKeyboardActions): Likewise.
(uninstallDefaults): Likewise.
(uninstallListeners): Likewise.
(uninstallKeyboardActions): Likewise.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/text/AbstractDocument.java:
Fixed some typos in comments.
(insertString): Reimplemented.
(remove): Likewise.
(replace): New method.
(children): Dont use fully qualified class name.
(DefaultDocumentEvent.offset): Renamed from off.
(DefaultDocumentEvent.length): Renamed from len.
(DefaultDocumentEvent.type): New field.
(DefaultDocumentEvent.DefaultDocumentEvent): New constructor.
(DefaultDocumentEvent.getType): Implemented.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicTextUI.java
(RootView.textComponent): Removed.
(RootView.RootView): Don't initialize textComponent.
(RootView.getViewFactory): New method.
(EventHandler): New inner class.
(rootView): Initialize at instance creation.
(eventHandler): New field.
(installUI): Don't create view hierarchy directly,
call modelChanged() instead.
(modelChanged): New method.
* javax/swing/text/JTextComponent.java
(setDocument): Fire property change event.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicTextUI.java
(RootView.paint): Removed debug output.
(paintSafely): Draw highlighter before text.
(paintBackground): Use background color of text component.
* javax/swing/plaf/basic/BasicToggleButtonUI.java:
Reformatted.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicToolBarUI.java
(BasicToolBarUI): Fixed arguments for constructor.
(createUI): Fixed creation of object.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/text/TabSet.java
(TabSet): Implements java.io.Serializable.
* javax/swing/text/TabStop.java
(TabStop): Implements java.io.Serializable.
(TabStop): Made public.
2004-09-04 Graydon Hoare <graydon@redhat.com>
* javax/swing/text/SimpleAttributeSet.java: New file.
* javax/swing/text/StyleConstants.java: New file.
* javax/swing/text/StyleContext.java: New file.
* javax/swing/text/TabSet.java: New file.
* javax/swing/text/TabStop.java: New file.
* javax/swing/text/AbstactDocument.java:
(AbstractElement): Implement attribute support.
* javax/swing/text/AttributeSet.java
(NameAttribute): New static field.
(ResolveAttribute): New static field.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/JComponent.java
(setUI): Fire PropertyChange.
* javax/swing/JLabel.java
(text): Renamed from labelText.
(horizontalAlignment): New default vlaue.
(icon): Renamed from activeIcon.
(displayedMnemonic): Renamed from mnemonicKey, added default value.
(displayedMnemonicIndex): Renamed from underlineChar.
(setDisplayedMnemonic): Reimplemented.
* javax/swing/JRadioButton.java
(JRadioButton): New constructors.
* javax/swing/JTextField.java
(JTextField): Throw exception if colums < 0, initialitialz
this.columns directly and initialize document with text
conditionally.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/plaf/basic/BasicFormattedTextFieldUI.java,
javax/swing/plaf/basic/BasicPasswordFieldUI.java,
javax/swing/plaf/basic/BasicTextAreaUI.java: New files.
* javax/swing/text/FieldView.java
(paint): Just call super method for now.
2004-09-04 Michael Koch <konqueror@gmx.de>
* javax/swing/TransferHandler.java,
javax/swing/plaf/basic/ComboPopup.java: New files
2004-09-04 Roman Kennke <roman@ontographics.com>
* javax/swing/text/Utilities.java: New file.
Diffstat (limited to 'javax/swing')
38 files changed, 4214 insertions, 552 deletions
diff --git a/javax/swing/AbstractButton.java b/javax/swing/AbstractButton.java index f6aec1623..d7328c2a2 100644 --- a/javax/swing/AbstractButton.java +++ b/javax/swing/AbstractButton.java @@ -444,34 +444,6 @@ public abstract class AbstractButton extends JComponent } /** - * Helper class used to subscribe to FocusEvents received by the button. - */ - private class ButtonFocusListener implements FocusListener - { - /** - * Possibly repaint the model in response to loss of focus. - * - * @param event The loss-of-focus event - */ - public void focusLost(FocusEvent event) - { - if (AbstractButton.this.isFocusPainted()) - AbstractButton.this.repaint(); - } - - /** - * Possibly repaint the button in response to acquisition of focus. - * - * @param event The gained-focus event - */ - public void focusGained(FocusEvent event) - { - if (AbstractButton.this.isFocusPainted()) - AbstractButton.this.repaint(); - } - } - - /** * Creates a new AbstractButton object. */ public AbstractButton() @@ -552,12 +524,12 @@ public abstract class AbstractButton extends JComponent borderPainted = true; contentAreaFilled = true; - iconTextGap = 4; + focusPainted = true; + setFocusable(true); setAlignmentX(LEFT_ALIGNMENT); setAlignmentY(CENTER_ALIGNMENT); - addFocusListener(new ButtonFocusListener()); setDisplayedMnemonicIndex(-1); } diff --git a/javax/swing/ActionMap.java b/javax/swing/ActionMap.java index 1b1f8401c..ddfacae54 100644 --- a/javax/swing/ActionMap.java +++ b/javax/swing/ActionMap.java @@ -86,7 +86,7 @@ public class ActionMap { Object result = actionMap.get(key); - if (result == null) + if (result == null && parent != null) result = parent.get(key); return (Action) result; diff --git a/javax/swing/InputMap.java b/javax/swing/InputMap.java index 4655aa1c4..afabca40b 100644 --- a/javax/swing/InputMap.java +++ b/javax/swing/InputMap.java @@ -89,7 +89,7 @@ public class InputMap { Object result = inputMap.get(keystroke); - if (result == null) + if (result == null && parent != null) result = parent.get(keystroke); return result; } diff --git a/javax/swing/JComponent.java b/javax/swing/JComponent.java index 4c4559039..12abeb4cf 100644 --- a/javax/swing/JComponent.java +++ b/javax/swing/JComponent.java @@ -37,6 +37,7 @@ exception statement from your version. */ package javax.swing; +import java.awt.AWTEvent; import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -48,11 +49,13 @@ import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.geom.Rectangle2D; import java.awt.image.ImageObserver; @@ -316,6 +319,11 @@ public abstract class JComponent extends Container implements Serializable */ private Hashtable clientProperties; + private InputMap inputMap_whenFocused; + private InputMap inputMap_whenAncestorOfFocused; + private InputMap inputMap_whenInFocusedWindow; + private ActionMap actionMap; + /** * A lock held during recursive painting; this is used to serialize * access to the double buffer, and also to select the "top level" @@ -747,18 +755,6 @@ public abstract class JComponent extends Container implements Serializable return null; } - /** - * Get the ActionListener (typically an {@link Action} object) which is - * associated with a particular keystroke. - * - * @param aKeyStroke The keystroke to retrieve the action of - * - * @return The action associated with the specified keystroke - */ - public ActionListener getActionForKeyStroke(KeyStroke aKeyStroke) - { - return null; - } /** * Get the value of the {@link #alignmentX} property. @@ -865,24 +861,6 @@ public abstract class JComponent extends Container implements Serializable return g; } - /** - * Return the condition that determines whether a registered action - * occurs in response to the specified keystroke. - * - * @param aKeyStroke The keystroke to return the condition of - * - * @return One of the values {@link #UNDEFINED_CONDITION}, {@link - * #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}, {@link #WHEN_FOCUSED}, or {@link - * #WHEN_IN_FOCUSED_WINDOW} - * - * @see #registerKeyboardAction - * @see #unregisterKeyboardAction - * @see #resetKeyboardActiond - */ - public int getConditionForKeyStroke(KeyStroke aKeyStroke) - { - return UNDEFINED_CONDITION; - } /** * Get the value of the {@link #debugGraphicsOptions} property. @@ -1522,6 +1500,51 @@ public abstract class JComponent extends Container implements Serializable registerKeyboardAction(act, null, stroke, cond); } + /* + * There is some charmingly undocumented behavior sun seems to be using + * to simulate the old register/unregister keyboard binding API. It's not + * clear to me why this matters, but we shall endeavour to follow suit. + * + * Two main thing seem to be happening when you do registerKeyboardAction(): + * + * - no actionMap() entry gets created, just an entry in inputMap() + * + * - the inputMap() entry is a proxy class which invokes the the + * binding's actionListener as a target, and which clobbers the command + * name sent in the ActionEvent, providing the binding command name + * instead. + * + * This much you can work out just by asking the input and action maps + * what they contain after making bindings, and watching the event which + * gets delivered to the recipient. Beyond that, it seems to be a + * sun-private solution so I will only immitate it as much as it matters + * to external observers. + */ + + private static class ActionListenerProxy + extends AbstractAction + { + ActionListener target; + String bindingCommandName; + + public ActionListenerProxy(ActionListener li, + String cmd) + { + target = li; + bindingCommandName = cmd; + } + + public void actionPerformed(ActionEvent e) + { + ActionEvent derivedEvent = new ActionEvent(e.getSource(), + e.getID(), + bindingCommandName, + e.getModifiers()); + target.actionPerformed(derivedEvent); + } + } + + /** * An obsolete method to register a keyboard action on this component. * You should use <code>getInputMap</code> and <code>getActionMap</code> @@ -1546,6 +1569,183 @@ public abstract class JComponent extends Container implements Serializable KeyStroke stroke, int cond) { + getInputMap(cond).put(stroke, new ActionListenerProxy(act, cmd)); + } + + + + public final void setInputMap(int condition, InputMap map) + { + enableEvents(AWTEvent.KEY_EVENT_MASK); + switch (condition) + { + case WHEN_FOCUSED: + inputMap_whenFocused = map; + break; + + case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: + inputMap_whenAncestorOfFocused = map; + break; + + case WHEN_IN_FOCUSED_WINDOW: + inputMap_whenInFocusedWindow = map; + break; + + case UNDEFINED_CONDITION: + default: + throw new IllegalArgumentException(); + } + } + + public final InputMap getInputMap(int condition) + { + enableEvents(AWTEvent.KEY_EVENT_MASK); + switch (condition) + { + case WHEN_FOCUSED: + if (inputMap_whenFocused == null) + inputMap_whenFocused = new InputMap(); + return inputMap_whenFocused; + + case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: + if (inputMap_whenAncestorOfFocused == null) + inputMap_whenAncestorOfFocused = new InputMap(); + return inputMap_whenAncestorOfFocused; + + case WHEN_IN_FOCUSED_WINDOW: + if (inputMap_whenInFocusedWindow == null) + inputMap_whenInFocusedWindow = new InputMap(); + return inputMap_whenInFocusedWindow; + + case UNDEFINED_CONDITION: + default: + return null; + } + } + + public final InputMap getInputMap() + { + return getInputMap(WHEN_FOCUSED); + } + + public final ActionMap getActionMap() + { + if (actionMap == null) + actionMap = new ActionMap(); + return actionMap; + } + + public final void setActionMap(ActionMap map) + { + actionMap = map; + } + + /** + * @deprecated As of 1.3 KeyStrokes can be registered with multiple + * simultaneous conditions. + * + * Return the condition that determines whether a registered action + * occurs in response to the specified keystroke. + * + * @param aKeyStroke The keystroke to return the condition of + * + * @return One of the values {@link #UNDEFINED_CONDITION}, {@link + * #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}, {@link #WHEN_FOCUSED}, or {@link + * #WHEN_IN_FOCUSED_WINDOW} + * + * @see #registerKeyboardAction + * @see #unregisterKeyboardAction + * @see #resetKeyboardActiond + */ + public int getConditionForKeyStroke(KeyStroke ks) + { + if (inputMap_whenFocused != null + && inputMap_whenFocused.get(ks) != null) + return WHEN_FOCUSED; + else if (inputMap_whenAncestorOfFocused != null + && inputMap_whenAncestorOfFocused.get(ks) != null) + return WHEN_ANCESTOR_OF_FOCUSED_COMPONENT; + else if (inputMap_whenInFocusedWindow != null + && inputMap_whenInFocusedWindow.get(ks) != null) + return WHEN_IN_FOCUSED_WINDOW; + else + return UNDEFINED_CONDITION; + } + + /** + * @deprecated Use {@link #getActionMap()} + * + * Get the ActionListener (typically an {@link Action} object) which is + * associated with a particular keystroke. + * + * @param aKeyStroke The keystroke to retrieve the action of + * + * @return The action associated with the specified keystroke + */ + public ActionListener getActionForKeyStroke(KeyStroke ks) + { + Object cmd = getInputMap().get(ks); + if (cmd != null) + { + if (cmd instanceof ActionListenerProxy) + return (ActionListenerProxy) cmd; + else if (cmd instanceof String) + return getActionMap().get(cmd); + } + return null; + } + + /** + * A hook for subclasses which want to customize event processing. + */ + protected void processComponentKeyEvent(KeyEvent e) + { + } + + /** + * Override the default key dispatch system from Component to hook into + * the swing {@link InputMap} / {@link ActionMap} system. + * + * See <a + * href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this + * report</a> for more details, it's somewhat complex. + */ + protected void processKeyEvent(KeyEvent e) + { + processComponentKeyEvent(e); + + // FIXME: this needs to be elaborated significantly, to do all the + // focus / ancestor / window searching for the various binding modes. + if (! e.isConsumed() && + processKeyBinding(KeyStroke.getKeyStrokeForEvent(e), + e, WHEN_FOCUSED, e.getID() == KeyEvent.KEY_PRESSED)) + e.consume(); + } + + protected boolean processKeyBinding(KeyStroke ks, + KeyEvent e, + int condition, + boolean pressed) + { + if (isEnabled()) + { + Action act = null; + InputMap map = getInputMap(condition); + if (map != null) + { + Object cmd = map.get(ks); + if (cmd != null) + { + if (cmd instanceof ActionListenerProxy) + act = (Action) cmd; + else + act = (Action) getActionMap().get(cmd); + } + } + if (act != null && act.isEnabled()) + return SwingUtilities.notifyAction(act, ks, e, this, e.getModifiers()); + } + return false; } /** @@ -1571,6 +1771,10 @@ public abstract class JComponent extends Container implements Serializable */ public void resetKeyboardActions() { + inputMap_whenFocused.clear(); + inputMap_whenAncestorOfFocused.clear(); + inputMap_whenInFocusedWindow.clear(); + actionMap.clear(); } @@ -1878,11 +2082,14 @@ public abstract class JComponent extends Container implements Serializable if (ui != null) ui.uninstallUI(this); + ComponentUI oldUI = ui; ui = newUI; if (ui != null) ui.installUI(this); + firePropertyChange("UI", oldUI, newUI); + revalidate(); repaint(); } diff --git a/javax/swing/JLabel.java b/javax/swing/JLabel.java index 0a15cfc9c..9cd422712 100644 --- a/javax/swing/JLabel.java +++ b/javax/swing/JLabel.java @@ -40,6 +40,7 @@ package javax.swing; import java.awt.Component; import java.awt.Image; import java.awt.Font; +import java.awt.event.KeyEvent; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.swing.Icon; @@ -63,10 +64,10 @@ public class JLabel extends JComponent implements Accessible, SwingConstants protected Component labelFor; /** The label's text. */ - private transient String labelText; + private transient String text; /** Where the label will be positioned horizontally. */ - private transient int horizontalAlignment = CENTER; + private transient int horizontalAlignment = LEADING; /** Where the label text will be placed horizontally relative to the icon. */ private transient int horizontalTextPosition = TRAILING; @@ -78,16 +79,16 @@ public class JLabel extends JComponent implements Accessible, SwingConstants private transient int verticalTextPosition = CENTER; /** The icon painted when the label is enabled. */ - private transient Icon activeIcon; + private transient Icon icon; /** The icon painted when the label is disabled. */ private transient Icon disabledIcon; /** The label's mnemnonic key. */ - private transient char mnemonicKey; + private transient int displayedMnemonic = KeyEvent.VK_UNDEFINED; /** The index of the menemonic character in the text. */ - private transient int underlinedChar = -1; + private transient int displayedMnemonicIndex = -1; /** The gap between the icon and the text. */ private transient int iconTextGap = 4; @@ -209,8 +210,8 @@ public class JLabel extends JComponent implements Accessible, SwingConstants */ public JLabel(String text, Icon icon, int horizontalAlignment) { - labelText = text; - activeIcon = icon; + this.text = text; + this.icon = icon; this.horizontalAlignment = horizontalAlignment; updateUI(); } @@ -273,24 +274,26 @@ public class JLabel extends JComponent implements Accessible, SwingConstants */ public String getText() { - return labelText; + return text; } /** * This method changes the "text" property. The given text will be painted * in the label. * - * @param text The label's text. + * @param newText The label's text. */ - public void setText(String text) + public void setText(String newText) { - if (text != labelText) + if (text != newText) { - String oldText = labelText; - labelText = text; - firePropertyChange(TEXT_CHANGED_PROPERTY, oldText, labelText); - if (labelText != null && labelText.length() <= underlinedChar) - setDisplayedMnemonicIndex(labelText.length() - 1); + String oldText = text; + text = newText; + firePropertyChange(TEXT_CHANGED_PROPERTY, oldText, newText); + + if (text != null + && text.length() <= displayedMnemonicIndex) + setDisplayedMnemonicIndex(text.length() - 1); } } @@ -302,22 +305,22 @@ public class JLabel extends JComponent implements Accessible, SwingConstants */ public Icon getIcon() { - return activeIcon; + return icon; } /** * This method changes the "icon" property. This icon (the active icon) will * be the one displayed when the label is enabled. * - * @param icon The active icon. + * @param newIcon The active icon. */ - public void setIcon(Icon icon) + public void setIcon(Icon newIcon) { - if (icon != activeIcon) + if (icon != newIcon) { - Icon oldIcon = activeIcon; - activeIcon = icon; - firePropertyChange(ICON_CHANGED_PROPERTY, oldIcon, activeIcon); + Icon oldIcon = icon; + icon = newIcon; + firePropertyChange(ICON_CHANGED_PROPERTY, oldIcon, newIcon); } } @@ -331,8 +334,9 @@ public class JLabel extends JComponent implements Accessible, SwingConstants */ public Icon getDisabledIcon() { - if (disabledIcon == null && activeIcon instanceof ImageIcon) - disabledIcon = new ImageIcon(GrayFilter.createDisabledImage(((ImageIcon) activeIcon).getImage())); + if (disabledIcon == null && icon instanceof ImageIcon) + disabledIcon = new ImageIcon(GrayFilter.createDisabledImage(((ImageIcon) icon).getImage())); + return disabledIcon; } @@ -340,16 +344,15 @@ public class JLabel extends JComponent implements Accessible, SwingConstants * This method changes the "disabledIcon" property. This icon (the disabled * icon) will be the one displayed when the label is disabled. * - * @param disabledIcon The disabled icon. + * @param newIcon The disabled icon. */ - public void setDisabledIcon(Icon disabledIcon) + public void setDisabledIcon(Icon newIcon) { - if (disabledIcon != this.disabledIcon) + if (disabledIcon != newIcon) { - Icon oldDisabledIcon = this.disabledIcon; - this.disabledIcon = disabledIcon; - firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldDisabledIcon, - this.disabledIcon); + Icon oldIcon = disabledIcon; + disabledIcon = newIcon; + firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, oldIcon, newIcon); } } @@ -358,31 +361,32 @@ public class JLabel extends JComponent implements Accessible, SwingConstants * label is used as a label for another component, the label will give * focus to that component when the mnemonic is activated. * - * @param key The keycode to use for the mnemonic. + * @param mnemonic The keycode to use for the mnemonic. */ - public void setDisplayedMnemonic(int key) + public void setDisplayedMnemonic(int mnemonic) { - setDisplayedMnemonic((char) key); + if (displayedMnemonic != mnemonic) + { + firePropertyChange(DISPLAYED_MNEMONIC_CHANGED_PROPERTY, + displayedMnemonic, mnemonic); + displayedMnemonic = mnemonic; + + if (text != null) + setDisplayedMnemonicIndex(text.indexOf(mnemonic)); + } } + /** * This method sets the character that will be the mnemonic used. If the * label is used as a label for another component, the label will give * focus to that component when the mnemonic is activated. * - * @param aChar The character to use for the mnemonic. + * @param menmonic The character to use for the mnemonic. */ - public void setDisplayedMnemonic(char aChar) - { - if (aChar != mnemonicKey) + public void setDisplayedMnemonic(char mnemonic) { - char oldKey = mnemonicKey; - mnemonicKey = aChar; - firePropertyChange(DISPLAYED_MNEMONIC_CHANGED_PROPERTY, oldKey, - mnemonicKey); - if (labelText != null) - setDisplayedMnemonicIndex(labelText.indexOf(mnemonicKey)); - } + setDisplayedMnemonic((int) mnemonic); } /** @@ -392,7 +396,7 @@ public class JLabel extends JComponent implements Accessible, SwingConstants */ public int getDisplayedMnemonic() { - return (int) mnemonicKey; + return (int) displayedMnemonic; } /** @@ -401,26 +405,25 @@ public class JLabel extends JComponent implements Accessible, SwingConstants * no mnemonic. If the index is less than -1 or if the index is equal to * the length, this method will throw an IllegalArgumentException. * - * @param index The index of the character to underline. + * @param newIndex The index of the character to underline. * * @throws IllegalArgumentException If index less than -1 or index equals * length. */ - public void setDisplayedMnemonicIndex(int index) + public void setDisplayedMnemonicIndex(int newIndex) throws IllegalArgumentException { - if (index < -1 || labelText != null && index >= labelText.length()) + if (newIndex < -1 || (text != null && newIndex >= text.length())) throw new IllegalArgumentException(); - if (labelText == null || labelText.charAt(index) != mnemonicKey) - index = -1; + if (text == null || text.charAt(newIndex) != displayedMnemonic) + newIndex = -1; - if (index != underlinedChar) + if (newIndex != displayedMnemonicIndex) { - int oldIndex = underlinedChar; - underlinedChar = index; firePropertyChange(DISPLAYED_MNEMONIC_INDEX_CHANGED_PROPERTY, - oldIndex, underlinedChar); + displayedMnemonicIndex, newIndex); + displayedMnemonicIndex = newIndex; } } @@ -432,7 +435,7 @@ public class JLabel extends JComponent implements Accessible, SwingConstants */ public int getDisplayedMnemonicIndex() { - return underlinedChar; + return displayedMnemonicIndex; } /** @@ -490,16 +493,15 @@ public class JLabel extends JComponent implements Accessible, SwingConstants * This method changes the "iconTextGap" property. The iconTextGap * determines how much space there is between the icon and the text. * - * @param iconTextGap The gap between the icon and the text. + * @param newGap The gap between the icon and the text. */ - public void setIconTextGap(int iconTextGap) + public void setIconTextGap(int newGap) { - if (iconTextGap != this.iconTextGap) + if (iconTextGap != newGap) { - int oldIconTextGap = this.iconTextGap; - this.iconTextGap = iconTextGap; - firePropertyChange(ICON_TEXT_GAP_CHANGED_PROPERTY, oldIconTextGap, - iconTextGap); + firePropertyChange(ICON_TEXT_GAP_CHANGED_PROPERTY, iconTextGap, + newGap); + iconTextGap = newGap; } } @@ -632,11 +634,12 @@ public class JLabel extends JComponent implements Accessible, SwingConstants public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, int h) { - Icon currIcon = (isEnabled()) ? activeIcon : disabledIcon; + Icon currIcon = isEnabled() ? icon : disabledIcon; - //Is this the correct way to check for image equality? + // XXX: Is this the correct way to check for image equality? if (currIcon != null && currIcon instanceof ImageIcon) return (((ImageIcon) currIcon).getImage() == img); + return false; } @@ -664,9 +667,8 @@ public class JLabel extends JComponent implements Accessible, SwingConstants { if (c != labelFor) { - Component oldLabelFor = labelFor; + firePropertyChange(LABEL_FOR_CHANGED_PROPERTY, labelFor, c); labelFor = c; - firePropertyChange(LABEL_FOR_CHANGED_PROPERTY, oldLabelFor, labelFor); } } diff --git a/javax/swing/JRadioButton.java b/javax/swing/JRadioButton.java index 21e545edd..70ccb7ca8 100644 --- a/javax/swing/JRadioButton.java +++ b/javax/swing/JRadioButton.java @@ -48,6 +48,7 @@ public class JRadioButton extends JToggleButton { this(null, null); } + public JRadioButton(Action a) { this(); @@ -59,11 +60,21 @@ public class JRadioButton extends JToggleButton this(null, icon); } + public JRadioButton(Icon icon, boolean selected) + { + this(null, icon, selected); + } + public JRadioButton(String text) { this(text, null); } + public JRadioButton(String text, boolean selected) + { + this(text, null, selected); + } + public JRadioButton(String text, Icon icon) { super(text, icon); @@ -71,6 +82,9 @@ public class JRadioButton extends JToggleButton contentAreaFilled = false; } + public JRadioButton(String text, Icon icon, boolean selected) + { + } public AccessibleContext getAccessibleContext() { diff --git a/javax/swing/JTextArea.java b/javax/swing/JTextArea.java index 1ebc164ea..00b90ac09 100644 --- a/javax/swing/JTextArea.java +++ b/javax/swing/JTextArea.java @@ -103,13 +103,15 @@ public class JTextArea extends JTextComponent /** * Whether line wrapping is enabled or not. */ - private boolean wrapping; + private boolean lineWrap; /** * The number of characters equal to a tab within the text. */ private int tabSize = 8; + private boolean wrapStyleWord; + /** * Creates a new <code>JTextArea</code> object. */ @@ -221,7 +223,7 @@ public class JTextArea extends JTextComponent */ public boolean getScrollableTracksViewportWidth() { - return wrapping ? true : super.getScrollableTracksViewportWidth(); + return lineWrap ? true : super.getScrollableTracksViewportWidth(); } /** @@ -287,29 +289,58 @@ public class JTextArea extends JTextComponent /** * Checks whether line wrapping is enabled. * - * @return true if line wrapping is enabled, false otherwise + * @return <code>true</code> if line wrapping is enabled, + * <code>false</code> otherwise */ public boolean getLineWrap() { - return wrapping; + return lineWrap; } /** * Enables/disables line wrapping. * - * @param wrapping true to enable line wrapping, false otherwise + * @param wrapping <code>true</code> to enable line wrapping, + * <code>false</code> otherwise */ public void setLineWrap(boolean flag) { - if (wrapping == flag) + if (lineWrap == flag) return; - boolean oldValue = wrapping; - wrapping = flag; - firePropertyChange("lineWrap", oldValue, wrapping); + boolean oldValue = lineWrap; + lineWrap = flag; + firePropertyChange("lineWrap", oldValue, lineWrap); } /** + * Checks whether word style wrapping is enabled. + * + * @return <code>true</code> if word style wrapping is enabled, + * <code>false</code> otherwise + */ + public boolean getWrapStyleWord() + { + return wrapStyleWord; + } + + /** + * Enables/Disables word style wrapping. + * + * @param flag <code>true</code> to enable word style wrapping, + * <code>false</code> otherwise + */ + public void setWrapStyleWord(boolean flag) + { + if (wrapStyleWord == flag) + return; + + boolean oldValue = wrapStyleWord; + wrapStyleWord = flag; + firePropertyChange("wrapStyleWord", oldValue, wrapStyleWord); + } + + /** * Returns the number of characters used for a tab. * This defaults to 8. * diff --git a/javax/swing/JTextField.java b/javax/swing/JTextField.java index 86be4b7b4..e0b720917 100644 --- a/javax/swing/JTextField.java +++ b/javax/swing/JTextField.java @@ -138,9 +138,15 @@ public class JTextField extends JTextComponent */ public JTextField(Document doc, String text, int columns) { + if (columns < 0) + throw new IllegalArgumentException(); + + this.columns = columns; + setDocument(doc == null ? createDefaultModel() : doc); + + if (text != null) setText(text); - setColumns(columns); } /** diff --git a/javax/swing/KeyStroke.java b/javax/swing/KeyStroke.java index ef3d78790..1088051e8 100644 --- a/javax/swing/KeyStroke.java +++ b/javax/swing/KeyStroke.java @@ -37,235 +37,69 @@ exception statement from your version. */ package javax.swing; +import java.awt.AWTKeyStroke; import java.awt.event.KeyEvent; import java.io.Serializable; -/** - * KeyStroke - * @author Andrew Selkirk - * @version 1.0 - */ -public class KeyStroke implements Serializable +public class KeyStroke + extends AWTKeyStroke + implements Serializable { static final long serialVersionUID = -9060180771037902530L; - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * keyChar - */ - private char keyChar = 0; - - /** - * keyCode - */ - private int keyCode = 0; - - /** - * modifiers - */ - private int modifiers = 0; - - /** - * onKeyRelease - */ - private boolean onKeyRelease = false; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor KeyStroke - */ private KeyStroke() { - } // KeyStroke() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * hashCode - * @returns int - */ - public int hashCode() { - return 0; // TODO - } // hashCode() - - /** - * equals - * @param object TODO - * @returns boolean - */ - public boolean equals(Object object) { - - // Variables - KeyStroke key; - - if (object instanceof KeyStroke) { - key = (KeyStroke) object; - if (key.keyChar == keyChar && - key.keyCode == keyCode && - key.modifiers == modifiers && - key.onKeyRelease == onKeyRelease) { - return true; - } // if - } // if - return false; - - } // equals() - - /** - * toString - * @returns String - */ - public String toString() { - return null; // TODO - } // toString() - - /** - * getKeyStroke - * @param keyChar TODO - * @returns KeyStroke - */ - public static KeyStroke getKeyStroke(char keyChar) { + } - // Variables - KeyStroke key; + protected KeyStroke(char keyChar, int keyCode, int modifiers, + boolean onKeyRelease) + { + super(keyChar, keyCode, modifiers, onKeyRelease); + } - key = new KeyStroke(); - key.keyChar = keyChar; - return key; + static + { + AWTKeyStroke.registerSubclass(KeyStroke.class); + } - } // getKeyStroke() + public static KeyStroke getKeyStroke(char keyChar) + { + return (KeyStroke) getAWTKeyStroke(keyChar); + } /** - * getKeyStroke - deprecated - * @param keyChar TODO - * @param onKeyRelease TODO - * @returns KeyStroke - * @deprecated Use getKeyStroke(char) + * @deprecated Use {@link #getKeyStroke(char)} + * + * This method, unlike all the other factory methods on this object, + * returns a non-cached, non-shared object. New code should not use it. */ - public static KeyStroke getKeyStroke(char keyChar, boolean onKeyRelease) { - KeyStroke key = getKeyStroke(keyChar); - key.onKeyRelease = onKeyRelease; - return key; - } // getKeyStroke() + public static KeyStroke getKeyStroke(char keyChar, boolean onKeyRelease) + { + return new KeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, onKeyRelease); + } - /** - * getKeyStroke - * @param keyChar TODO - * @param modifiers TODO - * @returns KeyStroke - */ - public static KeyStroke getKeyStroke(Character keyChar, int modifiers) { - KeyStroke key = getKeyStroke(keyChar.charValue()); - key.modifiers = modifiers; - return key; - } // getKeyStroke() + public static KeyStroke getKeyStroke(Character keyChar, int modifiers) + { + return (KeyStroke) getAWTKeyStroke(keyChar, modifiers); + } - /** - * getKeyStroke - * @param keyCode TODO - * @param modifiers TODO - * @param onKeyRelease TODO - * @returns KeyStroke - */ public static KeyStroke getKeyStroke(int keyCode, int modifiers, - boolean onKeyRelease) { - - // Variables - KeyStroke key; - - key = new KeyStroke(); - key.keyCode = keyCode; - key.modifiers = modifiers; - key.onKeyRelease = onKeyRelease; - return key; - - } // getKeyStroke() - - /** - * getKeyStroke - * @param keyCode TODO - * @param modifiers TODO - * @returns KeyStroke - */ - public static KeyStroke getKeyStroke(int keyCode, int modifiers) { - return getKeyStroke(keyCode, modifiers, false); - } // getKeyStroke() - - /** - * getKeyStroke - * @param string TODO - * @returns KeyStroke - */ - public static KeyStroke getKeyStroke(String string) { - return null; // TODO - } // getKeyStroke() - - /** - * getKeyStrokeForEvent - * @param event TODO - * @returns KeyStroke - */ - public static KeyStroke getKeyStrokeForEvent(KeyEvent event) { - - // Variables - int eventID; - int eventMod; - - // Get Event ID - eventID = event.getID(); - eventMod = event.getModifiersEx(); - - // Check for KEY_TYPED event - if (eventID == KeyEvent.KEY_TYPED) { - return getKeyStroke(event.getKeyChar(), eventMod); - - // KEY_PRESSED or KEY_RELEASED event - } else { - return getKeyStroke(event.getKeyCode(), eventMod); - } // if - - } // getKeyStrokeForEvent() - - /** - * getKeyChar - * @returns char - */ - public char getKeyChar() { - return keyChar; - } // getKeyChar() - - /** - * getKeyCode - * @returns int - */ - public int getKeyCode() { - return keyCode; - } // getKeyCode() - - /** - * getModifiers - * @returns int - */ - public int getModifiers() { - return modifiers; // TODO - } // getModifiers() - - /** - * isOnKeyRelease - * @returns boolean - */ - public boolean isOnKeyRelease() { - return onKeyRelease; - } // isOnKeyRelease() - - -} // KeyStroke + boolean onKeyRelease) + { + return (KeyStroke) getAWTKeyStroke(keyCode, modifiers, onKeyRelease); + } + + public static KeyStroke getKeyStroke(int keyCode, int modifiers) + { + return (KeyStroke) getAWTKeyStroke(keyCode, modifiers); + } + + public static KeyStroke getKeyStroke(String str) + { + return (KeyStroke) getAWTKeyStroke(str); + } + + public static KeyStroke getKeyStrokeForEvent(KeyEvent event) + { + return (KeyStroke) getAWTKeyStrokeForEvent(event); + } + +} diff --git a/javax/swing/SwingUtilities.java b/javax/swing/SwingUtilities.java index 05a3a6191..731a7bc8d 100644 --- a/javax/swing/SwingUtilities.java +++ b/javax/swing/SwingUtilities.java @@ -51,9 +51,13 @@ import java.awt.Rectangle; import java.awt.Shape; import java.awt.Toolkit; import java.awt.Window; +import java.awt.event.ActionEvent; import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.lang.reflect.InvocationTargetException; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.InputMapUIResource; /** @@ -911,4 +915,103 @@ public class SwingUtilities implements SwingConstants return true; } } + + public static boolean notifyAction(Action action, + KeyStroke ks, + KeyEvent event, + Object sender, + int modifiers) + { + if (action != null && action.isEnabled()) + { + String name = (String) action.getValue(Action.ACTION_COMMAND_KEY); + if (name == null + && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED) + name = new String(new char[] {event.getKeyChar()}); + action.actionPerformed(new ActionEvent(sender, + ActionEvent.ACTION_PERFORMED, + name, modifiers)); + return true; + } + return false; + } + + /** + * <p>Change the shared, UI-managed {@link ActionMap} for a given + * component. ActionMaps are arranged in a hierarchy, in order to + * encourage sharing of common actions between components. The hierarchy + * unfortunately places UI-managed ActionMaps at the <em>end</em> of the + * parent-pointer chain, as illustrated:</p> + * + * <pre> + * [{@link javax.swing.JComponent#getActionMap()}] + * --> [{@link javax.swing.ActionMap}] + * parent --> [{@link javax.swing.text.KeymapActionMap}] + * parent --> [{@link javax.swing.plaf.ActionMapUIResource}] + * </pre> + * + * <p>Our goal with this method is to replace the first ActionMap along + * this chain which is an instance of {@link ActionMapUIResource}, since + * these are the ActionMaps which are supposed to be shared between + * components.</p> + * + * <p>If the provided ActionMap is <code>null</code>, we interpret the + * call as a request to remove the UI-managed ActionMap from the + * component's ActionMap parent chain.</p> + */ + public static void replaceUIActionMap(JComponent component, + ActionMap uiActionMap) + { + ActionMap child = component.getActionMap(); + if (child == null) + component.setActionMap(uiActionMap); + else + { + while(child.getParent() != null + && !(child.getParent() instanceof ActionMapUIResource)) + child = child.getParent(); + if (child != null) + child.setParent(uiActionMap); + } + } + + /** + * <p>Change the shared, UI-managed {@link InputMap} for a given + * component. InputMaps are arranged in a hierarchy, in order to + * encourage sharing of common input mappings between components. The + * hierarchy unfortunately places UI-managed InputMaps at the + * <em>end</em> of the parent-pointer chain, as illustrated:</p> + * + * <pre> + * [{@link javax.swing.JComponent#getInputMap()}] + * --> [{@link javax.swing.InputMap}] + * parent --> [{@link javax.swing.text.KeymapWrapper}] + * parent --> [{@link javax.swing.plaf.InputMapUIResource}] + * </pre> + * + * <p>Our goal with this method is to replace the first InputMap along + * this chain which is an instance of {@link InputMapUIResource}, since + * these are the InputMaps which are supposed to be shared between + * components.</p> + * + * <p>If the provided InputMap is <code>null</code>, we interpret the + * call as a request to remove the UI-managed InputMap from the + * component's InputMap parent chain.</p> + */ + public static void replaceUIInputMap(JComponent component, + int condition, + InputMap uiInputMap) + { + InputMap child = component.getInputMap(condition); + if (child == null) + component.setInputMap(condition, uiInputMap); + else + { + while(child.getParent() != null + && !(child.getParent() instanceof InputMapUIResource)) + child = child.getParent(); + if (child != null) + child.setParent(uiInputMap); + } + } } diff --git a/javax/swing/TransferHandler.java b/javax/swing/TransferHandler.java new file mode 100644 index 000000000..e64df9c4c --- /dev/null +++ b/javax/swing/TransferHandler.java @@ -0,0 +1,55 @@ +/* TransferHandler.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing; + +import java.io.Serializable; + +public class TransferHandler implements Serializable +{ + private static final long serialVersionUID = -7908749299918704233L; + + public static final int NONE = 0; + public static final int COPY = 1; + public static final int MOVE = 2; + public static final int COPY_OR_MOVE = 3; + + protected TransferHandler() + { + // Do nothing here. + } +} diff --git a/javax/swing/plaf/basic/BasicButtonListener.java b/javax/swing/plaf/basic/BasicButtonListener.java index c06ca0e9d..7392c7e58 100644 --- a/javax/swing/plaf/basic/BasicButtonListener.java +++ b/javax/swing/plaf/basic/BasicButtonListener.java @@ -37,6 +37,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.InputEvent; @@ -46,6 +47,7 @@ import java.awt.event.MouseMotionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; import javax.swing.AbstractButton; import javax.swing.ButtonModel; import javax.swing.JComponent; @@ -64,6 +66,12 @@ public class BasicButtonListener } public void focusGained(FocusEvent e) { + if (e.getSource() instanceof AbstractButton) + { + AbstractButton button = (AbstractButton) e.getSource(); + if (button.isFocusPainted()) + button.repaint(); + } } public void focusLost(FocusEvent e) @@ -73,13 +81,43 @@ public class BasicButtonListener AbstractButton button = (AbstractButton) e.getSource(); ButtonModel model = button.getModel(); model.setArmed(false); + + if (button.isFocusPainted()) + button.repaint(); } } public void installKeyboardActions(JComponent c) { + c.getActionMap().put("pressed", + new AbstractAction() + { + public void actionPerformed(ActionEvent e) + { + AbstractButton button = (AbstractButton) e.getSource(); + ButtonModel model = button.getModel(); + // It is important that these transitions happen in this order. + model.setArmed(true); + model.setPressed(true); + } + }); + + c.getActionMap().put("released", + new AbstractAction() + { + public void actionPerformed(ActionEvent e) + { + AbstractButton button = (AbstractButton) e.getSource(); + ButtonModel model = button.getModel(); + // It is important that these transitions happen in this order. + model.setPressed(false); + model.setArmed(false); + } + }); } public void uninstallKeyboardActions(JComponent c) { + c.getActionMap().put("pressed", null); + c.getActionMap().put("released", null); } public void stateChanged(ChangeEvent e) { diff --git a/javax/swing/plaf/basic/BasicButtonUI.java b/javax/swing/plaf/basic/BasicButtonUI.java index 9134fb2e2..df7ac4269 100644 --- a/javax/swing/plaf/basic/BasicButtonUI.java +++ b/javax/swing/plaf/basic/BasicButtonUI.java @@ -50,6 +50,7 @@ import java.awt.Stroke; import javax.swing.AbstractButton; import javax.swing.ButtonModel; import javax.swing.Icon; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; @@ -59,13 +60,21 @@ import javax.swing.plaf.ComponentUI; public class BasicButtonUI extends ButtonUI { - /** A constant used to pad out elements in the button's layout and - preferred size calculations. */ - int defaultTextIconGap = 4; + /** + * A constant used to pad out elements in the button's layout and + * preferred size calculations. + */ + protected int defaultTextIconGap = 4; - /** A constant added to the defaultTextIconGap to adjust the text - within this particular button. */ - int defaultTextShiftOffset = 0; + /** + * A constant added to the defaultTextIconGap to adjust the text + * within this particular button. + */ + protected int defaultTextShiftOffset = 0; + + private int textShiftOffset; + + private Color focusColor; /** * Factory method to create an instance of BasicButtonUI for a given @@ -85,14 +94,32 @@ public class BasicButtonUI extends ButtonUI return defaultTextIconGap; } + protected void clearTextShiftOffset() + { + textShiftOffset = 0; + } + + protected int getTextShiftOffset() + { + return textShiftOffset; + } + + protected void setTextShiftOffset() + { + textShiftOffset = defaultTextShiftOffset; + } + protected void installDefaults(AbstractButton b) { UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + focusColor = defaults.getColor("Button.focus"); b.setForeground(defaults.getColor("Button.foreground")); b.setBackground(defaults.getColor("Button.background")); b.setMargin(defaults.getInsets("Button.margin")); b.setBorder(defaults.getBorder("Button.border")); b.setIconTextGap(defaults.getInt("Button.textIconGap")); + b.setInputMap(JComponent.WHEN_FOCUSED, + (InputMap) defaults.get("Button.focusInputMap")); b.setOpaque(true); } @@ -133,10 +160,12 @@ public class BasicButtonUI extends ButtonUI protected void installKeyboardActions(AbstractButton b) { + listener.installKeyboardActions(b); } protected void uninstallKeyboardActions(AbstractButton b) { + listener.uninstallKeyboardActions(b); } /** @@ -271,22 +300,12 @@ public class BasicButtonUI extends ButtonUI { if (b.hasFocus() && b.isFocusPainted()) { - Graphics2D g2 = (Graphics2D) g; - Stroke saved_stroke = g2.getStroke(); - Color saved_color = g2.getColor(); - float dashes[] = new float[] {1.0f, 1.0f}; - BasicStroke s = new BasicStroke(1.0f, - BasicStroke.CAP_SQUARE, - BasicStroke.JOIN_MITER, - 10, dashes, 0.0f); - g2.setStroke(s); - g2.setColor(Color.BLACK); - g2.drawRect(vr.x + 2, - vr.y + 2, - vr.width - 4, - vr.height - 4); - g2.setStroke(saved_stroke); - g2.setColor(saved_color); + Color saved_color = g.getColor(); + g.setColor(focusColor); + Rectangle focusRect = ir.union(tr); + g.drawRect(focusRect.x, focusRect.y, + focusRect.width, focusRect.height); + g.setColor(saved_color); } } diff --git a/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java b/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java new file mode 100644 index 000000000..c61dc40a5 --- /dev/null +++ b/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java @@ -0,0 +1,62 @@ +/* BasicFormattedTextFieldUI.java + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.plaf.basic; + +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; + +/** + * @since 1.4 + */ +public class BasicFormattedTextFieldUI extends BasicTextFieldUI +{ + public BasicFormattedTextFieldUI() + { + } + + public static ComponentUI createUI(JComponent c) + { + return new BasicFormattedTextFieldUI(); + } + + protected String getPropertyPrefix() + { + return "FormattedTextField"; + } +}
\ No newline at end of file diff --git a/javax/swing/plaf/basic/BasicLookAndFeel.java b/javax/swing/plaf/basic/BasicLookAndFeel.java index 154d7ed4d..689cd8f57 100644 --- a/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -41,6 +41,7 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.io.Serializable; import java.util.Enumeration; @@ -247,6 +248,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "SPACE", "pressed", "released SPACE", "released" }), + "Button.focus", midPurple, "Button.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Button.foreground", new ColorUIResource(Color.black), "Button.highlight", new ColorUIResource(Color.white), @@ -821,7 +823,14 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TextField.keyBindings", new JTextComponent.KeyBinding[] { new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), - "notify-field-accept")}, + "notify-field-accept"), + new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, + InputEvent.SHIFT_DOWN_MASK), + "selection-backward"), + new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, + InputEvent.SHIFT_DOWN_MASK), + "selection-forward"), + }, "TextField.margin", new InsetsUIResource(0, 0, 0, 0), "TextField.selectionBackground", new ColorUIResource(lightPurple), "TextField.selectionForeground", new ColorUIResource(Color.black), diff --git a/javax/swing/plaf/basic/BasicPasswordFieldUI.java b/javax/swing/plaf/basic/BasicPasswordFieldUI.java new file mode 100644 index 000000000..fe1c4902a --- /dev/null +++ b/javax/swing/plaf/basic/BasicPasswordFieldUI.java @@ -0,0 +1,61 @@ +/* BasicPasswordFieldUI.java + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.plaf.basic; + +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.text.Element; +import javax.swing.text.View; + +public class BasicPasswordFieldUI extends BasicTextFieldUI +{ + public BasicPasswordFieldUI() + { + } + + public static ComponentUI createUI(JComponent c) + { + return new BasicPasswordFieldUI(); + } + + protected String getPropertyPrefix() + { + return "PasswordField"; + } +} diff --git a/javax/swing/plaf/basic/BasicTextAreaUI.java b/javax/swing/plaf/basic/BasicTextAreaUI.java new file mode 100644 index 000000000..f1714c20d --- /dev/null +++ b/javax/swing/plaf/basic/BasicTextAreaUI.java @@ -0,0 +1,69 @@ +/* BasicTextAreaUI.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.plaf.basic; + +import java.beans.PropertyChangeEvent; + +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.text.Element; +import javax.swing.text.PlainView; +import javax.swing.text.View; + +public class BasicTextAreaUI extends BasicTextUI +{ + public static ComponentUI createUI(JComponent comp) + { + return new BasicTextAreaUI(); + } + + public BasicTextAreaUI() + { + } + + public View create(Element elem) + { + return new PlainView(elem); + } + + protected String getPropertyPrefix() + { + return "TextArea"; + } +} diff --git a/javax/swing/plaf/basic/BasicTextUI.java b/javax/swing/plaf/basic/BasicTextUI.java index ad617cab2..0d073e8e3 100644 --- a/javax/swing/plaf/basic/BasicTextUI.java +++ b/javax/swing/plaf/basic/BasicTextUI.java @@ -46,8 +46,16 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.InputMap; import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.TextUI; import javax.swing.plaf.UIResource; @@ -61,7 +69,9 @@ import javax.swing.text.EditorKit; import javax.swing.text.Element; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; +import javax.swing.text.Keymap; import javax.swing.text.PlainDocument; +import javax.swing.text.PlainView; import javax.swing.text.Position; import javax.swing.text.View; import javax.swing.text.ViewFactory; @@ -88,13 +98,17 @@ public abstract class BasicTextUI extends TextUI private class RootView extends View { - private JTextComponent textComponent; private View view; - public RootView(JTextComponent parent) + public RootView() { super(null); - textComponent = parent; + } + + public ViewFactory getViewFactory() + { + // FIXME: Handle EditorKit somehow. + return BasicTextUI.this; } public void setView(View v) @@ -123,17 +137,34 @@ public abstract class BasicTextUI extends TextUI public void paint(Graphics g, Shape s) { - System.out.println("Michael: BasicTextUI.RootView.paint"); - if (view != null) view.paint(g, s); } + + protected Rectangle modelToView(int position, Shape a, Position.Bias bias) + throws BadLocationException + { + return ((PlainView) view).modelToView(position, a, bias).getBounds(); + } } - - RootView rootView; + + class UpdateHandler implements PropertyChangeListener + { + public void propertyChange(PropertyChangeEvent event) + { + if (event.getPropertyName().equals("document")) + { + // Document changed. + modelChanged(); + } + } + } + + static EditorKit kit = new DefaultEditorKit(); + + RootView rootView = new RootView(); JTextComponent textComponent; - int gap = 3; - EditorKit kit = new DefaultEditorKit(); + UpdateHandler updateHandler = new UpdateHandler(); public BasicTextUI() { @@ -164,12 +195,12 @@ public abstract class BasicTextUI extends TextUI Document doc = textComponent.getDocument(); if (doc == null) { - doc = new PlainDocument(); + doc = getEditorKit(textComponent).createDefaultDocument(); textComponent.setDocument(doc); } - rootView = new RootView(textComponent); - setView(create(doc.getDefaultRootElement())); + textComponent.addPropertyChangeListener(updateHandler); + modelChanged(); installDefaults(); installListeners(); @@ -178,20 +209,115 @@ public abstract class BasicTextUI extends TextUI protected void installDefaults() { + Caret caret = textComponent.getCaret(); + if (caret == null) + { + caret = createCaret(); + textComponent.setCaret(caret); + } + + Highlighter highlighter = textComponent.getHighlighter(); + if (highlighter == null) + textComponent.setHighlighter(createHighlighter()); + + String prefix = getPropertyPrefix(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + textComponent.setBackground(defaults.getColor(prefix + ".background")); + textComponent.setForeground(defaults.getColor(prefix + ".foreground")); + textComponent.setMargin(defaults.getInsets(prefix + ".margin")); + textComponent.setBorder(defaults.getBorder(prefix + ".border")); + textComponent.setFont(defaults.getFont(prefix + ".font")); + + caret.setBlinkRate(defaults.getInt(prefix + ".caretBlinkRate")); } protected void installListeners() { + // Do nothing here. + } + + protected String getKeymapName() + { + return "BasicTextUI"; + } + + protected Keymap createKeymap() + { + String prefix = getPropertyPrefix(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + JTextComponent.KeyBinding[] bindings = + (JTextComponent.KeyBinding[]) defaults.get(prefix + ".keyBindings"); + Keymap km = JTextComponent.addKeymap(getKeymapName(), + JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP)); + JTextComponent.loadKeymap(km, bindings, textComponent.getActions()); + return km; } protected void installKeyboardActions() { + // load any bindings for the older Keymap interface + Keymap km = JTextComponent.getKeymap(getKeymapName()); + if (km == null) + km = createKeymap(); + textComponent.setKeymap(km); + + // load any bindings for the newer InputMap / ActionMap interface + SwingUtilities.replaceUIInputMap(textComponent, + JComponent.WHEN_FOCUSED, + getInputMap(JComponent.WHEN_FOCUSED)); + SwingUtilities.replaceUIActionMap(textComponent, getActionMap()); + } + + InputMap getInputMap(int condition) + { + String prefix = getPropertyPrefix(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + switch (condition) + { + case JComponent.WHEN_IN_FOCUSED_WINDOW: + // FIXME: is this the right string? nobody seems to use it. + return (InputMap) defaults.get(prefix + ".windowInputMap"); + case JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: + return (InputMap) defaults.get(prefix + ".ancestorInputMap"); + default: + case JComponent.WHEN_FOCUSED: + return (InputMap) defaults.get(prefix + ".focusInputMap"); + } + } + + ActionMap getActionMap() + { + String prefix = getPropertyPrefix(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + ActionMap am = (ActionMap) defaults.get(prefix + ".actionMap"); + if (am == null) + { + am = createActionMap(); + defaults.put(prefix + ".actionMap", am); + } + return am; + } + + ActionMap createActionMap() + { + Action[] actions = textComponent.getActions(); + ActionMap am = new ActionMap(); + for (int i = 0; i < actions.length; ++i) + { + String name = (String) actions[i].getValue(Action.NAME); + if (name != null) + am.put(name, actions[i]); + } + return am; } - public void uninstallUI(final JComponent c) + public void uninstallUI(final JComponent component) { - super.uninstallUI(c); - rootView = null; + super.uninstallUI(component); + rootView.setView(null); + + textComponent.removePropertyChangeListener(updateHandler); + textComponent = null; uninstallDefaults(); uninstallListeners(); @@ -200,14 +326,17 @@ public abstract class BasicTextUI extends TextUI protected void uninstallDefaults() { + // Do nothing here. } protected void uninstallListeners() { + // Do nothing here. } protected void uninstallKeyboardActions() { + // Do nothing here. } protected abstract String getPropertyPrefix(); @@ -235,18 +364,19 @@ public abstract class BasicTextUI extends TextUI if (textComponent.isOpaque()) paintBackground(g); - rootView.paint(g, getVisibleEditorRect()); - - if (highlighter != null) + if (highlighter != null + && textComponent.getSelectionStart() != textComponent.getSelectionEnd()) highlighter.paint(g); + rootView.paint(g, getVisibleEditorRect()); + if (caret != null) caret.paint(g); } protected void paintBackground(Graphics g) { - g.setColor(Color.WHITE); // FIXME: set background color + g.setColor(textComponent.getBackground()); g.fillRect(0, 0, textComponent.getWidth(), textComponent.getHeight()); } @@ -281,13 +411,13 @@ public abstract class BasicTextUI extends TextUI public Rectangle modelToView(JTextComponent t, int pos) throws BadLocationException { - return modelToView(t, pos, null); + return modelToView(t, pos, Position.Bias.Forward); } public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) throws BadLocationException { - return null; + return rootView.modelToView(pos, getVisibleEditorRect(), bias).getBounds(); } public int viewToModel(JTextComponent t, Point pt) @@ -331,4 +461,11 @@ public abstract class BasicTextUI extends TextUI rootView.setView(view); view.setParent(rootView); } + + protected void modelChanged() + { + ViewFactory factory = rootView.getViewFactory(); + Element elem = textComponent.getDocument().getDefaultRootElement(); + setView(factory.create(elem)); + } } diff --git a/javax/swing/plaf/basic/BasicToggleButtonUI.java b/javax/swing/plaf/basic/BasicToggleButtonUI.java index a319b0b4e..1dc6423ee 100644 --- a/javax/swing/plaf/basic/BasicToggleButtonUI.java +++ b/javax/swing/plaf/basic/BasicToggleButtonUI.java @@ -43,12 +43,10 @@ import javax.swing.plaf.ComponentUI; public class BasicToggleButtonUI extends BasicButtonUI { - - public static ComponentUI createUI(final JComponent c) { - return new BasicToggleButtonUI(); - } -} - - + public static ComponentUI createUI(final JComponent component) + { + return new BasicToggleButtonUI(); + } +} diff --git a/javax/swing/plaf/basic/BasicToolBarUI.java b/javax/swing/plaf/basic/BasicToolBarUI.java index 6bffee0d5..85366a8f8 100644 --- a/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/javax/swing/plaf/basic/BasicToolBarUI.java @@ -157,10 +157,8 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants /** * This method creates a new BasicToolBarUI object for the given JToolBar. - * - * @param b The JToolBar to represent with this UI. */ - public BasicToolBarUI(JToolBar b) + public BasicToolBarUI() { super(); } @@ -353,7 +351,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public static ComponentUI createUI(JComponent c) { - return new BasicToolBarUI((JToolBar) c); + return new BasicToolBarUI(); } /** diff --git a/javax/swing/plaf/basic/ComboPopup.java b/javax/swing/plaf/basic/ComboPopup.java new file mode 100644 index 000000000..5b4f282ed --- /dev/null +++ b/javax/swing/plaf/basic/ComboPopup.java @@ -0,0 +1,46 @@ +/* ComboPopup.java + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.plaf.basic; + +public interface ComboPopup +{ + void hide(); + void show(); + boolean isVisible(); +} diff --git a/javax/swing/text/AbstractDocument.java b/javax/swing/text/AbstractDocument.java index 322deb06a..cc439f3b8 100644 --- a/javax/swing/text/AbstractDocument.java +++ b/javax/swing/text/AbstractDocument.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.text; import java.io.Serializable; +import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; import java.util.EventListener; @@ -63,33 +64,37 @@ public abstract class AbstractDocument public static final String ParagraphElementName = "paragraph"; public static final String SectionElementName = "section"; public static final String ElementNameAttribute = "$ename"; + Content content; + AttributeContext context; protected EventListenerList listenerList = new EventListenerList(); protected AbstractDocument(Content doc) { - this(doc, null); + this(doc, StyleContext.getDefaultStyleContext()); } - protected AbstractDocument(Content doc, AttributeContext context) + protected AbstractDocument(Content doc, AttributeContext ctx) { content = doc; + context = ctx; } - // these still need to be implemented by a derived class: + // These still need to be implemented by a derived class: public abstract Element getParagraphElement(int pos); public abstract Element getDefaultRootElement(); - protected Element createBranchElement(Element parent, AttributeSet a) + protected Element createBranchElement(Element parent, + AttributeSet attributes) { - return new BranchElement(parent, a, 0, 0); + return new BranchElement(parent, attributes); } - protected Element createLeafElement(Element parent, AttributeSet a, int p0, - int p1) + protected Element createLeafElement(Element parent, AttributeSet attributes, + int start, int end) { - return new LeafElement(parent, a, p0, p1 - p0); + return new LeafElement(parent, attributes, start, end); } public Position createPosition(final int offset) throws BadLocationException @@ -145,7 +150,7 @@ public abstract class AbstractDocument protected AttributeContext getAttributeContext() { - return null; + return context; } public Element getBidiRootElement() @@ -170,7 +175,13 @@ public abstract class AbstractDocument public Position getEndPosition() { - return null; + return new Position() + { + public int getOffset() + { + return getLength(); + } + }; } public int getLength() @@ -190,12 +201,20 @@ public abstract class AbstractDocument public Element[] getRootElements() { - return null; + Element[] elements = new Element[1]; + elements[0] = getDefaultRootElement(); + return elements; } public Position getStartPosition() { - return null; + return new Position() + { + public int getOffset() + { + return 0; + } + }; } public String getText(int offset, int length) throws BadLocationException @@ -203,34 +222,25 @@ public abstract class AbstractDocument return content.getString(offset, length); } - public void getText(int offset, int length, Segment txt) + public void getText(int offset, int length, Segment segment) throws BadLocationException { - String a = getText(offset, length); - - if (a == null) - { - txt.offset = 0; - txt.count = 0; - txt.array = new char[0]; - - return; - } - - txt.offset = offset; - txt.count = length; - - char[] chars = new char[a.length()]; - - a.getChars(0, a.length(), chars, 0); - - txt.array = chars; + content.getChars(offset, length, segment); } - public void insertString(int offs, String str, AttributeSet a) + public void insertString(int offset, String text, AttributeSet attributes) throws BadLocationException { - content.insertString(offs, str); + // Just return when no text to insert was given. + if (text == null || text.length() == 0) + return; + + DefaultDocumentEvent event = + new DefaultDocumentEvent(offset, text.length(), + DocumentEvent.EventType.INSERT); + content.insertString(offset, text); + insertUpdate(event, attributes); + fireInsertUpdate(event); } protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) @@ -255,6 +265,26 @@ public abstract class AbstractDocument public void remove(int offset, int length) throws BadLocationException { + DefaultDocumentEvent event = + new DefaultDocumentEvent(offset, length, + DocumentEvent.EventType.REMOVE); + removeUpdate(event); + content.remove(offset, length); + postRemoveUpdate(event); + fireRemoveUpdate(event); + } + + /** + * Replaces some text in the document. + * + * @since 1.4 + */ + public void replace(int offset, int length, String text, + AttributeSet attributes) + throws BadLocationException + { + remove(offset, length); + insertString(offset, text, attributes); } /** @@ -375,27 +405,31 @@ public abstract class AbstractDocument } public abstract class AbstractElement - implements Element, TreeNode, Serializable + implements Element, MutableAttributeSet, TreeNode, Serializable { private static final long serialVersionUID = 1265312733007397733L; int count; int offset; - AttributeSet attr; - Vector elts = new Vector(); - String name; - Element parent; - Vector kids = new Vector(); + + AttributeSet attributes; + + Element element_parent; + Vector element_children; + TreeNode tree_parent; + Vector tree_children; public AbstractElement(Element p, AttributeSet s) { - parent = p; - attr = s; + element_parent = p; + attributes = s; } + // TreeNode implementation + public Enumeration children() { - return kids.elements(); + return Collections.enumeration(tree_children); } public boolean getAllowsChildren() @@ -405,17 +439,17 @@ public abstract class AbstractDocument public TreeNode getChildAt(int index) { - return (TreeNode) kids.elementAt(index); + return (TreeNode) tree_children.get(index); } public int getChildCount() { - return kids.size(); + return tree_children.size(); } public int getIndex(TreeNode node) { - return kids.indexOf(node); + return tree_children.indexOf(node); } public TreeNode getParent() @@ -423,9 +457,94 @@ public abstract class AbstractDocument return tree_parent; } + public abstract boolean isLeaf(); + + + // MutableAttributeSet support + + public void addAttribute(Object name, Object value) + { + attributes = getAttributeContext().addAttribute(attributes, name, value); + } + + public void addAttributes(AttributeSet attrs) + { + attributes = getAttributeContext().addAttributes(attributes, attrs); + } + + public void removeAttribute(Object name) + { + attributes = getAttributeContext().removeAttribute(attributes, name); + } + + public void removeAttributes(AttributeSet attrs) + { + attributes = getAttributeContext().removeAttributes(attributes, attrs); + } + + public void removeAttributes(Enumeration names) + { + attributes = getAttributeContext().removeAttributes(attributes, names); + } + + public void setResolveParent(AttributeSet parent) + { + attributes = getAttributeContext().addAttribute(attributes, ResolveAttribute, parent); + } + + + // AttributeSet interface support + + public boolean containsAttribute(Object name, Object value) + { + return attributes.containsAttribute(name, value); + } + + public boolean containsAttributes(AttributeSet attrs) + { + return attributes.containsAttributes(attrs); + } + + public AttributeSet copyAttributes() + { + return attributes.copyAttributes(); + } + + public Object getAttribute(Object key) + { + return attributes.getAttribute(key); + } + + public int getAttributeCount() + { + return attributes.getAttributeCount(); + } + + public Enumeration getAttributeNames() + { + return attributes.getAttributeNames(); + } + + public AttributeSet getResolveParent() + { + return attributes.getResolveParent(); + } + + public boolean isDefined(Object attrName) + { + return attributes.isDefined(attrName); + } + + public boolean isEqual(AttributeSet attrs) + { + return attributes.isEqual(attrs); + } + + // Element interface support + public AttributeSet getAttributes() { - return attr; + return attributes; } public Document getDocument() @@ -435,21 +554,19 @@ public abstract class AbstractDocument public Element getElement(int index) { - return (Element) elts.elementAt(index); + return (Element) element_children.get(index); } public String getName() { - return name; + return (String) getAttribute(NameAttribute); } public Element getParentElement() { - return parent; + return element_parent; } - public abstract boolean isLeaf(); - public abstract int getEndOffset(); public abstract int getElementCount(); @@ -462,16 +579,12 @@ public abstract class AbstractDocument public class BranchElement extends AbstractElement { private static final long serialVersionUID = -8595176318868717313L; - private int start; - private int end; + private Vector children = new Vector(); - public BranchElement(Element parent, AttributeSet attributes, int start, - int end) + public BranchElement(Element parent, AttributeSet attributes) { super(parent, attributes); - this.start = start; - this.end = end; } public Enumeration children() @@ -486,6 +599,9 @@ public abstract class AbstractDocument public Element getElement(int index) { + if (index < 0 || index >= children.size()) + return null; + return (Element) children.get(index); } @@ -496,12 +612,20 @@ public abstract class AbstractDocument public int getElementIndex(int offset) { - return children.indexOf(positionToElement(offset)); + if (children.size() == 0) + return 0; + + Element element = positionToElement(offset); + + if (element == null) + return 0; + + return children.indexOf(element); } public int getEndOffset() { - return end; + return ((Element) children.lastElement()).getEndOffset(); } public String getName() @@ -511,7 +635,7 @@ public abstract class AbstractDocument public int getStartOffset() { - return start; + return ((Element) children.firstElement()).getStartOffset(); } public boolean isLeaf() @@ -554,8 +678,18 @@ public abstract class AbstractDocument implements DocumentEvent { private static final long serialVersionUID = -7406103236022413522L; - public int len; - public int off; + + private int offset; + private int length; + private DocumentEvent.EventType type; + + public DefaultDocumentEvent(int offset, int length, + DocumentEvent.EventType type) + { + this.offset = offset; + this.length = length; + this.type = type; + } public Document getDocument() { @@ -564,17 +698,17 @@ public abstract class AbstractDocument public int getLength() { - return len; + return length; } public int getOffset() { - return off; + return offset; } public DocumentEvent.EventType getType() { - return null; + return type; } public DocumentEvent.ElementChange getChange(Element elem) @@ -584,8 +718,43 @@ public abstract class AbstractDocument } public static class ElementEdit extends AbstractUndoableEdit + implements DocumentEvent.ElementChange { private static final long serialVersionUID = -1216620962142928304L; + + private Element elem; + private int index; + private Element[] removed; + private Element[] added; + + public ElementEdit(Element elem, int index, + Element[] removed, Element[] added) + { + this.elem = elem; + this.index = index; + this.removed = removed; + this.added = added; + } + + public Element[] getChildrenAdded() + { + return added; + } + + public Element[] getChildrenRemoved() + { + return removed; + } + + public Element getElement() + { + return elem; + } + + public int getIndex() + { + return index; + } } public class LeafElement extends AbstractElement diff --git a/javax/swing/text/AttributeSet.java b/javax/swing/text/AttributeSet.java index 19f90a0f4..f37371d54 100644 --- a/javax/swing/text/AttributeSet.java +++ b/javax/swing/text/AttributeSet.java @@ -57,6 +57,9 @@ public interface AttributeSet { } + static Object NameAttribute = StyleConstants.NameAttribute; + static Object ResolveAttribute = StyleConstants.ResolveAttribute; + boolean containsAttribute(Object name, Object value); boolean containsAttributes(AttributeSet attributes); AttributeSet copyAttributes(); diff --git a/javax/swing/text/DefaultCaret.java b/javax/swing/text/DefaultCaret.java index 968bf1ffd..516f0726b 100644 --- a/javax/swing/text/DefaultCaret.java +++ b/javax/swing/text/DefaultCaret.java @@ -61,15 +61,15 @@ public class DefaultCaret extends Rectangle protected ChangeEvent changeEvent = new ChangeEvent(this); protected EventListenerList listenerList = new EventListenerList(); - Color color = new Color(0, 0, 0); - JTextComponent parent; - Point magic = null; - int mark = 0; - boolean vis_sel = true; - int blink = 500; - int dot = 0; - boolean vis = true; - + private JTextComponent textComponent; + + private boolean selectionVisible = true; + private int blinkRate = 0; + private int dot = 0; + private int mark = 0; + private Point magicCaretPosition = null; + private boolean visible = true; + private Object highlightEntry; public void mouseDragged(MouseEvent event) { @@ -117,27 +117,29 @@ public class DefaultCaret extends Rectangle public void deinstall(JTextComponent c) { - parent.removeFocusListener(this); - parent.removeMouseListener(this); - parent = null; + textComponent.removeFocusListener(this); + textComponent.removeMouseListener(this); + textComponent.removeMouseMotionListener(this); + textComponent = null; } public void install(JTextComponent c) { - parent.addFocusListener(this); - parent.addMouseListener(this); - parent = c; + textComponent = c; + textComponent.addFocusListener(this); + textComponent.addMouseListener(this); + textComponent.addMouseMotionListener(this); repaint(); } public void setMagicCaretPosition(Point p) { - magic = p; + magicCaretPosition = p; } public Point getMagicCaretPosition() { - return magic; + return magicCaretPosition; } public int getMark() @@ -145,27 +147,92 @@ public class DefaultCaret extends Rectangle return mark; } + private void handleHighlight() + { + Highlighter highlighter = textComponent.getHighlighter(); + + if (highlighter == null) + return; + + int p0 = Math.min(dot, mark); + int p1 = Math.max(dot, mark); + + if (selectionVisible && p0 != p1) + { + try + { + if (highlightEntry == null) + highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter()); + else + highlighter.changeHighlight(highlightEntry, p0, p1); + } + catch (BadLocationException e) + { + // This should never happen. + throw new InternalError(); + } + } + else + { + if (highlightEntry != null) + { + highlighter.removeHighlight(highlightEntry); + highlightEntry = null; + } + } + } + public void setSelectionVisible(boolean v) { - vis_sel = v; + if (selectionVisible == v) + return; + + selectionVisible = v; + handleHighlight(); repaint(); } public boolean isSelectionVisible() { - return vis_sel; + return selectionVisible; } protected final void repaint() { - if (parent != null) - parent.repaint(); + if (textComponent != null) + textComponent.repaint(); } public void paint(Graphics g) { - g.setColor(color); - g.drawLine(x, y, x, y + height); + if (textComponent == null) + return; + + int dot = getDot(); + Rectangle rect = null; + + try + { + rect = textComponent.modelToView(dot); + } + catch (BadLocationException e) + { + // This should never happen as dot should be always valid. + return; + } + + if (rect == null) + return; + + // First we need to delete the old caret. + // FIXME: Implement deleting of old caret. + + // Now draw the caret on the new position if visible. + if (visible) + { + g.setColor(textComponent.getCaretColor()); + g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height); + } } public EventListener[] getListeners(Class listenerType) @@ -198,17 +265,17 @@ public class DefaultCaret extends Rectangle protected final JTextComponent getComponent() { - return parent; + return textComponent; } public int getBlinkRate() { - return blink; + return blinkRate; } public void setBlinkRate(int rate) { - blink = rate; + blinkRate = rate; } public int getDot() @@ -218,23 +285,32 @@ public class DefaultCaret extends Rectangle public void moveDot(int dot) { - setDot(dot); + this.dot = dot; + handleHighlight(); + repaint(); } public void setDot(int dot) { this.dot = dot; + this.mark = dot; + handleHighlight(); repaint(); } public boolean isVisible() { - return vis; + return visible; } public void setVisible(boolean v) { - vis = v; + visible = v; repaint(); } + + protected Highlighter.HighlightPainter getSelectionPainter() + { + return DefaultHighlighter.DefaultPainter; + } } diff --git a/javax/swing/text/DefaultEditorKit.java b/javax/swing/text/DefaultEditorKit.java index 858c640fc..69d0c1235 100644 --- a/javax/swing/text/DefaultEditorKit.java +++ b/javax/swing/text/DefaultEditorKit.java @@ -64,6 +64,105 @@ public class DefaultEditorKit extends EditorKit } } + public static class CopyAction + extends TextAction + { + public CopyAction() + { + super(copyAction); + } + public void actionPerformed(ActionEvent event) + { + } + } + + public static class CutAction + extends TextAction + { + public CutAction() + { + super(cutAction); + } + public void actionPerformed(ActionEvent event) + { + } + } + + public static class DefaultKeyTypedAction + extends TextAction + { + public DefaultKeyTypedAction() + { + super(defaultKeyTypedAction); + } + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + if (t != null) + { + try + { + t.getDocument().insertString(t.getCaret().getDot(), event.getActionCommand(), null); + t.getCaret().setDot(Math.min(t.getCaret().getDot() + 1, + t.getDocument().getEndPosition().getOffset())); + t.repaint(); + } + catch (BadLocationException be) + { + // FIXME: we're not authorized to throw this.. swallow it? + } + } + } + } + + public static class InsertBreakAction + extends TextAction + { + public InsertBreakAction() + { + super(insertBreakAction); + } + public void actionPerformed(ActionEvent event) + { + } + } + + public static class InsertContentAction + extends TextAction + { + public InsertContentAction() + { + super(insertContentAction); + } + public void actionPerformed(ActionEvent event) + { + } + } + + public static class InsertTabAction + extends TextAction + { + public InsertTabAction() + { + super(insertTabAction); + } + public void actionPerformed(ActionEvent event) + { + } + } + + public static class PasteAction + extends TextAction + { + public PasteAction() + { + super(pasteAction); + } + public void actionPerformed(ActionEvent event) + { + } + } + private static final long serialVersionUID = 9017245433028523428L; public static final String backwardAction = "caret-backward"; @@ -121,6 +220,113 @@ public class DefaultEditorKit extends EditorKit { } + private static Action[] defaultActions = + new Action[] { + new BeepAction(), + new CopyAction(), + new CutAction(), + new DefaultKeyTypedAction(), + new InsertBreakAction(), + new InsertContentAction(), + new InsertTabAction(), + new PasteAction(), + new TextAction(deleteNextCharAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + if (t != null) + { + try + { + int pos = t.getCaret().getDot(); + if (pos < t.getDocument().getEndPosition().getOffset()) + { + t.getDocument().remove(t.getCaret().getDot(), 1); + t.repaint(); + } + } + catch (BadLocationException e) + { + // FIXME: we're not authorized to throw this.. swallow it? + } + } + } + }, + new TextAction(deletePrevCharAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + if (t != null) + { + try + { + int pos = t.getCaret().getDot(); + if (pos > t.getDocument().getStartPosition().getOffset()) + { + t.getDocument().remove(pos - 1, 1); + t.getCaret().setDot(pos - 1); + t.repaint(); + } + } + catch (BadLocationException e) + { + // FIXME: we're not authorized to throw this.. swallow it? + } + } + } + }, + new TextAction(backwardAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + if (t != null) + { + t.getCaret().setDot(Math.max(t.getCaret().getDot() - 1, + t.getDocument().getStartPosition().getOffset())); + } + } + }, + new TextAction(forwardAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + if (t != null) + { + t.getCaret().setDot(Math.min(t.getCaret().getDot() + 1, + t.getDocument().getEndPosition().getOffset())); + } + } + }, + new TextAction(selectionBackwardAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + if (t != null) + { + t.getCaret().moveDot(Math.max(t.getCaret().getDot() - 1, + t.getDocument().getStartPosition().getOffset())); + } + } + }, + new TextAction(selectionForwardAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + if (t != null) + { + t.getCaret().moveDot(Math.min(t.getCaret().getDot() + 1, + t.getDocument().getEndPosition().getOffset())); + } + } + }, + }; + /** * Called when the kit is being removed from the JEditorPane. */ @@ -134,7 +340,7 @@ public class DefaultEditorKit extends EditorKit public Caret createCaret() { - return null; + return new DefaultCaret(); } public Document createDefaultDocument() @@ -144,7 +350,7 @@ public class DefaultEditorKit extends EditorKit public Action[] getActions() { - return null; + return defaultActions; } public String getContentType() diff --git a/javax/swing/text/DefaultHighlighter.java b/javax/swing/text/DefaultHighlighter.java index 003d17d1a..234fcad60 100644 --- a/javax/swing/text/DefaultHighlighter.java +++ b/javax/swing/text/DefaultHighlighter.java @@ -38,7 +38,9 @@ exception statement from your version. */ package javax.swing.text; +import java.awt.Color; import java.awt.Graphics; +import java.awt.Rectangle; import java.awt.Shape; import java.util.Vector; @@ -48,6 +50,87 @@ import javax.swing.text.View; public class DefaultHighlighter extends LayeredHighlighter { + public static class DefaultHighlightPainter + extends LayerPainter + { + private Color color; + + public DefaultHighlightPainter(Color c) + { + super(); + color = c; + } + + public Color getColor() + { + return color; + } + + private void paintHighlight(Graphics g, Rectangle rect) + { + g.fillRect(rect.x, rect.y, rect.width, rect.height); + } + + public void paint(Graphics g, int p0, int p1, Shape bounds, + JTextComponent c) + { + Rectangle r0 = null; + Rectangle r1 = null; + Rectangle rect = bounds.getBounds(); + + try + { + r0 = c.modelToView(p0); + r1 = c.modelToView(p1); + } + catch (BadLocationException e) + { + // This should never occur. + return; + } + + if (r0 == null || r1 == null) + return; + + if (color == null) + g.setColor(c.getSelectionColor()); + else + g.setColor(color); + + // Check if only one line to highlight. + if (r0.y == r1.y) + { + r0.width = r1.x - r0.x; + paintHighlight(g, r0); + return; + } + + // First line, from p0 to end-of-line. + r0.width = rect.x + rect.width - r0.x; + paintHighlight(g, r0); + + // FIXME: All the full lines in between, if any (assumes that all lines + // have the same height -- not a good assumption with JEditorPane/JTextPane). + r0.y += r0.height; + r0.x = rect.x; + + while (r0.y < r1.y) + { + paintHighlight(g, r0); + r0.y += r0.height; + } + + // Last line, from beginnin-of-line to p1. + paintHighlight(g, r1); + } + + public Shape paintLayer(Graphics g, int p0, int p1, Shape bounds, + JTextComponent c, View view) + { + throw new InternalError(); + } + } + private class HighlightEntry { int p0; @@ -77,6 +160,12 @@ public class DefaultHighlighter extends LayeredHighlighter } } + /** + * @specnote final as of 1.4 + */ + public static final LayeredHighlighter.LayerPainter DefaultPainter = + new DefaultHighlightPainter(null); + private JTextComponent textComponent; private Vector highlights = new Vector(); @@ -146,5 +235,16 @@ public class DefaultHighlighter extends LayeredHighlighter public void paint(Graphics g) { + // Check if there are any highlights. + if (highlights.size() == 0) + return; + + Shape bounds = textComponent.getBounds(); + + for (int index = 0; index < highlights.size(); ++index) + { + HighlightEntry entry = (HighlightEntry) highlights.get(index); + entry.painter.paint(g, entry.p0, entry.p1, bounds, textComponent); + } } } diff --git a/javax/swing/text/FieldView.java b/javax/swing/text/FieldView.java index 29a00acfa..06514b73f 100644 --- a/javax/swing/text/FieldView.java +++ b/javax/swing/text/FieldView.java @@ -90,8 +90,14 @@ public class FieldView extends PlainView return axis = axis == X_AXIS ? 1 : 0; } + public Shape modelToView(int pos, Shape a, Position.Bias bias) + throws BadLocationException + { + return super.modelToView(pos, a, bias); + } + public void paint(Graphics g, Shape s) { - drawLine(0, g, 0, 0); + super.paint(g, s); } } diff --git a/javax/swing/text/JTextComponent.java b/javax/swing/text/JTextComponent.java index e44c5296d..edc73a02e 100644 --- a/javax/swing/text/JTextComponent.java +++ b/javax/swing/text/JTextComponent.java @@ -46,13 +46,20 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.InputMethodListener; +import java.awt.event.KeyEvent; + +import java.util.Enumeration; +import java.util.Hashtable; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleStateSet; import javax.accessibility.AccessibleText; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.Icon; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JViewport; import javax.swing.KeyStroke; @@ -62,6 +69,8 @@ import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TextUI; @@ -283,11 +292,505 @@ public abstract class JTextComponent extends JComponent } } + /** + * According to <a + * href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this + * report</a>, a pair of private classes wraps a {@link + * javax.swing.text.Keymap} in the new {@link InputMap} / {@link + * ActionMap} interfaces, such that old Keymap-using code can make use of + * the new framework.</p> + * + * <p>A little bit of experimentation with these classes reveals the following + * structure: + * + * <ul> + * + * <li>KeymapWrapper extends {@link InputMap} and holds a reference to + * the underlying {@link Keymap}.</li> + * + * <li>KeymapWrapper maps {@link KeyStroke} objects to {@link Action} + * objects, by delegation to the underlying {@link Keymap}.</li> + * + * <li>KeymapActionMap extends {@link ActionMap} also holds a reference to + * the underlying {@link Keymap} but only appears to use it for listing + * its keys. </li> + * + * <li>KeymapActionMap maps all {@link Action} objects to + * <em>themselves</em>, whether they exist in the underlying {@link + * Keymap} or not, and passes other objects to the parent {@link + * ActionMap} for resolving. + * + * </ul> + */ + + private class KeymapWrapper extends InputMap + { + Keymap map; + + public KeymapWrapper(Keymap k) + { + map = k; + } + + public int size() + { + return map.getBoundKeyStrokes().length + super.size(); + } + + public Object get(KeyStroke ks) + { + Action mapped = null; + Keymap m = map; + while(mapped == null && m != null) + { + mapped = m.getAction(ks); + if (mapped == null && ks.getKeyEventType() == KeyEvent.KEY_TYPED) + mapped = m.getDefaultAction(); + if (mapped == null) + m = m.getResolveParent(); + } + + if (mapped == null) + return super.get(ks); + else + return mapped; + } + + public KeyStroke[] keys() + { + KeyStroke[] superKeys = super.keys(); + KeyStroke[] mapKeys = map.getBoundKeyStrokes(); + KeyStroke[] bothKeys = new KeyStroke[superKeys.length + mapKeys.length]; + for (int i = 0; i < superKeys.length; ++i) + bothKeys[i] = superKeys[i]; + for (int i = 0; i < mapKeys.length; ++i) + bothKeys[i + superKeys.length] = mapKeys[i]; + return bothKeys; + } + + public KeyStroke[] allKeys() + { + KeyStroke[] superKeys = super.allKeys(); + KeyStroke[] mapKeys = map.getBoundKeyStrokes(); + KeyStroke[] bothKeys = new KeyStroke[superKeys.length + mapKeys.length]; + for (int i = 0; i < superKeys.length; ++i) + bothKeys[i] = superKeys[i]; + for (int i = 0; i < mapKeys.length; ++i) + bothKeys[i + superKeys.length] = mapKeys[i]; + return bothKeys; + } + } + + private class KeymapActionMap extends ActionMap + { + Keymap map; + + public KeymapActionMap(Keymap k) + { + map = k; + } + + public Action get(Object cmd) + { + if (cmd instanceof Action) + return (Action) cmd; + else + return super.get(cmd); + } + + public int size() + { + return map.getBoundKeyStrokes().length + super.size(); + } + + public Object[] keys() + { + Object[] superKeys = super.keys(); + Object[] mapKeys = map.getBoundKeyStrokes(); + Object[] bothKeys = new Object[superKeys.length + mapKeys.length]; + for (int i = 0; i < superKeys.length; ++i) + bothKeys[i] = superKeys[i]; + for (int i = 0; i < mapKeys.length; ++i) + bothKeys[i + superKeys.length] = mapKeys[i]; + return bothKeys; + } + + public Object[] allKeys() + { + Object[] superKeys = super.allKeys(); + Object[] mapKeys = map.getBoundKeyStrokes(); + Object[] bothKeys = new Object[superKeys.length + mapKeys.length]; + for (int i = 0; i < superKeys.length; ++i) + bothKeys[i] = superKeys[i]; + for (int i = 0; i < mapKeys.length; ++i) + bothKeys[i + superKeys.length] = mapKeys[i]; + return bothKeys; + } + + } + + static class DefaultKeymap implements Keymap + { + String name; + Keymap parent; + Hashtable map; + Action defaultAction; + + public DefaultKeymap(String name) + { + this.name = name; + this.map = new Hashtable(); + } + + public void addActionForKeyStroke(KeyStroke key, Action a) + { + map.put(key, a); + } + + /** + * Looks up a KeyStroke either in the current map or the parent Keymap; + * does <em>not</em> return the default action if lookup fails. + * + * @param key The KeyStroke to look up an Action for. + * + * @return The mapping for <code>key</code>, or <code>null</code> + * if no mapping exists in this Keymap or any of its parents. + */ + public Action getAction(KeyStroke key) + { + if (map.containsKey(key)) + return (Action) map.get(key); + else if (parent != null) + return parent.getAction(key); + else + return null; + } + + public Action[] getBoundActions() + { + Action [] ret = new Action[map.size()]; + Enumeration e = map.elements(); + int i = 0; + while (e.hasMoreElements()) + { + ret[i++] = (Action) e.nextElement(); + } + return ret; + } + + public KeyStroke[] getBoundKeyStrokes() + { + KeyStroke [] ret = new KeyStroke[map.size()]; + Enumeration e = map.keys(); + int i = 0; + while (e.hasMoreElements()) + { + ret[i++] = (KeyStroke) e.nextElement(); + } + return ret; + } + + public Action getDefaultAction() + { + return defaultAction; + } + + public KeyStroke[] getKeyStrokesForAction(Action a) + { + int i = 0; + Enumeration e = map.keys(); + while (e.hasMoreElements()) + { + if (map.get(e.nextElement()).equals(a)) + ++i; + } + KeyStroke [] ret = new KeyStroke[i]; + i = 0; + e = map.keys(); + while (e.hasMoreElements()) + { + KeyStroke k = (KeyStroke) e.nextElement(); + if (map.get(k).equals(a)) + ret[i++] = k; + } + return ret; + } + + public String getName() + { + return name; + } + + public Keymap getResolveParent() + { + return parent; + } + + public boolean isLocallyDefined(KeyStroke key) + { + return map.containsKey(key); + } + + public void removeBindings() + { + map.clear(); + } + + public void removeKeyStrokeBinding(KeyStroke key) + { + map.remove(key); + } + + public void setDefaultAction(Action a) + { + defaultAction = a; + } + + public void setResolveParent(Keymap p) + { + parent = p; + } + + } + private static final long serialVersionUID = -8796518220218978795L; public static final String DEFAULT_KEYMAP = "default"; public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey"; + private static Hashtable keymaps = new Hashtable(); + private Keymap keymap; + + /** + * Get a Keymap from the global keymap table, by name. + * + * @param n The name of the Keymap to look up + * + * @return A Keymap associated with the provided name, or + * <code>null</code> if no such Keymap exists + * + * @see #addKeymap() + * @see #removeKeymap() + * @see #keymaps + */ + public static Keymap getKeymap(String n) + { + return (Keymap) keymaps.get(n); + } + + /** + * Remove a Keymap from the global Keymap table, by name. + * + * @param n The name of the Keymap to remove + * + * @return The keymap removed from the global table + * + * @see #addKeymap() + * @see #getKeymap() + * @see #keymaps + */ + public static Keymap removeKeymap(String n) + { + Keymap km = (Keymap) keymaps.get(n); + keymaps.remove(n); + return km; + } + + /** + * Create a new Keymap with a specific name and parent, and add the new + * Keymap to the global keymap table. The name may be <code>null</code>, + * in which case the new Keymap will <em>not</em> be added to the global + * Keymap table. The parent may also be <code>null</code>, which is + * harmless. + * + * @param n The name of the new Keymap, or <code>null</code> + * @param parent The parent of the new Keymap, or <code>null</code> + * + * @return The newly created Keymap + * + * @see #removeKeymap() + * @see #getKeymap() + * @see #keymaps + */ + public static Keymap addKeymap(String n, Keymap parent) + { + Keymap k = new DefaultKeymap(n); + k.setResolveParent(parent); + if (n != null) + keymaps.put(n, k); + return k; + } + + /** + * Get the current Keymap of this component. + * + * @return The component's current Keymap + * + * @see #setKeymap() + * @see #keymap + */ + Keymap getKeymap() + { + return keymap; + } + + /** + * Set the current Keymap of this component, installing appropriate + * {@link KeymapWrapper} and {@link KeymapActionMap} objects in the + * {@link InputMap} and {@link ActionMap} parent chains, respectively, + * and fire a property change event with name <code>"keymap"</code>. + * + * @see #getKeymap() + * @see #keymap + */ + public void setKeymap(Keymap k) + { + + // phase 1: replace the KeymapWrapper entry in the InputMap chain. + // the goal here is to always maintain the following ordering: + // + // [InputMap]? -> [KeymapWrapper]? -> [InputMapUIResource]* + // + // that is to say, component-specific InputMaps need to remain children + // of Keymaps, and Keymaps need to remain children of UI-installed + // InputMaps (and the order of each group needs to be preserved, of + // course). + + KeymapWrapper kw = (k == null ? null : new KeymapWrapper(k)); + InputMap childInputMap = getInputMap(JComponent.WHEN_FOCUSED); + if (childInputMap == null) + setInputMap(JComponent.WHEN_FOCUSED, kw); + else + { + while (childInputMap.getParent() != null + && !(childInputMap.getParent() instanceof KeymapWrapper) + && !(childInputMap.getParent() instanceof InputMapUIResource)) + childInputMap = childInputMap.getParent(); + + // option 1: there is nobody to replace at the end of the chain + if (childInputMap.getParent() == null) + childInputMap.setParent(kw); + + // option 2: there is already a KeymapWrapper in the chain which + // needs replacing (possibly with its own parents, possibly without) + else if (childInputMap.getParent() instanceof KeymapWrapper) + { + if (kw == null) + childInputMap.setParent(childInputMap.getParent().getParent()); + else + { + kw.setParent(childInputMap.getParent().getParent()); + childInputMap.setParent(kw); + } + } + + // option 3: there is an InputMapUIResource in the chain, which marks + // the place where we need to stop and insert ourselves + else if (childInputMap.getParent() instanceof InputMapUIResource) + { + if (kw != null) + { + kw.setParent(childInputMap.getParent()); + childInputMap.setParent(kw); + } + } + } + + // phase 2: replace the KeymapActionMap entry in the ActionMap chain + + KeymapActionMap kam = (k == null ? null : new KeymapActionMap(k)); + ActionMap childActionMap = getActionMap(); + if (childActionMap == null) + setActionMap(kam); + else + { + while (childActionMap.getParent() != null + && !(childActionMap.getParent() instanceof KeymapActionMap) + && !(childActionMap.getParent() instanceof ActionMapUIResource)) + childActionMap = childActionMap.getParent(); + + // option 1: there is nobody to replace at the end of the chain + if (childActionMap.getParent() == null) + childActionMap.setParent(kam); + + // option 2: there is already a KeymapActionMap in the chain which + // needs replacing (possibly with its own parents, possibly without) + else if (childActionMap.getParent() instanceof KeymapActionMap) + { + if (kam == null) + childActionMap.setParent(childActionMap.getParent().getParent()); + else + { + kam.setParent(childActionMap.getParent().getParent()); + childActionMap.setParent(kam); + } + } + + // option 3: there is an ActionMapUIResource in the chain, which marks + // the place where we need to stop and insert ourselves + else if (childActionMap.getParent() instanceof ActionMapUIResource) + { + if (kam != null) + { + kam.setParent(childActionMap.getParent()); + childActionMap.setParent(kam); + } + } + } + + // phase 3: update the explicit keymap field + + Keymap old = keymap; + keymap = k; + firePropertyChange("keymap", old, k); + } + + /** + * Resolves a set of bindings against a set of actions and inserts the + * results into a {@link Keymap}. Specifically, for each provided binding + * <code>b</code>, if there exists a provided action <code>a</code> such + * that <code>a.getValue(Action.NAME) == b.ActionName</code> then an + * entry is added to the Keymap mapping <code>b</code> to + * </code>a</code>. + * + * @param map The Keymap to add new mappings to + * @param bindings The set of bindings to add to the Keymap + * @param actions The set of actions to resolve binding names against + * + * @see Action#NAME + * @see Action#getValue() + * @see KeyBinding#ActionName + */ + public static void loadKeymap(Keymap map, + JTextComponent.KeyBinding[] bindings, + Action[] actions) + { + Hashtable acts = new Hashtable(actions.length); + for (int i = 0; i < actions.length; ++i) + acts.put(actions[i].getValue(Action.NAME), actions[i]); + for (int i = 0; i < bindings.length; ++i) + if (acts.containsKey(bindings[i].actionName)) + map.addActionForKeyStroke(bindings[i].key, (Action) acts.get(bindings[i].actionName)); + } + + /** + * Returns the set of available Actions this component's associated + * editor can run. Equivalent to calling + * <code>getUI().getEditorKit().getActions()</code>. This set of Actions + * is a reasonable value to provide as a parameter to {@link + * #loadKeymap()}, when resolving a set of {@link #KeyBinding} objects + * against this component. + * + * @return The set of available Actions on this component's {@link EditorKit} + * + * @see TextUI#getEditorKit() + * @see EditorKit#getActions() + */ + public Action[] getActions() + { + return getUI().getEditorKit(this).getActions(); + } + private Document doc; private Caret caret; private Highlighter highlighter; @@ -296,19 +799,47 @@ public abstract class JTextComponent extends JComponent private Color selectedTextColor; private Color selectionColor; private boolean editable; + private Insets margin; /** * Creates a new <code>JTextComponent</code> instance. */ public JTextComponent() { + Keymap defkeymap = getKeymap(DEFAULT_KEYMAP); + boolean creatingKeymap = false; + if (defkeymap == null) + { + defkeymap = addKeymap(DEFAULT_KEYMAP, null); + defkeymap.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction()); + creatingKeymap = true; + } + + setFocusable(true); enableEvents(AWTEvent.KEY_EVENT_MASK); updateUI(); + + // need to do this after updateUI() + if (creatingKeymap) + loadKeymap(defkeymap, + new KeyBinding[] { + new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), + DefaultEditorKit.backwardAction), + new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), + DefaultEditorKit.forwardAction), + new KeyBinding(KeyStroke.getKeyStroke("typed \b"), + DefaultEditorKit.deletePrevCharAction), + new KeyBinding(KeyStroke.getKeyStroke("typed \u007f"), + DefaultEditorKit.deleteNextCharAction) + }, + getActions()); } - public void setDocument(Document s) + public void setDocument(Document newDoc) { - doc = s; + Document oldDoc = doc; + doc = newDoc; + firePropertyChange("document", oldDoc, newDoc); revalidate(); repaint(); } @@ -328,18 +859,22 @@ public abstract class JTextComponent extends JComponent return null; } + public void setMargin(Insets m) + { + margin = m; + } + public Insets getMargin() { - // FIXME: Not implemented. - return null; + return margin; } public void setText(String text) { try { - getDocument().remove(0, doc.getLength()); - getDocument().insertString(0, text, null); + doc.remove(0, doc.getLength()); + doc.insertString(0, text, null); } catch (BadLocationException e) { @@ -488,8 +1023,16 @@ public abstract class JTextComponent extends JComponent */ public void setCaret(Caret newCaret) { - firePropertyChange("caret", caret, newCaret); + if (caret != null) + caret.deinstall(this); + + Caret oldCaret = caret; caret = newCaret; + + if (caret != null) + caret.install(this); + + firePropertyChange("caret", oldCaret, newCaret); } public Color getCaretColor() @@ -499,8 +1042,9 @@ public abstract class JTextComponent extends JComponent public void setCaretColor(Color newColor) { - firePropertyChange("caretColor", caretColor, newColor); + Color oldCaretColor = caretColor; caretColor = newColor; + firePropertyChange("caretColor", oldCaretColor, newColor); } public Color getDisabledTextColor() @@ -510,8 +1054,9 @@ public abstract class JTextComponent extends JComponent public void setDisabledTextColor(Color newColor) { - firePropertyChange("disabledTextColor", caretColor, newColor); + Color oldColor = disabledTextColor; disabledTextColor = newColor; + firePropertyChange("disabledTextColor", oldColor, newColor); } public Color getSelectedTextColor() @@ -521,8 +1066,9 @@ public abstract class JTextComponent extends JComponent public void setSelectedTextColor(Color newColor) { - firePropertyChange("selectedTextColor", caretColor, newColor); + Color oldColor = selectedTextColor; selectedTextColor = newColor; + firePropertyChange("selectedTextColor", oldColor, newColor); } public Color getSelectionColor() @@ -532,8 +1078,9 @@ public abstract class JTextComponent extends JComponent public void setSelectionColor(Color newColor) { - firePropertyChange("selectionColor", caretColor, newColor); + Color oldColor = selectionColor; selectionColor = newColor; + firePropertyChange("selectionColor", oldColor, newColor); } /** @@ -584,8 +1131,16 @@ public abstract class JTextComponent extends JComponent public void setHighlighter(Highlighter newHighlighter) { - firePropertyChange("highlighter", highlighter, newHighlighter); + if (highlighter != null) + highlighter.deinstall(this); + + Highlighter oldHighlighter = highlighter; highlighter = newHighlighter; + + if (highlighter != null) + highlighter.install(this); + + firePropertyChange("highlighter", oldHighlighter, newHighlighter); } /** @@ -773,4 +1328,9 @@ public abstract class JTextComponent extends JComponent { return (InputMethodListener[]) getListeners(InputMethodListener.class); } + + public Rectangle modelToView(int position) throws BadLocationException + { + return getUI().modelToView(this, position); + } } diff --git a/javax/swing/text/PlainView.java b/javax/swing/text/PlainView.java index 229c7e193..3dd8fe448 100644 --- a/javax/swing/text/PlainView.java +++ b/javax/swing/text/PlainView.java @@ -39,6 +39,8 @@ exception statement from your version. */ package javax.swing.text; import java.awt.Color; +import java.awt.Component; +import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; @@ -48,12 +50,70 @@ import java.awt.Shape; public class PlainView extends View implements TabExpander { + private Color selectedColor; + private Color unselectedColor; + private Font font; + protected FontMetrics metrics; public PlainView(Element elem) { super(elem); } + + /** + * @since 1.4 + */ + protected void updateMetrics() + { + Component component = getContainer(); + Font font = component.getFont(); + + if (this.font != font) + { + this.font = font; + metrics = component.getFontMetrics(font); + } + } + + /** + * @since 1.4 + */ + protected Rectangle lineToRect(Shape a, int line) + { + // Ensure metrics are up-to-date. + updateMetrics(); + + Rectangle rect = a.getBounds(); + int fontHeight = metrics.getHeight(); + return new Rectangle(rect.x, rect.y + (line * fontHeight), + rect.width, fontHeight); + } + + public Shape modelToView(int position, Shape a, Position.Bias b) + throws BadLocationException + { + Document document = getDocument(); + + // Get rectangle of the line containing position. + int lineIndex = getElement().getElementIndex(position); + Rectangle rect = lineToRect(a, lineIndex); + + // Get the rectangle for position. + Element line = getElement().getElement(lineIndex); + int lineStart = line.getStartOffset(); + Segment segment = new Segment(); + document.getText(lineStart, position - lineStart, segment); + int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x, + this, lineStart); + + // Calc the real rectangle. + rect.x += xoffset; + rect.width = 1; + rect.height = metrics.getHeight(); + + return rect; + } public void drawLine(int lineIndex, Graphics g, int x, int y) { @@ -73,30 +133,31 @@ public class PlainView extends View public int drawSelectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { - String text = getDocument().getText(p0, p1); - g.setColor(Color.WHITE); - g.drawString(text, x, y); - return metrics.stringWidth(text); + g.setColor(selectedColor); + Segment segment = new Segment(); + getDocument().getText(p0, p1 - p0, segment); + return Utilities.drawTabbedText(segment, x, y, g, this, 0); } public int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { - String text = getDocument().getText(p0, p1); - g.setColor(Color.BLACK); - g.drawString(text, x, y); - return metrics.stringWidth(text); + g.setColor(unselectedColor); + Segment segment = new Segment(); + getDocument().getText(p0, p1 - p0, segment); + return Utilities.drawTabbedText(segment, x, y, g, this, 0); } public void paint(Graphics g, Shape s) { - System.out.println("Michael: PlainView.paint"); + JTextComponent textComponent = (JTextComponent) getContainer(); + + g.setFont(textComponent.getFont()); + selectedColor = textComponent.getSelectedTextColor(); + unselectedColor = textComponent.getForeground(); Rectangle rect = s.getBounds(); - g.setColor(Color.WHITE); - g.fillRect(rect.x, rect.y, rect.width, rect.height); - // FIXME: Text may be scrolled. drawLine(0, g, rect.x, rect.y); } @@ -106,10 +167,18 @@ public class PlainView extends View return 8; } + /** + * Returns the next tab stop position after a given reference position. + * + * This implementation ignores the <code>tabStop</code> argument. + * + * @param x the current x position in pixels + * @param tabStop the position within the text stream that the tab occured at + */ public float nextTabStop(float x, int tabStop) { - System.out.println("Michael: PlainView.nextTabpStop: missing implementation"); - return x; + float tabSizePixels = getTabSize() + metrics.charWidth('m'); + return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels; } public float getPreferredSpan(int axis) @@ -119,4 +188,5 @@ public class PlainView extends View return 10; } -}
\ No newline at end of file +} + diff --git a/javax/swing/text/Segment.java b/javax/swing/text/Segment.java index e23cd5a40..3b1d9e7aa 100644 --- a/javax/swing/text/Segment.java +++ b/javax/swing/text/Segment.java @@ -39,11 +39,11 @@ package javax.swing.text; import java.text.CharacterIterator; - public class Segment implements Cloneable, CharacterIterator { private boolean partialReturn; + private int current; public char[] array; public int count; @@ -74,13 +74,20 @@ public class Segment public char current() { - return array[getIndex()]; + if (count == 0 + || current >= getEndIndex()) + return DONE; + + return array[current]; } public char first() { - offset = getBeginIndex(); - return array[offset]; + if (count == 0) + return DONE; + + current = getBeginIndex(); + return array[current]; } public int getBeginIndex() @@ -95,31 +102,55 @@ public class Segment public int getIndex() { - return offset; + return current; } public char last() { - offset = getEndIndex() - 1; - return array[offset]; + if (count == 0) + return DONE; + + current = getEndIndex() - 1; + return array[current]; } public char next() { - offset++; - return array[offset]; + if (count == 0) + return DONE; + + if ((current + 1) >= getEndIndex()) + { + current = getEndIndex(); + return DONE; + } + + current++; + return array[current]; } public char previous() { - offset--; - return array[offset]; + if (count == 0 + || current == getBeginIndex()) + return DONE; + + current--; + return array[current]; } public char setIndex(int position) { - offset = position; - return array[offset]; + if (position < getBeginIndex() + || position > getEndIndex()) + throw new IllegalArgumentException(); + + current = position; + + if (position == getEndIndex()) + return DONE; + + return array[current]; } public String toString() diff --git a/javax/swing/text/SimpleAttributeSet.java b/javax/swing/text/SimpleAttributeSet.java new file mode 100644 index 000000000..746056dfe --- /dev/null +++ b/javax/swing/text/SimpleAttributeSet.java @@ -0,0 +1,192 @@ +/* SimpleAttributeSet.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.text; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Hashtable; +import java.lang.Cloneable; + +public class SimpleAttributeSet + implements MutableAttributeSet, Serializable, Cloneable +{ + Hashtable tab; + + static AttributeSet EMPTY = new SimpleAttributeSet(); + + public SimpleAttributeSet() + { + this(null); + } + + public SimpleAttributeSet(AttributeSet a) + { + tab = new Hashtable(); + addAttributes(a); + } + + public void addAttribute(Object name, Object value) + { + tab.put(name, value); + } + + public void addAttributes(AttributeSet attributes) + { + Enumeration e = attributes.getAttributeNames(); + while (e.hasMoreElements()) + { + Object name = e.nextElement(); + Object val = attributes.getAttribute(name); + tab.put(name, val); + } + } + + public Object clone() + { + SimpleAttributeSet s = new SimpleAttributeSet(); + s.tab = (Hashtable) tab.clone(); + return s; + } + + public boolean containsAttribute(Object name, Object value) + { + return tab.containsKey(name) + && tab.get(name).equals(value); + } + + public boolean containsAttributes(AttributeSet attributes) + { + Enumeration e = attributes.getAttributeNames(); + while (e.hasMoreElements()) + { + Object name = e.nextElement(); + Object val = attributes.getAttribute(name); + if (! containsAttribute(name, val)) + return false; + } + return true; + } + + public AttributeSet copyAttributes() + { + return (AttributeSet) clone(); + } + + public boolean equals(Object obj) + { + return (obj != null) + && (obj instanceof SimpleAttributeSet) + && ((SimpleAttributeSet)obj).tab.equals(this.tab); + } + + public Object getAttribute(Object name) + { + Object val = tab.get(name); + if (val != null) + return val; + + Object p = getResolveParent(); + if (p != null && p instanceof AttributeSet) + return (((AttributeSet)p).getAttribute(name)); + + return null; + } + + public int getAttributeCount() + { + return tab.size(); + } + + public Enumeration getAttributeNames() + { + return tab.keys(); + } + + public AttributeSet getResolveParent() + { + return (AttributeSet) tab.get(ResolveAttribute); + } + + public int hashCode() + { + return tab.hashCode(); + } + + public boolean isDefined(Object attrName) + { + return tab.containsKey(attrName); + } + + public boolean isEmpty() + { + return tab.isEmpty(); + } + + public boolean isEqual(AttributeSet attr) + { + return this.equals(attr); + } + + public void removeAttribute(Object name) + { + tab.remove(name); + } + + public void removeAttributes(AttributeSet attributes) + { + removeAttributes(attributes.getAttributeNames()); + } + + public void removeAttributes(Enumeration names) + { + while (names.hasMoreElements()) + { + removeAttribute(names.nextElement()); + } + } + + public void setResolveParent(AttributeSet parent) + { + addAttribute(ResolveAttribute, parent); + } + + public String toString() + { + return tab.toString(); + } +} diff --git a/javax/swing/text/StyleConstants.java b/javax/swing/text/StyleConstants.java new file mode 100644 index 000000000..2201c47b5 --- /dev/null +++ b/javax/swing/text/StyleConstants.java @@ -0,0 +1,439 @@ +/* StyleConstants.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.text; + +import java.awt.Color; +import java.awt.Component; +import javax.swing.Icon; + +public class StyleConstants +{ + + String keyname; + private StyleConstants(String k) + { + keyname = k; + } + + public String toString() + { + return keyname; + } + + public static int ALIGN_CENTER; + public static int ALIGN_JUSTIFIED; + public static int ALIGN_LEFT; + public static int ALIGN_RIGHT; + + public static Object Background = CharacterConstants.Background; + public static Object BidiLevel = CharacterConstants.BidiLevel; + public static Object Bold = CharacterConstants.Bold; + public static Object ComponentAttribute = CharacterConstants.ComponentAttribute; + public static Object FontFamily = CharacterConstants.Family; + public static Object FontSize = CharacterConstants.Size; + public static Object Foreground = CharacterConstants.Foreground; + public static Object IconAttribute = CharacterConstants.IconAttribute; + public static Object Italic = CharacterConstants.Italic; + public static Object StrikeThrough = CharacterConstants.StrikeThrough; + public static Object Subscript = CharacterConstants.Subscript; + public static Object Superscript = CharacterConstants.Superscript; + public static Object Underline = CharacterConstants.Underline; + + public static Object Alignment = ParagraphConstants.Alignment; + public static Object FirstLineIndent = ParagraphConstants.FirstLineIndent; + public static Object LeftIndent = ParagraphConstants.LeftIndent; + public static Object LineSpacing = ParagraphConstants.LineSpacing; + public static Object Orientation = ParagraphConstants.Orientation; + public static Object RightIndent = ParagraphConstants.RightIndent; + public static Object SpaceAbove = ParagraphConstants.SpaceAbove; + public static Object SpaceBelow = ParagraphConstants.SpaceBelow; + public static Object TabSet = ParagraphConstants.TabSet; + + public static String ComponentElementName = new String("component"); + public static String IconElementName = new String("icon"); + + public static Object ComposedTextAttribute = new StyleConstants("composed text"); + public static Object ModelAttribute = new StyleConstants("model"); + public static Object NameAttribute = new StyleConstants("name"); + public static Object ResolveAttribute = new StyleConstants("resolver"); + + public static int getAlignment(AttributeSet a) + { + if (a.isDefined(Alignment)) + return ((Integer)a.getAttribute(Alignment)).intValue(); + else + return ALIGN_LEFT; + } + + public static Color getBackground(AttributeSet a) + { + if (a.isDefined(Background)) + return (Color) a.getAttribute(Background); + else + return Color.BLACK; + } + + public static int getBidiLevel(AttributeSet a) + { + if (a.isDefined(BidiLevel)) + return ((Integer)a.getAttribute(BidiLevel)).intValue(); + else + return 0; + } + + public static Component getComponent(AttributeSet a) + { + if (a.isDefined(ComponentAttribute)) + return (Component) a.getAttribute(ComponentAttribute); + else + return (Component) null; + } + + public static float getFirstLineIndent(AttributeSet a) + { + if (a.isDefined(FirstLineIndent)) + return ((Float)a.getAttribute(FirstLineIndent)).floatValue(); + else + return 0.f; + } + + public static String getFontFamily(AttributeSet a) + { + if (a.isDefined(FontFamily)) + return (String) a.getAttribute(FontFamily); + else + return "Monospaced"; + } + + public static int getFontSize(AttributeSet a) + { + if (a.isDefined(FontSize)) + return ((Integer)a.getAttribute(FontSize)).intValue(); + else + return 12; + } + + public static Color getForeground(AttributeSet a) + { + if (a.isDefined(Foreground)) + return (Color) a.getAttribute(Foreground); + else + return Color.BLACK; + } + + public static Icon getIcon(AttributeSet a) + { + if (a.isDefined(IconAttribute)) + return (Icon) a.getAttribute(IconAttribute); + else + return (Icon) null; + } + + public static float getLeftIndent(AttributeSet a) + { + if (a.isDefined(LeftIndent)) + return ((Float)a.getAttribute(LeftIndent)).floatValue(); + else + return 0.f; + } + + public static float getLineSpacing(AttributeSet a) + { + if (a.isDefined(LineSpacing)) + return ((Float)a.getAttribute(LineSpacing)).floatValue(); + else + return 0.f; + } + + public static float getRightIndent(AttributeSet a) + { + if (a.isDefined(RightIndent)) + return ((Float)a.getAttribute(RightIndent)).floatValue(); + else + return 0.f; + } + + public static float getSpaceAbove(AttributeSet a) + { + if (a.isDefined(SpaceAbove)) + return ((Float)a.getAttribute(SpaceAbove)).floatValue(); + else + return 0.f; + } + + public static float getSpaceBelow(AttributeSet a) + { + if (a.isDefined(SpaceBelow)) + return ((Float)a.getAttribute(SpaceBelow)).floatValue(); + else + return 0.f; + } + + public static javax.swing.text.TabSet getTabSet(AttributeSet a) + { + if (a.isDefined(StyleConstants.TabSet)) + return (javax.swing.text.TabSet) a.getAttribute(StyleConstants.TabSet); + else + return (javax.swing.text.TabSet) null; + } + + public static boolean isBold(AttributeSet a) + { + if (a.isDefined(Bold)) + return ((Boolean) a.getAttribute(Bold)).booleanValue(); + else + return false; + } + + public static boolean isItalic(AttributeSet a) + { + if (a.isDefined(Italic)) + return ((Boolean) a.getAttribute(Italic)).booleanValue(); + else + return false; + } + + public static boolean isStrikeThrough(AttributeSet a) + { + if (a.isDefined(StrikeThrough)) + return ((Boolean) a.getAttribute(StrikeThrough)).booleanValue(); + else + return false; + } + + public static boolean isSubscript(AttributeSet a) + { + if (a.isDefined(Subscript)) + return ((Boolean) a.getAttribute(Subscript)).booleanValue(); + else + return false; + } + + public static boolean isSuperscript(AttributeSet a) + { + if (a.isDefined(Superscript)) + return ((Boolean) a.getAttribute(Superscript)).booleanValue(); + else + return false; + } + + public static boolean isUnderline(AttributeSet a) + { + if (a.isDefined(Underline)) + return ((Boolean) a.getAttribute(Underline)).booleanValue(); + else + return false; + } + + public static void setAlignment(MutableAttributeSet a, int align) + { + a.addAttribute(Alignment, new Integer(align)); + } + + public static void setBackground(MutableAttributeSet a, Color fg) + { + a.addAttribute(Background, fg); + } + + public static void setBidiLevel(MutableAttributeSet a, int lev) + { + a.addAttribute(BidiLevel, new Integer(lev)); + } + + public static void setBold(MutableAttributeSet a, boolean b) + { + a.addAttribute(Bold, new Boolean(b)); + } + + public static void setComponent(MutableAttributeSet a, Component c) + { + a.addAttribute(ComponentAttribute, c); + } + + public static void setFirstLineIndent(MutableAttributeSet a, float i) + { + a.addAttribute(FirstLineIndent, new Float(i)); + } + + public static void setFontFamily(MutableAttributeSet a, String fam) + { + a.addAttribute(FontFamily, fam); + } + + public static void setFontSize(MutableAttributeSet a, int s) + { + a.addAttribute(FontSize, new Integer(s)); + } + + public static void setForeground(MutableAttributeSet a, Color fg) + { + a.addAttribute(Foreground, fg); + } + + public static void setIcon(MutableAttributeSet a, Icon c) + { + a.addAttribute(IconAttribute, c); + } + + public static void setItalic(MutableAttributeSet a, boolean b) + { + a.addAttribute(Italic, new Boolean(b)); + } + + public static void setLeftIndent(MutableAttributeSet a, float i) + { + a.addAttribute(LeftIndent, new Float(i)); + } + + public static void setLineSpacing(MutableAttributeSet a, float i) + { + a.addAttribute(LineSpacing, new Float(i)); + } + + public static void setRightIndent(MutableAttributeSet a, float i) + { + a.addAttribute(RightIndent, new Float(i)); + } + + public static void setSpaceAbove(MutableAttributeSet a, float i) + { + a.addAttribute(SpaceAbove, new Float(i)); + } + + public static void setSpaceBelow(MutableAttributeSet a, float i) + { + a.addAttribute(SpaceBelow, new Float(i)); + } + + public static void setStrikeThrough(MutableAttributeSet a, boolean b) + { + a.addAttribute(StrikeThrough, new Boolean(b)); + } + + public static void setSubscript(MutableAttributeSet a, boolean b) + { + a.addAttribute(Subscript, new Boolean(b)); + } + + public static void setSuperscript(MutableAttributeSet a, boolean b) + { + a.addAttribute(Superscript, new Boolean(b)); + } + + public static void setTabSet(MutableAttributeSet a, javax.swing.text.TabSet tabs) + { + a.addAttribute(StyleConstants.TabSet, tabs); + } + + public static void setUnderline(MutableAttributeSet a, boolean b) + { + a.addAttribute(Underline, new Boolean(b)); + } + + // The remainder are so-called "typesafe enumerations" which + // alias subsets of the above constants. + public static class CharacterConstants + extends StyleConstants + implements AttributeSet.CharacterAttribute + { + private CharacterConstants(String k) + { + super(k); + } + + public static Object Background = ColorConstants.Background; + public static Object BidiLevel = new CharacterConstants("bidiLevel"); + public static Object Bold = FontConstants.Bold; + public static Object ComponentAttribute = new CharacterConstants("component"); + public static Object Family = FontConstants.Family; + public static Object Size = FontConstants.Size; + public static Object Foreground = ColorConstants.Foreground; + public static Object IconAttribute = new CharacterConstants("icon"); + public static Object Italic = FontConstants.Italic; + public static Object StrikeThrough = new CharacterConstants("strikethrough"); + public static Object Subscript = new CharacterConstants("subscript"); + public static Object Superscript = new CharacterConstants("superscript"); + public static Object Underline = new CharacterConstants("underline"); + } + + public static class ColorConstants + extends StyleConstants + implements AttributeSet.ColorAttribute, AttributeSet.CharacterAttribute + { + private ColorConstants(String k) + { + super(k); + } + public static Object Foreground = new ColorConstants("foreground"); + public static Object Background = new ColorConstants("background"); + } + + public static class FontConstants + extends StyleConstants + implements AttributeSet.FontAttribute, AttributeSet.CharacterAttribute + { + private FontConstants(String k) + { + super(k); + } + public static Object Bold = new FontConstants("bold"); + public static Object Family = new FontConstants("family"); + public static Object Italic = new FontConstants("italic"); + public static Object Size = new FontConstants("size"); + } + + public static class ParagraphConstants + extends StyleConstants + implements AttributeSet.ParagraphAttribute + { + private ParagraphConstants(String k) + { + super(k); + } + public static Object Alignment = new ParagraphConstants("Alignment"); + public static Object FirstLineIndent = new ParagraphConstants("FirstLineIndent"); + public static Object LeftIndent = new ParagraphConstants("LeftIndent"); + public static Object LineSpacing = new ParagraphConstants("LineSpacing"); + public static Object Orientation = new ParagraphConstants("Orientation"); + public static Object RightIndent = new ParagraphConstants("RightIndent"); + public static Object SpaceAbove = new ParagraphConstants("SpaceAbove"); + public static Object SpaceBelow = new ParagraphConstants("SpaceBelow"); + public static Object TabSet = new ParagraphConstants("TabSet"); + } + +} diff --git a/javax/swing/text/StyleContext.java b/javax/swing/text/StyleContext.java new file mode 100644 index 000000000..8accd9b90 --- /dev/null +++ b/javax/swing/text/StyleContext.java @@ -0,0 +1,697 @@ +/* StyleContext.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.text; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Toolkit; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import javax.swing.event.EventListenerList; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.util.Enumeration; +import java.util.EventListener; +import java.util.Hashtable; + +public class StyleContext + implements Serializable, AbstractDocument.AttributeContext +{ + public class NamedStyle + implements Serializable, Style + { + protected ChangeEvent changeEvent; + protected EventListenerList listenerList; + + AttributeSet attributes; + String name; + + public NamedStyle() + { + this(null, null); + } + + public NamedStyle(Style parent) + { + this(null, parent); + } + + public NamedStyle(String name, Style parent) + { + this.name = name; + this.attributes = getEmptySet(); + this.changeEvent = new ChangeEvent(this); + this.listenerList = new EventListenerList(); + setResolveParent(parent); + } + + public String getName() + { + return name; + } + + public void setName(String n) + { + name = n; + fireStateChanged(); + } + + public void addChangeListener(ChangeListener l) + { + listenerList.add(ChangeListener.class, l); + } + + public void removeChangeListener(ChangeListener l) + { + listenerList.remove(ChangeListener.class, l); + } + + public EventListener[] getListeners(Class listenerType) + { + return listenerList.getListeners(listenerType); + } + + public ChangeListener[] getChangeListeners() + { + return (ChangeListener[]) getListeners(ChangeListener.class); + } + + protected void fireStateChanged() + { + ChangeListener[] listeners = getChangeListeners(); + for (int i = 0; i < listeners.length; ++i) + { + listeners[i].stateChanged(changeEvent); + } + } + + public void addAttribute(Object name, Object value) + { + attributes = StyleContext.this.addAttribute(attributes, name, value); + fireStateChanged(); + } + + public void addAttributes(AttributeSet attr) + { + attributes = StyleContext.this.addAttributes(attributes, attr); + fireStateChanged(); + } + + public boolean containsAttribute(Object name, Object value) + { + return attributes.containsAttribute(name, value); + } + + public boolean containsAttributes(AttributeSet attrs) + { + return attributes.containsAttributes(attrs); + } + + public AttributeSet copyAttributes() + { + return attributes.copyAttributes(); + } + + public Object getAttribute(Object attrName) + { + return attributes.getAttribute(attrName); + } + + public int getAttributeCount() + { + return attributes.getAttributeCount(); + } + + public Enumeration getAttributeNames() + { + return attributes.getAttributeNames(); + } + + public boolean isDefined(Object attrName) + { + return attributes.isDefined(attrName); + } + + public boolean isEqual(AttributeSet attr) + { + return attributes.isEqual(attr); + } + + public void removeAttribute(Object name) + { + attributes = StyleContext.this.removeAttribute(attributes, name); + fireStateChanged(); + } + + public void removeAttributes(AttributeSet attrs) + { + attributes = StyleContext.this.removeAttributes(attributes, attrs); + fireStateChanged(); + } + + public void removeAttributes(Enumeration names) + { + attributes = StyleContext.this.removeAttributes(attributes, names); + fireStateChanged(); + } + + + public AttributeSet getResolveParent() + { + return attributes.getResolveParent(); + } + + public void setResolveParent(AttributeSet parent) + { + attributes = StyleContext.this.addAttribute(attributes, ResolveAttribute, parent); + fireStateChanged(); + } + + public String toString() + { + return ("[NamedStyle: name=" + name + ", attrs=" + attributes.toString() + "]"); + } + } + + public class SmallAttributeSet + implements AttributeSet + { + final Object [] attrs; + public SmallAttributeSet(AttributeSet a) + { + if (a == null) + attrs = new Object[0]; + else + { + int n = a.getAttributeCount(); + int i = 0; + attrs = new Object[n * 2]; + Enumeration e = a.getAttributeNames(); + while (e.hasMoreElements()) + { + Object name = e.nextElement(); + attrs[i++] = name; + attrs[i++] = a.getAttribute(name); + } + } + } + + public SmallAttributeSet(Object [] a) + { + if (a == null) + attrs = new Object[0]; + else + { + attrs = new Object[a.length]; + System.arraycopy(a, 0, attrs, 0, a.length); + } + } + + public Object clone() + { + return new SmallAttributeSet(this.attrs); + } + + public boolean containsAttribute(Object name, Object value) + { + for (int i = 0; i < attrs.length; i += 2) + { + if (attrs[i].equals(name) && + attrs[i+1].equals(value)) + return true; + } + return false; + } + + public boolean containsAttributes(AttributeSet a) + { + Enumeration e = a.getAttributeNames(); + while (e.hasMoreElements()) + { + Object name = e.nextElement(); + Object val = a.getAttribute(name); + if (!containsAttribute(name, val)) + return false; + } + return true; + } + + public AttributeSet copyAttributes() + { + return (AttributeSet) clone(); + } + + public boolean equals(Object obj) + { + return + (obj instanceof SmallAttributeSet) + && this.isEqual((AttributeSet)obj); + } + + public Object getAttribute(Object key) + { + for (int i = 0; i < attrs.length; i += 2) + { + if (attrs[i].equals(key)) + return attrs[i+1]; + } + + Object p = getResolveParent(); + if (p != null && p instanceof AttributeSet) + return (((AttributeSet)p).getAttribute(key)); + + return null; + } + + public int getAttributeCount() + { + return attrs.length / 2; + } + + public Enumeration getAttributeNames() + { + return new Enumeration() + { + int i = 0; + public boolean hasMoreElements() + { + return i < attrs.length; + } + public Object nextElement() + { + i += 2; + return attrs[i-2]; + } + }; + } + + public AttributeSet getResolveParent() + { + return (AttributeSet) getAttribute(ResolveAttribute); + } + + public int hashCode() + { + return java.util.Arrays.asList(attrs).hashCode(); + } + + public boolean isDefined(Object key) + { + for (int i = 0; i < attrs.length; i += 2) + { + if (attrs[i].equals(key)) + return true; + } + return false; + } + + public boolean isEqual(AttributeSet attr) + { + return attr != null + && attr.containsAttributes(this) + && this.containsAttributes(attr); + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("[StyleContext.SmallattributeSet:"); + for (int i = 0; i < attrs.length; ++i) + { + sb.append(" ("); + sb.append(attrs[i].toString()); + sb.append("="); + sb.append(attrs[i+1].toString()); + sb.append(")"); + } + sb.append("]"); + return sb.toString(); + } + } + + // FIXME: official javadocs suggest that these might be more usefully + // implemented using a WeakHashMap, but not sure if that works most + // places or whether it really matters anyways. + // + // FIXME: also not sure if these tables ought to be static (singletons), + // shared across all StyleContexts. I think so, but it's not clear in + // docs. revert to non-shared if you think it matters. + + public static final String DEFAULT_STYLE = "default"; + + static Hashtable sharedAttributeSets = new Hashtable(); + static Hashtable sharedFonts = new Hashtable(); + + static StyleContext defaultStyleContext = new StyleContext(); + static final int compressionThreshold = 9; + + EventListenerList listenerList; + Hashtable styleTable; + + public StyleContext() + { + listenerList = new EventListenerList(); + styleTable = new Hashtable(); + } + + protected SmallAttributeSet createSmallAttributeSet(AttributeSet a) + { + return new SmallAttributeSet(a); + } + + protected MutableAttributeSet createLargeAttributeSet(AttributeSet a) + { + return new SimpleAttributeSet(a); + } + + public void addChangeListener(ChangeListener listener) + { + listenerList.add(ChangeListener.class, listener); + } + + public void removeChangeListener(ChangeListener listener) + { + listenerList.remove(ChangeListener.class, listener); + } + + public ChangeListener[] getChangeListeners() + { + return (ChangeListener[]) listenerList.getListeners(ChangeListener.class); + } + + public Style addStyle(String name, Style parent) + { + Style newStyle = new NamedStyle(name, parent); + if (name != null) + styleTable.put(name, newStyle); + return newStyle; + } + + public void removeStyle(String name) + { + styleTable.remove(name); + } + + public Style getStyle(String name) + { + return (Style) styleTable.get(name); + } + + public Enumeration getStyleNames() + { + return styleTable.keys(); + } + + // + // StyleContexts only understand the "simple" model of fonts present in + // pre-java2d systems: fonts are a family name, a size (integral number + // of points), and a mask of style parameters (plain, bold, italic, or + // bold|italic). We have an inner class here called SimpleFontSpec which + // holds such triples. + // + // A SimpleFontSpec can be built for *any* AttributeSet because the size, + // family, and style keys in an AttributeSet have default values (defined + // over in StyleConstants). + // + // We keep a static cache mapping SimpleFontSpecs to java.awt.Fonts, so + // that we reuse Fonts between styles and style contexts. + // + + private static class SimpleFontSpec + { + String family; + int style; + int size; + public SimpleFontSpec(String family, + int style, + int size) + { + this.family = family; + this.style = style; + this.size = size; + } + public boolean equals(Object obj) + { + return (obj != null) + && (obj instanceof SimpleFontSpec) + && (((SimpleFontSpec)obj).family.equals(this.family)) + && (((SimpleFontSpec)obj).style == this.style) + && (((SimpleFontSpec)obj).size == this.size); + } + public int hashCode() + { + return family.hashCode() + style + size; + } + } + + public Font getFont(AttributeSet attr) + { + String family = StyleConstants.getFontFamily(attr); + int style = Font.PLAIN; + if (StyleConstants.isBold(attr)) + style += Font.BOLD; + if (StyleConstants.isItalic(attr)) + style += Font.ITALIC; + int size = StyleConstants.getFontSize(attr); + return getFont(family, style, size); + } + + public Font getFont(String family, int style, int size) + { + SimpleFontSpec spec = new SimpleFontSpec(family, style, size); + if (sharedFonts.containsKey(spec)) + return (Font) sharedFonts.get(spec); + else + { + Font tmp = new Font(family, style, size); + sharedFonts.put(spec, tmp); + return tmp; + } + } + + public FontMetrics getFontMetrics(Font f) + { + return Toolkit.getDefaultToolkit().getFontMetrics(f); + } + + public Color getForeground(AttributeSet a) + { + return StyleConstants.getForeground(a); + } + + public Color getBackground(AttributeSet a) + { + return StyleConstants.getBackground(a); + } + + protected int getCompressionThreshold() + { + return compressionThreshold; + } + + public static StyleContext getDefaultStyleContext() + { + return defaultStyleContext; + } + + public AttributeSet addAttribute(AttributeSet old, Object name, Object value) + { + if (old instanceof MutableAttributeSet) + { + ((MutableAttributeSet)old).addAttribute(name, value); + return old; + } + else + { + MutableAttributeSet mutable = createLargeAttributeSet(old); + mutable.addAttribute(name, value); + if (mutable.getAttributeCount() >= getCompressionThreshold()) + return mutable; + else + { + SmallAttributeSet small = createSmallAttributeSet(mutable); + if (sharedAttributeSets.containsKey(small)) + small = (SmallAttributeSet) sharedAttributeSets.get(small); + else + sharedAttributeSets.put(small,small); + return small; + } + } + } + + public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes) + { + if (old instanceof MutableAttributeSet) + { + ((MutableAttributeSet)old).addAttributes(attributes); + return old; + } + else + { + MutableAttributeSet mutable = createLargeAttributeSet(old); + mutable.addAttributes(attributes); + if (mutable.getAttributeCount() >= getCompressionThreshold()) + return mutable; + else + { + SmallAttributeSet small = createSmallAttributeSet(mutable); + if (sharedAttributeSets.containsKey(small)) + small = (SmallAttributeSet) sharedAttributeSets.get(small); + else + sharedAttributeSets.put(small,small); + return small; + } + } + } + + public AttributeSet getEmptySet() + { + AttributeSet e = createSmallAttributeSet(null); + if (sharedAttributeSets.containsKey(e)) + e = (AttributeSet) sharedAttributeSets.get(e); + else + sharedAttributeSets.put(e, e); + return e; + } + + public void reclaim(AttributeSet attributes) + { + if (sharedAttributeSets.containsKey(attributes)) + sharedAttributeSets.remove(attributes); + } + + public AttributeSet removeAttribute(AttributeSet old, Object name) + { + if (old instanceof MutableAttributeSet) + { + ((MutableAttributeSet)old).removeAttribute(name); + if (old.getAttributeCount() < getCompressionThreshold()) + { + SmallAttributeSet small = createSmallAttributeSet(old); + if (!sharedAttributeSets.containsKey(small)) + sharedAttributeSets.put(small,small); + old = (AttributeSet) sharedAttributeSets.get(small); + } + return old; + } + else + { + MutableAttributeSet mutable = createLargeAttributeSet(old); + mutable.removeAttribute(name); + SmallAttributeSet small = createSmallAttributeSet(mutable); + if (sharedAttributeSets.containsKey(small)) + small = (SmallAttributeSet) sharedAttributeSets.get(small); + else + sharedAttributeSets.put(small,small); + return small; + } + } + + public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes) + { + return removeAttributes(old, attributes.getAttributeNames()); + } + + public AttributeSet removeAttributes(AttributeSet old, Enumeration names) + { + if (old instanceof MutableAttributeSet) + { + ((MutableAttributeSet)old).removeAttributes(names); + if (old.getAttributeCount() < getCompressionThreshold()) + { + SmallAttributeSet small = createSmallAttributeSet(old); + if (!sharedAttributeSets.containsKey(small)) + sharedAttributeSets.put(small,small); + old = (AttributeSet) sharedAttributeSets.get(small); + } + return old; + } + else + { + MutableAttributeSet mutable = createLargeAttributeSet(old); + mutable.removeAttributes(names); + SmallAttributeSet small = createSmallAttributeSet(mutable); + if (sharedAttributeSets.containsKey(small)) + small = (SmallAttributeSet) sharedAttributeSets.get(small); + else + sharedAttributeSets.put(small,small); + return small; + } + } + + + // FIXME: there's some sort of quasi-serialization stuff in here which I + // have left incomplete; I'm not sure I understand the intent properly. + + public static Object getStaticAttribute(Object key) + { + throw new InternalError("not implemented"); + } + + public static Object getStaticAttributeKey(Object key) + { + throw new InternalError("not implemented"); + } + + public static void readAttributeSet(ObjectInputStream in, MutableAttributeSet a) + throws ClassNotFoundException, IOException + { + throw new InternalError("not implemented"); + } + + public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a) + throws IOException + { + throw new InternalError("not implemented"); + } + + public void readAttributes(ObjectInputStream in, MutableAttributeSet a) + throws ClassNotFoundException, IOException + { + throw new InternalError("not implemented"); + } + + public void writeAttributes(ObjectOutputStream out, AttributeSet a) + throws IOException + { + throw new InternalError("not implemented"); + } +} diff --git a/javax/swing/text/TabSet.java b/javax/swing/text/TabSet.java new file mode 100644 index 000000000..58ae65ef0 --- /dev/null +++ b/javax/swing/text/TabSet.java @@ -0,0 +1,102 @@ +/* TabSet.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.text; + +import java.io.Serializable; + +public class TabSet implements Serializable +{ + TabStop[] tabs; + + public TabSet(TabStop[] t) + { + tabs = t; + } + + public TabStop getTab(int i) + { + return tabs[i]; + } + + public TabStop getTabAfter(float location) + { + int idx = getTabIndexAfter(location); + if (idx == -1) + return null; + else + return tabs[idx]; + } + + public int getTabCount() + { + return tabs.length; + } + + public int getTabIndex(TabStop tab) + { + for (int i = 0; i < tabs.length; ++i) + if (tabs[i] == tab) + return i; + return -1; + } + + public int getTabIndexAfter(float location) + { + int idx = -1; + for (int i = 0; i < tabs.length; ++i) + { + if (location < tabs[i].getPosition()) + idx = i; + } + return idx; + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("["); + for (int i = 0; i < tabs.length; ++i) + { + if (i != 0) + sb.append(" - "); + sb.append(tabs[i].toString()); + } + sb.append("]"); + return sb.toString(); + } +} diff --git a/javax/swing/text/TabStop.java b/javax/swing/text/TabStop.java new file mode 100644 index 000000000..2c95a63e4 --- /dev/null +++ b/javax/swing/text/TabStop.java @@ -0,0 +1,133 @@ +/* TabSet.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.text; + +import java.io.Serializable; + +public class TabStop implements Serializable +{ + public static final int ALIGN_LEFT = 0; + public static final int ALIGN_RIGHT = 1; + public static final int ALIGN_CENTER = 2; + public static final int ALIGN_DECIMAL = 4; + public static final int ALIGN_BAR = 5; + + public static final int LEAD_NONE = 0; + public static final int LEAD_DOTS = 1; + public static final int LEAD_HYPHENS = 2; + public static final int LEAD_UNDERLINE = 3; + public static final int LEAD_THICKLINE = 4; + public static final int LEAD_EQUALS = 5; + + float pos; + int align; + int leader; + + public TabStop(float pos) + { + this(pos, ALIGN_LEFT, LEAD_NONE); + } + + public TabStop(float pos, int align, int leader) + { + this.pos = pos; + this.align = align; + this.leader = leader; + } + + public boolean equals(Object other) + { + return (other != null) + && (other instanceof TabStop) + && (((TabStop)other).getPosition() == this.getPosition()) + && (((TabStop)other).getLeader() == this.getLeader()) + && (((TabStop)other).getAlignment() == this.getAlignment()); + } + + public int getAlignment() + { + return align; + } + + public int getLeader() + { + return leader; + } + + public float getPosition() + { + return pos; + } + + public int hashCode() + { + return (int) pos + (int) leader + (int) align; + } + + public String toString() + { + String prefix = ""; + switch (align) + { + case ALIGN_LEFT: + prefix = "left "; + break; + case ALIGN_RIGHT: + prefix = "right "; + break; + + case ALIGN_CENTER: + prefix = "center "; + break; + + case ALIGN_DECIMAL: + prefix = "decimal "; + break; + + case ALIGN_BAR: + prefix = "bar "; + break; + + default: + break; + } + + return (prefix + "tab @" + pos + ((leader == LEAD_NONE) ? "" : "(w/leaders)")); + } + +} diff --git a/javax/swing/text/TextAction.java b/javax/swing/text/TextAction.java index bfa723157..bdcf95095 100644 --- a/javax/swing/text/TextAction.java +++ b/javax/swing/text/TextAction.java @@ -54,7 +54,7 @@ public abstract class TextAction extends AbstractAction */ public TextAction(String name) { - // TODO + super(name); } /** @@ -64,7 +64,11 @@ public abstract class TextAction extends AbstractAction */ protected final JTextComponent getTextComponent(ActionEvent event) { - return null; // TODO + if (event.getSource() != null && + event.getSource() instanceof JTextComponent) + return (JTextComponent) event.getSource(); + else + return getFocusedComponent(); } /** diff --git a/javax/swing/text/Utilities.java b/javax/swing/text/Utilities.java new file mode 100644 index 000000000..61c13eef5 --- /dev/null +++ b/javax/swing/text/Utilities.java @@ -0,0 +1,182 @@ +/* Utilities.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text; + +import java.awt.FontMetrics; +import java.awt.Graphics; + +/** + * A set of utilities to deal with text. This is used by several other classes + * inside this package. + * + * @author Roman Kennke <roman@ontographics.com> + */ +public class Utilities +{ + /** + * The length of the char buffer that holds the characters to be drawn. + */ + private static final int BUF_LENGTH = 64; + + /** + * Creates a new <code>Utilities</code> object. + */ + public Utilities() + { + // Nothing to be done here. + } + + /** + * Draws the given text segment. Contained tabs and newline characters + * are taken into account. Tabs are expanded using the + * specified {@link TabExpander}. + * + * @param s the text fragment to be drawn. + * @param x the x position for drawing. + * @param y the y position for drawing. + * @param g the {@link Graphics} context for drawing. + * @param e the {@link TabExpander} which specifies the Tab-expanding + * technique. + * @param startOffset starting offset in the text. + * @return the x coordinate at the end of the drawn text. + */ + public static final int drawTabbedText(Segment s, int x, int y, Graphics g, + TabExpander e, int startOffset) + { + // This buffers the chars to be drawn. + char[] buffer = s.array; + + + // The current x and y pixel coordinates. + int pixelX = x; + int pixelY = y; + + // The font metrics of the current selected font. + FontMetrics metrics = g.getFontMetrics(); + int ascent = metrics.getAscent(); + + for (int offset = s.offset; offset < (s.offset + s.count); ++offset) + { + switch (buffer[offset]) + { + case '\t': + // In case we have a tab, we just 'jump' over the tab. + // When we have no tab expander we just use the width of 'm'. + if (e != null) + pixelX = (int) e.nextTabStop((float) pixelX, + startOffset + offset - s.offset); + else + pixelX += metrics.charWidth(' '); + break; + case '\n': + // In case we have a newline, we must draw + // the buffer and jump on the next line. + g.drawChars(buffer, offset, 1, pixelX, y); + pixelY += metrics.getHeight(); + pixelX = x; + break; + default: + // Here we draw the char. + g.drawChars(buffer, offset, 1, pixelX, pixelY + ascent); + pixelX += metrics.charWidth(buffer[offset]); + break; + } + } + + return pixelX; + } + + /** + * Determines the width, that the given text <code>s</code> would take + * if it was printed with the given {@link java.awt.FontMetrics} on the + * specified screen position. + * @param s the text fragment + * @param metrics the font metrics of the font to be used + * @param x the x coordinate of the point at which drawing should be done + * @param e the {@link TabExpander} to be used + * @param startOffset the index in <code>s</code> where to start + * @returns the width of the given text s. This takes tabs and newlines + * into account. + */ + public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, + int x, TabExpander e, + int startOffset) + { + // This buffers the chars to be drawn. + char[] buffer = s.array; + + // The current x coordinate. + int pixelX = x; + + // The current maximum width. + int maxWidth = 0; + + for (int offset = s.offset; offset < (s.offset + s.count); ++offset) + { + switch (buffer[offset]) + { + case '\t': + // In case we have a tab, we just 'jump' over the tab. + // When we have no tab expander we just use the width of 'm'. + if (e != null) + pixelX = (int) e.nextTabStop((float) pixelX, + startOffset + offset - s.offset); + else + pixelX += metrics.charWidth(' '); + break; + case '\n': + // In case we have a newline, we must 'draw' + // the buffer and jump on the next line. + pixelX += metrics.charWidth(buffer[offset]); + maxWidth = Math.max(maxWidth, pixelX - x); + pixelX = x; + break; + default: + // Here we draw the char. + pixelX += metrics.charWidth(buffer[offset]); + break; + } + } + + // Take the last line into account. + maxWidth = Math.max(maxWidth, pixelX - x); + + return maxWidth; + } +} diff --git a/javax/swing/text/View.java b/javax/swing/text/View.java index 00c6368ae..5ff46ed22 100644 --- a/javax/swing/text/View.java +++ b/javax/swing/text/View.java @@ -174,5 +174,36 @@ public abstract class View implements SwingConstants { return elt.getEndOffset(); } + + public Shape getChildAllocation(int index, Shape a) + { + return null; + } + + /** + * @since 1.4 + */ + public int getViewIndex(float x, float y, Shape allocation) + { + return -1; + } + + /** + * @since 1.4 + */ + public String getToolTipText(float x, float y, Shape allocation) + { + int index = getViewIndex(x, y, allocation); + + if (index < -1) + return null; + + Shape childAllocation = getChildAllocation(index, allocation); + + if (childAllocation.getBounds().contains(x, y)) + return getView(index).getToolTipText(x, y, childAllocation); + + return null; + } } |