summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing
diff options
context:
space:
mode:
authorupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
committerupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
commit554fd8c5195424bdbcabf5de30fdc183aba391bd (patch)
tree976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/javax/swing
downloadcbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2
cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository.
Diffstat (limited to 'libjava/classpath/javax/swing')
-rw-r--r--libjava/classpath/javax/swing/AbstractAction.java276
-rw-r--r--libjava/classpath/javax/swing/AbstractButton.java2652
-rw-r--r--libjava/classpath/javax/swing/AbstractCellEditor.java195
-rw-r--r--libjava/classpath/javax/swing/AbstractListModel.java181
-rw-r--r--libjava/classpath/javax/swing/AbstractSpinnerModel.java123
-rw-r--r--libjava/classpath/javax/swing/Action.java153
-rw-r--r--libjava/classpath/javax/swing/ActionMap.java195
-rw-r--r--libjava/classpath/javax/swing/BorderFactory.java456
-rw-r--r--libjava/classpath/javax/swing/BoundedRangeModel.java193
-rw-r--r--libjava/classpath/javax/swing/Box.java290
-rw-r--r--libjava/classpath/javax/swing/BoxLayout.java451
-rw-r--r--libjava/classpath/javax/swing/ButtonGroup.java223
-rw-r--r--libjava/classpath/javax/swing/ButtonModel.java301
-rw-r--r--libjava/classpath/javax/swing/CellEditor.java108
-rw-r--r--libjava/classpath/javax/swing/CellRendererPane.java251
-rw-r--r--libjava/classpath/javax/swing/ComboBoxEditor.java96
-rw-r--r--libjava/classpath/javax/swing/ComboBoxModel.java69
-rw-r--r--libjava/classpath/javax/swing/CompatibilityFocusTraversalPolicy.java164
-rw-r--r--libjava/classpath/javax/swing/ComponentInputMap.java141
-rw-r--r--libjava/classpath/javax/swing/DebugGraphics.java1125
-rw-r--r--libjava/classpath/javax/swing/DefaultBoundedRangeModel.java475
-rw-r--r--libjava/classpath/javax/swing/DefaultButtonModel.java578
-rw-r--r--libjava/classpath/javax/swing/DefaultCellEditor.java570
-rw-r--r--libjava/classpath/javax/swing/DefaultComboBoxModel.java286
-rw-r--r--libjava/classpath/javax/swing/DefaultDesktopManager.java644
-rw-r--r--libjava/classpath/javax/swing/DefaultFocusManager.java169
-rw-r--r--libjava/classpath/javax/swing/DefaultListCellRenderer.java199
-rw-r--r--libjava/classpath/javax/swing/DefaultListModel.java521
-rw-r--r--libjava/classpath/javax/swing/DefaultListSelectionModel.java855
-rw-r--r--libjava/classpath/javax/swing/DefaultSingleSelectionModel.java191
-rw-r--r--libjava/classpath/javax/swing/DesktopManager.java177
-rw-r--r--libjava/classpath/javax/swing/FocusManager.java524
-rw-r--r--libjava/classpath/javax/swing/GrayFilter.java97
-rw-r--r--libjava/classpath/javax/swing/Icon.java73
-rw-r--r--libjava/classpath/javax/swing/ImageIcon.java471
-rw-r--r--libjava/classpath/javax/swing/InputMap.java235
-rw-r--r--libjava/classpath/javax/swing/InputVerifier.java79
-rw-r--r--libjava/classpath/javax/swing/InternalFrameFocusTraversalPolicy.java60
-rw-r--r--libjava/classpath/javax/swing/JApplet.java224
-rw-r--r--libjava/classpath/javax/swing/JButton.java276
-rw-r--r--libjava/classpath/javax/swing/JCheckBox.java176
-rw-r--r--libjava/classpath/javax/swing/JCheckBoxMenuItem.java275
-rw-r--r--libjava/classpath/javax/swing/JColorChooser.java644
-rw-r--r--libjava/classpath/javax/swing/JComboBox.java1499
-rw-r--r--libjava/classpath/javax/swing/JComponent.java3801
-rw-r--r--libjava/classpath/javax/swing/JDesktopPane.java386
-rw-r--r--libjava/classpath/javax/swing/JDialog.java583
-rw-r--r--libjava/classpath/javax/swing/JEditorPane.java1222
-rw-r--r--libjava/classpath/javax/swing/JFileChooser.java1626
-rw-r--r--libjava/classpath/javax/swing/JFormattedTextField.java648
-rw-r--r--libjava/classpath/javax/swing/JFrame.java410
-rw-r--r--libjava/classpath/javax/swing/JInternalFrame.java1820
-rw-r--r--libjava/classpath/javax/swing/JLabel.java1122
-rw-r--r--libjava/classpath/javax/swing/JLayeredPane.java755
-rw-r--r--libjava/classpath/javax/swing/JList.java2499
-rw-r--r--libjava/classpath/javax/swing/JMenu.java1290
-rw-r--r--libjava/classpath/javax/swing/JMenuBar.java692
-rw-r--r--libjava/classpath/javax/swing/JMenuItem.java809
-rw-r--r--libjava/classpath/javax/swing/JOptionPane.java1637
-rw-r--r--libjava/classpath/javax/swing/JPanel.java203
-rw-r--r--libjava/classpath/javax/swing/JPasswordField.java297
-rw-r--r--libjava/classpath/javax/swing/JPopupMenu.java947
-rw-r--r--libjava/classpath/javax/swing/JProgressBar.java861
-rw-r--r--libjava/classpath/javax/swing/JRadioButton.java256
-rw-r--r--libjava/classpath/javax/swing/JRadioButtonMenuItem.java230
-rw-r--r--libjava/classpath/javax/swing/JRootPane.java700
-rw-r--r--libjava/classpath/javax/swing/JScrollBar.java700
-rw-r--r--libjava/classpath/javax/swing/JScrollPane.java712
-rw-r--r--libjava/classpath/javax/swing/JSeparator.java218
-rw-r--r--libjava/classpath/javax/swing/JSlider.java1144
-rw-r--r--libjava/classpath/javax/swing/JSpinner.java754
-rw-r--r--libjava/classpath/javax/swing/JSplitPane.java942
-rw-r--r--libjava/classpath/javax/swing/JTabbedPane.java1728
-rw-r--r--libjava/classpath/javax/swing/JTable.java5157
-rw-r--r--libjava/classpath/javax/swing/JTextArea.java607
-rw-r--r--libjava/classpath/javax/swing/JTextField.java570
-rw-r--r--libjava/classpath/javax/swing/JTextPane.java424
-rw-r--r--libjava/classpath/javax/swing/JToggleButton.java350
-rw-r--r--libjava/classpath/javax/swing/JToolBar.java798
-rw-r--r--libjava/classpath/javax/swing/JToolTip.java244
-rw-r--r--libjava/classpath/javax/swing/JTree.java3186
-rw-r--r--libjava/classpath/javax/swing/JViewport.java948
-rw-r--r--libjava/classpath/javax/swing/JWindow.java290
-rw-r--r--libjava/classpath/javax/swing/KeyStroke.java123
-rw-r--r--libjava/classpath/javax/swing/KeyboardManager.java281
-rw-r--r--libjava/classpath/javax/swing/LayoutFocusTraversalPolicy.java89
-rw-r--r--libjava/classpath/javax/swing/ListCellRenderer.java50
-rw-r--r--libjava/classpath/javax/swing/ListModel.java80
-rw-r--r--libjava/classpath/javax/swing/ListSelectionModel.java332
-rw-r--r--libjava/classpath/javax/swing/LookAndFeel.java433
-rw-r--r--libjava/classpath/javax/swing/MenuElement.java89
-rw-r--r--libjava/classpath/javax/swing/MenuSelectionManager.java440
-rw-r--r--libjava/classpath/javax/swing/MutableComboBoxModel.java82
-rw-r--r--libjava/classpath/javax/swing/OverlayLayout.java412
-rw-r--r--libjava/classpath/javax/swing/Popup.java301
-rw-r--r--libjava/classpath/javax/swing/PopupFactory.java171
-rw-r--r--libjava/classpath/javax/swing/ProgressMonitor.java460
-rw-r--r--libjava/classpath/javax/swing/ProgressMonitorInputStream.java249
-rw-r--r--libjava/classpath/javax/swing/Renderer.java68
-rw-r--r--libjava/classpath/javax/swing/RepaintManager.java860
-rw-r--r--libjava/classpath/javax/swing/RootPaneContainer.java96
-rw-r--r--libjava/classpath/javax/swing/ScrollPaneConstants.java152
-rw-r--r--libjava/classpath/javax/swing/ScrollPaneLayout.java491
-rw-r--r--libjava/classpath/javax/swing/Scrollable.java106
-rw-r--r--libjava/classpath/javax/swing/SingleSelectionModel.java103
-rw-r--r--libjava/classpath/javax/swing/SizeRequirements.java523
-rw-r--r--libjava/classpath/javax/swing/SizeSequence.java225
-rw-r--r--libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java333
-rw-r--r--libjava/classpath/javax/swing/SpinnerDateModel.java317
-rw-r--r--libjava/classpath/javax/swing/SpinnerListModel.java295
-rw-r--r--libjava/classpath/javax/swing/SpinnerModel.java111
-rw-r--r--libjava/classpath/javax/swing/SpinnerNumberModel.java361
-rw-r--r--libjava/classpath/javax/swing/Spring.java745
-rw-r--r--libjava/classpath/javax/swing/SpringLayout.java832
-rw-r--r--libjava/classpath/javax/swing/SwingConstants.java76
-rw-r--r--libjava/classpath/javax/swing/SwingUtilities.java1754
-rw-r--r--libjava/classpath/javax/swing/Timer.java476
-rw-r--r--libjava/classpath/javax/swing/ToolTipManager.java593
-rw-r--r--libjava/classpath/javax/swing/TransferHandler.java654
-rw-r--r--libjava/classpath/javax/swing/UIDefaults.java847
-rw-r--r--libjava/classpath/javax/swing/UIManager.java948
-rw-r--r--libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java57
-rw-r--r--libjava/classpath/javax/swing/ViewportLayout.java202
-rw-r--r--libjava/classpath/javax/swing/WindowConstants.java79
-rw-r--r--libjava/classpath/javax/swing/border/AbstractBorder.java199
-rw-r--r--libjava/classpath/javax/swing/border/BevelBorder.java584
-rw-r--r--libjava/classpath/javax/swing/border/Border.java103
-rw-r--r--libjava/classpath/javax/swing/border/CompoundBorder.java255
-rw-r--r--libjava/classpath/javax/swing/border/EmptyBorder.java223
-rw-r--r--libjava/classpath/javax/swing/border/EtchedBorder.java414
-rw-r--r--libjava/classpath/javax/swing/border/LineBorder.java347
-rw-r--r--libjava/classpath/javax/swing/border/MatteBorder.java407
-rw-r--r--libjava/classpath/javax/swing/border/SoftBevelBorder.java327
-rw-r--r--libjava/classpath/javax/swing/border/TitledBorder.java1078
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/BevelBorder-1.pngbin0 -> 4440 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/BevelBorder-2.pngbin0 -> 3667 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/BevelBorder-3.pngbin0 -> 4981 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/EmptyBorder-1.pngbin0 -> 11522 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/EtchedBorder-1.pngbin0 -> 4820 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/EtchedBorder-2.pngbin0 -> 3850 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/LineBorder-1.pngbin0 -> 6133 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/MatteBorder-1.pngbin0 -> 5447 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/MatteBorder-2.pngbin0 -> 5099 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/MatteBorder-3.pngbin0 -> 5726 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/MatteBorder-4.pngbin0 -> 7220 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/MatteBorder-5.pngbin0 -> 7971 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/MatteBorder-6.pngbin0 -> 5511 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-1.pngbin0 -> 4877 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-2.pngbin0 -> 3860 bytes
-rw-r--r--libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-3.pngbin0 -> 5155 bytes
-rw-r--r--libjava/classpath/javax/swing/border/package.html47
-rw-r--r--libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java193
-rw-r--r--libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java86
-rw-r--r--libjava/classpath/javax/swing/colorchooser/ColorSelectionModel.java86
-rw-r--r--libjava/classpath/javax/swing/colorchooser/DefaultColorSelectionModel.java163
-rw-r--r--libjava/classpath/javax/swing/colorchooser/DefaultHSBChooserPanel.java890
-rw-r--r--libjava/classpath/javax/swing/colorchooser/DefaultPreviewPanel.java318
-rw-r--r--libjava/classpath/javax/swing/colorchooser/DefaultRGBChooserPanel.java402
-rw-r--r--libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java896
-rw-r--r--libjava/classpath/javax/swing/colorchooser/package.html47
-rw-r--r--libjava/classpath/javax/swing/event/AncestorEvent.java100
-rw-r--r--libjava/classpath/javax/swing/event/AncestorListener.java69
-rw-r--r--libjava/classpath/javax/swing/event/CaretEvent.java70
-rw-r--r--libjava/classpath/javax/swing/event/CaretListener.java56
-rw-r--r--libjava/classpath/javax/swing/event/CellEditorListener.java62
-rw-r--r--libjava/classpath/javax/swing/event/ChangeEvent.java66
-rw-r--r--libjava/classpath/javax/swing/event/ChangeListener.java63
-rw-r--r--libjava/classpath/javax/swing/event/DocumentEvent.java156
-rw-r--r--libjava/classpath/javax/swing/event/DocumentListener.java68
-rw-r--r--libjava/classpath/javax/swing/event/EventListenerList.java359
-rw-r--r--libjava/classpath/javax/swing/event/HyperlinkEvent.java162
-rw-r--r--libjava/classpath/javax/swing/event/HyperlinkListener.java57
-rw-r--r--libjava/classpath/javax/swing/event/InternalFrameAdapter.java126
-rw-r--r--libjava/classpath/javax/swing/event/InternalFrameEvent.java154
-rw-r--r--libjava/classpath/javax/swing/event/InternalFrameListener.java92
-rw-r--r--libjava/classpath/javax/swing/event/ListDataEvent.java132
-rw-r--r--libjava/classpath/javax/swing/event/ListDataListener.java82
-rw-r--r--libjava/classpath/javax/swing/event/ListSelectionEvent.java135
-rw-r--r--libjava/classpath/javax/swing/event/ListSelectionListener.java61
-rw-r--r--libjava/classpath/javax/swing/event/MenuDragMouseEvent.java104
-rw-r--r--libjava/classpath/javax/swing/event/MenuDragMouseListener.java74
-rw-r--r--libjava/classpath/javax/swing/event/MenuEvent.java59
-rw-r--r--libjava/classpath/javax/swing/event/MenuKeyEvent.java102
-rw-r--r--libjava/classpath/javax/swing/event/MenuKeyListener.java68
-rw-r--r--libjava/classpath/javax/swing/event/MenuListener.java68
-rw-r--r--libjava/classpath/javax/swing/event/MouseInputAdapter.java119
-rw-r--r--libjava/classpath/javax/swing/event/MouseInputListener.java53
-rw-r--r--libjava/classpath/javax/swing/event/PopupMenuEvent.java58
-rw-r--r--libjava/classpath/javax/swing/event/PopupMenuListener.java68
-rw-r--r--libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java70
-rw-r--r--libjava/classpath/javax/swing/event/TableColumnModelEvent.java93
-rw-r--r--libjava/classpath/javax/swing/event/TableColumnModelListener.java94
-rw-r--r--libjava/classpath/javax/swing/event/TableModelEvent.java220
-rw-r--r--libjava/classpath/javax/swing/event/TableModelListener.java60
-rw-r--r--libjava/classpath/javax/swing/event/TreeExpansionEvent.java77
-rw-r--r--libjava/classpath/javax/swing/event/TreeExpansionListener.java62
-rw-r--r--libjava/classpath/javax/swing/event/TreeModelEvent.java168
-rw-r--r--libjava/classpath/javax/swing/event/TreeModelListener.java74
-rw-r--r--libjava/classpath/javax/swing/event/TreeSelectionEvent.java257
-rw-r--r--libjava/classpath/javax/swing/event/TreeSelectionListener.java60
-rw-r--r--libjava/classpath/javax/swing/event/TreeWillExpandListener.java65
-rw-r--r--libjava/classpath/javax/swing/event/UndoableEditEvent.java80
-rw-r--r--libjava/classpath/javax/swing/event/UndoableEditListener.java56
-rw-r--r--libjava/classpath/javax/swing/event/package.html47
-rw-r--r--libjava/classpath/javax/swing/filechooser/FileFilter.java85
-rw-r--r--libjava/classpath/javax/swing/filechooser/FileSystemView.java410
-rw-r--r--libjava/classpath/javax/swing/filechooser/FileView.java128
-rw-r--r--libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java180
-rw-r--r--libjava/classpath/javax/swing/filechooser/package.html47
-rw-r--r--libjava/classpath/javax/swing/package.html47
-rw-r--r--libjava/classpath/javax/swing/plaf/ActionMapUIResource.java62
-rw-r--r--libjava/classpath/javax/swing/plaf/BorderUIResource.java930
-rw-r--r--libjava/classpath/javax/swing/plaf/ButtonUI.java52
-rw-r--r--libjava/classpath/javax/swing/plaf/ColorChooserUI.java58
-rw-r--r--libjava/classpath/javax/swing/plaf/ColorUIResource.java121
-rw-r--r--libjava/classpath/javax/swing/plaf/ComboBoxUI.java92
-rw-r--r--libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java68
-rw-r--r--libjava/classpath/javax/swing/plaf/ComponentUI.java331
-rw-r--r--libjava/classpath/javax/swing/plaf/DesktopIconUI.java56
-rw-r--r--libjava/classpath/javax/swing/plaf/DesktopPaneUI.java58
-rw-r--r--libjava/classpath/javax/swing/plaf/DimensionUIResource.java66
-rw-r--r--libjava/classpath/javax/swing/plaf/FileChooserUI.java138
-rw-r--r--libjava/classpath/javax/swing/plaf/FontUIResource.java99
-rw-r--r--libjava/classpath/javax/swing/plaf/IconUIResource.java124
-rw-r--r--libjava/classpath/javax/swing/plaf/InputMapUIResource.java61
-rw-r--r--libjava/classpath/javax/swing/plaf/InsetsUIResource.java76
-rw-r--r--libjava/classpath/javax/swing/plaf/InternalFrameUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/LabelUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/ListUI.java114
-rw-r--r--libjava/classpath/javax/swing/plaf/MenuBarUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/MenuItemUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/OptionPaneUI.java75
-rw-r--r--libjava/classpath/javax/swing/plaf/PanelUI.java58
-rw-r--r--libjava/classpath/javax/swing/plaf/PopupMenuUI.java117
-rw-r--r--libjava/classpath/javax/swing/plaf/ProgressBarUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/RootPaneUI.java58
-rw-r--r--libjava/classpath/javax/swing/plaf/ScrollBarUI.java58
-rw-r--r--libjava/classpath/javax/swing/plaf/ScrollPaneUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/SeparatorUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/SliderUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/SpinnerUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/SplitPaneUI.java133
-rw-r--r--libjava/classpath/javax/swing/plaf/TabbedPaneUI.java110
-rw-r--r--libjava/classpath/javax/swing/plaf/TableHeaderUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/TableUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/TextUI.java281
-rw-r--r--libjava/classpath/javax/swing/plaf/ToolBarUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/ToolTipUI.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/TreeUI.java211
-rw-r--r--libjava/classpath/javax/swing/plaf/UIResource.java59
-rw-r--r--libjava/classpath/javax/swing/plaf/ViewportUI.java60
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java422
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicBorders.java1768
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java370
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java636
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java102
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java75
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java344
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java181
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java151
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java1410
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java1104
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java592
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java470
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java586
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java96
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java1437
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java69
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java821
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicHTML.java471
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java328
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java1015
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java1786
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java542
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicListUI.java1421
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java1734
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java481
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java1339
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java678
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java1404
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java132
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java74
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java114
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java1019
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java962
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java101
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java301
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java292
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java1513
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java823
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java252
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java2339
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java561
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java1077
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java1623
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java4003
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java566
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java1410
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java116
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java118
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java92
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java1538
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java134
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java124
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java1610
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java292
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java3939
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java75
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/ComboPopup.java103
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java78
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java91
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.pngbin0 -> 454 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.pngbin0 -> 857 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.pngbin0 -> 1787 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.pngbin0 -> 5267 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.pngbin0 -> 14735 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.pngbin0 -> 3180 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.pngbin0 -> 2667 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.pngbin0 -> 8803 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.pngbin0 -> 5976 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.pngbin0 -> 7169 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.pngbin0 -> 1874 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.pngbin0 -> 4844 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.pngbin0 -> 3771 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.pngbin0 -> 13480 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.pngbin0 -> 4832 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.pngbin0 -> 6884 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.pngbin0 -> 6816 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.pngbin0 -> 3676 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/package.html46
-rw-r--r--libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.diabin0 -> 3085 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.pngbin0 -> 32683 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/doc-files/TreeUI-1.pngbin0 -> 8660 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java305
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalBorders.java1626
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java74
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java298
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java142
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java88
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java294
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java190
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java102
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java372
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java72
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java2120
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java2626
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java457
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java183
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java108
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java1372
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java94
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java77
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java147
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java183
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java1075
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java584
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java483
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java159
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java131
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java475
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java264
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java89
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java1269
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java82
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalTheme.java576
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java230
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java298
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java276
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java317
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/MetalUtils.java597
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/OceanTheme.java318
-rw-r--r--libjava/classpath/javax/swing/plaf/metal/package.html55
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiButtonUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiColorChooserUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiComboBoxUI.java430
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiDesktopIconUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiDesktopPaneUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiFileChooserUI.java511
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiInternalFrameUI.java353
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiLabelUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiListUI.java449
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java243
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiMenuBarUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiMenuItemUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiOptionPaneUI.java398
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiPanelUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiPopupMenuUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiProgressBarUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiRootPaneUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiScrollBarUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiScrollPaneUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiSeparatorUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiSliderUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiSpinnerUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiSplitPaneUI.java494
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiTabbedPaneUI.java447
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiTableHeaderUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiTableUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiTextUI.java617
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiToolBarUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiToolTipUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiTreeUI.java628
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/MultiViewportUI.java352
-rw-r--r--libjava/classpath/javax/swing/plaf/multi/package.html46
-rw-r--r--libjava/classpath/javax/swing/plaf/package.html47
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/ColorType.java135
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/Region.java474
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/SynthConstants.java85
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/SynthContext.java134
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/SynthGraphicsUtils.java289
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java279
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/SynthPainter.java1999
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/SynthStyle.java203
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/SynthStyleFactory.java64
-rw-r--r--libjava/classpath/javax/swing/plaf/synth/package.html47
-rw-r--r--libjava/classpath/javax/swing/table/AbstractTableModel.java303
-rw-r--r--libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java276
-rw-r--r--libjava/classpath/javax/swing/table/DefaultTableColumnModel.java671
-rw-r--r--libjava/classpath/javax/swing/table/DefaultTableModel.java634
-rw-r--r--libjava/classpath/javax/swing/table/JTableHeader.java1055
-rw-r--r--libjava/classpath/javax/swing/table/TableCellEditor.java65
-rw-r--r--libjava/classpath/javax/swing/table/TableCellRenderer.java66
-rw-r--r--libjava/classpath/javax/swing/table/TableColumn.java693
-rw-r--r--libjava/classpath/javax/swing/table/TableColumnModel.java232
-rw-r--r--libjava/classpath/javax/swing/table/TableModel.java134
-rw-r--r--libjava/classpath/javax/swing/table/package.html47
-rw-r--r--libjava/classpath/javax/swing/text/AbstractDocument.java2906
-rw-r--r--libjava/classpath/javax/swing/text/AbstractWriter.java481
-rw-r--r--libjava/classpath/javax/swing/text/AsyncBoxView.java1480
-rw-r--r--libjava/classpath/javax/swing/text/AttributeSet.java195
-rw-r--r--libjava/classpath/javax/swing/text/BadLocationException.java78
-rw-r--r--libjava/classpath/javax/swing/text/BoxView.java1112
-rw-r--r--libjava/classpath/javax/swing/text/Caret.java207
-rw-r--r--libjava/classpath/javax/swing/text/ChangedCharSetException.java100
-rw-r--r--libjava/classpath/javax/swing/text/ComponentView.java494
-rw-r--r--libjava/classpath/javax/swing/text/CompositeView.java792
-rw-r--r--libjava/classpath/javax/swing/text/DateFormatter.java85
-rw-r--r--libjava/classpath/javax/swing/text/DefaultCaret.java1287
-rw-r--r--libjava/classpath/javax/swing/text/DefaultEditorKit.java1702
-rw-r--r--libjava/classpath/javax/swing/text/DefaultFormatter.java433
-rw-r--r--libjava/classpath/javax/swing/text/DefaultFormatterFactory.java280
-rw-r--r--libjava/classpath/javax/swing/text/DefaultHighlighter.java478
-rw-r--r--libjava/classpath/javax/swing/text/DefaultStyledDocument.java2526
-rw-r--r--libjava/classpath/javax/swing/text/DefaultTextUI.java62
-rw-r--r--libjava/classpath/javax/swing/text/Document.java221
-rw-r--r--libjava/classpath/javax/swing/text/DocumentFilter.java83
-rw-r--r--libjava/classpath/javax/swing/text/EditorKit.java98
-rw-r--r--libjava/classpath/javax/swing/text/Element.java54
-rw-r--r--libjava/classpath/javax/swing/text/ElementIterator.java272
-rw-r--r--libjava/classpath/javax/swing/text/EmptyAttributeSet.java153
-rw-r--r--libjava/classpath/javax/swing/text/FieldView.java310
-rw-r--r--libjava/classpath/javax/swing/text/FlowView.java841
-rw-r--r--libjava/classpath/javax/swing/text/GapContent.java1095
-rw-r--r--libjava/classpath/javax/swing/text/GlyphView.java1333
-rw-r--r--libjava/classpath/javax/swing/text/Highlighter.java78
-rw-r--r--libjava/classpath/javax/swing/text/IconView.java175
-rw-r--r--libjava/classpath/javax/swing/text/InternationalFormatter.java356
-rw-r--r--libjava/classpath/javax/swing/text/JTextComponent.java2059
-rw-r--r--libjava/classpath/javax/swing/text/Keymap.java58
-rw-r--r--libjava/classpath/javax/swing/text/LabelView.java322
-rw-r--r--libjava/classpath/javax/swing/text/LayeredHighlighter.java57
-rw-r--r--libjava/classpath/javax/swing/text/LayoutQueue.java116
-rw-r--r--libjava/classpath/javax/swing/text/MaskFormatter.java558
-rw-r--r--libjava/classpath/javax/swing/text/MutableAttributeSet.java117
-rw-r--r--libjava/classpath/javax/swing/text/NavigationFilter.java98
-rw-r--r--libjava/classpath/javax/swing/text/NumberFormatter.java86
-rw-r--r--libjava/classpath/javax/swing/text/ParagraphView.java528
-rw-r--r--libjava/classpath/javax/swing/text/PasswordView.java254
-rw-r--r--libjava/classpath/javax/swing/text/PlainDocument.java292
-rw-r--r--libjava/classpath/javax/swing/text/PlainView.java724
-rw-r--r--libjava/classpath/javax/swing/text/Position.java62
-rw-r--r--libjava/classpath/javax/swing/text/Segment.java297
-rw-r--r--libjava/classpath/javax/swing/text/SimpleAttributeSet.java415
-rw-r--r--libjava/classpath/javax/swing/text/StringContent.java569
-rw-r--r--libjava/classpath/javax/swing/text/Style.java64
-rw-r--r--libjava/classpath/javax/swing/text/StyleConstants.java1083
-rw-r--r--libjava/classpath/javax/swing/text/StyleContext.java990
-rw-r--r--libjava/classpath/javax/swing/text/StyledDocument.java140
-rw-r--r--libjava/classpath/javax/swing/text/StyledEditorKit.java707
-rw-r--r--libjava/classpath/javax/swing/text/TabExpander.java43
-rw-r--r--libjava/classpath/javax/swing/text/TabSet.java210
-rw-r--r--libjava/classpath/javax/swing/text/TabStop.java190
-rw-r--r--libjava/classpath/javax/swing/text/TabableView.java44
-rw-r--r--libjava/classpath/javax/swing/text/TableView.java491
-rw-r--r--libjava/classpath/javax/swing/text/TextAction.java240
-rw-r--r--libjava/classpath/javax/swing/text/Utilities.java730
-rw-r--r--libjava/classpath/javax/swing/text/View.java881
-rw-r--r--libjava/classpath/javax/swing/text/ViewFactory.java50
-rw-r--r--libjava/classpath/javax/swing/text/WrappedPlainView.java795
-rw-r--r--libjava/classpath/javax/swing/text/ZoneView.java442
-rw-r--r--libjava/classpath/javax/swing/text/html/BRView.java70
-rw-r--r--libjava/classpath/javax/swing/text/html/BlockView.java721
-rw-r--r--libjava/classpath/javax/swing/text/html/CSS.java736
-rw-r--r--libjava/classpath/javax/swing/text/html/CSSBorder.java421
-rw-r--r--libjava/classpath/javax/swing/text/html/CSSParser.java561
-rw-r--r--libjava/classpath/javax/swing/text/html/FormSubmitEvent.java123
-rw-r--r--libjava/classpath/javax/swing/text/html/FormView.java870
-rw-r--r--libjava/classpath/javax/swing/text/html/FrameSetView.java274
-rw-r--r--libjava/classpath/javax/swing/text/html/FrameView.java233
-rw-r--r--libjava/classpath/javax/swing/text/html/HRuleView.java189
-rw-r--r--libjava/classpath/javax/swing/text/html/HTML.java1253
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLDocument.java2298
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLEditorKit.java1520
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java130
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLWriter.java1088
-rw-r--r--libjava/classpath/javax/swing/text/html/ImageView.java591
-rw-r--r--libjava/classpath/javax/swing/text/html/InlineView.java307
-rw-r--r--libjava/classpath/javax/swing/text/html/ListView.java128
-rw-r--r--libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java453
-rw-r--r--libjava/classpath/javax/swing/text/html/MultiAttributeSet.java213
-rw-r--r--libjava/classpath/javax/swing/text/html/MultiStyle.java136
-rw-r--r--libjava/classpath/javax/swing/text/html/NullView.java102
-rw-r--r--libjava/classpath/javax/swing/text/html/ObjectView.java110
-rw-r--r--libjava/classpath/javax/swing/text/html/Option.java159
-rw-r--r--libjava/classpath/javax/swing/text/html/ParagraphView.java322
-rw-r--r--libjava/classpath/javax/swing/text/html/ResetableModel.java50
-rw-r--r--libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java82
-rw-r--r--libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java70
-rw-r--r--libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java84
-rw-r--r--libjava/classpath/javax/swing/text/html/SelectListModel.java106
-rw-r--r--libjava/classpath/javax/swing/text/html/StyleSheet.java1455
-rw-r--r--libjava/classpath/javax/swing/text/html/TableView.java974
-rw-r--r--libjava/classpath/javax/swing/text/html/ViewAttributeSet.java163
-rw-r--r--libjava/classpath/javax/swing/text/html/package.html50
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/AttributeList.java294
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/ContentModel.java223
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/DTD.java609
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/DTDConstants.java292
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/DocumentParser.java268
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/Element.java317
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/Entity.java183
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/Parser.java446
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java207
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/TagElement.java142
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/package.html50
-rw-r--r--libjava/classpath/javax/swing/text/package.html46
-rw-r--r--libjava/classpath/javax/swing/text/rtf/ControlWordToken.java86
-rw-r--r--libjava/classpath/javax/swing/text/rtf/RTFEditorKit.java114
-rw-r--r--libjava/classpath/javax/swing/text/rtf/RTFParseException.java65
-rw-r--r--libjava/classpath/javax/swing/text/rtf/RTFParser.java203
-rw-r--r--libjava/classpath/javax/swing/text/rtf/RTFScanner.java294
-rw-r--r--libjava/classpath/javax/swing/text/rtf/TextToken.java65
-rw-r--r--libjava/classpath/javax/swing/text/rtf/Token.java91
-rw-r--r--libjava/classpath/javax/swing/text/rtf/package.html48
-rw-r--r--libjava/classpath/javax/swing/tree/AbstractLayoutCache.java456
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java1217
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java792
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java720
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeModel.java622
-rw-r--r--libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java1202
-rw-r--r--libjava/classpath/javax/swing/tree/ExpandVetoException.java76
-rw-r--r--libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java628
-rw-r--r--libjava/classpath/javax/swing/tree/MutableTreeNode.java104
-rw-r--r--libjava/classpath/javax/swing/tree/RowMapper.java54
-rw-r--r--libjava/classpath/javax/swing/tree/TreeCellEditor.java71
-rw-r--r--libjava/classpath/javax/swing/tree/TreeCellRenderer.java73
-rw-r--r--libjava/classpath/javax/swing/tree/TreeModel.java105
-rw-r--r--libjava/classpath/javax/swing/tree/TreeNode.java113
-rw-r--r--libjava/classpath/javax/swing/tree/TreePath.java312
-rw-r--r--libjava/classpath/javax/swing/tree/TreeSelectionModel.java112
-rw-r--r--libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java657
-rw-r--r--libjava/classpath/javax/swing/tree/package.html47
-rw-r--r--libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java323
-rw-r--r--libjava/classpath/javax/swing/undo/CannotRedoException.java56
-rw-r--r--libjava/classpath/javax/swing/undo/CannotUndoException.java58
-rw-r--r--libjava/classpath/javax/swing/undo/CompoundEdit.java400
-rw-r--r--libjava/classpath/javax/swing/undo/StateEdit.java265
-rw-r--r--libjava/classpath/javax/swing/undo/StateEditable.java114
-rw-r--r--libjava/classpath/javax/swing/undo/UndoManager.java625
-rw-r--r--libjava/classpath/javax/swing/undo/UndoableEdit.java157
-rw-r--r--libjava/classpath/javax/swing/undo/UndoableEditSupport.java272
-rw-r--r--libjava/classpath/javax/swing/undo/package.html46
572 files changed, 238924 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/AbstractAction.java b/libjava/classpath/javax/swing/AbstractAction.java
new file mode 100644
index 000000000..535a3e71e
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractAction.java
@@ -0,0 +1,276 @@
+/* AbstractAction.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.HashMap;
+
+import javax.swing.event.SwingPropertyChangeSupport;
+
+/**
+ * A base class for implementing the {@link Action} interface.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class AbstractAction
+ implements Action, Cloneable, Serializable
+{
+ private static final long serialVersionUID = -6803159439231523484L;
+
+ /**
+ * A flag that indicates whether or not the action is enabled.
+ */
+ protected boolean enabled = true;
+
+ /**
+ * Provides support for property change event notification.
+ */
+ protected SwingPropertyChangeSupport changeSupport =
+ new SwingPropertyChangeSupport(this);
+
+ /**
+ * store
+ */
+ private transient HashMap store = new HashMap();
+
+ /**
+ * Creates a new action with no properties set.
+ */
+ public AbstractAction()
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * Creates a new action with the specified name. The name is stored as a
+ * property with the key {@link Action#NAME}, and no other properties are
+ * initialised.
+ *
+ * @param name the name (<code>null</code> permitted).
+ */
+ public AbstractAction(String name)
+ {
+ putValue(NAME, name);
+ }
+
+ /**
+ * Creates a new action with the specified name and icon. The name is stored
+ * as a property with the key {@link Action#NAME}, the icon is stored as a
+ * property with the key {@link Action#SMALL_ICON}, and no other properties
+ * are initialised.
+ *
+ * @param name the name (<code>null</code> permitted).
+ * @param icon the icon (<code>null</code> permitted).
+ */
+ public AbstractAction(String name, Icon icon)
+ {
+ putValue(NAME, name);
+ putValue(SMALL_ICON, icon);
+ }
+
+ /**
+ * Returns a clone of the action.
+ *
+ * @return A clone of the action.
+ *
+ * @exception CloneNotSupportedException if there is a problem cloning the
+ * action.
+ */
+ protected Object clone() throws CloneNotSupportedException
+ {
+ AbstractAction copy = (AbstractAction) super.clone();
+ copy.store = (HashMap) store.clone();
+ return copy;
+ }
+
+ /**
+ * Returns the value associated with the specified key.
+ *
+ * @param key the key (not <code>null</code>).
+ *
+ * @return The value associated with the specified key, or
+ * <code>null</code> if the key is not found.
+ *
+ * @see #putValue(String, Object)
+ */
+ public Object getValue(String key)
+ {
+ return store.get(key);
+ }
+
+ /**
+ * Sets the value associated with the specified key and sends a
+ * {@link java.beans.PropertyChangeEvent} to all registered listeners.
+ * The standard keys are:
+ * <ul>
+ * <li>{@link #NAME}</li>
+ * <li>{@link #SHORT_DESCRIPTION}</li>
+ * <li>{@link #LONG_DESCRIPTION}</li>
+ * <li>{@link #SMALL_ICON}</li>
+ * <li>{@link #ACTION_COMMAND_KEY}</li>
+ * <li>{@link #ACCELERATOR_KEY}</li>
+ * <li>{@link #MNEMONIC_KEY}</li>
+ * </ul>
+ * Any existing value associated with the key will be overwritten.
+ *
+ * @param key the key (not <code>null</code>).
+ * @param value the value (<code>null</code> permitted).
+ */
+ public void putValue(String key, Object value)
+ {
+ Object old = getValue(key);
+ if ((old == null && value != null) || (old != null && !old.equals(value)))
+ {
+ store.put(key, value);
+ firePropertyChange(key, old, value);
+ }
+ }
+
+ /**
+ * Returns the flag that indicates whether or not the action is enabled.
+ *
+ * @return The flag.
+ *
+ * @see #setEnabled(boolean)
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * Sets the flag that indicates whether or not the action is enabled and, if
+ * the value of the flag changed from the previous setting, sends a
+ * {@link java.beans.PropertyChangeEvent} to all registered listeners (using
+ * the property name 'enabled').
+ *
+ * @param enabled the new flag value.
+ *
+ * @see #isEnabled()
+ */
+ public void setEnabled(boolean enabled)
+ {
+ if (enabled != this.enabled)
+ {
+ this.enabled = enabled;
+ firePropertyChange("enabled", !this.enabled, this.enabled);
+ }
+ }
+
+ /**
+ * Returns an array of the keys for the property values that have been
+ * defined via the {@link #putValue(String, Object)} method (or the class
+ * constructor).
+ *
+ * @return An array of keys.
+ */
+ public Object[] getKeys()
+ {
+ return store.keySet().toArray();
+ }
+
+ /**
+ * Sends a {@link PropertyChangeEvent} for the named property to all
+ * registered listeners.
+ *
+ * @param propertyName the property name.
+ * @param oldValue the old value of the property.
+ * @param newValue the new value of the property.
+ */
+ protected void firePropertyChange(String propertyName, Object oldValue,
+ Object newValue)
+ {
+ changeSupport.firePropertyChange(propertyName, oldValue, newValue);
+ }
+
+ /**
+ * Sends a {@link PropertyChangeEvent} for the named property to all
+ * registered listeners. This private method is called by the
+ * {@link #setEnabled(boolean)} method.
+ *
+ * @param propertyName the property name.
+ * @param oldValue the old value of the property.
+ * @param newValue the new value of the property.
+ */
+ private void firePropertyChange(String propertyName, boolean oldValue,
+ boolean newValue)
+ {
+ changeSupport.firePropertyChange(propertyName, oldValue, newValue);
+ }
+
+ /**
+ * Registers a listener to receive {@link PropertyChangeEvent} notifications
+ * from this action.
+ *
+ * @param listener the listener.
+ *
+ * @see #removePropertyChangeListener(PropertyChangeListener)
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ changeSupport.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Deregisters a listener so that it no longer receives
+ * {@link PropertyChangeEvent} notifications from this action.
+ *
+ * @param listener the listener.
+ *
+ * @see #addPropertyChangeListener(PropertyChangeListener)
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ changeSupport.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Returns all registered listeners.
+ *
+ * @return An array of listeners.
+ *
+ * @since 1.4
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners()
+ {
+ return changeSupport.getPropertyChangeListeners();
+ }
+}
diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java
new file mode 100644
index 000000000..6cbc7751f
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractButton.java
@@ -0,0 +1,2652 @@
+/* AbstractButton.java -- Provides basic button functionality.
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.ItemSelectable;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.image.ImageObserver;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.Enumeration;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleIcon;
+import javax.accessibility.AccessibleRelation;
+import javax.accessibility.AccessibleRelationSet;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ButtonUI;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.Position;
+import javax.swing.text.StyledDocument;
+import javax.swing.text.View;
+
+
+/**
+ * Provides an abstract implementation of common button behaviour,
+ * data model and look &amp; feel.
+ *
+ * <p>This class is supposed to serve as a base class for
+ * several kinds of buttons with similar but non-identical semantics:
+ * toggle buttons (radio buttons and checkboxes), simple push buttons,
+ * menu items, etc.</p>
+ *
+ * <p>Buttons have many properties, some of which are stored in this class
+ * while others are delegated to the button's model. The following properties
+ * are available:</p>
+ *
+ * <table>
+ * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr>
+ *
+ * <tr><td>action </td><td>button</td> <td>no</td></tr>
+ * <tr><td>actionCommand </td><td>model</td> <td>no</td></tr>
+ * <tr><td>borderPainted </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>contentAreaFilled </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>disabledIcon </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>disabledSelectedIcon </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>displayedMnemonicIndex </td><td>button</td> <td>no</td></tr>
+ * <tr><td>enabled </td><td>model</td> <td>no</td></tr>
+ * <tr><td>focusPainted </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>horizontalAlignment </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>horizontalTextPosition </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>icon </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>iconTextGap </td><td>button</td> <td>no</td></tr>
+ * <tr><td>label (same as text) </td><td>model</td> <td>yes</td></tr>
+ * <tr><td>margin </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>multiClickThreshold </td><td>button</td> <td>no</td></tr>
+ * <tr><td>pressedIcon </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>rolloverEnabled </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>rolloverIcon </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>rolloverSelectedIcon </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>selected </td><td>model</td> <td>no</td></tr>
+ * <tr><td>selectedIcon </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>selectedObjects </td><td>button</td> <td>no</td></tr>
+ * <tr><td>text </td><td>model</td> <td>yes</td></tr>
+ * <tr><td>UI </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>verticalAlignment </td><td>button</td> <td>yes</td></tr>
+ * <tr><td>verticalTextPosition </td><td>button</td> <td>yes</td></tr>
+ *
+ * </table>
+ *
+ * <p>The various behavioral aspects of these properties follows:</p>
+ *
+ * <ul>
+ *
+ * <li>When non-bound properties stored in the button change, the button
+ * fires ChangeEvents to its ChangeListeners.</li>
+ *
+ * <li>When bound properties stored in the button change, the button fires
+ * PropertyChangeEvents to its PropertyChangeListeners</li>
+ *
+ * <li>If any of the model's properties change, it fires a ChangeEvent to
+ * its ChangeListeners, which include the button.</li>
+ *
+ * <li>If the button receives a ChangeEvent from its model, it will
+ * propagate the ChangeEvent to its ChangeListeners, with the ChangeEvent's
+ * "source" property set to refer to the button, rather than the model. The
+ * the button will request a repaint, to paint its updated state.</li>
+ *
+ * <li>If the model's "selected" property changes, the model will fire an
+ * ItemEvent to its ItemListeners, which include the button, in addition to
+ * the ChangeEvent which models the property change. The button propagates
+ * ItemEvents directly to its ItemListeners.</li>
+ *
+ * <li>If the model's armed and pressed properties are simultaneously
+ * <code>true</code>, the model will fire an ActionEvent to its
+ * ActionListeners, which include the button. The button will propagate
+ * this ActionEvent to its ActionListeners, with the ActionEvent's "source"
+ * property set to refer to the button, rather than the model.</li>
+ *
+ * </ul>
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Graydon Hoare (graydon@redhat.com)
+ */
+
+public abstract class AbstractButton extends JComponent
+ implements ItemSelectable, SwingConstants
+{
+ private static final long serialVersionUID = -937921345538462020L;
+
+ /**
+ * An extension of ChangeListener to be serializable.
+ */
+ protected class ButtonChangeListener
+ implements ChangeListener, Serializable
+ {
+ private static final long serialVersionUID = 1471056094226600578L;
+
+ /**
+ * The spec has no public/protected constructor for this class, so do we.
+ */
+ ButtonChangeListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notified when the target of the listener changes its state.
+ *
+ * @param ev the ChangeEvent describing the change
+ */
+ public void stateChanged(ChangeEvent ev)
+ {
+ getEventHandler().stateChanged(ev);
+ }
+ }
+
+ /**
+ * The combined event handler for ActionEvent, ChangeEvent and
+ * ItemEvent. This combines ButtonChangeListener, ActionListener
+ */
+ private class EventHandler
+ implements ActionListener, ChangeListener, ItemListener
+ {
+ public void actionPerformed(ActionEvent ev)
+ {
+ fireActionPerformed(ev);
+ }
+
+ public void stateChanged(ChangeEvent ev)
+ {
+ fireStateChanged();
+ repaint();
+ }
+
+ public void itemStateChanged(ItemEvent ev)
+ {
+ fireItemStateChanged(ev);
+ }
+ }
+
+ /** The icon displayed by default. */
+ Icon default_icon;
+
+ /** The icon displayed when the button is pressed. */
+ Icon pressed_icon;
+
+ /** The icon displayed when the button is disabled. */
+ Icon disabledIcon;
+
+ /** The icon displayed when the button is selected. */
+ Icon selectedIcon;
+
+ /** The icon displayed when the button is selected but disabled. */
+ Icon disabledSelectedIcon;
+
+ /** The icon displayed when the button is rolled over. */
+ Icon rolloverIcon;
+
+ /** The icon displayed when the button is selected and rolled over. */
+ Icon rolloverSelectedIcon;
+
+ /** The icon currently displayed. */
+ Icon current_icon;
+
+ /** The text displayed in the button. */
+ String text;
+
+ /**
+ * The gap between icon and text, if both icon and text are
+ * non-<code>null</code>.
+ */
+ int iconTextGap;
+
+ /** The vertical alignment of the button's text and icon. */
+ int verticalAlignment;
+
+ /** The horizontal alignment of the button's text and icon. */
+ int horizontalAlignment;
+
+ /** The horizontal position of the button's text relative to its icon. */
+ int horizontalTextPosition;
+
+ /** The vertical position of the button's text relative to its icon. */
+ int verticalTextPosition;
+
+ /** Whether or not the button paints its border. */
+ boolean borderPainted;
+
+ /** Whether or not the button paints its focus state. */
+ boolean focusPainted;
+
+ /** Whether or not the button fills its content area. */
+ boolean contentAreaFilled;
+
+ /** Whether rollover is enabled. */
+ boolean rollOverEnabled;
+
+ /** The action taken when the button is clicked. */
+ Action action;
+
+ /** The button's current state. */
+ protected ButtonModel model;
+
+ /** The margin between the button's border and its label. */
+ Insets margin;
+
+ /**
+ * A hint to the look and feel class, suggesting which character in the
+ * button's label should be underlined when drawing the label.
+ */
+ int mnemonicIndex;
+
+ /**
+ * Listener the button uses to receive ActionEvents from its model.
+ */
+ protected ActionListener actionListener;
+
+ /**
+ * Listener the button uses to receive ItemEvents from its model.
+ */
+ protected ItemListener itemListener;
+
+ /**
+ * Listener the button uses to receive ChangeEvents from its model.
+ */
+ protected ChangeListener changeListener;
+
+ /**
+ * The event handler for ActionEvent, ItemEvent and ChangeEvent.
+ * This replaces the above three handlers and combines them
+ * into one for efficiency.
+ */
+ private EventHandler eventHandler;
+
+ /**
+ * The time in milliseconds in which clicks get coalesced into a single
+ * <code>ActionEvent</code>.
+ */
+ long multiClickThreshhold;
+
+ /**
+ * Listener the button uses to receive PropertyChangeEvents from its
+ * Action.
+ */
+ PropertyChangeListener actionPropertyChangeListener;
+
+ /** ChangeEvent that is fired to button's ChangeEventListeners */
+ protected ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /**
+ * Indicates if the borderPainted property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientBorderPaintedSet = false;
+
+ /**
+ * Indicates if the rolloverEnabled property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientRolloverEnabledSet = false;
+
+ /**
+ * Indicates if the iconTextGap property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientIconTextGapSet = false;
+
+ /**
+ * Indicates if the contentAreaFilled property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientContentAreaFilledSet = false;
+
+ /**
+ * Fired in a PropertyChangeEvent when the "borderPainted" property changes.
+ */
+ public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "contentAreaFilled" property
+ * changes.
+ */
+ public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY =
+ "contentAreaFilled";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "disabledIcon" property changes.
+ */
+ public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "disabledSelectedIcon" property
+ * changes.
+ */
+ public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY =
+ "disabledSelectedIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "focusPainted" property changes.
+ */
+ public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "horizontalAlignment" property
+ * changes.
+ */
+ public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY =
+ "horizontalAlignment";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "horizontalTextPosition" property
+ * changes.
+ */
+ public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY =
+ "horizontalTextPosition";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "icon" property changes. */
+ public static final String ICON_CHANGED_PROPERTY = "icon";
+
+ /** Fired in a PropertyChangeEvent when the "margin" property changes. */
+ public static final String MARGIN_CHANGED_PROPERTY = "margin";
+
+ /** Fired in a PropertyChangeEvent when the "mnemonic" property changes. */
+ public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
+
+ /** Fired in a PropertyChangeEvent when the "model" property changes. */
+ public static final String MODEL_CHANGED_PROPERTY = "model";
+
+ /** Fired in a PropertyChangeEvent when the "pressedIcon" property changes. */
+ public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "rolloverEnabled" property
+ * changes.
+ */
+ public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY =
+ "rolloverEnabled";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "rolloverIcon" property changes.
+ */
+ public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "rolloverSelectedIcon" property
+ * changes.
+ */
+ public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY =
+ "rolloverSelectedIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "selectedIcon" property changes.
+ */
+ public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
+
+ /** Fired in a PropertyChangeEvent when the "text" property changes. */
+ public static final String TEXT_CHANGED_PROPERTY = "text";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "verticalAlignment" property
+ * changes.
+ */
+ public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY =
+ "verticalAlignment";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "verticalTextPosition" property
+ * changes.
+ */
+ public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY =
+ "verticalTextPosition";
+
+ /**
+ * A Java Accessibility extension of the AbstractButton.
+ */
+ protected abstract class AccessibleAbstractButton
+ extends AccessibleJComponent implements AccessibleAction, AccessibleValue,
+ AccessibleText
+ {
+ private static final long serialVersionUID = -5673062525319836790L;
+
+ protected AccessibleAbstractButton()
+ {
+ // Nothing to do here yet.
+ }
+
+ /**
+ * Returns the accessible state set of this object. In addition to the
+ * superclass's states, the <code>AccessibleAbstractButton</code>
+ * supports the following states: {@link AccessibleState#ARMED},
+ * {@link AccessibleState#FOCUSED}, {@link AccessibleState#PRESSED} and
+ * {@link AccessibleState#CHECKED}.
+ *
+ * @return the current state of this accessible object
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+
+ if (getModel().isArmed())
+ state.add(AccessibleState.ARMED);
+ if (getModel().isPressed())
+ state.add(AccessibleState.PRESSED);
+ if (isSelected())
+ state.add(AccessibleState.CHECKED);
+
+ return state;
+ }
+
+ /**
+ * Returns the accessible name for the button.
+ */
+ public String getAccessibleName()
+ {
+ String result = super.getAccessibleName();
+ if (result == null)
+ result = text;
+ return result;
+ }
+
+ /**
+ * Returns the accessible icons of this object. If the AbstractButton's
+ * icon is an Accessible, and it's AccessibleContext is an AccessibleIcon,
+ * then this AccessibleIcon is returned, otherwise <code>null</code>.
+ *
+ * @return the accessible icons of this object, or <code>null</code> if
+ * there is no accessible icon
+ */
+ public AccessibleIcon[] getAccessibleIcon()
+ {
+ AccessibleIcon[] ret = null;
+ Icon icon = getIcon();
+ if (icon instanceof Accessible)
+ {
+ AccessibleContext ctx = ((Accessible) icon).getAccessibleContext();
+ if (ctx instanceof AccessibleIcon)
+ {
+ ret = new AccessibleIcon[]{ (AccessibleIcon) ctx };
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the accessible relations of this AccessibleAbstractButton.
+ * If the AbstractButton is part of a ButtonGroup, then all the buttons
+ * in this button group are added as targets in a MEMBER_OF relation,
+ * otherwise an empty relation set is returned (from super).
+ *
+ * @return the accessible relations of this AccessibleAbstractButton
+ */
+ public AccessibleRelationSet getAccessibleRelationSet()
+ {
+ AccessibleRelationSet relations = super.getAccessibleRelationSet();
+ ButtonModel model = getModel();
+ if (model instanceof DefaultButtonModel)
+ {
+ ButtonGroup group = ((DefaultButtonModel) model).getGroup();
+ if (group != null)
+ {
+ Object[] target = new Object[group.getButtonCount()];
+ Enumeration els = group.getElements();
+
+ for (int index = 0; els.hasMoreElements(); ++index)
+ {
+ target[index] = els.nextElement();
+ }
+
+ AccessibleRelation rel =
+ new AccessibleRelation(AccessibleRelation.MEMBER_OF);
+ rel.setTarget(target);
+ relations.add(rel);
+ }
+ }
+ return relations;
+ }
+
+ /**
+ * Returns the accessible action associated with this object. For buttons,
+ * this will be <code>this</code>.
+ *
+ * @return <code>this</code>
+ */
+ public AccessibleAction getAccessibleAction()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible value of this AccessibleAbstractButton, which
+ * is always <code>this</code>.
+ *
+ * @return the accessible value of this AccessibleAbstractButton, which
+ * is always <code>this</code>
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the number of accessible actions that are supported by this
+ * object. Buttons support one action by default ('press button'), so this
+ * method always returns <code>1</code>.
+ *
+ * @return <code>1</code>, the number of supported accessible actions
+ */
+ public int getAccessibleActionCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns a description for the action with the specified index or
+ * <code>null</code> if such action does not exist.
+ *
+ * @param actionIndex the zero based index to the actions
+ *
+ * @return a description for the action with the specified index or
+ * <code>null</code> if such action does not exist
+ */
+ public String getAccessibleActionDescription(int actionIndex)
+ {
+ String descr = null;
+ if (actionIndex == 0)
+ {
+ // FIXME: Supply localized descriptions in the UIDefaults.
+ descr = UIManager.getString("AbstractButton.clickText");
+ }
+ return descr;
+ }
+
+ /**
+ * Performs the acccessible action with the specified index on this object.
+ * Since buttons have only one action by default (which is to press the
+ * button), this method performs a 'press button' when the specified index
+ * is <code>0</code> and nothing otherwise.
+ *
+ * @param actionIndex a zero based index into the actions of this button
+ *
+ * @return <code>true</code> if the specified action has been performed
+ * successfully, <code>false</code> otherwise
+ */
+ public boolean doAccessibleAction(int actionIndex)
+ {
+ boolean retVal = false;
+ if (actionIndex == 0)
+ {
+ doClick();
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the current value of this object as a number. This
+ * implementation returns an <code>Integer(1)</code> if the button is
+ * selected, <code>Integer(0)</code> if the button is not selected.
+ *
+ * @return the current value of this object as a number
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ Integer retVal;
+ if (isSelected())
+ retVal = new Integer(1);
+ else
+ retVal = new Integer(0);
+ return retVal;
+ }
+
+ /**
+ * Sets the current accessible value as object. If the specified number
+ * is 0 the button will be deselected, otherwise the button will
+ * be selected.
+ *
+ * @param value 0 for deselected button, other for selected button
+ *
+ * @return <code>true</code> if the value has been set, <code>false</code>
+ * otherwise
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ boolean retVal = false;
+ if (value != null)
+ {
+ if (value.intValue() == 0)
+ setSelected(false);
+ else
+ setSelected(true);
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the minimum accessible value for the AccessibleAbstractButton,
+ * which is <code>0</code>.
+ *
+ * @return the minimimum accessible value for the AccessibleAbstractButton,
+ * which is <code>0</code>
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(0);
+ }
+
+ /**
+ * Returns the maximum accessible value for the AccessibleAbstractButton,
+ * which is <code>1</code>.
+ *
+ * @return the maximum accessible value for the AccessibleAbstractButton,
+ * which is <code>1</code>
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(1);
+ }
+
+ /**
+ * Returns the accessible text for this AccessibleAbstractButton. This
+ * will be <code>null</code> if the button has a non-HTML label, otherwise
+ * <code>this</code>.
+ *
+ * @return the accessible text for this AccessibleAbstractButton
+ */
+ public AccessibleText getAccessibleText()
+ {
+ AccessibleText accessibleText = null;
+ if (getClientProperty(BasicHTML.propertyKey) != null)
+ accessibleText = this;
+
+ return accessibleText;
+ }
+
+ /**
+ * Returns the index of the label's character at the specified point,
+ * relative to the local bounds of the button. This only works for
+ * HTML labels.
+ *
+ * @param p the point, relative to the buttons local bounds
+ *
+ * @return the index of the label's character at the specified point
+ */
+ public int getIndexAtPoint(Point p)
+ {
+ int index = -1;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
+ index = view.viewToModel(p.x, p.y, shape, new Position.Bias[1]);
+ }
+ return index;
+ }
+
+ /**
+ * Returns the bounds of the character at the specified index of the
+ * button's label. This will only work for HTML labels.
+ *
+ * @param i the index of the character of the label
+ *
+ * @return the bounds of the character at the specified index of the
+ * button's label
+ */
+ public Rectangle getCharacterBounds(int i)
+ {
+ Rectangle rect = null;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
+ try
+ {
+ Shape s = view.modelToView(i, shape, Position.Bias.Forward);
+ rect = s.getBounds();
+ }
+ catch (BadLocationException ex)
+ {
+ rect = null;
+ }
+ }
+ return rect;
+ }
+
+ /**
+ * Returns the number of characters in the button's label.
+ *
+ * @return the bounds of the character at the specified index of the
+ * button's label
+ */
+ public int getCharCount()
+ {
+ int charCount;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ charCount = view.getDocument().getLength();
+ }
+ else
+ {
+ charCount = getAccessibleName().length();
+ }
+ return charCount;
+ }
+
+ /**
+ * This always returns <code>-1</code> since there is no caret in a button.
+ *
+ * @return <code>-1</code> since there is no caret in a button
+ */
+ public int getCaretPosition()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns the character, word or sentence at the specified index. The
+ * <code>part</code> parameter determines what is returned, the character,
+ * word or sentence after the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence after <code>index</code>
+ */
+ public String getAtIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index));
+ break;
+ case AccessibleText.WORD:
+ startIndex = text.lastIndexOf(' ', index);
+ endIndex = text.indexOf(' ', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ startIndex = text.lastIndexOf('.', index);
+ endIndex = text.indexOf('.', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the character, word or sentence after the specified index. The
+ * <code>part</code> parameter determines what is returned, the character,
+ * word or sentence after the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence after <code>index</code>
+ */
+ public String getAfterIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index + 1));
+ break;
+ case AccessibleText.WORD:
+ startIndex = text.indexOf(' ', index);
+ endIndex = text.indexOf(' ', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ startIndex = text.indexOf('.', index);
+ endIndex = text.indexOf('.', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the character, word or sentence before the specified index. The
+ * <code>part</code> parameter determines what is returned, the character,
+ * word or sentence before the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence before <code>index</code>
+ */
+ public String getBeforeIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index - 1));
+ break;
+ case AccessibleText.WORD:
+ endIndex = text.lastIndexOf(' ', index);
+ if (endIndex == -1)
+ endIndex = 0;
+ startIndex = text.lastIndexOf(' ', endIndex - 1);
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ endIndex = text.lastIndexOf('.', index);
+ if (endIndex == -1)
+ endIndex = 0;
+ startIndex = text.lastIndexOf('.', endIndex - 1);
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the text attribute for the character at the specified character
+ * index.
+ *
+ * @param i the character index
+ *
+ * @return the character attributes for the specified character or
+ * <code>null</code> if the character has no attributes
+ */
+ public AttributeSet getCharacterAttribute(int i)
+ {
+ AttributeSet atts = null;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Document doc = view.getDocument();
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument sDoc = (StyledDocument) doc;
+ Element charEl = sDoc.getCharacterElement(i);
+ if (charEl != null)
+ atts = charEl.getAttributes();
+ }
+ }
+ return atts;
+ }
+
+ /**
+ * This always returns <code>-1</code> since
+ * button labels can't be selected.
+ *
+ * @return <code>-1</code>, button labels can't be selected
+ */
+ public int getSelectionStart()
+ {
+ return -1;
+ }
+
+ /**
+ * This always returns <code>-1</code> since
+ * button labels can't be selected.
+ *
+ * @return <code>-1</code>, button labels can't be selected
+ */
+ public int getSelectionEnd()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns the selected text. This always returns <code>null</code> since
+ * button labels can't be selected.
+ *
+ * @return <code>null</code>, button labels can't be selected
+ */
+ public String getSelectedText()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Creates a new AbstractButton object. Subclasses should call the following
+ * sequence in their constructor in order to initialize the button correctly:
+ * <pre>
+ * super();
+ * init(text, icon);
+ * </pre>
+ *
+ * The {@link #init(String, Icon)} method is not called automatically by this
+ * constructor.
+ *
+ * @see #init(String, Icon)
+ */
+ public AbstractButton()
+ {
+ horizontalAlignment = CENTER;
+ horizontalTextPosition = TRAILING;
+ verticalAlignment = CENTER;
+ verticalTextPosition = CENTER;
+ borderPainted = true;
+ contentAreaFilled = true;
+ focusPainted = true;
+ setFocusable(true);
+ setAlignmentX(CENTER_ALIGNMENT);
+ setAlignmentY(CENTER_ALIGNMENT);
+ setDisplayedMnemonicIndex(-1);
+ setOpaque(true);
+ text = "";
+ // testing on JRE1.5 shows that the iconTextGap default value is
+ // hard-coded here and the 'Button.iconTextGap' setting in the
+ // UI defaults is ignored, at least by the MetalLookAndFeel
+ iconTextGap = 4;
+ }
+
+ /**
+ * Get the model the button is currently using.
+ *
+ * @return The current model
+ */
+ public ButtonModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Set the model the button is currently using. This un-registers all
+ * listeners associated with the current model, and re-registers them
+ * with the new model.
+ *
+ * @param newModel The new model
+ */
+ public void setModel(ButtonModel newModel)
+ {
+ if (newModel == model)
+ return;
+
+ if (model != null)
+ {
+ model.removeActionListener(actionListener);
+ actionListener = null;
+ model.removeChangeListener(changeListener);
+ changeListener = null;
+ model.removeItemListener(itemListener);
+ itemListener = null;
+ }
+ ButtonModel old = model;
+ model = newModel;
+ if (model != null)
+ {
+ actionListener = createActionListener();
+ model.addActionListener(actionListener);
+ changeListener = createChangeListener();
+ model.addChangeListener(changeListener);
+ itemListener = createItemListener();
+ model.addItemListener(itemListener);
+ }
+ firePropertyChange(MODEL_CHANGED_PROPERTY, old, model);
+ revalidate();
+ repaint();
+ }
+
+ protected void init(String text, Icon icon)
+ {
+ // If text is null, we fall back to the empty
+ // string (which is set using AbstractButton's
+ // constructor).
+ // This way the behavior of the JDK is matched.
+ if(text != null)
+ setText(text);
+
+ if (icon != null)
+ default_icon = icon;
+
+ updateUI();
+ }
+
+ /**
+ * <p>Returns the action command string for this button's model.</p>
+ *
+ * <p>If the action command was set to <code>null</code>, the button's
+ * text (label) is returned instead.</p>
+ *
+ * @return The current action command string from the button's model
+ */
+ public String getActionCommand()
+ {
+ String ac = model.getActionCommand();
+ if (ac != null)
+ return ac;
+ else
+ return text;
+ }
+
+ /**
+ * Sets the action command string for this button's model.
+ *
+ * @param actionCommand The new action command string to set in the button's
+ * model.
+ */
+ public void setActionCommand(String actionCommand)
+ {
+ if (model != null)
+ model.setActionCommand(actionCommand);
+ }
+
+ /**
+ * Adds an ActionListener to the button's listener list. When the
+ * button's model is clicked it fires an ActionEvent, and these
+ * listeners will be called.
+ *
+ * @param l The new listener to add
+ */
+ public void addActionListener(ActionListener l)
+ {
+ listenerList.add(ActionListener.class, l);
+ }
+
+ /**
+ * Removes an ActionListener from the button's listener list.
+ *
+ * @param l The listener to remove
+ */
+ public void removeActionListener(ActionListener l)
+ {
+ listenerList.remove(ActionListener.class, l);
+ }
+
+ /**
+ * Returns all added <code>ActionListener</code> objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) listenerList.getListeners(ActionListener.class);
+ }
+
+ /**
+ * Adds an ItemListener to the button's listener list. When the button's
+ * model changes state (between any of ARMED, ENABLED, PRESSED, ROLLOVER
+ * or SELECTED) it fires an ItemEvent, and these listeners will be
+ * called.
+ *
+ * @param l The new listener to add
+ */
+ public void addItemListener(ItemListener l)
+ {
+ listenerList.add(ItemListener.class, l);
+ }
+
+ /**
+ * Removes an ItemListener from the button's listener list.
+ *
+ * @param l The listener to remove
+ */
+ public void removeItemListener(ItemListener l)
+ {
+ listenerList.remove(ItemListener.class, l);
+ }
+
+ /**
+ * Returns all added <code>ItemListener</code> objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public ItemListener[] getItemListeners()
+ {
+ return (ItemListener[]) listenerList.getListeners(ItemListener.class);
+ }
+
+ /**
+ * Adds a ChangeListener to the button's listener list. When the button's
+ * model changes any of its (non-bound) properties, these listeners will be
+ * called.
+ *
+ * @param l The new listener to add
+ */
+ public void addChangeListener(ChangeListener l)
+ {
+ listenerList.add(ChangeListener.class, l);
+ }
+
+ /**
+ * Removes a ChangeListener from the button's listener list.
+ *
+ * @param l The listener to remove
+ */
+ public void removeChangeListener(ChangeListener l)
+ {
+ listenerList.remove(ChangeListener.class, l);
+ }
+
+ /**
+ * Returns all added <code>ChangeListener</code> objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Calls {@link ItemListener#itemStateChanged} on each ItemListener in
+ * the button's listener list.
+ *
+ * @param e The event signifying that the button's model changed state
+ */
+ protected void fireItemStateChanged(ItemEvent e)
+ {
+ e.setSource(this);
+ ItemListener[] listeners = getItemListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].itemStateChanged(e);
+ }
+
+ /**
+ * Calls {@link ActionListener#actionPerformed} on each {@link
+ * ActionListener} in the button's listener list.
+ *
+ * @param e The event signifying that the button's model was clicked
+ */
+ protected void fireActionPerformed(ActionEvent e)
+ {
+ // Dispatch a copy of the given ActionEvent in order to
+ // set the source and action command correctly.
+ ActionEvent ae = new ActionEvent(
+ this,
+ e.getID(),
+ getActionCommand(),
+ e.getWhen(),
+ e.getModifiers());
+
+ ActionListener[] listeners = getActionListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].actionPerformed(ae);
+ }
+
+ /**
+ * Calls {@link ChangeListener#stateChanged} on each {@link ChangeListener}
+ * in the button's listener list.
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Get the current keyboard mnemonic value. This value corresponds to a
+ * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
+ * codes) and is used to activate the button when pressed in conjunction
+ * with the "mouseless modifier" of the button's look and feel class, and
+ * when focus is in one of the button's ancestors.
+ *
+ * @return The button's current keyboard mnemonic
+ */
+ public int getMnemonic()
+ {
+ ButtonModel mod = getModel();
+ if (mod != null)
+ return mod.getMnemonic();
+ return -1;
+ }
+
+ /**
+ * Set the current keyboard mnemonic value. This value corresponds to a
+ * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
+ * codes) and is used to activate the button when pressed in conjunction
+ * with the "mouseless modifier" of the button's look and feel class, and
+ * when focus is in one of the button's ancestors.
+ *
+ * @param mne A new mnemonic to use for the button
+ */
+ public void setMnemonic(char mne)
+ {
+ setMnemonic((int) mne);
+ }
+
+ /**
+ * Set the current keyboard mnemonic value. This value corresponds to a
+ * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
+ * codes) and is used to activate the button when pressed in conjunction
+ * with the "mouseless modifier" of the button's look and feel class, and
+ * when focus is in one of the button's ancestors.
+ *
+ * @param mne A new mnemonic to use for the button
+ */
+ public void setMnemonic(int mne)
+ {
+ ButtonModel mod = getModel();
+ int old = -1;
+ if (mod != null)
+ old = mod.getMnemonic();
+
+ if (old != mne)
+ {
+ if (mod != null)
+ mod.setMnemonic(mne);
+
+ if (text != null && !text.equals(""))
+ {
+ // Since lower case char = upper case char for
+ // mnemonic, we will convert both text and mnemonic
+ // to upper case before checking if mnemonic character occurs
+ // in the menu item text.
+ int upperCaseMne = Character.toUpperCase((char) mne);
+ String upperCaseText = text.toUpperCase();
+ setDisplayedMnemonicIndex(upperCaseText.indexOf(upperCaseMne));
+ }
+
+ firePropertyChange(MNEMONIC_CHANGED_PROPERTY, old, mne);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Sets the button's mnemonic index. The mnemonic index is a hint to the
+ * look and feel class, suggesting which character in the button's label
+ * should be underlined when drawing the label. If the mnemonic index is
+ * -1, no mnemonic will be displayed.
+ *
+ * If no mnemonic index is set, the button will choose a mnemonic index
+ * by default, which will be the first occurrence of the mnemonic
+ * character in the button's text.
+ *
+ * @param index An offset into the "text" property of the button
+ * @throws IllegalArgumentException If <code>index</code> is not within the
+ * range of legal offsets for the "text" property of the button.
+ * @since 1.4
+ */
+
+ public void setDisplayedMnemonicIndex(int index)
+ {
+ if (index < -1 || (text != null && index >= text.length()))
+ throw new IllegalArgumentException();
+
+ mnemonicIndex = index;
+ }
+
+ /**
+ * Get the button's mnemonic index, which is an offset into the button's
+ * "text" property. The character specified by this offset should be
+ * underlined when the look and feel class draws this button.
+ *
+ * @return An index into the button's "text" property
+ */
+ public int getDisplayedMnemonicIndex()
+ {
+ return mnemonicIndex;
+ }
+
+
+ /**
+ * Set the "rolloverEnabled" property. When rollover is enabled, and the
+ * look and feel supports it, the button will change its icon to
+ * rolloverIcon, when the mouse passes over it.
+ *
+ * @param r Whether or not to enable rollover icon changes
+ */
+ public void setRolloverEnabled(boolean r)
+ {
+ clientRolloverEnabledSet = true;
+ if (rollOverEnabled != r)
+ {
+ rollOverEnabled = r;
+ firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, !r, r);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Returns whether or not rollover icon changes are enabled on the
+ * button.
+ *
+ * @return The state of the "rolloverEnabled" property
+ */
+ public boolean isRolloverEnabled()
+ {
+ return rollOverEnabled;
+ }
+
+ /**
+ * Set the value of the button's "selected" property. Selection is only
+ * meaningful for toggle-type buttons (check boxes, radio buttons).
+ *
+ * @param s New value for the property
+ */
+ public void setSelected(boolean s)
+ {
+ ButtonModel mod = getModel();
+ if (mod != null)
+ mod.setSelected(s);
+ }
+
+ /**
+ * Get the value of the button's "selected" property. Selection is only
+ * meaningful for toggle-type buttons (check boxes, radio buttons).
+ *
+ * @return The value of the property
+ */
+ public boolean isSelected()
+ {
+ ButtonModel mod = getModel();
+ if (mod != null)
+ return mod.isSelected();
+ return false;
+ }
+
+ /**
+ * Enables or disables the button. A button will neither be selectable
+ * nor preform any actions unless it is enabled.
+ *
+ * @param b Whether or not to enable the button
+ */
+ public void setEnabled(boolean b)
+ {
+ // Do nothing if state does not change.
+ if (b == isEnabled())
+ return;
+ super.setEnabled(b);
+ setFocusable(b);
+ ButtonModel mod = getModel();
+ if (mod != null)
+ mod.setEnabled(b);
+ }
+
+ /**
+ * Set the horizontal alignment of the button's text and icon. The
+ * alignment is a numeric constant from {@link SwingConstants}. It must
+ * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>. The default is
+ * <code>CENTER</code>.
+ *
+ * @return The current horizontal alignment
+ *
+ * @see #setHorizontalAlignment(int)
+ */
+ public int getHorizontalAlignment()
+ {
+ return horizontalAlignment;
+ }
+
+ /**
+ * Set the horizontal alignment of the button's text and icon. The
+ * alignment is a numeric constant from {@link SwingConstants}. It must
+ * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>. The default is
+ * <code>CENTER</code>.
+ *
+ * @param a The new horizontal alignment
+ * @throws IllegalArgumentException If alignment is not one of the legal
+ * constants.
+ *
+ * @see #getHorizontalAlignment()
+ */
+ public void setHorizontalAlignment(int a)
+ {
+ if (horizontalAlignment == a)
+ return;
+ if (a != LEFT && a != CENTER && a != RIGHT && a != LEADING
+ && a != TRAILING)
+ throw new IllegalArgumentException("Invalid alignment.");
+ int old = horizontalAlignment;
+ horizontalAlignment = a;
+ firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the horizontal position of the button's text relative to its
+ * icon. The position is a numeric constant from {@link
+ * SwingConstants}. It must be one of: <code>RIGHT</code>,
+ * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
+ * <code>TRAILING</code>. The default is <code>TRAILING</code>.
+ *
+ * @return The current horizontal text position
+ */
+ public int getHorizontalTextPosition()
+ {
+ return horizontalTextPosition;
+ }
+
+ /**
+ * Set the horizontal position of the button's text relative to its
+ * icon. The position is a numeric constant from {@link
+ * SwingConstants}. It must be one of: <code>RIGHT</code>,
+ * <code>LEFT</code>, <code>CENTER</code>, <code>LEADING</code> or
+ * <code>TRAILING</code>. The default is <code>TRAILING</code>.
+ *
+ * @param t The new horizontal text position
+ * @throws IllegalArgumentException If position is not one of the legal
+ * constants.
+ */
+ public void setHorizontalTextPosition(int t)
+ {
+ if (horizontalTextPosition == t)
+ return;
+ if (t != LEFT && t != CENTER && t != RIGHT && t != LEADING
+ && t != TRAILING)
+ throw new IllegalArgumentException("Invalid alignment.");
+
+ int old = horizontalTextPosition;
+ horizontalTextPosition = t;
+ firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the vertical alignment of the button's text and icon. The
+ * alignment is a numeric constant from {@link SwingConstants}. It must
+ * be one of: <code>CENTER</code>, <code>TOP</code>, or
+ * <code>BOTTOM</code>. The default is <code>CENTER</code>.
+ *
+ * @return The current vertical alignment
+ *
+ * @see #setVerticalAlignment(int)
+ */
+ public int getVerticalAlignment()
+ {
+ return verticalAlignment;
+ }
+
+ /**
+ * Set the vertical alignment of the button's text and icon. The
+ * alignment is a numeric constant from {@link SwingConstants}. It must
+ * be one of: <code>CENTER</code>, <code>TOP</code>, or
+ * <code>BOTTOM</code>. The default is <code>CENTER</code>.
+ *
+ * @param a The new vertical alignment
+ * @throws IllegalArgumentException If alignment is not one of the legal
+ * constants.
+ *
+ * @see #getVerticalAlignment()
+ */
+ public void setVerticalAlignment(int a)
+ {
+ if (verticalAlignment == a)
+ return;
+ if (a != TOP && a != CENTER && a != BOTTOM)
+ throw new IllegalArgumentException("Invalid alignment.");
+
+ int old = verticalAlignment;
+ verticalAlignment = a;
+ firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the vertical position of the button's text relative to its
+ * icon. The alignment is a numeric constant from {@link
+ * SwingConstants}. It must be one of: <code>CENTER</code>,
+ * <code>TOP</code>, or <code>BOTTOM</code>. The default is
+ * <code>CENTER</code>.
+ *
+ * @return The current vertical position
+ */
+ public int getVerticalTextPosition()
+ {
+ return verticalTextPosition;
+ }
+
+ /**
+ * Set the vertical position of the button's text relative to its
+ * icon. The alignment is a numeric constant from {@link
+ * SwingConstants}. It must be one of: <code>CENTER</code>,
+ * <code>TOP</code>, or <code>BOTTOM</code>. The default is
+ * <code>CENTER</code>.
+ *
+ * @param t The new vertical position
+ * @throws IllegalArgumentException If position is not one of the legal
+ * constants.
+ */
+ public void setVerticalTextPosition(int t)
+ {
+ if (verticalTextPosition == t)
+ return;
+ if (t != TOP && t != CENTER && t != BOTTOM)
+ throw new IllegalArgumentException("Invalid alignment.");
+
+ int old = verticalTextPosition;
+ verticalTextPosition = t;
+ firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the "borderPainted" property. If set to
+ * <code>false</code>, the button's look and feel class should not paint
+ * a border for the button. The default is <code>true</code>.
+ *
+ * @return The current value of the property.
+ */
+ public boolean isBorderPainted()
+ {
+ return borderPainted;
+ }
+
+ /**
+ * Set the value of the "borderPainted" property. If set to
+ * <code>false</code>, the button's look and feel class should not paint
+ * a border for the button. The default is <code>true</code>.
+ *
+ * @param b The new value of the property.
+ */
+ public void setBorderPainted(boolean b)
+ {
+ clientBorderPaintedSet = true;
+ if (borderPainted == b)
+ return;
+ boolean old = borderPainted;
+ borderPainted = b;
+ firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the value of the "action" property.
+ *
+ * @return The current value of the "action" property
+ */
+ public Action getAction()
+ {
+ return action;
+ }
+
+ /**
+ * <p>Set the button's "action" property, subscribing the new action to the
+ * button, as an ActionListener, if it is not already subscribed. The old
+ * Action, if it exists, is unsubscribed, and the button is unsubscribed
+ * from the old Action if it was previously subscribed as a
+ * PropertyChangeListener.</p>
+ *
+ * <p>This method also configures several of the button's properties from
+ * the Action, by calling {@link #configurePropertiesFromAction}, and
+ * subscribes the button to the Action as a PropertyChangeListener.
+ * Subsequent changes to the Action will thus reconfigure the button
+ * automatically.</p>
+ *
+ * @param a The new value of the "action" property
+ */
+ public void setAction(Action a)
+ {
+ if (action != null)
+ {
+ action.removePropertyChangeListener(actionPropertyChangeListener);
+ removeActionListener(action);
+ if (actionPropertyChangeListener != null)
+ {
+ action.removePropertyChangeListener(actionPropertyChangeListener);
+ actionPropertyChangeListener = null;
+ }
+ }
+
+ Action old = action;
+ action = a;
+ configurePropertiesFromAction(action);
+ if (action != null)
+ {
+ actionPropertyChangeListener = createActionPropertyChangeListener(a);
+ action.addPropertyChangeListener(actionPropertyChangeListener);
+ addActionListener(action);
+ }
+ }
+
+ /**
+ * Return the button's default "icon" property.
+ *
+ * @return The current default icon
+ */
+ public Icon getIcon()
+ {
+ return default_icon;
+ }
+
+ /**
+ * Set the button's default "icon" property. This icon is used as a basis
+ * for the pressed and disabled icons, if none are explicitly set.
+ *
+ * @param i The new default icon
+ */
+ public void setIcon(Icon i)
+ {
+ if (default_icon == i)
+ return;
+
+ Icon old = default_icon;
+ default_icon = i;
+ firePropertyChange(ICON_CHANGED_PROPERTY, old, i);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's "text" property. This property is synonymous with
+ * the "label" property.
+ *
+ * @return The current "text" property
+ */
+ public String getText()
+ {
+ return text;
+ }
+
+ /**
+ * Set the button's "label" property. This property is synonymous with the
+ * "text" property.
+ *
+ * @param label The new "label" property
+ *
+ * @deprecated use <code>setText(text)</code>
+ */
+ public void setLabel(String label)
+ {
+ setText(label);
+ }
+
+ /**
+ * Return the button's "label" property. This property is synonymous with
+ * the "text" property.
+ *
+ * @return The current "label" property
+ *
+ * @deprecated use <code>getText()</code>
+ */
+ public String getLabel()
+ {
+ return getText();
+ }
+
+ /**
+ * Set the button's "text" property. This property is synonymous with the
+ * "label" property.
+ *
+ * @param t The new "text" property
+ */
+ public void setText(String t)
+ {
+ if (text == t)
+ return;
+
+ String old = text;
+ text = t;
+ firePropertyChange(TEXT_CHANGED_PROPERTY, old, t);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #iconTextGap} property.
+ *
+ * @param i The new value of the property
+ *
+ * @since 1.4
+ */
+ public void setIconTextGap(int i)
+ {
+ clientIconTextGapSet = true;
+ if (iconTextGap == i)
+ return;
+
+ int old = iconTextGap;
+ iconTextGap = i;
+ firePropertyChange("iconTextGap", new Integer(old), new Integer(i));
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the value of the {@link #iconTextGap} property.
+ *
+ * @return The current value of the property
+ *
+ * @since 1.4
+ */
+ public int getIconTextGap()
+ {
+ return iconTextGap;
+ }
+
+ /**
+ * Return the button's "margin" property, which is an {@link Insets} object
+ * describing the distance between the button's border and its text and
+ * icon.
+ *
+ * @return The current "margin" property
+ */
+ public Insets getMargin()
+ {
+ return margin;
+ }
+
+ /**
+ * Set the button's "margin" property, which is an {@link Insets} object
+ * describing the distance between the button's border and its text and
+ * icon.
+ *
+ * @param m The new "margin" property
+ */
+ public void setMargin(Insets m)
+ {
+ if (margin == m)
+ return;
+
+ Insets old = margin;
+ margin = m;
+ firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's "pressedIcon" property. The look and feel class
+ * should paint this icon when the "pressed" property of the button's
+ * {@link ButtonModel} is <code>true</code>. This property may be
+ * <code>null</code>, in which case the default icon is used.
+ *
+ * @return The current "pressedIcon" property
+ */
+ public Icon getPressedIcon()
+ {
+ return pressed_icon;
+ }
+
+ /**
+ * Set the button's "pressedIcon" property. The look and feel class
+ * should paint this icon when the "pressed" property of the button's
+ * {@link ButtonModel} is <code>true</code>. This property may be
+ * <code>null</code>, in which case the default icon is used.
+ *
+ * @param pressedIcon The new "pressedIcon" property
+ */
+ public void setPressedIcon(Icon pressedIcon)
+ {
+ if (pressed_icon == pressedIcon)
+ return;
+
+ Icon old = pressed_icon;
+ pressed_icon = pressedIcon;
+ firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's "disabledIcon" property. The look and feel class
+ * should paint this icon when the "enabled" property of the button's
+ * {@link ButtonModel} is <code>false</code>. This property may be
+ * <code>null</code>, in which case an icon is constructed, based on the
+ * default icon.
+ *
+ * @return The current "disabledIcon" property
+ */
+ public Icon getDisabledIcon()
+ {
+ if (disabledIcon == null && default_icon instanceof ImageIcon)
+ {
+ Image iconImage = ((ImageIcon) default_icon).getImage();
+ Image grayImage = GrayFilter.createDisabledImage(iconImage);
+ disabledIcon = new ImageIcon(grayImage);
+ }
+
+ return disabledIcon;
+ }
+
+ /**
+ * Set the button's "disabledIcon" property. The look and feel class should
+ * paint this icon when the "enabled" property of the button's {@link
+ * ButtonModel} is <code>false</code>. This property may be
+ * <code>null</code>, in which case an icon is constructed, based on the
+ * default icon.
+ *
+ * @param d The new "disabledIcon" property
+ */
+ public void setDisabledIcon(Icon d)
+ {
+ if (disabledIcon == d)
+ return;
+ Icon old = disabledIcon;
+ disabledIcon = d;
+ firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, old, d);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's "paintFocus" property. This property controls
+ * whether or not the look and feel class will paint a special indicator
+ * of focus state for the button. If it is false, the button still paints
+ * when focused, but no special decoration is painted to indicate the
+ * presence of focus.
+ *
+ * @return The current "paintFocus" property
+ */
+ public boolean isFocusPainted()
+ {
+ return focusPainted;
+ }
+
+ /**
+ * Set the button's "paintFocus" property. This property controls whether
+ * or not the look and feel class will paint a special indicator of focus
+ * state for the button. If it is false, the button still paints when
+ * focused, but no special decoration is painted to indicate the presence
+ * of focus.
+ *
+ * @param p The new "paintFocus" property
+ */
+ public void setFocusPainted(boolean p)
+ {
+ if (focusPainted == p)
+ return;
+
+ boolean old = focusPainted;
+ focusPainted = p;
+ firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Verifies that a particular key is one of the valid constants used for
+ * describing horizontal alignment and positioning. The valid constants
+ * are the following members of {@link SwingConstants}:
+ * <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>.
+ *
+ * @param key The key to check
+ * @param exception A message to include in an IllegalArgumentException
+ *
+ * @return the value of key
+ *
+ * @throws IllegalArgumentException If key is not one of the valid constants
+ *
+ * @see #setHorizontalTextPosition(int)
+ * @see #setHorizontalAlignment(int)
+ */
+ protected int checkHorizontalKey(int key, String exception)
+ {
+ switch (key)
+ {
+ case SwingConstants.RIGHT:
+ case SwingConstants.LEFT:
+ case SwingConstants.CENTER:
+ case SwingConstants.LEADING:
+ case SwingConstants.TRAILING:
+ break;
+ default:
+ throw new IllegalArgumentException(exception);
+ }
+ return key;
+ }
+
+ /**
+ * Verifies that a particular key is one of the valid constants used for
+ * describing vertical alignment and positioning. The valid constants are
+ * the following members of {@link SwingConstants}: <code>TOP</code>,
+ * <code>BOTTOM</code> or <code>CENTER</code>.
+ *
+ * @param key The key to check
+ * @param exception A message to include in an IllegalArgumentException
+ *
+ * @return the value of key
+ *
+ * @throws IllegalArgumentException If key is not one of the valid constants
+ *
+ * @see #setVerticalTextPosition(int)
+ * @see #setVerticalAlignment(int)
+ */
+ protected int checkVerticalKey(int key, String exception)
+ {
+ switch (key)
+ {
+ case SwingConstants.TOP:
+ case SwingConstants.BOTTOM:
+ case SwingConstants.CENTER:
+ break;
+ default:
+ throw new IllegalArgumentException(exception);
+ }
+ return key;
+ }
+
+ /**
+ * Configure various properties of the button by reading properties
+ * of an {@link Action}. The mapping of properties is as follows:
+ *
+ * <table>
+ *
+ * <tr><th>Action keyed property</th> <th>AbstractButton property</th></tr>
+ *
+ * <tr><td>NAME </td> <td>text </td></tr>
+ * <tr><td>SMALL_ICON </td> <td>icon </td></tr>
+ * <tr><td>SHORT_DESCRIPTION </td> <td>toolTipText </td></tr>
+ * <tr><td>MNEMONIC_KEY </td> <td>mnemonic </td></tr>
+ * <tr><td>ACTION_COMMAND_KEY </td> <td>actionCommand </td></tr>
+ *
+ * </table>
+ *
+ * <p>In addition, this method always sets the button's "enabled" property to
+ * the value of the Action's "enabled" property.</p>
+ *
+ * <p>If the provided Action is <code>null</code>, the text, icon, and
+ * toolTipText properties of the button are set to <code>null</code>, and
+ * the "enabled" property is set to <code>true</code>; the mnemonic and
+ * actionCommand properties are unchanged.</p>
+ *
+ * @param a An Action to configure the button from
+ */
+ protected void configurePropertiesFromAction(Action a)
+ {
+ if (a == null)
+ {
+ setText(null);
+ setIcon(null);
+ setEnabled(true);
+ setToolTipText(null);
+ }
+ else
+ {
+ setText((String) (a.getValue(Action.NAME)));
+ setIcon((Icon) (a.getValue(Action.SMALL_ICON)));
+ setEnabled(a.isEnabled());
+ setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
+ if (a.getValue(Action.MNEMONIC_KEY) != null)
+ setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue());
+ String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY));
+
+ // Set actionCommand to button's text by default if it is not specified
+ if (actionCommand != null)
+ setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY)));
+ else
+ setActionCommand(getText());
+ }
+ }
+
+ /**
+ * <p>A factory method which should return an {@link ActionListener} that
+ * propagates events from the button's {@link ButtonModel} to any of the
+ * button's ActionListeners. By default, this is an inner class which
+ * calls {@link AbstractButton#fireActionPerformed} with a modified copy
+ * of the incoming model {@link ActionEvent}.</p>
+ *
+ * <p>The button calls this method during construction, stores the
+ * resulting ActionListener in its <code>actionListener</code> member
+ * field, and subscribes it to the button's model. If the button's model
+ * is changed, this listener is unsubscribed from the old model and
+ * subscribed to the new one.</p>
+ *
+ * @return A new ActionListener
+ */
+ protected ActionListener createActionListener()
+ {
+ return getEventHandler();
+ }
+
+ /**
+ * <p>A factory method which should return a {@link PropertyChangeListener}
+ * that accepts changes to the specified {@link Action} and reconfigure
+ * the {@link AbstractButton}, by default using the {@link
+ * #configurePropertiesFromAction} method.</p>
+ *
+ * <p>The button calls this method whenever a new Action is assigned to
+ * the button's "action" property, via {@link #setAction}, and stores the
+ * resulting PropertyChangeListener in its
+ * <code>actionPropertyChangeListener</code> member field. The button
+ * then subscribes the listener to the button's new action. If the
+ * button's action is changed subsequently, the listener is unsubscribed
+ * from the old action and subscribed to the new one.</p>
+ *
+ * @param a The Action which will be listened to, and which should be
+ * the same as the source of any PropertyChangeEvents received by the
+ * new listener returned from this method.
+ *
+ * @return A new PropertyChangeListener
+ */
+ protected PropertyChangeListener createActionPropertyChangeListener(Action a)
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ Action act = (Action) (e.getSource());
+ if (e.getPropertyName().equals("enabled"))
+ setEnabled(act.isEnabled());
+ else if (e.getPropertyName().equals(Action.NAME))
+ setText((String) (act.getValue(Action.NAME)));
+ else if (e.getPropertyName().equals(Action.SMALL_ICON))
+ setIcon((Icon) (act.getValue(Action.SMALL_ICON)));
+ else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION))
+ setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION)));
+ else if (e.getPropertyName().equals(Action.MNEMONIC_KEY))
+ if (act.getValue(Action.MNEMONIC_KEY) != null)
+ setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY)))
+ .intValue());
+ else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY))
+ setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ };
+ }
+
+ /**
+ * <p>Factory method which creates a {@link ChangeListener}, used to
+ * subscribe to ChangeEvents from the button's model. Subclasses of
+ * AbstractButton may wish to override the listener used to subscribe to
+ * such ChangeEvents. By default, the listener just propagates the
+ * {@link ChangeEvent} to the button's ChangeListeners, via the {@link
+ * AbstractButton#fireStateChanged} method.</p>
+ *
+ * <p>The button calls this method during construction, stores the
+ * resulting ChangeListener in its <code>changeListener</code> member
+ * field, and subscribes it to the button's model. If the button's model
+ * is changed, this listener is unsubscribed from the old model and
+ * subscribed to the new one.</p>
+ *
+ * @return The new ChangeListener
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return getEventHandler();
+ }
+
+ /**
+ * <p>Factory method which creates a {@link ItemListener}, used to
+ * subscribe to ItemEvents from the button's model. Subclasses of
+ * AbstractButton may wish to override the listener used to subscribe to
+ * such ItemEvents. By default, the listener just propagates the
+ * {@link ItemEvent} to the button's ItemListeners, via the {@link
+ * AbstractButton#fireItemStateChanged} method.</p>
+ *
+ * <p>The button calls this method during construction, stores the
+ * resulting ItemListener in its <code>changeListener</code> member
+ * field, and subscribes it to the button's model. If the button's model
+ * is changed, this listener is unsubscribed from the old model and
+ * subscribed to the new one.</p>
+ *
+ * <p>Note that ItemEvents are only generated from the button's model
+ * when the model's <em>selected</em> property changes. If you want to
+ * subscribe to other properties of the model, you must subscribe to
+ * ChangeEvents.
+ *
+ * @return The new ItemListener
+ */
+ protected ItemListener createItemListener()
+ {
+ return getEventHandler();
+ }
+
+ /**
+ * Programmatically perform a "click" on the button: arming, pressing,
+ * waiting, un-pressing, and disarming the model.
+ */
+ public void doClick()
+ {
+ doClick(100);
+ }
+
+ /**
+ * Programmatically perform a "click" on the button: arming, pressing,
+ * waiting, un-pressing, and disarming the model.
+ *
+ * @param pressTime The number of milliseconds to wait in the pressed state
+ */
+ public void doClick(int pressTime)
+ {
+ ButtonModel mod = getModel();
+ if (mod != null)
+ {
+ mod.setArmed(true);
+ mod.setPressed(true);
+ try
+ {
+ java.lang.Thread.sleep(pressTime);
+ }
+ catch (java.lang.InterruptedException e)
+ {
+ // probably harmless
+ }
+ mod.setPressed(false);
+ mod.setArmed(false);
+ }
+ }
+
+ /**
+ * Return the button's disabled selected icon. The look and feel class
+ * should paint this icon when the "enabled" property of the button's model
+ * is <code>false</code> and its "selected" property is
+ * <code>true</code>. This icon can be <code>null</code>, in which case
+ * it is synthesized from the button's selected icon.
+ *
+ * @return The current disabled selected icon
+ */
+ public Icon getDisabledSelectedIcon()
+ {
+ return disabledSelectedIcon;
+ }
+
+ /**
+ * Set the button's disabled selected icon. The look and feel class
+ * should paint this icon when the "enabled" property of the button's model
+ * is <code>false</code> and its "selected" property is
+ * <code>true</code>. This icon can be <code>null</code>, in which case
+ * it is synthesized from the button's selected icon.
+ *
+ * @param icon The new disabled selected icon
+ */
+ public void setDisabledSelectedIcon(Icon icon)
+ {
+ if (disabledSelectedIcon == icon)
+ return;
+
+ Icon old = disabledSelectedIcon;
+ disabledSelectedIcon = icon;
+ firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's rollover icon. The look and feel class should
+ * paint this icon when the "rolloverEnabled" property of the button is
+ * <code>true</code> and the mouse rolls over the button.
+ *
+ * @return The current rollover icon
+ */
+ public Icon getRolloverIcon()
+ {
+ return rolloverIcon;
+ }
+
+ /**
+ * Set the button's rollover icon and sets the <code>rolloverEnabled</code>
+ * property to <code>true</code>. The look and feel class should
+ * paint this icon when the "rolloverEnabled" property of the button is
+ * <code>true</code> and the mouse rolls over the button.
+ *
+ * @param r The new rollover icon
+ */
+ public void setRolloverIcon(Icon r)
+ {
+ if (rolloverIcon == r)
+ return;
+
+ Icon old = rolloverIcon;
+ rolloverIcon = r;
+ firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon);
+ setRolloverEnabled(true);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's rollover selected icon. The look and feel class
+ * should paint this icon when the "rolloverEnabled" property of the button
+ * is <code>true</code>, the "selected" property of the button's model is
+ * <code>true</code>, and the mouse rolls over the button.
+ *
+ * @return The current rollover selected icon
+ */
+ public Icon getRolloverSelectedIcon()
+ {
+ return rolloverSelectedIcon;
+ }
+
+ /**
+ * Set the button's rollover selected icon and sets the
+ * <code>rolloverEnabled</code> property to <code>true</code>. The look and
+ * feel class should paint this icon when the "rolloverEnabled" property of
+ * the button is <code>true</code>, the "selected" property of the button's
+ * model is <code>true</code>, and the mouse rolls over the button.
+ *
+ * @param r The new rollover selected icon.
+ */
+ public void setRolloverSelectedIcon(Icon r)
+ {
+ if (rolloverSelectedIcon == r)
+ return;
+
+ Icon old = rolloverSelectedIcon;
+ rolloverSelectedIcon = r;
+ firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r);
+ setRolloverEnabled(true);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's selected icon. The look and feel class should
+ * paint this icon when the "selected" property of the button's model is
+ * <code>true</code>, and either the "rolloverEnabled" property of the
+ * button is <code>false</code> or the mouse is not currently rolled
+ * over the button.
+ *
+ * @return The current selected icon
+ */
+ public Icon getSelectedIcon()
+ {
+ return selectedIcon;
+ }
+
+ /**
+ * Set the button's selected icon. The look and feel class should
+ * paint this icon when the "selected" property of the button's model is
+ * <code>true</code>, and either the "rolloverEnabled" property of the
+ * button is <code>false</code> or the mouse is not currently rolled
+ * over the button.
+ *
+ * @param s The new selected icon
+ */
+ public void setSelectedIcon(Icon s)
+ {
+ if (selectedIcon == s)
+ return;
+
+ Icon old = selectedIcon;
+ selectedIcon = s;
+ firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Returns an single-element array containing the "text" property of the
+ * button if the "selected" property of the button's model is
+ * <code>true</code>, otherwise returns <code>null</code>.
+ *
+ * @return The button's "selected object" array
+ */
+ public Object[] getSelectedObjects()
+ {
+ if (isSelected())
+ {
+ Object[] objs = new Object[1];
+ objs[0] = getText();
+ return objs;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Called when image data becomes available for one of the button's icons.
+ *
+ * @param img The image being updated
+ * @param infoflags One of the constant codes in {@link ImageObserver} used
+ * to describe updated portions of an image.
+ * @param x X coordinate of the region being updated
+ * @param y Y coordinate of the region being updated
+ * @param w Width of the region beign updated
+ * @param h Height of the region being updated
+ *
+ * @return <code>true</code> if img is equal to the button's current icon,
+ * otherwise <code>false</code>
+ */
+ public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
+ int h)
+ {
+ return current_icon == img;
+ }
+
+ /**
+ * Returns the value of the button's "contentAreaFilled" property. This
+ * property indicates whether the area surrounding the text and icon of
+ * the button should be filled by the look and feel class. If this
+ * property is <code>false</code>, the look and feel class should leave
+ * the content area transparent.
+ *
+ * @return The current value of the "contentAreaFilled" property
+ */
+ public boolean isContentAreaFilled()
+ {
+ return contentAreaFilled;
+ }
+
+ /**
+ * Sets the value of the button's "contentAreaFilled" property. This
+ * property indicates whether the area surrounding the text and icon of
+ * the button should be filled by the look and feel class. If this
+ * property is <code>false</code>, the look and feel class should leave
+ * the content area transparent.
+ *
+ * @param b The new value of the "contentAreaFilled" property
+ */
+ public void setContentAreaFilled(boolean b)
+ {
+ clientContentAreaFilledSet = true;
+ if (contentAreaFilled == b)
+ return;
+
+ // The JDK sets the opaque property to the value of the contentAreaFilled
+ // property, so should we do.
+ setOpaque(b);
+ boolean old = contentAreaFilled;
+ contentAreaFilled = b;
+ firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b);
+ }
+
+ /**
+ * Paints the button's border, if the button's "borderPainted" property is
+ * <code>true</code>, by out calling to the button's look and feel class.
+ *
+ * @param g The graphics context used to paint the border
+ */
+ protected void paintBorder(Graphics g)
+ {
+ if (isBorderPainted())
+ super.paintBorder(g);
+ }
+
+ /**
+ * Returns a string, used only for debugging, which identifies or somehow
+ * represents this button. The exact value is implementation-defined.
+ *
+ * @return A string representation of the button
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(super.paramString());
+ sb.append(",defaultIcon=");
+ if (getIcon() != null)
+ sb.append(getIcon());
+ sb.append(",disabledIcon=");
+ if (getDisabledIcon() != null)
+ sb.append(getDisabledIcon());
+ sb.append(",disabledSelectedIcon=");
+ if (getDisabledSelectedIcon() != null)
+ sb.append(getDisabledSelectedIcon());
+ sb.append(",margin=");
+ if (getMargin() != null)
+ sb.append(getMargin());
+ sb.append(",paintBorder=").append(isBorderPainted());
+ sb.append(",paintFocus=").append(isFocusPainted());
+ sb.append(",pressedIcon=");
+ if (getPressedIcon() != null)
+ sb.append(getPressedIcon());
+ sb.append(",rolloverEnabled=").append(isRolloverEnabled());
+ sb.append(",rolloverIcon=");
+ if (getRolloverIcon() != null)
+ sb.append(getRolloverIcon());
+ sb.append(",rolloverSelected=");
+ if (getRolloverSelectedIcon() != null)
+ sb.append(getRolloverSelectedIcon());
+ sb.append(",selectedIcon=");
+ if (getSelectedIcon() != null)
+ sb.append(getSelectedIcon());
+ sb.append(",text=");
+ if (getText() != null)
+ sb.append(getText());
+ return sb.toString();
+ }
+
+ /**
+ * Set the "UI" property of the button, which is a look and feel class
+ * responsible for handling the button's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(ButtonUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Set the "UI" property of the button, which is a look and feel class
+ * responsible for handling the button's input events and painting it.
+ *
+ * @return The current "UI" property
+ */
+ public ButtonUI getUI()
+ {
+ return (ButtonUI) ui;
+ }
+
+ /**
+ * Set the "UI" property to a class constructed, via the {@link
+ * UIManager}, from the current look and feel. This should be overridden
+ * for each subclass of AbstractButton, to retrieve a suitable {@link
+ * ButtonUI} look and feel class.
+ */
+ public void updateUI()
+ {
+ // TODO: What to do here?
+ }
+
+ /**
+ * Returns the current time in milliseconds in which clicks gets coalesced
+ * into a single <code>ActionEvent</code>.
+ *
+ * @return the time in milliseconds
+ *
+ * @since 1.4
+ */
+ public long getMultiClickThreshhold()
+ {
+ return multiClickThreshhold;
+ }
+
+ /**
+ * Sets the time in milliseconds in which clicks gets coalesced into a single
+ * <code>ActionEvent</code>.
+ *
+ * @param threshhold the time in milliseconds
+ *
+ * @since 1.4
+ */
+ public void setMultiClickThreshhold(long threshhold)
+ {
+ if (threshhold < 0)
+ throw new IllegalArgumentException();
+
+ multiClickThreshhold = threshhold;
+ }
+
+ /**
+ * Adds the specified component to this AbstractButton. This overrides the
+ * default in order to install an {@link OverlayLayout} layout manager
+ * before adding the component. The layout manager is only installed if
+ * no other layout manager has been installed before.
+ *
+ * @param comp the component to be added
+ * @param constraints constraints for the layout manager
+ * @param index the index at which the component is added
+ *
+ * @since 1.5
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // We use a client property here, so that no extra memory is used in
+ // the common case with no layout manager.
+ if (getClientProperty("AbstractButton.customLayoutSet") == null)
+ setLayout(new OverlayLayout(this));
+ super.addImpl(comp, constraints, index);
+ }
+
+ /**
+ * Sets a layout manager on this AbstractButton. This is overridden in order
+ * to detect if the application sets a custom layout manager. If no custom
+ * layout manager is set, {@link #addImpl(Component, Object, int)} installs
+ * an OverlayLayout before adding a component.
+ *
+ * @param layout the layout manager to install
+ *
+ * @since 1.5
+ */
+ public void setLayout(LayoutManager layout)
+ {
+ // We use a client property here, so that no extra memory is used in
+ // the common case with no layout manager.
+ putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE);
+ super.setLayout(layout);
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if <code>c</code> or
+ * <code>propertyValue</code> is <code>null</code>
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("borderPainted"))
+ {
+ if (! clientBorderPaintedSet)
+ {
+ setBorderPainted(((Boolean) value).booleanValue());
+ clientBorderPaintedSet = false;
+ }
+ }
+ else if (propertyName.equals("rolloverEnabled"))
+ {
+ if (! clientRolloverEnabledSet)
+ {
+ setRolloverEnabled(((Boolean) value).booleanValue());
+ clientRolloverEnabledSet = false;
+ }
+ }
+ else if (propertyName.equals("iconTextGap"))
+ {
+ if (! clientIconTextGapSet)
+ {
+ setIconTextGap(((Integer) value).intValue());
+ clientIconTextGapSet = false;
+ }
+ }
+ else if (propertyName.equals("contentAreaFilled"))
+ {
+ if (! clientContentAreaFilledSet)
+ {
+ setContentAreaFilled(((Boolean) value).booleanValue());
+ clientContentAreaFilledSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+
+ /**
+ * Returns the combined event handler. The instance is created if
+ * necessary.
+ *
+ * @return the combined event handler
+ */
+ EventHandler getEventHandler()
+ {
+ if (eventHandler == null)
+ eventHandler = new EventHandler();
+ return eventHandler;
+ }
+}
diff --git a/libjava/classpath/javax/swing/AbstractCellEditor.java b/libjava/classpath/javax/swing/AbstractCellEditor.java
new file mode 100644
index 000000000..eade00c07
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractCellEditor.java
@@ -0,0 +1,195 @@
+/* AbstractCellEditor.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.EventObject;
+
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.EventListenerList;
+
+/**
+ * An abstract superclass for table and tree cell editors. This provides some
+ * common shared functionality.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class AbstractCellEditor
+ implements CellEditor, Serializable
+{
+ private static final long serialVersionUID = -1048006551406220959L;
+
+ /**
+ * Our Swing event listeners.
+ */
+ protected EventListenerList listenerList;
+
+ /**
+ * The cached ChangeEvent.
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * Creates a new instance of AbstractCellEditor.
+ */
+ public AbstractCellEditor()
+ {
+ listenerList = new EventListenerList();
+ changeEvent = new ChangeEvent(this);
+ }
+
+ /**
+ * Returns <code>true</code> if the cell is editable using
+ * <code>event</code>, <code>false</code>
+ * if it's not. The default behaviour is to return <code>true</code>.
+ *
+ * @param event an event
+ *
+ * @return <code>true</code> if the cell is editable using
+ * <code>event</code>, <code>false</code> if it's not
+ */
+ public boolean isCellEditable(EventObject event)
+ {
+ return true;
+ }
+
+ /**
+ * Returns <code>true</code> if the editing cell should be selected,
+ * <code>false</code> otherwise. This is usually returning <code>true</code>,
+ * but in some special cases it might be useful not to switch cell selection
+ * when editing one cell.
+ *
+ * @param event an event
+ *
+ * @return <code>true</code> if the editing cell should be selected,
+ * <code>false</code> otherwise
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ return true;
+ }
+
+ /**
+ * Stop editing the cell and accept any partial value that has been entered
+ * into the cell.
+ *
+ * @return <code>true</code> if editing has been stopped successfully,
+ * <code>false</code>otherwise
+ */
+ public boolean stopCellEditing()
+ {
+ fireEditingStopped();
+ return true;
+ }
+
+ /**
+ * Stop editing the cell and do not accept any partial value that has
+ * been entered into the cell.
+ */
+ public void cancelCellEditing()
+ {
+ fireEditingCanceled();
+ }
+
+ /**
+ * Adds a CellEditorListener to the list of CellEditorListeners of this
+ * CellEditor.
+ *
+ * @param listener the CellEditorListener to add
+ */
+ public void addCellEditorListener(CellEditorListener listener)
+ {
+ listenerList.add(CellEditorListener.class, listener);
+ }
+
+ /**
+ * Removes the specified CellEditorListener from the list of the
+ * CellEditorListeners of this CellEditor.
+ *
+ * @param listener the CellEditorListener to remove
+ */
+ public void removeCellEditorListener(CellEditorListener listener)
+ {
+ listenerList.remove(CellEditorListener.class, listener);
+ }
+
+ /**
+ * Returns the list of CellEditorListeners that have been registered
+ * in this CellEditor.
+ *
+ * @return the list of CellEditorListeners that have been registered
+ * in this CellEditor
+ *
+ * @since 1.4
+ */
+ public CellEditorListener[] getCellEditorListeners()
+ {
+ return (CellEditorListener[]) listenerList.getListeners(
+ CellEditorListener.class);
+ }
+
+ /**
+ * Notifies all registered listeners that the editing of the cell has has been
+ * stopped.
+ */
+ protected void fireEditingStopped()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ {
+ listeners[index].editingStopped(changeEvent);
+ }
+ }
+
+ /**
+ * Notifies all registered listeners that the editing of the cell has
+ * has been canceled.
+ */
+ protected void fireEditingCanceled()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ {
+ listeners[index].editingCanceled(changeEvent);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/AbstractListModel.java b/libjava/classpath/javax/swing/AbstractListModel.java
new file mode 100644
index 000000000..72aefbcd5
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractListModel.java
@@ -0,0 +1,181 @@
+/* AbstractListModel.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.EventListener;
+
+import javax.swing.event.EventListenerList;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+
+/**
+ * Provides standard implementations of some methods in {@link ListModel}.
+ *
+ * @author Ronald Veldema
+ * @author Andrew Selkirk
+ */
+public abstract class AbstractListModel implements ListModel, Serializable
+{
+ private static final long serialVersionUID = -3285184064379168730L;
+
+ /** List of ListDataListeners called for each change to the list. */
+ protected EventListenerList listenerList;
+
+ /**
+ * Creates a new model instance - initialises the event listener list.
+ */
+ public AbstractListModel()
+ {
+ listenerList = new EventListenerList();
+ }
+
+ /**
+ * Add a listener object to this model. The listener will be called
+ * any time the set of elements in the model is changed.
+ *
+ * @param listener The listener to add
+ */
+ public void addListDataListener(ListDataListener listener)
+ {
+ listenerList.add(ListDataListener.class, listener);
+ }
+
+ /**
+ * Add a listener object to this model. The listener will no longer be
+ * called when the set of elements in the model is changed.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeListDataListener(ListDataListener listener)
+ {
+ listenerList.remove(ListDataListener.class, listener);
+ }
+
+ /**
+ * Call {@link ListDataListener#contentsChanged} on each element of the
+ * {@link #listenerList} which is a {@link ListDataListener}. The event
+ * fired has type {@link ListDataEvent#CONTENTS_CHANGED} and represents a
+ * change to the data elements in the range [startIndex, endIndex]
+ * inclusive.
+ *
+ * @param source The source of the change, typically <code>this</code>
+ * @param startIndex The index of the first element which changed
+ * @param endIndex The index of the last element which changed
+ */
+ protected void fireContentsChanged(Object source, int startIndex,
+ int endIndex)
+ {
+ ListDataEvent event = new ListDataEvent(source, ListDataEvent.CONTENTS_CHANGED,
+ startIndex, endIndex);
+ ListDataListener[] listeners = getListDataListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].contentsChanged(event);
+ }
+
+ /**
+ * Call {@link ListDataListener#intervalAdded} on each element of the
+ * {@link #listenerList} which is a {@link ListDataListener}. The event
+ * fired has type {@link ListDataEvent#INTERVAL_ADDED} and represents an
+ * addition of the data elements in the range [startIndex, endIndex]
+ * inclusive.
+ *
+ * @param source The source of the change, typically <code>this</code>
+ * @param startIndex The index of the first new element
+ * @param endIndex The index of the last new element
+ */
+ protected void fireIntervalAdded(Object source, int startIndex, int endIndex)
+ {
+ ListDataEvent event =
+ new ListDataEvent(source, ListDataEvent.INTERVAL_ADDED,
+ startIndex, endIndex);
+ ListDataListener[] listeners = getListDataListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].intervalAdded(event);
+ }
+
+ /**
+ * Call {@link ListDataListener#intervalRemoved} on each element of the
+ * {@link #listenerList} which is a {@link ListDataListener}. The event
+ * fired has type {@link ListDataEvent#INTERVAL_REMOVED} and represents a
+ * removal of the data elements in the range [startIndex, endIndex]
+ * inclusive.
+ *
+ * @param source The source of the change, typically <code>this</code>
+ * @param startIndex The index of the first element removed
+ * @param endIndex The index of the last element removed
+ */
+ protected void fireIntervalRemoved(Object source, int startIndex,
+ int endIndex)
+ {
+ ListDataEvent event =
+ new ListDataEvent(source, ListDataEvent.INTERVAL_REMOVED,
+ startIndex, endIndex);
+ ListDataListener[] listeners = getListDataListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].intervalRemoved(event);
+ }
+
+ /**
+ * Return the subset of {@link EventListener} objects found in this
+ * object's {@link #listenerList} which are elements of the specified
+ * type.
+ *
+ * @param listenerType The type of listeners to select
+ *
+ * @return The set of listeners of the specified type
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * A synonym for <code>getListeners(ListDataListener.class)</code>.
+ *
+ * @return The set of ListDataListeners found in the {@link #listenerList}
+ */
+ public ListDataListener[] getListDataListeners()
+ {
+ return (ListDataListener[]) getListeners(ListDataListener.class);
+ }
+}
diff --git a/libjava/classpath/javax/swing/AbstractSpinnerModel.java b/libjava/classpath/javax/swing/AbstractSpinnerModel.java
new file mode 100644
index 000000000..984e52af2
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractSpinnerModel.java
@@ -0,0 +1,123 @@
+/* AbstractSpinnerModel.java --
+ Copyright (C) 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.EventListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * Provides standard implementations for some of the methods in
+ * {@link SpinnerModel}.
+ *
+ * @since 1.4
+ *
+ * @author Ka-Hing Cheung
+ */
+public abstract class AbstractSpinnerModel implements SpinnerModel
+{
+ private ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /** Stores the listeners registered with the model. */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Creates an <code>AbstractSpinnerModel</code>.
+ */
+ public AbstractSpinnerModel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Registers a <code>ChangeListener</code> with the model so that it will
+ * receive {@link ChangeEvent} notifications when the model changes.
+ *
+ * @param listener the listener to add (<code>null</code> is ignored).
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Gets all the listeners that are of a particular type.
+ *
+ * @param c the type of listener
+ * @return the listeners that are of the specific type
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> c)
+ {
+ return listenerList.getListeners(c);
+ }
+
+ /**
+ * Gets all the <code>ChangeListener</code>s.
+ *
+ * @return all the <code>ChangeListener</code>s
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Remove a particular listener.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Fires a <code>ChangeEvent</code> to all the <code>ChangeListener</code>s
+ * added to this model
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].stateChanged(changeEvent);
+ }
+}
diff --git a/libjava/classpath/javax/swing/Action.java b/libjava/classpath/javax/swing/Action.java
new file mode 100644
index 000000000..fa1925f45
--- /dev/null
+++ b/libjava/classpath/javax/swing/Action.java
@@ -0,0 +1,153 @@
+/* Action.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.event.ActionListener;
+import java.beans.PropertyChangeListener;
+
+/**
+ * Provides a convenient central point of control for some task
+ * that can be triggered by more than one control in a Swing user interface
+ * (for example, a menu item and a toolbar button).
+ *
+ * @see AbstractButton#setAction(Action)
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Andrew Selkirk
+ */
+public interface Action extends ActionListener {
+
+ /**
+ * A key to access the default property for the action (this is not used).
+ */
+ String DEFAULT = "Default";
+
+ /**
+ * A key to access the long description for the action.
+ */
+ String LONG_DESCRIPTION = "LongDescription";
+
+ /**
+ * A key to access the name for the action.
+ */
+ String NAME = "Name";
+
+ /**
+ * A key to access the short description for the action (the short
+ * description is typically used as the tool tip text).
+ */
+ String SHORT_DESCRIPTION = "ShortDescription";
+
+ /**
+ * A key to access the icon for the action.
+ */
+ String SMALL_ICON = "SmallIcon";
+
+ /**
+ * A key to access the {@link KeyStroke} used as the accelerator for the
+ * action.
+ */
+ String ACCELERATOR_KEY = "AcceleratorKey";
+
+ /**
+ * A key to access the action command string for the action.
+ */
+ String ACTION_COMMAND_KEY = "ActionCommandKey";
+
+ /**
+ * A key to access the mnemonic for the action.
+ */
+ String MNEMONIC_KEY = "MnemonicKey";
+
+ /**
+ * Returns the value associated with the specified key.
+ *
+ * @param key the key (not <code>null</code>).
+ *
+ * @return The value associated with the specified key, or
+ * <code>null</code> if the key is not found.
+ */
+ Object getValue(String key);
+
+ /**
+ * Sets the value associated with the specified key and sends a
+ * {@link java.beans.PropertyChangeEvent} to all registered listeners.
+ * The standard keys are defined in this interface: {@link #NAME},
+ * {@link #SHORT_DESCRIPTION}, {@link #LONG_DESCRIPTION},
+ * {@link #SMALL_ICON}, {@link #ACTION_COMMAND_KEY},
+ * {@link #ACCELERATOR_KEY} and {@link #MNEMONIC_KEY}. Any existing value
+ * associated with the key will be overwritten.
+ *
+ * @param key the key (not <code>null</code>).
+ * @param value the value (<code>null</code> permitted).
+ */
+ void putValue(String key, Object value);
+
+ /**
+ * Returns the flag that indicates whether or not this action is enabled.
+ *
+ * @return The flag.
+ */
+ boolean isEnabled();
+
+ /**
+ * Sets the flag that indicates whether or not this action is enabled. If
+ * the value changes, a {@link java.beans.PropertyChangeEvent} is sent to
+ * all registered listeners.
+ *
+ * @param b the new value of the flag.
+ */
+ void setEnabled(boolean b);
+
+ /**
+ * Registers a listener to receive notification whenever one of the
+ * action's properties is modified.
+ *
+ * @param listener the listener.
+ */
+ void addPropertyChangeListener(PropertyChangeListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives notification of
+ * changes to the action's properties.
+ *
+ * @param listener the listener.
+ */
+ void removePropertyChangeListener(PropertyChangeListener listener);
+
+} // Action
diff --git a/libjava/classpath/javax/swing/ActionMap.java b/libjava/classpath/javax/swing/ActionMap.java
new file mode 100644
index 000000000..0d6706c3b
--- /dev/null
+++ b/libjava/classpath/javax/swing/ActionMap.java
@@ -0,0 +1,195 @@
+/* ActionMap.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Maps arbitrary keys (usually Strings) to {@link Action} instances. This
+ * is used in combination with {@link InputMap}s.
+ *
+ * If a component receives an input event, this is looked up in
+ * the component's <code>InputMap</code>. The result is an object which
+ * serves as a key to the components <code>ActionMap</code>. Finally
+ * the <code>Action</code> that is stored is executed.
+ *
+ * @author Andrew Selkirk
+ * @author Michael Koch
+ */
+public class ActionMap
+ implements Serializable
+{
+ private static final long serialVersionUID = -6277518704513986346L;
+
+ /**
+ * actionMap
+ */
+ private Map actionMap = new HashMap();
+
+ /**
+ * parent
+ */
+ private ActionMap parent;
+
+ /**
+ * Creates a new <code>ActionMap</code> instance.
+ */
+ public ActionMap()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns an action associated with an object.
+ *
+ * @param key the key of the enty
+ *
+ * @return the action associated with key, may be null
+ */
+ public Action get(Object key)
+ {
+ Object result = actionMap.get(key);
+
+ if (result == null && parent != null)
+ result = parent.get(key);
+
+ return (Action) result;
+ }
+
+ /**
+ * Puts a new <code>Action</code> into the <code>ActionMap</code>.
+ * If action is null an existing entry will be removed.
+ *
+ * @param key the key for the entry
+ * @param action the action.
+ */
+ public void put(Object key, Action action)
+ {
+ if (action == null)
+ actionMap.remove(key);
+ else
+ actionMap.put(key, action);
+ }
+
+ /**
+ * Remove an entry from the <code>ActionMap</code>.
+ *
+ * @param key the key of the entry to remove
+ */
+ public void remove(Object key)
+ {
+ actionMap.remove(key);
+ }
+
+ /**
+ * Returns the parent of this <code>ActionMap</code>.
+ *
+ * @return the parent, may be null.
+ */
+ public ActionMap getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Sets a parent for this <code>ActionMap</code>.
+ *
+ * @param parentMap the new parent
+ */
+ public void setParent(ActionMap parentMap)
+ {
+ if (parentMap != this)
+ parent = parentMap;
+ }
+
+ /**
+ * Returns the number of entries in this <code>ActionMap</code>.
+ *
+ * @return the number of entries
+ */
+ public int size()
+ {
+ return actionMap.size();
+ }
+
+ /**
+ * Clears the <code>ActionMap</code>.
+ */
+ public void clear()
+ {
+ actionMap.clear();
+ }
+
+ /**
+ * Returns all keys of entries in this <code>ActionMap</code>.
+ *
+ * @return an array of keys
+ */
+ public Object[] keys()
+ {
+ if (size() != 0)
+ return actionMap.keySet().toArray();
+ return null;
+ }
+
+ /**
+ * Returns all keys of entries in this <code>ActionMap</code>
+ * and all its parents.
+ *
+ * @return an array of keys
+ */
+ public Object[] allKeys()
+ {
+ Set set = new HashSet();
+
+ if (parent != null)
+ set.addAll(Arrays.asList(parent.allKeys()));
+
+ set.addAll(actionMap.keySet());
+ if (set.size() != 0)
+ return set.toArray();
+ return null;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/BorderFactory.java b/libjava/classpath/javax/swing/BorderFactory.java
new file mode 100644
index 000000000..f5c4cbbb6
--- /dev/null
+++ b/libjava/classpath/javax/swing/BorderFactory.java
@@ -0,0 +1,456 @@
+/* BorderFactory.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Font;
+
+import javax.swing.border.BevelBorder;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+import javax.swing.border.LineBorder;
+import javax.swing.border.MatteBorder;
+import javax.swing.border.TitledBorder;
+
+/**
+ * A factory for commonly used borders.
+ *
+ * @author original author unknown
+ */
+public class BorderFactory
+{
+ private BorderFactory()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Creates a line border withe the specified color.
+ *
+ * @param color A color to use for the line.
+ *
+ * @return The Border object
+ */
+ public static Border createLineBorder(Color color)
+ {
+ return createLineBorder(color, 1);
+ }
+
+ /**
+ * Creates a line border withe the specified color and width. The width
+ * applies to all 4 sides of the border. To specify widths individually for
+ * the top, bottom, left, and right, use
+ * createMatteBorder(int,int,int,int,Color).
+ *
+ * @param color A color to use for the line.
+ * @param thickness An int specifying the width in pixels.
+ *
+ * @return The Border object
+ */
+ public static Border createLineBorder(Color color, int thickness)
+ {
+ return new LineBorder(color, thickness);
+ }
+
+ /**
+ * Created a border with a raised beveled edge, using brighter shades of
+ * the component's current background color for highlighting, and darker
+ * shading for shadows. (In a raised border, highlights are on top and
+ * shadows are underneath.)
+ *
+ * @return The Border object
+ */
+ public static Border createRaisedBevelBorder()
+ {
+ return new BevelBorder(BevelBorder.RAISED);
+ }
+
+ /**
+ * Created a border with a lowered beveled edge, using brighter shades of
+ * the component's current background color for highlighting, and darker
+ * shading for shadows. (In a lowered border, shadows are on top and
+ * highlights are underneath.)
+ *
+ * @return The Border object
+ */
+ public static Border createLoweredBevelBorder()
+ {
+ return new BevelBorder(BevelBorder.LOWERED);
+ }
+
+ /**
+ * Create a beveled border of the specified type, using brighter shades of
+ * the component's current background color for highlighting, and darker
+ * shading for shadows. (In a lowered border, shadows are on top and
+ * highlights are underneath.).
+ *
+ * @param type An int specifying either BevelBorder.LOWERED or
+ * BevelBorder.RAISED
+ *
+ * @return The Border object
+ */
+ public static Border createBevelBorder(int type)
+ {
+ return new BevelBorder(type);
+ }
+
+ /**
+ * Create a beveled border of the specified type, using the specified
+ * highlighting and shadowing. The outer edge of the highlighted area uses
+ * a brighter shade of the highlight color. The inner edge of the shadow
+ * area uses a brighter shade of the shadaw color.
+ *
+ * @param type An int specifying either BevelBorder.LOWERED or
+ * BevelBorder.RAISED
+ * @param highlight A Color object for highlights
+ * @param shadow A Color object for shadows
+ *
+ * @return The Border object
+ */
+ public static Border createBevelBorder(int type, Color highlight, Color shadow)
+ {
+ return new BevelBorder(type, highlight, shadow);
+ }
+
+ /**
+ * Create a beveled border of the specified type, using the specified colors
+ * for the inner and outer highlight and shadow areas.
+ *
+ * @param type An int specifying either BevelBorder.LOWERED or
+ * BevelBorder.RAISED
+ * @param highlightOuter A Color object for the outer edge of the
+ * highlight area
+ * @param highlightInner A Color object for the inner edge of the
+ * highlight area
+ * @param shadowOuter A Color object for the outer edge of the shadow area
+ * @param shadowInner A Color object for the inner edge of the shadow area
+ *
+ * @return The Border object
+ */
+ public static Border createBevelBorder(int type, Color highlightOuter,
+ Color highlightInner,
+ Color shadowOuter, Color shadowInner)
+ {
+ return new BevelBorder(type, highlightOuter, highlightInner, shadowOuter,
+ shadowInner);
+ }
+
+ /**
+ * Create a border with an "etched" look using the component's current
+ * background color for highlighting and shading.
+ *
+ * @return The Border object
+ */
+ public static Border createEtchedBorder()
+ {
+ return new EtchedBorder();
+ }
+
+ /**
+ * Create a border with an "etched" look using the component's current
+ * background color for highlighting and shading.
+ *
+ * @return The Border object
+ */
+ public static Border createEtchedBorder(int etchType)
+ {
+ return new EtchedBorder(etchType);
+ }
+
+ /**
+ * Create a border with an "etched" look using the specified highlighting and
+ * shading colors.
+ *
+ * @param highlight A Color object for the border highlights
+ * @param shadow A Color object for the border shadows
+ *
+ * @return The Border object
+ */
+ public static Border createEtchedBorder(Color highlight, Color shadow)
+ {
+ return new EtchedBorder(highlight, shadow);
+ }
+
+ /**
+ * Create a border with an "etched" look using the specified highlighting and
+ * shading colors.
+ *
+ * @param highlight A Color object for the border highlights
+ * @param shadow A Color object for the border shadows
+ *
+ * @return The Border object
+ */
+ public static Border createEtchedBorder(int etchType, Color highlight,
+ Color shadow)
+ {
+ return new EtchedBorder(etchType, highlight, shadow);
+ }
+
+ /**
+ * Create a new title border specifying the text of the title, using the
+ * default border (etched), using the default text position (sitting on the
+ * top line) and default justification (left) and using the default font and
+ * text color determined by the current look and feel.
+ *
+ * @param title A String containing the text of the title
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(String title)
+ {
+ return new TitledBorder(title);
+ }
+
+ /**
+ * Create a new title border with an empty title specifying the border
+ * object, using the default text position (sitting on the top line) and
+ * default justification (left) and using the default font, text color,
+ * and border determined by the current look and feel. (The Motif and Windows
+ * look and feels use an etched border; The Java look and feel use a
+ * gray border.)
+ *
+ * @param border The Border object to add the title to
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border)
+ {
+ return new TitledBorder(border);
+ }
+
+ /**
+ * Add a title to an existing border, specifying the text of the title, using
+ * the default positioning (sitting on the top line) and default
+ * justification (left) and using the default font and text color determined
+ * by the current look and feel.
+ *
+ * @param border The Border object to add the title to
+ * @param title A String containing the text of the title
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border, String title)
+ {
+ return new TitledBorder(border, title);
+ }
+
+ /**
+ * Add a title to an existing border, specifying the text of the title along
+ * with its positioning, using the default font and text color determined by
+ * the current look and feel.
+ *
+ * @param border The Border object to add the title to
+ * @param title A String containing the text of the title
+ * @param titleJustification An int specifying the left/right position of
+ * the title -- one of TitledBorder.LEFT, TitledBorder.CENTER, or
+ * TitledBorder.RIGHT, TitledBorder.DEFAULT_JUSTIFICATION (left).
+ * @param titlePosition An int specifying the vertical position of the text
+ * in relation to the border -- one of: TitledBorder.ABOVE_TOP,
+ * TitledBorder.TOP (sitting on the top line), TitledBorder.BELOW_TOP,
+ * TitledBorder.ABOVE_BOTTOM, TitledBorder.BOTTOM (sitting on the bottom
+ * line), TitledBorder.BELOW_BOTTOM, or TitledBorder.DEFAULT_POSITION
+ * (top).
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border, String title,
+ int titleJustification,
+ int titlePosition)
+ {
+ return new TitledBorder(border, title, titleJustification, titlePosition);
+ }
+
+ /**
+ * Add a title to an existing border, specifying the text of the title along
+ * with its positioning and font, using the default text color determined by
+ * the current look and feel.
+ *
+ * @param border - the Border object to add the title to
+ * @param title - a String containing the text of the title
+ * @param titleJustification - an int specifying the left/right position of
+ * the title -- one of TitledBorder.LEFT, TitledBorder.CENTER, or
+ * TitledBorder.RIGHT, TitledBorder.DEFAULT_JUSTIFICATION (left).
+ * @param titlePosition - an int specifying the vertical position of the
+ * text in relation to the border -- one of: TitledBorder.ABOVE_TOP,
+ * TitledBorder.TOP (sitting on the top line), TitledBorder.BELOW_TOP,
+ * TitledBorder.ABOVE_BOTTOM, TitledBorder.BOTTOM (sitting on the bottom
+ * line), TitledBorder.BELOW_BOTTOM, or TitledBorder.DEFAULT_POSITION (top).
+ * @param titleFont - a Font object specifying the title font
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border, String title,
+ int titleJustification,
+ int titlePosition,
+ Font titleFont)
+ {
+ return new TitledBorder(border, title, titleJustification, titlePosition,
+ titleFont);
+ }
+
+ /**
+ * Add a title to an existing border, specifying the text of the title along
+ * with its positioning, font, and color.
+ *
+ * @param border - the Border object to add the title to
+ * @param title - a String containing the text of the title
+ * @param titleJustification - an int specifying the left/right position of
+ * the title -- one of TitledBorder.LEFT, TitledBorder.CENTER, or
+ * TitledBorder.RIGHT, TitledBorder.DEFAULT_JUSTIFICATION (left).
+ * @param titlePosition - an int specifying the vertical position of the text
+ * in relation to the border -- one of: TitledBorder.ABOVE_TOP,
+ * TitledBorder.TOP (sitting on the top line), TitledBorder.BELOW_TOP,
+ * TitledBorder.ABOVE_BOTTOM, TitledBorder.BOTTOM (sitting on the bottom
+ * line), TitledBorder.BELOW_BOTTOM, or TitledBorder.DEFAULT_POSITION (top).
+ * @param titleFont - a Font object specifying the title font
+ * @param titleColor - a Color object specifying the title color
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border, String title,
+ int titleJustification,
+ int titlePosition,
+ Font titleFont, Color titleColor)
+ {
+ return new TitledBorder(border, title, titleJustification, titlePosition,
+ titleFont, titleColor);
+ }
+
+ /**
+ * Creates an empty border that takes up no space. (The width of the top,
+ * bottom, left, and right sides are all zero.)
+ *
+ * @return The Border object
+ */
+ public static Border createEmptyBorder()
+ {
+ return new EmptyBorder(0, 0, 0, 0);
+ }
+
+ /**
+ * Creates an empty border that takes up no space but which does no drawing,
+ * specifying the width of the top, left, bottom, and right sides.
+ *
+ * @param top An int specifying the width of the top in pixels
+ * @param left An int specifying the width of the left side in pixels
+ * @param bottom An int specifying the width of the right side in pixels
+ * @param right An int specifying the width of the bottom in pixels
+ *
+ * @return The Border object
+ */
+ public static Border createEmptyBorder(int top, int left, int bottom,
+ int right)
+ {
+ return new EmptyBorder(top, left, bottom, right);
+ }
+
+ /**
+ * Create a compound border with a null inside edge and a null outside edge.
+ *
+ * @return The CompoundBorder object
+ */
+ public static CompoundBorder createCompoundBorder()
+ {
+ return new CompoundBorder();
+ }
+
+ /**
+ * Create a compound border specifying the border objects to use for the
+ * outside and inside edges.
+ *
+ * @param outsideBorder A Border object for the outer edge of the
+ * compound border
+ * @param insideBorder A Border object for the inner edge of the
+ * compound border
+ *
+ * @return The CompoundBorder object
+ */
+ public static CompoundBorder createCompoundBorder(Border outsideBorder,
+ Border insideBorder)
+ {
+ return new CompoundBorder(outsideBorder, insideBorder);
+ }
+
+ /**
+ * Create a matte-look border using a solid color. (The difference between
+ * this border and a line border is that you can specify the individual border
+ * dimensions.)
+ *
+ * @param top
+ * An int specifying the width of the top in pixels
+ * @param left
+ * An int specifying the width of the left side in pixels
+ * @param bottom
+ * An int specifying the width of the right side in pixels
+ * @param right
+ * An int specifying the width of the bottom in pixels
+ * @param color
+ * A Color to use for the border
+ * @return The MatteBorder object
+ */
+ public static MatteBorder createMatteBorder(int top, int left, int bottom,
+ int right, Color color)
+ {
+ return new MatteBorder(top, left, bottom, right, color);
+ }
+
+ /**
+ * Create a matte-look border that consists of multiple tiles of a specified
+ * icon. Multiple copies of the icon are placed side-by-side to fill up the
+ * border area.
+ *
+ * Note:
+ * If the icon doesn't load, the border area is painted gray.
+ *
+ * @param top An int specifying the width of the top in pixels
+ * @param left An int specifying the width of the left side in pixels
+ * @param bottom An int specifying the width of the right side in pixels
+ * @param right An int specifying the width of the bottom in pixels
+ * @param tileIcon The Icon object used for the border tiles
+ *
+ * @return The MatteBorder object
+ */
+ public static MatteBorder createMatteBorder(int top, int left, int bottom,
+ int right, Icon tileIcon)
+ {
+ return new MatteBorder(top, left, bottom, right, tileIcon);
+ }
+}
diff --git a/libjava/classpath/javax/swing/BoundedRangeModel.java b/libjava/classpath/javax/swing/BoundedRangeModel.java
new file mode 100644
index 000000000..87fec5f0f
--- /dev/null
+++ b/libjava/classpath/javax/swing/BoundedRangeModel.java
@@ -0,0 +1,193 @@
+/* BoundedRangeModel.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * The data model that represents a <i>range</i> that is constrained to fit
+ * within specified <i>bounds</i>. The range is defined as <code>value</code>
+ * to <code>value + extent</code>, where both <code>value</code> and
+ * <code>extent</code> are integers, and <code>extent >= 0</code>. The bounds
+ * are defined by integers <code>minimum</code> and <code>maximum</code>.
+ * <p>
+ * This type of model is used in components that display a range of values,
+ * like {@link JProgressBar} and {@link JSlider}.
+ *
+ * @author Andrew Selkirk
+ */
+public interface BoundedRangeModel
+{
+ /**
+ * Returns the current value for the model.
+ *
+ * @return The current value for the model.
+ *
+ * @see #setValue(int)
+ */
+ int getValue();
+
+ /**
+ * Sets the value for the model and sends a {@link ChangeEvent} to
+ * all registered listeners. The new value must satisfy the constraint
+ * <code>min <= value <= value + extent <= max</code>.
+ *
+ * @param value the value
+ *
+ * @see #getValue()
+ */
+ void setValue(int value);
+
+ /**
+ * Returns the lower bound for the model. The start of the model's range
+ * (see {@link #getValue()}) cannot be less than this lower bound.
+ *
+ * @return The lower bound for the model.
+ *
+ * @see #setMinimum(int)
+ * @see #getMaximum()
+ */
+ int getMinimum();
+
+ /**
+ * Sets the lower bound for the model and sends a {@link ChangeEvent} to all
+ * registered listeners. The new minimum must be less than or equal to the
+ * start value of the model's range (as returned by {@link #getValue()}).
+ *
+ * @param minimum the minimum value
+ *
+ * @see #getMinimum()
+ */
+ void setMinimum(int minimum);
+
+ /**
+ * Returns the upper bound for the model. This sets an upper limit for the
+ * end value of the model's range ({@link #getValue()} +
+ * {@link #getExtent()}).
+ *
+ * @return The upper bound for the model.
+ *
+ * @see #setMaximum(int)
+ * @see #getMinimum()
+ */
+ int getMaximum();
+
+ /**
+ * Sets the upper bound for the model and sends a {@link ChangeEvent} to all
+ * registered listeners. The new maximum must be greater than or equal to the
+ * end value of the model's range (as returned by {@link #getValue()} +
+ * {@link #getExtent()}).
+ *
+ * @param maximum the maximum value
+ *
+ * @see #getMaximum()
+ */
+ void setMaximum(int maximum);
+
+ /**
+ * Returns the value of the <code>valueIsAdjusting</code> property.
+ *
+ * @return <code>true</code> if value is adjusting,
+ * otherwise <code>false</code>
+ *
+ * @see #setValueIsAdjusting(boolean)
+ */
+ boolean getValueIsAdjusting();
+
+ /**
+ * Sets the <code>valueIsAdjusting</code> property.
+ *
+ * @param adjusting <code>true</code> if adjusting,
+ * <code>false</code> otherwise
+ *
+ * @see #getValueIsAdjusting()
+ */
+ void setValueIsAdjusting(boolean adjusting);
+
+ /**
+ * Returns the current extent.
+ *
+ * @return the extent
+ *
+ * @see #setExtent(int)
+ */
+ int getExtent();
+
+ /**
+ * Sets the extent, which is the length of the model's range, and sends a
+ * {@link ChangeEvent} to all registered listeners.
+ *
+ * @param extent the extent
+ *
+ * @see #getExtent()
+ */
+ void setExtent(int extent);
+
+ /**
+ * Sets all the properties for the model in a single call.
+ *
+ * @param value the value
+ * @param extent the extent
+ * @param minimum the minimum value
+ * @param maximum the maximum value
+ * @param adjusting a flag that indicates the model is being adjusted
+ * continuously.
+ */
+ void setRangeProperties(int value, int extent, int minimum, int maximum,
+ boolean adjusting);
+
+ /**
+ * Adds a <code>ChangeListener</code> to this object.
+ *
+ * @param listener the listener to add
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ void addChangeListener(ChangeListener listener);
+
+ /**
+ * Removes a <code>ChangeListener</code> from this object.
+ *
+ * @param listener the listener to remove
+ *
+ * @see #addChangeListener(ChangeListener)
+ */
+ void removeChangeListener(ChangeListener listener);
+}
diff --git a/libjava/classpath/javax/swing/Box.java b/libjava/classpath/javax/swing/Box.java
new file mode 100644
index 000000000..ce9cb8f20
--- /dev/null
+++ b/libjava/classpath/javax/swing/Box.java
@@ -0,0 +1,290 @@
+/* Box.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTError;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.LayoutManager;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * A component that uses a {@link BoxLayout} as Layout Manager.
+ *
+ * In addition to that, this class provides a set of static methods for
+ * creating some filler components ('struts' and 'glue') for use in
+ * containers that are laid out using BoxLayout.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class Box extends JComponent implements Accessible
+{
+ private static final long serialVersionUID = 1525417495883046342L;
+
+ /**
+ * Provides accessibility support for <code>Box</code>es.
+ */
+ protected class AccessibleBox extends Container.AccessibleAWTContainer
+ {
+ private static final long serialVersionUID = -7775079816389931944L;
+
+ protected AccessibleBox()
+ {
+ // Nothing to do here.
+ }
+
+ public AccessibleRole getAccessibleRole()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * A component that servers as a filler in BoxLayout controlled containers.
+ */
+ public static class Filler extends JComponent implements Accessible
+ {
+ private static final long serialVersionUID = -1204263191910183998L;
+
+ /**
+ * Provides accessibility support for <code>Box.Filler</code>.
+ */
+ protected class AccessibleBoxFiller
+ extends Component.AccessibleAWTComponent
+ {
+ private static final long serialVersionUID = 164963348357479321L;
+
+ protected AccessibleBoxFiller()
+ {
+ // Nothing to do here.
+ }
+
+ public AccessibleRole getAccessibleRole()
+ {
+ return null;
+ }
+ }
+
+ private transient Dimension min, pref, max;
+
+ /**
+ * Creates a new instance of Filler.
+ *
+ * @param min the minimum size of the filler.
+ * @param pref the preferred size of the filler.
+ * @param max the maximum size of the filler.
+ */
+ public Filler(Dimension min, Dimension pref, Dimension max)
+ {
+ changeShape(min, pref, max);
+ }
+
+ /**
+ * Changes the dimensions of this Filler.
+ *
+ * @param min the new minimum size of the filler.
+ * @param pref the new preferred size of the filler.
+ * @param max the new maximum size of the filler.
+ */
+ public void changeShape(Dimension min, Dimension pref, Dimension max)
+ {
+ this.min = min;
+ this.pref = pref;
+ this.max = max;
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleBoxFiller();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns the maximum size of this Filler.
+ *
+ * @return the maximum size of this Filler.
+ */
+ public Dimension getMaximumSize()
+ {
+ return max;
+ }
+
+ /**
+ * Returns the minimum size of this Filler.
+ *
+ * @return the minimum size of this Filler.
+ */
+ public Dimension getMinimumSize()
+ {
+ return min;
+ }
+
+ /**
+ * Returns the preferred size of this Filler.
+ *
+ * @return the preferred size of this Filler.
+ */
+ public Dimension getPreferredSize()
+ {
+ return pref;
+ }
+ }
+
+ /**
+ * Creates a new Box component, that lays out its children according
+ * to the <code>axis</code> parameter.
+ *
+ * @param axis the orientation of the BoxLayout.
+ *
+ * @see BoxLayout#X_AXIS
+ * @see BoxLayout#Y_AXIS
+ * @see BoxLayout#LINE_AXIS
+ * @see BoxLayout#PAGE_AXIS
+ */
+ public Box(int axis)
+ {
+ super.setLayout(new BoxLayout(this, axis));
+ }
+
+ /**
+ * Creates a filler component which acts as glue between components.
+ * It does not take space unless some extra space is available. If extra
+ * space is available, this component can expand in both X and Y directions.
+ *
+ * @return a glue-like filler component.
+ */
+ public static Component createGlue()
+ {
+ Filler glue = new Filler(new Dimension(0, 0), new Dimension(0, 0),
+ new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
+ return glue;
+ }
+
+ public static Box createHorizontalBox()
+ {
+ return new Box(BoxLayout.X_AXIS);
+ }
+
+ /**
+ * Creates a filler component which acts as glue between components.
+ * It does not take space unless some extra space is available. If extra
+ * space is available, this component can expand in the X direction.
+ *
+ * @return a glue-like filler component.
+ */
+ public static Component createHorizontalGlue()
+ {
+ Filler glue = new Filler(new Dimension(0, 0), new Dimension(0, 0),
+ new Dimension(Short.MAX_VALUE, 0));
+ return glue;
+ }
+
+ /**
+ * Creates a filler component which acts as strut between components.
+ * It will fill exactly the specified horizontal size.
+ *
+ * @param width the width of this strut in pixels.
+ *
+ * @return a strut-like filler component.
+ */
+ public static Component createHorizontalStrut(int width)
+ {
+ Filler strut = new Filler(new Dimension(width, 0),
+ new Dimension(width, 0),
+ new Dimension(width, Integer.MAX_VALUE));
+ return strut;
+ }
+
+ public static Component createRigidArea(Dimension d)
+ {
+ return new Filler(d, d, d);
+ }
+
+ public static Box createVerticalBox()
+ {
+ return new Box(BoxLayout.Y_AXIS);
+ }
+
+ /**
+ * Creates a filler component which acts as glue between components.
+ * It does not take space unless some extra space is available. If extra
+ * space is available, this component can expand in the Y direction.
+ *
+ * @return a glue-like filler component.
+ */
+ public static Component createVerticalGlue()
+ {
+ return createGlue();
+ }
+
+ /**
+ * Creates a filler component which acts as strut between components.
+ * It will fill exactly the specified vertical size.
+ *
+ * @param height the height of this strut in pixels.
+ *
+ * @return a strut-like filler component.
+ */
+ public static Component createVerticalStrut(int height)
+ {
+ Filler strut = new Filler(new Dimension(0, height),
+ new Dimension(0, height),
+ new Dimension(Integer.MAX_VALUE, height));
+ return strut;
+ }
+
+ public void setLayout(LayoutManager l)
+ {
+ throw new AWTError("Not allowed to set layout managers for boxes.");
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleBox();
+ return accessibleContext;
+ }
+
+
+}
diff --git a/libjava/classpath/javax/swing/BoxLayout.java b/libjava/classpath/javax/swing/BoxLayout.java
new file mode 100644
index 000000000..55c489eb9
--- /dev/null
+++ b/libjava/classpath/javax/swing/BoxLayout.java
@@ -0,0 +1,451 @@
+/* BoxLayout.java -- A layout for swing components.
+ Copyright (C) 2002, 2003, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTError;
+import java.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager2;
+import java.io.Serializable;
+
+/**
+ * A layout that stacks the children of a container in a Box, either
+ * horizontally or vertically.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class BoxLayout implements LayoutManager2, Serializable
+{
+
+ /**
+ * Specifies that components are laid out left to right.
+ */
+ public static final int X_AXIS = 0;
+
+ /**
+ * Specifies that components are laid out top to bottom.
+ */
+ public static final int Y_AXIS = 1;
+
+ /**
+ * Specifies that components are laid out in the direction of a line of text.
+ */
+ public static final int LINE_AXIS = 2;
+
+ /**
+ * Sepcifies that components are laid out in the direction of the line flow.
+ */
+ public static final int PAGE_AXIS = 3;
+
+ /*
+ * Needed for serialization.
+ */
+ private static final long serialVersionUID = -2474455742719112368L;
+
+ /*
+ * The container given to the constructor.
+ */
+ private Container container;
+
+ /**
+ * Current type of component layouting. Defaults to X_AXIS.
+ */
+ private int way = X_AXIS;
+
+ /**
+ * The size requirements of the containers children for the X direction.
+ */
+ private SizeRequirements[] xChildren;
+
+ /**
+ * The size requirements of the containers children for the Y direction.
+ */
+ private SizeRequirements[] yChildren;
+
+ /**
+ * The size requirements of the container to be laid out for the X direction.
+ */
+ private SizeRequirements xTotal;
+
+ /**
+ * The size requirements of the container to be laid out for the Y direction.
+ */
+ private SizeRequirements yTotal;
+
+ /**
+ * The offsets of the child components in the X direction.
+ */
+ private int[] offsetsX;
+
+ /**
+ * The offsets of the child components in the Y direction.
+ */
+ private int[] offsetsY;
+
+ /**
+ * The spans of the child components in the X direction.
+ */
+ private int[] spansX;
+
+ /**
+ * The spans of the child components in the Y direction.
+ */
+ private int[] spansY;
+
+ /**
+ * Constructs a <code>BoxLayout</code> object.
+ *
+ * @param container The container that needs to be laid out.
+ * @param way The orientation of the components.
+ *
+ * @exception AWTError If way has an invalid value.
+ */
+ public BoxLayout(Container container, int way)
+ {
+ if (way != X_AXIS && way != Y_AXIS && way != LINE_AXIS && way != PAGE_AXIS)
+ throw new AWTError("Invalid axis");
+
+ int width = 0;
+ int height = 0;
+ this.container = container;
+ this.way = way;
+ }
+
+ /**
+ * Adds a component to the layout. Not used in BoxLayout.
+ *
+ * @param name The name of the component to add.
+ * @param component the component to add to the layout.
+ */
+ public void addLayoutComponent(String name, Component component)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Removes a component from the layout. Not used in BoxLayout.
+ *
+ * @param component The component to remove from the layout.
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ // Nothing to do here.
+ }
+
+ private boolean isHorizontalIn(Container parent)
+ {
+ ComponentOrientation orientation = parent.getComponentOrientation();
+ return this.way == X_AXIS
+ || (this.way == LINE_AXIS
+ && orientation.isHorizontal())
+ || (this.way == PAGE_AXIS
+ && (!orientation.isHorizontal()));
+ }
+
+
+
+ /**
+ * Returns the preferred size of the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ Insets i = container.getInsets();
+ return new Dimension(xTotal.preferred + i.left + i.right,
+ yTotal.preferred + i.top + i.bottom);
+ }
+ }
+
+ /**
+ * Returns the minimum size of the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ Insets i = container.getInsets();
+ return new Dimension(xTotal.minimum + i.left + i.right,
+ yTotal.minimum + i.top + i.bottom);
+ }
+ }
+
+ /**
+ * Lays out the specified container using this layout.
+ *
+ * @param parent The container that needs to be laid out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkLayout();
+ Component[] children = container.getComponents();
+ Insets in = container.getInsets();
+ for (int i = 0; i < children.length; i++)
+ children[i].setBounds(offsetsX[i] + in.left, offsetsY[i] + in.top,
+ spansX[i], spansY[i]);
+ }
+ }
+
+ /**
+ * Adds a component to the layout. Not used in BoxLayout
+ *
+ * @param child The component to add to the layout.
+ * @param constraints The constraints for the component in the layout.
+ */
+ public void addLayoutComponent(Component child, Object constraints)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the alignment along the X axis for the container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The alignment.
+ */
+ public float getLayoutAlignmentX(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ return xTotal.alignment;
+ }
+ }
+
+ /**
+ * Returns the alignment along the Y axis for the container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The alignment.
+ */
+ public float getLayoutAlignmentY(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ return yTotal.alignment;
+ }
+ }
+
+ /**
+ * Invalidates the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ */
+ public void invalidateLayout(Container parent)
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ synchronized (container.getTreeLock())
+ {
+ xChildren = null;
+ yChildren = null;
+ xTotal = null;
+ yTotal = null;
+ offsetsX = null;
+ offsetsY = null;
+ spansX = null;
+ spansY = null;
+ }
+ }
+
+ /**
+ * Returns the maximum size of the layout gived the components
+ * in the given container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension maximumLayoutSize(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ Insets i = container.getInsets();
+ int xDim = xTotal.maximum + i.left + i.right;
+ int yDim = yTotal.maximum + i.top + i.bottom;
+
+ // Check for overflow
+ if (xDim < xTotal.maximum)
+ xDim = Integer.MAX_VALUE;
+ if (yDim < yTotal.maximum)
+ yDim = Integer.MAX_VALUE;
+ return new Dimension(xDim, yDim);
+ }
+ }
+
+ /**
+ * Makes sure that the xTotal and yTotal fields are set up correctly. A call
+ * to {@link #invalidateLayout} sets these fields to null and they have to be
+ * recomputed.
+ */
+ private void checkTotalRequirements()
+ {
+ if (xTotal == null || yTotal == null)
+ {
+ checkRequirements();
+ if (isHorizontalIn(container))
+ {
+ xTotal = SizeRequirements.getTiledSizeRequirements(xChildren);
+ yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren);
+ }
+ else
+ {
+ xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren);
+ yTotal = SizeRequirements.getTiledSizeRequirements(yChildren);
+ }
+ }
+ }
+
+ /**
+ * Makes sure that the xChildren and yChildren fields are correctly set up.
+ * A call to {@link #invalidateLayout(Container)} sets these fields to null,
+ * so they have to be set up again.
+ */
+ private void checkRequirements()
+ {
+ if (xChildren == null || yChildren == null)
+ {
+ Component[] children = container.getComponents();
+ xChildren = new SizeRequirements[children.length];
+ yChildren = new SizeRequirements[children.length];
+ for (int i = 0; i < children.length; i++)
+ {
+ if (! children[i].isVisible())
+ {
+ xChildren[i] = new SizeRequirements();
+ yChildren[i] = new SizeRequirements();
+ }
+ else
+ {
+ xChildren[i] =
+ new SizeRequirements(children[i].getMinimumSize().width,
+ children[i].getPreferredSize().width,
+ children[i].getMaximumSize().width,
+ children[i].getAlignmentX());
+ yChildren[i] =
+ new SizeRequirements(children[i].getMinimumSize().height,
+ children[i].getPreferredSize().height,
+ children[i].getMaximumSize().height,
+ children[i].getAlignmentY());
+ }
+ }
+ }
+ }
+
+ /**
+ * Makes sure that the offsetsX, offsetsY, spansX and spansY fields are set
+ * up correctly. A call to {@link #invalidateLayout} sets these fields
+ * to null and they have to be recomputed.
+ */
+ private void checkLayout()
+ {
+ if (offsetsX == null || offsetsY == null || spansX == null
+ || spansY == null)
+ {
+ checkRequirements();
+ checkTotalRequirements();
+ int len = container.getComponents().length;
+ offsetsX = new int[len];
+ offsetsY = new int[len];
+ spansX = new int[len];
+ spansY = new int[len];
+
+ Insets in = container.getInsets();
+ int width = container.getWidth() - in.left - in.right;
+ int height = container.getHeight() - in.top - in.bottom;
+
+ if (isHorizontalIn(container))
+ {
+ SizeRequirements.calculateTiledPositions(width,
+ xTotal, xChildren,
+ offsetsX, spansX);
+ SizeRequirements.calculateAlignedPositions(height,
+ yTotal, yChildren,
+ offsetsY, spansY);
+ }
+ else
+ {
+ SizeRequirements.calculateAlignedPositions(width,
+ xTotal, xChildren,
+ offsetsX, spansX);
+ SizeRequirements.calculateTiledPositions(height,
+ yTotal, yChildren,
+ offsetsY, spansY);
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/ButtonGroup.java b/libjava/classpath/javax/swing/ButtonGroup.java
new file mode 100644
index 000000000..d4168bb99
--- /dev/null
+++ b/libjava/classpath/javax/swing/ButtonGroup.java
@@ -0,0 +1,223 @@
+/* ButtonGroup.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.Enumeration;
+import java.util.Vector;
+
+
+/**
+ * Logically groups a set of buttons, so that only one of the buttons in
+ * a <code>ButtonGroup</code> can be selected at the same time. If one
+ * button in a <code>ButtonGroup</code> is selected, all other buttons
+ * are automatically deselected.
+ *
+ * While <code>ButtonGroup</code> can be used for all buttons that are derived
+ * from {@link AbstractButton}, it is normally only used for
+ * {@link JRadioButton}s, {@link JRadioButtonMenuItem}s and
+ * {@link JToggleButton}s.
+ *
+ * You could use it for {@link JCheckBox}es, but for the sake of usability
+ * this is strongly discouraged because the common expectation of checkboxes
+ * is that the user is allowed to make multiple selections.
+ *
+ * It makes no sense to put {@link JButton}s or {@link JMenuItem}s in
+ * a <code>ButtonGroup</code> because they don't implement the
+ * <code>selected</code> semantics.
+ *
+ * @author original author unknown
+ */
+public class ButtonGroup implements Serializable
+{
+ private static final long serialVersionUID = 4259076101881721375L;
+
+ /** Stores references to the buttons added to this button group. */
+ protected Vector<AbstractButton> buttons = new Vector<AbstractButton>();
+
+ /** The currently selected button model. */
+ ButtonModel sel;
+
+ /**
+ * Creates a new button group.
+ */
+ public ButtonGroup()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds a button to this group. If the button is in the selected state, then:
+ * <ul>
+ * <li>if the group has no current selection, the new button becomes the
+ * selected button for the group;</li>
+ * <li>if the group already has a selected button, the new button is set to
+ * "not selected".</li>
+ * </ul>
+ *
+ * @param b the button to add (<code>null</code> is ignored).
+ */
+ public void add(AbstractButton b)
+ {
+ if (b == null)
+ return;
+ b.getModel().setGroup(this);
+ if (b.isSelected())
+ {
+ if (sel == null)
+ sel = b.getModel();
+ else
+ b.setSelected(false);
+ }
+ buttons.addElement(b);
+ }
+
+ /**
+ * Removes the specified button from this group. If the button is the
+ * selected button, the current selection is set to <code>null</code>.
+ * The group for the removed button's model is set to <code>null</code>.
+ *
+ * @param b the button to remove (<code>null</code> is ignored).
+ */
+ public void remove(AbstractButton b)
+ {
+ if (b == null)
+ return;
+ b.getModel().setGroup(null);
+ if (b.getModel() == sel)
+ sel = null;
+ buttons.removeElement(b);
+ }
+
+ /**
+ * Returns the currently added buttons.
+ *
+ * @return <code>Enumeration</code> over all added buttons
+ */
+ public Enumeration<AbstractButton> getElements()
+ {
+ return buttons.elements();
+ }
+
+ /**
+ * Returns the currently selected button model.
+ *
+ * @return the currently selected button model, null if none was selected
+ * yet
+ */
+ public ButtonModel getSelection()
+ {
+ return sel;
+ }
+
+ /**
+ * Returns the button that has the specified model, or <code>null</code> if
+ * there is no such button in the group.
+ *
+ * @param m the button model.
+ *
+ * @return The button that has the specified model, or <code>null</code>.
+ */
+ AbstractButton findButton(ButtonModel m)
+ {
+ for (int i = 0; i < buttons.size(); i++)
+ {
+ AbstractButton a = (AbstractButton) buttons.get(i);
+ if (a.getModel() == m)
+ return a;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the currently selected button model. Only one button of a group can
+ * be selected at a time.
+ *
+ * @param m the model to select
+ * @param b true if this button is to be selected, false otherwise
+ */
+ public void setSelected(ButtonModel m, boolean b)
+ {
+ if ((sel != m || b) && (! b || sel == m))
+ return;
+
+ if (b && sel != m)
+ {
+ ButtonModel old = sel;
+ sel = m;
+
+ if (old != null)
+ old.setSelected(false);
+
+ if (m != null)
+ sel.setSelected(true);
+
+ AbstractButton button = findButton(old);
+ if (button != null)
+ button.repaint();
+ }
+ else if (!b && sel == m)
+ m.setSelected(true);
+ }
+
+ /**
+ * Checks if the given <code>ButtonModel</code> is selected in this button
+ * group.
+ *
+ * @param m the button model (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if <code>m</code> is the selected button model
+ * in this group, and <code>false</code> otherwise.
+ */
+ public boolean isSelected(ButtonModel m)
+ {
+ return m == sel;
+ }
+
+ /**
+ * Return the number of buttons in this button group.
+ *
+ * @return the number of buttons
+ *
+ * @since 1.3
+ */
+ public int getButtonCount()
+ {
+ return buttons.size();
+ }
+}
diff --git a/libjava/classpath/javax/swing/ButtonModel.java b/libjava/classpath/javax/swing/ButtonModel.java
new file mode 100644
index 000000000..d48eb1e65
--- /dev/null
+++ b/libjava/classpath/javax/swing/ButtonModel.java
@@ -0,0 +1,301 @@
+/* ButtonModel.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.ItemSelectable;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemListener;
+
+import javax.swing.event.ChangeListener;
+
+/**
+ * The data model that is used in all kinds of buttons.
+ */
+public interface ButtonModel extends ItemSelectable
+{
+
+ /**
+ * Returns <code>true</code> if the button is armed, <code>false</code>
+ * otherwise.
+ *
+ * A button is armed, when the user has pressed the mouse over it, but has
+ * not yet released the mouse.
+ *
+ * @return <code>true</code> if the button is armed, <code>false</code>
+ * otherwise
+ *
+ * @see #setArmed(boolean)
+ */
+ boolean isArmed();
+
+ /**
+ * Sets the armed flag of the button.
+ *
+ * A button is armed, when the user has pressed the mouse over it, but has
+ * not yet released the mouse.
+ *
+ * @param b <code>true</code> if the button is armed, <code>false</code>
+ * otherwise
+ *
+ * @see #isArmed()
+ */
+ void setArmed(boolean b);
+
+ /**
+ * Returns <code>true</code> if the button is enabled, <code>false</code>
+ * otherwise.
+ *
+ * When a button is disabled, it is usually grayed out and the user cannot
+ * change its state.
+ *
+ * @return <code>true</code> if the button is enabled, <code>false</code>
+ * otherwise
+ *
+ * @see #setEnabled(boolean)
+ */
+ boolean isEnabled();
+
+ /**
+ * Sets the enabled flag of the button.
+ *
+ * When a button is disabled, it is usually grayed out and the user cannot
+ * change its state.
+ *
+ * @param b <code>true</code> if the button is enabled, <code>false</code>
+ * otherwise
+ *
+ * @see #isEnabled()
+ */
+ void setEnabled(boolean b);
+
+ /**
+ * Sets the pressed flag of the button.
+ *
+ * The button usually gets pressed when the user clicks on a button, it will
+ * be un-pressed when the user releases the mouse.
+ *
+ * @param b <code>true</code> if the button is pressed, <code>false</code>
+ * otherwise
+ *
+ * @see #isPressed()
+ */
+ void setPressed(boolean b);
+
+ /**
+ * Returns <code>true</code> if the button is pressed, <code>false</code>
+ * otherwise.
+ *
+ * The button usually gets pressed when the user clicks on a button, it will
+ * be un-pressed when the user releases the mouse.
+ *
+ * @return <code>true</code> if the button is pressed, <code>false</code>
+ * otherwise
+ *
+ * @see #setPressed(boolean)
+ */
+ boolean isPressed();
+
+ /**
+ * Removes an {@link ActionListener} from the list of registered listeners.
+ *
+ * @param l the action listener to remove
+ *
+ * @see #addActionListener(ActionListener)
+ */
+ void removeActionListener(ActionListener l);
+
+ /**
+ * Adds an {@link ActionListener} to the list of registered listeners.
+ *
+ * An <code>ActionEvent</code> is usually fired when the user clicks on a
+ * button.
+ *
+ * @param l the action listener to add
+ *
+ * @see #removeActionListener(ActionListener)
+ */
+ void addActionListener(ActionListener l);
+
+ /**
+ * Adds an {@link ItemListener} to the list of registered listeners.
+ *
+ * An <code>ItemEvent</code> is usually fired when a button's selected
+ * state changes. This applies only to buttons that support the selected
+ * flag.
+ *
+ * @param l the item listener to add
+ *
+ * @see #removeItemListener(ItemListener)
+ */
+ void addItemListener(ItemListener l);
+
+ /**
+ * Adds an {@link ItemListener} to the list of registered listeners.
+ *
+ * @param l the item listener to add
+ *
+ * @see #removeItemListener(ItemListener)
+ */
+ void removeItemListener(ItemListener l);
+
+ /**
+ * Adds an {@link ChangeListener} to the list of registered listeners.
+ *
+ * A <code>ChangeEvent</code> is fired when any one of the button's flags
+ * changes.
+ *
+ * @param l the change listener to add
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ void addChangeListener(ChangeListener l);
+
+ /**
+ * Adds an {@link ChangeListener} to the list of registered listeners.
+ *
+ * @param l the change listener to add
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ void removeChangeListener(ChangeListener l);
+
+ /**
+ * Sets the rollover flag of the button.
+ *
+ * A button is rollover-ed, when the user has moved the mouse over it, but has
+ * not yet pressed the mouse.
+ *
+ * @param b <code>true</code> if the button is rollover, <code>false</code>
+ * otherwise
+ *
+ * @see #isRollover()
+ */
+ void setRollover(boolean b);
+
+ /**
+ * Returns <code>true</code> if the button is rollover-ed, <code>false</code>
+ * otherwise.
+ *
+ * A button is rollover-ed, when the user has moved the mouse over it, but has
+ * not yet pressed the mouse.
+ *
+ * @return <code>true</code> if the button is rollover, <code>false</code>
+ * otherwise
+ *
+ * @see #setRollover(boolean)
+ */
+ boolean isRollover();
+
+ /**
+ * Returns the keyboard mnemonic for the button. This specifies a shortcut
+ * or accelerator key that can be used to activate the button.
+ *
+ * @return the keyboard mnemonic for the button
+ *
+ * @see #setMnemonic(int)
+ */
+ int getMnemonic();
+
+ /**
+ * Sets the keyboard mnemonic for the button. This specifies a shortcut
+ * or accelerator key that can be used to activate the button.
+ *
+ * @param key the keyboard mnemonic for the button
+ *
+ * @see #getMnemonic()
+ */
+ void setMnemonic(int key);
+
+ /**
+ * Sets the action command for the button. This will be used in
+ * <code>ActionEvents</code> fired by the button.
+ *
+ * @param s the action command to set
+ *
+ * @see #getActionCommand()
+ */
+ void setActionCommand(String s);
+
+ /**
+ * Returns the action command of the button.
+ *
+ * @return the action command of the button
+ *
+ * @see #setActionCommand(String)
+ */
+ String getActionCommand();
+
+ /**
+ * Sets the button group for the button. Some kinds of button (e.g. radio
+ * buttons) allow only one button within a button group selected at any one
+ * time.
+ *
+ * @param group the button group to set
+ */
+ void setGroup(ButtonGroup group);
+
+ /**
+ * Sets the selected flag of the button.
+ *
+ * Some kinds of buttons (e.g. toggle buttons, check boxes, radio buttons)
+ * can be in one of two states: selected or unselected. The selected state
+ * is usually toggled by clicking on the button.
+ *
+ * @param b <code>true</code> if the button is selected, <code>false</code>
+ * otherwise
+ *
+ * @see #isSelected()
+ */
+ void setSelected(boolean b);
+
+ /**
+ * Returns <code>true</code> if the button is selected, <code>false</code>
+ * otherwise.
+ *
+ * Some kinds of buttons (e.g. toggle buttons, check boxes, radio buttons)
+ * can be in one of two states: selected or unselected. The selected state
+ * is usually toggled by clicking on the button.
+ *
+ * @return <code>true</code> if the button is selected, <code>false</code>
+ * otherwise
+ *
+ * @see #setSelected(boolean)
+ */
+ boolean isSelected();
+}
diff --git a/libjava/classpath/javax/swing/CellEditor.java b/libjava/classpath/javax/swing/CellEditor.java
new file mode 100644
index 000000000..134c316e4
--- /dev/null
+++ b/libjava/classpath/javax/swing/CellEditor.java
@@ -0,0 +1,108 @@
+/* CellEditor.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.EventObject;
+
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+
+/**
+ * Provides edit capabilities for components that display cells like
+ * {@link JTable}, {@link JList} and {@link JTree}.
+ *
+ * @author Andrew Selkirk
+ */
+public interface CellEditor
+{
+ /**
+ * Returns the current value for the <code>CellEditor</code>.
+ *
+ * @return The value.
+ */
+ Object getCellEditorValue();
+
+ /**
+ * Returns <code>true</code> if the specified event makes the editor
+ * editable, and <code>false</code> otherwise.
+ *
+ * @param event the event.
+ *
+ * @return A boolean.
+ */
+ boolean isCellEditable(EventObject event);
+
+ /**
+ * shouldSelectCell
+ * @param event TODO
+ * @return boolean
+ */
+ boolean shouldSelectCell(EventObject event);
+
+ /**
+ * Signals to the <code>CellEditor</code> that it should stop editing,
+ * accepting the current cell value, and returns <code>true</code> if the
+ * editor actually stops editing, and <code>false</code> otherwise.
+ *
+ * @return A boolean.
+ */
+ boolean stopCellEditing();
+
+ /**
+ * Signals to the <code>CellEditor</code> that it should cancel editing.
+ */
+ void cancelCellEditing();
+
+ /**
+ * Registers a listener to receive {@link ChangeEvent} notifications from the
+ * <code>CellEditor</code>.
+ *
+ * @param listener the listener.
+ */
+ void addCellEditorListener(CellEditorListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives {@link ChangeEvent}
+ * notifications from the <code>CellEditor</code>.
+ *
+ * @param listener the listener.
+ */
+ void removeCellEditorListener(CellEditorListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/CellRendererPane.java b/libjava/classpath/javax/swing/CellRendererPane.java
new file mode 100644
index 000000000..0140b4c2c
--- /dev/null
+++ b/libjava/classpath/javax/swing/CellRendererPane.java
@@ -0,0 +1,251 @@
+/* CellRendererPane.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * Paints the cells of JList, JTable and JTree.
+ * It intercepts the usual paint tree, so that we don't walk up and
+ * repaint everything.
+ *
+ * @author Andrew Selkirk
+ */
+public class CellRendererPane extends Container implements Accessible
+{
+ private static final long serialVersionUID = -7642183829532984273L;
+
+ /**
+ * Provides accessibility support for CellRendererPanes.
+ */
+ protected class AccessibleCellRendererPane extends AccessibleAWTContainer
+ {
+ private static final long serialVersionUID = -8981090083147391074L;
+
+ /**
+ * Constructor AccessibleCellRendererPane
+ */
+ protected AccessibleCellRendererPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * getAccessibleRole
+ * @return AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PANEL;
+ }
+ }
+
+ /**
+ * accessibleContext
+ */
+ protected AccessibleContext accessibleContext = null;
+
+ /**
+ * Constructs a new CellRendererPane.
+ */
+ public CellRendererPane()
+ {
+ setVisible(false);
+ }
+
+ /**
+ * Should not be called.
+ *
+ * @param graphics not used here
+ */
+ public void update(Graphics graphics)
+ {
+ //Nothing to do here.
+ }
+
+ /**
+ * Despite normal behaviour this does <em>not</em> cause the container
+ * to be invalidated. This prevents propagating up the paint tree.
+ */
+ public void invalidate()
+ {
+ // Overridden to do nothing.
+ }
+
+ /**
+ * Should not be called.
+ *
+ * @param graphics not used here
+ */
+ public void paint(Graphics graphics)
+ {
+ // Overridden to do nothing.
+ }
+
+ /**
+ * Overridden to check if a component is already a child of this Container.
+ * If it's already a child, nothing is done. Otherwise we pass this to
+ * <code>super.addImpl()</code>.
+ *
+ * @param c the component to add
+ * @param constraints not used here
+ * @param index not used here
+ */
+ protected void addImpl(Component c, Object constraints, int index)
+ {
+ if (!isAncestorOf(c))
+ {
+ super.addImpl(c, constraints, index);
+ }
+ }
+
+ /**
+ * Paints the specified component <code>c</code> on the {@link Graphics}
+ * context <code>graphics</code>. The Graphics context is tranlated to
+ * (x,y) and the components bounds are set to (w,h). If
+ * <code>shouldValidate</code>
+ * is set to true, then the component is validated before painting.
+ *
+ * @param graphics the graphics context to paint on
+ * @param c the component to be painted
+ * @param p the parent of the component
+ * @param x the X coordinate of the upper left corner where c should
+ be painted
+ * @param y the Y coordinate of the upper left corner where c should
+ be painted
+ * @param w the width of the components drawing area
+ * @param h the height of the components drawing area
+ * @param shouldValidate if <code>c</code> should be validated before
+ * painting
+ */
+ public void paintComponent(Graphics graphics, Component c,
+ Container p, int x, int y, int w, int h,
+ boolean shouldValidate)
+ {
+ // reparent c
+ addImpl(c, null, 0);
+
+ Rectangle oldClip = graphics.getClipBounds();
+ boolean translated = false;
+ try
+ {
+ // translate to (x,y)
+ graphics.translate(x, y);
+ translated = true;
+ graphics.clipRect(0, 0, w, h);
+ // set bounds of c
+ c.setBounds(0, 0, w, h);
+
+ // validate if necessary
+ if (shouldValidate)
+ {
+ c.validate();
+ }
+
+ // paint component
+ c.paint(graphics);
+ }
+ finally
+ {
+ // untranslate g
+ if (translated)
+ graphics.translate(-x, -y);
+ graphics.setClip(oldClip);
+ }
+ }
+
+ /**
+ * Paints the specified component <code>c</code> on the {@link Graphics}
+ * context <code>graphics</code>. The Graphics context is tranlated to (x,y)
+ * and the components bounds are set to (w,h). The component is <em>not</em>
+ * validated before painting.
+ *
+ * @param graphics the graphics context to paint on
+ * @param c the component to be painted
+ * @param p the parent of the component
+ * @param x the X coordinate of the upper left corner where c should
+ be painted
+ * @param y the Y coordinate of the upper left corner where c should
+ be painted
+ * @param w the width of the components drawing area
+ * @param h the height of the components drawing area
+ */
+ public void paintComponent(Graphics graphics, Component c,
+ Container p, int x, int y, int w, int h)
+ {
+ paintComponent(graphics, c, p, x, y, w, h, false);
+ }
+
+ /**
+ * Paints the specified component <code>c</code> on the {@link Graphics}
+ * context <code>g</code>. The Graphics context is tranlated to (r.x,r.y) and
+ * the components bounds are set to (r.width,r.height).
+ * The component is <em>not</em>
+ * validated before painting.
+ *
+ * @param graphics the graphics context to paint on
+ * @param c the component to be painted
+ * @param p the component on which we paint
+ * @param r the bounding rectangle of c
+ */
+ public void paintComponent(Graphics graphics, Component c,
+ Container p, Rectangle r)
+ {
+ paintComponent(graphics, c, p, r.x, r.y, r.width, r.height);
+ }
+
+ /**
+ * getAccessibleContext <em>TODO</em>
+ * @return AccessibleContext
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleCellRendererPane();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/ComboBoxEditor.java b/libjava/classpath/javax/swing/ComboBoxEditor.java
new file mode 100644
index 000000000..8e914e4b9
--- /dev/null
+++ b/libjava/classpath/javax/swing/ComboBoxEditor.java
@@ -0,0 +1,96 @@
+/* ComboBoxEditor.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.event.ActionListener;
+
+/**
+ * Provides edit capabilities for {@link JComboBox}es.
+ *
+ * @author Andrew Selkirk
+ * @author Olga Rodimina
+ */
+public interface ComboBoxEditor
+{
+ /**
+ * This method returns component that will be used by the combo box to
+ * display/edit currently selected item in the combo box.
+ *
+ * @return Component that will be used by the combo box to display/edit
+ * currently selected item
+ */
+ Component getEditorComponent();
+
+ /**
+ * Sets item that should be editted when any editting operation is performed
+ * by the user. The value is always equal to the currently selected value
+ * in the combo box. Thus, whenever a different value is selected from the
+ * combo box list then this method should be called to change editting item
+ * to the new selected item.
+ *
+ * @param item item that is currently selected in the combo box
+ */
+ void setItem(Object item);
+
+ /**
+ * This method returns item that is currently editable.
+ *
+ * @return Item in the combo box that is currently editable
+ */
+ Object getItem();
+
+ /**
+ * selectAll
+ */
+ void selectAll();
+
+ /**
+ * This method adds specified ActionListener to this ComboBoxEditor.
+ *
+ * @param listener
+ */
+ void addActionListener(ActionListener listener);
+
+ /**
+ * This method removes given ActionListener from this ComboBoxEditor.
+ *
+ * @param listener TODO
+ */
+ void removeActionListener(ActionListener listener);
+} // ComboBoxEditor
diff --git a/libjava/classpath/javax/swing/ComboBoxModel.java b/libjava/classpath/javax/swing/ComboBoxModel.java
new file mode 100644
index 000000000..ce252faed
--- /dev/null
+++ b/libjava/classpath/javax/swing/ComboBoxModel.java
@@ -0,0 +1,69 @@
+/* ComboBoxModel.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+
+/**
+ * The data model for a {@link JComboBox}. This model keeps track of elements
+ * contained in the <code>JComboBox</code> as well as the current
+ * combo box selection. Whenever the selection in the <code>JComboBox</code>
+ * changes, the <code>ComboBoxModel</code> should fire a {@link ListDataEvent}
+ * to the model's {@link ListDataListener}s.
+ *
+ * @author Andrew Selkirk
+ */
+public interface ComboBoxModel extends ListModel
+{
+ /**
+ * Sets the selected item in the combo box. Classes implementing this
+ * interface should fire a {@link ListDataEvent} to all registered
+ * {@link ListDataListener}s to indicate that the selection has changed.
+ *
+ * @param item the selected item (<code>null</code> permitted).
+ */
+ void setSelectedItem(Object item);
+
+ /**
+ * Returns the currently selected item in the combo box.
+ *
+ * @return The selected item (possibly <code>null</code>).
+ */
+ Object getSelectedItem();
+}
diff --git a/libjava/classpath/javax/swing/CompatibilityFocusTraversalPolicy.java b/libjava/classpath/javax/swing/CompatibilityFocusTraversalPolicy.java
new file mode 100644
index 000000000..40c2010c3
--- /dev/null
+++ b/libjava/classpath/javax/swing/CompatibilityFocusTraversalPolicy.java
@@ -0,0 +1,164 @@
+/* CompatibilityFocusTraversalPolicy.java -- Provides compatibility to old
+ focus API
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.FocusTraversalPolicy;
+import java.util.HashMap;
+
+/**
+ * Provides compatibility to the older focus API in
+ * {@link JComponent#setNextFocusableComponent(Component)}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class CompatibilityFocusTraversalPolicy
+ extends FocusTraversalPolicy
+{
+
+ /**
+ * The focus traversal policy that has been installed on the focus cycle
+ * root before, and to which we fall back.
+ */
+ private FocusTraversalPolicy fallback;
+
+ /**
+ * Maps components to their next focused components.
+ */
+ private HashMap forward;
+
+ /**
+ * Maps components to their previous focused components.
+ */
+ private HashMap backward;
+
+ /**
+ * Creates a new CompatibilityFocusTraversalPolicy with the specified
+ * policy as fallback.
+ *
+ * @param p the fallback policy
+ */
+ CompatibilityFocusTraversalPolicy(FocusTraversalPolicy p)
+ {
+ fallback = p;
+ forward = new HashMap();
+ backward = new HashMap();
+ }
+
+ public Component getComponentAfter(Container root, Component current)
+ {
+ Component next = (Component) forward.get(current);
+ if (next == null && fallback != null)
+ next = fallback.getComponentAfter(root, current);
+ return next;
+ }
+
+ public Component getComponentBefore(Container root, Component current)
+ {
+ Component previous = (Component) backward.get(current);
+ if (previous == null && fallback != null)
+ previous = fallback.getComponentAfter(root, current);
+ return previous;
+ }
+
+ public Component getFirstComponent(Container root)
+ {
+ Component first = null;
+ if (fallback != null)
+ first = fallback.getFirstComponent(root);
+ return first;
+ }
+
+ public Component getLastComponent(Container root)
+ {
+ Component last = null;
+ if (fallback != null)
+ last = fallback.getLastComponent(root);
+ return last;
+ }
+
+ public Component getDefaultComponent(Container root)
+ {
+ Component def = null;
+ if (fallback != null)
+ def = fallback.getDefaultComponent(root);
+ return def;
+ }
+
+ /**
+ * Sets a next focused component for a specified component. This is called
+ * by {@link JComponent#setNextFocusableComponent(Component)}.
+ *
+ * @param current the current component
+ * @param next the next focused component
+ */
+ void setNextFocusableComponent(Component current, Component next)
+ {
+ forward.put(current, next);
+ backward.put(next, current);
+ }
+
+ /**
+ * Sets a next focused component for a specified component. This is called
+ * by {@link JComponent#setNextFocusableComponent(Component)}.
+ *
+ * @param current the current component
+ * @param next the next focused component
+ */
+ void addNextFocusableComponent(Component current, Component next)
+ {
+ forward.put(current, next);
+ backward.put(next, current);
+ }
+
+ /**
+ * Removes a focused component mapping. This is called
+ * by {@link JComponent#setNextFocusableComponent(Component)}.
+ *
+ * @param current the current component
+ * @param next the next focused component
+ */
+ void removeNextFocusableComponent(Component current, Component next)
+ {
+ forward.remove(current);
+ backward.remove(next);
+ }
+}
diff --git a/libjava/classpath/javax/swing/ComponentInputMap.java b/libjava/classpath/javax/swing/ComponentInputMap.java
new file mode 100644
index 000000000..dc4d0bfd4
--- /dev/null
+++ b/libjava/classpath/javax/swing/ComponentInputMap.java
@@ -0,0 +1,141 @@
+/* ComponentInputMap.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An {@link InputMap} that is associated with a particular {@link JComponent}.
+ * The component is notified when its <code>ComponentInputMap</code> changes.
+ *
+ * @author Andrew Selkirk
+ * @author Michael Koch
+ */
+public class ComponentInputMap extends InputMap
+{
+ /**
+ * The component to notify.
+ */
+ private JComponent component;
+
+ /**
+ * Creates <code>ComponentInputMap</code> object that notifies the given
+ * component about changes to it.
+ *
+ * @param comp the component to notify
+ *
+ * @exception IllegalArgumentException if comp is null
+ */
+ public ComponentInputMap(JComponent comp)
+ {
+ if (comp == null)
+ throw new IllegalArgumentException();
+
+ this.component = comp;
+ }
+
+ /**
+ * Puts a new entry into the <code>InputMap</code>.
+ * If actionMapKey is null an existing entry will be removed.
+ *
+ * @param keystroke the keystroke for the entry
+ * @param value the action.
+ */
+ public void put(KeyStroke keystroke, Object value)
+ {
+ super.put(keystroke, value);
+ if (component != null)
+ component.updateComponentInputMap(this);
+ }
+
+ /**
+ * Clears the <code>InputMap</code>.
+ */
+ public void clear()
+ {
+ super.clear();
+ if (component != null)
+ component.updateComponentInputMap(this);
+ }
+
+ /**
+ * Remove an entry from the <code>InputMap</code>.
+ *
+ * @param keystroke the key of the entry to remove
+ */
+ public void remove(KeyStroke keystroke)
+ {
+ super.remove(keystroke);
+ if (component != null)
+ component.updateComponentInputMap(this);
+ }
+
+ /**
+ * Sets a parent for this <code>ComponentInputMap</code>.
+ *
+ * @param parentMap the new parent
+ *
+ * @exception IllegalArgumentException if parentMap is not a
+ * <code>ComponentInputMap</code> or not associated with the same component
+ */
+ public void setParent(InputMap parentMap)
+ {
+ if (parentMap != null && !(parentMap instanceof ComponentInputMap))
+ throw new IllegalArgumentException("ComponentInputMaps can only have " +
+ "ComponentInputMaps for parents");
+
+ if (parentMap != null &&
+ ((ComponentInputMap) parentMap).getComponent() != component)
+ throw new
+ IllegalArgumentException("ComponentInputMaps' parents must " +
+ "be associated with the same JComponents");
+
+ super.setParent(parentMap);
+ if (component != null)
+ component.updateComponentInputMap(this);
+ }
+
+ /**
+ * Returns the component to notify about changes.
+ *
+ * @return a <code>JComponent</code> object
+ */
+ public JComponent getComponent()
+ {
+ return component;
+ }
+}
diff --git a/libjava/classpath/javax/swing/DebugGraphics.java b/libjava/classpath/javax/swing/DebugGraphics.java
new file mode 100644
index 000000000..d96de4a22
--- /dev/null
+++ b/libjava/classpath/javax/swing/DebugGraphics.java
@@ -0,0 +1,1125 @@
+/* DebugGraphics.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.image.ImageObserver;
+import java.io.PrintStream;
+import java.text.AttributedCharacterIterator;
+
+
+/**
+ * An extension of {@link Graphics} that can be used for debugging
+ * custom Swing widgets. <code>DebugGraphics</code> has the ability to
+ * draw slowly and can log drawing actions.
+ *
+ * @author Andrew Selkirk
+ */
+public class DebugGraphics extends Graphics
+{
+ /**
+ * LOG_OPTION
+ */
+ public static final int LOG_OPTION = 1;
+
+ /**
+ * FLASH_OPTION
+ */
+ public static final int FLASH_OPTION = 2;
+
+ /**
+ * BUFFERED_OPTION
+ */
+ public static final int BUFFERED_OPTION = 4;
+
+ /**
+ * NONE_OPTION
+ */
+ public static final int NONE_OPTION = -1;
+
+ static Color debugFlashColor = Color.RED;
+ static int debugFlashCount = 10;
+ static int debugFlashTime = 1000;
+ static PrintStream debugLogStream = System.out;
+
+ /**
+ * Counts the created DebugGraphics objects. This is used by the
+ * logging facility.
+ */
+ static int counter = 0;
+
+ /**
+ * graphics
+ */
+ Graphics graphics;
+
+ /**
+ * buffer
+ */
+ Image buffer;
+
+ /**
+ * debugOptions
+ */
+ int debugOptions;
+
+ /**
+ * graphicsID
+ */
+ int graphicsID;
+
+ /**
+ * xOffset
+ */
+ int xOffset;
+
+ /**
+ * yOffset
+ */
+ int yOffset;
+
+ /**
+ * Creates a <code>DebugGraphics</code> object.
+ */
+ public DebugGraphics()
+ {
+ counter++;
+ }
+
+ /**
+ * Creates a <code>DebugGraphics</code> object.
+ *
+ * @param graphics The <code>Graphics</code> object to wrap
+ * @param component TODO
+ */
+ public DebugGraphics(Graphics graphics, JComponent component)
+ {
+ this(graphics);
+ // FIXME: What shall we do with component ?
+ }
+
+ /**
+ * Creates a <code>DebugGraphics</code> object.
+ *
+ * @param graphics The <code>Graphics</code> object to wrap
+ */
+ public DebugGraphics(Graphics graphics)
+ {
+ this();
+ this.graphics = graphics;
+ }
+
+ /**
+ * Sets the color to draw stuff with.
+ *
+ * @param color The color
+ */
+ public void setColor(Color color)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting color: " + color);
+
+ graphics.setColor(color);
+ }
+
+ /**
+ * Creates a overrides <code>Graphics.create</code> to create a
+ * <code>DebugGraphics</code> object.
+ *
+ * @return a new <code>DebugGraphics</code> object.
+ */
+ public Graphics create()
+ {
+ DebugGraphics copy = new DebugGraphics(graphics.create());
+ copy.debugOptions = debugOptions;
+ return copy;
+ }
+
+ /**
+ * Creates a overrides <code>Graphics.create</code> to create a
+ * <code>DebugGraphics</code> object.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ *
+ * @return a new <code>DebugGraphics</code> object.
+ */
+ public Graphics create(int x, int y, int width, int height)
+ {
+ DebugGraphics copy = new DebugGraphics(graphics.create(x, y, width,
+ height));
+ copy.debugOptions = debugOptions;
+ return copy;
+ }
+
+ /**
+ * flashColor
+ *
+ * @return Color
+ */
+ public static Color flashColor()
+ {
+ return debugFlashColor;
+ }
+
+ /**
+ * setFlashColor
+ *
+ * @param color the color to use for flashing
+ */
+ public static void setFlashColor(Color color)
+ {
+ debugFlashColor = color;
+ }
+
+ /**
+ * flashTime
+ *
+ * @return The time in milliseconds
+ */
+ public static int flashTime()
+ {
+ return debugFlashTime;
+ }
+
+ /**
+ * setFlashTime
+ *
+ * @param time The time in milliseconds
+ */
+ public static void setFlashTime(int time)
+ {
+ debugFlashTime = time;
+ }
+
+ /**
+ * flashCount
+ *
+ * @return The number of flashes
+ */
+ public static int flashCount()
+ {
+ return debugFlashCount;
+ }
+
+ /**
+ * setFlashCount
+ *
+ * @param count The number of flashes
+ */
+ public static void setFlashCount(int count)
+ {
+ debugFlashCount = count;
+ }
+
+ /**
+ * logStream
+ *
+ * @return The <code>PrintStream</code> to write logging messages to
+ */
+ public static PrintStream logStream()
+ {
+ return debugLogStream;
+ }
+
+ /**
+ * setLogStream
+ *
+ * @param stream The currently set <code>PrintStream</code>.
+ */
+ public static void setLogStream(PrintStream stream)
+ {
+ debugLogStream = stream;
+ }
+
+ /**
+ * getFont
+ *
+ * @return The font
+ */
+ public Font getFont()
+ {
+ return graphics.getFont();
+ }
+
+ /**
+ * setFont
+ *
+ * @param font The font to use for drawing text
+ */
+ public void setFont(Font font)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting font: " + font);
+
+ graphics.setFont(font);
+ }
+
+ /**
+ * Returns the color used for drawing.
+ *
+ * @return The color.
+ */
+ public Color getColor()
+ {
+ return graphics.getColor();
+ }
+
+ /**
+ * Returns the font metrics of the current font.
+ *
+ * @return a <code>FontMetrics</code> object
+ */
+ public FontMetrics getFontMetrics()
+ {
+ return graphics.getFontMetrics();
+ }
+
+ /**
+ * Returns the font metrics for a given font.
+ *
+ * @param font the font to get the metrics for
+ *
+ * @return a <code>FontMetrics</code> object
+ */
+ public FontMetrics getFontMetrics(Font font)
+ {
+ return graphics.getFontMetrics(font);
+ }
+
+ /**
+ * translate
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public void translate(int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Translating by: " + new Point(x, y));
+
+ graphics.translate(x, y);
+ }
+
+ /**
+ * setPaintMode
+ */
+ public void setPaintMode()
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting paint mode");
+
+ graphics.setPaintMode();
+ }
+
+ /**
+ * setXORMode
+ *
+ * @param color the color
+ */
+ public void setXORMode(Color color)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting XOR mode: " + color);
+
+ graphics.setXORMode(color);
+ }
+
+ /**
+ * getClipBounds
+ *
+ * @return Rectangle
+ */
+ public Rectangle getClipBounds()
+ {
+ return graphics.getClipBounds();
+ }
+
+ /**
+ * Intersects the current clip region with the given region.
+ *
+ * @param x The x-position of the region
+ * @param y The y-position of the region
+ * @param width The width of the region
+ * @param height The height of the region
+ */
+ public void clipRect(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().print(prefix() + " Setting clipRect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.clipRect(x, y, width, height);
+
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(" Netting clipRect: " + graphics.getClipBounds());
+ }
+
+ /**
+ * Sets the clipping region.
+ *
+ * @param x The x-position of the region
+ * @param y The y-position of the region
+ * @param width The width of the region
+ * @param height The height of the region
+ */
+ public void setClip(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Setting new clipRect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.setClip(x, y, width, height);
+ }
+
+ /**
+ * Returns the current clipping region.
+ *
+ * @return Shape
+ */
+ public Shape getClip()
+ {
+ return graphics.getClip();
+ }
+
+ /**
+ * Sets the current clipping region
+ *
+ * @param shape The clippin region
+ */
+ public void setClip(Shape shape)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting new clipRect: " + shape);
+
+ graphics.setClip(shape);
+ }
+
+ private void sleep(int milliseconds)
+ {
+ try
+ {
+ Thread.sleep(milliseconds);
+ }
+ catch (InterruptedException e)
+ {
+ // Ignore this.
+ }
+ }
+
+ /**
+ * Draws a rectangle.
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ */
+ public void drawRect(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing rect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ if ((debugOptions & FLASH_OPTION) != 0)
+ {
+ Color color = graphics.getColor();
+ for (int index = 0; index < (debugFlashCount - 1); ++index)
+ {
+ graphics.setColor(color);
+ graphics.drawRect(x, y, width, height);
+ sleep(debugFlashTime);
+ graphics.setColor(debugFlashColor);
+ graphics.drawRect(x, y, width, height);
+ sleep(debugFlashTime);
+ }
+ graphics.setColor(color);
+ }
+
+ graphics.drawRect(x, y, width, height);
+ }
+
+ /**
+ * Draws a filled rectangle.
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ */
+ public void fillRect(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling rect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ if ((debugOptions & FLASH_OPTION) != 0)
+ {
+ Color color = graphics.getColor();
+ for (int index = 0; index < (debugFlashCount - 1); ++index)
+ {
+ graphics.setColor(color);
+ graphics.fillRect(x, y, width, height);
+ sleep(debugFlashTime);
+ graphics.setColor(debugFlashColor);
+ graphics.fillRect(x, y, width, height);
+ sleep(debugFlashTime);
+ }
+ graphics.setColor(color);
+ }
+
+ graphics.fillRect(x, y, width, height);
+ }
+
+ /**
+ * clearRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ */
+ public void clearRect(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Clearing rect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.clearRect(x, y, width, height);
+ }
+
+ /**
+ * drawRoundRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ * @param arcWidth TODO
+ * @param arcHeight TODO
+ */
+ public void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing round rect: "
+ + new Rectangle(x, y, width, height)
+ + " arcWidth: " + arcWidth
+ + " arcHeight: " + arcHeight);
+ }
+
+ graphics.drawRoundRect(x, y, width, height, arcWidth, arcHeight);
+ }
+
+ /**
+ * fillRoundRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ * @param arcWidth TODO
+ * @param arcHeight TODO
+ */
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling round rect: "
+ + new Rectangle(x, y, width, height)
+ + " arcWidth: " + arcWidth
+ + " arcHeight: " + arcHeight);
+ }
+
+ graphics.fillRoundRect(x, y, width, height, arcWidth, arcHeight);
+ }
+
+ /**
+ * drawLine
+ *
+ * @param x1 The x-position of the start
+ * @param y1 The y-position of the start
+ * @param x2 The x-position of the end
+ * @param y2 The y-position of the end
+ */
+ public void drawLine(int x1, int y1, int x2, int y2)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing line: from (" + x1 + ", "
+ + y1 + ") to (" + x2 + ", " + y2 + ")");
+ }
+
+ graphics.drawLine(x1, y1, x2, y2);
+ }
+
+ /**
+ * draw3DRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ * @param raised TODO
+ */
+ public void draw3DRect(int x, int y, int width, int height, boolean raised)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing 3D rect: "
+ + new Rectangle(x, y, width, height)
+ + "Raised bezel: " + raised);
+ }
+
+ graphics.draw3DRect(x, y, width, height, raised);
+ }
+
+ /**
+ * fill3DRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ * @param raised TODO
+ */
+ public void fill3DRect(int x, int y, int width, int height, boolean raised)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling 3D rect: "
+ + new Rectangle(x, y, width, height)
+ + "Raised bezel: " + raised);
+ }
+
+ graphics.fill3DRect(x, y, width, height, raised);
+ }
+
+ /**
+ * drawOval
+ *
+ * @param x the x coordinate
+ * @param y the y coordiante
+ * @param width the width
+ * @param height the height
+ */
+ public void drawOval(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing oval: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.drawOval(x, y, width, height);
+ }
+
+ /**
+ * fillOval
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ */
+ public void fillOval(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling oval: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.fillOval(x, y, width, height);
+ }
+
+ /**
+ * drawArc
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ * @param startAngle TODO
+ * @param arcAngle TODO
+ */
+ public void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing arc: "
+ + new Rectangle(x, y, width, height)
+ + " startAngle: " + startAngle
+ + " arcAngle: " + arcAngle);
+ }
+
+ graphics.drawArc(x, y, width, height, startAngle, arcAngle);
+ }
+
+ /**
+ * fillArc
+ *
+ * @param x the coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ * @param startAngle TODO
+ * @param arcAngle TODO
+ */
+ public void fillArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling arc: "
+ + new Rectangle(x, y, width, height)
+ + " startAngle: " + startAngle
+ + " arcAngle: " + arcAngle);
+ }
+
+ graphics.fillArc(x, y, width, height, startAngle, arcAngle);
+ }
+
+ /**
+ * drawPolyline
+ *
+ * @param xpoints TODO
+ * @param ypoints TODO
+ * @param npoints TODO
+ */
+ public void drawPolyline(int[] xpoints, int[] ypoints, int npoints)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing polyline: nPoints: " + npoints
+ + " X's: " + xpoints + " Y's: " + ypoints);
+ }
+
+ graphics.drawPolyline(xpoints, ypoints, npoints);
+ }
+
+ /**
+ * drawPolygon
+ *
+ * @param xpoints TODO
+ * @param ypoints TODO
+ * @param npoints TODO
+ */
+ public void drawPolygon(int[] xpoints, int[] ypoints, int npoints)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing polygon: nPoints: " + npoints
+ + " X's: " + xpoints + " Y's: " + ypoints);
+ }
+
+ graphics.drawPolygon(xpoints, ypoints, npoints);
+ }
+
+ /**
+ * fillPolygon
+ *
+ * @param xpoints TODO
+ * @param ypoints TODO
+ * @param npoints TODO
+ */
+ public void fillPolygon(int[] xpoints, int[] ypoints, int npoints)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing polygon: nPoints: " + npoints
+ + " X's: " + xpoints + " Y's: " + ypoints);
+ }
+
+ graphics.fillPolygon(xpoints, ypoints, npoints);
+ }
+
+ /**
+ * drawString
+ *
+ * @param string the string
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public void drawString(String string, int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing string: \"" + string
+ + "\" at: " + new Point(x, y));
+ }
+
+ graphics.drawString(string, x, y);
+ }
+
+ /**
+ * drawString
+ *
+ * @param iterator TODO
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public void drawString(AttributedCharacterIterator iterator,
+ int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing string: \"" + iterator
+ + "\" at: " + new Point(x, y));
+ }
+
+ graphics.drawString(iterator, x, y);
+ }
+
+ /**
+ * drawBytes
+ *
+ * @param data TODO
+ * @param offset TODO
+ * @param length TODO
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public void drawBytes(byte[] data, int offset, int length,
+ int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Drawing bytes at: " + new Point(x, y));
+
+ graphics.drawBytes(data, offset, length, x, y);
+ }
+
+ /**
+ * drawChars
+ *
+ * @param data array of characters to draw
+ * @param offset offset in array
+ * @param length number of characters in array to draw
+ * @param x x-position
+ * @param y y-position
+ */
+ public void drawChars(char[] data, int offset, int length,
+ int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Drawing chars at: " + new Point(x, y));
+
+ if ((debugOptions & FLASH_OPTION) != 0)
+ {
+ Color color = graphics.getColor();
+ for (int index = 0; index < (debugFlashCount - 1); ++index)
+ {
+ graphics.setColor(color);
+ graphics.drawChars(data, offset, length, x, y);
+ sleep(debugFlashTime);
+ graphics.setColor(debugFlashColor);
+ graphics.drawChars(data, offset, length, x, y);
+ sleep(debugFlashTime);
+ }
+ graphics.setColor(color);
+ }
+
+ graphics.drawChars(data, offset, length, x, y);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param x The x position
+ * @param y The y position
+ * @param observer The image observer
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int x, int y,
+ ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image + " at: "
+ + new Point(x, y));
+ }
+
+ return graphics.drawImage(image, x, y, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param x The x position
+ * @param y The y position
+ * @param width The width of the area to draw the image
+ * @param height The height of the area to draw the image
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int x, int y, int width,
+ int height, ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " at: " + new Rectangle(x, y, width, height));
+ }
+
+ return graphics.drawImage(image, x, y, width, height, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param x The x position
+ * @param y The y position
+ * @param background The color for the background in the opaque regions
+ * of the image
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int x, int y,
+ Color background, ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " at: " + new Point(x, y)
+ + ", bgcolor: " + background);
+ }
+
+ return graphics.drawImage(image, x, y, background, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param x The x position
+ * @param y The y position
+ * @param width The width of the area to draw the image
+ * @param height The height of the area to draw the image
+ * @param background The color for the background in the opaque regions
+ * of the image
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int x, int y, int width, int height,
+ Color background, ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " at: " + new Rectangle(x, y, width, height)
+ + ", bgcolor: " + background);
+ }
+
+ return graphics.drawImage(image, x, y, width, height, background, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param dx1 TODO
+ * @param dy1 TODO
+ * @param dx2 TODO
+ * @param dy2 TODO
+ * @param sx1 TODO
+ * @param sy1 TODO
+ * @param sx2 TODO
+ * @param sy2 TODO
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int dx1, int dy1,
+ int dx2, int dy2, int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " destination: " + new Rectangle(dx1, dy1, dx2, dy2)
+ + " source: " + new Rectangle(sx1, sy1, sx2, sy2));
+ }
+
+ return graphics.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param dx1 TODO
+ * @param dy1 TODO
+ * @param dx2 TODO
+ * @param dy2 TODO
+ * @param sx1 TODO
+ * @param sy1 TODO
+ * @param sx2 TODO
+ * @param sy2 TODO
+ * @param background The color for the background in the opaque regions
+ * of the image
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int dx1, int dy1,
+ int dx2, int dy2, int sx1, int sy1, int sx2, int sy2,
+ Color background, ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " destination: " + new Rectangle(dx1, dy1, dx2, dy2)
+ + " source: " + new Rectangle(sx1, sy1, sx2, sy2)
+ + ", bgcolor: " + background);
+ }
+
+ return graphics.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, background, observer);
+ }
+
+ /**
+ * copyArea
+ *
+ * @param x The x position of the source area
+ * @param y The y position of the source area
+ * @param width The width of the area
+ * @param height The height of the area
+ * @param destx The x position of the destination area
+ * @param desty The y posiiton of the destination area
+ */
+ public void copyArea(int x, int y, int width, int height,
+ int destx, int desty)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Copying area from: "
+ + new Rectangle(x, y, width, height)
+ + " to: " + new Point(destx, desty));
+ }
+
+ graphics.copyArea(x, y, width, height, destx, desty);
+ }
+
+ /**
+ * Releases all system resources that this <code>Graphics</code> is using.
+ */
+ public void dispose()
+ {
+ graphics.dispose();
+ graphics = null;
+ }
+
+ /**
+ * isDrawingBuffer
+ *
+ * @return boolean
+ */
+ public boolean isDrawingBuffer()
+ {
+ return false; // TODO
+ }
+
+ /**
+ * setDebugOptions
+ *
+ * @param options the debug options
+ */
+ public void setDebugOptions(int options)
+ {
+ debugOptions = options;
+ if ((debugOptions & LOG_OPTION) != 0)
+ if (options == NONE_OPTION)
+ logStream().println(prefix() + "Disabling debug");
+ else
+ logStream().println(prefix() + "Enabling debug");
+ }
+
+ /**
+ * getDebugOptions
+ *
+ * @return the debug options
+ */
+ public int getDebugOptions()
+ {
+ return debugOptions;
+ }
+
+ /**
+ * Creates and returns the prefix that should be prepended to all logging
+ * messages. The prefix is made up like this:
+ *
+ * <code>Graphics(<counter>-1)</code> where counter is an integer number
+ * saying how many DebugGraphics objects have been created so far. The second
+ * number always seem to be 1 on Sun's JDK, this has to be investigated a
+ * little more.
+ *
+ * @return the prefix that should be prepended to all logging
+ * messages
+ */
+ private String prefix()
+ {
+ return "Graphics(" + counter + "-1)";
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java
new file mode 100644
index 000000000..c1d0fe74a
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java
@@ -0,0 +1,475 @@
+/* DefaultBoundedRangeModel.java -- Default implementation
+ of BoundedRangeModel.
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * The default implementation of <code>BoundedRangeModel</code>.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class DefaultBoundedRangeModel
+ implements BoundedRangeModel, Serializable
+{
+ /**
+ * The identifier of this class in object serialization. Verified
+ * using the serialver tool of Sun J2SE 1.4.1_01.
+ */
+ private static final long serialVersionUID = 5034068491295259790L;
+
+ /**
+ * An event that is sent to all registered {@link ChangeListener}s
+ * when the state of this range model has changed.
+ *
+ * <p>The event object is created on demand, the first time it
+ * is actually needed.</p>
+ *
+ * @see #fireStateChanged()
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * The list of the currently registered EventListeners.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * The current value of the range model, which is always between
+ * {@link #minimum} and ({@link #maximum} - {@link #extent}). In a
+ * scroll bar visualization of a {@link BoundedRangeModel}, the
+ * <code>value</code> is displayed as the position of the thumb.
+ */
+ private int value;
+
+ /**
+ * The current extent of the range model, which is a number greater
+ * than or equal to zero. In a scroll bar visualization of a {@link
+ * BoundedRangeModel}, the <code>extent</code> is displayed as the
+ * size of the thumb.
+ */
+ private int extent;
+
+ /**
+ * The current minimum value of the range model, which is always
+ * less than or equal to {@link #maximum}.
+ */
+ private int minimum;
+
+ /**
+ * The current maximum value of the range model, which is always
+ * greater than or equal to {@link #minimum}.
+ */
+ private int maximum;
+
+ /**
+ * A property that indicates whether the value of this {@link
+ * BoundedRangeModel} is going to change in the immediate future.
+ */
+ private boolean isAdjusting;
+
+ /**
+ * Constructs a <code>DefaultBoundedRangeModel</code> with default
+ * values for the properties. The properties <code>value</code>,
+ * <code>extent</code> and <code>minimum</code> will be initialized
+ * to zero; <code>maximum</code> will be set to 100; the property
+ * <code>valueIsAdjusting</code> will be <code>false</code>.
+ */
+ public DefaultBoundedRangeModel()
+ {
+ // The fields value, extent, minimum have the default value 0, and
+ // isAdjusting is already false. These fields no not need to be
+ // set explicitly.
+ maximum = 100;
+ }
+
+ /**
+ * Constructs a <code>DefaultBoundedRangeModel</code> with the
+ * specified values for some properties.
+ *
+ * @param value the initial value of the range model, which must be
+ * a number between <code>minimum</code> and <code>(maximum -
+ * extent)</code>. In a scroll bar visualization of a {@link
+ * BoundedRangeModel}, the <code>value</code> is displayed as the
+ * position of the thumb.
+ * @param extent the initial extent of the range model, which is a
+ * number greater than or equal to zero. In a scroll bar
+ * visualization of a {@link BoundedRangeModel}, the
+ * <code>extent</code> is displayed as the size of the thumb.
+ * @param minimum the initial minimal value of the range model.
+ * @param maximum the initial maximal value of the range model.
+ *
+ * @throws IllegalArgumentException if the following condition is
+ * not satisfied: <code>minimum &lt;= value &lt;= value + extent &lt;=
+ * maximum</code>.
+ */
+ public DefaultBoundedRangeModel(int value, int extent, int minimum,
+ int maximum)
+ {
+ if (!(minimum <= value && extent >= 0 && (value + extent) <= maximum))
+ throw new IllegalArgumentException();
+
+ this.value = value;
+ this.extent = extent;
+ this.minimum = minimum;
+ this.maximum = maximum;
+
+ // The isAdjusting field already has a false value by default.
+ }
+
+ /**
+ * Returns a string with all relevant properties of this range
+ * model.
+ *
+ * @return a string representing the object
+ */
+ public String toString()
+ {
+ return getClass().getName()
+ + "[value=" + value
+ + ", extent=" + extent
+ + ", min=" + minimum
+ + ", max=" + maximum
+ + ", adj=" + isAdjusting
+ + ']';
+ }
+
+ /**
+ * Returns the current value of this bounded range model. In a
+ * scroll bar visualization of a {@link BoundedRangeModel}, the
+ * <code>value</code> is displayed as the position of the thumb.
+ *
+ * @return the value
+ */
+ public int getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Changes the current value of this bounded range model. In a
+ * scroll bar visualization of a {@link BoundedRangeModel}, the
+ * <code>value</code> is displayed as the position of the thumb;
+ * changing the <code>value</code> of a scroll bar's model
+ * thus moves the thumb to a different position.
+ *
+ * @param value the value
+ */
+ public void setValue(int value)
+ {
+ value = Math.max(minimum, value);
+ if (value + extent > maximum)
+ value = maximum - extent;
+
+ if (value != this.value)
+ {
+ this.value = value;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the current extent of this bounded range model, which is
+ * a number greater than or equal to zero. In a scroll bar
+ * visualization of a {@link BoundedRangeModel}, the
+ * <code>extent</code> is displayed as the size of the thumb.
+ *
+ * @return the extent
+ */
+ public int getExtent()
+ {
+ return extent;
+ }
+
+ /**
+ * Changes the current extent of this bounded range model. In a
+ * scroll bar visualization of a {@link BoundedRangeModel}, the
+ * <code>extent</code> is displayed as the size of the thumb.
+ *
+ * @param extent the new extent of the range model, which is a
+ * number greater than or equal to zero.
+ */
+ public void setExtent(int extent)
+ {
+ extent = Math.max(extent, 0);
+ if (value + extent > maximum)
+ extent = maximum - value;
+
+ if (extent != this.extent)
+ {
+ this.extent = extent;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the current minimal value of this bounded range model.
+ */
+ public int getMinimum()
+ {
+ return minimum;
+ }
+
+ /**
+ * Changes the current minimal value of this bounded range model.
+ *
+ * @param minimum the new minimal value.
+ */
+ public void setMinimum(int minimum)
+ {
+ int value, maximum;
+
+ maximum = Math.max(minimum, this.maximum);
+ value = Math.max(minimum, this.value);
+
+ setRangeProperties(value, extent, minimum, maximum, isAdjusting);
+ }
+
+ /**
+ * Returns the current maximal value of this bounded range model.
+ *
+ * @return the maximum
+ */
+ public int getMaximum()
+ {
+ return maximum;
+ }
+
+ /**
+ * Changes the current maximal value of this bounded range model.
+ *
+ * @param maximum the new maximal value.
+ */
+ public void setMaximum(int maximum)
+ {
+ int value, extent, minimum;
+
+ minimum = Math.min(this.minimum, maximum);
+ extent = Math.min(this.extent, maximum - minimum);
+ value = Math.min(this.value, maximum - extent);
+
+ setRangeProperties(value, extent, minimum, maximum, isAdjusting);
+ }
+
+ /**
+ * Returns whether or not the value of this bounded range model is
+ * going to change in the immediate future. Scroll bars set this
+ * property to <code>true</code> while the thumb is being dragged
+ * around; when the mouse is relased, they set the property to
+ * <code>false</code> and post a final {@link ChangeEvent}.
+ *
+ * @return <code>true</code> if the value will change soon again;
+ * <code>false</code> if the value will probably not change soon.
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return isAdjusting;
+ }
+
+ /**
+ * Specifies whether or not the value of this bounded range model is
+ * going to change in the immediate future. Scroll bars set this
+ * property to <code>true</code> while the thumb is being dragged
+ * around; when the mouse is relased, they set the property to
+ * <code>false</code>.
+ *
+ * @param isAdjusting <code>true</code> if the value will change
+ * soon again; <code>false</code> if the value will probably not
+ * change soon.
+ */
+ public void setValueIsAdjusting(boolean isAdjusting)
+ {
+ if (isAdjusting == this.isAdjusting)
+ return;
+
+ this.isAdjusting = isAdjusting;
+ fireStateChanged();
+ }
+
+ /**
+ * Sets all properties.
+ *
+ * @param value the new value of the range model. In a scroll bar
+ * visualization of a {@link BoundedRangeModel}, the
+ * <code>value</code> is displayed as the position of the thumb.
+ * @param extent the new extent of the range model, which is a
+ * number greater than or equal to zero. In a scroll bar
+ * visualization of a {@link BoundedRangeModel}, the
+ * <code>extent</code> is displayed as the size of the thumb.
+ * @param minimum the new minimal value of the range model.
+ * @param maximum the new maximal value of the range model.
+ * @param isAdjusting whether or not the value of this bounded range
+ * model is going to change in the immediate future. Scroll bars set
+ * this property to <code>true</code> while the thumb is being
+ * dragged around; when the mouse is relased, they set the property
+ * to <code>false</code>.
+ */
+ public void setRangeProperties(int value, int extent, int minimum,
+ int maximum, boolean isAdjusting)
+ {
+ minimum = Math.min(Math.min(minimum, maximum), value);
+ maximum = Math.max(value, maximum);
+ if (extent + value > maximum)
+ extent = maximum - value;
+ extent = Math.max(0, extent);
+
+ if ((value == this.value)
+ && (extent == this.extent)
+ && (minimum == this.minimum)
+ && (maximum == this.maximum)
+ && (isAdjusting == this.isAdjusting))
+ return;
+
+ this.value = value;
+ this.extent = extent;
+ this.minimum = minimum;
+ this.maximum = maximum;
+ this.isAdjusting = isAdjusting;
+
+ fireStateChanged();
+ }
+
+ /**
+ * Subscribes a ChangeListener to state changes.
+ *
+ * @param listener the listener to be subscribed.
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Cancels the subscription of a ChangeListener.
+ *
+ * @param listener the listener to be unsubscribed.
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Sends a {@link ChangeEvent} to any registered {@link
+ * ChangeListener}s.
+ *
+ * @see #addChangeListener(ChangeListener)
+ * @see #removeChangeListener(ChangeListener)
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+
+ for (int i = listeners.length - 1; i >= 0; --i)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Retrieves the current listeners of the specified class.
+ *
+ * @param listenerType the class of listeners; usually {@link
+ * ChangeListener}<code>.class</code>.
+ *
+ * @return an array with the currently subscribed listeners, or
+ * an empty array if there are currently no listeners.
+ *
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Returns all <code>ChangeListeners</code> that are currently
+ * subscribed for changes to this
+ * <code>DefaultBoundedRangeModel</code>.
+ *
+ * @return an array with the currently subscribed listeners, or
+ * an empty array if there are currently no listeners.
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Provides serialization support.
+ *
+ * @param stream the output stream (<code>null</code> not permitted).
+ *
+ * @throws IOException if there is an I/O error.
+ */
+ private void writeObject(ObjectOutputStream stream)
+ throws IOException
+ {
+ stream.defaultWriteObject();
+ }
+
+ /**
+ * Provides serialization support.
+ *
+ * @param stream the input stream (<code>null</code> not permitted).
+ *
+ * @throws IOException if there is an I/O error.
+ * @throws ClassNotFoundException if there is a classpath problem.
+ */
+ private void readObject(ObjectInputStream stream)
+ throws ClassNotFoundException, IOException
+ {
+ stream.defaultReadObject();
+ listenerList = new EventListenerList();
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/DefaultButtonModel.java b/libjava/classpath/javax/swing/DefaultButtonModel.java
new file mode 100644
index 000000000..d29a23ed3
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultButtonModel.java
@@ -0,0 +1,578 @@
+/* DefaultButtonModel.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.ItemSelectable;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * The default implementation of {@link ButtonModel}.
+ * The purpose of this class is to model the dynamic state of an abstract
+ * button. The concrete button type holding this state may be a a "toggle"
+ * button (checkbox, radio button) or a "push" button (menu button, button).
+ * If the model is disabled, only the "selected" property can be changed. An
+ * attempt to change the "armed", "rollover" or "pressed" properties while
+ * the model is disabled will be blocked. Any successful (non-blocked) change
+ * to the model's properties will trigger the firing of a ChangeEvent. Any
+ * change to the "selected" property will trigger the firing of an ItemEvent
+ * in addition to ChangeEvent. This is true whether the model is enabled or
+ * not. One other state change is special: the transition from "enabled,
+ * armed and pressed" to "enabled, armed and not-pressed". This is considered
+ * the "trailing edge" of a successful mouse click, and therefore fires an
+ * ActionEvent in addition to a ChangeEvent. In all other respects this class
+ * is just a container of boolean flags.
+ *
+ * @author Graydon Hoare (graydon_at_redhat.com)
+ */
+public class DefaultButtonModel implements ButtonModel, Serializable
+{
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -5342609566534980231L;
+
+ /**
+ * Indicates that the button is <em>partially</em> committed to being
+ * pressed, but not entirely. This usually happens when a user has pressed
+ * but not yet released the mouse button.
+ */
+ public static final int ARMED = 1;
+
+ /**
+ * State constant indicating that the button is enabled. Buttons cannot be
+ * pressed or selected unless they are enabled.
+ */
+ public static final int ENABLED = 8;
+
+ /**
+ * State constant indicating that the user is holding down the button. When
+ * this transitions from true to false, an ActionEvent may be fired,
+ * depending on the value of the "armed" property.
+ */
+ public static final int PRESSED = 4;
+
+ /**
+ * State constant indicating that the mouse is currently positioned over the
+ * button.
+ */
+ public static final int ROLLOVER = 16;
+
+ /**
+ * State constant indicating that the button is selected. This constant is
+ * only meaningful for toggle-type buttons (radio buttons, checkboxes).
+ */
+ public static final int SELECTED = 2;
+
+ /**
+ * Represents the "state properties" (armed, enabled, pressed, rollover and
+ * selected) by a bitwise combination of integer constants.
+ */
+ protected int stateMask = ENABLED;
+
+ /**
+ * List of ItemListeners, ChangeListeners, and ActionListeners registered on
+ * this model.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /** The single ChangeEvent this model (re)uses to call its ChangeListeners. */
+ protected ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /**
+ * The group this model belongs to. Only one button in a group may be
+ * selected at any given time.
+ */
+ protected ButtonGroup group;
+
+ /**
+ * The key code (one of {@link java.awt.event.KeyEvent} VK_) used to press
+ * this button via a keyboard interface.
+ */
+ protected int mnemonic = KeyEvent.VK_UNDEFINED;
+
+ /**
+ * The string used as the "command" property of any ActionEvent this model
+ * sends.
+ */
+ protected String actionCommand;
+
+ /**
+ * Creates a new DefaultButtonModel object.
+ */
+ public DefaultButtonModel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Return <code>null</code>. Use {@link AbstractButton} if you wish to
+ * interface with a button via an {@link ItemSelectable} interface.
+ *
+ * @return <code>null</code>
+ */
+ public Object[] getSelectedObjects()
+ {
+ return null;
+ }
+
+ /**
+ * Returns a specified class of listeners.
+ *
+ * @param listenerType the type of listener to return
+ *
+ * @return array of listeners
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Add an ActionListener to the model. Usually only called to subscribe an
+ * AbstractButton's listener to the model.
+ *
+ * @param l The listener to add
+ */
+ public void addActionListener(ActionListener l)
+ {
+ listenerList.add(ActionListener.class, l);
+ }
+
+ /**
+ * Remove an ActionListener to the model. Usually only called to unsubscribe
+ * an AbstractButton's listener to the model.
+ *
+ * @param l The listener to remove
+ */
+ public void removeActionListener(ActionListener l)
+ {
+ listenerList.remove(ActionListener.class, l);
+ }
+
+ /**
+ * Returns all registered <code>ActionListener</code> objects.
+ *
+ * @return array of <code>ActionListener</code> objects
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) listenerList.getListeners(ActionListener.class);
+ }
+
+ /**
+ * Add an ItemListener to the model. Usually only called to subscribe an
+ * AbstractButton's listener to the model.
+ *
+ * @param l The listener to add
+ */
+ public void addItemListener(ItemListener l)
+ {
+ listenerList.add(ItemListener.class, l);
+ }
+
+ /**
+ * Remove an ItemListener to the model. Usually only called to unsubscribe
+ * an AbstractButton's listener to the model.
+ *
+ * @param l The listener to remove
+ */
+ public void removeItemListener(ItemListener l)
+ {
+ listenerList.remove(ItemListener.class, l);
+ }
+
+ /**
+ * Returns all registered <code>ItemListener</code> objects.
+ *
+ * @return array of <code>ItemListener</code> objects
+ */
+ public ItemListener[] getItemListeners()
+ {
+ return (ItemListener[]) listenerList.getListeners(ItemListener.class);
+ }
+
+ /**
+ * Add a ChangeListener to the model. Usually only called to subscribe an
+ * AbstractButton's listener to the model.
+ *
+ * @param l The listener to add
+ */
+ public void addChangeListener(ChangeListener l)
+ {
+ listenerList.add(ChangeListener.class, l);
+ }
+
+ /**
+ * Remove a ChangeListener to the model. Usually only called to unsubscribe
+ * an AbstractButton's listener to the model.
+ *
+ * @param l The listener to remove
+ */
+ public void removeChangeListener(ChangeListener l)
+ {
+ listenerList.remove(ChangeListener.class, l);
+ }
+
+ /**
+ * Returns all registered <code>ChangeListener</code> objects.
+ *
+ * @return array of <code>ChangeListener</code> objects
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Inform each ItemListener in the {@link #listenerList} that an ItemEvent
+ * has occurred. This happens in response to any change to the {@link
+ * #stateMask} field.
+ *
+ * @param e The ItemEvent to fire
+ */
+ protected void fireItemStateChanged(ItemEvent e)
+ {
+ ItemListener[] ll = getItemListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].itemStateChanged(e);
+ }
+
+ /**
+ * Inform each ActionListener in the {@link #listenerList} that an
+ * ActionEvent has occurred. This happens in response to the any change to
+ * the {@link #stateMask} field which makes the enabled, armed and pressed
+ * properties all simultaneously <code>true</code>.
+ *
+ * @param e The ActionEvent to fire
+ */
+ protected void fireActionPerformed(ActionEvent e)
+ {
+ ActionListener[] ll = getActionListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].actionPerformed(e);
+ }
+
+ /**
+ * Inform each ChangeListener in the {@link #listenerList} that a ChangeEvent
+ * has occurred. This happens in response to the any change to a property
+ * of the model.
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] ll = getChangeListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Get the value of the model's "armed" property.
+ *
+ * @return The current "armed" property
+ */
+ public boolean isArmed()
+ {
+ return (stateMask & ARMED) == ARMED;
+ }
+
+ /**
+ * Set the value of the model's "armed" property.
+ *
+ * @param a The new "armed" property
+ */
+ public void setArmed(boolean a)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if ((a && isArmed()) || (!a && !isArmed()))
+ return;
+
+ // cannot change ARMED state unless button is enabled
+ if (!isEnabled())
+ return;
+
+ // make the change
+ if (a)
+ stateMask = stateMask | ARMED;
+ else
+ stateMask = stateMask & (~ARMED);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+ }
+
+ /**
+ * Get the value of the model's "enabled" property.
+ *
+ * @return The current "enabled" property.
+ */
+ public boolean isEnabled()
+ {
+ return (stateMask & ENABLED) == ENABLED;
+ }
+
+ /**
+ * Set the value of the model's "enabled" property.
+ *
+ * @param e The new "enabled" property
+ */
+ public void setEnabled(boolean e)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if ((e && isEnabled()) || (!e && !isEnabled()))
+ return;
+
+ // make the change
+ if (e)
+ stateMask = stateMask | ENABLED;
+ else
+ stateMask = stateMask & (~ENABLED) & (~ARMED) & (~PRESSED);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+ }
+
+ /**
+ * Set the value of the model's "pressed" property.
+ *
+ * @param p The new "pressed" property
+ */
+ public void setPressed(boolean p)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if ((p && isPressed()) || (!p && !isPressed()))
+ return;
+
+ // cannot changed PRESSED state unless button is enabled
+ if (!isEnabled())
+ return;
+
+ // make the change
+ if (p)
+ stateMask = stateMask | PRESSED;
+ else
+ stateMask = stateMask & (~PRESSED);
+
+ // if button is armed and was released, fire action event
+ if (!p && isArmed())
+ fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ actionCommand));
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+ }
+
+ /**
+ * Get the value of the model's "pressed" property.
+ *
+ * @return The current "pressed" property
+ */
+ public boolean isPressed()
+ {
+ return (stateMask & PRESSED) == PRESSED;
+ }
+
+ /**
+ * Set the value of the model's "rollover" property.
+ *
+ * @param r The new "rollover" property
+ */
+ public void setRollover(boolean r)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if (r == isRollover())
+ return;
+
+ // cannot set ROLLOVER property unless button is enabled
+ if (!isEnabled())
+ return;
+
+ // make the change
+ if (r)
+ stateMask = stateMask | ROLLOVER;
+ else
+ stateMask = stateMask & (~ROLLOVER);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+ }
+
+ /**
+ * Set the value of the model's "selected" property.
+ *
+ * @param s The new "selected" property
+ */
+ public void setSelected(boolean s)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if ((s && isSelected()) || (!s && !isSelected()))
+ return;
+
+ // make the change
+ if (s)
+ stateMask = stateMask | SELECTED;
+ else
+ stateMask = stateMask & (~SELECTED);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+
+ // fire ItemStateChanged events
+ if (s)
+ {
+ fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
+ this, ItemEvent.SELECTED));
+ if (group != null)
+ group.setSelected(this, true);
+ }
+ else
+ {
+ fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
+ this, ItemEvent.DESELECTED));
+ if (group != null)
+ group.setSelected(this, false);
+ }
+ }
+
+ /**
+ * Get the value of the model's "selected" property.
+ *
+ * @return The current "selected" property
+ */
+ public boolean isSelected()
+ {
+ return (stateMask & SELECTED) == SELECTED;
+ }
+
+ /**
+ * Get the value of the model's "rollover" property.
+ *
+ * @return The current "rollover" property
+ */
+ public boolean isRollover()
+ {
+ return (stateMask & ROLLOVER) == ROLLOVER;
+ }
+
+ /**
+ * Get the value of the model's "mnemonic" property.
+ *
+ * @return The current "mnemonic" property
+ */
+ public int getMnemonic()
+ {
+ return mnemonic;
+ }
+
+ /**
+ * Set the value of the model's "mnemonic" property.
+ *
+ * @param key The new "mnemonic" property
+ */
+ public void setMnemonic(int key)
+ {
+ if (mnemonic != key)
+ {
+ mnemonic = key;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Set the value of the model's "actionCommand" property. This property is
+ * used as the "command" property of the {@link ActionEvent} fired from the
+ * model.
+ *
+ * @param s The new "actionCommand" property.
+ */
+ public void setActionCommand(String s)
+ {
+ if (actionCommand != s)
+ {
+ actionCommand = s;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the current value of the model's "actionCommand" property.
+ *
+ * @return The current "actionCommand" property
+ */
+ public String getActionCommand()
+ {
+ return actionCommand;
+ }
+
+ /**
+ * Set the value of the model's "group" property. The model is said to be a
+ * member of the {@link ButtonGroup} held in its "group" property, and only
+ * one model in a given group can have their "selected" property be
+ * <code>true</code> at a time.
+ *
+ * @param g The new "group" property (<code>null</code> permitted).
+ *
+ * @see #getGroup()
+ */
+ public void setGroup(ButtonGroup g)
+ {
+ group = g;
+ }
+
+ /**
+ * Returns the current value of the model's "group" property.
+ *
+ * @return The value of the "group" property
+ *
+ * @see #setGroup(ButtonGroup)
+ */
+ public ButtonGroup getGroup()
+ {
+ return group;
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultCellEditor.java b/libjava/classpath/javax/swing/DefaultCellEditor.java
new file mode 100644
index 000000000..46f637071
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultCellEditor.java
@@ -0,0 +1,570 @@
+/* DefaultCellEditor.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.MouseEvent;
+import java.io.Serializable;
+import java.util.EventObject;
+
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.event.CellEditorListener;
+import javax.swing.table.TableCellEditor;
+import javax.swing.tree.TreeCellEditor;
+
+/**
+ * The default implementation of {@link TableCellEditor} and
+ * {@link TreeCellEditor}. It provides editor components for
+ * some standard object types.
+ *
+ * @author Andrew Selkirk
+ * @author Audrius Meskauskas
+ */
+public class DefaultCellEditor
+ extends AbstractCellEditor
+ implements TableCellEditor, TreeCellEditor
+{
+ private static final long serialVersionUID = 3564035141373880027L;
+
+ /**
+ * This changeable module access the editor component in the component
+ * specific way. For instance, to set the value for JTextField, we need to
+ * call setText(String), and for JCheckBox we need to call
+ * setSelected(boolean). Each default editor has the component specific
+ * derivative of this class. These derivatives are private inner classes of
+ * the DefaultCellEditor.
+ *
+ * The editor delegate is also set for the editor component as the action
+ * listener. It listens for the events that indicate that editing has stopped.
+ */
+ protected class EditorDelegate
+ implements ActionListener, ItemListener, Serializable
+ {
+ /**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = -1420007406015481933L;
+
+ /**
+ * The object value (updated when getting and setting the value).
+ */
+ protected Object value;
+
+ /**
+ * Constructor EditorDelegate
+ */
+ protected EditorDelegate()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Set the value for the editor component. This method is normally
+ * overridden to set the value in the way, specific for the text
+ * component, check box or combo box.
+ *
+ * @param aValue the value to set (String, Boolean or Number).
+ */
+ public void setValue(Object aValue)
+ {
+ value = aValue;
+ }
+
+ /**
+ * Get the value for the editor component. This method is normally
+ * overridden to obtain the value in the way, specific for the text
+ * component, check box or combo box.
+ *
+ * @return value the value of the component (String, Boolean or Number).
+ */
+ public Object getCellEditorValue()
+ {
+ return value;
+ }
+
+ /**
+ * The default method returns true for the {@link MouseEvent} and false
+ * for any other events.
+ *
+ * @param event the event to check
+ *
+ * @return true if the passed event is the mouse event and false otherwise.
+ */
+ public boolean isCellEditable(EventObject event)
+ {
+ if (event == null || !(event instanceof MouseEvent) ||
+ (((MouseEvent) event).getClickCount() >= getClickCountToStart()))
+ return true;
+ return false;
+ } // isCellEditable()
+
+ /**
+ * Returns true to indicate that the editing cell can be selected.
+ *
+ * The default method returns true without action but may be overridden
+ * in derived classes for more specific behavior.
+ *
+ * @param event unused in default method
+ *
+ * @return true always
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ // return true to indicate that the editing cell may be selected
+ return true;
+ }
+
+ /**
+ * Finish the cell editing session. This method notifies the registered
+ * cell editor listeners (including the table) that the editing has been
+ * stopped.
+ *
+ * @return boolean
+ */
+ public boolean stopCellEditing()
+ {
+ fireEditingStopped();
+ return true;
+ } // stopCellEditing()
+
+ /**
+ * Cancel the cell editing session. This method notifies the registered
+ * cell editor listeners (including the table) that the editing has been
+ * canceled.
+ */
+ public void cancelCellEditing()
+ {
+ fireEditingCanceled();
+ } // cancelCellEditing()
+
+ /**
+ * Start editing session and returns true to indicate the editing has begun.
+ * The default method returns true without action but may be overridden
+ * in derived classes for more specific behavior.
+ *
+ * @param event the event.
+ *
+ * @return true, always
+ */
+ public boolean startCellEditing(EventObject event)
+ {
+ // return true to indicate that editing has begun
+ return true;
+ } // startCellEditing()
+
+ /**
+ * This event is fired by the editor component (for instance, by pressing
+ * ENTER in the {@link JTextField}. The default method delegates call to
+ * the {@link #stopCellEditing}, finishing the editing session.
+ *
+ * @param event unused in default method
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ stopCellEditing();
+ } // actionPerformed()
+
+ /**
+ * This event is fired by the editor component.The default method delegates
+ * call to the {@link #stopCellEditing}, finishing the editing session.
+ *
+ * @param event unused in default method
+ */
+ public void itemStateChanged(ItemEvent event)
+ {
+ stopCellEditing();
+ } // itemStateChanged()
+
+ /**
+ * Notify the registered listeners (including the table) that the editing
+ * has been completed.
+ */
+ void fireEditingStopped()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].editingStopped(changeEvent);
+
+ }
+
+ /**
+ * Notify the registered listeners (including the table) that the editing
+ * has been canceled.
+ */
+ void fireEditingCanceled()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].editingCanceled(changeEvent);
+ }
+ } // EditorDelegate
+
+ /**
+ * Provides getter and setter methods to work with the text component.
+ *
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+ private class JTextFieldDelegate extends EditorDelegate
+ {
+ /**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = 1;
+
+ /**
+ * Set the value for the editor component.
+ *
+ * @param aValue the value to set (toString() will be called).
+ */
+ public void setValue(Object aValue)
+ {
+ value = aValue;
+ JTextField f = (JTextField) editorComponent;
+ if (value == null)
+ f.setText("");
+ else
+ f.setText(value.toString());
+ }
+
+ /**
+ * Get the value for the editor component.
+ *
+ * @return value the value of the component (String)
+ */
+ public Object getCellEditorValue()
+ {
+ JTextField f = (JTextField) editorComponent;
+ return value = f.getText();
+ }
+ }
+
+ /**
+ * Provides getter and setter methods to work with the combo box.
+ *
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+ private class JComboBoxDelegate extends EditorDelegate
+ {
+ /**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = 1;
+
+ /**
+ * Set the value for the editor component.
+ *
+ * @param aValue the value to set.
+ */
+ public void setValue(Object aValue)
+ {
+ value = aValue;
+ JComboBox c = (JComboBox) editorComponent;
+ if (value != null)
+ c.setSelectedItem(value);
+ }
+
+ /**
+ * Get the value for the editor component.
+ *
+ * @return value the value of the component (as String)
+ */
+ public Object getCellEditorValue()
+ {
+ JComboBox c = (JComboBox) editorComponent;
+ return value = c.getSelectedItem();
+ }
+
+ /**
+ * Returns true to indicate that the editing cell can be selected. If the
+ * check box is not editable, expands it. If it is editable, brings
+ * focus to the editor field.
+ *
+ * @param event unused in default method
+ *
+ * @return true always
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ JComboBox c = (JComboBox) editorComponent;
+ if (!c.isEditable)
+ c.showPopup();
+ return true;
+ }
+ }
+
+ /**
+ * Provides getter and setter methods to work with the check box.
+ *
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+ private class JCheckBoxDelegate extends EditorDelegate
+ {
+ /**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = 1;
+
+ /**
+ * Set the value for the editor component.
+ *
+ * @param value the value to set (must be Boolean).
+ */
+ public void setValue(Object value)
+ {
+ JCheckBox c = (JCheckBox) editorComponent;
+
+ if (value == null)
+ c.setSelected(false);
+ else
+ c.setSelected( ((Boolean) value).booleanValue());
+ }
+
+ /**
+ * Get the value for the editor component.
+ *
+ * @return value the value of the component (must be CharSequence)
+ */
+ public Object getCellEditorValue()
+ {
+ JCheckBox c = (JCheckBox) editorComponent;
+ value = c.isSelected() ? Boolean.TRUE : Boolean.FALSE;
+ return value;
+ }
+ }
+
+ /**
+ * The Swing JComponent, performing the editing session.
+ */
+ protected JComponent editorComponent;
+
+ /**
+ * The editor delegate, responsible for listening the {@link #editorComponent}
+ * events and getting/setting its value.
+ */
+ protected EditorDelegate delegate;
+
+ /**
+ * The number of the mouse clicks, required to start the editing session.
+ */
+ protected int clickCountToStart;
+
+ /**
+ * Create the DefaultCellEditor that uses the text field as its editor
+ * component (appropriate for the text content)
+ *
+ * @param textfield the text field as will be used as the editor component
+ */
+ public DefaultCellEditor(JTextField textfield)
+ {
+ editorComponent = textfield;
+ clickCountToStart = 2;
+ delegate = new JTextFieldDelegate();
+ textfield.addActionListener(delegate);
+ } // DefaultCellEditor()
+
+ /**
+ * Constructor DefaultCellEditor that uses the checkbox (appropriate
+ * for boolean values)
+ *
+ * @param checkbox the checkbox that will be used with this editor.
+ */
+ public DefaultCellEditor(JCheckBox checkbox)
+ {
+ editorComponent = checkbox;
+ clickCountToStart = 1;
+ delegate = new JCheckBoxDelegate();
+ checkbox.addActionListener(delegate);
+ } // DefaultCellEditor()
+
+ /**
+ * Constructor DefaultCellEditor that uses the combo box.
+ *
+ * @param combobox the combo box that will be used with this editor.
+ */
+ public DefaultCellEditor(JComboBox combobox)
+ {
+ editorComponent = combobox;
+ clickCountToStart = 1;
+ delegate = new JComboBoxDelegate();
+ combobox.addActionListener(delegate);
+ } // DefaultCellEditor()
+
+ /**
+ * Get the component that performs the editing sessions. It is the same
+ * component that was passed in constructor.
+ *
+ * @return the component, performing the editing sessions.
+ */
+ public Component getComponent()
+ {
+ return editorComponent;
+ } // getComponent()
+
+ /**
+ * Get the number of mouse clicks, required to start the editing session.
+ *
+ * @return int the number of mouse clicks, required to start the session
+ */
+ public int getClickCountToStart()
+ {
+ return clickCountToStart;
+ } // getClickCountToStart()
+
+ /**
+ * Set the number of mouse clicks, required to start the editing session.
+ *
+ * @param count the number of clicks, required to start the session
+ */
+ public void setClickCountToStart(int count)
+ {
+ clickCountToStart = count;
+ } // setClickCountToStart()
+
+ /**
+ * Get the value, currently being displayed by the editor component. The
+ * call is forwarded to the {@link #delegate}.
+ *
+ * @return Object the value (class depends on the editor component)
+ */
+ public Object getCellEditorValue()
+ {
+ return delegate.getCellEditorValue();
+ } // getCellEditorValue()
+
+ /**
+ * Forwards call to the {@link #delegate}.
+ *
+ * @param event forwarded to the delegate.
+ *
+ * @return boolean returned by delegate
+ */
+ public boolean isCellEditable(EventObject event)
+ {
+ return delegate.isCellEditable(event);
+ } // isCellEditable()
+
+ /**
+ * Forwards call to the {@link #delegate}.
+ *
+ * @param event forwarded to the delegate.
+ *
+ * @return boolean returned by delegate
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ return delegate.shouldSelectCell(event);
+ } // shouldSelectCell()
+
+ /**
+ * Forwards call to the {@link #delegate}.
+ *
+ * @return boolean returned by delegate
+ */
+ public boolean stopCellEditing()
+ {
+ return delegate.stopCellEditing();
+ } // stopCellEditing()
+
+ /**
+ * Forwards call to the {@link #delegate}.
+ */
+ public void cancelCellEditing()
+ {
+ delegate.cancelCellEditing();
+ } // cancelCellEditing()
+
+ /**
+ * Sets an initial value for the editor.
+ * This will cause the editor to stopEditing and lose any partially
+ * edited value if the editor is editing when this method is called.
+ * Returns the component that should be added to the client's Component
+ * hierarchy. Once installed in the client's hierarchy this component will
+ * then be able to draw and receive user input.
+ *
+ * @param tree - the JTree that is asking the editor to edit; this
+ * parameter can be null
+ * @param value - the value of the cell to be edited
+ * @param isSelected - true is the cell is to be renderer with selection
+ * highlighting
+ * @param expanded - true if the node is expanded
+ * @param leaf - true if the node is a leaf node
+ * @param row - the row index of the node being edited
+ *
+ * @return Component the component for editing
+ */
+ public Component getTreeCellEditorComponent(JTree tree, Object value,
+ boolean isSelected,
+ boolean expanded, boolean leaf,
+ int row)
+ {
+ delegate.setValue(value);
+ return editorComponent;
+ } // getTreeCellEditorComponent()
+
+ /**
+ * Get the cell editor component that will perform the editing session. If
+ * returned once, the same component is also returned on the repetetive calls
+ * again (reused).
+ *
+ * @param table the table where the editing is performed
+ * @param value the current value of the table. It is set as the initial
+ * component value.
+ * @param isSelected if true, the cell is currently selected
+ * @param row the row of the cell being edited
+ * @param column the column of the cell being edited
+ *
+ * @return Component the component that will perform the editing session
+ */
+ public Component getTableCellEditorComponent(JTable table, Object value,
+ boolean isSelected, int row,
+ int column)
+ {
+ // NOTE: as specified by Sun, we don't call new() everytime, we return
+ // editorComponent on each call to getTableCellEditorComponent or
+ // getTreeCellEditorComponent.
+ delegate.setValue(value);
+ return editorComponent;
+ } // getTableCellEditorComponent()
+
+}
diff --git a/libjava/classpath/javax/swing/DefaultComboBoxModel.java b/libjava/classpath/javax/swing/DefaultComboBoxModel.java
new file mode 100644
index 000000000..a90b89d93
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultComboBoxModel.java
@@ -0,0 +1,286 @@
+/* DefaultComboBoxModel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.Arrays;
+import java.util.Vector;
+
+import javax.swing.event.ListDataEvent;
+
+
+/**
+ * A model that stores a list of elements and a selected item (which may be
+ * <code>null</code>). Changes to the model are signalled to listeners using
+ * {@link ListDataEvent}. This model is designed for use by the
+ * {@link JComboBox} component.
+ *
+ * @author Andrew Selkirk
+ * @author Olga Rodimina
+ * @author Robert Schuster
+ */
+public class DefaultComboBoxModel extends AbstractListModel
+ implements MutableComboBoxModel, Serializable
+{
+ private static final long serialVersionUID = 6698657703676921904L;
+
+ /**
+ * Storage for the elements in the model's list.
+ */
+ private Vector list;
+
+ /**
+ * The selected item (<code>null</code> indicates no selection).
+ */
+ private Object selectedItem = null;
+
+ /**
+ * Creates a new model, initially empty.
+ */
+ public DefaultComboBoxModel()
+ {
+ list = new Vector();
+ }
+
+ /**
+ * Creates a new model and initializes its item list to the values in the
+ * given array. The selected item is set to the first item in the array, or
+ * <code>null</code> if the array length is zero.
+ *
+ * @param items an array containing items for the model (<code>null</code>
+ * not permitted).
+ *
+ * @throws NullPointerException if <code>items</code> is <code>null</code>.
+ */
+ public DefaultComboBoxModel(Object[] items)
+ {
+ list = new Vector(Arrays.asList(items));
+ if (list.size() > 0)
+ selectedItem = list.get(0);
+ }
+
+ /**
+ * Creates a new model and initializes its item list to the values in the
+ * given vector. The selected item is set to the first item in the vector,
+ * or <code>null</code> if the vector length is zero.
+ *
+ * @param vector a vector containing items for the model (<code>null</code>
+ * not permitted).
+ *
+ * @throws NullPointerException if <code>vector</code> is <code>null</code>.
+ */
+ public DefaultComboBoxModel(Vector<?> vector)
+ {
+ this.list = vector;
+ if (getSize() > 0)
+ selectedItem = vector.get(0);
+ }
+
+ /**
+ * Adds an element to the model's item list and sends a {@link ListDataEvent}
+ * to all registered listeners. If the new element is the first item added
+ * to the list, and the selected item is <code>null</code>, the new element
+ * is set as the selected item.
+ *
+ * @param object item to add to the model's item list.
+ */
+ public void addElement(Object object)
+ {
+ list.addElement(object);
+ int index = list.size() - 1;
+ fireIntervalAdded(this, index, index);
+ if (list.size() == 1 && selectedItem == null)
+ setSelectedItem(object);
+ }
+
+ /**
+ * Removes the element at the specified index from the model's item list
+ * and sends a {@link ListDataEvent} to all registered listeners. If the
+ * element removed was the selected item, then the preceding element becomes
+ * the new selected item (or the next element, if there is no preceding
+ * element).
+ *
+ * @param index the index of the item to remove.
+ *
+ * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of
+ * bounds.
+ */
+ public void removeElementAt(int index)
+ {
+ int selected = getIndexOf(selectedItem);
+ if (selected == index) // choose a new selected item
+ {
+ if (selected > 0)
+ setSelectedItem(getElementAt(selected - 1));
+ else
+ setSelectedItem(getElementAt(selected + 1));
+ }
+ list.removeElementAt(index);
+ fireIntervalRemoved(this, index, index);
+ }
+
+ /**
+ * Adds an element at the specified index in the model's item list
+ * and sends a {@link ListDataEvent} to all registered listeners.
+ *
+ * @param object element to insert
+ * @param index index specifing position in the list where given element
+ * should be inserted.
+ *
+ * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of
+ * bounds.
+ *
+ * @see #addElement(Object)
+ */
+ public void insertElementAt(Object object, int index)
+ {
+ list.insertElementAt(object, index);
+ fireIntervalAdded(this, index, index);
+ }
+
+ /**
+ * Removes an element from the model's item list and sends a
+ * {@link ListDataEvent} to all registered listeners. If the item to be
+ * removed is the current selected item, a new selected item will be set.
+ * If the element is not found in the model's item list, this method does
+ * nothing.
+ *
+ * @param object the element to remove.
+ */
+ public void removeElement(Object object)
+ {
+ int index = getIndexOf(object);
+ if (index != -1)
+ removeElementAt(index);
+ }
+
+ /**
+ * Removes all the items from the model's item list, resets and selected item
+ * to <code>null</code>, and sends a {@link ListDataEvent} to all registered
+ * listeners.
+ */
+ public void removeAllElements()
+ {
+ selectedItem = null;
+ int size = getSize();
+ if (size > 0)
+ {
+ list.clear();
+ fireIntervalRemoved(this, 0, size - 1);
+ }
+ }
+
+ /**
+ * Returns the number of items in the model's item list.
+ *
+ * @return The number of items in the model's item list.
+ */
+ public int getSize()
+ {
+ return list.size();
+ }
+
+ /**
+ * Sets the selected item for the model and sends a {@link ListDataEvent} to
+ * all registered listeners. The start and end index of the event is set to
+ * -1 to indicate the model's selection has changed, and not its contents.
+ *
+ * @param object the new selected item (<code>null</code> permitted).
+ */
+ public void setSelectedItem(Object object)
+ {
+ // No item is selected and object is null, so no change required.
+ if (selectedItem == null && object == null)
+ return;
+
+ // object is already selected so no change required.
+ if (selectedItem != null && selectedItem.equals(object))
+ return;
+
+ // Simply return if object is not in the list.
+ if (object != null && getIndexOf(object) == -1)
+ return;
+
+ // Here we know that object is either an item in the list or null.
+
+ // Handle the three change cases: selectedItem is null, object is
+ // non-null; selectedItem is non-null, object is null;
+ // selectedItem is non-null, object is non-null and they're not
+ // equal.
+ selectedItem = object;
+ fireContentsChanged(this, -1, -1);
+ }
+
+ /**
+ * Returns the selected item.
+ *
+ * @return The selected item (possibly <code>null</code>).
+ */
+ public Object getSelectedItem()
+ {
+ return selectedItem;
+ }
+
+ /**
+ * Returns the element at the specified index in the model's item list.
+ *
+ * @param index the element index.
+ *
+ * @return The element at the specified index in the model's item list, or
+ * <code>null</code> if the <code>index</code> is outside the bounds
+ * of the list.
+ */
+ public Object getElementAt(int index)
+ {
+ if (index < 0 || index >= list.size())
+ return null;
+ return list.elementAt(index);
+ }
+
+ /**
+ * Returns the index of the specified element in the model's item list.
+ *
+ * @param object the element.
+ *
+ * @return The index of the specified element in the model's item list.
+ */
+ public int getIndexOf(Object object)
+ {
+ return list.indexOf(object);
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultDesktopManager.java b/libjava/classpath/javax/swing/DefaultDesktopManager.java
new file mode 100644
index 000000000..9613449ff
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultDesktopManager.java
@@ -0,0 +1,644 @@
+/* DefaultDesktopManager.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.beans.PropertyVetoException;
+import java.io.Serializable;
+
+import javax.swing.JInternalFrame.JDesktopIcon;
+
+/**
+ * The default implementation of DesktopManager for
+ * Swing. It implements the basic beaviours for JInternalFrames in arbitrary
+ * parents. The methods provided by the class are not meant to be called by
+ * the user, instead, the JInternalFrame methods will call these methods.
+ */
+public class DefaultDesktopManager implements DesktopManager, Serializable
+{
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = 4657624909838017887L;
+
+ /** The property change event fired when the wasIcon property changes. */
+ static final String WAS_ICON_ONCE_PROPERTY = "wasIconOnce";
+
+ /**
+ * The method of dragging used by the JDesktopPane that parents the
+ * JInternalFrame that is being dragged.
+ */
+ private int currentDragMode = 0;
+
+ /**
+ * The cache of the bounds used to draw the outline rectangle when
+ * OUTLINE_DRAG_MODE is used.
+ */
+ private transient Rectangle dragCache = new Rectangle();
+
+ /**
+ * A cached JDesktopPane that is stored when the JInternalFrame is initially
+ * dragged.
+ */
+ private transient Container pane;
+
+ /**
+ * An array of Rectangles that holds the bounds of the JDesktopIcons in the
+ * JDesktopPane when looking for where to place a new icon.
+ */
+ private transient Rectangle[] iconRects;
+
+ /**
+ * This creates a new DefaultDesktopManager object.
+ */
+ public DefaultDesktopManager()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is not normally called since the user will typically add the
+ * JInternalFrame to a Container. If this is called, it will try to
+ * determine the parent of the JInternalFrame and remove any icon that
+ * represents this JInternalFrame and add this JInternalFrame.
+ *
+ * @param frame The JInternalFrame to open.
+ */
+ public void openFrame(JInternalFrame frame)
+ {
+ Container c = frame.getParent();
+ if (c == null)
+ c = frame.getDesktopIcon().getParent();
+ if (c == null)
+ return;
+
+ c.remove(frame.getDesktopIcon());
+ c.add(frame);
+ frame.setVisible(true);
+ }
+
+ /**
+ * This method removes the JInternalFrame and JDesktopIcon (if one is
+ * present) from their parents.
+ *
+ * @param frame The JInternalFrame to close.
+ */
+ public void closeFrame(JInternalFrame frame)
+ {
+ Container c = frame.getParent();
+ if (c != null)
+ {
+ if (frame.isIcon())
+ c.remove(frame.getDesktopIcon());
+ else
+ c.remove(frame);
+ c.repaint();
+ }
+ }
+
+ /**
+ * This method resizes the JInternalFrame to match its parent's bounds.
+ *
+ * @param frame The JInternalFrame to maximize.
+ */
+ public void maximizeFrame(JInternalFrame frame)
+ {
+ // Can't maximize from iconified state.
+ // It can only return to maximized state, but that would fall under
+ // deiconify.
+ if (frame.isIcon())
+ return;
+ frame.setNormalBounds(frame.getBounds());
+
+ Container p = frame.getParent();
+ if (p != null)
+ {
+ Rectangle pBounds = p.getBounds();
+ Insets insets = p.getInsets();
+ pBounds.width -= insets.left + insets.right;
+ pBounds.height -= insets.top + insets.bottom;
+
+ setBoundsForFrame(frame, 0, 0, pBounds.width, pBounds.height);
+ }
+ if (p instanceof JDesktopPane)
+ ((JDesktopPane) p).setSelectedFrame(frame);
+ else
+ {
+ try
+ {
+ frame.setSelected(true);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing.
+ }
+ }
+ }
+
+ /**
+ * This method restores the JInternalFrame's bounds to what they were
+ * previous to the setMaximize call.
+ *
+ * @param frame The JInternalFrame to minimize.
+ */
+ public void minimizeFrame(JInternalFrame frame)
+ {
+ Rectangle normalBounds = frame.getNormalBounds();
+
+ JDesktopPane p = frame.getDesktopPane();
+ if (p != null)
+ p.setSelectedFrame(frame);
+ else
+ {
+ try
+ {
+ frame.setSelected(true);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing.
+ }
+ }
+
+ setBoundsForFrame(frame, normalBounds.x, normalBounds.y,
+ normalBounds.width, normalBounds.height);
+ }
+
+ /**
+ * This method removes the JInternalFrame from its parent and adds its
+ * JDesktopIcon representation.
+ *
+ * @param frame The JInternalFrame to iconify.
+ */
+ public void iconifyFrame(JInternalFrame frame)
+ {
+ JDesktopPane p = frame.getDesktopPane();
+ JDesktopIcon icon = frame.getDesktopIcon();
+ if (p != null && p.getSelectedFrame() == frame)
+ p.setSelectedFrame(null);
+ else
+ {
+ try
+ {
+ frame.setSelected(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing if attempt is vetoed.
+ }
+ }
+
+ Container c = frame.getParent();
+
+ if (!wasIcon(frame))
+ {
+ Rectangle r = getBoundsForIconOf(frame);
+ icon.setBounds(r);
+ setWasIcon(frame, Boolean.TRUE);
+ }
+
+ if (c != null)
+ {
+ if (icon != null)
+ {
+ c.add(icon);
+ icon.setVisible(true);
+ }
+ Rectangle b = frame.getBounds();
+ c.remove(frame);
+ c.repaint(b.x, b.y, b.width, b.height);
+ }
+ }
+
+ /**
+ * This method removes the JInternalFrame's JDesktopIcon representation and
+ * adds the JInternalFrame back to its parent.
+ *
+ * @param frame The JInternalFrame to deiconify.
+ */
+ public void deiconifyFrame(JInternalFrame frame)
+ {
+ JDesktopIcon icon = frame.getDesktopIcon();
+ Container c = icon.getParent();
+
+ removeIconFor(frame);
+ c.add(frame);
+ frame.setVisible(true);
+
+ if (!frame.isSelected())
+ {
+ JDesktopPane p = frame.getDesktopPane();
+ if (p != null)
+ p.setSelectedFrame(frame);
+ else
+ {
+ try
+ {
+ frame.setSelected(true);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing.
+ }
+ }
+ }
+
+ c.invalidate();
+ }
+
+ /**
+ * This method activates the JInternalFrame by moving it to the front and
+ * selecting it.
+ *
+ * @param frame The JInternalFrame to activate.
+ */
+ public void activateFrame(JInternalFrame frame)
+ {
+ JDesktopPane p = frame.getDesktopPane();
+ JInternalFrame active = null;
+ if (p != null)
+ active = p.getSelectedFrame();
+ if (active == null)
+ {
+ if (p != null)
+ {
+ p.setSelectedFrame(frame);
+ }
+ }
+ else if (active != frame)
+ {
+ if (active.isSelected())
+ {
+ try
+ {
+ active.setSelected(false);
+ }
+ catch (PropertyVetoException ex)
+ {
+ // Not allowed.
+ }
+ }
+ if (p != null)
+ {
+ p.setSelectedFrame(frame);
+ }
+
+ }
+ frame.toFront();
+ }
+
+ /**
+ * This method is called when the JInternalFrame loses focus.
+ *
+ * @param frame The JInternalFram to deactivate.
+ */
+ public void deactivateFrame(JInternalFrame frame)
+ {
+ JDesktopPane p = frame.getDesktopPane();
+ if (p != null)
+ {
+ if (p.getSelectedFrame() == frame)
+ p.setSelectedFrame(null);
+ }
+ else
+ {
+ try
+ {
+ frame.setSelected(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing if attempt is vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method is called to indicate that the DesktopManager should prepare
+ * to drag the JInternalFrame. Any state information needed to drag the
+ * frame will be prepared now.
+ *
+ * @param component The JComponent to drag, usually a JInternalFrame.
+ */
+ public void beginDraggingFrame(JComponent component)
+ {
+ if (component instanceof JDesktopIcon)
+ pane = ((JDesktopIcon) component).getInternalFrame().getDesktopPane();
+ else
+ pane = ((JInternalFrame) component).getDesktopPane();
+ if (pane == null)
+ return;
+
+ dragCache = component.getBounds();
+
+ if (! (pane instanceof JDesktopPane))
+ currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
+ else
+ currentDragMode = ((JDesktopPane) pane).getDragMode();
+ }
+
+ /**
+ * This method is called to drag the JInternalFrame to a new location.
+ *
+ * @param component The JComponent to drag, usually a JInternalFrame.
+ *
+ * @param newX The new x coordinate.
+ * @param newY The new y coordinate.
+ */
+ public void dragFrame(JComponent component, int newX, int newY)
+ {
+ if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
+ {
+ // FIXME: Do outline drag mode painting.
+ }
+ else
+ {
+ Rectangle b = component.getBounds();
+ if (component instanceof JDesktopIcon)
+ component.setBounds(newX, newY, b.width, b.height);
+ else
+ setBoundsForFrame((JInternalFrame) component, newX, newY, b.width,
+ b.height);
+ }
+ }
+
+ /**
+ * This method indicates that the dragging is done. Any state information
+ * stored by the DesktopManager can be cleared.
+ *
+ * @param component The JComponent that has finished dragging.
+ */
+ public void endDraggingFrame(JComponent component)
+ {
+ if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
+ {
+ setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
+ dragCache.width, dragCache.height);
+ pane = null;
+ dragCache = null;
+ component.repaint();
+ }
+ }
+
+ /**
+ * This method is called to indicate that the given JComponent will be
+ * resized. Any state information necessary to resize the JComponent will
+ * be prepared now.
+ *
+ * @param component The JComponent to resize, usually a JInternalFrame.
+ * @param direction The direction to drag in (a SwingConstant).
+ */
+ public void beginResizingFrame(JComponent component, int direction)
+ {
+ pane = ((JInternalFrame) component).getDesktopPane();
+ if (pane == null)
+ return;
+
+ dragCache = component.getBounds();
+ if (! (pane instanceof JDesktopPane))
+ currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
+ else
+ currentDragMode = ((JDesktopPane) pane).getDragMode();
+ }
+
+ /**
+ * This method resizes the give JComponent.
+ *
+ * @param component The JComponent to resize.
+ * @param newX The new x coordinate.
+ * @param newY The new y coordinate.
+ * @param newWidth The new width.
+ * @param newHeight The new height.
+ */
+ public void resizeFrame(JComponent component, int newX, int newY,
+ int newWidth, int newHeight)
+ {
+ dragCache.setBounds(newX, newY, newWidth, newHeight);
+
+ if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
+ {
+ // FIXME: Do outline drag painting.
+ }
+ else
+ setBoundsForFrame(component, dragCache.x, dragCache.y, dragCache.width,
+ dragCache.height);
+ }
+
+ /**
+ * This method is called to indicate that the given JComponent has finished
+ * dragging. Any state information stored by the DesktopManager can be
+ * cleared.
+ *
+ * @param component The JComponent that finished resizing.
+ */
+ public void endResizingFrame(JComponent component)
+ {
+ if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
+ {
+ setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
+ dragCache.width, dragCache.height);
+ pane = null;
+ dragCache = null;
+ component.repaint();
+ }
+ }
+
+ /**
+ * This method calls setBounds with the given parameters and repaints the
+ * JComponent.
+ *
+ * @param component The JComponent to set bounds for.
+ * @param newX The new x coordinate.
+ * @param newY The new y coordinate.
+ * @param newWidth The new width.
+ * @param newHeight The new height.
+ */
+ public void setBoundsForFrame(JComponent component, int newX, int newY,
+ int newWidth, int newHeight)
+ {
+ component.setBounds(newX, newY, newWidth, newHeight);
+ }
+
+ /**
+ * This is a helper method that removes the JDesktopIcon of the given
+ * JInternalFrame from the parent.
+ *
+ * @param frame The JInternalFrame to remove an icon for.
+ */
+ protected void removeIconFor(JInternalFrame frame)
+ {
+ JDesktopIcon icon = frame.getDesktopIcon();
+ Container c = icon.getParent();
+ if (c != null && icon != null)
+ {
+ Rectangle b = icon.getBounds();
+ c.remove(icon);
+ c.repaint(b.x, b.y, b.width, b.height);
+ }
+ }
+
+ /**
+ * This method is called by iconifyFrame to determine the bounds of the
+ * JDesktopIcon for the given JInternalFrame.
+ *
+ * @param frame The JInternalFrame to find the bounds of its JDesktopIcon
+ * for.
+ *
+ * @return The bounds of the JDesktopIcon.
+ */
+ protected Rectangle getBoundsForIconOf(JInternalFrame frame)
+ {
+ // IconRects has no order to it.
+ // The icon _must_ be placed in the first free slot (working from
+ // the bottom left corner)
+ // The icon also must not be placed where another icon is placed
+ // (regardless whether that frame is an icon currently or not)
+ JDesktopPane desktopPane = frame.getDesktopPane();
+
+ if (desktopPane == null)
+ return frame.getDesktopIcon().getBounds();
+
+ Rectangle paneBounds = desktopPane.getBounds();
+ Insets insets = desktopPane.getInsets();
+ Dimension pref = frame.getDesktopIcon().getPreferredSize();
+
+ Component[] frames = desktopPane.getComponents();
+
+ int count = 0;
+ for (int i = 0, j = 0; i < frames.length; i++)
+ if (frames[i] instanceof JDesktopIcon
+ || frames[i] instanceof JInternalFrame
+ && ((JInternalFrame) frames[i]).getWasIcon() && frames[i] != frame)
+ count++;
+ iconRects = new Rectangle[count];
+ for (int i = 0, j = 0; i < frames.length; i++)
+ if (frames[i] instanceof JDesktopIcon)
+ iconRects[--count] = frames[i].getBounds();
+ else if (frames[i] instanceof JInternalFrame
+ && ((JInternalFrame) frames[i]).getWasIcon()
+ && frames[i] != frame)
+ iconRects[--count] = ((JInternalFrame) frames[i])
+ .getDesktopIcon().getBounds();
+
+ int startingX = insets.left;
+ int startingY = paneBounds.height - insets.bottom - pref.height;
+ Rectangle ideal = new Rectangle(startingX, startingY, pref.width,
+ pref.height);
+ boolean clear = true;
+
+ while (iconRects.length > 0)
+ {
+ clear = true;
+ for (int i = 0; i < iconRects.length; i++)
+ {
+ if (iconRects[i] != null && iconRects[i].intersects(ideal))
+ {
+ clear = false;
+ break;
+ }
+ }
+ if (clear)
+ return ideal;
+
+ startingX += pref.width;
+ if (startingX + pref.width > paneBounds.width - insets.right)
+ {
+ startingX = insets.left;
+ startingY -= pref.height;
+ }
+ ideal.setBounds(startingX, startingY, pref.width, pref.height);
+ }
+
+ return ideal;
+ }
+
+ /**
+ * This method sets the bounds of the JInternalFrame right before the
+ * maximizeFrame call.
+ *
+ * @param frame The JInternalFrame being maximized.
+ * @param rect The normal bounds.
+ */
+ protected void setPreviousBounds(JInternalFrame frame, Rectangle rect)
+ {
+ frame.setNormalBounds(rect);
+ }
+
+ /**
+ * This method returns the normal bounds of the JInternalFrame from before
+ * the maximize call.
+ *
+ * @param frame The JInternalFrame that is being restored.
+ *
+ * @return The previous bounds of the JInternalFrame.
+ */
+ protected Rectangle getPreviousBounds(JInternalFrame frame)
+ {
+ return frame.getNormalBounds();
+ }
+
+ /**
+ * This method sets the value to true if the given JInternalFrame has been
+ * iconized and the bounds of its DesktopIcon are valid.
+ *
+ * @param frame The JInternalFrame for the JDesktopIcon.
+ * @param value True if the JInternalFrame has been iconized and the bounds
+ * of the JDesktopIcon are valid.
+ */
+ protected void setWasIcon(JInternalFrame frame, Boolean value)
+ {
+ frame.setWasIcon(value.booleanValue(), WAS_ICON_ONCE_PROPERTY);
+ }
+
+ /**
+ * This method returns true if the given JInternalFrame has been iconized
+ * and the bounds of its DesktopIcon are valid.
+ *
+ * @param frame The JInternalFrame for the JDesktopIcon.
+ *
+ * @return True if the given JInternalFrame has been iconized and the bounds
+ * of its DesktopIcon are valid.
+ */
+ protected boolean wasIcon(JInternalFrame frame)
+ {
+ return frame.getWasIcon();
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultFocusManager.java b/libjava/classpath/javax/swing/DefaultFocusManager.java
new file mode 100644
index 000000000..3c5daa0e6
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultFocusManager.java
@@ -0,0 +1,169 @@
+/* DefaultFocusManager.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.event.KeyEvent;
+import java.util.Stack;
+
+/**
+ * This class has been obsoleted by the new
+ * {@link java.awt.KeyboardFocusManager} and
+ * {@link java.awt.DefaultKeyboardFocusManager} API.
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultFocusManager extends FocusManager
+{
+
+ /**
+ * historyStack
+ */
+ private Stack historyStack;
+
+ /**
+ * Constructor DefaultFocusManager
+ */
+ public DefaultFocusManager()
+ {
+ // TODO
+ } // DefaultFocusManager()
+
+ /**
+ * processKeyEvent
+ *
+ * @param component
+ * TODO
+ * @param event
+ * TODO
+ */
+ public void processKeyEvent(Component component, KeyEvent event)
+ {
+ // TODO
+ } // processKeyEvent()
+
+ /**
+ * focusNextComponent
+ *
+ * @param component
+ * TODO
+ */
+ public void focusNextComponent(Component component)
+ {
+ // TODO
+ } // focusNextComponent()
+
+ /**
+ * focusPreviousComponent
+ *
+ * @param component
+ * TODO
+ */
+ public void focusPreviousComponent(Component component)
+ {
+ // TODO
+ } // focusPreviousComponent()
+
+ /**
+ * getFirstComponent
+ *
+ * @param container
+ * TODO
+ * @return Component
+ */
+ public Component getFirstComponent(Container container)
+ {
+ return null; // TODO
+ } // getFirstComponent()
+
+ /**
+ * getLastComponent
+ *
+ * @param container
+ * TODO
+ * @return Component
+ */
+ public Component getLastComponent(Container container)
+ {
+ return null; // TODO
+ } // getLastComponent()
+
+ /**
+ * getComponentBefore
+ *
+ * @param container
+ * TODO
+ * @param component
+ * TODO
+ * @return Component
+ */
+ public Component getComponentBefore(Container container, Component component)
+ {
+ return null; // TODO
+ } // getComponentBefore()
+
+ /**
+ * getComponentAfter
+ *
+ * @param container
+ * TODO
+ * @param component
+ * TODO
+ * @return Component
+ */
+ public Component getComponentAfter(Container container, Component component)
+ {
+ return null; // TODO
+ } // getComponentAfter()
+
+ /**
+ * compareTabOrder
+ *
+ * @param component1
+ * TODO
+ * @param component2
+ * TODO
+ * @return boolean
+ */
+ public boolean compareTabOrder(Component component1, Component component2)
+ {
+ return false; // TODO
+ } // compareTabOrder()
+
+} // DefaultFocusManager
diff --git a/libjava/classpath/javax/swing/DefaultListCellRenderer.java b/libjava/classpath/javax/swing/DefaultListCellRenderer.java
new file mode 100644
index 000000000..598627fac
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultListCellRenderer.java
@@ -0,0 +1,199 @@
+/* DefaultListCellRenderer.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.swing.border.Border;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * The default implementation {@link ListCellRenderer}. It provides a standard
+ * renderer for data objects of all types via {@link Object#toString()}.
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultListCellRenderer extends JLabel
+ implements ListCellRenderer, Serializable
+{
+ private static final long serialVersionUID = 7708947179685189462L;
+
+ /**
+ * Subclasses <code>DefaultListCellRenderers</code> and implements
+ * {@link javax.swing.plaf.UIResource}. This is used by
+ * {@link javax.swing.plaf.ListUI} subclasses to provide a default for
+ * the <code>List.cellRenderer</code> property. If you want to override
+ * this property, use <code>DefaultListCellRenderer</code> or a subclass.
+ */
+ public static class UIResource extends DefaultListCellRenderer
+ implements javax.swing.plaf.UIResource
+ {
+ public UIResource()
+ {
+ super();
+ }
+ }
+
+ /**
+ * This border is used whenever renderer doesn't have a focus.
+ */
+ protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+
+ /**
+ * getListCellRendererComponent
+ *
+ * @param list JList list for the 'value'
+ * @param value object that should be rendered in the cell
+ * @param index index of the cell
+ * @param isSelected draw cell highlighted if isSelected is true
+ * @param cellHasFocus draw focus rectangle around cell if the cell has
+ * focus
+ *
+ * @return Component that will be painted to the desired cell.
+ */
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected,
+ boolean cellHasFocus)
+ {
+ String s = value != null ? value.toString() : "";
+ setText(s);
+ setOpaque(true);
+ setHorizontalAlignment(LEFT);
+
+ if (isSelected)
+ {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ }
+ else
+ {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+
+ setEnabled(list.isEnabled());
+ setFont(list.getFont());
+
+ // Use focusCellHighlightBorder when renderer has focus and
+ // noFocusBorder otherwise
+
+ if (cellHasFocus)
+ setBorder(UIManager.getBorder("List.focusCellHighlightBorder"));
+ else
+ setBorder(noFocusBorder);
+
+ return this;
+ }
+
+ public void validate()
+ {
+ // Overridden to do nothing.
+ }
+
+ public void revalidate()
+ {
+ // Overridden to do nothing.
+ }
+
+ public void repaint(long tm, int x, int y, int w, int h)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void repaint(Rectangle rect)
+ {
+ // Overridden to do nothing.
+ }
+
+ protected void firePropertyChange(String propertyName, Object oldValue,
+ Object newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, byte oldValue,
+ byte newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, char oldValue,
+ char newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, short oldValue,
+ short newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, int oldValue,
+ int newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, long oldValue,
+ long newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, float oldValue,
+ float newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, double oldValue,
+ double newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, boolean oldValue,
+ boolean newValue)
+ {
+ // Overridden to do nothing.
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultListModel.java b/libjava/classpath/javax/swing/DefaultListModel.java
new file mode 100644
index 000000000..aa2600760
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultListModel.java
@@ -0,0 +1,521 @@
+/* DefaultListModel.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * The default implementation of {@link AbstractListModel}, used by
+ * {@link javax.swing.JList} and similar objects as the model of a list of
+ * values. The implementation is based on an underlying {@link
+ * java.util.Vector}.
+ *
+ * @author Andrew Selkirk
+ * @author Graydon Hoare (graydon@redhat.com)
+ */
+
+public class DefaultListModel extends AbstractListModel
+{
+ private static final long serialVersionUID = 2315945659722172272L;
+
+ /**
+ * The vector of elements in this list model.
+ */
+ private Vector elements = new Vector();
+
+ /**
+ * Gets an element of the list at the provided index.
+ *
+ * @param index The index of the element to get
+ *
+ * @return The object at the given index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list <code>[0, size())</code>
+ */
+ public Object elementAt(int index)
+ {
+ return elements.elementAt(index);
+ }
+
+ /**
+ * Convert the list to a string representation.
+ *
+ * @return A string representation of the list
+ */
+ public String toString()
+ {
+ return elements.toString();
+ }
+
+ /**
+ * Gets the first index of a particular element in the list.
+ *
+ * @param element The element to search for
+ *
+ * @return The first index in the list at which an object
+ * <code>obj</code> exists such that <code>obj.equals(element)</code> is
+ * <code>true</code>; if no such object exists, the method returns
+ * <code>-1</code>
+ */
+ public int indexOf(Object element)
+ {
+ return elements.indexOf(element);
+ }
+
+ /**
+ * Gets the first index of a particular element in a list which occurs
+ * <em>at or after</em> a particular index.
+ *
+ * @param element The element to search for
+ * @param startIndex The index to begin searching at
+ *
+ * @return The first index in the list, greater than or equal to
+ * <code>startIndex</code>, at which an object <code>obj</code> exists
+ * such that <code>obj.equals(element)</code> is <code>true</code>; if no
+ * such object exists, the method returns <code>-1</code>
+ */
+ public int indexOf(Object element, int startIndex)
+ {
+ return elements.indexOf(element, startIndex);
+ }
+
+ /**
+ * Gets the last index of a particular element in the list.
+ *
+ * @param element The element to search for
+ *
+ * @return The last index in the list at which an object
+ * <code>obj</code> exists such that <code>obj.equals(element)</code> is
+ * <code>true</code>; if no such object exists, the method returns
+ * <code>-1</code>
+ */
+ public int lastIndexOf(Object element)
+ {
+ return elements.lastIndexOf(element);
+ }
+
+ /**
+ * Gets the last index of a particular element in a list which occurs
+ * <em>at or before</em> a particular index.
+ *
+ * @param element The element to search for
+ * @param endIndex The index to finish searching at
+ *
+ * @return The last index in the list, less than to or equal to
+ * <code>endIndexIndex</code>, at which an object <code>obj</code> exists
+ * such that <code>obj.equals(element)</code> is <code>true</code>; if no
+ * such object exists, the method returns <code>-1</code>
+ */
+ public int lastIndexOf(Object element, int endIndex)
+ {
+ return elements.lastIndexOf(element, endIndex);
+ }
+
+ /**
+ * Gets the list element at a particular index.
+ *
+ * @param index The index to get the list value at
+ *
+ * @return The list value at the provided index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list <code>[0, size())</code>
+ */
+ public Object get(int index)
+ {
+ return elements.get(index);
+ }
+
+ /**
+ * Sets the list element at a particular index.
+ *
+ * @param index The list index at which to set a value
+ * @param element The value to set at the specified index
+ *
+ * @return The value previously held at the specified index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list <code>[0, size())</code>
+ */
+ public Object set(int index, Object element)
+ {
+ Object result;
+ result = elements.set(index, element);
+ fireContentsChanged(this, index, index);
+ return result;
+ }
+
+ /**
+ * Inserts an element at a particular index in the list. Each element at
+ * index <code>i >= index</code> is shifted to position <code>i + 1</code>.
+ * If <code>index</code> is equal to <code>size()</code>, this is
+ * equivalent to appending an element to the array. Any
+ * <code>index</code> greater than <code>size()</code> is illegal.
+ *
+ * @param index The index to insert the element at
+ * @param element The element to insert at the index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds <code>[0, size()]</code>
+ */
+ public void add(int index, Object element)
+ {
+ elements.add(index, element);
+ fireIntervalAdded(this, index, index);
+ }
+
+ /**
+ * Inserts an element at the end of the list. This is equivalent to
+ * calling <code>list.add(list.size(), element)</code>.
+ *
+ * @param element The element to add to the list
+ */
+ public void addElement(Object element)
+ {
+ int s = elements.size();
+ elements.add(element);
+ fireIntervalAdded(this, s, s);
+ }
+
+ /**
+ * Gets the number of elements in the list.
+ *
+ * @return The number of elements in the list
+ */
+ public int size()
+ {
+ return elements.size();
+ }
+
+ /**
+ * Gets an array containing the elements of the list.
+ *
+ * @return An array of the objects in the list, in the order they occur
+ * in the list
+ */
+ public Object[] toArray()
+ {
+ return elements.toArray();
+ }
+
+ /**
+ * Determines whether a particular element is a member of the list.
+ *
+ * @param element The element to search for
+ *
+ * @return <code>true</code> if <code>element</code> is a member of the
+ * list, otherwise <code>false</code>
+ */
+ public boolean contains(Object element)
+ {
+ return elements.contains(element);
+ }
+
+ /**
+ * Copies the list into a provided array. The provided array must be at
+ * least as large as the list.
+ *
+ * @param array The array to copy the list into
+ *
+ * @throws IndexOutOfBoundsException if the array is too small to hold the
+ * elements of the list
+ */
+ public void copyInto(Object[] array)
+ {
+ elements.copyInto(array);
+ }
+
+ /**
+ * Erases all the elements of the list, setting the list's size to 0.
+ */
+ public void clear()
+ {
+ int s = elements.size();
+ if (s > 0)
+ {
+ elements.clear();
+ fireIntervalRemoved(this, 0, s - 1);
+ }
+ }
+
+ /**
+ * Removes the element at a particular index from the list.
+ *
+ * @param index The index of the element to remove
+ *
+ * @return The value at the index, which has been removed from the list
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list <code>[0, size())</code>
+ */
+ public Object remove(int index)
+ {
+ Object result;
+ result = elements.remove(index);
+ fireIntervalRemoved(this, index, index);
+ return result;
+ }
+
+ /**
+ * Determines whether the list is empty.
+ *
+ * @return <code>true</code> if the list is empty, otherwise
+ * <code>false</code>
+ */
+ public boolean isEmpty()
+ {
+ return elements.isEmpty();
+ }
+
+ /**
+ * Returns an {@link java.util.Enumeration} over the elements of the list.
+ *
+ * @return A new enumeration which iterates over the list
+ */
+ public Enumeration<?> elements()
+ {
+ return elements.elements();
+ }
+
+ /**
+ * Sets the capacity of the list to be equal to its size. The list's capacity
+ * is the number of elements it can hold before it needs to be reallocated.
+ * The list's size is the number of elements it currently holds.
+ */
+ public void trimToSize()
+ {
+ elements.trimToSize();
+ }
+
+ /**
+ * Ensures that the list's capacity is at least equal to
+ * <code>size</code>. The list's capacity is the number of elements it
+ * can hold before it needs to be reallocated.
+ *
+ * @param size The capacity to ensure the list can hold
+ */
+ public void ensureCapacity(int size)
+ {
+ elements.ensureCapacity(size);
+ }
+
+ /**
+ * Sets the size of the list to a particular value. If the specified size
+ * is greater than the current size, the values at the excess list
+ * indices are set to <code>null</code>. If the specified size is less
+ * than the current size, the excess elements are removed from the list.
+ *
+ * @param size The new size to set the list to
+ */
+ public void setSize(int size)
+ {
+ int oldSize = elements.size();
+ elements.setSize(size);
+ if (oldSize < size)
+ {
+ fireIntervalAdded(this, oldSize, size - 1);
+ }
+ else if (oldSize > size)
+ {
+ this.fireIntervalRemoved(this, size, oldSize - 1);
+ }
+ }
+
+ /**
+ * Gets the capacity of the list. The list's capacity is the number of
+ * elements it can hold before it needs to be reallocated.
+ *
+ * @return The capacity of the list
+ */
+ public int capacity()
+ {
+ return elements.capacity();
+ }
+
+ /**
+ * Gets the first element in the list.
+ *
+ * @return The first element in the list
+ */
+ public Object firstElement()
+ {
+ return elements.firstElement();
+ }
+
+ /**
+ * Gets the last element in the list.
+ *
+ * @return The last element in the list
+ */
+ public Object lastElement()
+ {
+ return elements.lastElement();
+ }
+
+ /**
+ * Sets the list element at a particular index.
+ *
+ * @param element The value to set at the specified index
+ * @param index The list index at which to set a value
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list <code>[0, size())</code>
+ */
+ public void setElementAt(Object element, int index)
+ {
+ elements.setElementAt(element, index);
+ fireContentsChanged(this, index, index);
+ }
+
+ /**
+ * Removes the element at a particular index from the list.
+ *
+ * @param index The index of the element to remove
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list <code>[0, size())</code>
+ */
+ public void removeElementAt(int index)
+ {
+ elements.remove(index);
+ fireIntervalRemoved(this, index, index);
+ }
+
+ /**
+ * Inserts an element at a particular index in the list. Each element at
+ * index <code>i >= index</code> is shifted to position <code>i + 1</code>.
+ * If <code>index</code> is equal to <code>size()</code>, this is
+ * equivalent to appending an element to the array. Any
+ * <code>index</code> greater than <code>size()</code> is illegal.
+ *
+ * @param element The element to insert at the index
+ * @param index The index to insert the element at
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds <code>[0, size()]</code>
+ */
+ public void insertElementAt(Object element, int index)
+ {
+ elements.insertElementAt(element, index);
+ fireIntervalAdded(this, index, index);
+ }
+
+ /**
+ * Removes the first occurrence of a particular element in the list. If the
+ * element does not exist in the list, nothing happens.
+ *
+ * @param element The element to remove
+ *
+ * @return <code>true</code> if the element existed in the list (and was
+ * removed), <code>false</code> otherwise
+ */
+ public boolean removeElement(Object element)
+ {
+ int index;
+ index = elements.indexOf(element);
+ if (index != -1)
+ {
+ elements.remove(index);
+ fireIntervalRemoved(this, index, index);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove all elements in the list.
+ */
+ public void removeAllElements()
+ {
+ int size;
+ size = size();
+ if (size > 0)
+ {
+ elements.clear();
+ fireIntervalRemoved(this, 0, size - 1);
+ }
+ }
+
+ /**
+ * Remove all elements between <code>startIndex</code> and
+ * <code>endIndex</code> inclusive.
+ *
+ * @param startIndex The first index in the range to remove
+ * @param endIndex The last index in the range to remove
+ *
+ * @throws ArrayIndexOutOfBoundsException if either index is outside the
+ * valid range of indices for this list <code>[0, size())</code>
+ * @throws IllegalArgumentException if <code>startIndex &gt; endIndex</code>
+ */
+ public void removeRange(int startIndex, int endIndex)
+ {
+ int index;
+ if (startIndex > endIndex)
+ throw new IllegalArgumentException();
+ for (index = endIndex; index >= startIndex; index--)
+ elements.remove(index);
+ fireIntervalRemoved(this, startIndex, endIndex);
+ }
+
+ /**
+ * Gets the size of the list.
+ *
+ * @return The number of elements currently in the list
+ */
+ public int getSize()
+ {
+ return elements.size();
+ }
+
+ /**
+ * Gets the list element at a particular index.
+ *
+ * @param index The index to get the list value at
+ *
+ * @return The list value at the provided index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list <code>[0, size())</code>
+ */
+ public Object getElementAt(int index)
+ {
+ return elements.get(index);
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultListSelectionModel.java b/libjava/classpath/javax/swing/DefaultListSelectionModel.java
new file mode 100644
index 000000000..273ca0d15
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultListSelectionModel.java
@@ -0,0 +1,855 @@
+/* DefaultListSelectionModel.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.BitSet;
+import java.util.EventListener;
+
+import javax.swing.event.EventListenerList;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+/**
+ * The default implementation of {@link ListSelectionModel},
+ * which is used by {@link javax.swing.JList} and
+ * similar classes to manage the selection status of a number of data
+ * elements.
+ *
+ * <p>The class is organized <em>abstractly</em> as a set of intervals of
+ * integers. Each interval indicates an inclusive range of indices in a
+ * list -- held by some other object and unknown to this class -- which is
+ * considered "selected". There are various accessors for querying and
+ * modifying the set of intervals, with simplified forms accepting a single
+ * index, representing an interval with only one element. </p>
+ */
+public class DefaultListSelectionModel implements Cloneable,
+ ListSelectionModel,
+ Serializable
+{
+ private static final long serialVersionUID = -5718799865110415860L;
+
+ /** The list of ListSelectionListeners subscribed to this selection model. */
+ protected EventListenerList listenerList = new EventListenerList();
+
+
+ /**
+ * The current list selection mode. Must be one of the numeric constants
+ * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code>
+ * or <code>MULTIPLE_INTERVAL_SELECTION</code> from {@link
+ * ListSelectionModel}. The default value is
+ * <code>MULTIPLE_INTERVAL_SELECTION</code>.
+ */
+ int selectionMode = MULTIPLE_INTERVAL_SELECTION;
+
+ /**
+ * The index of the "lead" of the most recent selection. The lead is the
+ * second argument in any call to {@link #setSelectionInterval}, {@link
+ * #addSelectionInterval} or {@link #removeSelectionInterval}. Generally
+ * the lead refers to the most recent position a user dragged their mouse
+ * over.
+ */
+ int leadSelectionIndex = -1;
+
+ /**
+ * The index of the "anchor" of the most recent selection. The anchor is
+ * the first argument in any call to {@link #setSelectionInterval},
+ * {@link #addSelectionInterval} or {@link
+ * #removeSelectionInterval}. Generally the anchor refers to the first
+ * recent position a user clicks when they begin to drag their mouse over
+ * a list.
+ *
+ * @see #getAnchorSelectionIndex
+ * @see #setAnchorSelectionIndex
+ */
+ int anchorSelectionIndex = -1;
+
+ /**
+ * controls the range of indices provided in any {@link
+ * ListSelectionEvent} fired by the selectionModel. Let
+ * <code>[A,L]</code> be the range of indices between {@link
+ * #anchorSelectionIndex} and {@link #leadSelectionIndex} inclusive, and
+ * let <code>[i0,i1]</code> be the range of indices changed in a given
+ * call which generates a {@link ListSelectionEvent}. Then when this
+ * property is <code>true</code>, the {@link ListSelectionEvent} contains
+ * the range <code>[A,L] union [i0,i1]</code>; when <code>false</code> it
+ * will contain only <code>[i0,i1]</code>. The default is
+ * <code>true</code>.
+ *
+ * @see #isLeadAnchorNotificationEnabled
+ * @see #setLeadAnchorNotificationEnabled
+ */
+ protected boolean leadAnchorNotificationEnabled = true;
+
+ /**
+ * Whether the selection is currently "adjusting". Any {@link
+ * ListSelectionEvent} events constructed in response to changes in this
+ * list selection model will have their {@link
+ * ListSelectionEvent#isAdjusting} field set to this value.
+ *
+ * @see #getValueIsAdjusting
+ * @see #setValueIsAdjusting
+ */
+ boolean valueIsAdjusting = false;
+
+
+ /**
+ * The current set of "intervals", represented simply by a {@link
+ * java.util.BitSet}. A set bit indicates a selected index, whereas a
+ * cleared bit indicates a non-selected index.
+ */
+ BitSet sel = new BitSet();
+
+ /**
+ * A variable to store the previous value of sel.
+ * Used to make sure we only fireValueChanged when the BitSet
+ * actually does change.
+ */
+ Object oldSel;
+
+ /**
+ * Whether this call of setLeadSelectionInterval was called locally
+ * from addSelectionInterval
+ */
+ boolean setLeadCalledFromAdd = false;
+
+ /**
+ * Returns the selection mode, which is one of {@link #SINGLE_SELECTION},
+ * {@link #SINGLE_INTERVAL_SELECTION} and
+ * {@link #MULTIPLE_INTERVAL_SELECTION}. The default value is
+ * {@link #MULTIPLE_INTERVAL_SELECTION}.
+ *
+ * @return The selection mode.
+ *
+ * @see #setSelectionMode(int)
+ */
+ public int getSelectionMode()
+ {
+ return selectionMode;
+ }
+
+ /**
+ * Sets the value of the {@link #selectionMode} property.
+ *
+ * @param mode The new value of the property
+ */
+ public void setSelectionMode(int mode)
+ {
+ if (mode < ListSelectionModel.SINGLE_SELECTION
+ || mode > ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ throw new IllegalArgumentException("Unrecognised mode: " + mode);
+ selectionMode = mode;
+ }
+
+ /**
+ * Gets the value of the {@link #anchorSelectionIndex} property.
+ *
+ * @return The current property value
+ *
+ * @see #setAnchorSelectionIndex
+ */
+ public int getAnchorSelectionIndex()
+ {
+ return anchorSelectionIndex;
+ }
+
+ /**
+ * Sets the value of the {@link #anchorSelectionIndex} property.
+ *
+ * @param index The new property value
+ *
+ * @see #getAnchorSelectionIndex
+ */
+ public void setAnchorSelectionIndex(int index)
+ {
+ if (anchorSelectionIndex != index)
+ {
+ int old = anchorSelectionIndex;
+ anchorSelectionIndex = index;
+ if (leadAnchorNotificationEnabled)
+ fireValueChanged(index, old);
+ }
+ }
+
+ /**
+ * Gets the value of the {@link #leadSelectionIndex} property.
+ *
+ * @return The current property value
+ *
+ * @see #setLeadSelectionIndex
+ */
+ public int getLeadSelectionIndex()
+ {
+ return leadSelectionIndex;
+ }
+
+ /**
+ * <p>Sets the value of the {@link #anchorSelectionIndex} property. As a
+ * side effect, alters the selection status of two ranges of indices. Let
+ * <code>OL</code> be the old lead selection index, <code>NL</code> be
+ * the new lead selection index, and <code>A</code> be the anchor
+ * selection index. Then if <code>A</code> is a valid selection index,
+ * one of two things happens depending on the seleciton status of
+ * <code>A</code>:</p>
+ *
+ * <ul>
+ *
+ * <li><code>isSelectedIndex(A) == true</code>: set <code>[A,OL]</code>
+ * to <em>deselected</em>, then set <code>[A,NL]</code> to
+ * <em>selected</em>.</li>
+ *
+ * <li><code>isSelectedIndex(A) == false</code>: set <code>[A,OL]</code>
+ * to <em>selected</em>, then set <code>[A,NL]</code> to
+ * <em>deselected</em>.</li>
+ *
+ * </ul>
+ *
+ * <p>This method generates at most a single {@link ListSelectionEvent}
+ * despite changing multiple ranges. The range of values provided to the
+ * {@link ListSelectionEvent} includes only the minimum range of values
+ * which changed selection status between the beginning and end of the
+ * method.</p>
+ *
+ * @param leadIndex The new property value
+ *
+ * @see #getAnchorSelectionIndex
+ */
+ public void setLeadSelectionIndex(int leadIndex)
+ {
+ // Only set the lead selection index to < 0 if anchorSelectionIndex < 0.
+ if (leadIndex < 0)
+ {
+ if (anchorSelectionIndex < 0)
+ leadSelectionIndex = -1;
+ else
+ return;
+ }
+
+ // Only touch the lead selection index if the anchor is >= 0.
+ if (anchorSelectionIndex < 0)
+ return;
+
+ if (selectionMode == SINGLE_SELECTION)
+ setSelectionInterval (leadIndex, leadIndex);
+
+ int oldLeadIndex = leadSelectionIndex;
+ if (oldLeadIndex == -1)
+ oldLeadIndex = leadIndex;
+ if (setLeadCalledFromAdd == false)
+ oldSel = sel.clone();
+ leadSelectionIndex = leadIndex;
+
+ if (anchorSelectionIndex == -1)
+ return;
+
+ int R1 = Math.min(anchorSelectionIndex, oldLeadIndex);
+ int R2 = Math.max(anchorSelectionIndex, oldLeadIndex);
+ int S1 = Math.min(anchorSelectionIndex, leadIndex);
+ int S2 = Math.max(anchorSelectionIndex, leadIndex);
+
+ int lo = Math.min(R1, S1);
+ int hi = Math.max(R2, S2);
+
+ if (isSelectedIndex(anchorSelectionIndex))
+ {
+ sel.clear(R1, R2+1);
+ sel.set(S1, S2+1);
+ }
+ else
+ {
+ sel.set(R1, R2+1);
+ sel.clear(S1, S2+1);
+ }
+
+ int beg = sel.nextSetBit(0), end = -1;
+ for(int i=beg; i >= 0; i=sel.nextSetBit(i+1))
+ end = i;
+
+ BitSet old = (BitSet) oldSel;
+
+ // The new and previous lead location requires repainting.
+ old.set(oldLeadIndex, !sel.get(oldLeadIndex));
+ old.set(leadSelectionIndex, !sel.get(leadSelectionIndex));
+
+ fireDifference(sel, old);
+ }
+
+ /**
+ * Moves the lead selection index to <code>leadIndex</code> without
+ * changing the selection values.
+ *
+ * If leadAnchorNotificationEnabled is true, send a notification covering the
+ * old and new lead cells.
+ *
+ * @param leadIndex the new lead selection index
+ * @since 1.5
+ */
+ public void moveLeadSelectionIndex (int leadIndex)
+ {
+ if (leadSelectionIndex == leadIndex)
+ return;
+
+ leadSelectionIndex = leadIndex;
+ if (isLeadAnchorNotificationEnabled())
+ fireValueChanged(Math.min(leadSelectionIndex, leadIndex),
+ Math.max(leadSelectionIndex, leadIndex));
+ }
+
+ /**
+ * Gets the value of the {@link #leadAnchorNotificationEnabled} property.
+ *
+ * @return The current property value
+ *
+ * @see #setLeadAnchorNotificationEnabled
+ */
+ public boolean isLeadAnchorNotificationEnabled()
+ {
+ return leadAnchorNotificationEnabled;
+ }
+
+ /**
+ * Sets the value of the {@link #leadAnchorNotificationEnabled} property.
+ *
+ * @param l The new property value
+ *
+ * @see #isLeadAnchorNotificationEnabled
+ */
+ public void setLeadAnchorNotificationEnabled(boolean l)
+ {
+ leadAnchorNotificationEnabled = l;
+ }
+
+ /**
+ * Gets the value of the {@link #valueIsAdjusting} property.
+ *
+ * @return The current property value
+ *
+ * @see #setValueIsAdjusting
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return valueIsAdjusting;
+ }
+
+ /**
+ * Sets the value of the {@link #valueIsAdjusting} property.
+ *
+ * @param v The new property value
+ *
+ * @see #getValueIsAdjusting
+ */
+ public void setValueIsAdjusting(boolean v)
+ {
+ valueIsAdjusting = v;
+ }
+
+ /**
+ * Determines whether the selection is empty.
+ *
+ * @return <code>true</code> if the selection is empty, otherwise
+ * <code>false</code>
+ */
+ public boolean isSelectionEmpty()
+ {
+ return sel.isEmpty();
+ }
+
+ /**
+ * Gets the smallest index which is currently a member of a selection
+ * interval.
+ *
+ * @return The least integer <code>i</code> such that <code>i >=
+ * 0</code> and <code>i</code> is a member of a selected interval, or
+ * <code>-1</code> if there are no selected intervals
+ *
+ * @see #getMaxSelectionIndex
+ */
+ public int getMinSelectionIndex()
+ {
+ if (isSelectionEmpty())
+ return -1;
+
+ return sel.nextSetBit(0);
+ }
+
+ /**
+ * Gets the largest index which is currently a member of a selection
+ * interval.
+ *
+ * @return The greatest integer <code>i</code> such that <code>i >=
+ * 0</code> and <code>i</code> is a member of a selected interval, or
+ * <code>-1</code> if there are no selected intervals
+ *
+ * @see #getMinSelectionIndex
+ */
+ public int getMaxSelectionIndex()
+ {
+ if (isSelectionEmpty())
+ return -1;
+
+ int mx = -1;
+ for(int i=sel.nextSetBit(0); i >= 0; i=sel.nextSetBit(i+1))
+ {
+ mx = i;
+ }
+ return mx;
+ }
+
+ /**
+ * Determines whether a particular index is a member of a selection
+ * interval.
+ *
+ * @param a The index to search for
+ *
+ * @return <code>true</code> if the index is a member of a selection interval,
+ * otherwise <code>false</code>
+ */
+ public boolean isSelectedIndex(int a)
+ {
+ // TODO: Probably throw an exception here?
+ if (a >= sel.length() || a < 0)
+ return false;
+ return sel.get(a);
+ }
+
+ /**
+ * If the {@link #selectionMode} property is equal to
+ * <code>SINGLE_SELECTION</code> equivalent to calling
+ * <code>setSelectionInterval(index1, index2)</code>;
+ * If the {@link #selectionMode} property is equal to
+ * <code>SINGLE_INTERVAL_SELECTION</code> and the interval being
+ * added is not adjacent to an already selected interval,
+ * equivalent to <code>setSelectionInterval(index1, index2)</code>.
+ * Otherwise adds the range <code>[index0, index1]</code>
+ * to the selection interval set.
+ *
+ * @param index0 The beginning of the range of indices to select
+ * @param index1 The end of the range of indices to select
+ *
+ * @see #setSelectionInterval
+ * @see #removeSelectionInterval
+ */
+ public void addSelectionInterval(int index0, int index1)
+ {
+ if (index0 == -1 || index1 == -1)
+ return;
+
+ if (selectionMode == SINGLE_SELECTION)
+ setSelectionInterval(index0, index1);
+ else
+ {
+ int lo = Math.min(index0, index1);
+ int hi = Math.max(index0, index1);
+ oldSel = sel.clone();
+
+
+ // COMPAT: Like Sun (but not like IBM), we allow calls to
+ // addSelectionInterval when selectionMode is
+ // SINGLE_SELECTION_INTERVAL iff the interval being added
+ // is adjacent to an already selected interval
+ if (selectionMode == SINGLE_INTERVAL_SELECTION)
+ if (!(isSelectedIndex(index0) ||
+ isSelectedIndex(index1) ||
+ isSelectedIndex(Math.max(lo-1,0)) ||
+ isSelectedIndex(Math.min(hi+1,sel.size()))))
+ sel.clear();
+
+ // We have to update the anchorSelectionIndex and leadSelectionIndex
+ // variables
+
+ // The next if statements breaks down to "if this selection is adjacent
+ // to the previous selection and going in the same direction"
+ if ((isSelectedIndex(leadSelectionIndex))
+ && ((index0 - 1 == leadSelectionIndex
+ && (index1 >= index0)
+ && (leadSelectionIndex >= anchorSelectionIndex))
+ || (index0 + 1 == leadSelectionIndex && (index1 <= index0)
+ && (leadSelectionIndex <= anchorSelectionIndex)))
+ && (anchorSelectionIndex != -1 || leadSelectionIndex != -1))
+ {
+ // setting setLeadCalledFromAdd to true tells setLeadSelectionIndex
+ // not to update oldSel
+ setLeadCalledFromAdd = true;
+ setLeadSelectionIndex(index1);
+ setLeadCalledFromAdd = false;
+ }
+ else
+ {
+ leadSelectionIndex = index1;
+ anchorSelectionIndex = index0;
+ sel.set(lo, hi+1);
+ fireDifference(sel, (BitSet) oldSel);
+ }
+ }
+ }
+
+
+ /**
+ * Deselects all indices in the inclusive range
+ * <code>[index0,index1]</code>.
+ *
+ * @param index0 The beginning of the range of indices to deselect
+ * @param index1 The end of the range of indices to deselect
+ *
+ * @see #addSelectionInterval
+ * @see #setSelectionInterval
+ */
+ public void removeSelectionInterval(int index0,
+ int index1)
+ {
+ if (index0 == -1 || index1 == -1)
+ return;
+
+ oldSel = sel.clone();
+ int lo = Math.min(index0, index1);
+ int hi = Math.max(index0, index1);
+
+ // if selectionMode is SINGLE_INTERVAL_SELECTION and removing the interval
+ // (index0,index1) would leave two disjoint selection intervals, remove all
+ // selected indices from lo to the last selected index
+ if (getMinSelectionIndex() > 0 && getMinSelectionIndex() < lo &&
+ selectionMode == SINGLE_INTERVAL_SELECTION)
+ hi = sel.size() - 1;
+
+ sel.clear(lo, hi+1);
+ //update anchorSelectionIndex and leadSelectionIndex variables
+ //TODO: will probably need MouseDragged to test properly and know if this works
+ setAnchorSelectionIndex(index0);
+ leadSelectionIndex = index1;
+
+ fireDifference(sel, (BitSet) oldSel);
+ }
+
+ /**
+ * Removes all intervals in the selection set.
+ */
+ public void clearSelection()
+ {
+ // Find the selected interval.
+ int from = sel.nextSetBit(0);
+ if (from < 0)
+ return; // Empty selection - nothing to do.
+ int to = from;
+
+ int i;
+
+ for (i = from; i>=0; i=sel.nextSetBit(i+1))
+ to = i;
+
+ sel.clear();
+ fireValueChanged(from, to, valueIsAdjusting);
+ }
+
+ /**
+ * Fire the change event, covering the difference between the two sets.
+ *
+ * @param current the current set
+ * @param x the previous set, the object will be reused.
+ */
+ private void fireDifference(BitSet current, BitSet x)
+ {
+ x.xor(current);
+ int from = x.nextSetBit(0);
+ if (from < 0)
+ return; // No difference.
+ int to = from;
+ int i;
+
+ for (i = from; i >= 0; i = x.nextSetBit(i+1))
+ to = i;
+
+ fireValueChanged(from, to, valueIsAdjusting);
+ }
+
+ /**
+ * Clears the current selection and marks a given interval as "selected". If
+ * the current selection mode is <code>SINGLE_SELECTION</code> only the
+ * index <code>index2</code> is selected.
+ *
+ * @param anchor the anchor selection index.
+ * @param lead the lead selection index.
+ */
+ public void setSelectionInterval(int anchor, int lead)
+ {
+ if (anchor == -1 || lead == -1)
+ return;
+ if (selectionMode == SINGLE_SELECTION)
+ {
+ int lo = lead;
+ int hi = lead;
+ int selected = sel.nextSetBit(0);
+ if (selected == lead)
+ return; // the selection is not changing
+ if (selected >= 0)
+ {
+ lo = Math.min(lo, selected);
+ hi = Math.max(hi, selected);
+ }
+ if (anchorSelectionIndex >= 0)
+ {
+ lo = Math.min(lo, anchorSelectionIndex);
+ hi = Math.max(hi, anchorSelectionIndex);
+ }
+ sel.clear();
+ sel.set(lead);
+ leadSelectionIndex = lead;
+ anchorSelectionIndex = lead;
+ fireValueChanged(lo, hi);
+ }
+ else if (selectionMode == SINGLE_INTERVAL_SELECTION)
+ {
+ // determine the current interval
+ int first = sel.nextSetBit(0);
+ int last = first;
+ if (first >= 0)
+ last += (sel.cardinality() - 1);
+
+ // update the selection
+ int lo = Math.min(anchor, lead);
+ int hi = Math.max(anchor, lead);
+ if (lo == first && hi == last)
+ return; // selected interval is not being changed
+ sel.clear();
+ sel.set(lo, hi + 1);
+
+ // include the old selection in the event range
+ if (first >= 0)
+ lo = Math.min(lo, first);
+ if (last >= 0)
+ hi = Math.max(hi, last);
+ if (anchorSelectionIndex >= 0)
+ {
+ lo = Math.min(lo, anchorSelectionIndex);
+ hi = Math.max(hi, anchorSelectionIndex);
+ }
+ anchorSelectionIndex = anchor;
+ leadSelectionIndex = lead;
+ fireValueChanged(lo, hi);
+ }
+ else
+ {
+ BitSet oldSel = (BitSet) sel.clone();
+ sel.clear();
+ if (selectionMode == SINGLE_SELECTION)
+ anchor = lead;
+
+ int lo = Math.min(anchor, lead);
+ int hi = Math.max(anchor, lead);
+ sel.set(lo, hi+1);
+ // update the anchorSelectionIndex and leadSelectionIndex variables
+ setAnchorSelectionIndex(anchor);
+ leadSelectionIndex = lead;
+
+ fireDifference(sel, oldSel);
+ }
+ }
+
+ /**
+ * Inserts a number of indices either before or after a particular
+ * position in the set of indices. Renumbers all indices after the
+ * inserted range. The new indices in the inserted range are not
+ * selected. This method is typically called to synchronize the selection
+ * model with an inserted range of elements in a {@link ListModel}.
+ *
+ * @param index The position to insert indices at
+ * @param length The number of indices to insert
+ * @param before Indicates whether to insert the indices before the index
+ * or after it
+ */
+ public void insertIndexInterval(int index,
+ int length,
+ boolean before)
+ {
+ if (!before)
+ {
+ index++;
+ length--;
+ }
+ BitSet tmp = sel.get(index, sel.size());
+ sel.clear(index, sel.size());
+ int n = tmp.size();
+ for (int i = 0; i < n; ++i)
+ sel.set(index + length + i, tmp.get(i));
+ }
+
+ /**
+ * Removes a range from the set of indices. Renumbers all indices after
+ * the removed range. This method is typically called to synchronize the
+ * selection model with a deleted range of elements in a {@link
+ * ListModel}.
+ *
+ * @param index0 The first index to remove (inclusive)
+ * @param index1 The last index to remove (inclusive)
+ */
+ public void removeIndexInterval(int index0,
+ int index1)
+ {
+ int lo = Math.min(index0, index1);
+ int hi = Math.max(index0, index1);
+
+ BitSet tmp = sel.get(hi, sel.size());
+ sel.clear(lo, sel.size());
+ int n = tmp.size();
+ for (int i = 0; i < n; ++i)
+ sel.set(lo + i, tmp.get(i));
+ }
+
+ /**
+ * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
+ * ListSelectionListener} registered with this selection model to
+ * indicate that a series of adjustment has just ended.
+ *
+ * The values of {@link #getMinSelectionIndex} and
+ * {@link #getMaxSelectionIndex} are used in the {@link ListSelectionEvent}
+ * that gets fired.
+ *
+ * @param isAdjusting <code>true</code> if this is the final change
+ * in a series of adjustments, <code>false/code> otherwise
+ */
+ protected void fireValueChanged(boolean isAdjusting)
+ {
+ fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(),
+ isAdjusting);
+ }
+
+ /**
+ * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
+ * ListSelectionListener} registered with this selection model.
+ *
+ * @param firstIndex The low index of the changed range
+ * @param lastIndex The high index of the changed range
+ */
+ protected void fireValueChanged(int firstIndex, int lastIndex)
+ {
+ fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
+ }
+
+ /**
+ * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
+ * ListSelectionListener} registered with this selection model.
+ *
+ * @param firstIndex The low index of the changed range
+ * @param lastIndex The high index of the changed range
+ * @param isAdjusting Whether this change is part of a seqence of adjustments
+ * made to the selection, such as during interactive scrolling
+ */
+ protected void fireValueChanged(int firstIndex, int lastIndex,
+ boolean isAdjusting)
+ {
+ ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex,
+ lastIndex, isAdjusting);
+ ListSelectionListener[] listeners = getListSelectionListeners();
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].valueChanged(evt);
+ }
+
+ /**
+ * Adds a listener.
+ *
+ * @param listener The listener to add
+ *
+ * @see #removeListSelectionListener
+ * @see #getListSelectionListeners
+ */
+ public void addListSelectionListener(ListSelectionListener listener)
+ {
+ listenerList.add(ListSelectionListener.class, listener);
+ }
+
+ /**
+ * Removes a registered listener.
+ *
+ * @param listener The listener to remove
+ *
+ * @see #addListSelectionListener
+ * @see #getListSelectionListeners
+ */
+ public void removeListSelectionListener(ListSelectionListener listener)
+ {
+ listenerList.remove(ListSelectionListener.class, listener);
+ }
+
+ /**
+ * Returns an array of all registerers listeners.
+ *
+ * @param listenerType The type of listener to retrieve
+ *
+ * @return The array
+ *
+ * @see #getListSelectionListeners
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Returns an array of all registerd list selection listeners.
+ *
+ * @return the array
+ *
+ * @see #addListSelectionListener
+ * @see #removeListSelectionListener
+ * @see #getListeners
+ * @since 1.4
+ */
+ public ListSelectionListener[] getListSelectionListeners()
+ {
+ return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
+ }
+
+ /**
+ * Returns a clone of this object.
+ * <code>listenerList</code> don't gets duplicated.
+ *
+ * @return the cloned object
+ *
+ * @throws CloneNotSupportedException if an error occurs
+ */
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ DefaultListSelectionModel model =
+ (DefaultListSelectionModel) super.clone();
+ model.sel = (BitSet) sel.clone();
+ model.listenerList = new EventListenerList();
+ return model;
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java b/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java
new file mode 100644
index 000000000..93d129ebb
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java
@@ -0,0 +1,191 @@
+/* DefaultSingleSelectionModel.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.EventListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * The default implementation of {@link SingleSelectionModel}, used in
+ * {@link JTabbedPane}, {@link JMenuBar} and {@link JPopupMenu}.
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultSingleSelectionModel
+ implements SingleSelectionModel, Serializable
+{
+ private static final long serialVersionUID = 3676229404753786004L;
+
+ /**
+ * changeEvent
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * listenerList
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * The selected index (or -1 for no selection).
+ */
+ private int index = -1;
+
+ /**
+ * Creates a new <code>DefaultSingleSelectionModel</code> with no current
+ * selection.
+ */
+ public DefaultSingleSelectionModel()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Returns the selected index or <code>-1</code> if there is no selection.
+ *
+ * @return The selected index.
+ *
+ * @see #setSelectedIndex(int)
+ */
+ public int getSelectedIndex()
+ {
+ return index;
+ }
+
+ /**
+ * Sets the selected index and, if this is different to the previous
+ * selection, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param index the index (use <code>-1</code> to represent no selection).
+ *
+ * @see #getSelectedIndex()
+ * @see #clearSelection
+ */
+ public void setSelectedIndex(int index)
+ {
+ if (this.index != index)
+ {
+ this.index = index;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Clears the selection by setting the selected index to <code>-1</code> and
+ * sends a {@link ChangeEvent} to all registered listeners. If the selected
+ * index is already <code>-1</code>, this method does nothing.
+ */
+ public void clearSelection()
+ {
+ setSelectedIndex(-1);
+ }
+
+ /**
+ * Returns <code>true</code> if there is a selection, and <code>false</code>
+ * otherwise.
+ *
+ * @return A boolean.
+ */
+ public boolean isSelected()
+ {
+ return index != -1;
+ }
+
+ /**
+ * Registers a listener to receive {@link ChangeEvent} notifications from
+ * this model whenever the selected index changes.
+ *
+ * @param listener the listener to add.
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Deregisters a listener so that it no longer receives {@link ChangeEvent}
+ * notifications from this model.
+ *
+ * @param listener the listener to remove.
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * fireStateChanged
+ */
+ protected void fireStateChanged()
+ {
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ ChangeListener[] listeners = getChangeListeners();
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * getListeners
+ *
+ * @param listenerClass the type fo listener
+ *
+ * @return an array of listeners
+ *
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerClass)
+ {
+ return listenerList.getListeners(listenerClass);
+ }
+
+ /**
+ * getChangeListeners
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) getListeners(ChangeListener.class);
+ }
+}
diff --git a/libjava/classpath/javax/swing/DesktopManager.java b/libjava/classpath/javax/swing/DesktopManager.java
new file mode 100644
index 000000000..620c7ffb8
--- /dev/null
+++ b/libjava/classpath/javax/swing/DesktopManager.java
@@ -0,0 +1,177 @@
+/* DesktopManager.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * DesktopManagers are responsible for implementing the behaviours for the
+ * JInternalFrames that belong to JDesktopPanes. Actions such as maximizing,
+ * minimizing, iconifying, etc will be delegated to the DesktopManager.
+ */
+public interface DesktopManager
+{
+ /**
+ * This method will cause the JInternalFrame to be displayed in the set
+ * location. This usually is not needed since the user will add the
+ * JInternalFrame to a Container separately.
+ *
+ * @param frame The JInternalFrame to open.
+ */
+ void openFrame(JInternalFrame frame);
+
+ /**
+ * This method should remove the JInternalFrame from its parent.
+ *
+ * @param frame The JInternalFrame to close.
+ */
+ void closeFrame(JInternalFrame frame);
+
+ /**
+ * This method should maximize the JInternalFrame to match its parent's
+ * bounds.
+ *
+ * @param frame The JInternalFrame to maximize.
+ */
+ void maximizeFrame(JInternalFrame frame);
+
+ /**
+ * This method should restore the JInternalFrame to its normal bounds.
+ *
+ * @param frame The JInternalFrame to minimize.
+ */
+ void minimizeFrame(JInternalFrame frame);
+
+ /**
+ * This method should remove the JInternalFrame from its parent and replace
+ * it with a JDesktopIcon.
+ *
+ * @param frame The JInternalFrame to iconify.
+ */
+ void iconifyFrame(JInternalFrame frame);
+
+ /**
+ * This method should remove the JDesktopIcon from its parent and replace it
+ * with the JInternalFrame that the JDesktopIcon represents.
+ *
+ * @param frame The JInternalFrame to deiconify.
+ */
+ void deiconifyFrame(JInternalFrame frame);
+
+ /**
+ * This method should give focus to the JInternalFrame and its default focus
+ * owner.
+ *
+ * @param vframe The JInternalFrame to activate.
+ */
+ void activateFrame(JInternalFrame vframe);
+
+ /**
+ * This method should be called when the JInternalFrame gets deselected and
+ * subsequently loses focus.
+ *
+ * @param frame The JInternalFrame to deactivate.
+ */
+ void deactivateFrame(JInternalFrame frame);
+
+ /**
+ * This method should be called in preparation for dragging. This needs to
+ * be called prior to dragFrame calls so that the DesktopManager can
+ * prepare any state information.
+ *
+ * @param frame The JInternalFrame to prepare for dragging.
+ */
+ void beginDraggingFrame(JComponent frame);
+
+ /**
+ * This method drags the given JInternalFrame to the given x and y
+ * coordinates.
+ *
+ * @param frame The JInternalFrame to drag.
+ * @param x The new x coordinate.
+ * @param y The new y coordinate.
+ */
+ void dragFrame(JComponent frame, int x, int y);
+
+ /**
+ * This method should be called after dragFrame calls. Any information used
+ * by the DesktopManager for dragging the JInternalFrame can be cleared.
+ *
+ * @param frame The JInternalFrame that finished dragging.
+ */
+ void endDraggingFrame(JComponent frame);
+
+ /**
+ * This method should be called prior to any resizeFrame calls. Any state
+ * information needed by the DesktopManager to resize the JInternalFrame
+ * will be prepared here.
+ *
+ * @param frame The JInternalFrame to resize.
+ * @param direction One of eight directions specified by SwingConstants.
+ */
+ void beginResizingFrame(JComponent frame, int direction);
+
+ /**
+ * This method is called to resize the given JInternalFrame to the given
+ * bounds.
+ *
+ * @param frame The JInternalFrame to resize.
+ * @param x The new x coordinate.
+ * @param y The new y coordinate.
+ * @param width The new width.
+ * @param height The new height.
+ */
+ void resizeFrame(JComponent frame, int x, int y, int width, int height);
+
+ /**
+ * This method is called to signify that the resize is finished. Any
+ * information used to resize the JInternalFrame can now be cleared.
+ *
+ * @param frame The JInternalFrame that just finished dragging.
+ */
+ void endResizingFrame(JComponent frame);
+
+ /**
+ * This method does the actual work for reshaping the JInternalFrame.
+ *
+ * @param frame The JInternalFrame to resize.
+ * @param x The new x coordinate.
+ * @param y The new y coordinate.
+ * @param width The new width.
+ * @param height The new height.
+ */
+ void setBoundsForFrame(JComponent frame, int x, int y, int width, int height);
+} // DesktopManager
diff --git a/libjava/classpath/javax/swing/FocusManager.java b/libjava/classpath/javax/swing/FocusManager.java
new file mode 100644
index 000000000..b8459d4d5
--- /dev/null
+++ b/libjava/classpath/javax/swing/FocusManager.java
@@ -0,0 +1,524 @@
+/* FocusManager.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.DefaultKeyboardFocusManager;
+import java.awt.FocusTraversalPolicy;
+import java.awt.KeyEventDispatcher;
+import java.awt.KeyEventPostProcessor;
+import java.awt.KeyboardFocusManager;
+import java.awt.Window;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.VetoableChangeListener;
+import java.util.Set;
+
+/**
+ * This class has been obsoleted by the new
+ * {@link java.awt.KeyboardFocusManager} and
+ * {@link java.awt.DefaultKeyboardFocusManager} API.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class FocusManager
+ extends DefaultKeyboardFocusManager
+{
+ /**
+ * A FocusManager that wraps an AWT KeyboardFocusManager and forwards all
+ * method calls to it. This is used for compatibility with the new focus
+ * system.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class WrappingFocusManager
+ extends FocusManager
+ {
+ /**
+ * The wrapped KeyboardFocusManager.
+ */
+ private KeyboardFocusManager wrapped;
+
+ /**
+ * Creates a new instance of WrappedFocusManager.
+ *
+ * @param fm the focus manager to wrap
+ */
+ WrappingFocusManager(KeyboardFocusManager fm)
+ {
+ wrapped = fm;
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#dispatchEvent(AWTEvent)}.
+ *
+ * @param ev the event to dispatch
+ *
+ * @return <code>true</code> if the event has been dispatched,
+ * <code>false</code> otherwise
+ */
+ public boolean dispatchEvent(AWTEvent ev)
+ {
+ return wrapped.dispatchEvent(ev);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#dispatchKeyEvent(KeyEvent)}.
+ *
+ * @param ev the event to dispatch
+ *
+ * @return <code>true</code> if the event has been dispatched,
+ * <code>false</code> otherwise
+ */
+ public boolean dispatchKeyEvent(KeyEvent ev)
+ {
+ return wrapped.dispatchKeyEvent(ev);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#downFocusCycle(Container)}.
+ *
+ * @param c the container
+ */
+ public void downFocusCycle(Container c)
+ {
+ wrapped.downFocusCycle(c);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#upFocusCycle(Container)}.
+ *
+ * @param c the container
+ */
+ public void upFocusCycle(Container c)
+ {
+ wrapped.upFocusCycle(c);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#focusNextComponent(Component)}.
+ *
+ * @param c the component
+ */
+ public void focusNextComponent(Component c)
+ {
+ wrapped.focusNextComponent(c);
+ }
+
+ /**
+ * Wraps
+ * {@link DefaultKeyboardFocusManager#focusPreviousComponent(Component)}.
+ *
+ * @param c the component
+ */
+ public void focusPreviousComponent(Component c)
+ {
+ wrapped.focusPreviousComponent(c);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#postProcessKeyEvent(KeyEvent)}.
+ *
+ * @param e the key event
+ *
+ * @return a boolead
+ */
+ public boolean postProcessKeyEvent(KeyEvent e)
+ {
+ return wrapped.postProcessKeyEvent(e);
+ }
+
+ /**
+ * Wraps
+ * {@link DefaultKeyboardFocusManager#processKeyEvent(Component, KeyEvent)}.
+ *
+ * @param c the component
+ * @param e the key event
+ */
+ public void processKeyEvent(Component c, KeyEvent e)
+ {
+ wrapped.processKeyEvent(c, e);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#addKeyEventDispatcher(KeyEventDispatcher)}.
+ *
+ * @param d the dispatcher
+ */
+ public void addKeyEventDispatcher(KeyEventDispatcher d)
+ {
+ wrapped.addKeyEventDispatcher(d);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#addKeyEventPostProcessor(KeyEventPostProcessor)}.
+ *
+ * @param p the post processor
+ */
+ public void addKeyEventPostProcessor(KeyEventPostProcessor p)
+ {
+ wrapped.addKeyEventPostProcessor(p);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#addPropertyChangeListener(PropertyChangeListener)}.
+ *
+ * @param l the property change listener
+ */
+ public void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ wrapped.addPropertyChangeListener(l);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#addPropertyChangeListener(String, PropertyChangeListener)}.
+ *
+ * @param p the property name
+ * @param l the property change listener
+ */
+ public void addPropertyChangeListener(String p, PropertyChangeListener l)
+ {
+ wrapped.addPropertyChangeListener(p, l);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#addVetoableChangeListener(String, VetoableChangeListener)}.
+ *
+ * @param p the property name
+ * @param l the vetoable change listener
+ */
+ public void addVetoableChangeListener(String p, VetoableChangeListener l)
+ {
+ wrapped.addVetoableChangeListener(p, l);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#addVetoableChangeListener(VetoableChangeListener)}.
+ *
+ * @param l the vetoable change listener
+ */
+ public void addVetoableChangeListener(VetoableChangeListener l)
+ {
+ wrapped.addVetoableChangeListener(l);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#clearGlobalFocusOwner()}.
+ */
+ public void clearGlobalFocusOwner()
+ {
+ wrapped.clearGlobalFocusOwner();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getActiveWindow()}.
+ *
+ * @return the active window
+ */
+ public Window getActiveWindow()
+ {
+ return wrapped.getActiveWindow();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getCurrentFocusCycleRoot()}.
+ *
+ * @return the focus cycle root
+ */
+ public Container getCurrentFocusCycleRoot()
+ {
+ return wrapped.getCurrentFocusCycleRoot();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getDefaultFocusTraversalKeys(int)}.
+ *
+ * @param i the ID
+ *
+ * @return the focus traversal keys
+ */
+ public Set getDefaultFocusTraversalKeys(int i)
+ {
+ return wrapped.getDefaultFocusTraversalKeys(i);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getDefaultFocusTraversalPolicy()}.
+ *
+ * @return the focus traversal policy
+ */
+ public FocusTraversalPolicy getDefaultFocusTraversalPolicy()
+ {
+ return wrapped.getDefaultFocusTraversalPolicy();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getFocusedWindow()}.
+ *
+ * @return the focused window
+ */
+ public Window getFocusedWindow()
+ {
+ return wrapped.getFocusedWindow();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getFocusOwner()}.
+ *
+ * @return the focus owner
+ */
+ public Component getFocusOwner()
+ {
+ return wrapped.getFocusOwner();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getPermanentFocusOwner()}.
+ *
+ * @return the focus owner
+ */
+ public Component getPermanentFocusOwner()
+ {
+ return wrapped.getPermanentFocusOwner();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getPropertyChangeListeners()}.
+ *
+ * @return the property change listeners
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners()
+ {
+ return wrapped.getPropertyChangeListeners();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getPropertyChangeListeners(String)}.
+ *
+ * @param n the property name
+ *
+ * @return the property change listeners
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners(String n)
+ {
+ return wrapped.getPropertyChangeListeners(n);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getVetoableChangeListeners()}.
+ *
+ * @return the vetoable change listeners
+ */
+ public VetoableChangeListener[] getVetoableChangeListeners()
+ {
+ return wrapped.getVetoableChangeListeners();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getVetoableChangeListeners(String)}.
+ *
+ * @param n the property name
+ *
+ * @return the vetoable change listeners
+ */
+ public VetoableChangeListener[] getVetoableChangeListeners(String n)
+ {
+ return wrapped.getVetoableChangeListeners(n);
+ }
+
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removeKeyEventDispatcher(KeyEventDispatcher)}.
+ *
+ * @param d the key event dispatcher to remove
+ */
+ public void removeKeyEventDispatcher(KeyEventDispatcher d)
+ {
+ wrapped.removeKeyEventDispatcher(d);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removeKeyEventPostProcessor(KeyEventPostProcessor)}.
+ *
+ * @param p the post processor
+ */
+ public void removeKeyEventPostProcessor(KeyEventPostProcessor p)
+ {
+ wrapped.removeKeyEventPostProcessor(p);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removePropertyChangeListener(PropertyChangeListener)}.
+ *
+ * @param l the listener
+ */
+ public void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ wrapped.removePropertyChangeListener(l);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removePropertyChangeListener(String, PropertyChangeListener)}.
+ *
+ * @param n the property name
+ * @param l the listener
+ */
+ public void removePropertyChangeListener(String n, PropertyChangeListener l)
+ {
+ wrapped.removePropertyChangeListener(n, l);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removeVetoableChangeListener(VetoableChangeListener)}.
+ *
+ * @param l the listener
+ */
+ public void removeVetoableChangeListener(VetoableChangeListener l)
+ {
+ wrapped.removeVetoableChangeListener(l);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removeVetoableChangeListener(String, VetoableChangeListener)}.
+ *
+ * @param n the property name
+ * @param l the listener
+ */
+ public void removeVetoableChangeListener(String n, VetoableChangeListener l)
+ {
+ wrapped.removeVetoableChangeListener(n, l);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#setDefaultFocusTraversalKeys(int, Set)}.
+ *
+ * @param id the ID
+ * @param k the keystrokes
+ */
+ public void setDefaultFocusTraversalKeys(int id, Set k)
+ {
+ wrapped.setDefaultFocusTraversalKeys(id, k);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#setDefaultFocusTraversalPolicy(FocusTraversalPolicy)}.
+ *
+ * @param p the focus traversal policy
+ */
+ public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy p)
+ {
+ wrapped.setDefaultFocusTraversalPolicy(p);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#setGlobalCurrentFocusCycleRoot(Container)}.
+ *
+ * @param r the focus cycle root
+ */
+ public void setGlobalCurrentFocusCycleRoot(Container r)
+ {
+ wrapped.setGlobalCurrentFocusCycleRoot(r);
+ }
+ }
+
+ /**
+ * FOCUS_MANAGER_CLASS_PROPERTY
+ */
+ public static final String FOCUS_MANAGER_CLASS_PROPERTY =
+ "FocusManagerClassName";
+
+ /**
+ * Constructor FocusManager
+ */
+ public FocusManager()
+ {
+ super();
+ }
+
+ /**
+ * getCurrentManager
+ * @return FocusManager
+ */
+ public static FocusManager getCurrentManager()
+ {
+ KeyboardFocusManager m =
+ KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ return new WrappingFocusManager(m);
+ }
+
+ /**
+ * setCurrentManager
+ * @param manager TODO
+ */
+ public static void setCurrentManager(FocusManager manager)
+ {
+ KeyboardFocusManager.setCurrentKeyboardFocusManager(manager);
+ }
+
+ /**
+ * disableSwingFocusManager
+ * @deprecated 1.4
+ */
+ public static void disableSwingFocusManager()
+ {
+ // TODO
+ }
+
+ /**
+ * isFocusManagerEnabled
+ * @return boolean
+ * @deprecated 1.4
+ */
+ public static boolean isFocusManagerEnabled()
+ {
+ return false; // TODO
+ }
+}
diff --git a/libjava/classpath/javax/swing/GrayFilter.java b/libjava/classpath/javax/swing/GrayFilter.java
new file mode 100644
index 000000000..a7f969386
--- /dev/null
+++ b/libjava/classpath/javax/swing/GrayFilter.java
@@ -0,0 +1,97 @@
+/* GrayFilter.java -- Java class for filtering Pixels to produce Gray Pictures
+ Copyright (C) 1999, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Image;
+import java.awt.Toolkit;
+import java.awt.image.FilteredImageSource;
+import java.awt.image.RGBImageFilter;
+
+/**
+ * Produces grayscale images out of colored images. This is used to provide
+ * default disabled icons for buttons.
+ *
+ * @author original author unknown
+ */
+public class GrayFilter extends RGBImageFilter
+{
+ private boolean b;
+ private double p;
+
+ /**
+ * Create a GrayFilter. If b is true then brighten. Also, indicate how much
+ * gray.
+ *
+ * @param b if brighten
+ * @param p percent of gray, 0 - 100
+ */
+ public GrayFilter(boolean b, int p)
+ {
+ this.b = b; //FIXME - HANDLE THIS
+ this.p = (1. - (p / 100.)) / 3.;
+ }
+
+ /**
+ * Create grayed image
+ *
+ * @param src image to gray
+ *
+ * @return a grayed image
+ */
+ public static Image createDisabledImage(Image src)
+ {
+ return (Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(
+ src.getSource(), new GrayFilter(true, 0))));
+ }
+
+ /**
+ * Filter RGB to gray
+ */
+ public int filterRGB(int x, int y, int rgb)
+ {
+ int alpha = 0xff000000 & rgb;
+ int red = (0xff0000 & rgb) >> 16;
+ int green = (0xff00 & rgb) >> 8;
+ int blue = (0xff & rgb);
+ int gray = (int) ((0.299 * red + 0.587 * green + 0.114 * blue) * p);
+ if (b)
+ gray = Math.min(gray + 128, 255);
+ return gray | gray << 8 | gray << 16 | alpha ;
+ }
+}
diff --git a/libjava/classpath/javax/swing/Icon.java b/libjava/classpath/javax/swing/Icon.java
new file mode 100644
index 000000000..ed7f94fda
--- /dev/null
+++ b/libjava/classpath/javax/swing/Icon.java
@@ -0,0 +1,73 @@
+/* Icon.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Graphics;
+
+/**
+ * Defines the methods that an object must implement if it should be used
+ * as an icon in Swing.
+ */
+public interface Icon
+{
+ /**
+ * Returns the height of the icon.
+ *
+ * @return The height of the icon.
+ */
+ int getIconHeight();
+
+ /**
+ * Returns the width of the icon.
+ *
+ * @return The width of the icon.
+ */
+ int getIconWidth();
+
+ /**
+ * Draws the icon at the location (x, y) on the specified graphics device.
+ *
+ * @param c a component related to the icon in some way (can be ignored by
+ some implementing classes).
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ */
+ void paintIcon(Component c, Graphics g, int x, int y);
+}
diff --git a/libjava/classpath/javax/swing/ImageIcon.java b/libjava/classpath/javax/swing/ImageIcon.java
new file mode 100644
index 000000000..c78d04c58
--- /dev/null
+++ b/libjava/classpath/javax/swing/ImageIcon.java
@@ -0,0 +1,471 @@
+/* ImageIcon.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Graphics;
+import java.awt.IllegalComponentStateException;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Toolkit;
+import java.awt.image.ImageObserver;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.Locale;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleIcon;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleStateSet;
+
+/**
+ * An {@link Icon} implementation that is backed by an {@link Image}.
+ */
+public class ImageIcon
+ implements Icon, Serializable, Accessible
+{
+ /**
+ * Provides the accessibility features for the <code>ImageIcon</code>
+ * class.
+ */
+ protected class AccessibleImageIcon
+ extends AccessibleContext
+ implements AccessibleIcon, Serializable
+ {
+ private static final long serialVersionUID = 2113430526551336564L;
+
+ /**
+ * Creates a new instance of <code>AccessibleImageIcon</code>.
+ */
+ protected AccessibleImageIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the <code>ImageIcon</code>.
+ *
+ * @return {@link AccessibleRole#ICON}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.ICON;
+ }
+
+ /**
+ * Returns the accessible state for the <code>ImageIcon</code>. To
+ * match the reference implementation, this method always returns
+ * <code>null</code>.
+ *
+ * @return <code>null</code>.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // refer to Sun's bug report 4269253
+ return null;
+ }
+
+ /**
+ * Returns the accessible parent of this object. To match the reference
+ * implementation, this method always returns <code>null</code>.
+ *
+ * @return <code>null</code>.
+ */
+ public Accessible getAccessibleParent()
+ {
+ // refer to Sun's bug report 4269253
+ return null;
+ }
+
+ /**
+ * Returns the index of this object in its accessible parent. To match
+ * the reference implementation, this method always returns <code>-1</code>.
+ *
+ * @return <code>-1</code>.
+ */
+ public int getAccessibleIndexInParent()
+ {
+ // refer to Sun's bug report 4269253
+ return -1;
+ }
+
+ /**
+ * Returns the number of accessible children of this component,
+ * which is 0, because an {@link ImageIcon} has no children.
+ *
+ * @return <code>0</code>.
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the accessible child at index <code>i</code>, which is
+ * <code>null</code> in this case because an {@link ImageIcon} has no
+ * children.
+ *
+ * @param i the index of the child to be fetched
+ *
+ * @return <code>null</code>.
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the locale of this object. To match the reference
+ * implementation, this method always returns <code>null</code>.
+ *
+ * @return <code>null</code>.
+ */
+ public Locale getLocale()
+ throws IllegalComponentStateException
+ {
+ // refer to Sun's bug report 4269253
+ return null;
+ }
+
+ /**
+ * Returns the accessible icon description. This returns the
+ * <code>description</code> property of the underlying {@link ImageIcon}.
+ *
+ * @return The description (possibly <code>null</code>).
+ *
+ * @see #setAccessibleIconDescription(String)
+ */
+ public String getAccessibleIconDescription()
+ {
+ return getDescription();
+ }
+
+ /**
+ * Sets the accessible icon description. This sets the
+ * <code>description</code> property of the underlying {@link ImageIcon}.
+ *
+ * @param newDescr the description (<code>null</code> permitted).
+ *
+ * @see #getAccessibleIconDescription()
+ */
+ public void setAccessibleIconDescription(String newDescr)
+ {
+ setDescription(newDescr);
+ }
+
+ /**
+ * Returns the icon height. This returns the <code>iconHeight</code>
+ * property of the underlying {@link ImageIcon}.
+ *
+ * @return The icon height.
+ */
+ public int getAccessibleIconHeight()
+ {
+ return getIconHeight();
+ }
+
+ /**
+ * Returns the icon width. This returns the <code>iconWidth</code> property
+ * of the underlying {@link ImageIcon}.
+ *
+ * @return The icon width.
+ */
+ public int getAccessibleIconWidth()
+ {
+ return getIconWidth();
+ }
+ } // AccessibleIcon
+
+ private static final long serialVersionUID = 532615968316031794L;
+
+ /** A dummy Component that is used in the MediaTracker. */
+ protected static final Component component = new Component()
+ {
+ // No need to implement this.
+ };
+
+ /** The MediaTracker used to monitor the loading of images. */
+ protected static final MediaTracker tracker = new MediaTracker(component);
+
+ /** The ID that is used in the tracker. */
+ private static int id;
+
+ Image image;
+ String description;
+ ImageObserver observer;
+
+ /** The image loading status. */
+ private int loadStatus;
+
+ /** The AccessibleContext of this ImageIcon. */
+ private AccessibleContext accessibleContext;
+
+ /**
+ * Creates an ImageIcon without any properties set.
+ */
+ public ImageIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Constructs an ImageIcon given a filename. The icon's description
+ * is initially set to the filename itself. A filename of "" means
+ * create a blank icon.
+ *
+ * @param filename name of file to load or "" for a blank icon
+ */
+ public ImageIcon(String filename)
+ {
+ this(filename, filename);
+ }
+
+ /**
+ * Constructs an ImageIcon from the given filename, setting its
+ * description to the given description. A filename of "" means
+ * create a blank icon.
+ *
+ * @param filename name of file to load or "" for a blank icon
+ * @param description human-readable description of this icon
+ */
+ public ImageIcon(String filename, String description)
+ {
+ this(Toolkit.getDefaultToolkit().getImage(filename), description);
+ }
+
+ /**
+ * Creates an ImageIcon from the given byte array without any
+ * description set.
+ */
+ public ImageIcon(byte[] imageData)
+ {
+ this(imageData, null);
+ }
+
+ /**
+ * Creates an ImageIcon from the given byte array and sets the given
+ * description.
+ */
+ public ImageIcon(byte[] imageData, String description)
+ {
+ this(Toolkit.getDefaultToolkit().createImage(imageData), description);
+ }
+
+ /**
+ * Creates an ImageIcon from the given URL and sets the description
+ * to the URL String representation.
+ */
+ public ImageIcon(URL url)
+ {
+ this(url, url.toString());
+ }
+
+ /**
+ * Creates an ImageIcon from the given URL and sets the given
+ * description.
+ */
+ public ImageIcon(URL url, String description)
+ {
+ this(Toolkit.getDefaultToolkit().getImage(url), description);
+ }
+
+ /**
+ * Creates an ImageIcon from the given Image without any description
+ * set.
+ */
+ public ImageIcon(Image image)
+ {
+ this(image, null);
+ }
+
+ /**
+ * Creates an ImageIcon from the given Image and sets the given
+ * description.
+ */
+ public ImageIcon(Image image, String description)
+ {
+ setImage(image);
+ setDescription(description);
+ }
+
+ /**
+ * Returns the ImageObserver that is used for all Image
+ * operations. Defaults to null when not explicitly set.
+ */
+ public ImageObserver getImageObserver()
+ {
+ return observer;
+ }
+
+ /**
+ * Sets the ImageObserver that will be used for all Image
+ * operations. Can be set to null (the default) when no observer is
+ * needed.
+ */
+ public void setImageObserver(ImageObserver newObserver)
+ {
+ observer = newObserver;
+ }
+
+ /**
+ * Returns the backing Image for this ImageIcon. Might be set to
+ * null in which case no image is shown.
+ */
+ public Image getImage()
+ {
+ return image;
+ }
+
+ /**
+ * Explicitly sets the backing Image for this ImageIcon. Will call
+ * loadImage() to make sure that the Image is completely loaded
+ * before returning.
+ */
+ public void setImage(Image image)
+ {
+ loadImage(image);
+ this.image = image;
+ }
+
+ /**
+ * Returns a human readable description for this ImageIcon or null
+ * when no description is set or available.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Sets a human readable description for this ImageIcon. Can be set
+ * to null when no description is available.
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /**
+ * Returns the the height of the backing Image, or -1 if the backing
+ * Image is null. The getHeight() method of the Image will be called
+ * with the set observer of this ImageIcon.
+ */
+ public int getIconHeight()
+ {
+ if (image == null)
+ return -1;
+
+ return image.getHeight(observer);
+ }
+
+ /**
+ * Returns the the width of the backing Image, or -1 if the backing
+ * Image is null. The getWidth() method of the Image will be called
+ * with the set observer of this ImageIcon.
+ */
+ public int getIconWidth()
+ {
+ if (image == null)
+ return -1;
+
+ return image.getWidth(observer);
+ }
+
+ /**
+ * Calls <code>g.drawImage()</code> on the backing Image using the
+ * set observer of this ImageIcon. If the set observer is null, the
+ * given Component is used as observer.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.drawImage(image, x, y, observer != null ? observer : c);
+ }
+
+ /**
+ * Loads the image and blocks until the loading operation is finished.
+ *
+ * @param image the image to be loaded
+ */
+ protected void loadImage(Image image)
+ {
+ try
+ {
+ tracker.addImage(image, id);
+ id++;
+ tracker.waitForID(id - 1);
+ }
+ catch (InterruptedException ex)
+ {
+ // Ignore this for now.
+ }
+ finally
+ {
+ loadStatus = tracker.statusID(id - 1, false);
+ tracker.removeImage(image, id - 1);
+ }
+ }
+
+ /**
+ * Returns the load status of the icon image.
+ *
+ * @return the load status of the icon image
+ *
+ * @see MediaTracker#COMPLETE
+ * @see MediaTracker#ABORTED
+ * @see MediaTracker#ERRORED
+ */
+ public int getImageLoadStatus()
+ {
+ return loadStatus;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>ImageIcon</code> instance.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleImageIcon}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleImageIcon();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/InputMap.java b/libjava/classpath/javax/swing/InputMap.java
new file mode 100644
index 000000000..40c43daa4
--- /dev/null
+++ b/libjava/classpath/javax/swing/InputMap.java
@@ -0,0 +1,235 @@
+/* InputMap.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Maps {@link KeyStroke}s to arbitrary objects, usually Strings. This
+ * is used in combination with {@link ActionMap}s.
+ *
+ * If a component receives an input event, this is looked up in
+ * the component's <code>InputMap</code>. The result is an object which
+ * serves as a key to the component's <code>ActionMap</code>. Finally
+ * the <code>Action</code> that is stored is executed.
+ *
+ * @author Andrew Selkirk
+ * @author Michael Koch
+ *
+ * @since 1.3
+ */
+public class InputMap
+ implements Serializable
+{
+ private static final long serialVersionUID = -5429059542008604257L;
+
+ /**
+ * Storage for the KeyStroke --> Object mappings.
+ */
+ private Map inputMap;
+
+ /**
+ * An optional parent map.
+ */
+ private InputMap parent;
+
+ /**
+ * Creates a new <code>InputMap</code> instance. This default instance
+ * contains no mappings and has no parent.
+ */
+ public InputMap()
+ {
+ // nothing to do
+ }
+
+ /**
+ * Returns the binding for the specified keystroke, if there is one.
+ *
+ * @param keystroke the key of the entry (<code>null</code> is ignored).
+ *
+ * @return The binding associated with the specified keystroke (or
+ * <code>null</code>).
+ */
+ public Object get(KeyStroke keystroke)
+ {
+ Object result = null;
+ if (inputMap != null)
+ result = inputMap.get(keystroke);
+
+ if (result == null && parent != null)
+ result = parent.get(keystroke);
+ return result;
+ }
+
+ /**
+ * Puts a new entry into the <code>InputMap</code>. If
+ * <code>actionMapKey</code> is <code>null</code> any existing entry will be
+ * removed.
+ *
+ * @param keystroke the keystroke for the entry (<code>null</code> is
+ * ignored).
+ * @param actionMapKey the action (<code>null</code> permitted).
+ */
+ public void put(KeyStroke keystroke, Object actionMapKey)
+ {
+ if (keystroke == null)
+ return;
+ if (inputMap == null)
+ inputMap = new HashMap();
+ if (actionMapKey == null)
+ inputMap.remove(keystroke);
+ else
+ inputMap.put(keystroke, actionMapKey);
+ }
+
+ /**
+ * Removes an entry from this <code>InputMap</code>. Note that this will
+ * not remove any entry from the parent map, if there is one.
+ *
+ * @param keystroke the key of the entry to remove (<code>null</code> is
+ * ignored).
+ */
+ public void remove(KeyStroke keystroke)
+ {
+ if (inputMap != null)
+ inputMap.remove(keystroke);
+ }
+
+ /**
+ * Returns the parent of this <code>InputMap</code>. The default value
+ * is <code>null</code>.
+ *
+ * @return The parent map (possibly <code>null</code>).
+ *
+ * @see #setParent(InputMap)
+ */
+ public InputMap getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Sets a parent for this <code>InputMap</code>. If a parent is specified,
+ * the {@link #get(KeyStroke)} method will look in the parent if it cannot
+ * find an entry in this map.
+ *
+ * @param parentMap the new parent (<code>null</code> permitted).
+ *
+ * @see #getParent()
+ */
+ public void setParent(InputMap parentMap)
+ {
+ parent = parentMap;
+ }
+
+ /**
+ * Returns the number of entries in this <code>InputMap</code>. This count
+ * does not include any entries from the parent map, if there is one.
+ *
+ * @return The number of entries.
+ */
+ public int size()
+ {
+ int result = 0;
+ if (inputMap != null)
+ result = inputMap.size();
+ return result;
+ }
+
+ /**
+ * Clears the entries from this <code>InputMap</code>. The parent map, if
+ * there is one, is not cleared.
+ */
+ public void clear()
+ {
+ if (inputMap != null)
+ inputMap.clear();
+ }
+
+ /**
+ * Returns all keys of entries in this <code>InputMap</code>. This does not
+ * include keys defined in the parent, if there is one (use the
+ * {@link #allKeys()} method for that case).
+ * <br><br>
+ * Following the behaviour of the reference implementation, this method will
+ * return <code>null</code> when no entries have been added to the map,
+ * and a zero length array if entries have been added but subsequently
+ * removed (or cleared) from the map.
+ *
+ * @return An array of keys (may be <code>null</code> or have zero length).
+ */
+ public KeyStroke[] keys()
+ {
+ if (inputMap != null)
+ {
+ KeyStroke[] array = new KeyStroke[size()];
+ return (KeyStroke[]) inputMap.keySet().toArray(array);
+ }
+ return null;
+ }
+
+ /**
+ * Returns all keys of entries in this <code>InputMap</code> and all its
+ * parents.
+ *
+ * @return An array of keys (may be <code>null</code> or have zero length).
+ */
+ public KeyStroke[] allKeys()
+ {
+ Set set = new HashSet();
+
+ if (parent != null)
+ {
+ Object[] parentKeys = parent.allKeys();
+ if (parentKeys != null)
+ set.addAll(Arrays.asList(parentKeys));
+ }
+ if (inputMap != null)
+ set.addAll(inputMap.keySet());
+ if (set.size() == 0)
+ return null;
+ KeyStroke[] array = new KeyStroke[set.size()];
+ return (KeyStroke[]) set.toArray(array);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/InputVerifier.java b/libjava/classpath/javax/swing/InputVerifier.java
new file mode 100644
index 000000000..eeb81b5d5
--- /dev/null
+++ b/libjava/classpath/javax/swing/InputVerifier.java
@@ -0,0 +1,79 @@
+/* InputVerifier.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * Verifies the user input on a component before the focus is shifted.
+ * It is sometimes necessary that input components have a valid state before
+ * they loose focus. Such components can have a <code>InputVerifier</code>
+ * subclass registered, that permits or vetos a focus change request.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class InputVerifier
+{
+ /**
+ * Creates a <code>InputVerifier</code>
+ */
+ public InputVerifier()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * verify
+ *
+ * @param component the component to verify
+ *
+ * @return <code>true</code> if valid, <code>false</code> otherwise.
+ */
+ public abstract boolean verify(JComponent component);
+
+ /**
+ * shouldYieldFocus
+ *
+ * @param component the component to verify
+ *
+ * @return <code>true</code> if valid, <code>false</code> otherwise.
+ */
+ public boolean shouldYieldFocus(JComponent component)
+ {
+ return verify(component);
+ }
+}
diff --git a/libjava/classpath/javax/swing/InternalFrameFocusTraversalPolicy.java b/libjava/classpath/javax/swing/InternalFrameFocusTraversalPolicy.java
new file mode 100644
index 000000000..9187f2924
--- /dev/null
+++ b/libjava/classpath/javax/swing/InternalFrameFocusTraversalPolicy.java
@@ -0,0 +1,60 @@
+/* InternalFrameFocusTraversalPolicy.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.FocusTraversalPolicy;
+
+/**
+ * A {@link FocusTraversalPolicy} that provides the additional capability
+ * to determine a {@link JInternalFrame}'s initially focused component
+ * when it is selected.
+ *
+ * @author Michael Koch
+ *
+ * @since 1.4
+ */
+public abstract class InternalFrameFocusTraversalPolicy
+ extends FocusTraversalPolicy
+{
+ public Component getInitialComponent(JInternalFrame frame)
+ {
+ return getDefaultComponent(frame);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JApplet.java b/libjava/classpath/javax/swing/JApplet.java
new file mode 100644
index 000000000..d725131e6
--- /dev/null
+++ b/libjava/classpath/javax/swing/JApplet.java
@@ -0,0 +1,224 @@
+/* JApplet.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.applet.Applet;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.LayoutManager;
+import java.awt.event.KeyEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+/**
+ * A top-level container that is usually used in web browsers.
+ *
+ * @author original author unknown
+ */
+public class JApplet extends Applet
+ implements RootPaneContainer, Accessible
+{
+ /**
+ * Provides accessibility support for <code>JApplet</code>.
+ */
+ protected class AccessibleJApplet extends Applet.AccessibleApplet
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJApplet</code>.
+ */
+ protected AccessibleJApplet()
+ {
+ super();
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * The accessible context for this <code>JApplet</code>.
+ */
+ protected AccessibleContext accessibleContext;
+
+ private static final long serialVersionUID = 7269359214497372587L;
+
+ protected JRootPane rootPane;
+
+ /**
+ * @specnote rootPaneCheckingEnabled is false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ public JApplet()
+ {
+ super.setLayout(new BorderLayout(1, 1));
+ getRootPane(); // Will do set/create.
+ setRootPaneCheckingEnabled(true); // Init stage is now over.
+ }
+
+ public Dimension getPreferredSize()
+ {
+ return super.getPreferredSize();
+ }
+
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ public void setLayeredPane(JLayeredPane layeredPane)
+ {
+ getRootPane().setLayeredPane(layeredPane);
+ }
+
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ public JRootPane getRootPane()
+ {
+ if (rootPane == null)
+ setRootPane(createRootPane());
+ return rootPane;
+ }
+
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ rootPane = root;
+ add(rootPane, BorderLayout.CENTER);
+ }
+
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ public void setContentPane(Container contentPane)
+ {
+ getRootPane().setContentPane(contentPane);
+ }
+
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ public void setGlassPane(Component glassPane)
+ {
+ getRootPane().setGlassPane(glassPane);
+ }
+
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're adding in the initialization stage use super.add.
+ // Otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().add(comp, constraints, index);
+ else
+ super.addImpl(comp, constraints, index);
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJApplet();
+ return accessibleContext;
+ }
+
+ public JMenuBar getJMenuBar()
+ {
+ return getRootPane().getJMenuBar();
+ }
+
+ public void setJMenuBar(JMenuBar menubar)
+ {
+ getRootPane().setJMenuBar(menubar);
+ }
+
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+
+ protected void processKeyEvent(KeyEvent e)
+ {
+ super.processKeyEvent(e);
+ }
+
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead
+ if (comp == rootPane)
+ super.remove(rootPane);
+ else
+ getContentPane().remove(comp);
+ }
+
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JButton.java b/libjava/classpath/javax/swing/JButton.java
new file mode 100644
index 000000000..40c104194
--- /dev/null
+++ b/libjava/classpath/javax/swing/JButton.java
@@ -0,0 +1,276 @@
+/* JButton.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.ButtonUI;
+
+
+/**
+ * A general purpose push button. <code>JButton</code>s can display a label,
+ * an {@link Icon} or both.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JButton extends AbstractButton
+ implements Accessible
+{
+
+ /**
+ * Accessibility support for JButtons.
+ */
+ protected class AccessibleJButton
+ extends AbstractButton.AccessibleAbstractButton
+ {
+ /**
+ * Returns the accessible role that this component represents.
+ * This is {@link AccessibleRole#PUSH_BUTTON} for <code>JButton</code>s.
+ *
+ * @return the accessible role that this component represents
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PUSH_BUTTON;
+ }
+ }
+
+ private static final long serialVersionUID = -1907255238954382202L;
+
+ /**
+ * Indicates if this button is capable to become the default button.
+ */
+ private boolean defaultCapable;
+
+ /**
+ * Creates a new button with an empty string for the button text and no
+ * icon.
+ */
+ public JButton()
+ {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new button from the specified action.
+ *
+ * @param a the action (<code>null</code> permitted).
+ *
+ * @see AbstractButton#setAction(Action)
+ */
+ public JButton(Action a)
+ {
+ this();
+ setAction(a);
+ }
+
+ /**
+ * Creates a new button with the specified icon (and an empty string for
+ * the button text).
+ *
+ * @param icon the icon (<code>null</code> permitted).
+ */
+ public JButton(Icon icon)
+ {
+ this(null, icon);
+ }
+
+ /**
+ * Creates a new button with the specified text and no icon.
+ *
+ * @param text the button text (<code>null</code> permitted, will be
+ * substituted by an empty string).
+ */
+ public JButton(String text)
+ {
+ this(text, null);
+ }
+
+ /**
+ * Creates a new button with the specified text and icon.
+ *
+ * @param text the button text (<code>null</code> permitted, will be
+ * substituted by an empty string).
+ * @param icon the icon (<code>null</code> permitted).
+ */
+ public JButton(String text, Icon icon)
+ {
+ super();
+ setModel(new DefaultButtonModel());
+ init(text, icon);
+ defaultCapable = true;
+ }
+
+ protected void configurePropertiesFromAction(Action a)
+ {
+ super.configurePropertiesFromAction(a);
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JButton</code> component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJButton}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJButton();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns the suffix (<code>"ButtonUI"</code> in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a <code>JButton</code>.
+ *
+ * @return <code>"ButtonUI"</code>.
+ */
+ public String getUIClassID()
+ {
+ // Returns a string that specifies the name of the L&F class that renders
+ // this component.
+ return "ButtonUI";
+ }
+
+ /**
+ * Returns <code>true</code> if this button is the default button in
+ * its <code>JRootPane</code>. The default button gets automatically
+ * activated when the user presses <code>ENTER</code> (or whatever
+ * key this is bound to in the current Look and Feel).
+ *
+ * @return <code>true</code> if this button is the default button in
+ * its <code>JRootPane</code>
+ *
+ * @see #isDefaultCapable()
+ * @see #setDefaultCapable(boolean)
+ * @see JRootPane#getDefaultButton()
+ * @see JRootPane#setDefaultButton(JButton)
+ */
+ public boolean isDefaultButton()
+ {
+ // The default button is managed by the JRootPane, so the safest way
+ // to determine this property is to ask the root pane of this button,
+ // if it exists.
+ JRootPane rp = SwingUtilities.getRootPane(this);
+ boolean isDefault = false;
+ if (rp != null)
+ isDefault = rp.getDefaultButton() == this;
+ return isDefault;
+ }
+
+ /**
+ * Returns <code>true</code> if this button can act as the default button.
+ * This is <code>true</code> by default.
+ *
+ * @return <code>true</code> if this button can act as the default button
+ *
+ * @see #setDefaultCapable(boolean)
+ * @see #isDefaultButton()
+ * @see JRootPane#getDefaultButton()
+ * @see JRootPane#setDefaultButton(JButton)
+ */
+ public boolean isDefaultCapable()
+ {
+ // Returns whether or not this button is capable of being the default
+ // button on the RootPane.
+ return defaultCapable;
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this <code>JButton</code>.
+ *
+ * @return A string describing the attributes of this <code>JButton</code>
+ * (never <code>null</code>).
+ */
+ protected String paramString()
+ {
+ String superParam = super.paramString();
+
+ // 41 is the maximum number of chars which may be needed.
+ CPStringBuilder sb = new CPStringBuilder(41);
+ sb.append(",defaultButton=").append(isDefaultButton());
+ sb.append(",defaultCapable=").append(defaultCapable);
+
+ return superParam + sb.toString();
+ }
+
+ /**
+ * Overrides JComponent.removeNotify to check if this button is currently
+ * set as the default button on the RootPane, and if so, sets the RootPane's
+ * default button to null to ensure the RootPane doesn't hold onto an invalid
+ * button reference.
+ */
+ public void removeNotify()
+ {
+ JRootPane root = SwingUtilities.getRootPane(this);
+ if (root != null && root.getDefaultButton() == this)
+ root.setDefaultButton(null);
+ super.removeNotify();
+ }
+
+ /**
+ * Sets the <code>defaultCapable</code> property which indicates if
+ * this button may become the default button in its <code>JRootPane</code>.
+ *
+ * @param defaultCapable <code>true</code> if this button can become the
+ * default button in its JRootPane, <code>false</code> otherwise
+ *
+ * @see #setDefaultCapable(boolean)
+ * @see #isDefaultButton()
+ * @see JRootPane#getDefaultButton()
+ * @see JRootPane#setDefaultButton(JButton)
+ */
+ public void setDefaultCapable(boolean defaultCapable)
+ {
+ this.defaultCapable = defaultCapable;
+ }
+
+ /**
+ * Sets this button's UI delegate to the default (obtained from the
+ * {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ButtonUI) UIManager.getUI(this));
+ }
+}
diff --git a/libjava/classpath/javax/swing/JCheckBox.java b/libjava/classpath/javax/swing/JCheckBox.java
new file mode 100644
index 000000000..0bf871a50
--- /dev/null
+++ b/libjava/classpath/javax/swing/JCheckBox.java
@@ -0,0 +1,176 @@
+/* JCheckBox.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * A small box that displays a check or not, depending on it's
+ * <code>selected</code> state. This works very similar to
+ * {@link JToggleButton} and {@link JRadioButton}, but in UI design it
+ * has different semantics. <code>JCheckBox</code>es are usually
+ * used in multiple-choice scenarios, where a user can select 0..n
+ * of n different options. (This is in contrast to the general RadioButton
+ * semantics where the user can select exactly one of n options).
+ *
+ * Note however that this semantics is in no way enforced by the
+ * <code>JCheckBox</code>.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JCheckBox extends JToggleButton implements Accessible
+{
+
+ /**
+ * Provides accessibility support for <code>JCheckBox</code>.
+ */
+ protected class AccessibleJCheckBox extends AccessibleJToggleButton
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJCheckBox</code>.
+ */
+ protected AccessibleJCheckBox()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessble role of <code>JCheckBox</code>,
+ * {@link AccessibleRole#CHECK_BOX}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.CHECK_BOX;
+ }
+ }
+
+ private static final long serialVersionUID = -5246739313864538930L;
+
+ public static final String BORDER_PAINTED_FLAT_CHANGED_PROPERTY =
+ "borderPaintedFlat";
+
+ private boolean borderPaintedFlat;
+
+ private void init()
+ {
+ borderPainted = false;
+ contentAreaFilled = false;
+ }
+
+ public JCheckBox()
+ {
+ this(null, null, false);
+ }
+
+ public JCheckBox(Action action)
+ {
+ super(action);
+ }
+
+ public JCheckBox(Icon icon)
+ {
+ this(null, icon, false);
+ }
+
+ public JCheckBox(Icon icon, boolean selected)
+ {
+ this(null, icon, selected);
+ }
+
+ public JCheckBox(String text)
+ {
+ this(text, null, false);
+ }
+
+ public JCheckBox(String text, boolean selected)
+ {
+ this(text, null, selected);
+ }
+
+ public JCheckBox(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ public JCheckBox(String text, Icon icon, boolean selected)
+ {
+ super(text, icon, selected);
+ setHorizontalAlignment(LEADING);
+ setBorderPainted(false);
+ }
+
+ /**
+ * Returns a string that specifies the name of the Look and Feel class
+ * that renders this component.
+ */
+ public String getUIClassID()
+ {
+ return "CheckBoxUI";
+ }
+
+ protected String paramString()
+ {
+ return super.paramString() + ",borderPaintedFlat=" + borderPaintedFlat;
+ }
+
+ public boolean isBorderPaintedFlat()
+ {
+ return borderPaintedFlat;
+ }
+
+ public void setBorderPaintedFlat(boolean newValue)
+ {
+ firePropertyChange("borderPaintedFlat", borderPaintedFlat, newValue);
+ borderPaintedFlat = newValue;
+ }
+
+ /**
+ * Returns the accessible context for this <code>JCheckBox</code>.
+ *
+ * @return the accessible context for this <code>JCheckBox</code>
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJCheckBox();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java
new file mode 100644
index 000000000..b3045be27
--- /dev/null
+++ b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java
@@ -0,0 +1,275 @@
+/* JCheckBoxMenuItem.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 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 javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * A menu item that displays a checkbox. Its behaviour is very similar to
+ * {@link JCheckBox}. Just like the <code>JCheckBox</code>, user can check
+ * and uncheck this menu item by clicking on it. Also
+ * {@link AbstractButton#setSelected} and {@link #setState} can be use used for
+ * the same purpose. <code>JCheckBoxMenuItem</code> uses
+ * <code>ToggleButtonModel</code> to keep track of its selection.
+ *
+ * @author original author unknown
+ */
+public class JCheckBoxMenuItem
+ extends JMenuItem
+ implements SwingConstants, Accessible
+{
+ private static final long serialVersionUID = - 6676402307973384715L;
+
+ /** name for the UI delegate for this menuItem. */
+ private static final String uiClassID = "CheckBoxMenuItemUI";
+
+ /** Indicates whether this menu item is checked. */
+ private boolean state;
+
+ /**
+ * This array contains text of this menu item if this menu item is in checked
+ * state and null it is not.
+ */
+ private Object[] selectedObjects = new Object[1];
+
+ /**
+ * Creates a new JCheckBoxMenuItem object.
+ */
+ public JCheckBoxMenuItem()
+ {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem with given icon
+ *
+ * @param icon Icon for this menu item
+ */
+ public JCheckBoxMenuItem(Icon icon)
+ {
+ this(null, icon);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem with given label
+ *
+ * @param text Label for this menu item
+ */
+ public JCheckBoxMenuItem(String text)
+ {
+ this(text, null);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem using given action
+ *
+ * @param action Action for this menu item.
+ */
+ public JCheckBoxMenuItem(Action action)
+ {
+ this();
+ setAction(action);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem object with given label and icon
+ *
+ * @param text Label for this menu item
+ * @param icon Icon for this menu item
+ */
+ public JCheckBoxMenuItem(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem object using specified label and marked as
+ * checked if given 'state' is true.
+ *
+ * @param text Label for this menu item
+ * @param state <code>true</code> if this item should be in checked state
+ * and <code>false</code> otherwise
+ */
+ public JCheckBoxMenuItem(String text, boolean state)
+ {
+ this(text, null, state);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem object with given label, icon, and marked
+ * as checked if given 'state' is true.
+ *
+ * @param text Label for this menu item
+ * @param icon icon for this menu item
+ * @param state <code>true</code> if this item should be in checked state
+ * and false otherwise
+ */
+ public JCheckBoxMenuItem(String text, Icon icon, boolean state)
+ {
+ super(text, icon);
+ setModel(new JToggleButton.ToggleButtonModel());
+ this.state = state;
+ if (state == true)
+ this.setSelected(true);
+ setFocusable(false);
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menuItem.
+ *
+ * @return The Look and Feel classID. "JCheckBoxMenuItemUI"
+ */
+ public String getUIClassID()
+ {
+ return uiClassID;
+ }
+
+ /**
+ * Returns checked state for this check box menu item.
+ *
+ * @return Returns true if this menu item is in checked state and false
+ * otherwise.
+ */
+ public boolean getState()
+ {
+ return state;
+ }
+
+ /**
+ * Sets state for this check box menu item. If given 'state' is true, then
+ * mark menu item as checked, and uncheck this menu item otherwise.
+ *
+ * @param state new state for this menu item
+ */
+ public synchronized void setState(boolean state)
+ {
+ this.state = state;
+ }
+
+ /**
+ * This method returns array containing label of this menu item if it is
+ * selected and null otherwise.
+ *
+ * @return Array containing label of this menu item if this menu item is
+ * selected or null otherwise.
+ */
+ public Object[] getSelectedObjects()
+ {
+ if (state == true)
+ selectedObjects[0] = this.getText();
+ else
+ selectedObjects[0] = null;
+
+ return selectedObjects;
+ }
+
+ /**
+ * This method overrides JComponent.requestFocus with an empty implementation,
+ * since JCheckBoxMenuItems should not receive focus in general.
+ */
+ public void requestFocus()
+ {
+ // Should do nothing here
+ }
+
+ /**
+ * Returns a string describing the attributes for the
+ * <code>JCheckBoxMenuItem</code> component, for use in debugging. The
+ * return value is guaranteed to be non-<code>null</code>, but the format
+ * of the string may vary between implementations.
+ *
+ * @return A string describing the attributes of the
+ * <code>JCheckBoxMenuItem</code>.
+ */
+ protected String paramString()
+ {
+ // calling super seems to be sufficient to match the reference
+ // implementation here...
+ return super.paramString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JCheckBoxMenuItem</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJCheckBoxMenuItem}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJCheckBoxMenuItem();
+
+ return accessibleContext;
+ }
+
+ /**
+ * Provides the accessibility features for the <code>JCheckBoxMenuItem</code>
+ * component.
+ *
+ * @see JCheckBoxMenuItem#getAccessibleContext()
+ */
+ protected class AccessibleJCheckBoxMenuItem
+ extends AccessibleJMenuItem
+ {
+ private static final long serialVersionUID = 1079958073579370777L;
+
+ /**
+ * Creates a new <code>AccessibleJCheckBoxMenuItem</code> instance.
+ */
+ protected AccessibleJCheckBoxMenuItem()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the <code>JCheckBoxMenuItem</code>
+ * component.
+ *
+ * @return {@link AccessibleRole#CHECK_BOX}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.CHECK_BOX;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JColorChooser.java b/libjava/classpath/javax/swing/JColorChooser.java
new file mode 100644
index 000000000..674f0fd82
--- /dev/null
+++ b/libjava/classpath/javax/swing/JColorChooser.java
@@ -0,0 +1,644 @@
+/* JColorChooser.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTError;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dialog;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.colorchooser.AbstractColorChooserPanel;
+import javax.swing.colorchooser.ColorSelectionModel;
+import javax.swing.colorchooser.DefaultColorSelectionModel;
+import javax.swing.plaf.ColorChooserUI;
+
+
+/**
+ * A Swing widget that offers users different ways to
+ * select a color. By default, three different panels are presented to the
+ * user that are capable of changing the selected color. There are three ways
+ * to utilize JColorChooser. The first is to build a JColorChooser and add it
+ * to the content pane. The second is to use the createDialog method to
+ * create a JDialog that holds a JColorChooser. The third is to show a
+ * JColorChooser in a JDialog directly using the showDialog method.
+ *
+ * @author original author unknown
+ */
+public class JColorChooser extends JComponent implements Accessible
+{
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = 9168066781620640889L;
+
+ /**
+ * Accessibility support for <code>JColorChooser</code>.
+ */
+ protected class AccessibleJColorChooser
+ extends JComponent.AccessibleJComponent
+ {
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -2038297864782299082L;
+
+ /**
+ * Constructor AccessibleJColorChooser
+ */
+ protected AccessibleJColorChooser()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * getAccessibleRole
+ *
+ * @return AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.COLOR_CHOOSER;
+ } // getAccessibleRole()
+ } // AccessibleJColorChooser
+
+ /** The model used with the JColorChooser. */
+ private ColorSelectionModel selectionModel;
+
+ /** The preview panel associated with the JColorChooser. */
+ private JComponent previewPanel;
+
+ /**
+ * The set of AbstractColorChooserPanels associated with the JColorChooser.
+ */
+ private AbstractColorChooserPanel[] chooserPanels;
+
+ /** A Drag and Drop property. */
+ private boolean dragEnabled;
+
+ /**
+ * The property fired by the JColorChooser when the selectionModel property
+ * changes.
+ */
+ public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
+
+ /**
+ * The property fired by the JColorChooser when the previewPanel property
+ * changes.
+ */
+ public static final String PREVIEW_PANEL_PROPERTY = "previewPanel";
+
+ /**
+ * The property fired by the JColorChooser when the chooserPanels property
+ * changes.
+ */
+ public static final String CHOOSER_PANELS_PROPERTY = "chooserPanels";
+
+ /** accessibleContext */
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * This method creates a new JColorChooser with the default initial color.
+ */
+ public JColorChooser()
+ {
+ this(new DefaultColorSelectionModel());
+ } // JColorChooser()
+
+ /**
+ * This method creates a new JColorChooser with the given initial color.
+ *
+ * @param initial The initial color.
+ */
+ public JColorChooser(Color initial)
+ {
+ this(new DefaultColorSelectionModel(initial));
+ } // JColorChooser()
+
+ /**
+ * This method creates a new JColorChooser with the given model. The model
+ * will dictate what the initial color for the JColorChooser is.
+ *
+ * @param model The Model to use with the JColorChooser.
+ */
+ public JColorChooser(ColorSelectionModel model)
+ {
+ if (model == null)
+ model = new DefaultColorSelectionModel();
+ selectionModel = model;
+ updateUI();
+ } // JColorChooser()
+
+ /**
+ * This method sets the current color for the JColorChooser.
+ *
+ * @param color The new color for the JColorChooser.
+ */
+ public void setColor(Color color)
+ {
+ if (color != null)
+ selectionModel.setSelectedColor(color);
+ } // setColor()
+
+ /**
+ * This method sets the current color for the JColorChooser using RGB
+ * values.
+ *
+ * @param r The red value.
+ * @param g The green value.
+ * @param b The blue value.
+ */
+ public void setColor(int r, int g, int b)
+ {
+ selectionModel.setSelectedColor(new Color(r, g, b));
+ } // setColor()
+
+ /**
+ * This method sets the current color for the JColorChooser using the
+ * integer value. Bits 0-7 represent the blue value. Bits 8-15 represent
+ * the green value. Bits 16-23 represent the red value.
+ *
+ * @param color The new current color of the JColorChooser.
+ */
+ public void setColor(int color)
+ {
+ setColor(new Color(color, false));
+ } // setColor()
+
+ /**
+ * This method shows a JColorChooser inside a JDialog. The JDialog will
+ * block until it is hidden. The JDialog comes with three buttons: OK,
+ * Cancel, and Reset. Pressing OK or Cancel hide the JDialog. Pressing
+ * Reset will reset the JColorChooser to its initial value.
+ *
+ * @param component The Component that parents the JDialog.
+ * @param title The title displayed in the JDialog.
+ * @param initial The initial color.
+ *
+ * @return The selected color.
+ */
+ public static Color showDialog(Component component, String title,
+ Color initial)
+ {
+ JColorChooser choose = new JColorChooser(initial);
+
+ JDialog dialog = createDialog(component, title, true, choose, null, null);
+
+ dialog.getContentPane().add(choose);
+ dialog.pack();
+ dialog.show();
+
+ return choose.getColor();
+ } // showDialog()
+
+ /**
+ * This is a helper method to make the given JDialog block until it is
+ * hidden. This is package-private to avoid an accessor method.
+ *
+ * @param dialog The JDialog to block.
+ */
+ static void makeModal(JDialog dialog)
+ {
+ try
+ {
+ synchronized (dialog)
+ {
+ while (dialog.isVisible())
+ dialog.wait();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // TODO: Should this be handled?
+ }
+ }
+
+ /**
+ * This is a helper method to find the first Frame or Dialog ancestor of the
+ * given Component.
+ *
+ * @param c The Component to find ancestors for.
+ *
+ * @return A Frame or Dialog ancestor. Null if none are found.
+ */
+ private static Component findParent(Component c)
+ {
+ Component parent = SwingUtilities.getAncestorOfClass(Frame.class, c);
+ if (parent != null)
+ return parent;
+ parent = SwingUtilities.getAncestorOfClass(Dialog.class, c);
+ return parent;
+ }
+
+ /**
+ * This method will take the given JColorChooser and place it in a JDialog
+ * with the given modal property. Three buttons are displayed in the
+ * JDialog: OK, Cancel and Reset. If OK or Cancel are pressed, the JDialog
+ * is hidden. If Reset is pressed, then the JColorChooser will take on its
+ * default color value. The given okListener will be registered to the OK
+ * button and the cancelListener will be registered to the Cancel button.
+ * If the modal property is set, then the JDialog will block until it is
+ * hidden.
+ *
+ * @param component The Component that will parent the JDialog.
+ * @param title The title displayed in the JDialog.
+ * @param modal The modal property.
+ * @param chooserPane The JColorChooser to place in the JDialog.
+ * @param okListener The ActionListener to register to the OK button.
+ * @param cancelListener The ActionListener to register to the Cancel
+ * button.
+ *
+ * @return A JDialog with the JColorChooser inside of it.
+ *
+ * @throws AWTError If the component is not a suitable parent.
+ */
+ public static JDialog createDialog(Component component, String title,
+ boolean modal, JColorChooser chooserPane,
+ ActionListener okListener,
+ ActionListener cancelListener)
+ {
+ Component parent = findParent(component);
+ if (parent == null)
+ throw new AWTError("No suitable parent found for Component.");
+ JDialog dialog;
+ if (parent instanceof Frame)
+ dialog = new JDialog((Frame) parent, title, true);
+ else
+ dialog = new JDialog((Dialog) parent, title, true);
+
+ dialog.getContentPane().setLayout(new BorderLayout());
+
+ JPanel panel = new JPanel();
+ panel.setLayout(new FlowLayout());
+
+ ActionListener al = new DefaultOKCancelListener(dialog);
+
+ JButton ok = new JButton("OK");
+ ok.addActionListener(okListener);
+ ok.addActionListener(al);
+
+ JButton cancel = new JButton("Cancel");
+ cancel.addActionListener(cancelListener);
+ cancel.addActionListener(al);
+
+ JButton reset = new JButton("Reset");
+ reset.addActionListener(new DefaultResetListener(chooserPane));
+
+ dialog.getContentPane().add(chooserPane, BorderLayout.NORTH);
+
+ panel.add(ok);
+ panel.add(cancel);
+ panel.add(reset);
+
+ dialog.getContentPane().add(panel, BorderLayout.SOUTH);
+
+ return dialog;
+ } // createDialog()
+
+ /**
+ * This method returns the UI Component used for this JColorChooser.
+ *
+ * @return The UI Component for this JColorChooser.
+ */
+ public ColorChooserUI getUI()
+ {
+ return (ColorChooserUI) ui;
+ } // getUI()
+
+ /**
+ * This method sets the UI Component used for this JColorChooser.
+ *
+ * @param ui The UI Component to use with this JColorChooser.
+ */
+ public void setUI(ColorChooserUI ui)
+ {
+ super.setUI(ui);
+ } // setUI()
+
+ /**
+ * This method resets the UI Component property to the Look and Feel
+ * default.
+ */
+ public void updateUI()
+ {
+ setUI((ColorChooserUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a String identifier for the UI Class to be used with
+ * the JColorChooser.
+ *
+ * @return The String identifier for the UI Class.
+ */
+ public String getUIClassID()
+ {
+ return "ColorChooserUI";
+ } // getUIClassID()
+
+ /**
+ * This method returns the current color for the JColorChooser.
+ *
+ * @return The current color for the JColorChooser.
+ */
+ public Color getColor()
+ {
+ return selectionModel.getSelectedColor(); // TODO
+ } // getColor()
+
+ /**
+ * This method changes the previewPanel property for the JTabbedPane. The
+ * previewPanel is responsible for indicating the current color of the
+ * JColorChooser.
+ *
+ * @param component The Component that will act as the previewPanel.
+ */
+ public void setPreviewPanel(JComponent component)
+ {
+ if (component != previewPanel)
+ {
+ JComponent old = previewPanel;
+ previewPanel = component;
+ firePropertyChange(PREVIEW_PANEL_PROPERTY, old, previewPanel);
+ }
+ } // setPreviewPanel()
+
+ /**
+ * This method returns the current previewPanel used with this
+ * JColorChooser.
+ *
+ * @return The current previewPanel.
+ */
+ public JComponent getPreviewPanel()
+ {
+ return previewPanel; // TODO
+ } // getPreviewPanel()
+
+ /**
+ * This method adds the given AbstractColorChooserPanel to the list of the
+ * JColorChooser's chooserPanels.
+ *
+ * @param panel The AbstractColorChooserPanel to add.
+ */
+ public void addChooserPanel(AbstractColorChooserPanel panel)
+ {
+ if (panel == null)
+ return;
+ AbstractColorChooserPanel[] old = chooserPanels;
+ AbstractColorChooserPanel[] newPanels =
+ new AbstractColorChooserPanel[(old == null) ? 1 : old.length + 1];
+ if (old != null)
+ System.arraycopy(old, 0, newPanels, 0, old.length);
+ newPanels[newPanels.length - 1] = panel;
+ chooserPanels = newPanels;
+ panel.installChooserPanel(this);
+ firePropertyChange(CHOOSER_PANELS_PROPERTY, old, newPanels);
+ } // addChooserPanel()
+
+ /**
+ * This method removes the given AbstractColorChooserPanel from the
+ * JColorChooser's list of chooserPanels.
+ *
+ * @param panel The AbstractColorChooserPanel to remove.
+ *
+ * @return The AbstractColorChooserPanel that was removed.
+ */
+ public AbstractColorChooserPanel removeChooserPanel(AbstractColorChooserPanel panel)
+ {
+ int index = -1;
+ for (int i = 0; i < chooserPanels.length; i++)
+ if (panel == chooserPanels[i])
+ {
+ index = i;
+ break;
+ }
+
+ if (index == -1)
+ return null;
+
+ AbstractColorChooserPanel[] old = chooserPanels;
+ if (chooserPanels.length == 1)
+ chooserPanels = null;
+ else
+ {
+ AbstractColorChooserPanel[] newPanels =
+ new AbstractColorChooserPanel[chooserPanels.length - 1];
+ System.arraycopy(chooserPanels, 0, newPanels, 0, index);
+ System.arraycopy(chooserPanels, index, newPanels, index - 1,
+ chooserPanels.length - index);
+ chooserPanels = newPanels;
+ }
+ panel.uninstallChooserPanel(this);
+ firePropertyChange(CHOOSER_PANELS_PROPERTY, old, chooserPanels);
+ return panel;
+ }
+
+ /**
+ * This method sets the chooserPanels property for this JColorChooser.
+ *
+ * @param panels The new set of AbstractColorChooserPanels to use.
+ */
+ public void setChooserPanels(AbstractColorChooserPanel[] panels)
+ {
+ if (panels != chooserPanels)
+ {
+ if (chooserPanels != null)
+ for (int i = 0; i < chooserPanels.length; i++)
+ if (chooserPanels[i] != null)
+ chooserPanels[i].uninstallChooserPanel(this);
+
+ AbstractColorChooserPanel[] old = chooserPanels;
+ chooserPanels = panels;
+
+ if (panels != null)
+ for (int i = 0; i < panels.length; i++)
+ if (panels[i] != null)
+ panels[i].installChooserPanel(this);
+
+ firePropertyChange(CHOOSER_PANELS_PROPERTY, old, chooserPanels);
+ }
+ } // setChooserPanels()
+
+ /**
+ * This method returns the AbstractColorChooserPanels used with this
+ * JColorChooser.
+ *
+ * @return The AbstractColorChooserPanels used with this JColorChooser.
+ */
+ public AbstractColorChooserPanel[] getChooserPanels()
+ {
+ return chooserPanels;
+ } // getChooserPanels()
+
+ /**
+ * This method returns the ColorSelectionModel used with this JColorChooser.
+ *
+ * @return The ColorSelectionModel.
+ */
+ public ColorSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ } // getSelectionModel()
+
+ /**
+ * This method sets the ColorSelectionModel to be used with this
+ * JColorChooser.
+ *
+ * @param model The ColorSelectionModel to be used with this JColorChooser.
+ *
+ * @throws AWTError If the given model is null.
+ */
+ public void setSelectionModel(ColorSelectionModel model)
+ {
+ if (model == null)
+ throw new AWTError("ColorSelectionModel is not allowed to be null.");
+ selectionModel = model;
+ } // setSelectionModel()
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param b DOCUMENT ME!
+ */
+ public void setDragEnabled(boolean b)
+ {
+ dragEnabled = b;
+ }
+
+ /**
+ * This method returns a String describing the JColorChooser.
+ *
+ * @return A String describing the JColorChooser.
+ */
+ protected String paramString()
+ {
+ return "JColorChooser";
+ } // paramString()
+
+ /**
+ * getAccessibleContext
+ *
+ * @return AccessibleContext
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJColorChooser();
+
+ return accessibleContext;
+ }
+
+ /**
+ * A helper class that hides a JDialog when the action is performed.
+ */
+ static class DefaultOKCancelListener implements ActionListener
+ {
+ /** The JDialog to hide. */
+ private JDialog dialog;
+
+ /**
+ * Creates a new DefaultOKCancelListener with the given JDialog to hide.
+ *
+ * @param dialog The JDialog to hide.
+ */
+ public DefaultOKCancelListener(JDialog dialog)
+ {
+ super();
+ this.dialog = dialog;
+ }
+
+ /**
+ * This method hides the JDialog when called.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ dialog.hide();
+ }
+ }
+
+ /**
+ * This method resets the JColorChooser color to the initial color when the
+ * action is performed.
+ */
+ static class DefaultResetListener implements ActionListener
+ {
+ /** The JColorChooser to reset. */
+ private JColorChooser chooser;
+
+ /** The initial color. */
+ private Color init;
+
+ /**
+ * Creates a new DefaultResetListener with the given JColorChooser.
+ *
+ * @param chooser The JColorChooser to reset.
+ */
+ public DefaultResetListener(JColorChooser chooser)
+ {
+ super();
+ this.chooser = chooser;
+ init = chooser.getColor();
+ }
+
+ /**
+ * This method resets the JColorChooser to its initial color.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ chooser.setColor(init);
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JComboBox.java b/libjava/classpath/javax/swing/JComboBox.java
new file mode 100644
index 000000000..0d2be0c17
--- /dev/null
+++ b/libjava/classpath/javax/swing/JComboBox.java
@@ -0,0 +1,1499 @@
+/* JComboBox.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.ItemSelectable;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.plaf.ComboBoxUI;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.ComboPopup;
+
+/**
+ * A component that allows a user to select any item in its list and
+ * displays the selected item to the user. JComboBox also can show/hide a
+ * popup menu containing its list of item whenever the mouse is pressed
+ * over it.
+ *
+ * @author Andrew Selkirk
+ * @author Olga Rodimina
+ * @author Robert Schuster
+ */
+public class JComboBox extends JComponent implements ItemSelectable,
+ ListDataListener,
+ ActionListener,
+ Accessible
+{
+
+ private static final long serialVersionUID = 5654585963292734470L;
+
+ /**
+ * Classes implementing this interface are
+ * responsible for matching key characters typed by the user with combo
+ * box's items.
+ */
+ public static interface KeySelectionManager
+ {
+ int selectionForKey(char aKey, ComboBoxModel aModel);
+ }
+
+ /**
+ * Maximum number of rows that should be visible by default in the
+ * JComboBox's popup
+ */
+ private static final int DEFAULT_MAXIMUM_ROW_COUNT = 8;
+
+ /**
+ * Data model used by JComboBox to keep track of its list data and currently
+ * selected element in the list.
+ */
+ protected ComboBoxModel dataModel;
+
+ /**
+ * Renderer renders(paints) every object in the combo box list in its
+ * associated list cell. This ListCellRenderer is used only when this
+ * JComboBox is uneditable.
+ */
+ protected ListCellRenderer renderer;
+
+ /**
+ * Editor that is responsible for editing an object in a combo box list.
+ */
+ protected ComboBoxEditor editor;
+
+ /**
+ * Number of rows that will be visible in the JComboBox's popup.
+ */
+ protected int maximumRowCount;
+
+ /**
+ * This field indicates if textfield of this JComboBox is editable or not.
+ */
+ protected boolean isEditable;
+
+ /**
+ * This field is reference to the current selection of the combo box.
+ */
+ protected Object selectedItemReminder;
+
+ /**
+ * keySelectionManager
+ */
+ protected KeySelectionManager keySelectionManager;
+
+ /**
+ * This actionCommand is used in ActionEvent that is fired to JComboBox's
+ * ActionListeneres.
+ */
+ protected String actionCommand;
+
+ /**
+ * This property indicates if heavyweight popup or lightweight popup will be
+ * used to diplay JComboBox's elements.
+ */
+ protected boolean lightWeightPopupEnabled;
+
+ /**
+ * The action taken when new item is selected in the JComboBox
+ */
+ private Action action;
+
+ /**
+ * since 1.4 If this field is set then comboBox's display area for the
+ * selected item will be set by default to this value.
+ */
+ private Object prototypeDisplayValue;
+
+ /**
+ * Constructs JComboBox object with specified data model for it.
+ * <p>Note that the JComboBox will not change the value that
+ * is preselected by your ComboBoxModel implementation.</p>
+ *
+ * @param model Data model that will be used by this JComboBox to keep track
+ * of its list of items.
+ */
+ public JComboBox(ComboBoxModel model)
+ {
+ setEditable(false);
+ setEnabled(true);
+ setMaximumRowCount(DEFAULT_MAXIMUM_ROW_COUNT);
+ setModel(model);
+ setActionCommand("comboBoxChanged");
+
+ lightWeightPopupEnabled = true;
+ isEditable = false;
+
+ updateUI();
+ }
+
+ /**
+ * Constructs JComboBox with specified list of items.
+ *
+ * @param itemArray array containing list of items for this JComboBox
+ */
+ public JComboBox(Object[] itemArray)
+ {
+ this(new DefaultComboBoxModel(itemArray));
+
+ if (itemArray.length > 0)
+ setSelectedIndex(0);
+ }
+
+ /**
+ * Constructs JComboBox object with specified list of items.
+ *
+ * @param itemVector vector containing list of items for this JComboBox.
+ */
+ public JComboBox(Vector<?> itemVector)
+ {
+ this(new DefaultComboBoxModel(itemVector));
+
+ if (itemVector.size() > 0)
+ setSelectedIndex(0);
+ }
+
+ /**
+ * Constructor. Creates new empty JComboBox. ComboBox's data model is set to
+ * DefaultComboBoxModel.
+ */
+ public JComboBox()
+ {
+ this(new DefaultComboBoxModel());
+ }
+
+ /**
+ * This method returns true JComboBox is editable and false otherwise
+ *
+ * @return boolean true if JComboBox is editable and false otherwise
+ */
+ public boolean isEditable()
+ {
+ return isEditable;
+ }
+
+ /*
+ * This method adds ancestor listener to this JComboBox.
+ */
+ protected void installAncestorListener()
+ {
+ /* FIXME: Need to implement.
+ *
+ * Need to add ancestor listener to this JComboBox. This listener
+ * should close combo box's popup list of items whenever it
+ * receives an AncestorEvent.
+ */
+ }
+
+ /**
+ * Set the "UI" property of the combo box, which is a look and feel class
+ * responsible for handling comboBox's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(ComboBoxUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets this comboBox's UI to the UIManager's default for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ComboBoxUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the String identifier for the UI class to the used
+ * with the JComboBox.
+ *
+ * @return The String identifier for the UI class.
+ */
+ public String getUIClassID()
+ {
+ return "ComboBoxUI";
+ }
+
+ /**
+ * This method returns the UI used to display the JComboBox.
+ *
+ * @return The UI used to display the JComboBox.
+ */
+ public ComboBoxUI getUI()
+ {
+ return (ComboBoxUI) ui;
+ }
+
+ /**
+ * Set the data model for this JComboBox. This un-registers all listeners
+ * associated with the current model, and re-registers them with the new
+ * model.
+ *
+ * @param newDataModel The new data model for this JComboBox
+ */
+ public void setModel(ComboBoxModel newDataModel)
+ {
+ // dataModel is null if it this method is called from inside the constructors.
+ if (dataModel != null)
+ {
+ // Prevents unneccessary updates.
+ if (dataModel == newDataModel)
+ return;
+
+ // Removes itself (as DataListener) from the to-be-replaced model.
+ dataModel.removeListDataListener(this);
+ }
+
+ /* Adds itself as a DataListener to the new model.
+ * It is intentioned that this operation will fail with a NullPointerException if the
+ * caller delivered a null argument.
+ */
+ newDataModel.addListDataListener(this);
+
+ // Stores old data model for event notification.
+ ComboBoxModel oldDataModel = dataModel;
+ dataModel = newDataModel;
+ selectedItemReminder = newDataModel.getSelectedItem();
+
+ // Notifies the listeners of the model change.
+ firePropertyChange("model", oldDataModel, dataModel);
+ }
+
+ /**
+ * This method returns data model for this comboBox.
+ *
+ * @return ComboBoxModel containing items for this combo box.
+ */
+ public ComboBoxModel getModel()
+ {
+ return dataModel;
+ }
+
+ /**
+ * This method sets JComboBox's popup to be either lightweight or
+ * heavyweight. If 'enabled' is true then lightweight popup is used and
+ * heavyweight otherwise. By default lightweight popup is used to display
+ * this JComboBox's elements.
+ *
+ * @param enabled indicates if lightweight popup or heavyweight popup should
+ * be used to display JComboBox's elements.
+ */
+ public void setLightWeightPopupEnabled(boolean enabled)
+ {
+ lightWeightPopupEnabled = enabled;
+ }
+
+ /**
+ * This method returns whether popup menu that is used to display list of
+ * combo box's item is lightWeight or not.
+ *
+ * @return boolean true if popup menu is lightweight and false otherwise.
+ */
+ public boolean isLightWeightPopupEnabled()
+ {
+ return lightWeightPopupEnabled;
+ }
+
+ /**
+ * This method sets editability of the combo box. If combo box is editable
+ * the user can choose component from the combo box list by typing
+ * component's name in the editor(JTextfield by default). Otherwise if not
+ * editable, the user should use the list to choose the component. This
+ * method fires PropertyChangeEvents to JComboBox's registered
+ * PropertyChangeListeners to indicate that 'editable' property of the
+ * JComboBox has changed.
+ *
+ * @param editable indicates if the JComboBox's textfield should be editable
+ * or not.
+ */
+ public void setEditable(boolean editable)
+ {
+ if (isEditable != editable)
+ {
+ isEditable = editable;
+ firePropertyChange("editable", !isEditable, isEditable);
+ }
+ }
+
+ /**
+ * Sets number of rows that should be visible in this JComboBox's popup. If
+ * this JComboBox's popup has more elements that maximum number or rows
+ * then popup will have a scroll pane to allow users to view other
+ * elements.
+ *
+ * @param rowCount number of rows that will be visible in JComboBox's popup.
+ */
+ public void setMaximumRowCount(int rowCount)
+ {
+ if (maximumRowCount != rowCount)
+ {
+ int oldMaximumRowCount = maximumRowCount;
+ maximumRowCount = rowCount;
+ firePropertyChange("maximumRowCount", oldMaximumRowCount,
+ maximumRowCount);
+ }
+ }
+
+ /**
+ * This method returns number of rows visible in the JComboBox's list of
+ * items.
+ *
+ * @return int maximun number of visible rows in the JComboBox's list.
+ */
+ public int getMaximumRowCount()
+ {
+ return maximumRowCount;
+ }
+
+ /**
+ * This method sets cell renderer for this JComboBox that will be used to
+ * paint combo box's items. The Renderer should only be used only when
+ * JComboBox is not editable. In the case when JComboBox is editable the
+ * editor must be used. This method also fires PropertyChangeEvent when
+ * cellRendered for this JComboBox has changed.
+ *
+ * @param aRenderer cell renderer that will be used by this JComboBox to
+ * paint its elements.
+ */
+ public void setRenderer(ListCellRenderer aRenderer)
+ {
+ if (renderer != aRenderer)
+ {
+ ListCellRenderer oldRenderer = renderer;
+ renderer = aRenderer;
+ firePropertyChange("renderer", oldRenderer, renderer);
+ }
+ }
+
+ /**
+ * This method returns renderer responsible for rendering selected item in
+ * the combo box
+ *
+ * @return ListCellRenderer
+ */
+ public ListCellRenderer getRenderer()
+ {
+ return renderer;
+ }
+
+ /**
+ * Sets editor for this JComboBox
+ *
+ * @param newEditor ComboBoxEditor for this JComboBox. This method fires
+ * PropertyChangeEvent when 'editor' property is changed.
+ */
+ public void setEditor(ComboBoxEditor newEditor)
+ {
+ if (editor == newEditor)
+ return;
+
+ if (editor != null)
+ editor.removeActionListener(this);
+
+ ComboBoxEditor oldEditor = editor;
+ editor = newEditor;
+
+ if (editor != null)
+ editor.addActionListener(this);
+
+ firePropertyChange("editor", oldEditor, editor);
+ }
+
+ /**
+ * Returns editor component that is responsible for displaying/editing
+ * selected item in the combo box.
+ *
+ * @return ComboBoxEditor
+ */
+ public ComboBoxEditor getEditor()
+ {
+ return editor;
+ }
+
+ /**
+ * Forces combo box to select given item
+ *
+ * @param item element in the combo box to select.
+ */
+ public void setSelectedItem(Object item)
+ {
+ dataModel.setSelectedItem(item);
+ fireActionEvent();
+ }
+
+ /**
+ * Returns currently selected item in the combo box.
+ * The result may be <code>null</code> to indicate that nothing is
+ * currently selected.
+ *
+ * @return element that is currently selected in this combo box.
+ */
+ public Object getSelectedItem()
+ {
+ return dataModel.getSelectedItem();
+ }
+
+ /**
+ * Forces JComboBox to select component located in the given index in the
+ * combo box.
+ * <p>If the index is below -1 or exceeds the upper bound an
+ * <code>IllegalArgumentException</code> is thrown.<p/>
+ * <p>If the index is -1 then no item gets selected.</p>
+ *
+ * @param index index specifying location of the component that should be
+ * selected.
+ */
+ public void setSelectedIndex(int index)
+ {
+ if (index < -1 || index >= dataModel.getSize())
+ // Fails because index is out of bounds.
+ throw new IllegalArgumentException("illegal index: " + index);
+ else
+ // Selects the item at the given index or clears the selection if the
+ // index value is -1.
+ setSelectedItem((index == -1) ? null : dataModel.getElementAt(index));
+ }
+
+ /**
+ * Returns index of the item that is currently selected in the combo box. If
+ * no item is currently selected, then -1 is returned.
+ * <p>
+ * Note: For performance reasons you should minimize invocation of this
+ * method. If the data model is not an instance of
+ * <code>DefaultComboBoxModel</code> the complexity is O(n) where n is the
+ * number of elements in the combo box.
+ * </p>
+ *
+ * @return int Index specifying location of the currently selected item in the
+ * combo box or -1 if nothing is selected in the combo box.
+ */
+ public int getSelectedIndex()
+ {
+ Object selectedItem = getSelectedItem();
+
+ if (selectedItem != null)
+ {
+ if (dataModel instanceof DefaultComboBoxModel)
+ // Uses special method of DefaultComboBoxModel to retrieve the index.
+ return ((DefaultComboBoxModel) dataModel).getIndexOf(selectedItem);
+ else
+ {
+ // Iterates over all items to retrieve the index.
+ int size = dataModel.getSize();
+
+ for (int i = 0; i < size; i++)
+ {
+ Object o = dataModel.getElementAt(i);
+
+ // XXX: Is special handling of ComparableS neccessary?
+ if ((selectedItem != null) ? selectedItem.equals(o) : o == null)
+ return i;
+ }
+ }
+ }
+
+ // returns that no item is currently selected
+ return -1;
+ }
+
+ /**
+ * Returns an object that is used as the display value when calculating the
+ * preferred size for the combo box. This value is, of course, never
+ * displayed anywhere.
+ *
+ * @return The prototype display value (possibly <code>null</code>).
+ *
+ * @since 1.4
+ * @see #setPrototypeDisplayValue(Object)
+ */
+ public Object getPrototypeDisplayValue()
+ {
+ return prototypeDisplayValue;
+ }
+
+ /**
+ * Sets the object that is assumed to be the displayed item when calculating
+ * the preferred size for the combo box. A {@link PropertyChangeEvent} (with
+ * the name <code>prototypeDisplayValue</code>) is sent to all registered
+ * listeners.
+ *
+ * @param value the new value (<code>null</code> permitted).
+ *
+ * @since 1.4
+ * @see #getPrototypeDisplayValue()
+ */
+ public void setPrototypeDisplayValue(Object value)
+ {
+ Object oldValue = prototypeDisplayValue;
+ prototypeDisplayValue = value;
+ firePropertyChange("prototypeDisplayValue", oldValue, value);
+ }
+
+ /**
+ * This method adds given element to this JComboBox.
+ * <p>A <code>RuntimeException</code> is thrown if the data model is not
+ * an instance of {@link MutableComboBoxModel}.</p>
+ *
+ * @param element element to add
+ */
+ public void addItem(Object element)
+ {
+ if (dataModel instanceof MutableComboBoxModel)
+ ((MutableComboBoxModel) dataModel).addElement(element);
+ else
+ throw new RuntimeException("Unable to add the item because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * Inserts given element at the specified index to this JComboBox.
+ * <p>A <code>RuntimeException</code> is thrown if the data model is not
+ * an instance of {@link MutableComboBoxModel}.</p>
+ *
+ * @param element element to insert
+ * @param index position where to insert the element
+ */
+ public void insertItemAt(Object element, int index)
+ {
+ if (dataModel instanceof MutableComboBoxModel)
+ ((MutableComboBoxModel) dataModel).insertElementAt(element, index);
+ else
+ throw new RuntimeException("Unable to insert the item because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * This method removes given element from this JComboBox.
+ * <p>A <code>RuntimeException</code> is thrown if the data model is not
+ * an instance of {@link MutableComboBoxModel}.</p>
+ *
+ * @param element element to remove
+ */
+ public void removeItem(Object element)
+ {
+ if (dataModel instanceof MutableComboBoxModel)
+ ((MutableComboBoxModel) dataModel).removeElement(element);
+ else
+ throw new RuntimeException("Unable to remove the item because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * This method remove element location in the specified index in the
+ * JComboBox.
+ * <p>A <code>RuntimeException</code> is thrown if the data model is not
+ * an instance of {@link MutableComboBoxModel}.</p>
+ *
+ * @param index index specifying position of the element to remove
+ */
+ public void removeItemAt(int index)
+ {
+ if (dataModel instanceof MutableComboBoxModel)
+ ((MutableComboBoxModel) dataModel).removeElementAt(index);
+ else
+ throw new RuntimeException("Unable to remove the item because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * This method removes all elements from this JComboBox.
+ * <p>
+ * A <code>RuntimeException</code> is thrown if the data model is not an
+ * instance of {@link MutableComboBoxModel}.
+ * </p>
+ */
+ public void removeAllItems()
+ {
+ if (dataModel instanceof DefaultComboBoxModel)
+ // Uses special method if we have a DefaultComboBoxModel.
+ ((DefaultComboBoxModel) dataModel).removeAllElements();
+ else if (dataModel instanceof MutableComboBoxModel)
+ {
+ // Iterates over all items and removes each.
+ MutableComboBoxModel mcbm = (MutableComboBoxModel) dataModel;
+
+ // We intentionally remove the items backwards to support models which
+ // shift their content to the beginning (e.g. linked lists)
+ for (int i = mcbm.getSize() - 1; i >= 0; i--)
+ mcbm.removeElementAt(i);
+ }
+ else
+ throw new RuntimeException("Unable to remove the items because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * This method displays popup with list of combo box's items on the screen
+ */
+ public void showPopup()
+ {
+ setPopupVisible(true);
+ }
+
+ /**
+ * This method hides popup containing list of combo box's items
+ */
+ public void hidePopup()
+ {
+ setPopupVisible(false);
+ }
+
+ /**
+ * This method either displayes or hides the popup containing list of combo
+ * box's items.
+ *
+ * @param visible show popup if 'visible' is true and hide it otherwise
+ */
+ public void setPopupVisible(boolean visible)
+ {
+ getUI().setPopupVisible(this, visible);
+ }
+
+ /**
+ * Checks if popup is currently visible on the screen.
+ *
+ * @return boolean true if popup is visible and false otherwise
+ */
+ public boolean isPopupVisible()
+ {
+ return getUI().isPopupVisible(this);
+ }
+
+ /**
+ * This method sets actionCommand to the specified string. ActionEvent fired
+ * to this JComboBox registered ActionListeners will contain this
+ * actionCommand.
+ *
+ * @param aCommand new action command for the JComboBox's ActionEvent
+ */
+ public void setActionCommand(String aCommand)
+ {
+ actionCommand = aCommand;
+ }
+
+ /**
+ * Returns actionCommand associated with the ActionEvent fired by the
+ * JComboBox to its registered ActionListeners.
+ *
+ * @return String actionCommand for the ActionEvent
+ */
+ public String getActionCommand()
+ {
+ return actionCommand;
+ }
+
+ /**
+ * setAction
+ *
+ * @param a action to set
+ */
+ public void setAction(Action a)
+ {
+ Action old = action;
+ action = a;
+ configurePropertiesFromAction(action);
+ if (action != null)
+ // FIXME: remove from old action and add to new action
+ // PropertyChangeListener to listen to changes in the action
+ addActionListener(action);
+ }
+
+ /**
+ * This method returns Action that is invoked when selected item is changed
+ * in the JComboBox.
+ *
+ * @return Action
+ */
+ public Action getAction()
+ {
+ return action;
+ }
+
+ /**
+ * Configure properties of the JComboBox by reading properties of specified
+ * action. This method always sets the comboBox's "enabled" property to the
+ * value of the Action's "enabled" property.
+ *
+ * @param a An Action to configure the combo box from
+ */
+ protected void configurePropertiesFromAction(Action a)
+ {
+ if (a == null)
+ {
+ setEnabled(true);
+ setToolTipText(null);
+ }
+ else
+ {
+ setEnabled(a.isEnabled());
+ setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
+ }
+ }
+
+ /**
+ * Creates PropertyChangeListener to listen for the changes in comboBox's
+ * action properties.
+ *
+ * @param action action to listen to for property changes
+ *
+ * @return a PropertyChangeListener that listens to changes in
+ * action properties.
+ */
+ protected PropertyChangeListener createActionPropertyChangeListener(Action action)
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ Action act = (Action) (e.getSource());
+ configurePropertiesFromAction(act);
+ }
+ };
+ }
+
+ /**
+ * This method fires ItemEvent to this JComboBox's registered ItemListeners.
+ * This method is invoked when currently selected item in this combo box
+ * has changed.
+ *
+ * @param e the ItemEvent describing the change in the combo box's
+ * selection.
+ */
+ protected void fireItemStateChanged(ItemEvent e)
+ {
+ ItemListener[] ll = getItemListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].itemStateChanged(e);
+ }
+
+ /**
+ * This method fires ActionEvent to this JComboBox's registered
+ * ActionListeners. This method is invoked when user explicitly changes
+ * currently selected item.
+ */
+ protected void fireActionEvent()
+ {
+ ActionListener[] ll = getActionListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].actionPerformed(new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED,
+ actionCommand));
+ }
+
+ /**
+ * Fires a popupMenuCanceled() event to all <code>PopupMenuListeners</code>.
+ *
+ * Note: This method is intended for use by plaf classes only.
+ */
+ public void firePopupMenuCanceled()
+ {
+ PopupMenuListener[] listeners = getPopupMenuListeners();
+ PopupMenuEvent e = new PopupMenuEvent(this);
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].popupMenuCanceled(e);
+ }
+
+ /**
+ * Fires a popupMenuWillBecomeInvisible() event to all
+ * <code>PopupMenuListeners</code>.
+ *
+ * Note: This method is intended for use by plaf classes only.
+ */
+ public void firePopupMenuWillBecomeInvisible()
+ {
+ PopupMenuListener[] listeners = getPopupMenuListeners();
+ PopupMenuEvent e = new PopupMenuEvent(this);
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].popupMenuWillBecomeInvisible(e);
+ }
+
+ /**
+ * Fires a popupMenuWillBecomeVisible() event to all
+ * <code>PopupMenuListeners</code>.
+ *
+ * Note: This method is intended for use by plaf classes only.
+ */
+ public void firePopupMenuWillBecomeVisible()
+ {
+ PopupMenuListener[] listeners = getPopupMenuListeners();
+ PopupMenuEvent e = new PopupMenuEvent(this);
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].popupMenuWillBecomeVisible(e);
+ }
+
+ /**
+ * This method is invoked whenever selected item changes in the combo box's
+ * data model. It fires ItemEvent and ActionEvent to all registered
+ * ComboBox's ItemListeners and ActionListeners respectively, indicating
+ * the change.
+ */
+ protected void selectedItemChanged()
+ {
+ // Fire ItemEvent to indicated that previously selected item is now
+ // deselected
+ if (selectedItemReminder != null)
+ fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
+ selectedItemReminder,
+ ItemEvent.DESELECTED));
+
+ // Fire ItemEvent to indicate that new item is selected
+ Object newSelection = getSelectedItem();
+ if (newSelection != null)
+ fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
+ newSelection, ItemEvent.SELECTED));
+
+ // Fire Action Event to JComboBox's registered listeners
+ fireActionEvent();
+
+ selectedItemReminder = newSelection;
+ }
+
+ /**
+ * Returns Object array of size 1 containing currently selected element in
+ * the JComboBox.
+ *
+ * @return Object[] Object array of size 1 containing currently selected
+ * element in the JComboBox.
+ */
+ public Object[] getSelectedObjects()
+ {
+ return new Object[] { getSelectedItem() };
+ }
+
+ /**
+ * This method handles actionEvents fired by the ComboBoxEditor. It changes
+ * this JComboBox's selection to the new value currently in the editor and
+ * hides list of combo box items.
+ *
+ * @param e the ActionEvent
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ setSelectedItem(getEditor().getItem());
+ setPopupVisible(false);
+ }
+
+ /**
+ * This method selects item in this combo box that matches specified
+ * specified keyChar and returns true if such item is found. Otherwise
+ * false is returned.
+ *
+ * @param keyChar character indicating which item in the combo box should be
+ * selected.
+ *
+ * @return boolean true if item corresponding to the specified keyChar
+ * exists in the combo box. Otherwise false is returned.
+ */
+ public boolean selectWithKeyChar(char keyChar)
+ {
+ if (keySelectionManager == null)
+ {
+ keySelectionManager = createDefaultKeySelectionManager();
+ }
+
+ int index = keySelectionManager.selectionForKey(keyChar, getModel());
+ boolean retVal = false;
+ if (index >= 0)
+ {
+ setSelectedIndex(index);
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * The part of implementation of ListDataListener interface. This method is
+ * invoked when some items where added to the JComboBox's data model.
+ *
+ * @param event ListDataEvent describing the change
+ */
+ public void intervalAdded(ListDataEvent event)
+ {
+ // FIXME: Need to implement
+ repaint();
+ }
+
+ /**
+ * The part of implementation of ListDataListener interface. This method is
+ * invoked when some items where removed from the JComboBox's data model.
+ *
+ * @param event ListDataEvent describing the change.
+ */
+ public void intervalRemoved(ListDataEvent event)
+ {
+ // FIXME: Need to implement
+ repaint();
+ }
+
+ /**
+ * The part of implementation of ListDataListener interface. This method is
+ * invoked when contents of the JComboBox's data model changed.
+ *
+ * @param event ListDataEvent describing the change
+ */
+ public void contentsChanged(ListDataEvent event)
+ {
+ // if first and last index of the given ListDataEvent are both -1,
+ // then it indicates that selected item in the combo box data model
+ // have changed.
+ if (event.getIndex0() == -1 && event.getIndex1() == -1)
+ selectedItemChanged();
+ }
+
+ /**
+ * This method disables or enables JComboBox. If the JComboBox is enabled,
+ * then user is able to make item choice, otherwise if JComboBox is
+ * disabled then user is not able to make a selection.
+ *
+ * @param enabled if 'enabled' is true then enable JComboBox and disable it
+ */
+ public void setEnabled(boolean enabled)
+ {
+ boolean oldEnabled = super.isEnabled();
+ if (enabled != oldEnabled)
+ {
+ super.setEnabled(enabled);
+ firePropertyChange("enabled", oldEnabled, enabled);
+ }
+ }
+
+ /**
+ * This method initializes specified ComboBoxEditor to display given item.
+ *
+ * @param anEditor ComboBoxEditor to initialize
+ * @param anItem Item that should displayed in the specified editor
+ */
+ public void configureEditor(ComboBoxEditor anEditor, Object anItem)
+ {
+ anEditor.setItem(anItem);
+ }
+
+ /**
+ * This method is fired whenever a key is pressed with the combo box
+ * in focus
+ *
+ * @param e The KeyEvent indicating which key was pressed.
+ */
+ public void processKeyEvent(KeyEvent e)
+ {
+ if (e.getKeyCode() == KeyEvent.VK_TAB)
+ setPopupVisible(false);
+ else
+ super.processKeyEvent(e);
+ }
+
+ /**
+ * setKeySelectionManager
+ *
+ * @param aManager
+ */
+ public void setKeySelectionManager(KeySelectionManager aManager)
+ {
+ keySelectionManager = aManager;
+ }
+
+ /**
+ * getKeySelectionManager
+ *
+ * @return JComboBox.KeySelectionManager
+ */
+ public KeySelectionManager getKeySelectionManager()
+ {
+ return keySelectionManager;
+ }
+
+ /**
+ * This method returns number of elements in this JComboBox
+ *
+ * @return int number of elements in this JComboBox
+ */
+ public int getItemCount()
+ {
+ return dataModel.getSize();
+ }
+
+ /**
+ * Returns elements located in the combo box at the given index.
+ *
+ * @param index index specifying location of the component to return.
+ *
+ * @return component in the combo box that is located in the given index.
+ */
+ public Object getItemAt(int index)
+ {
+ return dataModel.getElementAt(index);
+ }
+
+ /**
+ * createDefaultKeySelectionManager
+ *
+ * @return KeySelectionManager
+ */
+ protected KeySelectionManager createDefaultKeySelectionManager()
+ {
+ return new DefaultKeySelectionManager();
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this <code>JComboBox</code>.
+ *
+ * @return A string describing the attributes of this <code>JComboBox</code>
+ * (never <code>null</code>).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",isEditable=").append(isEditable());
+ sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled());
+ sb.append(",maximumRowCount=").append(getMaximumRowCount());
+
+ sb.append(",selectedItemReminder=");
+ if (selectedItemReminder != null)
+ sb.append(selectedItemReminder);
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JComboBox</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJComboBox}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJComboBox();
+
+ return accessibleContext;
+ }
+
+ /**
+ * This methods adds specified ActionListener to this JComboBox.
+ *
+ * @param listener to add
+ */
+ public void addActionListener(ActionListener listener)
+ {
+ listenerList.add(ActionListener.class, listener);
+ }
+
+ /**
+ * This method removes specified ActionListener from this JComboBox.
+ *
+ * @param listener ActionListener
+ */
+ public void removeActionListener(ActionListener listener)
+ {
+ listenerList.remove(ActionListener.class, listener);
+ }
+
+ /**
+ * This method returns array of ActionListeners that are registered with
+ * this JComboBox.
+ *
+ * @since 1.4
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) getListeners(ActionListener.class);
+ }
+
+ /**
+ * This method registers given ItemListener with this JComboBox
+ *
+ * @param listener to remove
+ */
+ public void addItemListener(ItemListener listener)
+ {
+ listenerList.add(ItemListener.class, listener);
+ }
+
+ /**
+ * This method unregisters given ItemListener from this JComboBox
+ *
+ * @param listener to remove
+ */
+ public void removeItemListener(ItemListener listener)
+ {
+ listenerList.remove(ItemListener.class, listener);
+ }
+
+ /**
+ * This method returns array of ItemListeners that are registered with this
+ * JComboBox.
+ *
+ * @since 1.4
+ */
+ public ItemListener[] getItemListeners()
+ {
+ return (ItemListener[]) getListeners(ItemListener.class);
+ }
+
+ /**
+ * Adds PopupMenuListener to combo box to listen to the events fired by the
+ * combo box's popup menu containing its list of items
+ *
+ * @param listener to add
+ */
+ public void addPopupMenuListener(PopupMenuListener listener)
+ {
+ listenerList.add(PopupMenuListener.class, listener);
+ }
+
+ /**
+ * Removes PopupMenuListener to combo box to listen to the events fired by
+ * the combo box's popup menu containing its list of items
+ *
+ * @param listener to add
+ */
+ public void removePopupMenuListener(PopupMenuListener listener)
+ {
+ listenerList.remove(PopupMenuListener.class, listener);
+ }
+
+ /**
+ * Returns array of PopupMenuListeners that are registered with combo box.
+ */
+ public PopupMenuListener[] getPopupMenuListeners()
+ {
+ return (PopupMenuListener[]) getListeners(PopupMenuListener.class);
+ }
+
+ /**
+ * Accessibility support for <code>JComboBox</code>.
+ */
+ protected class AccessibleJComboBox extends AccessibleJComponent
+ implements AccessibleAction, AccessibleSelection
+ {
+ private static final long serialVersionUID = 8217828307256675666L;
+
+ /**
+ * @specnote This constructor was protected in 1.4, but made public
+ * in 1.5.
+ */
+ public AccessibleJComboBox()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the number of accessible children of this object. The
+ * implementation of AccessibleJComboBox delegates this call to the UI
+ * of the associated JComboBox.
+ *
+ * @return the number of accessible children of this object
+ *
+ * @see ComponentUI#getAccessibleChildrenCount(JComponent)
+ */
+ public int getAccessibleChildrenCount()
+ {
+ ComponentUI ui = getUI();
+ int count;
+ if (ui != null)
+ count = ui.getAccessibleChildrenCount(JComboBox.this);
+ else
+ count = super.getAccessibleChildrenCount();
+ return count;
+ }
+
+ /**
+ * Returns the number of accessible children of this object. The
+ * implementation of AccessibleJComboBox delegates this call to the UI
+ * of the associated JComboBox.
+ *
+ * @param index the index of the accessible child to fetch
+ *
+ * @return the number of accessible children of this object
+ *
+ * @see ComponentUI#getAccessibleChild(JComponent, int)
+ */
+ public Accessible getAccessibleChild(int index)
+ {
+ ComponentUI ui = getUI();
+ Accessible child = null;
+ if (ui != null)
+ child = ui.getAccessibleChild(JComboBox.this, index);
+ else
+ child = super.getAccessibleChild(index);
+ return child;
+ }
+
+ /**
+ * Returns the AccessibleSelection object associated with this object.
+ * AccessibleJComboBoxes handle their selection themselves, so this
+ * always returns <code>this</code>.
+ *
+ * @return the AccessibleSelection object associated with this object
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible selection from this AccssibleJComboBox.
+ *
+ * @param index the index of the selected child to fetch
+ *
+ * @return the accessible selection from this AccssibleJComboBox
+ */
+ public Accessible getAccessibleSelection(int index)
+ {
+ // Get hold of the actual popup.
+ Accessible popup = getUI().getAccessibleChild(JComboBox.this, 0);
+ Accessible selected = null;
+ if (popup != null && popup instanceof ComboPopup)
+ {
+ ComboPopup cPopup = (ComboPopup) popup;
+ // Query the list for the currently selected child.
+ JList l = cPopup.getList();
+ AccessibleContext listCtx = l.getAccessibleContext();
+ if (listCtx != null)
+ {
+ AccessibleSelection s = listCtx.getAccessibleSelection();
+ if (s != null)
+ {
+ selected = s.getAccessibleSelection(index);
+ }
+ }
+ }
+ return selected;
+ }
+
+ /**
+ * Returns <code>true</code> if the accessible child with the specified
+ * <code>index</code> is selected, <code>false</code> otherwise.
+ *
+ * @param index the index of the accessible child
+ *
+ * @return <code>true</code> if the accessible child with the specified
+ * <code>index</code> is selected, <code>false</code> otherwise
+ */
+ public boolean isAccessibleChildSelected(int index)
+ {
+ return getSelectedIndex() == index;
+ }
+
+ /**
+ * Returns the accessible role for the <code>JComboBox</code> component.
+ *
+ * @return {@link AccessibleRole#COMBO_BOX}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.COMBO_BOX;
+ }
+
+ /**
+ * Returns the accessible action associated to this accessible object.
+ * AccessibleJComboBox implements its own AccessibleAction, so this
+ * method returns <code>this</code>.
+ *
+ * @return the accessible action associated to this accessible object
+ */
+ public AccessibleAction getAccessibleAction()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the description of the specified action. AccessibleJComboBox
+ * implements 1 action (toggle the popup menu) and thus returns
+ * <code>UIManager.getString("ComboBox.togglePopupText")</code>
+ *
+ * @param actionIndex the index of the action for which to return the
+ * description
+ *
+ * @return the description of the specified action
+ */
+ public String getAccessibleActionDescription(int actionIndex)
+ {
+ return UIManager.getString("ComboBox.togglePopupText");
+ }
+
+ /**
+ * Returns the number of accessible actions that can be performed by
+ * this object. AccessibleJComboBox implement s one accessible action
+ * (toggle the popup menu), so this method always returns <code>1</code>.
+ *
+ * @return the number of accessible actions that can be performed by
+ * this object
+ */
+ public int getAccessibleActionCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Performs the accessible action with the specified index.
+ * AccessibleJComboBox has 1 accessible action
+ * (<code>actionIndex == 0</code>), which is to toggle the
+ * popup menu. All other action indices have no effect and return
+ * <code<>false</code>.
+ *
+ * @param actionIndex the index of the action to perform
+ *
+ * @return <code>true</code> if the action has been performed,
+ * <code>false</code> otherwise
+ */
+ public boolean doAccessibleAction(int actionIndex)
+ {
+ boolean actionPerformed = false;
+ if (actionIndex == 0)
+ {
+ setPopupVisible(! isPopupVisible());
+ actionPerformed = true;
+ }
+ return actionPerformed;
+ }
+
+ /**
+ * Returns the number of selected accessible children of this object. This
+ * returns <code>1</code> if the combobox has a selected entry,
+ * <code>0</code> otherwise.
+ *
+ * @return the number of selected accessible children of this object
+ */
+ public int getAccessibleSelectionCount()
+ {
+ Object sel = getSelectedItem();
+ int count = 0;
+ if (sel != null)
+ count = 1;
+ return count;
+ }
+
+ /**
+ * Sets the current selection to the specified <code>index</code>.
+ *
+ * @param index the index to set as selection
+ */
+ public void addAccessibleSelection(int index)
+ {
+ setSelectedIndex(index);
+ }
+
+ /**
+ * Removes the specified index from the current selection.
+ *
+ * @param index the index to remove from the selection
+ */
+ public void removeAccessibleSelection(int index)
+ {
+ if (getSelectedIndex() == index)
+ clearAccessibleSelection();
+ }
+
+ /**
+ * Clears the current selection.
+ */
+ public void clearAccessibleSelection()
+ {
+ setSelectedIndex(-1);
+ }
+
+ /**
+ * Multiple selection is not supported by AccessibleJComboBox, so this
+ * does nothing.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ private class DefaultKeySelectionManager
+ implements KeySelectionManager
+ {
+
+ public int selectionForKey(char aKey, ComboBoxModel aModel)
+ {
+ int selectedIndex = getSelectedIndex();
+
+ // Start at currently selected item and iterate to end of list
+ for (int i = selectedIndex + 1; i < aModel.getSize(); i++)
+ {
+ String nextItem = aModel.getElementAt(i).toString();
+
+ if (nextItem.charAt(0) == aKey)
+ return i;
+ }
+
+ // Wrap to start of list if no match yet
+ for (int i = 0; i <= selectedIndex; i++)
+ {
+ String nextItem = aModel.getElementAt(i).toString();
+
+ if (nextItem.charAt(0) == aKey)
+ return i;
+ }
+
+ return - 1;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java
new file mode 100644
index 000000000..365f62aa9
--- /dev/null
+++ b/libjava/classpath/javax/swing/JComponent.java
@@ -0,0 +1,3801 @@
+/* JComponent.java -- Every component in swing inherits from this class.
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.applet.Applet;
+import java.awt.AWTEvent;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.FocusTraversalPolicy;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.dnd.DropTarget;
+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.peer.LightweightPeer;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+import java.beans.VetoableChangeListener;
+import java.beans.VetoableChangeSupport;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.EventListener;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleExtendedComponent;
+import javax.accessibility.AccessibleKeyBinding;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
+import javax.swing.event.EventListenerList;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * The base class of all Swing components.
+ * It contains generic methods to manage events, properties and sizes. Actual
+ * drawing of the component is channeled to a look-and-feel class that is
+ * implemented elsewhere.
+ *
+ * @author Ronald Veldema (rveldema&064;cs.vu.nl)
+ * @author Graydon Hoare (graydon&064;redhat.com)
+ */
+public abstract class JComponent extends Container implements Serializable
+{
+ private static final long serialVersionUID = -7908749299918704233L;
+
+ /**
+ * The accessible context of this <code>JComponent</code>.
+ */
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * Basic accessibility support for <code>JComponent</code> derived
+ * widgets.
+ */
+ public abstract class AccessibleJComponent
+ extends AccessibleAWTContainer
+ implements AccessibleExtendedComponent
+ {
+ /**
+ * Receives notification if the focus on the JComponent changes and
+ * fires appropriate PropertyChangeEvents to listeners registered with
+ * the AccessibleJComponent.
+ */
+ protected class AccessibleFocusHandler
+ implements FocusListener
+ {
+ /**
+ * Creates a new AccessibleFocusHandler.
+ */
+ protected AccessibleFocusHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification when the JComponent gained focus and fires
+ * a PropertyChangeEvent to listeners registered on the
+ * AccessibleJComponent with a property name of
+ * {@link AccessibleContext#ACCESSIBLE_STATE_PROPERTY} and a new value
+ * of {@link AccessibleState#FOCUSED}.
+ */
+ public void focusGained(FocusEvent event)
+ {
+ AccessibleJComponent.this.firePropertyChange
+ (AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null,
+ AccessibleState.FOCUSED);
+ }
+
+ /**
+ * Receives notification when the JComponent lost focus and fires
+ * a PropertyChangeEvent to listeners registered on the
+ * AccessibleJComponent with a property name of
+ * {@link AccessibleContext#ACCESSIBLE_STATE_PROPERTY} and an old value
+ * of {@link AccessibleState#FOCUSED}.
+ */
+ public void focusLost(FocusEvent valevent)
+ {
+ AccessibleJComponent.this.firePropertyChange
+ (AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.FOCUSED, null);
+ }
+ }
+
+ /**
+ * Receives notification if there are child components are added or removed
+ * from the JComponent and fires appropriate PropertyChangeEvents to
+ * interested listeners on the AccessibleJComponent.
+ */
+ protected class AccessibleContainerHandler
+ implements ContainerListener
+ {
+ /**
+ * Creates a new AccessibleContainerHandler.
+ */
+ protected AccessibleContainerHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification when a child component is added to the
+ * JComponent and fires a PropertyChangeEvent on listeners registered
+ * with the AccessibleJComponent with a property name of
+ * {@link AccessibleContext#ACCESSIBLE_CHILD_PROPERTY}.
+ *
+ * @param event the container event
+ */
+ public void componentAdded(ContainerEvent event)
+ {
+ Component c = event.getChild();
+ if (c != null && c instanceof Accessible)
+ {
+ AccessibleContext childCtx = c.getAccessibleContext();
+ AccessibleJComponent.this.firePropertyChange
+ (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, null, childCtx);
+ }
+ }
+
+ /**
+ * Receives notification when a child component is removed from the
+ * JComponent and fires a PropertyChangeEvent on listeners registered
+ * with the AccessibleJComponent with a property name of
+ * {@link AccessibleContext#ACCESSIBLE_CHILD_PROPERTY}.
+ *
+ * @param event the container event
+ */
+ public void componentRemoved(ContainerEvent event)
+ {
+ Component c = event.getChild();
+ if (c != null && c instanceof Accessible)
+ {
+ AccessibleContext childCtx = c.getAccessibleContext();
+ AccessibleJComponent.this.firePropertyChange
+ (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, childCtx, null);
+ }
+ }
+ }
+
+ private static final long serialVersionUID = -7047089700479897799L;
+
+ /**
+ * Receives notification when a child component is added to the
+ * JComponent and fires a PropertyChangeEvent on listeners registered
+ * with the AccessibleJComponent.
+ *
+ * @specnote AccessibleAWTContainer has a protected field with the same
+ * name. Looks like a bug or nasty misdesign to me.
+ */
+ protected ContainerListener accessibleContainerHandler;
+
+ /**
+ * Receives notification if the focus on the JComponent changes and
+ * fires appropriate PropertyChangeEvents to listeners registered with
+ * the AccessibleJComponent.
+ *
+ * @specnote AccessibleAWTComponent has a protected field
+ * accessibleAWTFocusHandler. Looks like a bug or nasty misdesign
+ * to me.
+ */
+ protected FocusListener accessibleFocusHandler;
+
+ /**
+ * Creates a new AccessibleJComponent.
+ */
+ protected AccessibleJComponent()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds a property change listener to the list of registered listeners.
+ *
+ * This sets up the {@link #accessibleContainerHandler} and
+ * {@link #accessibleFocusHandler} fields and calls
+ * <code>super.addPropertyChangeListener(listener)</code>.
+ *
+ * @param listener the listener to add
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ // Tests seem to indicate that this method also sets up the other two
+ // handlers.
+ if (accessibleContainerHandler == null)
+ {
+ accessibleContainerHandler = new AccessibleContainerHandler();
+ addContainerListener(accessibleContainerHandler);
+ }
+ if (accessibleFocusHandler == null)
+ {
+ accessibleFocusHandler = new AccessibleFocusHandler();
+ addFocusListener(accessibleFocusHandler);
+ }
+ super.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Removes a property change listener from the list of registered listeners.
+ *
+ * This uninstalls the {@link #accessibleContainerHandler} and
+ * {@link #accessibleFocusHandler} fields and calls
+ * <code>super.removePropertyChangeListener(listener)</code>.
+ *
+ * @param listener the listener to remove
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ // Tests seem to indicate that this method also resets the other two
+ // handlers.
+ if (accessibleContainerHandler != null)
+ {
+ removeContainerListener(accessibleContainerHandler);
+ accessibleContainerHandler = null;
+ }
+ if (accessibleFocusHandler != null)
+ {
+ removeFocusListener(accessibleFocusHandler);
+ accessibleFocusHandler = null;
+ }
+ super.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Returns the number of accessible children of this object.
+ *
+ * @return the number of accessible children of this object
+ */
+ public int getAccessibleChildrenCount()
+ {
+ // TODO: The functionality should be performed in the superclass.
+ // Find out why this is overridden. However, it is very well possible
+ // that this is left over from times when there was no such superclass
+ // method.
+ return super.getAccessibleChildrenCount();
+ }
+
+ /**
+ * Returns the accessible child component at index <code>i</code>.
+ *
+ * @param i the index of the accessible child to return
+ *
+ * @return the accessible child component at index <code>i</code>
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ // TODO: The functionality should be performed in the superclass.
+ // Find out why this is overridden. However, it is very well possible
+ // that this is left over from times when there was no such superclass
+ // method.
+ return super.getAccessibleChild(i);
+ }
+
+ /**
+ * Returns the accessible state set of this component.
+ *
+ * @return the accessible state set of this component
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // Note: While the java.awt.Component has an 'opaque' property, it
+ // seems that it is not added to the accessible state set there, even
+ // if this property is true. However, it is handled for JComponent, so
+ // we add it here.
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ if (isOpaque())
+ state.add(AccessibleState.OPAQUE);
+ return state;
+ }
+
+ /**
+ * Returns the localized name for this object. Generally this should
+ * almost never return {@link Component#getName()} since that is not
+ * a localized name. If the object is some kind of text component (like
+ * a menu item), then the value of the object may be returned. Also, if
+ * the object has a tooltip, the value of the tooltip may also be
+ * appropriate.
+ *
+ * @return the localized name for this object or <code>null</code> if this
+ * object has no name
+ */
+ public String getAccessibleName()
+ {
+ String name = super.getAccessibleName();
+
+ // There are two fallbacks provided by the JComponent in the case the
+ // superclass returns null:
+ // - If the component is inside a titled border, then it inherits the
+ // name from the border title.
+ // - If the component is not inside a titled border but has a label
+ // (via JLabel.setLabelFor()), then it gets the name from the label's
+ // accessible context.
+
+ if (name == null)
+ {
+ name = getTitledBorderText();
+ }
+
+ if (name == null)
+ {
+ Object l = getClientProperty(JLabel.LABEL_PROPERTY);
+ if (l instanceof Accessible)
+ {
+ AccessibleContext labelCtx =
+ ((Accessible) l).getAccessibleContext();
+ name = labelCtx.getAccessibleName();
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Returns the localized description of this object.
+ *
+ * @return the localized description of this object or <code>null</code>
+ * if this object has no description
+ */
+ public String getAccessibleDescription()
+ {
+ // There are two fallbacks provided by the JComponent in the case the
+ // superclass returns null:
+ // - If the component has a tooltip, then inherit the description from
+ // the tooltip.
+ // - If the component is not inside a titled border but has a label
+ // (via JLabel.setLabelFor()), then it gets the name from the label's
+ // accessible context.
+ String descr = super.getAccessibleDescription();
+
+ if (descr == null)
+ {
+ descr = getToolTipText();
+ }
+
+ if (descr == null)
+ {
+ Object l = getClientProperty(JLabel.LABEL_PROPERTY);
+ if (l instanceof Accessible)
+ {
+ AccessibleContext labelCtx =
+ ((Accessible) l).getAccessibleContext();
+ descr = labelCtx.getAccessibleName();
+ }
+ }
+
+ return descr;
+ }
+
+ /**
+ * Returns the accessible role of this component.
+ *
+ * @return the accessible role of this component
+ *
+ * @see AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SWING_COMPONENT;
+ }
+
+ /**
+ * Recursivly searches a border hierarchy (starting at <code>border) for
+ * a titled border and returns the title if one is found, <code>null</code>
+ * otherwise.
+ *
+ * @param border the border to start search from
+ *
+ * @return the border title of a possibly found titled border
+ */
+ protected String getBorderTitle(Border border)
+ {
+ String title = null;
+ if (border instanceof CompoundBorder)
+ {
+ CompoundBorder compound = (CompoundBorder) border;
+ Border inner = compound.getInsideBorder();
+ title = getBorderTitle(inner);
+ if (title == null)
+ {
+ Border outer = compound.getOutsideBorder();
+ title = getBorderTitle(outer);
+ }
+ }
+ else if (border instanceof TitledBorder)
+ {
+ TitledBorder titled = (TitledBorder) border;
+ title = titled.getTitle();
+ }
+ return title;
+ }
+
+ /**
+ * Returns the tooltip text for this accessible component.
+ *
+ * @return the tooltip text for this accessible component
+ */
+ public String getToolTipText()
+ {
+ return JComponent.this.getToolTipText();
+ }
+
+ /**
+ * Returns the title of the border of this accessible component if
+ * this component has a titled border, otherwise returns <code>null</code>.
+ *
+ * @return the title of the border of this accessible component if
+ * this component has a titled border, otherwise returns
+ * <code>null</code>
+ */
+ public String getTitledBorderText()
+ {
+ return getBorderTitle(getBorder());
+ }
+
+ /**
+ * Returns the keybindings associated with this accessible component or
+ * <code>null</code> if the component does not support key bindings.
+ *
+ * @return the keybindings associated with this accessible component
+ */
+ public AccessibleKeyBinding getAccessibleKeyBinding()
+ {
+ // The reference implementation seems to always return null here,
+ // independent of the key bindings of the JComponent. So do we.
+ return null;
+ }
+ }
+
+ /**
+ * A value between 0.0 and 1.0 indicating the preferred horizontal
+ * alignment of the component, relative to its siblings. The values
+ * {@link #LEFT_ALIGNMENT}, {@link #CENTER_ALIGNMENT}, and {@link
+ * #RIGHT_ALIGNMENT} can also be used, as synonyms for <code>0.0</code>,
+ * <code>0.5</code>, and <code>1.0</code>, respectively. Not all layout
+ * managers use this property.
+ *
+ * @see #getAlignmentX
+ * @see #setAlignmentX
+ * @see javax.swing.OverlayLayout
+ * @see javax.swing.BoxLayout
+ */
+ float alignmentX = -1.0F;
+
+ /**
+ * A value between 0.0 and 1.0 indicating the preferred vertical
+ * alignment of the component, relative to its siblings. The values
+ * {@link #TOP_ALIGNMENT}, {@link #CENTER_ALIGNMENT}, and {@link
+ * #BOTTOM_ALIGNMENT} can also be used, as synonyms for <code>0.0</code>,
+ * <code>0.5</code>, and <code>1.0</code>, respectively. Not all layout
+ * managers use this property.
+ *
+ * @see #getAlignmentY
+ * @see #setAlignmentY
+ * @see javax.swing.OverlayLayout
+ * @see javax.swing.BoxLayout
+ */
+ float alignmentY = -1.0F;
+
+ /**
+ * The border painted around this component.
+ *
+ * @see #paintBorder
+ */
+ Border border;
+
+ /**
+ * The popup menu for the component.
+ *
+ * @see #getComponentPopupMenu()
+ * @see #setComponentPopupMenu(JPopupMenu)
+ */
+ JPopupMenu componentPopupMenu;
+
+ /**
+ * A flag that controls whether the {@link #getComponentPopupMenu()} method
+ * looks to the component's parent when the <code>componentPopupMenu</code>
+ * field is <code>null</code>.
+ */
+ boolean inheritsPopupMenu;
+
+ /**
+ * <p>Whether to double buffer this component when painting. This flag
+ * should generally be <code>true</code>, to ensure good painting
+ * performance.</p>
+ *
+ * <p>All children of a double buffered component are painted into the
+ * double buffer automatically, so only the top widget in a window needs
+ * to be double buffered.</p>
+ *
+ * @see #setDoubleBuffered
+ * @see #isDoubleBuffered
+ * @see #paint
+ */
+ boolean doubleBuffered = true;
+
+ /**
+ * A set of flags indicating which debugging graphics facilities should
+ * be enabled on this component. The values should be a combination of
+ * {@link DebugGraphics#NONE_OPTION}, {@link DebugGraphics#LOG_OPTION},
+ * {@link DebugGraphics#FLASH_OPTION}, or {@link
+ * DebugGraphics#BUFFERED_OPTION}.
+ *
+ * @see #setDebugGraphicsOptions
+ * @see #getDebugGraphicsOptions
+ * @see DebugGraphics
+ * @see #getComponentGraphics
+ */
+ int debugGraphicsOptions;
+
+ /**
+ * <p>This property controls two independent behaviors simultaneously.</p>
+ *
+ * <p>First, it controls whether to fill the background of this widget
+ * when painting its body. This affects calls to {@link
+ * JComponent#paintComponent}, which in turn calls {@link
+ * ComponentUI#update} on the component's {@link #ui} property. If the
+ * component is opaque during this call, the background will be filled
+ * before calling {@link ComponentUI#paint}. This happens merely as a
+ * convenience; you may fill the component's background yourself too,
+ * but there is no need to do so if you will be filling with the same
+ * color.</p>
+ *
+ * <p>Second, it the opaque property informs swing's repaint system
+ * whether it will be necessary to paint the components "underneath" this
+ * component, in Z-order. If the component is opaque, it is considered to
+ * completely occlude components "underneath" it, so they will not be
+ * repainted along with the opaque component.</p>
+ *
+ * <p>The default value for this property is <code>false</code>, but most
+ * components will want to set it to <code>true</code> when installing UI
+ * defaults in {@link ComponentUI#installUI}.</p>
+ *
+ * @see #setOpaque
+ * @see #isOpaque
+ * @see #paintComponent
+ */
+ boolean opaque = false;
+
+ /**
+ * The user interface delegate for this component. Event delivery and
+ * repainting of the component are usually delegated to this object.
+ *
+ * @see #setUI
+ * @see #getUIClassID
+ * @see #updateUI
+ */
+ protected ComponentUI ui;
+
+ /**
+ * A hint to the focus system that this component should or should not
+ * get focus. If this is <code>false</code>, swing will not try to
+ * request focus on this component; if <code>true</code>, swing might
+ * try to request focus, but the request might fail. Thus it is only
+ * a hint guiding swing's behavior.
+ *
+ * @see #requestFocus()
+ * @see #isRequestFocusEnabled
+ * @see #setRequestFocusEnabled
+ */
+ boolean requestFocusEnabled;
+
+ /**
+ * Flag indicating behavior of this component when the mouse is dragged
+ * outside the component and the mouse <em>stops moving</em>. If
+ * <code>true</code>, synthetic mouse events will be delivered on regular
+ * timed intervals, continuing off in the direction the mouse exited the
+ * component, until the mouse is released or re-enters the component.
+ *
+ * @see #setAutoscrolls
+ * @see #getAutoscrolls
+ */
+ boolean autoscrolls = false;
+
+ /**
+ * Indicates whether the current paint call is already double buffered or
+ * not.
+ */
+ static boolean paintingDoubleBuffered = false;
+
+ /**
+ * Indicates whether we are calling paintDoubleBuffered() from
+ * paintImmadiately (RepaintManager) or from paint() (AWT refresh).
+ */
+ static boolean isRepainting = false;
+
+ /**
+ * Listeners for events other than {@link PropertyChangeEvent} are
+ * handled by this listener list. PropertyChangeEvents are handled in
+ * {@link #changeSupport}.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Handles VetoableChangeEvents.
+ */
+ private VetoableChangeSupport vetoableChangeSupport;
+
+ /**
+ * Storage for "client properties", which are key/value pairs associated
+ * with this component by a "client", such as a user application or a
+ * layout manager. This is lazily constructed when the component gets its
+ * first client property.
+ */
+ private Hashtable clientProperties;
+
+ private InputMap inputMap_whenFocused;
+ private InputMap inputMap_whenAncestorOfFocused;
+ private ComponentInputMap inputMap_whenInFocusedWindow;
+ private ActionMap actionMap;
+ /** @since 1.3 */
+ private boolean verifyInputWhenFocusTarget = true;
+ private InputVerifier inputVerifier;
+
+ private TransferHandler transferHandler;
+
+ /**
+ * Indicates if this component is currently painting a tile or not.
+ */
+ private boolean paintingTile;
+
+ /**
+ * A temporary buffer used for fast dragging of components.
+ */
+ private Image dragBuffer;
+
+ /**
+ * Indicates if the dragBuffer is already initialized.
+ */
+ private boolean dragBufferInitialized;
+
+ /**
+ * A cached Rectangle object to be reused. Be careful when you use that,
+ * so that it doesn't get modified in another context within the same
+ * method call chain.
+ */
+ private static transient Rectangle rectCache;
+
+ /**
+ * The default locale of the component.
+ *
+ * @see #getDefaultLocale
+ * @see #setDefaultLocale
+ */
+ private static Locale defaultLocale;
+
+ public static final String TOOL_TIP_TEXT_KEY = "ToolTipText";
+
+ /**
+ * Constant used to indicate that no condition has been assigned to a
+ * particular action.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ */
+ public static final int UNDEFINED_CONDITION = -1;
+
+ /**
+ * Constant used to indicate that an action should be performed only when
+ * the component has focus.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ */
+ public static final int WHEN_FOCUSED = 0;
+
+ /**
+ * Constant used to indicate that an action should be performed only when
+ * the component is an ancestor of the component which has focus.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ */
+ public static final int WHEN_ANCESTOR_OF_FOCUSED_COMPONENT = 1;
+
+ /**
+ * Constant used to indicate that an action should be performed only when
+ * the component is in the window which has focus.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ */
+ public static final int WHEN_IN_FOCUSED_WINDOW = 2;
+
+
+ /**
+ * Used to optimize painting. This is set in paintImmediately2() to specify
+ * the exact component path to be painted by paintChildren.
+ */
+ Component paintChild;
+
+ /**
+ * Indicates if the opaque property has been set by a client program or by
+ * the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientOpaqueSet = false;
+
+ /**
+ * Indicates if the autoscrolls property has been set by a client program or
+ * by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientAutoscrollsSet = false;
+
+ /**
+ * Creates a new <code>JComponent</code> instance.
+ */
+ public JComponent()
+ {
+ super();
+ setDropTarget(new DropTarget());
+ setLocale(getDefaultLocale());
+ debugGraphicsOptions = DebugGraphics.NONE_OPTION;
+ setRequestFocusEnabled(true);
+ }
+
+ /**
+ * Helper to lazily construct and return the client properties table.
+ *
+ * @return The current client properties table
+ *
+ * @see #clientProperties
+ * @see #getClientProperty
+ * @see #putClientProperty
+ */
+ private Hashtable getClientProperties()
+ {
+ if (clientProperties == null)
+ clientProperties = new Hashtable();
+ return clientProperties;
+ }
+
+ /**
+ * Get a client property associated with this component and a particular
+ * key.
+ *
+ * @param key The key with which to look up the client property
+ *
+ * @return A client property associated with this object and key
+ *
+ * @see #clientProperties
+ * @see #getClientProperties
+ * @see #putClientProperty
+ */
+ public final Object getClientProperty(Object key)
+ {
+ return getClientProperties().get(key);
+ }
+
+ /**
+ * Add a client property <code>value</code> to this component, associated
+ * with <code>key</code>. If there is an existing client property
+ * associated with <code>key</code>, it will be replaced. A
+ * {@link PropertyChangeEvent} is sent to registered listeners (with the
+ * name of the property being <code>key.toString()</code>).
+ *
+ * @param key The key of the client property association to add
+ * @param value The value of the client property association to add
+ *
+ * @see #clientProperties
+ * @see #getClientProperties
+ * @see #getClientProperty
+ */
+ public final void putClientProperty(Object key, Object value)
+ {
+ Hashtable t = getClientProperties();
+ Object old = t.get(key);
+ if (value != null)
+ t.put(key, value);
+ else
+ t.remove(key);
+
+ // When both old and new value are null, no event is fired. This is
+ // different from what firePropertyChange() normally does, so we add this
+ // check here.
+ if (old != null || value != null)
+ firePropertyChange(key.toString(), old, value);
+ }
+
+ /**
+ * Unregister an <code>AncestorListener</code>.
+ *
+ * @param listener The listener to unregister
+ *
+ * @see #addAncestorListener
+ */
+ public void removeAncestorListener(AncestorListener listener)
+ {
+ listenerList.remove(AncestorListener.class, listener);
+ }
+
+ /**
+ * Unregister a <code>VetoableChangeChangeListener</code>.
+ *
+ * @param listener The listener to unregister
+ *
+ * @see #addVetoableChangeListener
+ */
+ public void removeVetoableChangeListener(VetoableChangeListener listener)
+ {
+ if (vetoableChangeSupport != null)
+ vetoableChangeSupport.removeVetoableChangeListener(listener);
+ }
+
+ /**
+ * Register an <code>AncestorListener</code>.
+ *
+ * @param listener The listener to register
+ *
+ * @see #removeVetoableChangeListener
+ */
+ public void addAncestorListener(AncestorListener listener)
+ {
+ listenerList.add(AncestorListener.class, listener);
+ }
+
+ /**
+ * Register a <code>VetoableChangeListener</code>.
+ *
+ * @param listener The listener to register
+ *
+ * @see #removeVetoableChangeListener
+ * @see #listenerList
+ */
+ public void addVetoableChangeListener(VetoableChangeListener listener)
+ {
+ // Lazily instantiate this, it's rarely needed.
+ if (vetoableChangeSupport == null)
+ vetoableChangeSupport = new VetoableChangeSupport(this);
+ vetoableChangeSupport.addVetoableChangeListener(listener);
+ }
+
+ /**
+ * Returns all registered {@link EventListener}s of the given
+ * <code>listenerType</code>.
+ *
+ * @param listenerType the class of listeners to filter (<code>null</code>
+ * not permitted).
+ *
+ * @return An array of registered listeners.
+ *
+ * @throws ClassCastException if <code>listenerType</code> does not implement
+ * the {@link EventListener} interface.
+ * @throws NullPointerException if <code>listenerType</code> is
+ * <code>null</code>.
+ *
+ * @see #getAncestorListeners()
+ * @see #listenerList
+ *
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ if (listenerType == PropertyChangeListener.class)
+ return (T[]) getPropertyChangeListeners();
+ else if (listenerType == VetoableChangeListener.class)
+ return (T[]) getVetoableChangeListeners();
+ else
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Return all registered <code>AncestorListener</code> objects.
+ *
+ * @return The set of <code>AncestorListener</code> objects in {@link
+ * #listenerList}
+ */
+ public AncestorListener[] getAncestorListeners()
+ {
+ return (AncestorListener[]) getListeners(AncestorListener.class);
+ }
+
+ /**
+ * Return all registered <code>VetoableChangeListener</code> objects.
+ *
+ * @return An array of the <code>VetoableChangeListener</code> objects
+ * registered with this component (possibly empty but never
+ * <code>null</code>).
+ *
+ * @since 1.4
+ */
+ public VetoableChangeListener[] getVetoableChangeListeners()
+ {
+ return vetoableChangeSupport == null ? new VetoableChangeListener[0]
+ : vetoableChangeSupport.getVetoableChangeListeners();
+ }
+
+ /**
+ * Call {@link VetoableChangeListener#vetoableChange} on all listeners
+ * registered to listen to a given property. Any method which changes
+ * the specified property of this component should call this method.
+ *
+ * @param propertyName The property which changed
+ * @param oldValue The old value of the property
+ * @param newValue The new value of the property
+ *
+ * @throws PropertyVetoException if the change was vetoed by a listener
+ *
+ * @see #addVetoableChangeListener
+ * @see #removeVetoableChangeListener
+ */
+ protected void fireVetoableChange(String propertyName, Object oldValue,
+ Object newValue)
+ throws PropertyVetoException
+ {
+ if (vetoableChangeSupport != null)
+ vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
+ }
+
+
+ /**
+ * Fires a property change for a primitive integer property.
+ *
+ * @param property the name of the property
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ *
+ * @specnote This method is implemented in
+ * {@link Component#firePropertyChange(String, int, int)}. It is
+ * only here because it is specified to be public, whereas the
+ * Component method is protected.
+ */
+ public void firePropertyChange(String property, int oldValue, int newValue)
+ {
+ super.firePropertyChange(property, oldValue, newValue);
+ }
+
+ /**
+ * Fires a property change for a primitive boolean property.
+ *
+ * @param property the name of the property
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ *
+ * @specnote This method is implemented in
+ * {@link Component#firePropertyChange(String, boolean, boolean)}.
+ * It is only here because it is specified to be public, whereas
+ * the Component method is protected.
+ */
+ public void firePropertyChange(String property, boolean oldValue,
+ boolean newValue)
+ {
+ super.firePropertyChange(property, oldValue, newValue);
+ }
+
+ /**
+ * Get the value of the accessibleContext property for this component.
+ *
+ * @return the current value of the property
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return null;
+ }
+
+ /**
+ * Get the value of the {@link #alignmentX} property.
+ *
+ * @return The current value of the property.
+ *
+ * @see #setAlignmentX
+ * @see #alignmentY
+ */
+ public float getAlignmentX()
+ {
+ float ret = alignmentX;
+ if (alignmentX < 0)
+ // alignment has not been set explicitly.
+ ret = super.getAlignmentX();
+
+ return ret;
+ }
+
+ /**
+ * Get the value of the {@link #alignmentY} property.
+ *
+ * @return The current value of the property.
+ *
+ * @see #setAlignmentY
+ * @see #alignmentX
+ */
+ public float getAlignmentY()
+ {
+ float ret = alignmentY;
+ if (alignmentY < 0)
+ // alignment has not been set explicitly.
+ ret = super.getAlignmentY();
+
+ return ret;
+ }
+
+ /**
+ * Get the current value of the {@link #autoscrolls} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getAutoscrolls()
+ {
+ return autoscrolls;
+ }
+
+ /**
+ * Set the value of the {@link #border} property.
+ *
+ * @param newBorder The new value of the property
+ *
+ * @see #getBorder
+ */
+ public void setBorder(Border newBorder)
+ {
+ Border oldBorder = getBorder();
+ if (oldBorder == newBorder)
+ return;
+
+ border = newBorder;
+ firePropertyChange("border", oldBorder, newBorder);
+ repaint();
+ }
+
+ /**
+ * Get the value of the {@link #border} property.
+ *
+ * @return The property's current value
+ *
+ * @see #setBorder
+ */
+ public Border getBorder()
+ {
+ return border;
+ }
+
+ /**
+ * Get the component's current bounding box. If a rectangle is provided,
+ * use this as the return value (adjusting its fields in place);
+ * otherwise (of <code>null</code> is provided) return a new {@link
+ * Rectangle}.
+ *
+ * @param rv Optional return value to use
+ *
+ * @return A rectangle bounding the component
+ */
+ public Rectangle getBounds(Rectangle rv)
+ {
+ if (rv == null)
+ return new Rectangle(getX(), getY(), getWidth(), getHeight());
+ else
+ {
+ rv.setBounds(getX(), getY(), getWidth(), getHeight());
+ return rv;
+ }
+ }
+
+ /**
+ * Prepares a graphics context for painting this object. If {@link
+ * #debugGraphicsOptions} is not equal to {@link
+ * DebugGraphics#NONE_OPTION}, produce a new {@link DebugGraphics} object
+ * wrapping the parameter. Otherwise configure the parameter with this
+ * component's foreground color and font.
+ *
+ * @param g The graphics context to wrap or configure
+ *
+ * @return A graphics context to paint this object with
+ *
+ * @see #debugGraphicsOptions
+ * @see #paint
+ */
+ protected Graphics getComponentGraphics(Graphics g)
+ {
+ Graphics g2 = g;
+ int options = getDebugGraphicsOptions();
+ if (options != DebugGraphics.NONE_OPTION)
+ {
+ if (!(g2 instanceof DebugGraphics))
+ g2 = new DebugGraphics(g);
+ DebugGraphics dg = (DebugGraphics) g2;
+ dg.setDebugOptions(dg.getDebugOptions() | options);
+ }
+ g2.setFont(this.getFont());
+ g2.setColor(this.getForeground());
+ return g2;
+ }
+
+ /**
+ * Get the value of the {@link #debugGraphicsOptions} property.
+ *
+ * @return The current value of the property.
+ *
+ * @see #setDebugGraphicsOptions
+ * @see #debugGraphicsOptions
+ */
+ public int getDebugGraphicsOptions()
+ {
+ String option = System.getProperty("gnu.javax.swing.DebugGraphics");
+ int options = debugGraphicsOptions;
+ if (option != null && option.length() != 0)
+ {
+ if (options < 0)
+ options = 0;
+
+ if (option.equals("LOG"))
+ options |= DebugGraphics.LOG_OPTION;
+ else if (option.equals("FLASH"))
+ options |= DebugGraphics.FLASH_OPTION;
+ }
+ return options;
+ }
+
+ /**
+ * Get the component's insets, which are calculated from
+ * the {@link #border} property. If the border is <code>null</code>,
+ * calls {@link Container#getInsets}.
+ *
+ * @return The component's current insets
+ */
+ public Insets getInsets()
+ {
+ if (border == null)
+ return super.getInsets();
+ return getBorder().getBorderInsets(this);
+ }
+
+ /**
+ * Get the component's insets, which are calculated from the {@link
+ * #border} property. If the border is <code>null</code>, calls {@link
+ * Container#getInsets}. The passed-in {@link Insets} value will be
+ * used as the return value, if possible.
+ *
+ * @param insets Return value object to reuse, if possible
+ *
+ * @return The component's current insets
+ */
+ public Insets getInsets(Insets insets)
+ {
+ Insets t = getInsets();
+
+ if (insets == null)
+ return t;
+
+ insets.left = t.left;
+ insets.right = t.right;
+ insets.top = t.top;
+ insets.bottom = t.bottom;
+ return insets;
+ }
+
+ /**
+ * Get the component's location. The passed-in {@link Point} value
+ * will be used as the return value, if possible.
+ *
+ * @param rv Return value object to reuse, if possible
+ *
+ * @return The component's current location
+ */
+ public Point getLocation(Point rv)
+ {
+ if (rv == null)
+ return new Point(getX(), getY());
+
+ rv.setLocation(getX(), getY());
+ return rv;
+ }
+
+ /**
+ * Get the component's maximum size. If the <code>maximumSize</code> property
+ * has been explicitly set, it is returned. If the <code>maximumSize</code>
+ * property has not been set but the {@link #ui} property has been, the
+ * result of {@link ComponentUI#getMaximumSize} is returned. If neither
+ * property has been set, the result of {@link Container#getMaximumSize}
+ * is returned.
+ *
+ * @return the maximum size of the component
+ *
+ * @see Component#setMaximumSize
+ * @see Component#getMaximumSize()
+ * @see Component#isMaximumSizeSet()
+ * @see ComponentUI#getMaximumSize(JComponent)
+ */
+ public Dimension getMaximumSize()
+ {
+ Dimension size = null;
+ if (isMaximumSizeSet())
+ size = super.getMaximumSize();
+ else
+ {
+ if (ui != null)
+ size = ui.getMaximumSize(this);
+ if (size == null)
+ size = super.getMaximumSize();
+ }
+ return size;
+ }
+
+ /**
+ * Get the component's minimum size. If the <code>minimumSize</code> property
+ * has been explicitly set, it is returned. If the <code>minimumSize</code>
+ * property has not been set but the {@link #ui} property has been, the
+ * result of {@link ComponentUI#getMinimumSize} is returned. If neither
+ * property has been set, the result of {@link Container#getMinimumSize}
+ * is returned.
+ *
+ * @return The minimum size of the component
+ *
+ * @see Component#setMinimumSize
+ * @see Component#getMinimumSize()
+ * @see Component#isMinimumSizeSet()
+ * @see ComponentUI#getMinimumSize(JComponent)
+ */
+ public Dimension getMinimumSize()
+ {
+ Dimension size = null;
+ if (isMinimumSizeSet())
+ size = super.getMinimumSize();
+ else
+ {
+ if (ui != null)
+ size = ui.getMinimumSize(this);
+ if (size == null)
+ size = super.getMinimumSize();
+ }
+ return size;
+ }
+
+ /**
+ * Get the component's preferred size. If the <code>preferredSize</code>
+ * property has been explicitly set, it is returned. If the
+ * <code>preferredSize</code> property has not been set but the {@link #ui}
+ * property has been, the result of {@link ComponentUI#getPreferredSize} is
+ * returned. If neither property has been set, the result of {@link
+ * Container#getPreferredSize} is returned.
+ *
+ * @return The preferred size of the component
+ *
+ * @see Component#setPreferredSize
+ * @see Component#getPreferredSize()
+ * @see Component#isPreferredSizeSet()
+ * @see ComponentUI#getPreferredSize(JComponent)
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension size = null;
+ if (isPreferredSizeSet())
+ size = super.getPreferredSize();
+ else
+ {
+ if (ui != null)
+ size = ui.getPreferredSize(this);
+ if (size == null)
+ size = super.getPreferredSize();
+ }
+ return size;
+ }
+
+ /**
+ * Return the value of the <code>nextFocusableComponent</code> property.
+ *
+ * @return The current value of the property, or <code>null</code>
+ * if none has been set.
+ *
+ * @deprecated See {@link java.awt.FocusTraversalPolicy}
+ */
+ public Component getNextFocusableComponent()
+ {
+ Container focusRoot = this;
+ if (! this.isFocusCycleRoot())
+ focusRoot = getFocusCycleRootAncestor();
+
+ FocusTraversalPolicy policy = focusRoot.getFocusTraversalPolicy();
+ return policy.getComponentAfter(focusRoot, this);
+ }
+
+ /**
+ * Return the set of {@link KeyStroke} objects which are registered
+ * to initiate actions on this component.
+ *
+ * @return An array of the registered keystrokes (possibly empty but never
+ * <code>null</code>).
+ */
+ public KeyStroke[] getRegisteredKeyStrokes()
+ {
+ KeyStroke[] ks0;
+ KeyStroke[] ks1;
+ KeyStroke[] ks2;
+ if (inputMap_whenFocused != null)
+ ks0 = inputMap_whenFocused.keys();
+ else
+ ks0 = new KeyStroke[0];
+ if (inputMap_whenAncestorOfFocused != null)
+ ks1 = inputMap_whenAncestorOfFocused.keys();
+ else
+ ks1 = new KeyStroke[0];
+ if (inputMap_whenInFocusedWindow != null)
+ ks2 = inputMap_whenInFocusedWindow.keys();
+ else
+ ks2 = new KeyStroke[0];
+ int count = ks0.length + ks1.length + ks2.length;
+ KeyStroke[] result = new KeyStroke[count];
+ System.arraycopy(ks0, 0, result, 0, ks0.length);
+ System.arraycopy(ks1, 0, result, ks0.length, ks1.length);
+ System.arraycopy(ks2, 0, result, ks0.length + ks1.length, ks2.length);
+ return result;
+ }
+
+ /**
+ * Returns the first ancestor of this component which is a {@link JRootPane}.
+ * Equivalent to calling <code>SwingUtilities.getRootPane(this);</code>.
+ *
+ * @return An ancestral JRootPane, or <code>null</code> if none exists.
+ */
+ public JRootPane getRootPane()
+ {
+ JRootPane p = SwingUtilities.getRootPane(this);
+ return p;
+ }
+
+ /**
+ * Get the component's size. The passed-in {@link Dimension} value
+ * will be used as the return value, if possible.
+ *
+ * @param rv Return value object to reuse, if possible
+ *
+ * @return The component's current size
+ */
+ public Dimension getSize(Dimension rv)
+ {
+ if (rv == null)
+ return new Dimension(getWidth(), getHeight());
+ else
+ {
+ rv.setSize(getWidth(), getHeight());
+ return rv;
+ }
+ }
+
+ /**
+ * Return the <code>toolTip</code> property of this component, creating it and
+ * setting it if it is currently <code>null</code>. This method can be
+ * overridden in subclasses which wish to control the exact form of
+ * tooltip created.
+ *
+ * @return The current toolTip
+ */
+ public JToolTip createToolTip()
+ {
+ JToolTip toolTip = new JToolTip();
+ toolTip.setComponent(this);
+ return toolTip;
+ }
+
+ /**
+ * Return the location at which the <code>toolTipText</code> property should
+ * be displayed, when triggered by a particular mouse event.
+ *
+ * @param event The event the tooltip is being presented in response to
+ *
+ * @return The point at which to display a tooltip, or <code>null</code>
+ * if swing is to choose a default location.
+ */
+ public Point getToolTipLocation(MouseEvent event)
+ {
+ return null;
+ }
+
+ /**
+ * Set the tooltip text for this component. If a non-<code>null</code>
+ * value is set, this component is registered in the
+ * <code>ToolTipManager</code> in order to turn on tooltips for this
+ * component. If a <code>null</code> value is set, tooltips are turne off
+ * for this component.
+ *
+ * @param text the tooltip text for this component
+ *
+ * @see #getToolTipText()
+ * @see #getToolTipText(MouseEvent)
+ */
+ public void setToolTipText(String text)
+ {
+ String old = getToolTipText();
+ putClientProperty(TOOL_TIP_TEXT_KEY, text);
+ ToolTipManager ttm = ToolTipManager.sharedInstance();
+ if (text == null)
+ ttm.unregisterComponent(this);
+ else if (old == null)
+ ttm.registerComponent(this);
+ }
+
+ /**
+ * Returns the current tooltip text for this component, or <code>null</code>
+ * if none has been set.
+ *
+ * @return the current tooltip text for this component, or <code>null</code>
+ * if none has been set
+ *
+ * @see #setToolTipText
+ * @see #getToolTipText(MouseEvent)
+ */
+ public String getToolTipText()
+ {
+ return (String) getClientProperty(TOOL_TIP_TEXT_KEY);
+ }
+
+ /**
+ * Returns the tooltip text for this component for a particular mouse
+ * event. This can be used to support context sensitive tooltips that can
+ * change with the mouse location. By default this returns the static
+ * tooltip text returned by {@link #getToolTipText()}.
+ *
+ * @param event the mouse event which triggered the tooltip
+ *
+ * @return the tooltip text for this component for a particular mouse
+ * event
+ *
+ * @see #setToolTipText
+ * @see #getToolTipText()
+ */
+ public String getToolTipText(MouseEvent event)
+ {
+ return getToolTipText();
+ }
+
+ /**
+ * Returns the flag that controls whether or not the component inherits its
+ * parent's popup menu when no popup menu is specified for this component.
+ *
+ * @return A boolean.
+ *
+ * @since 1.5
+ *
+ * @see #setInheritsPopupMenu(boolean)
+ */
+ public boolean getInheritsPopupMenu()
+ {
+ return inheritsPopupMenu;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the component inherits its
+ * parent's popup menu when no popup menu is specified for this component.
+ * This is a bound property with the property name 'inheritsPopupMenu'.
+ *
+ * @param inherit the new flag value.
+ *
+ * @since 1.5
+ *
+ * @see #getInheritsPopupMenu()
+ */
+ public void setInheritsPopupMenu(boolean inherit)
+ {
+ if (inheritsPopupMenu != inherit)
+ {
+ inheritsPopupMenu = inherit;
+ this.firePropertyChange("inheritsPopupMenu", ! inherit, inherit);
+ }
+ }
+
+ /**
+ * Returns the popup menu for this component. If the popup menu is
+ * <code>null</code> AND the {@link #getInheritsPopupMenu()} method returns
+ * <code>true</code>, this method will return the parent's popup menu (if it
+ * has one).
+ *
+ * @return The popup menu (possibly <code>null</code>.
+ *
+ * @since 1.5
+ *
+ * @see #setComponentPopupMenu(JPopupMenu)
+ * @see #getInheritsPopupMenu()
+ */
+ public JPopupMenu getComponentPopupMenu()
+ {
+ if (componentPopupMenu == null && getInheritsPopupMenu())
+ {
+ Container parent = getParent();
+ if (parent instanceof JComponent)
+ return ((JComponent) parent).getComponentPopupMenu();
+ else
+ return null;
+ }
+ else
+ return componentPopupMenu;
+ }
+
+ /**
+ * Sets the popup menu for this component (this is a bound property with
+ * the property name 'componentPopupMenu').
+ *
+ * @param popup the popup menu (<code>null</code> permitted).
+ *
+ * @since 1.5
+ *
+ * @see #getComponentPopupMenu()
+ */
+ public void setComponentPopupMenu(JPopupMenu popup)
+ {
+ if (componentPopupMenu != popup)
+ {
+ JPopupMenu old = componentPopupMenu;
+ componentPopupMenu = popup;
+ firePropertyChange("componentPopupMenu", old, popup);
+ }
+ }
+
+ /**
+ * Return the top level ancestral container (usually a {@link
+ * java.awt.Window} or {@link java.applet.Applet}) which this component is
+ * contained within, or <code>null</code> if no ancestors exist.
+ *
+ * @return The top level container, if it exists
+ */
+ public Container getTopLevelAncestor()
+ {
+ Container c = getParent();
+ for (Container peek = c; peek != null; peek = peek.getParent())
+ c = peek;
+ return c;
+ }
+
+ /**
+ * Compute the component's visible rectangle, which is defined
+ * recursively as either the component's bounds, if it has no parent, or
+ * the intersection of the component's bounds with the visible rectangle
+ * of its parent.
+ *
+ * @param rect The return value slot to place the visible rectangle in
+ */
+ public void computeVisibleRect(Rectangle rect)
+ {
+ Component c = getParent();
+ if (c != null && c instanceof JComponent)
+ {
+ ((JComponent) c).computeVisibleRect(rect);
+ rect.translate(-getX(), -getY());
+ rect = SwingUtilities.computeIntersection(0, 0, getWidth(),
+ getHeight(), rect);
+ }
+ else
+ rect.setRect(0, 0, getWidth(), getHeight());
+ }
+
+ /**
+ * Return the component's visible rectangle in a new {@link Rectangle},
+ * rather than via a return slot.
+ *
+ * @return the component's visible rectangle
+ *
+ * @see #computeVisibleRect(Rectangle)
+ */
+ public Rectangle getVisibleRect()
+ {
+ Rectangle r = new Rectangle();
+ computeVisibleRect(r);
+ return r;
+ }
+
+ /**
+ * <p>Requests that this component receive input focus, giving window
+ * focus to the top level ancestor of this component. Only works on
+ * displayable, focusable, visible components.</p>
+ *
+ * <p>This method should not be called by clients; it is intended for
+ * focus implementations. Use {@link Component#requestFocus()} instead.</p>
+ *
+ * @see Component#requestFocus()
+ */
+ public void grabFocus()
+ {
+ requestFocus();
+ }
+
+ /**
+ * Get the value of the {@link #doubleBuffered} property.
+ *
+ * @return The property's current value
+ */
+ public boolean isDoubleBuffered()
+ {
+ return doubleBuffered;
+ }
+
+ /**
+ * Return <code>true</code> if the provided component has no native peer;
+ * in other words, if it is a "lightweight component".
+ *
+ * @param c The component to test for lightweight-ness
+ *
+ * @return Whether or not the component is lightweight
+ */
+ public static boolean isLightweightComponent(Component c)
+ {
+ return c.getPeer() instanceof LightweightPeer;
+ }
+
+ /**
+ * Return <code>true</code> if you wish this component to manage its own
+ * focus. In particular: if you want this component to be sent
+ * <code>TAB</code> and <code>SHIFT+TAB</code> key events, and to not
+ * have its children considered as focus transfer targets. If
+ * <code>true</code>, focus traversal around this component changes to
+ * <code>CTRL+TAB</code> and <code>CTRL+SHIFT+TAB</code>.
+ *
+ * @return <code>true</code> if you want this component to manage its own
+ * focus, otherwise (by default) <code>false</code>
+ *
+ * @deprecated 1.4 Use {@link Component#setFocusTraversalKeys(int, Set)} and
+ * {@link Container#setFocusCycleRoot(boolean)} instead
+ */
+ public boolean isManagingFocus()
+ {
+ return false;
+ }
+
+ /**
+ * Return the current value of the {@link #opaque} property.
+ *
+ * @return The current property value
+ */
+ public boolean isOpaque()
+ {
+ return opaque;
+ }
+
+ /**
+ * Return <code>true</code> if the component can guarantee that none of its
+ * children will overlap in Z-order. This is a hint to the painting system.
+ * The default is to return <code>true</code>, but some components such as
+ * {@link JLayeredPane} should override this to return <code>false</code>.
+ *
+ * @return Whether the component tiles its children
+ */
+ public boolean isOptimizedDrawingEnabled()
+ {
+ return true;
+ }
+
+ /**
+ * Return <code>true</code> if this component is currently painting a tile,
+ * this means that paint() is called again on another child component. This
+ * method returns <code>false</code> if this component does not paint a tile
+ * or if the last tile is currently painted.
+ *
+ * @return whether the component is painting a tile
+ */
+ public boolean isPaintingTile()
+ {
+ return paintingTile;
+ }
+
+ /**
+ * Get the value of the {@link #requestFocusEnabled} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean isRequestFocusEnabled()
+ {
+ return requestFocusEnabled;
+ }
+
+ /**
+ * Return <code>true</code> if this component is a validation root; this
+ * will cause calls to {@link #invalidate()} in this component's children
+ * to be "captured" at this component, and not propagate to its parents.
+ * For most components this should return <code>false</code>, but some
+ * components such as {@link JViewport} will want to return
+ * <code>true</code>.
+ *
+ * @return Whether this component is a validation root
+ */
+ public boolean isValidateRoot()
+ {
+ return false;
+ }
+
+ /**
+ * <p>Paint the component. This is a delicate process, and should only be
+ * called from the repaint thread, under control of the {@link
+ * RepaintManager}. Client code should usually call {@link #repaint()} to
+ * trigger painting.</p>
+ *
+ * <p>The body of the <code>paint</code> call involves calling {@link
+ * #paintComponent}, {@link #paintBorder}, and {@link #paintChildren} in
+ * order. If you want to customize painting behavior, you should override
+ * one of these methods rather than <code>paint</code>.</p>
+ *
+ * <p>For more details on the painting sequence, see <a
+ * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">
+ * this article</a>.</p>
+ *
+ * @param g The graphics context to paint with
+ *
+ * @see #paintImmediately(Rectangle)
+ */
+ public void paint(Graphics g)
+ {
+ RepaintManager rm = RepaintManager.currentManager(this);
+ // We do a little stunt act here to switch on double buffering if it's
+ // not already on. If we are not already doublebuffered, then we jump
+ // into the method paintDoubleBuffered, which turns on the double buffer
+ // and then calls paint(g) again. In the second call we go into the else
+ // branch of this if statement and actually paint things to the double
+ // buffer. When this method completes, the call stack unwinds back to
+ // paintDoubleBuffered, where the buffer contents is finally drawn to the
+ // screen.
+ if (!paintingDoubleBuffered && isDoubleBuffered()
+ && rm.isDoubleBufferingEnabled())
+ {
+ Rectangle clip = g.getClipBounds();
+ paintDoubleBuffered(clip.x, clip.y, clip.width, clip.height);
+ }
+ else
+ {
+ if (getClientProperty("bufferedDragging") != null
+ && dragBuffer == null)
+ {
+ initializeDragBuffer();
+ }
+ else if (getClientProperty("bufferedDragging") == null
+ && dragBuffer != null)
+ {
+ dragBuffer = null;
+ }
+
+ Rectangle clip = g.getClipBounds();
+ int clipX, clipY, clipW, clipH;
+ if (clip == null)
+ {
+ clipX = 0;
+ clipY = 0;
+ clipW = getWidth();
+ clipH = getHeight();
+ }
+ else
+ {
+ clipX = clip.x;
+ clipY = clip.y;
+ clipW = clip.width;
+ clipH = clip.height;
+ }
+ if (dragBuffer != null && dragBufferInitialized)
+ {
+ g.drawImage(dragBuffer, 0, 0, this);
+ }
+ else
+ {
+ Graphics g2 = getComponentGraphics(g);
+ if (! isOccupiedByChild(clipX, clipY, clipW, clipH))
+ {
+ paintComponent(g2);
+ paintBorder(g2);
+ }
+ paintChildren(g2);
+ }
+ }
+ }
+
+ /**
+ * Determines if a region of this component is completely occupied by
+ * an opaque child component, in which case we don't need to bother
+ * painting this component at all.
+ *
+ * @param x the area, x coordinate
+ * @param y the area, y coordinate
+ * @param w the area, width
+ * @param h the area, height
+ *
+ * @return <code>true</code> if the specified area is completely covered
+ * by a child component, <code>false</code> otherwise
+ */
+ private boolean isOccupiedByChild(int x, int y, int w, int h)
+ {
+ boolean occupied = false;
+ int count = getComponentCount();
+ for (int i = 0; i < count; i++)
+ {
+ Component child = getComponent(i);
+ int cx = child.getX();
+ int cy = child.getY();
+ int cw = child.getWidth();
+ int ch = child.getHeight();
+ if (child.isVisible() && x >= cx && x + w <= cx + cw && y >= cy
+ && y + h <= cy + ch)
+ {
+ occupied = child.isOpaque();
+ break;
+ }
+ }
+ return occupied;
+ }
+
+ /**
+ * Initializes the drag buffer by creating a new image and painting this
+ * component into it.
+ */
+ private void initializeDragBuffer()
+ {
+ dragBufferInitialized = false;
+ // Allocate new dragBuffer if the current one is too small.
+ if (dragBuffer == null || dragBuffer.getWidth(this) < getWidth()
+ || dragBuffer.getHeight(this) < getHeight())
+ {
+ dragBuffer = createImage(getWidth(), getHeight());
+ }
+ Graphics g = dragBuffer.getGraphics();
+ paint(g);
+ g.dispose();
+ dragBufferInitialized = true;
+ }
+
+ /**
+ * Paint the component's border. This usually means calling {@link
+ * Border#paintBorder} on the {@link #border} property, if it is
+ * non-<code>null</code>. You may override this if you wish to customize
+ * border painting behavior. The border is painted after the component's
+ * body, but before the component's children.
+ *
+ * @param g The graphics context with which to paint the border
+ *
+ * @see #paint
+ * @see #paintChildren
+ * @see #paintComponent
+ */
+ protected void paintBorder(Graphics g)
+ {
+ if (getBorder() != null)
+ getBorder().paintBorder(this, g, 0, 0, getWidth(), getHeight());
+ }
+
+ /**
+ * Paint the component's children. This usually means calling {@link
+ * Container#paint}, which recursively calls {@link #paint} on any of the
+ * component's children, with appropriate changes to coordinate space and
+ * clipping region. You may override this if you wish to customize
+ * children painting behavior. The children are painted after the
+ * component's body and border.
+ *
+ * @param g The graphics context with which to paint the children
+ *
+ * @see #paint
+ * @see #paintBorder
+ * @see #paintComponent
+ */
+ protected void paintChildren(Graphics g)
+ {
+ if (getComponentCount() > 0)
+ {
+ // Need to lock the tree to avoid problems with AWT and concurrency.
+ synchronized (getTreeLock())
+ {
+ // Fast forward to the child to paint, if set by
+ // paintImmediately2()
+ int i = getComponentCount() - 1;
+ if (paintChild != null && paintChild.isOpaque())
+ {
+ for (; i >= 0 && getComponent(i) != paintChild; i--)
+ ;
+ }
+ for (; i >= 0; i--)
+ {
+ Component child = getComponent(i);
+ if (child != null && child.isLightweight()
+ && child.isVisible())
+ {
+ int cx = child.getX();
+ int cy = child.getY();
+ int cw = child.getWidth();
+ int ch = child.getHeight();
+ if (g.hitClip(cx, cy, cw, ch))
+ {
+ if ((! isOptimizedDrawingEnabled()) && i > 0)
+ {
+ // Check if the child is completely obscured.
+ Rectangle clip = g.getClipBounds(); // A copy.
+ SwingUtilities.computeIntersection(cx, cy, cw, ch,
+ clip);
+ if (isCompletelyObscured(i, clip.x, clip.y,
+ clip.width, clip.height))
+ continue; // Continues the for-loop.
+ }
+ Graphics cg = g.create(cx, cy, cw, ch);
+ cg.setColor(child.getForeground());
+ cg.setFont(child.getFont());
+ try
+ {
+ child.paint(cg);
+ }
+ finally
+ {
+ cg.dispose();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines if a region of a child component is completely obscured by one
+ * of its siblings.
+ *
+ * @param index the index of the child component
+ * @param x the region to check, x coordinate
+ * @param y the region to check, y coordinate
+ * @param w the region to check, width
+ * @param h the region to check, height
+ *
+ * @return <code>true</code> if the region is completely obscured by a
+ * sibling, <code>false</code> otherwise
+ */
+ private boolean isCompletelyObscured(int index, int x, int y, int w, int h)
+ {
+ boolean obscured = false;
+ for (int i = index - 1; i >= 0 && obscured == false; i--)
+ {
+ Component sib = getComponent(i);
+ if (sib.isVisible())
+ {
+ Rectangle sibRect = sib.getBounds(rectCache);
+ if (sib.isOpaque() && x >= sibRect.x
+ && (x + w) <= (sibRect.x + sibRect.width)
+ && y >= sibRect.y
+ && (y + h) <= (sibRect.y + sibRect.height))
+ {
+ obscured = true;
+ }
+ }
+ }
+ return obscured;
+ }
+
+ /**
+ * Checks if a component/rectangle is partially obscured by one of its
+ * siblings.
+ * Note that this doesn't check for completely obscured, this is
+ * done by isCompletelyObscured() and should probably also be checked.
+ *
+ * @param i the component index from which to start searching
+ * @param x the x coordinate of the rectangle to check
+ * @param y the y coordinate of the rectangle to check
+ * @param w the width of the rectangle to check
+ * @param h the height of the rectangle to check
+ *
+ * @return <code>true</code> if the rectangle is partially obscured
+ */
+ private boolean isPartiallyObscured(int i, int x, int y, int w, int h)
+ {
+ boolean obscured = false;
+ for (int j = i - 1; j >= 0 && ! obscured; j--)
+ {
+ Component sibl = getComponent(j);
+ if (sibl.isVisible())
+ {
+ Rectangle rect = sibl.getBounds(rectCache);
+ if (!(x + w <= rect.x)
+ || (y + h <= rect.y)
+ || (x >= rect.x + rect.width)
+ || (y >= rect.y + rect.height))
+ obscured = true;
+ }
+ }
+ return obscured;
+ }
+
+ /**
+ * Paint the component's body. This usually means calling {@link
+ * ComponentUI#update} on the {@link #ui} property of the component, if
+ * it is non-<code>null</code>. You may override this if you wish to
+ * customize the component's body-painting behavior. The component's body
+ * is painted first, before the border and children.
+ *
+ * @param g The graphics context with which to paint the body
+ *
+ * @see #paint
+ * @see #paintBorder
+ * @see #paintChildren
+ */
+ protected void paintComponent(Graphics g)
+ {
+ if (ui != null)
+ {
+ Graphics g2 = g.create();
+ try
+ {
+ ui.update(g2, this);
+ }
+ finally
+ {
+ g2.dispose();
+ }
+ }
+ }
+
+ /**
+ * A variant of {@link #paintImmediately(Rectangle)} which takes
+ * integer parameters.
+ *
+ * @param x The left x coordinate of the dirty region
+ * @param y The top y coordinate of the dirty region
+ * @param w The width of the dirty region
+ * @param h The height of the dirty region
+ */
+ public void paintImmediately(int x, int y, int w, int h)
+ {
+ // Find opaque parent and call paintImmediately2() on it.
+ if (isShowing())
+ {
+ Component c = this;
+ Component p;
+ while (c != null && ! c.isOpaque())
+ {
+ p = c.getParent();
+ if (p != null)
+ {
+ x += c.getX();
+ y += c.getY();
+ c = p;
+ }
+ }
+ if (c instanceof JComponent)
+ ((JComponent) c).paintImmediately2(x, y, w, h);
+ else
+ c.repaint(x, y, w, h);
+ }
+ }
+
+ /**
+ * Transform the provided dirty rectangle for this component into the
+ * appropriate ancestral {@link JRootPane} and call {@link #paint} on
+ * that root pane. This method is called from the {@link RepaintManager}
+ * and should always be called within the painting thread.
+ *
+ * <p>This method will acquire a double buffer from the {@link
+ * RepaintManager} if the component's {@link #doubleBuffered} property is
+ * <code>true</code> and the <code>paint</code> call is the
+ * <em>first</em> recursive <code>paint</code> call inside swing.</p>
+ *
+ * <p>The method will also modify the provided {@link Graphics} context
+ * via the {@link #getComponentGraphics} method. If you want to customize
+ * the graphics object used for painting, you should override that method
+ * rather than <code>paint</code>.</p>
+ *
+ * @param r The dirty rectangle to paint
+ */
+ public void paintImmediately(Rectangle r)
+ {
+ paintImmediately(r.x, r.y, r.width, r.height);
+ }
+
+ /**
+ * Performs the actual work of paintImmediatly on the repaint root.
+ *
+ * @param x the area to be repainted, X coordinate
+ * @param y the area to be repainted, Y coordinate
+ */
+ void paintImmediately2(int x, int y, int w, int h)
+ {
+ // Optimization for components that are always painted on top.
+ boolean onTop = onTop() && isOpaque();
+
+ // Fetch the RepaintManager.
+ RepaintManager rm = RepaintManager.currentManager(this);
+
+ // The painting clip;
+ int paintX = x;
+ int paintY = y;
+ int paintW = w;
+ int paintH = h;
+
+ // If we should paint buffered or not.
+ boolean haveBuffer = false;
+
+ // The component that is finally triggered for painting.
+ JComponent paintRoot = this;
+
+ // Stores the component and all its parents. This will be used to limit
+ // the actually painted components in paintChildren by setting
+ // the field paintChild.
+ int pIndex = -1;
+ int pCount = 0;
+ ArrayList components = new ArrayList();
+
+ // Offset to subtract from the paintRoot rectangle when painting.
+ int offsX = 0;
+ int offsY = 0;
+
+ // The current component and its child.
+ Component child;
+ Container c;
+
+ // Find appropriate paint root.
+ for (c = this, child = null;
+ c != null && ! (c instanceof Window) && ! (c instanceof Applet);
+ child = c, c = c.getParent())
+ {
+ JComponent jc = c instanceof JComponent ? (JComponent) c : null;
+ components.add(c);
+ if (! onTop && jc != null && ! jc.isOptimizedDrawingEnabled())
+ {
+ // Indicates whether we reset the paint root to be the current
+ // component.
+ boolean updatePaintRoot = false;
+
+ // Check obscured state of the child.
+ // Generally, we have 3 cases here:
+ // 1. Not obscured. No need to paint from the parent.
+ // 2. Partially obscured. Paint from the parent.
+ // 3. Completely obscured. No need to paint anything.
+ if (c != this)
+ {
+ if (jc.isPaintRoot())
+ updatePaintRoot = true;
+ else
+ {
+ int count = c.getComponentCount();
+ int i = 0;
+ for (; i < count && c.getComponent(i) != child; i++)
+ ;
+
+ if (jc.isCompletelyObscured(i, paintX, paintY, paintW,
+ paintH))
+ return; // No need to paint anything.
+ else if (jc.isPartiallyObscured(i, paintX, paintY, paintW,
+ paintH))
+ updatePaintRoot = true;
+
+ }
+ }
+ if (updatePaintRoot)
+ {
+ // Paint from parent.
+ paintRoot = jc;
+ pIndex = pCount;
+ offsX = 0;
+ offsY = 0;
+ haveBuffer = false;
+ }
+ }
+ pCount++;
+ // Check if component is double buffered.
+ if (rm.isDoubleBufferingEnabled() && jc != null
+ && jc.isDoubleBuffered())
+ {
+ haveBuffer = true;
+ }
+
+ // Clip the paint region with the parent.
+ if (! onTop)
+ {
+ paintX = Math.max(0, paintX);
+ paintY = Math.max(0, paintY);
+ paintW = Math.min(c.getWidth(), paintW + paintX) - paintX;
+ paintH = Math.min(c.getHeight(), paintH + paintY) - paintY;
+ int dx = c.getX();
+ int dy = c.getY();
+ paintX += dx;
+ paintY += dy;
+ offsX += dx;
+ offsY += dy;
+ }
+ }
+ if (c != null && c.getPeer() != null && paintW > 0 && paintH > 0)
+ {
+ isRepainting = true;
+ paintX -= offsX;
+ paintY -= offsY;
+
+ // Set the painting path so that paintChildren paints only what we
+ // want.
+ if (paintRoot != this)
+ {
+ for (int i = pIndex; i > 0; i--)
+ {
+ Component paintParent = (Component) components.get(i);
+ if (paintParent instanceof JComponent)
+ ((JComponent) paintParent).paintChild =
+ (Component) components.get(i - 1);
+ }
+ }
+
+ // Actually trigger painting.
+ if (haveBuffer)
+ paintRoot.paintDoubleBuffered(paintX, paintY, paintW, paintH);
+ else
+ {
+ Graphics g = paintRoot.getGraphics();
+ try
+ {
+ g.setClip(paintX, paintY, paintW, paintH);
+ paintRoot.paint(g);
+ }
+ finally
+ {
+ g.dispose();
+ }
+ }
+
+ // Reset the painting path.
+ if (paintRoot != this)
+ {
+ for (int i = pIndex; i > 0; i--)
+ {
+ Component paintParent = (Component) components.get(i);
+ if (paintParent instanceof JComponent)
+ ((JComponent) paintParent).paintChild = null;
+ }
+ }
+
+ isRepainting = false;
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return false;
+ }
+
+ /**
+ * This returns true when a component needs to force itself as a paint
+ * origin. This is used for example in JViewport to make sure that it
+ * gets to update its backbuffer.
+ *
+ * @return true when a component needs to force itself as a paint
+ * origin
+ */
+ boolean isPaintRoot()
+ {
+ return false;
+ }
+
+ /**
+ * Performs double buffered repainting.
+ */
+ private void paintDoubleBuffered(int x, int y, int w, int h)
+ {
+ RepaintManager rm = RepaintManager.currentManager(this);
+
+ // Paint on the offscreen buffer.
+ Component root = SwingUtilities.getRoot(this);
+ Image buffer = rm.getVolatileOffscreenBuffer(this, root.getWidth(),
+ root.getHeight());
+
+ // The volatile offscreen buffer may be null when that's not supported
+ // by the AWT backend. Fall back to normal backbuffer in this case.
+ if (buffer == null)
+ buffer = rm.getOffscreenBuffer(this, root.getWidth(), root.getHeight());
+
+ //Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root);
+ Graphics g2 = buffer.getGraphics();
+ clipAndTranslateGraphics(root, this, g2);
+ g2.clipRect(x, y, w, h);
+ g2 = getComponentGraphics(g2);
+ paintingDoubleBuffered = true;
+ try
+ {
+ if (isRepainting) // Called from paintImmediately, go through paint().
+ paint(g2);
+ else // Called from paint() (AWT refresh), don't call it again.
+ {
+ paintComponent(g2);
+ paintBorder(g2);
+ paintChildren(g2);
+ }
+ }
+ finally
+ {
+ paintingDoubleBuffered = false;
+ g2.dispose();
+ }
+
+ // Paint the buffer contents on screen.
+ rm.commitBuffer(this, x, y, w, h);
+ }
+
+ /**
+ * Clips and translates the Graphics instance for painting on the double
+ * buffer. This has to be done, so that it reflects the component clip of the
+ * target component.
+ *
+ * @param root the root component (top-level container usually)
+ * @param target the component to be painted
+ * @param g the Graphics instance
+ */
+ private void clipAndTranslateGraphics(Component root, Component target,
+ Graphics g)
+ {
+ Component parent = target;
+ int deltaX = 0;
+ int deltaY = 0;
+ while (parent != root)
+ {
+ deltaX += parent.getX();
+ deltaY += parent.getY();
+ parent = parent.getParent();
+ }
+ g.translate(deltaX, deltaY);
+ g.clipRect(0, 0, target.getWidth(), target.getHeight());
+ }
+
+ /**
+ * Performs normal painting without double buffering.
+ *
+ * @param r the area that should be repainted
+ */
+ void paintSimple(Rectangle r)
+ {
+ Graphics g = getGraphics();
+ Graphics g2 = getComponentGraphics(g);
+ g2.setClip(r);
+ paint(g2);
+ g2.dispose();
+ if (g != g2)
+ g.dispose();
+ }
+
+ /**
+ * Return a string representation for this component, for use in
+ * debugging.
+ *
+ * @return A string describing this component.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(super.paramString());
+ sb.append(",alignmentX=").append(getAlignmentX());
+ sb.append(",alignmentY=").append(getAlignmentY());
+ sb.append(",border=");
+ if (getBorder() != null)
+ sb.append(getBorder());
+ sb.append(",maximumSize=");
+ if (getMaximumSize() != null)
+ sb.append(getMaximumSize());
+ sb.append(",minimumSize=");
+ if (getMinimumSize() != null)
+ sb.append(getMinimumSize());
+ sb.append(",preferredSize=");
+ if (getPreferredSize() != null)
+ sb.append(getPreferredSize());
+ return sb.toString();
+ }
+
+ /**
+ * A variant of {@link
+ * #registerKeyboardAction(ActionListener,String,KeyStroke,int)} which
+ * provides <code>null</code> for the command name.
+ *
+ * @param act the action listener to notify when the keystroke occurs.
+ * @param stroke the key stroke.
+ * @param cond the condition (one of {@link #WHEN_FOCUSED},
+ * {@link #WHEN_IN_FOCUSED_WINDOW} and
+ * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}).
+ */
+ public void registerKeyboardAction(ActionListener act,
+ KeyStroke stroke,
+ int cond)
+ {
+ 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>
+ * to fetch mapping tables from keystrokes to commands, and commands to
+ * actions, respectively, and modify those mappings directly.
+ *
+ * @param act The action to be registered
+ * @param cmd The command to deliver in the delivered {@link
+ * java.awt.event.ActionEvent}
+ * @param stroke The keystroke to register on
+ * @param cond One of the values {@link #UNDEFINED_CONDITION},
+ * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}, {@link #WHEN_FOCUSED}, or
+ * {@link #WHEN_IN_FOCUSED_WINDOW}, indicating the condition which must
+ * be met for the action to be fired
+ *
+ * @see #unregisterKeyboardAction
+ * @see #getConditionForKeyStroke
+ * @see #resetKeyboardActions
+ */
+ public void registerKeyboardAction(ActionListener act,
+ String cmd,
+ KeyStroke stroke,
+ int cond)
+ {
+ ActionListenerProxy proxy = new ActionListenerProxy(act, cmd);
+ getInputMap(cond).put(stroke, proxy);
+ getActionMap().put(proxy, proxy);
+ }
+
+ /**
+ * Sets the input map for the given condition.
+ *
+ * @param condition the condition (one of {@link #WHEN_FOCUSED},
+ * {@link #WHEN_IN_FOCUSED_WINDOW} and
+ * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}).
+ * @param map the map.
+ *
+ * @throws IllegalArgumentException if <code>condition</code> is not one of
+ * the specified values.
+ */
+ 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:
+ if (map != null && !(map instanceof ComponentInputMap))
+ throw new
+ IllegalArgumentException("WHEN_IN_FOCUSED_WINDOW " +
+ "InputMap must be a ComponentInputMap");
+ inputMap_whenInFocusedWindow = (ComponentInputMap)map;
+ break;
+
+ case UNDEFINED_CONDITION:
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Returns the input map associated with this component for the given
+ * state/condition.
+ *
+ * @param condition the state (one of {@link #WHEN_FOCUSED},
+ * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT} and
+ * {@link #WHEN_IN_FOCUSED_WINDOW}).
+ *
+ * @return The input map.
+ * @throws IllegalArgumentException if <code>condition</code> is not one of
+ * the specified values.
+ * @since 1.3
+ */
+ 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 ComponentInputMap(this);
+ return inputMap_whenInFocusedWindow;
+
+ case UNDEFINED_CONDITION:
+ default:
+ throw new IllegalArgumentException("Invalid 'condition' argument: "
+ + condition);
+ }
+ }
+
+ /**
+ * Returns the input map associated with this component for the
+ * {@link #WHEN_FOCUSED} state.
+ *
+ * @return The input map.
+ *
+ * @since 1.3
+ * @see #getInputMap(int)
+ */
+ 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;
+ }
+
+ /**
+ * Return the condition that determines whether a registered action
+ * occurs in response to the specified keystroke.
+ *
+ * As of 1.3 KeyStrokes can be registered with multiple simultaneous
+ * conditions.
+ *
+ * @param ks 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(ActionListener, KeyStroke, int)
+ * @see #unregisterKeyboardAction
+ * @see #resetKeyboardActions
+ */
+ 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;
+ }
+
+ /**
+ * Get the ActionListener (typically an {@link Action} object) which is
+ * associated with a particular keystroke.
+ *
+ * @param ks The keystroke to retrieve the action of
+ *
+ * @return The action associated with the specified keystroke
+ */
+ public ActionListener getActionForKeyStroke(KeyStroke ks)
+ {
+ Object key = getInputMap(JComponent.WHEN_FOCUSED).get(ks);
+ if (key == null)
+ key = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(ks);
+ if (key == null)
+ key = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).get(ks);
+ if (key != null)
+ {
+ if (key instanceof ActionListenerProxy)
+ return ((ActionListenerProxy) key).target;
+ else
+ return getActionMap().get(key);
+ }
+ return null;
+ }
+
+ /**
+ * A hook for subclasses which want to customize event processing.
+ */
+ protected void processComponentKeyEvent(KeyEvent e)
+ {
+ // This method does nothing, it is meant to be overridden by subclasses.
+ }
+
+ /**
+ * 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)
+ {
+ // let the AWT event processing send KeyEvents to registered listeners
+ super.processKeyEvent(e);
+ processComponentKeyEvent(e);
+
+ if (e.isConsumed())
+ return;
+
+ // Input maps are checked in this order:
+ // 1. The focused component's WHEN_FOCUSED map is checked.
+ // 2. The focused component's WHEN_ANCESTOR_OF_FOCUSED_COMPONENT map.
+ // 3. The WHEN_ANCESTOR_OF_FOCUSED_COMPONENT maps of the focused
+ // component's parent, then its parent's parent, and so on.
+ // Note: Input maps for disabled components are skipped.
+ // 4. The WHEN_IN_FOCUSED_WINDOW maps of all the enabled components in
+ // the focused window are searched.
+
+ KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e);
+ boolean pressed = e.getID() == KeyEvent.KEY_PRESSED;
+
+ if (processKeyBinding(keyStroke, e, WHEN_FOCUSED, pressed))
+ {
+ // This is step 1 from above comment.
+ e.consume();
+ return;
+ }
+ else if (processKeyBinding
+ (keyStroke, e, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed))
+ {
+ // This is step 2 from above comment.
+ e.consume();
+ return;
+ }
+
+ // This is step 3 from above comment.
+ Container current = getParent();
+ while (current != null)
+ {
+ // If current is a JComponent, see if it handles the event in its
+ // WHEN_ANCESTOR_OF_FOCUSED_COMPONENT maps.
+ if ((current instanceof JComponent) &&
+ ((JComponent)current).processKeyBinding
+ (keyStroke, e,WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed))
+ {
+ e.consume();
+ return;
+ }
+
+ // Stop when we've tried a top-level container and it didn't handle it
+ if (current instanceof Window || current instanceof Applet)
+ break;
+
+ // Move up the hierarchy
+ current = current.getParent();
+ }
+
+ // Current being null means the JComponent does not currently have a
+ // top-level ancestor, in which case we don't need to check
+ // WHEN_IN_FOCUSED_WINDOW bindings.
+ if (current == null || e.isConsumed())
+ return;
+
+ // This is step 4 from above comment. KeyboardManager maintains mappings
+ // related to WHEN_IN_FOCUSED_WINDOW bindings so that we don't have to
+ // traverse the containment hierarchy each time.
+ if (KeyboardManager.getManager().processKeyStroke(current, keyStroke, e))
+ e.consume();
+ }
+
+ protected boolean processKeyBinding(KeyStroke ks,
+ KeyEvent e,
+ int condition,
+ boolean pressed)
+ {
+ if (isEnabled())
+ {
+ Action act = null;
+ Object cmd = null;
+ InputMap map = getInputMap(condition);
+ if (map != null)
+ {
+ cmd = map.get(ks);
+ if (cmd != null)
+ {
+ if (cmd instanceof ActionListenerProxy)
+ act = (Action) cmd;
+ else
+ act = getActionMap().get(cmd);
+ }
+ }
+ if (act != null && act.isEnabled())
+ {
+ // Need to synchronize here so we don't get in trouble with
+ // our __command__ hack.
+ synchronized (act)
+ {
+ // We add the command as value to the action, so that
+ // the action can later determine the command with which it
+ // was called. This is undocumented, but shouldn't affect
+ // compatibility. It allows us to use only one Action instance
+ // to do the work for all components of one type, instead of
+ // having loads of small Actions. This effectivly saves startup
+ // time of Swing.
+ act.putValue("__command__", cmd);
+ return SwingUtilities.notifyAction(act, ks, e, this,
+ e.getModifiers());
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove a keyboard action registry.
+ *
+ * @param aKeyStroke The keystroke to unregister
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ * @see #getConditionForKeyStroke
+ * @see #resetKeyboardActions
+ */
+ public void unregisterKeyboardAction(KeyStroke aKeyStroke)
+ {
+ ActionMap am = getActionMap();
+ // This loops through the conditions WHEN_FOCUSED,
+ // WHEN_ANCESTOR_OF_FOCUSED_COMPONENT and WHEN_IN_FOCUSED_WINDOW.
+ for (int cond = 0; cond < 3; cond++)
+ {
+ InputMap im = getInputMap(cond);
+ if (im != null)
+ {
+ Object action = im.get(aKeyStroke);
+ if (action != null && am != null)
+ am.remove(action);
+ im.remove(aKeyStroke);
+ }
+ }
+ }
+
+
+ /**
+ * Reset all keyboard action registries.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ * @see #unregisterKeyboardAction
+ * @see #getConditionForKeyStroke
+ */
+ public void resetKeyboardActions()
+ {
+ if (inputMap_whenFocused != null)
+ inputMap_whenFocused.clear();
+ if (inputMap_whenAncestorOfFocused != null)
+ inputMap_whenAncestorOfFocused.clear();
+ if (inputMap_whenInFocusedWindow != null)
+ inputMap_whenInFocusedWindow.clear();
+ if (actionMap != null)
+ actionMap.clear();
+ }
+
+ /**
+ * Mark the described region of this component as dirty in the current
+ * {@link RepaintManager}. This will queue an asynchronous repaint using
+ * the system painting thread in the near future.
+ *
+ * @param tm ignored
+ * @param x coordinate of the region to mark as dirty
+ * @param y coordinate of the region to mark as dirty
+ * @param width dimension of the region to mark as dirty
+ * @param height dimension of the region to mark as dirty
+ */
+ public void repaint(long tm, int x, int y, int width, int height)
+ {
+ RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width,
+ height);
+ }
+
+ /**
+ * Mark the described region of this component as dirty in the current
+ * {@link RepaintManager}. This will queue an asynchronous repaint using
+ * the system painting thread in the near future.
+ *
+ * @param r The rectangle to mark as dirty
+ */
+ public void repaint(Rectangle r)
+ {
+ RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y, r.width,
+ r.height);
+ }
+
+ /**
+ * Request focus on the default component of this component's {@link
+ * FocusTraversalPolicy}.
+ *
+ * @return The result of {@link #requestFocus()}
+ *
+ * @deprecated Use {@link #requestFocus()} on the default component provided
+ * from the {@link FocusTraversalPolicy} instead.
+ */
+ public boolean requestDefaultFocus()
+ {
+ return false;
+ }
+
+ /**
+ * Queue a an invalidation and revalidation of this component, using
+ * {@link RepaintManager#addInvalidComponent}.
+ */
+ public void revalidate()
+ {
+ // As long as we don't have a parent we don't need to do any layout, since
+ // this is done anyway as soon as we get connected to a parent.
+ if (getParent() == null)
+ return;
+
+ if (! EventQueue.isDispatchThread())
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ revalidate();
+ }
+ });
+ else
+ {
+ invalidate();
+ RepaintManager.currentManager(this).addInvalidComponent(this);
+ }
+ }
+
+ /**
+ * Calls <code>scrollRectToVisible</code> on the component's parent.
+ * Components which can service this call should override.
+ *
+ * @param r The rectangle to make visible
+ */
+ public void scrollRectToVisible(Rectangle r)
+ {
+ // Search nearest JComponent.
+ int xOffs = getX();
+ int yOffs = getY();
+ Component p;
+ for (p = getParent(); p != null && ! (p instanceof JComponent);
+ p = p.getParent())
+ {
+ xOffs += p.getX();
+ yOffs += p.getY();
+ }
+ if (p != null)
+ {
+ r.x += xOffs;
+ r.y += yOffs;
+ JComponent jParent = (JComponent) p;
+ jParent.scrollRectToVisible(r);
+ r.x -= xOffs;
+ r.y -= yOffs;
+ }
+ }
+
+ /**
+ * Set the value of the {@link #alignmentX} property.
+ *
+ * @param a The new value of the property
+ */
+ public void setAlignmentX(float a)
+ {
+ if (a < 0.0F)
+ alignmentX = 0.0F;
+ else if (a > 1.0)
+ alignmentX = 1.0F;
+ else
+ alignmentX = a;
+ }
+
+ /**
+ * Set the value of the {@link #alignmentY} property.
+ *
+ * @param a The new value of the property
+ */
+ public void setAlignmentY(float a)
+ {
+ if (a < 0.0F)
+ alignmentY = 0.0F;
+ else if (a > 1.0)
+ alignmentY = 1.0F;
+ else
+ alignmentY = a;
+ }
+
+ /**
+ * Set the value of the {@link #autoscrolls} property.
+ *
+ * @param a The new value of the property
+ */
+ public void setAutoscrolls(boolean a)
+ {
+ autoscrolls = a;
+ clientAutoscrollsSet = true;
+ }
+
+ /**
+ * Set the value of the {@link #debugGraphicsOptions} property.
+ *
+ * @param debugOptions The new value of the property
+ */
+ public void setDebugGraphicsOptions(int debugOptions)
+ {
+ debugGraphicsOptions = debugOptions;
+ }
+
+ /**
+ * Set the value of the {@link #doubleBuffered} property.
+ *
+ * @param db The new value of the property
+ */
+ public void setDoubleBuffered(boolean db)
+ {
+ doubleBuffered = db;
+ }
+
+ /**
+ * Set the value of the <code>enabled</code> property.
+ *
+ * @param enable The new value of the property
+ */
+ public void setEnabled(boolean enable)
+ {
+ if (enable == isEnabled())
+ return;
+ super.setEnabled(enable);
+ firePropertyChange("enabled", !enable, enable);
+ repaint();
+ }
+
+ /**
+ * Set the value of the <code>font</code> property.
+ *
+ * @param f The new value of the property
+ */
+ public void setFont(Font f)
+ {
+ if (f == getFont())
+ return;
+ super.setFont(f);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the <code>background</code> property.
+ *
+ * @param bg The new value of the property
+ */
+ public void setBackground(Color bg)
+ {
+ if (bg == getBackground())
+ return;
+ super.setBackground(bg);
+ repaint();
+ }
+
+ /**
+ * Set the value of the <code>foreground</code> property.
+ *
+ * @param fg The new value of the property
+ */
+ public void setForeground(Color fg)
+ {
+ if (fg == getForeground())
+ return;
+ super.setForeground(fg);
+ repaint();
+ }
+
+ /**
+ * Set the specified component to be the next component in the
+ * focus cycle, overriding the {@link FocusTraversalPolicy} for
+ * this component.
+ *
+ * @param aComponent The component to set as the next focusable
+ *
+ * @deprecated Use FocusTraversalPolicy instead
+ */
+ public void setNextFocusableComponent(Component aComponent)
+ {
+ Container focusRoot = this;
+ if (! this.isFocusCycleRoot())
+ focusRoot = getFocusCycleRootAncestor();
+
+ FocusTraversalPolicy policy = focusRoot.getFocusTraversalPolicy();
+ if (policy instanceof CompatibilityFocusTraversalPolicy)
+ {
+ policy = new CompatibilityFocusTraversalPolicy(policy);
+ focusRoot.setFocusTraversalPolicy(policy);
+ }
+ CompatibilityFocusTraversalPolicy p =
+ (CompatibilityFocusTraversalPolicy) policy;
+
+ Component old = getNextFocusableComponent();
+ if (old != null)
+ {
+ p.removeNextFocusableComponent(this, old);
+ }
+
+ if (aComponent != null)
+ {
+ p.addNextFocusableComponent(this, aComponent);
+ }
+ }
+
+ /**
+ * Set the value of the {@link #requestFocusEnabled} property.
+ *
+ * @param e The new value of the property
+ */
+ public void setRequestFocusEnabled(boolean e)
+ {
+ requestFocusEnabled = e;
+ }
+
+ /**
+ * Get the value of the {@link #transferHandler} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setTransferHandler
+ */
+
+ public TransferHandler getTransferHandler()
+ {
+ return transferHandler;
+ }
+
+ /**
+ * Set the value of the {@link #transferHandler} property.
+ *
+ * @param newHandler The new value of the property
+ *
+ * @see #getTransferHandler
+ */
+
+ public void setTransferHandler(TransferHandler newHandler)
+ {
+ if (transferHandler == newHandler)
+ return;
+
+ TransferHandler oldHandler = transferHandler;
+ transferHandler = newHandler;
+ firePropertyChange("transferHandler", oldHandler, newHandler);
+ }
+
+ /**
+ * Set if the component should paint all pixels withing its bounds.
+ * If this property is set to false, the component expects the cleared
+ * background.
+ *
+ * @param isOpaque if true, paint all pixels. If false, expect the clean
+ * background.
+ *
+ * @see ComponentUI#update
+ */
+ public void setOpaque(boolean isOpaque)
+ {
+ boolean oldOpaque = opaque;
+ opaque = isOpaque;
+ clientOpaqueSet = true;
+ firePropertyChange("opaque", oldOpaque, opaque);
+ }
+
+ /**
+ * Set the value of the visible property.
+ *
+ * If the value is changed, then the AncestorListeners of this component
+ * and all its children (recursivly) are notified.
+ *
+ * @param v The new value of the property
+ */
+ public void setVisible(boolean v)
+ {
+ // No need to do anything if the actual value doesn't change.
+ if (isVisible() == v)
+ return;
+
+ super.setVisible(v);
+
+ // Notify AncestorListeners.
+ if (v == true)
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED);
+ else
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED);
+
+ Container parent = getParent();
+ if (parent != null)
+ parent.repaint(getX(), getY(), getWidth(), getHeight());
+ revalidate();
+ }
+
+ /**
+ * Call {@link #paint}.
+ *
+ * @param g The graphics context to paint into
+ */
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+ /**
+ * Get the value of the UIClassID property. This property should be a key
+ * in the {@link UIDefaults} table managed by {@link UIManager}, the
+ * value of which is the name of a class to load for the component's
+ * {@link #ui} property.
+ *
+ * @return A "symbolic" name which will map to a class to use for the
+ * component's UI, such as <code>"ComponentUI"</code>
+ *
+ * @see #setUI
+ * @see #updateUI
+ */
+ public String getUIClassID()
+ {
+ return "ComponentUI";
+ }
+
+ /**
+ * Install a new UI delegate as the component's {@link #ui} property. In
+ * the process, this will call {@link ComponentUI#uninstallUI} on any
+ * existing value for the {@link #ui} property, and {@link
+ * ComponentUI#installUI} on the new UI delegate.
+ *
+ * @param newUI The new UI delegate to install
+ *
+ * @see #updateUI
+ * @see #getUIClassID
+ */
+ protected void setUI(ComponentUI newUI)
+ {
+ if (ui != null)
+ ui.uninstallUI(this);
+
+ ComponentUI oldUI = ui;
+ ui = newUI;
+
+ if (ui != null)
+ ui.installUI(this);
+
+ firePropertyChange("UI", oldUI, newUI);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * This method should be overridden in subclasses. In JComponent, the
+ * method does nothing. In subclasses, it should a UI delegate
+ * (corresponding to the symbolic name returned from {@link
+ * #getUIClassID}) from the {@link UIManager}, and calls {@link #setUI}
+ * with the new delegate.
+ */
+ public void updateUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the locale used as the default for all new components. The
+ * default value is {@link Locale#getDefault()} (that is, the platform
+ * default locale).
+ *
+ * @return The locale (never <code>null</code>).
+ *
+ * @see #setDefaultLocale(Locale)
+ */
+ public static Locale getDefaultLocale()
+ {
+ if (defaultLocale == null)
+ defaultLocale = Locale.getDefault();
+ return defaultLocale;
+ }
+
+ /**
+ * Sets the locale to be used as the default for all new components. If this
+ * is set to <code>null</code>, the {@link #getDefaultLocale()} method will
+ * return the platform default locale.
+ *
+ * @param l the locale (<code>null</code> permitted).
+ */
+ public static void setDefaultLocale(Locale l)
+ {
+ defaultLocale = l;
+ }
+
+ /**
+ * Returns the currently set input verifier for this component.
+ *
+ * @return the input verifier, or <code>null</code> if none
+ */
+ public InputVerifier getInputVerifier()
+ {
+ return inputVerifier;
+ }
+
+ /**
+ * Sets the input verifier to use by this component.
+ *
+ * @param verifier the input verifier, or <code>null</code>
+ */
+ public void setInputVerifier(InputVerifier verifier)
+ {
+ InputVerifier oldVerifier = inputVerifier;
+ inputVerifier = verifier;
+ firePropertyChange("inputVerifier", oldVerifier, verifier);
+ }
+
+ /**
+ * @since 1.3
+ */
+ public boolean getVerifyInputWhenFocusTarget()
+ {
+ return verifyInputWhenFocusTarget;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setVerifyInputWhenFocusTarget(boolean verifyInputWhenFocusTarget)
+ {
+ if (this.verifyInputWhenFocusTarget == verifyInputWhenFocusTarget)
+ return;
+
+ this.verifyInputWhenFocusTarget = verifyInputWhenFocusTarget;
+ firePropertyChange("verifyInputWhenFocusTarget",
+ ! verifyInputWhenFocusTarget,
+ verifyInputWhenFocusTarget);
+ }
+
+ /**
+ * Requests that this component gets the input focus if the
+ * requestFocusEnabled property is set to <code>true</code>.
+ * This also means that this component's top-level window becomes
+ * the focused window, if that is not already the case.
+ *
+ * The preconditions that have to be met to become a focus owner is that
+ * the component must be displayable, visible and focusable.
+ *
+ * Note that this signals only a request for becoming focused. There are
+ * situations in which it is not possible to get the focus. So developers
+ * should not assume that the component has the focus until it receives
+ * a {@link java.awt.event.FocusEvent} with a value of
+ * {@link java.awt.event.FocusEvent#FOCUS_GAINED}.
+ *
+ * @see Component#requestFocus()
+ */
+ public void requestFocus()
+ {
+ if (isRequestFocusEnabled())
+ super.requestFocus();
+ }
+
+ /**
+ * This method is overridden to make it public so that it can be used
+ * by look and feel implementations.
+ *
+ * You should not use this method directly. Instead you are strongly
+ * encouraged to call {@link #requestFocus()} or
+ * {@link #requestFocusInWindow()} instead.
+ *
+ * @param temporary if the focus change is temporary
+ *
+ * @return <code>false</code> if the focus change request will definitly
+ * fail, <code>true</code> if it will likely succeed
+ *
+ * @see Component#requestFocus(boolean)
+ *
+ * @since 1.4
+ */
+ public boolean requestFocus(boolean temporary)
+ {
+ return super.requestFocus(temporary);
+ }
+
+ /**
+ * Requests that this component gets the input focus if the top level
+ * window that contains this component has the focus and the
+ * requestFocusEnabled property is set to <code>true</code>.
+ *
+ * The preconditions that have to be met to become a focus owner is that
+ * the component must be displayable, visible and focusable.
+ *
+ * Note that this signals only a request for becoming focused. There are
+ * situations in which it is not possible to get the focus. So developers
+ * should not assume that the component has the focus until it receives
+ * a {@link java.awt.event.FocusEvent} with a value of
+ * {@link java.awt.event.FocusEvent#FOCUS_GAINED}.
+ *
+ * @return <code>false</code> if the focus change request will definitly
+ * fail, <code>true</code> if it will likely succeed
+ *
+ * @see Component#requestFocusInWindow()
+ */
+ public boolean requestFocusInWindow()
+ {
+ if (isRequestFocusEnabled())
+ return super.requestFocusInWindow();
+ else
+ return false;
+ }
+
+ /**
+ * This method is overridden to make it public so that it can be used
+ * by look and feel implementations.
+ *
+ * You should not use this method directly. Instead you are strongly
+ * encouraged to call {@link #requestFocus()} or
+ * {@link #requestFocusInWindow()} instead.
+ *
+ * @param temporary if the focus change is temporary
+ *
+ * @return <code>false</code> if the focus change request will definitly
+ * fail, <code>true</code> if it will likely succeed
+ *
+ * @see Component#requestFocus(boolean)
+ *
+ * @since 1.4
+ */
+ protected boolean requestFocusInWindow(boolean temporary)
+ {
+ return super.requestFocusInWindow(temporary);
+ }
+
+ /**
+ * Receives notification if this component is added to a parent component.
+ *
+ * Notification is sent to all registered AncestorListeners about the
+ * new parent.
+ *
+ * This method sets up ActionListeners for all registered KeyStrokes of
+ * this component in the chain of parent components.
+ *
+ * A PropertyChange event is fired to indicate that the ancestor property
+ * has changed.
+ *
+ * This method is used internally and should not be used in applications.
+ */
+ public void addNotify()
+ {
+ // Register the WHEN_IN_FOCUSED_WINDOW keyboard bindings
+ // Note that here we unregister all bindings associated with
+ // this component and then re-register them. This may be more than
+ // necessary if the top-level ancestor hasn't changed. Should
+ // maybe improve this.
+ KeyboardManager km = KeyboardManager.getManager();
+ km.clearBindingsForComp(this);
+ km.registerEntireMap((ComponentInputMap)
+ this.getInputMap(WHEN_IN_FOCUSED_WINDOW));
+ super.addNotify();
+
+ // Notify AncestorListeners.
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED);
+
+ // fire property change event for 'ancestor'
+ firePropertyChange("ancestor", null, getParent());
+ }
+
+ /**
+ * Receives notification that this component no longer has a parent.
+ *
+ * This method sends an AncestorEvent to all registered AncestorListeners,
+ * notifying them that the parent is gone.
+ *
+ * The keybord actions of this component are removed from the parent and
+ * its ancestors.
+ *
+ * A PropertyChangeEvent is fired to indicate that the 'ancestor' property
+ * has changed.
+ *
+ * This method is called before the component is actually removed from
+ * its parent, so the parent is still visible through
+ * {@link Component#getParent}.
+ */
+ public void removeNotify()
+ {
+ super.removeNotify();
+
+ KeyboardManager.getManager().clearBindingsForComp(this);
+
+ // Notify ancestor listeners.
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED);
+
+ // fire property change event for 'ancestor'
+ firePropertyChange("ancestor", getParent(), null);
+ }
+
+ /**
+ * Returns <code>true</code> if the coordinates (x, y) lie within
+ * the bounds of this component and <code>false</code> otherwise.
+ * x and y are relative to the coordinate space of the component.
+ *
+ * @param x the X coordinate of the point to check
+ * @param y the Y coordinate of the point to check
+ *
+ * @return <code>true</code> if the specified point lies within the bounds
+ * of this component, <code>false</code> otherwise
+ */
+ public boolean contains(int x, int y)
+ {
+ if (ui == null)
+ return super.contains(x, y);
+ else
+ return ui.contains(this, x, y);
+ }
+
+ /**
+ * Disables this component.
+ *
+ * @deprecated replaced by {@link #setEnabled(boolean)}
+ */
+ public void disable()
+ {
+ super.disable();
+ }
+
+ /**
+ * Enables this component.
+ *
+ * @deprecated replaced by {@link #setEnabled(boolean)}
+ */
+ public void enable()
+ {
+ super.enable();
+ }
+
+ /**
+ * Returns the Graphics context for this component. This can be used
+ * to draw on a component.
+ *
+ * @return the Graphics context for this component
+ */
+ public Graphics getGraphics()
+ {
+ return super.getGraphics();
+ }
+
+ /**
+ * Returns the X coordinate of the upper left corner of this component.
+ * Prefer this method over {@link #getBounds} or {@link #getLocation}
+ * because it does not cause any heap allocation.
+ *
+ * @return the X coordinate of the upper left corner of the component
+ */
+ public int getX()
+ {
+ return super.getX();
+ }
+
+ /**
+ * Returns the Y coordinate of the upper left corner of this component.
+ * Prefer this method over {@link #getBounds} or {@link #getLocation}
+ * because it does not cause any heap allocation.
+ *
+ * @return the Y coordinate of the upper left corner of the component
+ */
+ public int getY()
+ {
+ return super.getY();
+ }
+
+ /**
+ * Returns the height of this component. Prefer this method over
+ * {@link #getBounds} or {@link #getSize} because it does not cause
+ * any heap allocation.
+ *
+ * @return the height of the component
+ */
+ public int getHeight()
+ {
+ return super.getHeight();
+ }
+
+ /**
+ * Returns the width of this component. Prefer this method over
+ * {@link #getBounds} or {@link #getSize} because it does not cause
+ * any heap allocation.
+ *
+ * @return the width of the component
+ */
+ public int getWidth()
+ {
+ return super.getWidth();
+ }
+
+ /**
+ * Prints this component to the given Graphics context. A call to this
+ * method results in calls to the methods {@link #printComponent},
+ * {@link #printBorder} and {@link #printChildren} in this order.
+ *
+ * Double buffering is temporarily turned off so the painting goes directly
+ * to the supplied Graphics context.
+ *
+ * @param g the Graphics context to print onto
+ */
+ public void print(Graphics g)
+ {
+ boolean doubleBufferState = isDoubleBuffered();
+ setDoubleBuffered(false);
+ printComponent(g);
+ printBorder(g);
+ printChildren(g);
+ setDoubleBuffered(doubleBufferState);
+ }
+
+ /**
+ * Prints this component to the given Graphics context. This invokes
+ * {@link #print}.
+ *
+ * @param g the Graphics context to print onto
+ */
+ public void printAll(Graphics g)
+ {
+ print(g);
+ }
+
+ /**
+ * Prints this component to the specified Graphics context. The default
+ * behaviour is to invoke {@link #paintComponent}. Override this
+ * if you want special behaviour for printing.
+ *
+ * @param g the Graphics context to print onto
+ *
+ * @since 1.3
+ */
+ protected void printComponent(Graphics g)
+ {
+ paintComponent(g);
+ }
+
+ /**
+ * Print this component's children to the specified Graphics context.
+ * The default behaviour is to invoke {@link #paintChildren}. Override this
+ * if you want special behaviour for printing.
+ *
+ * @param g the Graphics context to print onto
+ *
+ * @since 1.3
+ */
+ protected void printChildren(Graphics g)
+ {
+ paintChildren(g);
+ }
+
+ /**
+ * Print this component's border to the specified Graphics context.
+ * The default behaviour is to invoke {@link #paintBorder}. Override this
+ * if you want special behaviour for printing.
+ *
+ * @param g the Graphics context to print onto
+ *
+ * @since 1.3
+ */
+ protected void printBorder(Graphics g)
+ {
+ paintBorder(g);
+ }
+
+ /**
+ * Processes mouse motion event, like dragging and moving.
+ *
+ * @param ev the MouseEvent describing the mouse motion
+ */
+ protected void processMouseMotionEvent(MouseEvent ev)
+ {
+ super.processMouseMotionEvent(ev);
+ }
+
+ /**
+ * Moves and resizes the component.
+ *
+ * @param x the new horizontal location
+ * @param y the new vertial location
+ * @param w the new width
+ * @param h the new height
+ */
+ public void reshape(int x, int y, int w, int h)
+ {
+ int oldX = getX();
+ int oldY = getY();
+ super.reshape(x, y, w, h);
+ // Notify AncestorListeners.
+ if (oldX != getX() || oldY != getY())
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_MOVED);
+ }
+
+ /**
+ * Fires an AncestorEvent to this component's and all of its child
+ * component's AncestorListeners.
+ *
+ * @param ancestor the component that triggered the event
+ * @param id the kind of ancestor event that should be fired
+ */
+ void fireAncestorEvent(JComponent ancestor, int id)
+ {
+ // Fire event for registered ancestor listeners of this component.
+ AncestorListener[] listeners = getAncestorListeners();
+ if (listeners.length > 0)
+ {
+ AncestorEvent ev = new AncestorEvent(this, id,
+ ancestor, ancestor.getParent());
+ for (int i = 0; i < listeners.length; i++)
+ {
+ switch (id)
+ {
+ case AncestorEvent.ANCESTOR_MOVED:
+ listeners[i].ancestorMoved(ev);
+ break;
+ case AncestorEvent.ANCESTOR_ADDED:
+ listeners[i].ancestorAdded(ev);
+ break;
+ case AncestorEvent.ANCESTOR_REMOVED:
+ listeners[i].ancestorRemoved(ev);
+ break;
+ }
+ }
+ }
+ // Dispatch event to all children.
+ int numChildren = getComponentCount();
+ for (int i = 0; i < numChildren; i++)
+ {
+ Component child = getComponent(i);
+ if (! (child instanceof JComponent))
+ continue;
+ JComponent jc = (JComponent) child;
+ jc.fireAncestorEvent(ancestor, id);
+ }
+ }
+
+ /**
+ * This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map
+ * is changed.
+ *
+ * @param changed the JComponent associated with the WHEN_IN_FOCUSED_WINDOW
+ * map
+ */
+ void updateComponentInputMap(ComponentInputMap changed)
+ {
+ // Since you can change a component's input map via
+ // setInputMap, we have to check if <code>changed</code>
+ // is still in our WHEN_IN_FOCUSED_WINDOW map hierarchy
+ InputMap curr = getInputMap(WHEN_IN_FOCUSED_WINDOW);
+ while (curr != null && curr != changed)
+ curr = curr.getParent();
+
+ // If curr is null then changed is not in the hierarchy
+ if (curr == null)
+ return;
+
+ // Now we have to update the keyboard manager's hashtable
+ KeyboardManager km = KeyboardManager.getManager();
+
+ // This is a poor strategy, should be improved. We currently
+ // delete all the old bindings for the component and then register
+ // the current bindings.
+ km.clearBindingsForComp(changed.getComponent());
+ km.registerEntireMap((ComponentInputMap)
+ getInputMap(WHEN_IN_FOCUSED_WINDOW));
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if <code>c</code> or
+ * <code>propertyValue</code> is <code>null</code>
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("opaque"))
+ {
+ if (! clientOpaqueSet)
+ {
+ setOpaque(((Boolean) value).booleanValue());
+ clientOpaqueSet = false;
+ }
+ }
+ else if (propertyName.equals("autoscrolls"))
+ {
+ if (! clientAutoscrollsSet)
+ {
+ setAutoscrolls(((Boolean) value).booleanValue());
+ clientAutoscrollsSet = false;
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException
+ ("Unsupported property for LookAndFeel.installProperty(): "
+ + propertyName);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JDesktopPane.java b/libjava/classpath/javax/swing/JDesktopPane.java
new file mode 100644
index 000000000..6d8b21276
--- /dev/null
+++ b/libjava/classpath/javax/swing/JDesktopPane.java
@@ -0,0 +1,386 @@
+/* JDesktopPane.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.beans.PropertyVetoException;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.DesktopPaneUI;
+
+/**
+ * JDesktopPane is a container (usually for JInternalFrames) that simulates a
+ * desktop. Typically, the user will create JInternalFrames and place them in
+ * a JDesktopPane. The user can then interact with JInternalFrames like they
+ * usually would with JFrames. The actions (minimize, maximize, close, etc)
+ * are done by using a DesktopManager that is associated with the
+ * JDesktopPane.
+ */
+public class JDesktopPane extends JLayeredPane implements Accessible
+{
+ private static final long serialVersionUID = 766333777224038726L;
+
+ /**
+ * This specifies that when dragged, a JInternalFrame should be completely
+ * visible.
+ *
+ * @specnote final since 1.5.0.
+ */
+ public static final int LIVE_DRAG_MODE = 0;
+
+ /**
+ * This specifies that when dragged, a JInternalFrame should only be visible
+ * as an outline.
+ *
+ * @specnote final since 1.5.0.
+ */
+ public static final int OUTLINE_DRAG_MODE = 1;
+
+ /** The selected frame in the JDesktopPane. */
+ private transient JInternalFrame selectedFrame;
+
+ /** The JDesktopManager to use for acting on JInternalFrames. */
+ transient DesktopManager desktopManager;
+
+ /** The drag mode used by the JDesktopPane. */
+ private transient int dragMode = LIVE_DRAG_MODE;
+
+ /**
+ * Indicates if the dragMode property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientDragModeSet = false;
+
+ /**
+ * Provides the accessibility features for the <code>JDesktopPane</code>
+ * component.
+ */
+ protected class AccessibleJDesktopPane extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = 6079388927946077570L;
+
+ /**
+ * Creates a new <code>AccessibleJDesktopPane</code> instance.
+ */
+ protected AccessibleJDesktopPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the <code>JSlider</code> component.
+ *
+ * @return {@link AccessibleRole#DESKTOP_PANE}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.DESKTOP_PANE;
+ }
+ }
+
+ /**
+ * Creates a new JDesktopPane object.
+ */
+ public JDesktopPane()
+ {
+ setLayout(null);
+ updateUI();
+ }
+
+ /**
+ * This method returns the UI used with the JDesktopPane.
+ *
+ * @return The UI used with the JDesktopPane.
+ */
+ public DesktopPaneUI getUI()
+ {
+ return (DesktopPaneUI) ui;
+ }
+
+ /**
+ * This method sets the UI used with the JDesktopPane.
+ *
+ * @param ui The UI to use with the JDesktopPane.
+ */
+ public void setUI(DesktopPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets the drag mode to use with the JDesktopPane.
+ *
+ * @param mode The drag mode to use.
+ *
+ * @throws IllegalArgumentException If the drag mode given is not
+ * LIVE_DRAG_MODE or OUTLINE_DRAG_MODE.
+ */
+ public void setDragMode(int mode)
+ {
+ if ((mode != LIVE_DRAG_MODE) && (mode != OUTLINE_DRAG_MODE))
+ throw new IllegalArgumentException("Drag mode not valid.");
+
+ clientDragModeSet = true;
+
+ // FIXME: Unsupported mode.
+ if (mode == OUTLINE_DRAG_MODE)
+ // throw new IllegalArgumentException("Outline drag modes are
+ // unsupported.");
+ mode = LIVE_DRAG_MODE;
+
+ dragMode = mode;
+ }
+
+ /**
+ * This method returns the drag mode used with the JDesktopPane.
+ *
+ * @return The drag mode used with the JDesktopPane.
+ */
+ public int getDragMode()
+ {
+ return dragMode;
+ }
+
+ /**
+ * This method returns the DesktopManager used with the JDesktopPane.
+ *
+ * @return The DesktopManager to use with the JDesktopPane.
+ */
+ public DesktopManager getDesktopManager()
+ {
+ return desktopManager;
+ }
+
+ /**
+ * This method sets the DesktopManager to use with the JDesktopPane.
+ *
+ * @param manager The DesktopManager to use with the JDesktopPane.
+ */
+ public void setDesktopManager(DesktopManager manager)
+ {
+ desktopManager = manager;
+ }
+
+ /**
+ * This method restores the UI used with the JDesktopPane to the default.
+ */
+ public void updateUI()
+ {
+ setUI((DesktopPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a String identifier that allows the UIManager to know
+ * which class will act as JDesktopPane's UI.
+ *
+ * @return A String identifier for the UI class to use.
+ */
+ public String getUIClassID()
+ {
+ return "DesktopPaneUI";
+ }
+
+ /**
+ * This method returns all JInternalFrames that are in the JDesktopPane.
+ *
+ * @return All JInternalFrames that are in the JDesktopPane.
+ */
+ public JInternalFrame[] getAllFrames()
+ {
+ return getFramesFromComponents(getComponents());
+ }
+
+ /**
+ * This method returns the currently selected frame in the JDesktopPane.
+ *
+ * @return The currently selected frame in the JDesktopPane.
+ */
+ public JInternalFrame getSelectedFrame()
+ {
+ return selectedFrame;
+ }
+
+ /**
+ * This method sets the selected frame in the JDesktopPane.
+ *
+ * @param frame The selected frame in the JDesktopPane.
+ */
+ public void setSelectedFrame(JInternalFrame frame)
+ {
+ if (selectedFrame != null)
+ {
+ try
+ {
+ selectedFrame.setSelected(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // We do nothing when the attempt is vetoed.
+ }
+ }
+ selectedFrame = null;
+
+ try
+ {
+ if (frame != null)
+ frame.setSelected(true);
+
+ selectedFrame = frame;
+ }
+ catch (PropertyVetoException e)
+ {
+ // We do nothing when the attempt is vetoed.
+ }
+ }
+
+ /**
+ * This method returns all the JInternalFrames in the given layer.
+ *
+ * @param layer The layer to grab frames in.
+ *
+ * @return All JInternalFrames in the given layer.
+ */
+ public JInternalFrame[] getAllFramesInLayer(int layer)
+ {
+ return getFramesFromComponents(getComponentsInLayer(layer));
+ }
+
+ /**
+ * This method always returns true to indicate that it is not transparent.
+ *
+ * @return true.
+ */
+ public boolean isOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this <code>JDesktopPane</code>.
+ *
+ * @return A string describing the attributes of this <code>JDesktopPane</code>
+ * (never <code>null</code>).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",isOptimizedDrawingPossible=");
+ sb.append(isOptimizedDrawingEnabled());
+ sb.append(",desktopManager=");
+ if (desktopManager != null)
+ sb.append(desktopManager);
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * This method returns all the JInternalFrames in the given Component array.
+ *
+ * @param components An array to search for JInternalFrames in.
+ *
+ * @return An array of JInternalFrames found in the Component array.
+ */
+ private static JInternalFrame[] getFramesFromComponents(Component[] components)
+ {
+ int count = 0;
+
+ for (int i = 0; i < components.length; i++)
+ if (components[i] instanceof JInternalFrame)
+ count++;
+
+ JInternalFrame[] value = new JInternalFrame[count];
+ for (int i = 0, j = 0; i < components.length && j != count; i++)
+ if (components[i] instanceof JInternalFrame)
+ value[j++] = (JInternalFrame) components[i];
+ return value;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JDesktopPane</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJDesktopPane}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJDesktopPane();
+
+ return accessibleContext;
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if <code>c</code> or
+ * <code>propertyValue</code> is <code>null</code>
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("dragMode"))
+ {
+ if (! clientDragModeSet)
+ {
+ setDragMode(((Integer) value).intValue());
+ clientDragModeSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JDialog.java b/libjava/classpath/javax/swing/JDialog.java
new file mode 100644
index 000000000..04ec825bc
--- /dev/null
+++ b/libjava/classpath/javax/swing/JDialog.java
@@ -0,0 +1,583 @@
+/* JDialog.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dialog;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.IllegalComponentStateException;
+import java.awt.LayoutManager;
+import java.awt.event.WindowEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+/**
+ * A dialog window. This is an extension of {@link java.awt.Dialog} that
+ * provides support for the Swing architecture. Most importantly it contains a
+ * {@link JRootPane} as it's only top-level child, that manages the content
+ * pane, the menu and a glass pane.
+ *
+ * Also, unlike <code>java.awt.Dialog</code>s, JDialogs support the
+ * Swing Pluggable Look &amp; Feel architecture.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JDialog extends Dialog implements Accessible, WindowConstants,
+ RootPaneContainer
+{
+ /**
+ * Provides accessibility support for <code>JDialog</code>s.
+ */
+ protected class AccessibleJDialog extends Dialog.AccessibleAWTDialog
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJDialog</code>.
+ */
+ protected AccessibleJDialog()
+ {
+ super();
+ // Nothing to do here.
+ }
+ }
+
+ private static final long serialVersionUID = -864070866424508218L;
+
+ /** DOCUMENT ME! */
+ protected AccessibleContext accessibleContext;
+
+ /** The single RootPane in the Dialog. */
+ protected JRootPane rootPane;
+
+ /**
+ * Whether checking is enabled on the RootPane.
+ *
+ * @specnote Should be false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ /** The default action taken when closed. */
+ private int closeAction = HIDE_ON_CLOSE;
+
+ /** Whether JDialogs are decorated by the Look and Feel. */
+ private static boolean decorated;
+
+ /* Creates a new non-modal JDialog with no title
+ * using a shared Frame as the owner.
+ */
+ public JDialog()
+ {
+ this((Frame) SwingUtilities.getOwnerFrame(null), "", false, null);
+ }
+
+ /**
+ * Creates a new non-modal JDialog with no title
+ * using the given owner.
+ *
+ * @param owner The owner of the JDialog.
+ */
+ public JDialog(Dialog owner)
+ {
+ this(owner, "", false, null);
+ }
+
+ /**
+ * Creates a new JDialog with no title using the
+ * given modal setting and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ */
+ public JDialog(Dialog owner, boolean modal)
+ {
+ this(owner, "", modal, null);
+ }
+
+ /**
+ * Creates a new non-modal JDialog using the
+ * given title and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ */
+ public JDialog(Dialog owner, String title)
+ {
+ this(owner, title, false, null);
+ }
+
+ /**
+ * Creates a new JDialog using the given modal
+ * settings, title, and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ */
+ public JDialog(Dialog owner, String title, boolean modal)
+ {
+ this(owner, title, modal, null);
+ }
+
+ /**
+ * Creates a new JDialog using the given modal
+ * settings, title, owner and graphics configuration.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ * @param gc The Graphics Configuration to use.
+ */
+ public JDialog(Dialog owner, String title, boolean modal,
+ GraphicsConfiguration gc)
+ {
+ super(owner, title, modal, gc);
+ dialogInit();
+ }
+
+ /**
+ * Creates a new non-modal JDialog with no title
+ * using the given owner.
+ *
+ * @param owner The owner of the JDialog.
+ */
+ public JDialog(Frame owner)
+ {
+ this(owner, "", false, null);
+ }
+
+ /**
+ * Creates a new JDialog with no title using the
+ * given modal setting and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ */
+ public JDialog(Frame owner, boolean modal)
+ {
+ this(owner, "", modal, null);
+ }
+
+ /**
+ * Creates a new non-modal JDialog using the
+ * given title and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ */
+ public JDialog(Frame owner, String title)
+ {
+ this(owner, title, false, null);
+ }
+
+ /**
+ * Creates a new JDialog using the given modal
+ * settings, title, and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ */
+ public JDialog(Frame owner, String title, boolean modal)
+ {
+ this(owner, title, modal, null);
+ }
+
+ /**
+ * Creates a new JDialog using the given modal
+ * settings, title, owner and graphics configuration.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ * @param gc The Graphics Configuration to use.
+ */
+ public JDialog(Frame owner, String title, boolean modal,
+ GraphicsConfiguration gc)
+ {
+ super((Frame) SwingUtilities.getOwnerFrame(owner), title, modal, gc);
+ dialogInit();
+ }
+
+ /**
+ * This method is called to initialize the
+ * JDialog. It sets the layout used, the locale,
+ * and creates the RootPane.
+ */
+ protected void dialogInit()
+ {
+ // We need to explicitly enable events here so that our processKeyEvent()
+ // and processWindowEvent() gets called.
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK);
+
+ // FIXME: Do a check on GraphicsEnvironment.isHeadless()
+ setLocale(JComponent.getDefaultLocale());
+ getRootPane(); // Will do set/create.
+ invalidate();
+ // Now that initStageDone is true, adds and layouts apply to contentPane,
+ // not top-level.
+ setRootPaneCheckingEnabled(true);
+ }
+
+ /**
+ * This method returns whether JDialogs will have their
+ * window decorations provided by the Look and Feel.
+ *
+ * @return Whether the window decorations are Look and Feel provided.
+ */
+ public static boolean isDefaultLookAndFeelDecorated()
+ {
+ return decorated;
+ }
+
+ /**
+ * This method sets whether JDialogs will have their
+ * window decorations provided by the Look and Feel.
+ *
+ * @param defaultLookAndFeelDecorated Whether the window
+ * decorations are Look and Feel provided.
+ */
+ public static void setDefaultLookAndFeelDecorated(boolean defaultLookAndFeelDecorated)
+ {
+ decorated = defaultLookAndFeelDecorated;
+ }
+
+ /**
+ * This method returns the preferred size of
+ * the JDialog.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension d = super.getPreferredSize();
+ return d;
+ }
+
+ /**
+ * This method returns the JMenuBar used
+ * in this JDialog.
+ *
+ * @return The JMenuBar in the JDialog.
+ */
+ public JMenuBar getJMenuBar()
+ {
+ return getRootPane().getJMenuBar();
+ }
+
+ /**
+ * This method sets the JMenuBar used
+ * in this JDialog.
+ *
+ * @param menubar The JMenuBar to use.
+ */
+ public void setJMenuBar(JMenuBar menubar)
+ {
+ getRootPane().setJMenuBar(menubar);
+ }
+
+ /**
+ * This method sets the LayoutManager used in the JDialog.
+ * This method will throw an Error if rootPaneChecking is
+ * enabled.
+ *
+ * @param manager The LayoutManager to use.
+ */
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ /**
+ * This method sets the JLayeredPane used in the JDialog.
+ * If the given JLayeredPane is null, then this method
+ * will throw an Error.
+ *
+ * @param layeredPane The JLayeredPane to use.
+ */
+ public void setLayeredPane(JLayeredPane layeredPane)
+ {
+ if (layeredPane == null)
+ throw new IllegalComponentStateException("layeredPane cannot be null.");
+ getRootPane().setLayeredPane(layeredPane);
+ }
+
+ /**
+ * This method returns the JLayeredPane used with this JDialog.
+ *
+ * @return The JLayeredPane used with this JDialog.
+ */
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ /**
+ * This method returns the JRootPane used with this JDialog.
+ *
+ * @return The JRootPane used with this JDialog.
+ */
+ public JRootPane getRootPane()
+ {
+ if (rootPane == null)
+ setRootPane(createRootPane());
+ return rootPane;
+ }
+
+ /**
+ * This method sets the JRootPane used with this JDialog.
+ *
+ * @param root The JRootPane to use.
+ */
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ rootPane = root;
+ rootPane.show();
+ add(rootPane);
+ }
+
+ /**
+ * This method creates a new JRootPane.
+ *
+ * @return A new JRootPane.
+ */
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ /**
+ * This method returns the ContentPane
+ * in the JRootPane.
+ *
+ * @return The ContentPane in the JRootPane.
+ */
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ /**
+ * This method sets the ContentPane to use with this
+ * JDialog. If the ContentPane given is null, this method
+ * will throw an exception.
+ *
+ * @param contentPane The ContentPane to use with the JDialog.
+ */
+ public void setContentPane(Container contentPane)
+ {
+ if (contentPane == null)
+ throw new IllegalComponentStateException("contentPane cannot be null.");
+ getRootPane().setContentPane(contentPane);
+ }
+
+ /**
+ * This method returns the GlassPane for this JDialog.
+ *
+ * @return The GlassPane for this JDialog.
+ */
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ /**
+ * This method sets the GlassPane for this JDialog.
+ *
+ * @param glassPane The GlassPane for this JDialog.
+ */
+ public void setGlassPane(Component glassPane)
+ {
+ getRootPane().setGlassPane(glassPane);
+ }
+
+ /**
+ * This method is called when a component is added to the
+ * the JDialog. Calling this method with rootPaneCheckingEnabled
+ * will cause an Error to be thrown.
+ *
+ * @param comp The component to add.
+ * @param constraints The constraints.
+ * @param index The position of the component.
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're adding in the initialization stage use super.add.
+ // Otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().add(comp, constraints, index);
+ else
+ super.addImpl(comp, constraints, index);
+ }
+
+ /**
+ * This method removes a component from the JDialog.
+ *
+ * @param comp The component to remove.
+ */
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead.
+ if (comp == rootPane)
+ super.remove(rootPane);
+ else
+ getContentPane().remove(comp);
+ }
+
+ /**
+ * This method returns whether rootPane checking is enabled.
+ *
+ * @return Whether rootPane checking is enabled.
+ */
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ /**
+ * This method sets whether rootPane checking is enabled.
+ *
+ * @param enabled Whether rootPane checking is enabled.
+ */
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ /**
+ * This method simply calls paint and returns.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+
+ /**
+ * This method handles window events. This allows the JDialog
+ * to honour its default close operation.
+ *
+ * @param e The WindowEvent.
+ */
+ protected void processWindowEvent(WindowEvent e)
+ {
+ super.processWindowEvent(e);
+ if (e.getID() == WindowEvent.WINDOW_CLOSING)
+ {
+ switch (closeAction)
+ {
+ case EXIT_ON_CLOSE:
+ System.exit(0);
+ break;
+ case DISPOSE_ON_CLOSE:
+ dispose();
+ break;
+ case HIDE_ON_CLOSE:
+ setVisible(false);
+ break;
+ case DO_NOTHING_ON_CLOSE:
+ break;
+ }
+ }
+ }
+
+ /**
+ * This method sets the action to take
+ * when the JDialog is closed.
+ *
+ * @param operation The action to take.
+ */
+ public void setDefaultCloseOperation(int operation)
+ {
+ /* Reference implementation allows invalid operations
+ to be specified. If so, getDefaultCloseOperation
+ must return the invalid code, and the behaviour
+ defaults to DO_NOTHING_ON_CLOSE. processWindowEvent
+ above handles this */
+ closeAction = operation;
+ }
+
+ /**
+ * This method returns the action taken when
+ * the JDialog is closed.
+ *
+ * @return The action to take.
+ */
+ public int getDefaultCloseOperation()
+ {
+ return closeAction;
+ }
+
+ /**
+ * This method returns a String describing the JDialog.
+ *
+ * @return A String describing the JDialog.
+ */
+ protected String paramString()
+ {
+ return "JDialog";
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJDialog();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JEditorPane.java b/libjava/classpath/javax/swing/JEditorPane.java
new file mode 100644
index 000000000..8ad1095ee
--- /dev/null
+++ b/libjava/classpath/javax/swing/JEditorPane.java
@@ -0,0 +1,1222 @@
+/* JEditorPane.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Container;
+import java.awt.Dimension;
+import java.io.BufferedInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleHyperlink;
+import javax.accessibility.AccessibleHypertext;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.plaf.TextUI;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+import javax.swing.text.WrappedPlainView;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
+
+/**
+ * A powerful text editor component that can handle different types of
+ * content.
+ *
+ * The JEditorPane text component is driven by an instance of
+ * {@link EditorKit}. The editor kit is responsible for providing
+ * a default {@link Document} implementation, a mechanism for loading
+ * and saving documents of its supported content type and providing
+ * a set of {@link Action}s for manipulating the content.
+ *
+ * By default the following content types are supported:
+ * <ul>
+ * <li><code>text/plain</code>: Plain text, handled by
+ * {@link javax.swing.text.DefaultEditorKit}.</li>
+ * <li><code>text/html</code>: HTML 4.0 styled text, handled by
+ * {@link javax.swing.text.html.HTMLEditorKit}.</li>
+ * <li><code>text/rtf</code>: RTF text, handled by
+ * {@link javax.swing.text.rtf.RTFEditorKit}.</li>
+ * </ul>
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ */
+public class JEditorPane extends JTextComponent
+{
+ /**
+ * Provides accessibility support for <code>JEditorPane</code>.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJEditorPane extends AccessibleJTextComponent
+ {
+
+ /**
+ * Creates a new <code>AccessibleJEditorPane</code> object.
+ */
+ protected AccessibleJEditorPane()
+ {
+ super();
+ }
+
+ /**
+ * Returns a description of this <code>AccessibleJEditorPane</code>. If
+ * this property is not set, then this returns the content-type of the
+ * editor pane.
+ *
+ * @return a description of this AccessibleJEditorPane
+ */
+ public String getAccessibleDescription()
+ {
+ String descr = super.getAccessibleDescription();
+ if (descr == null)
+ return getContentType();
+ else
+ return descr;
+ }
+
+ /**
+ * Returns the accessible state of this <code>AccessibleJEditorPane</code>.
+ *
+ * @return the accessible state of this <code>AccessibleJEditorPane</code>
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ // TODO: Figure out what state must be added here to the super's state.
+ return state;
+ }
+ }
+
+ /**
+ * Provides accessibility support for <code>JEditorPane</code>s, when the
+ * editor kit is an instance of {@link HTMLEditorKit}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane
+ {
+ /**
+ * Returns the accessible text of the <code>JEditorPane</code>. This will
+ * be an instance of
+ * {@link JEditorPaneAccessibleHypertextSupport}.
+ *
+ * @return the accessible text of the <code>JEditorPane</code>
+ */
+ public AccessibleText getAccessibleText()
+ {
+ return new JEditorPaneAccessibleHypertextSupport();
+ }
+ }
+
+ /**
+ * This is the accessible text that is returned by
+ * {@link AccessibleJEditorPaneHTML#getAccessibleText()}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class JEditorPaneAccessibleHypertextSupport
+ extends AccessibleJEditorPane implements AccessibleHypertext
+ {
+
+ /**
+ * Creates a new JEditorPaneAccessibleHypertextSupport object.
+ */
+ public JEditorPaneAccessibleHypertextSupport()
+ {
+ super();
+ }
+
+ /**
+ * The accessible representation of a HTML link.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class HTMLLink extends AccessibleHyperlink
+ {
+
+ /**
+ * The element in the document that represents the link.
+ */
+ Element element;
+
+ /**
+ * Creates a new <code>HTMLLink</code>.
+ *
+ * @param el the link element
+ */
+ public HTMLLink(Element el)
+ {
+ this.element = el;
+ }
+
+ /**
+ * Returns <code>true</code> if this <code>HTMLLink</code> is still
+ * valid. A <code>HTMLLink</code> can become invalid when the document
+ * changes.
+ *
+ * @return <code>true</code> if this <code>HTMLLink</code> is still
+ * valid
+ */
+ public boolean isValid()
+ {
+ // I test here if the element at our element's start offset is the
+ // same as the element in the document at this offset. If this is true,
+ // I consider the link valid, if not, then this link no longer
+ // represented by this HTMLLink and therefor invalid.
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ return doc.getCharacterElement(element.getStartOffset()) == element;
+ }
+
+ /**
+ * Returns the number of AccessibleActions in this link object. In
+ * general, link have 1 AccessibleAction associated with them. There are
+ * special cases where links can have multiple actions associated, like
+ * in image maps.
+ *
+ * @return the number of AccessibleActions in this link object
+ */
+ public int getAccessibleActionCount()
+ {
+ // TODO: Implement the special cases.
+ return 1;
+ }
+
+ /**
+ * Performs the specified action on the link object. This ususally means
+ * activating the link.
+ *
+ * @return <code>true</code> if the action has been performed
+ * successfully, <code>false</code> otherwise
+ */
+ public boolean doAccessibleAction(int i)
+ {
+ String href = (String) element.getAttributes().getAttribute("href");
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ try
+ {
+ URL url = new URL(doc.getBase(), href);
+ setPage(url);
+ String desc = doc.getText(element.getStartOffset(),
+ element.getEndOffset() - element.getStartOffset());
+ HyperlinkEvent ev =
+ new HyperlinkEvent(JEditorPane.this,
+ HyperlinkEvent.EventType.ACTIVATED, url, desc,
+ element);
+ fireHyperlinkUpdate(ev);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the description of the action at action index <code>i</code>.
+ * This method returns the text within the element associated with this
+ * link.
+ *
+ * @param i the action index
+ *
+ * @return the description of the action at action index <code>i</code>
+ */
+ public String getAccessibleActionDescription(int i)
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ try
+ {
+ return doc.getText(element.getStartOffset(),
+ element.getEndOffset() - element.getStartOffset());
+ }
+ catch (BadLocationException ex)
+ {
+ throw (AssertionError)
+ new AssertionError("BadLocationException must not be thrown "
+ + "here.")
+ .initCause(ex);
+ }
+ }
+
+ /**
+ * Returns an {@link URL} object, that represents the action at action
+ * index <code>i</code>.
+ *
+ * @param i the action index
+ *
+ * @return an {@link URL} object, that represents the action at action
+ * index <code>i</code>
+ */
+ public Object getAccessibleActionObject(int i)
+ {
+ String href = (String) element.getAttributes().getAttribute("href");
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ try
+ {
+ URL url = new URL(doc.getBase(), href);
+ return url;
+ }
+ catch (MalformedURLException ex)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns an object that represents the link anchor. For examples, if
+ * the link encloses a string, then a <code>String</code> object is
+ * returned, if the link encloses an &lt;img&gt; tag, then an
+ * <code>ImageIcon</code> object is returned.
+ *
+ * @return an object that represents the link anchor
+ */
+ public Object getAccessibleActionAnchor(int i)
+ {
+ // TODO: This is only the String case. Implement all cases.
+ return getAccessibleActionDescription(i);
+ }
+
+ /**
+ * Returns the start index of the hyperlink element.
+ *
+ * @return the start index of the hyperlink element
+ */
+ public int getStartIndex()
+ {
+ return element.getStartOffset();
+ }
+
+ /**
+ * Returns the end index of the hyperlink element.
+ *
+ * @return the end index of the hyperlink element
+ */
+ public int getEndIndex()
+ {
+ return element.getEndOffset();
+ }
+
+ }
+
+ /**
+ * Returns the number of hyperlinks in the document.
+ *
+ * @return the number of hyperlinks in the document
+ */
+ public int getLinkCount()
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
+ int count = 0;
+ while (linkIter.isValid())
+ {
+ count++;
+ linkIter.next();
+ }
+ return count;
+ }
+
+ /**
+ * Returns the <code>i</code>-th hyperlink in the document or
+ * <code>null</code> if there is no hyperlink with the specified index.
+ *
+ * @param i the index of the hyperlink to return
+ *
+ * @return the <code>i</code>-th hyperlink in the document or
+ * <code>null</code> if there is no hyperlink with the specified
+ * index
+ */
+ public AccessibleHyperlink getLink(int i)
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
+ int count = 0;
+ while (linkIter.isValid())
+ {
+ count++;
+ if (count == i)
+ break;
+ linkIter.next();
+ }
+ if (linkIter.isValid())
+ {
+ int offset = linkIter.getStartOffset();
+ // TODO: I fetch the element for the link via getCharacterElement().
+ // I am not sure that this is correct, maybe we must use
+ // getParagraphElement()?
+ Element el = doc.getCharacterElement(offset);
+ HTMLLink link = new HTMLLink(el);
+ return link;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Returns the index of the link element at the character position
+ * <code>c</code> within the document, or <code>-1</code> if there is no
+ * link at the specified position.
+ *
+ * @param c the character index from which to fetch the link index
+ *
+ * @return the index of the link element at the character position
+ * <code>c</code> within the document, or <code>-1</code> if there
+ * is no link at the specified position
+ */
+ public int getLinkIndex(int c)
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
+ int count = 0;
+ while (linkIter.isValid())
+ {
+ if (linkIter.getStartOffset() <= c && linkIter.getEndOffset() > c)
+ break;
+ count++;
+ linkIter.next();
+ }
+ if (linkIter.isValid())
+ return count;
+ else
+ return -1;
+ }
+
+ /**
+ * Returns the link text of the link at index <code>i</code>, or
+ * <code>null</code>, if there is no link at the specified position.
+ *
+ * @param i the index of the link
+ *
+ * @return the link text of the link at index <code>i</code>, or
+ * <code>null</code>, if there is no link at the specified
+ * position
+ */
+ public String getLinkText(int i)
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
+ int count = 0;
+ while (linkIter.isValid())
+ {
+ count++;
+ if (count == i)
+ break;
+ linkIter.next();
+ }
+ if (linkIter.isValid())
+ {
+ int offset = linkIter.getStartOffset();
+ // TODO: I fetch the element for the link via getCharacterElement().
+ // I am not sure that this is correct, maybe we must use
+ // getParagraphElement()?
+ Element el = doc.getCharacterElement(offset);
+ try
+ {
+ String text = doc.getText(el.getStartOffset(),
+ el.getEndOffset() - el.getStartOffset());
+ return text;
+ }
+ catch (BadLocationException ex)
+ {
+ throw (AssertionError)
+ new AssertionError("BadLocationException must not be thrown "
+ + "here.")
+ .initCause(ex);
+ }
+ }
+ else
+ return null;
+ }
+ }
+
+ /**
+ * Used to store a mapping for content-type to editor kit class.
+ */
+ private static class EditorKitMapping
+ {
+ /**
+ * The classname of the editor kit.
+ */
+ String className;
+
+ /**
+ * The classloader with which the kit is to be loaded.
+ */
+ ClassLoader classLoader;
+
+ /**
+ * Creates a new EditorKitMapping object.
+ *
+ * @param cn the classname
+ * @param cl the classloader
+ */
+ EditorKitMapping(String cn, ClassLoader cl)
+ {
+ className = cn;
+ classLoader = cl;
+ }
+ }
+
+ /**
+ * An EditorKit used for plain text. This is the default editor kit for
+ * JEditorPanes.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class PlainEditorKit extends DefaultEditorKit
+ {
+
+ /**
+ * Returns a ViewFactory that supplies WrappedPlainViews.
+ */
+ public ViewFactory getViewFactory()
+ {
+ return new ViewFactory()
+ {
+ public View create(Element el)
+ {
+ return new WrappedPlainView(el);
+ }
+ };
+ }
+ }
+
+ /**
+ * A special stream that can be cancelled.
+ */
+ private class PageStream
+ extends FilterInputStream
+ {
+ /**
+ * True when the stream has been cancelled, false otherwise.
+ */
+ private boolean cancelled;
+
+ protected PageStream(InputStream in)
+ {
+ super(in);
+ cancelled = false;
+ }
+
+ private void checkCancelled()
+ throws IOException
+ {
+ if (cancelled)
+ throw new IOException("Stream has been cancelled");
+ }
+
+ void cancel()
+ {
+ cancelled = true;
+ }
+
+ public int read()
+ throws IOException
+ {
+ checkCancelled();
+ return super.read();
+ }
+
+ public int read(byte[] b, int off, int len)
+ throws IOException
+ {
+ checkCancelled();
+ return super.read(b, off, len);
+ }
+
+ public long skip(long n)
+ throws IOException
+ {
+ checkCancelled();
+ return super.skip(n);
+ }
+
+ public int available()
+ throws IOException
+ {
+ checkCancelled();
+ return super.available();
+ }
+
+ public void reset()
+ throws IOException
+ {
+ checkCancelled();
+ super.reset();
+ }
+ }
+
+ /**
+ * The thread that loads documents asynchronously.
+ */
+ private class PageLoader
+ implements Runnable
+ {
+ private Document doc;
+ private PageStream in;
+ private URL old;
+ URL page;
+ PageLoader(Document doc, InputStream in, URL old, URL page)
+ {
+ this.doc = doc;
+ this.in = new PageStream(in);
+ this.old = old;
+ this.page = page;
+ }
+
+ public void run()
+ {
+ try
+ {
+ read(in, doc);
+ }
+ catch (IOException ex)
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
+ }
+ finally
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ firePropertyChange("page", old, page);
+ else
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ firePropertyChange("page", old, page);
+ }
+ });
+ }
+ }
+ }
+
+ void cancel()
+ {
+ in.cancel();
+ }
+ }
+
+ private static final long serialVersionUID = 3140472492599046285L;
+
+ private EditorKit editorKit;
+
+ boolean focus_root;
+
+ /**
+ * Maps content-types to editor kit instances.
+ */
+ static HashMap editorKits;
+
+ // A mapping between content types and registered EditorKit types
+ static HashMap registerMap;
+
+ static
+ {
+ registerMap = new HashMap();
+ editorKits = new HashMap();
+ registerEditorKitForContentType("application/rtf",
+ "javax.swing.text.rtf.RTFEditorKit");
+ registerEditorKitForContentType("text/plain",
+ "javax.swing.JEditorPane$PlainEditorKit");
+ registerEditorKitForContentType("text/html",
+ "javax.swing.text.html.HTMLEditorKit");
+ registerEditorKitForContentType("text/rtf",
+ "javax.swing.text.rtf.RTFEditorKit");
+
+ }
+
+ // A mapping between content types and used EditorKits
+ HashMap editorMap;
+
+ /**
+ * The currently loading stream, if any.
+ */
+ private PageLoader loader;
+
+ public JEditorPane()
+ {
+ init();
+ setEditorKit(createDefaultEditorKit());
+ }
+
+ public JEditorPane(String url) throws IOException
+ {
+ this(new URL(url));
+ }
+
+ public JEditorPane(String type, String text)
+ {
+ init();
+ setEditorKit(createEditorKitForContentType(type));
+ setText(text);
+ }
+
+ public JEditorPane(URL url) throws IOException
+ {
+ init();
+ setEditorKit(createEditorKitForContentType("text/html"));
+ setPage(url);
+ }
+
+ /**
+ * Called by the constructors to set up the default bindings for content
+ * types and EditorKits.
+ */
+ void init()
+ {
+ editorMap = new HashMap();
+ }
+
+ protected EditorKit createDefaultEditorKit()
+ {
+ return new PlainEditorKit();
+ }
+
+ /**
+ * Creates and returns an EditorKit that is appropriate for the given
+ * content type. This is created using the default recognized types
+ * plus any EditorKit types that have been registered.
+ *
+ * @see #registerEditorKitForContentType(String, String)
+ * @see #registerEditorKitForContentType(String, String, ClassLoader)
+ * @param type the content type
+ * @return an EditorKit for use with the given content type
+ */
+ public static EditorKit createEditorKitForContentType(String type)
+ {
+ // Try cached instance.
+ EditorKit e = (EditorKit) editorKits.get(type);
+ if (e == null)
+ {
+ EditorKitMapping m = (EditorKitMapping) registerMap.get(type);
+ if (m != null)
+ {
+ String className = m.className;
+ ClassLoader loader = m.classLoader;
+ try
+ {
+ e = (EditorKit) loader.loadClass(className).newInstance();
+ }
+ catch (Exception e2)
+ {
+ // The reference implementation returns null when class is not
+ // loadable or instantiatable.
+ }
+ }
+ // Cache this for later retrieval.
+ if (e != null)
+ editorKits.put(type, e);
+ }
+ return e;
+ }
+
+ /**
+ * Sends a given <code>HyperlinkEvent</code> to all registered listeners.
+ *
+ * @param event the event to send
+ */
+ public void fireHyperlinkUpdate(HyperlinkEvent event)
+ {
+ HyperlinkListener[] listeners = getHyperlinkListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].hyperlinkUpdate(event);
+ }
+
+ /**
+ * Returns the accessible context associated with this editor pane.
+ *
+ * @return the accessible context associated with this editor pane
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ {
+ if (getEditorKit() instanceof HTMLEditorKit)
+ accessibleContext = new AccessibleJEditorPaneHTML();
+ else
+ accessibleContext = new AccessibleJEditorPane();
+ }
+ return accessibleContext;
+ }
+
+ public final String getContentType()
+ {
+ return getEditorKit().getContentType();
+ }
+
+ /**
+ * Returns the EditorKit. If there is no EditorKit set this method
+ * calls createDefaultEditorKit() and setEditorKit() first.
+ */
+ public EditorKit getEditorKit()
+ {
+ if (editorKit == null)
+ setEditorKit(createDefaultEditorKit());
+ return editorKit;
+ }
+
+ /**
+ * Returns the class name of the EditorKit associated with the given
+ * content type.
+ *
+ * @since 1.3
+ * @param type the content type
+ * @return the class name of the EditorKit associated with this content type
+ */
+ public static String getEditorKitClassNameForContentType(String type)
+ {
+ EditorKitMapping m = (EditorKitMapping) registerMap.get(type);
+ String kitName = m != null ? m.className : null;
+ return kitName;
+ }
+
+ /**
+ * Returns the EditorKit to use for the given content type. If an
+ * EditorKit has been explicitly set via
+ * <code>setEditorKitForContentType</code>
+ * then it will be returned. Otherwise an attempt will be made to create
+ * an EditorKit from the default recognzied content types or any
+ * EditorKits that have been registered. If none can be created, a
+ * PlainEditorKit is created.
+ *
+ * @see #registerEditorKitForContentType(String, String)
+ * @see #registerEditorKitForContentType(String, String, ClassLoader)
+ * @param type the content type
+ * @return an appropriate EditorKit for the given content type
+ */
+ public EditorKit getEditorKitForContentType(String type)
+ {
+ // First check if an EditorKit has been explicitly set.
+ EditorKit e = (EditorKit) editorMap.get(type);
+ // Then check to see if we can create one.
+ if (e == null)
+ {
+ e = createEditorKitForContentType(type);
+ if (e != null)
+ setEditorKitForContentType(type, e);
+ }
+ // Otherwise default to PlainEditorKit.
+ if (e == null)
+ e = createDefaultEditorKit();
+ return e;
+ }
+
+ /**
+ * Returns the preferred size for the JEditorPane. This is implemented to
+ * return the super's preferred size, unless one of
+ * {@link #getScrollableTracksViewportHeight()} or
+ * {@link #getScrollableTracksViewportWidth()} returns <code>true</code>,
+ * in which case the preferred width and/or height is replaced by the UI's
+ * minimum size.
+ *
+ * @return the preferred size for the JEditorPane
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension pref = super.getPreferredSize();
+ Container parent = getParent();
+ if (parent instanceof JViewport)
+ {
+ JViewport vp = (JViewport) getParent();
+ TextUI ui = getUI();
+ Dimension min = null;
+ if (! getScrollableTracksViewportWidth())
+ {
+ min = ui.getMinimumSize(this);
+ int vpWidth = vp.getWidth();
+ if (vpWidth != 0 && vpWidth < min.width)
+ pref.width = min.width;
+ }
+ if (! getScrollableTracksViewportHeight())
+ {
+ if (min == null)
+ min = ui.getMinimumSize(this);
+ int vpHeight = vp.getHeight();
+ if (vpHeight != 0 && vpHeight < min.height)
+ pref.height = min.height;
+ }
+ }
+ return pref;
+ }
+
+ /**
+ * Returns <code>true</code> when a Viewport should force the height of
+ * this component to match the viewport height. This is implemented to return
+ * <code>true</code> when the parent is an instance of JViewport and
+ * the viewport height > the UI's minimum height.
+ *
+ * @return <code>true</code> when a Viewport should force the height of
+ * this component to match the viewport height
+ */
+ public boolean getScrollableTracksViewportHeight()
+ {
+ // Tests show that this returns true when the parent is a JViewport
+ // and has a height > minimum UI height.
+ Container parent = getParent();
+ int height = parent.getHeight();
+ TextUI ui = getUI();
+ return parent instanceof JViewport
+ && height >= ui.getMinimumSize(this).height
+ && height <= ui.getMaximumSize(this).height;
+ }
+
+ /**
+ * Returns <code>true</code> when a Viewport should force the width of
+ * this component to match the viewport width. This is implemented to return
+ * <code>true</code> when the parent is an instance of JViewport and
+ * the viewport width > the UI's minimum width.
+ *
+ * @return <code>true</code> when a Viewport should force the width of
+ * this component to match the viewport width
+ */
+ public boolean getScrollableTracksViewportWidth()
+ {
+ // Tests show that this returns true when the parent is a JViewport
+ // and has a width > minimum UI width.
+ Container parent = getParent();
+ return parent != null && parent instanceof JViewport
+ && parent.getWidth() > getUI().getMinimumSize(this).width;
+ }
+
+ public URL getPage()
+ {
+ return loader != null ? loader.page : null;
+ }
+
+ protected InputStream getStream(URL page)
+ throws IOException
+ {
+ URLConnection conn = page.openConnection();
+ // Try to detect the content type of the stream data.
+ String type = conn.getContentType();
+ if (type != null)
+ setContentType(type);
+ InputStream stream = conn.getInputStream();
+ return new BufferedInputStream(stream);
+ }
+
+ public String getText()
+ {
+ return super.getText();
+ }
+
+ public String getUIClassID()
+ {
+ return "EditorPaneUI";
+ }
+
+ public boolean isFocusCycleRoot()
+ {
+ return focus_root;
+ }
+
+ protected String paramString()
+ {
+ return "JEditorPane";
+ }
+
+ /**
+ * This method initializes from a stream.
+ */
+ public void read(InputStream in, Object desc) throws IOException
+ {
+ EditorKit kit = getEditorKit();
+ if (kit instanceof HTMLEditorKit && desc instanceof HTMLDocument)
+ {
+ HTMLDocument doc = (HTMLDocument) desc;
+ setDocument(doc);
+ try
+ {
+ InputStreamReader reader = new InputStreamReader(in);
+ kit.read(reader, doc, 0);
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "BadLocationException must not be thrown here.";
+ }
+ }
+ else
+ {
+ Reader inRead = new InputStreamReader(in);
+ super.read(inRead, desc);
+ }
+ }
+
+ /**
+ * Establishes a binding between type and classname. This enables
+ * us to create an EditorKit later for the given content type.
+ *
+ * @param type the content type
+ * @param classname the name of the class that is associated with this
+ * content type
+ */
+ public static void registerEditorKitForContentType(String type,
+ String classname)
+ {
+ registerEditorKitForContentType(type, classname,
+ Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * Establishes the default bindings of type to classname.
+ */
+ public static void registerEditorKitForContentType(String type,
+ String classname,
+ ClassLoader loader)
+ {
+ registerMap.put(type, new EditorKitMapping(classname, loader));
+ }
+
+ /**
+ * Replaces the currently selected content with new content represented
+ * by the given string.
+ */
+ public void replaceSelection(String content)
+ {
+ // TODO: Implement this properly.
+ super.replaceSelection(content);
+ }
+
+ /**
+ * Scrolls the view to the given reference location (that is, the value
+ * returned by the UL.getRef method for the URL being displayed).
+ */
+ public void scrollToReference(String reference)
+ {
+ // TODO: Implement this properly.
+ }
+
+ public final void setContentType(String type)
+ {
+ // Strip off content type parameters.
+ int paramIndex = type.indexOf(';');
+ if (paramIndex > -1)
+ {
+ // TODO: Handle character encoding.
+ type = type.substring(0, paramIndex).trim();
+ }
+ if (editorKit != null
+ && editorKit.getContentType().equals(type))
+ return;
+
+ EditorKit kit = getEditorKitForContentType(type);
+
+ if (kit != null)
+ setEditorKit(kit);
+ }
+
+ public void setEditorKit(EditorKit newValue)
+ {
+ if (editorKit == newValue)
+ return;
+
+ if (editorKit != null)
+ editorKit.deinstall(this);
+
+ EditorKit oldValue = editorKit;
+ editorKit = newValue;
+
+ if (editorKit != null)
+ {
+ editorKit.install(this);
+ setDocument(editorKit.createDefaultDocument());
+ }
+
+ firePropertyChange("editorKit", oldValue, newValue);
+ invalidate();
+ repaint();
+ // Reset the accessibleContext since this depends on the editorKit.
+ accessibleContext = null;
+ }
+
+ /**
+ * Explicitly sets an EditorKit to be used for the given content type.
+ * @param type the content type
+ * @param k the EditorKit to use for the given content type
+ */
+ public void setEditorKitForContentType(String type, EditorKit k)
+ {
+ editorMap.put(type, k);
+ }
+
+ /**
+ * Sets the current URL being displayed.
+ */
+ public void setPage(String url) throws IOException
+ {
+ setPage(new URL(url));
+ }
+
+ /**
+ * Sets the current URL being displayed.
+ */
+ public void setPage(URL page) throws IOException
+ {
+ if (page == null)
+ throw new IOException("invalid url");
+
+ URL old = getPage();
+ // Only reload if the URL doesn't point to the same file.
+ // This is not the same as equals because there might be different
+ // URLs on the same file with different anchors.
+ if (old == null || ! old.sameFile(page))
+ {
+ InputStream in = getStream(page);
+ if (editorKit != null)
+ {
+ Document doc = editorKit.createDefaultDocument();
+ doc.putProperty(Document.StreamDescriptionProperty, page);
+
+ if (loader != null)
+ loader.cancel();
+ loader = new PageLoader(doc, in, old, page);
+
+ int prio = -1;
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument aDoc = (AbstractDocument) doc;
+ prio = aDoc.getAsynchronousLoadPriority();
+ }
+ if (prio >= 0)
+ {
+ // Load asynchronously.
+ setDocument(doc);
+ Thread loadThread = new Thread(loader,
+ "JEditorPane.PageLoader");
+ loadThread.setDaemon(true);
+ loadThread.setPriority(prio);
+ loadThread.start();
+ }
+ else
+ {
+ // Load synchronously.
+ loader.run();
+ setDocument(doc);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the text of the JEditorPane. The argument <code>t</code>
+ * is expected to be in the format of the current EditorKit. This removes
+ * the content of the current document and uses the EditorKit to read in the
+ * new text. This allows the EditorKit to handle the String rather than just
+ * inserting in plain text.
+ *
+ * @param t the text to display in this JEditorPane
+ */
+ public void setText(String t)
+ {
+ try
+ {
+ // Remove the current content.
+ Document doc = getDocument();
+ doc.remove(0, doc.getLength());
+ if (t == null || t.equals(""))
+ return;
+
+ // Let the EditorKit read the text into the Document.
+ getEditorKit().read(new StringReader(t), doc, 0);
+ }
+ catch (BadLocationException ble)
+ {
+ // TODO: Don't know what to do here.
+ }
+ catch (IOException ioe)
+ {
+ // TODO: Don't know what to do here.
+ }
+ }
+
+ /**
+ * Add a <code>HyperlinkListener</code> object to this editor pane.
+ *
+ * @param listener the listener to add
+ */
+ public void addHyperlinkListener(HyperlinkListener listener)
+ {
+ listenerList.add(HyperlinkListener.class, listener);
+ }
+
+ /**
+ * Removes a <code>HyperlinkListener</code> object to this editor pane.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeHyperlinkListener(HyperlinkListener listener)
+ {
+ listenerList.remove(HyperlinkListener.class, listener);
+ }
+
+ /**
+ * Returns all added <code>HyperlinkListener</code> objects.
+ *
+ * @return array of listeners
+ *
+ * @since 1.4
+ */
+ public HyperlinkListener[] getHyperlinkListeners()
+ {
+ return (HyperlinkListener[]) getListeners(HyperlinkListener.class);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JFileChooser.java b/libjava/classpath/javax/swing/JFileChooser.java
new file mode 100644
index 000000000..61b2fde73
--- /dev/null
+++ b/libjava/classpath/javax/swing/JFileChooser.java
@@ -0,0 +1,1626 @@
+/* JFileChooser.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Frame;
+import java.awt.GraphicsEnvironment;
+import java.awt.HeadlessException;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+import java.beans.PropertyChangeEvent;
+import java.io.File;
+import java.util.ArrayList;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileSystemView;
+import javax.swing.filechooser.FileView;
+import javax.swing.plaf.FileChooserUI;
+
+
+/**
+ * A component that provides the user a dialog box to browse through a
+ * filesystem and choose one or more files or directories.
+ *
+ * A JFileChooser can be configured to filter the displayed file list
+ * by adding a {@link FileFilter} instance using
+ * {@link #addChoosableFileFilter(FileFilter)}. Additional components can
+ * be embedded in the file chooser using {@link #setAccessory(JComponent)}.
+ * The JFileChooser properties also provide mechanisms to customize the
+ * behaviour of the file chooser.
+ *
+ * @author Kim Ho (kho@luxsci.net)
+ */
+public class JFileChooser extends JComponent implements Accessible
+{
+ private static final long serialVersionUID = 3162921138695327837L;
+
+ /**
+ * A dialog type for selecting a file to open.
+ * @see #setDialogType(int)
+ */
+ public static final int OPEN_DIALOG = 0;
+
+ /**
+ * A dialog type for selecting a file to save.
+ * @see #setDialogType(int)
+ */
+ public static final int SAVE_DIALOG = 1;
+
+ /**
+ * A dialog type for some custom purpose.
+ * @see #setDialogType(int)
+ */
+ public static final int CUSTOM_DIALOG = 2;
+
+ /**
+ * A return value indicating the file chooser has been closed by cancelling.
+ *
+ * @see #showOpenDialog(Component)
+ * @see #showSaveDialog(Component)
+ */
+ public static final int CANCEL_OPTION = 1;
+
+ /**
+ * A return value indicating the file chooser has been closed by approving
+ * the selection.
+ * @see #showOpenDialog(Component)
+ * @see #showSaveDialog(Component)
+ */
+ public static final int APPROVE_OPTION = 0;
+
+ /**
+ * A return value indicating the file chooser has been closed by some error.
+ * @see #showOpenDialog(Component)
+ * @see #showSaveDialog(Component)
+ */
+ public static final int ERROR_OPTION = -1;
+
+ /**
+ * A selection mode constant indicating acceptance of files only.
+ * @see #setFileSelectionMode(int)
+ */
+ public static final int FILES_ONLY = 0;
+
+ /**
+ * A selection mode constant indicating acceptance of directories only.
+ * @see #setFileSelectionMode(int)
+ */
+ public static final int DIRECTORIES_ONLY = 1;
+
+ /**
+ * A selection mode constant indicating acceptance of files and directories.
+ * @see #setFileSelectionMode(int)
+ */
+ public static final int FILES_AND_DIRECTORIES = 2;
+
+ /**
+ * Action command string for cancelling the current selection.
+ * @see #cancelSelection()
+ */
+ public static final String CANCEL_SELECTION = "CancelSelection";
+
+ /**
+ * Action command string for approving the current selection.
+ * @see #cancelSelection()
+ */
+ public static final String APPROVE_SELECTION = "ApproveSelection";
+
+ /**
+ * The name of the property for the approve button text.
+ * @see #setApproveButtonText(String)
+ */
+ public static final String APPROVE_BUTTON_TEXT_CHANGED_PROPERTY =
+ "ApproveButtonTextChangedProperty";
+
+ /**
+ * The name of the property for the approve button tool tip text.
+ * @see #setApproveButtonToolTipText(String)
+ */
+ public static final String APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY =
+ "ApproveButtonToolTipTextChangedProperty";
+
+ /**
+ * The name of the property for the approve button mnemonic.
+ * @see #setApproveButtonMnemonic(int)
+ */
+ public static final String APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY =
+ "ApproveButtonMnemonicChangedProperty";
+
+ /**
+ * The name of the property for control button visibility.
+ * @see #setControlButtonsAreShown(boolean)
+ */
+ public static final String CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY =
+ "ControlButtonsAreShownChangedProperty";
+
+ /**
+ * The name of the property for the current directory.
+ * @see #setCurrentDirectory(File)
+ */
+ public static final String DIRECTORY_CHANGED_PROPERTY = "directoryChanged";
+
+ /**
+ * The name of the property for the selected file.
+ * @see #setSelectedFile(File)
+ */
+ public static final String SELECTED_FILE_CHANGED_PROPERTY =
+ "SelectedFileChangedProperty";
+
+ /**
+ * The name of the property for the selected files.
+ * @see #setSelectedFiles(File[])
+ */
+ public static final String SELECTED_FILES_CHANGED_PROPERTY =
+ "SelectedFilesChangedProperty";
+
+ /**
+ * The name of the property for multi-selection.
+ * @see #setMultiSelectionEnabled(boolean)
+ */
+ public static final String MULTI_SELECTION_ENABLED_CHANGED_PROPERTY =
+ "MultiSelectionEnabledChangedProperty";
+
+ /**
+ * The name of the 'file system view' property.
+ * @see #setFileSystemView(FileSystemView)
+ */
+ public static final String FILE_SYSTEM_VIEW_CHANGED_PROPERTY =
+ "FileSystemViewChanged";
+
+ /**
+ * The name of the 'file view' property.
+ * @see #setFileView(FileView)
+ */
+ public static final String FILE_VIEW_CHANGED_PROPERTY = "fileViewChanged";
+
+ /**
+ * The name of the 'file hiding enabled' property.
+ * @see #setFileHidingEnabled(boolean)
+ */
+ public static final String FILE_HIDING_CHANGED_PROPERTY =
+ "FileHidingChanged";
+
+ /**
+ * The name of the 'file filter' property.
+ * @see #setFileFilter(FileFilter)
+ */
+ public static final String FILE_FILTER_CHANGED_PROPERTY =
+ "fileFilterChanged";
+
+ /**
+ * The name of the 'file selection mode' property.
+ * @see #setFileSelectionMode(int)
+ */
+ public static final String FILE_SELECTION_MODE_CHANGED_PROPERTY =
+ "fileSelectionChanged";
+
+ /**
+ * The name of the 'accessory' property.
+ * @see #setAccessory(JComponent)
+ */
+ public static final String ACCESSORY_CHANGED_PROPERTY =
+ "AccessoryChangedProperty";
+
+ /**
+ * The name of the 'accept all file filter used' property.
+ * @see #setAcceptAllFileFilterUsed(boolean)
+ */
+ public static final String ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY =
+ "acceptAllFileFilterUsedChanged";
+
+ /**
+ * The name of the 'dialog title' property.
+ * @see #setDialogTitle(String)
+ */
+ public static final String DIALOG_TITLE_CHANGED_PROPERTY =
+ "DialogTitleChangedProperty";
+
+ /**
+ * The name of the 'dialog type' property.
+ * @see #setDialogType(int)
+ */
+ public static final String DIALOG_TYPE_CHANGED_PROPERTY =
+ "DialogTypeChangedProperty";
+
+ /**
+ * The name of the 'choosable file filters' property.
+ * @see #addChoosableFileFilter(FileFilter)
+ */
+ public static final String CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY =
+ "ChoosableFileFilterChangedProperty";
+
+ /**
+ * The accessible context.
+ * @see #getAccessibleContext()
+ */
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * The file system view.
+ * @see #setFileSystemView(FileSystemView)
+ */
+ private FileSystemView fsv;
+
+ /**
+ * The accessory component.
+ * @see #setAccessory(JComponent)
+ */
+ private JComponent accessory;
+
+ /**
+ * The approve button mnemonic.
+ * @see #setApproveButtonMnemonic(int)
+ */
+ private int approveButtonMnemonic = 0;
+
+ /**
+ * The approve button text.
+ * @see #setApproveButtonText(String)
+ */
+ private String approveButtonText;
+
+ /**
+ * The approve button tool tip text.
+ * @see #setApproveButtonToolTipText(String)
+ */
+ private String approveButtonToolTipText;
+
+ /**
+ * The choosable file filters.
+ * @see #addChoosableFileFilter(FileFilter)
+ */
+ private ArrayList choosableFilters = new ArrayList();
+
+ /**
+ * A flag controlling whether the accept all file filter is used.
+ * @see #setAcceptAllFileFilterUsed(boolean)
+ */
+ private boolean isAcceptAll = true;
+
+ /**
+ * The dialog title.
+ * @see #setDialogTitle(String)
+ */
+ private String dialogTitle;
+
+ /**
+ * The dialog type.
+ * @see #setDialogType(int)
+ */
+ private int dialogType = OPEN_DIALOG;
+
+ /**
+ * The return value for the dialog.
+ * @see #showOpenDialog(Component)
+ * @see #showSaveDialog(Component)
+ */
+ private int retval = ERROR_OPTION;
+
+ /**
+ * A flag indicating whether the file chooser allows multiple selection.
+ * @see #isMultiSelectionEnabled()
+ */
+ private boolean multiSelection = false;
+
+ /**
+ * A flag indicating whether file hiding is enabled.
+ * @see #isFileHidingEnabled()
+ */
+ private boolean fileHiding = true;
+
+ /**
+ * The file selection mode.
+ * @see #setFileSelectionMode(int)
+ */
+ private int fileSelectionMode = FILES_ONLY;
+
+ /**
+ * The file view.
+ * @see #setFileView(FileView)
+ */
+ private FileView fv = null;
+
+ /**
+ * A flag controlling whether or not the control buttons are visible.
+ * @see #setControlButtonsAreShown(boolean)
+ */
+ private boolean controlButtonsShown = true;
+
+ /**
+ * The current directory.
+ * @see #setCurrentDirectory(File)
+ */
+ private File currentDir = null;
+
+ /**
+ * The current file filter.
+ * @see #setFileFilter(FileFilter)
+ */
+ private FileFilter currentFilter = null;
+
+ /**
+ * An array of selected files.
+ * @see #setSelectedFiles(File[])
+ */
+ private File[] selectedFiles;
+
+ /**
+ * The selected file.
+ * @see #setSelectedFile(File)
+ */
+ private File selectedFile;
+
+ /**
+ * The drag enabled property.
+ * @see #setDragEnabled(boolean)
+ * @see #getDragEnabled()
+ */
+ private boolean dragEnabled;
+
+ /**
+ * Creates a new <code>JFileChooser</code> object.
+ */
+ public JFileChooser()
+ {
+ setup(null);
+ setCurrentDirectory(null);
+ }
+
+ /**
+ * Creates a new <code>JFileChooser</code> object.
+ *
+ * @param currentDirectoryPath the directory that should initially be
+ * shown in the filechooser (if <code>null</code>, the user's home
+ * directory is used).
+ */
+ public JFileChooser(String currentDirectoryPath)
+ {
+ this(currentDirectoryPath, null);
+ }
+
+ /**
+ * Creates a new <code>JFileChooser</code> object with the specified
+ * directory and {@link FileSystemView}.
+ *
+ * @param currentDirectoryPath the directory that should initially be
+ * shown in the filechooser (if <code>null</code>, the user's home
+ * directory is used).
+ * @param fsv the file system view (if <code>null</code>, the default file
+ * system view is used).
+ */
+ public JFileChooser(String currentDirectoryPath, FileSystemView fsv)
+ {
+ setup(fsv);
+ File dir = null;
+ if (currentDirectoryPath != null)
+ dir = getFileSystemView().createFileObject(currentDirectoryPath);
+ setCurrentDirectory(dir);
+ }
+
+ /**
+ * Creates a new <code>JFileChooser</code> object.
+ *
+ * @param currentDirectory the directory that should initially be
+ * shown in the filechooser (if <code>null</code>, the user's home
+ * directory is used).
+ */
+ public JFileChooser(File currentDirectory)
+ {
+ setup(null);
+ setCurrentDirectory(currentDirectory);
+ }
+
+ /**
+ * Creates a new <code>JFileChooser</code> object.
+ *
+ * @param fsv the file system view (if <code>null</code>, the default file
+ * system view is used).
+ */
+ public JFileChooser(FileSystemView fsv)
+ {
+ setup(fsv);
+ setCurrentDirectory(null);
+ }
+
+ /**
+ * Creates a new <code>JFileChooser</code> object.
+ *
+ * @param currentDirectory the directory that should initially be
+ * shown in the filechooser (if <code>null</code>, the user's home
+ * directory is used).
+ * @param fsv the file system view (if <code>null</code>, the default file
+ * system view is used).
+ */
+ public JFileChooser(File currentDirectory, FileSystemView fsv)
+ {
+ setup(fsv);
+ setCurrentDirectory(currentDirectory);
+ }
+
+ /**
+ * Sets up the file chooser. This method is called by all the constructors.
+ *
+ * @param view the file system view (if <code>null</code>, the default file
+ * system view is used).
+ *
+ * @see FileSystemView#getFileSystemView()
+ */
+ protected void setup(FileSystemView view)
+ {
+ if (view == null)
+ view = FileSystemView.getFileSystemView();
+ setFileSystemView(view);
+ updateUI();
+ }
+
+ /**
+ * Sets the dragEnabled property, this disables/enables automatic drag
+ * handling (drag and drop) on this component. The default value of the
+ * dragEnabled property is false.
+ *
+ * Some look and feels might not support automatic drag and drop; they
+ * will ignore this property.
+ *
+ * @param b - the new dragEnabled value
+ */
+ public void setDragEnabled(boolean b)
+ {
+ if (b && GraphicsEnvironment.isHeadless())
+ throw new HeadlessException();
+
+ dragEnabled = b;
+ }
+
+ /**
+ * Returns true if dragging is enabled.
+ *
+ * @return true if dragging is enabled.
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * Returns the selected file, if there is one.
+ *
+ * @return The selected file (possibly <code>null</code>).
+ *
+ * @see #setSelectedFile(File)
+ */
+ public File getSelectedFile()
+ {
+ return selectedFile;
+ }
+
+ /**
+ * Sets the selected file and sends a {@link PropertyChangeEvent} to all
+ * registered listeners. The property name is
+ * {@link #SELECTED_FILE_CHANGED_PROPERTY}.
+ *
+ * @param file the file (<code>null</code> permitted).
+ */
+ public void setSelectedFile(File file)
+ {
+ if (selectedFile == null || !selectedFile.equals(file))
+ {
+ File old = selectedFile;
+ selectedFile = file;
+ firePropertyChange(SELECTED_FILE_CHANGED_PROPERTY, old, selectedFile);
+ }
+ }
+
+ /**
+ * Returns the selected file or files in an array. If no files are selected,
+ * an empty array is returned.
+ *
+ * @return An array of the selected files (possibly empty).
+ */
+ public File[] getSelectedFiles()
+ {
+ if (selectedFiles != null)
+ return selectedFiles;
+ if (selectedFile != null)
+ return new File[] { selectedFile };
+ return new File[0];
+ }
+
+ /**
+ * Sets the selected files and sends a {@link PropertyChangeEvent} (with the
+ * name {@link #SELECTED_FILES_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param selectedFiles the selected files (<code>null</code> permitted).
+ */
+ public void setSelectedFiles(File[] selectedFiles)
+ {
+ if (selectedFiles == null)
+ selectedFiles = new File[0];
+ if (selectedFiles.length > 0)
+ setSelectedFile(selectedFiles[0]);
+ else
+ setSelectedFile(null);
+ if (this.selectedFiles != selectedFiles)
+ {
+ File[] old = this.selectedFiles;
+ this.selectedFiles = selectedFiles;
+ firePropertyChange(SELECTED_FILES_CHANGED_PROPERTY, old, selectedFiles);
+ }
+
+ }
+
+ /**
+ * Returns the current directory.
+ *
+ * @return The current directory.
+ */
+ public File getCurrentDirectory()
+ {
+ return currentDir;
+ }
+
+ /**
+ * Sets the current directory and fires a {@link PropertyChangeEvent} (with
+ * the property name {@link #DIRECTORY_CHANGED_PROPERTY}) to all registered
+ * listeners. If <code>dir</code> is <code>null</code>, the current
+ * directory is set to the default directory returned by the file system
+ * view.
+ *
+ * @param dir the new directory (<code>null</code> permitted).
+ *
+ * @see FileSystemView#getDefaultDirectory()
+ */
+ public void setCurrentDirectory(File dir)
+ {
+ if (currentDir != dir || dir == null)
+ {
+ if (dir == null)
+ dir = fsv.getDefaultDirectory();
+
+ File old = currentDir;
+ currentDir = dir;
+ firePropertyChange(DIRECTORY_CHANGED_PROPERTY, old, currentDir);
+ }
+ }
+
+ /**
+ * Called by the UI delegate when the parent directory is changed.
+ */
+ public void changeToParentDirectory()
+ {
+ setCurrentDirectory(fsv.getParentDirectory(currentDir));
+ }
+
+ /**
+ * Rescans the current directory (this is handled by the UI delegate).
+ */
+ public void rescanCurrentDirectory()
+ {
+ getUI().rescanCurrentDirectory(this);
+ }
+
+ /**
+ * Ensures the the specified file is visible (this is handled by the
+ * UI delegate).
+ *
+ * @param f the file.
+ */
+ public void ensureFileIsVisible(File f)
+ {
+ getUI().ensureFileIsVisible(this, f);
+ }
+
+ /**
+ * Displays the file chooser in a modal dialog using the
+ * {@link #OPEN_DIALOG} type.
+ *
+ * @param parent the parent component.
+ *
+ * @return A return value indicating how the dialog was closed (one of
+ * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and
+ * {@link #ERROR_OPTION}).
+ *
+ * @throws HeadlessException DOCUMENT ME!
+ */
+ public int showOpenDialog(Component parent) throws HeadlessException
+ {
+ JDialog d = createDialog(parent);
+
+ // FIXME: Remove when we get ancestor property
+ d.setTitle("Open");
+ setDialogType(OPEN_DIALOG);
+
+ retval = ERROR_OPTION;
+
+ d.pack();
+ d.show();
+ return retval;
+ }
+
+ /**
+ * Displays the file chooser in a modal dialog using the
+ * {@link #SAVE_DIALOG} type.
+ *
+ * @param parent the parent component.
+ *
+ * @return A return value indicating how the dialog was closed (one of
+ * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and
+ * {@link #ERROR_OPTION}).
+ *
+ * @throws HeadlessException DOCUMENT ME!
+ */
+ public int showSaveDialog(Component parent) throws HeadlessException
+ {
+ JDialog d = createDialog(parent);
+ setDialogType(SAVE_DIALOG);
+
+ retval = ERROR_OPTION;
+
+ d.pack();
+ d.show();
+ return retval;
+ }
+
+ /**
+ * Displays the file chooser in a modal dialog using the
+ * {@link #CUSTOM_DIALOG} type.
+ *
+ * @param parent the parent component.
+ *
+ * @return A return value indicating how the dialog was closed (one of
+ * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and
+ * {@link #ERROR_OPTION}).
+ *
+ * @throws HeadlessException DOCUMENT ME!
+ */
+ public int showDialog(Component parent, String approveButtonText)
+ throws HeadlessException
+ {
+ JDialog d = createDialog(parent);
+ setApproveButtonText(approveButtonText);
+ setDialogType(CUSTOM_DIALOG);
+
+ retval = ERROR_OPTION;
+
+ d.pack();
+ d.show();
+ return retval;
+ }
+
+ /**
+ * Creates a modal dialog in which to display the file chooser.
+ *
+ * @param parent the parent component.
+ *
+ * @return The dialog.
+ *
+ * @throws HeadlessException DOCUMENT ME!
+ */
+ protected JDialog createDialog(Component parent) throws HeadlessException
+ {
+ Frame toUse = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent);
+ if (toUse == null)
+ toUse = (Frame) SwingUtilities.getOwnerFrame(null);
+
+ JDialog dialog = new JDialog(toUse);
+ setSelectedFile(null);
+ dialog.getContentPane().add(this);
+ dialog.addWindowListener( new WindowAdapter()
+ {
+ public void windowClosing(WindowEvent e)
+ {
+ cancelSelection();
+ }
+ });
+ dialog.setModal(true);
+ dialog.invalidate();
+ dialog.repaint();
+ return dialog;
+ }
+
+ /**
+ * Returns the flag that controls whether or not the control buttons are
+ * shown on the file chooser.
+ *
+ * @return A boolean.
+ *
+ * @see #setControlButtonsAreShown(boolean)
+ */
+ public boolean getControlButtonsAreShown()
+ {
+ return controlButtonsShown;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the control buttons are
+ * shown and, if it changes, sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY}) to
+ * all registered listeners.
+ *
+ * @param b the new value for the flag.
+ */
+ public void setControlButtonsAreShown(boolean b)
+ {
+ if (controlButtonsShown != b)
+ {
+ controlButtonsShown = b;
+ firePropertyChange(CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY,
+ ! controlButtonsShown, controlButtonsShown);
+ }
+ }
+
+ /**
+ * Returns the type of file chooser.
+ *
+ * @return {@link #OPEN_DIALOG}, {@link #SAVE_DIALOG} or
+ * {@link #CUSTOM_DIALOG}.
+ *
+ * @see #setDialogType(int)
+ */
+ public int getDialogType()
+ {
+ return dialogType;
+ }
+
+ /**
+ * Sets the dialog type and fires a {@link PropertyChangeEvent} (with the
+ * property name {@link #DIALOG_TYPE_CHANGED_PROPERTY}) to all
+ * registered listeners.
+ *
+ * @param dialogType the dialog type (one of: {@link #OPEN_DIALOG},
+ * {@link #SAVE_DIALOG}, {@link #CUSTOM_DIALOG}).
+ *
+ * @throws IllegalArgumentException if <code>dialogType</code> is not valid.
+ */
+ public void setDialogType(int dialogType)
+ {
+ if (dialogType != OPEN_DIALOG && dialogType != SAVE_DIALOG
+ && dialogType != CUSTOM_DIALOG)
+ throw new IllegalArgumentException("Choose allowable dialogType.");
+
+ if (this.dialogType != dialogType)
+ {
+ int old = this.dialogType;
+ this.dialogType = dialogType;
+ firePropertyChange(DIALOG_TYPE_CHANGED_PROPERTY, old, this.dialogType);
+ }
+ }
+
+ /**
+ * Sets the dialog title and sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #DIALOG_TITLE_CHANGED_PROPERTY}) to all
+ * registered listeners.
+ *
+ * @param dialogTitle the dialog title (<code>null</code> permitted).
+ *
+ * @see #getDialogTitle()
+ */
+ public void setDialogTitle(String dialogTitle)
+ {
+ if (this.dialogTitle != dialogTitle)
+ {
+ String old = this.dialogTitle;
+ this.dialogTitle = dialogTitle;
+ firePropertyChange(DIALOG_TITLE_CHANGED_PROPERTY, old, this.dialogTitle);
+ }
+ }
+
+ /**
+ * Returns the dialog title.
+ *
+ * @return The dialog title (possibly <code>null</code>).
+ *
+ * @see #setDialogTitle(String)
+ */
+ public String getDialogTitle()
+ {
+ return dialogTitle;
+ }
+
+ /**
+ * Sets the tool tip text for the approve button and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY}) to all
+ * registered listeners.
+ *
+ * @param toolTipText the text.
+ */
+ public void setApproveButtonToolTipText(String toolTipText)
+ {
+ if (approveButtonToolTipText != toolTipText)
+ {
+ String oldText = approveButtonToolTipText;
+ approveButtonToolTipText = toolTipText;
+ firePropertyChange(APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY,
+ oldText, approveButtonToolTipText);
+ }
+ }
+
+ /**
+ * Returns the tool tip text for the approve button.
+ *
+ * @return The tool tip text for the approve button.
+ *
+ * @see #setApproveButtonToolTipText(String)
+ */
+ public String getApproveButtonToolTipText()
+ {
+ return approveButtonToolTipText;
+ }
+
+ /**
+ * Returns the approve button mnemonic, or zero if no mnemonic has been set.
+ *
+ * @return The approve button mnemonic.
+ *
+ * @see #setApproveButtonMnemonic(int)
+ */
+ public int getApproveButtonMnemonic()
+ {
+ return approveButtonMnemonic;
+ }
+
+ /**
+ * Sets the mnemonic for the approve button and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param mnemonic the mnemonic.
+ *
+ * @see #setApproveButtonMnemonic(char)
+ */
+ public void setApproveButtonMnemonic(int mnemonic)
+ {
+ if (approveButtonMnemonic != mnemonic)
+ {
+ int oldMnemonic = approveButtonMnemonic;
+ approveButtonMnemonic = mnemonic;
+ firePropertyChange(APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY,
+ oldMnemonic, approveButtonMnemonic);
+ }
+ }
+
+ /**
+ * Sets the mnemonic for the approve button and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param mnemonic the mnemonic.
+ *
+ * @see #setApproveButtonMnemonic(int)
+ */
+ public void setApproveButtonMnemonic(char mnemonic)
+ {
+ setApproveButtonMnemonic((int) Character.toUpperCase(mnemonic));
+ }
+
+ /**
+ * Sets the approve button text and fires a {@link PropertyChangeEvent}
+ * (with the property name {@link #APPROVE_BUTTON_TEXT_CHANGED_PROPERTY}) to
+ * all registered listeners.
+ *
+ * @param approveButtonText the text (<code>null</code> permitted).
+ *
+ * @see #getApproveButtonText()
+ */
+ public void setApproveButtonText(String approveButtonText)
+ {
+ if (this.approveButtonText != approveButtonText)
+ {
+ String oldText = this.approveButtonText;
+ this.approveButtonText = approveButtonText;
+ firePropertyChange(APPROVE_BUTTON_TEXT_CHANGED_PROPERTY, oldText,
+ this.approveButtonText);
+ }
+ }
+
+ /**
+ * Returns the approve button text.
+ *
+ * @return The approve button text (possibly <code>null</code>).
+ *
+ * @see #setApproveButtonText(String)
+ */
+ public String getApproveButtonText()
+ {
+ return approveButtonText;
+ }
+
+ /**
+ * Returns the available file filters for this file chooser.
+ *
+ * @return The available file filters.
+ */
+ public FileFilter[] getChoosableFileFilters()
+ {
+ return (FileFilter[]) choosableFilters.toArray(new FileFilter[choosableFilters.size()]);
+ }
+
+ /**
+ * Adds a file filter to the list of available filters and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param filter the filter (<code>null</code> permitted).
+ */
+ public void addChoosableFileFilter(FileFilter filter)
+ {
+ if (filter != null)
+ {
+ FileFilter[] old = getChoosableFileFilters();
+ choosableFilters.add(filter);
+ FileFilter[] newFilters = getChoosableFileFilters();
+ firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, old,
+ newFilters);
+ }
+ setFileFilter(filter);
+ }
+
+ /**
+ * Removes a file filter from the list of available filters and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param f the file filter.
+ *
+ * @return <code>true</code> if the filter was removed and
+ * <code>false</code> otherwise.
+ */
+ public boolean removeChoosableFileFilter(FileFilter f)
+ {
+ if (f == currentFilter)
+ setFileFilter(null);
+ FileFilter[] old = getChoosableFileFilters();
+ if (! choosableFilters.remove(f))
+ return false;
+ FileFilter[] newFilters = getChoosableFileFilters();
+ firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, old, newFilters);
+ return true;
+ }
+
+ /**
+ * Clears the list of choosable file filters and installs the 'accept all'
+ * filter from the UI delegate.
+ */
+ public void resetChoosableFileFilters()
+ {
+ choosableFilters.clear();
+ choosableFilters.add(getUI().getAcceptAllFileFilter(this));
+ setFileFilter((FileFilter) choosableFilters.get(0));
+ }
+
+ /**
+ * Returns the 'accept all' file filter from the UI delegate.
+ *
+ * @return The 'accept all' file filter.
+ */
+ public FileFilter getAcceptAllFileFilter()
+ {
+ return getUI().getAcceptAllFileFilter(this);
+ }
+
+ /**
+ * Returns the flag that controls whether or not the 'accept all' file
+ * filter is included in the list of filters.
+ *
+ * @return A boolean.
+ *
+ * @see #setAcceptAllFileFilterUsed(boolean)
+ */
+ public boolean isAcceptAllFileFilterUsed()
+ {
+ return isAcceptAll;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the 'accept all' file filter
+ * is included in the list of filters, and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param b the new value of the flag.
+ */
+ public void setAcceptAllFileFilterUsed(boolean b)
+ {
+ if (isAcceptAll != b)
+ {
+ isAcceptAll = b;
+ if (b)
+ addChoosableFileFilter(getAcceptAllFileFilter());
+ else
+ removeChoosableFileFilter(getAcceptAllFileFilter());
+ firePropertyChange(ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY,
+ ! isAcceptAll, isAcceptAll);
+ }
+ }
+
+ /**
+ * Returns the accessory component for the file chooser. The default
+ * value is <code>null</code>.
+ *
+ * @return The accessory component (possibly <code>null</code>).
+ *
+ * @see #setAccessory(JComponent)
+ */
+ public JComponent getAccessory()
+ {
+ return accessory;
+ }
+
+ /**
+ * Sets the accessory component for the file chooser and sends a
+ * {@link PropertyChangeEvent} to all registered listeners. The property
+ * name is {@link #ACCESSORY_CHANGED_PROPERTY}.
+ *
+ * @param newAccessory the accessory component.
+ */
+ public void setAccessory(JComponent newAccessory)
+ {
+ if (accessory != newAccessory)
+ {
+ JComponent old = accessory;
+ accessory = newAccessory;
+ firePropertyChange(ACCESSORY_CHANGED_PROPERTY, old, accessory);
+ }
+ }
+
+ /**
+ * Sets the file selection mode and sends a {@link PropertyChangeEvent}
+ * to all registered listeners. The property name is
+ * {@link #FILE_SELECTION_MODE_CHANGED_PROPERTY}.
+ *
+ * @param mode the mode ({@link #FILES_ONLY}, {@link #DIRECTORIES_ONLY} or
+ * {@link #FILES_AND_DIRECTORIES}).
+ *
+ * @throws IllegalArgumentException if the mode is invalid.
+ */
+ public void setFileSelectionMode(int mode)
+ {
+ if (mode != FILES_ONLY && mode != DIRECTORIES_ONLY
+ && mode != FILES_AND_DIRECTORIES)
+ throw new IllegalArgumentException("Choose a correct file selection mode.");
+ if (fileSelectionMode != mode)
+ {
+ int old = fileSelectionMode;
+ fileSelectionMode = mode;
+ firePropertyChange(FILE_SELECTION_MODE_CHANGED_PROPERTY, old,
+ fileSelectionMode);
+ }
+ }
+
+ /**
+ * Returns the file selection mode, one of: {@link #FILES_ONLY},
+ * {@link #DIRECTORIES_ONLY} or {@link #FILES_AND_DIRECTORIES}. The
+ * default is {@link #FILES_ONLY}.
+ *
+ * @return The file selection mode.
+ *
+ * @see #setFileSelectionMode(int)
+ */
+ public int getFileSelectionMode()
+ {
+ return fileSelectionMode;
+ }
+
+ /**
+ * Returns <code>true</code> if file selection is enabled, and
+ * <code>false</code> otherwise. File selection is enabled when the
+ * file selection mode is {@link #FILES_ONLY} or
+ * {@link #FILES_AND_DIRECTORIES}.
+ *
+ * @return <code>true</code> if file selection is enabled.
+ *
+ * @see #getFileSelectionMode()
+ */
+ public boolean isFileSelectionEnabled()
+ {
+ return (fileSelectionMode == FILES_ONLY
+ || fileSelectionMode == FILES_AND_DIRECTORIES);
+ }
+
+ /**
+ * Returns <code>true</code> if directory selection is enabled, and
+ * <code>false</code> otherwise. Directory selection is enabled when the
+ * file selection mode is {@link #DIRECTORIES_ONLY} or
+ * {@link #FILES_AND_DIRECTORIES}.
+ *
+ * @return <code>true</code> if file selection is enabled.
+ *
+ * @see #getFileSelectionMode()
+ */
+ public boolean isDirectorySelectionEnabled()
+ {
+ return (fileSelectionMode == DIRECTORIES_ONLY
+ || fileSelectionMode == FILES_AND_DIRECTORIES);
+ }
+
+ /**
+ * Sets the flag that controls whether multiple selections are allowed in
+ * this filechooser and sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #MULTI_SELECTION_ENABLED_CHANGED_PROPERTY}) to all
+ * registered listeners.
+ *
+ * @param b the new value of the flag.
+ */
+ public void setMultiSelectionEnabled(boolean b)
+ {
+ if (multiSelection != b)
+ {
+ multiSelection = b;
+ firePropertyChange(MULTI_SELECTION_ENABLED_CHANGED_PROPERTY,
+ ! multiSelection, multiSelection);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if multiple selections are allowed within this
+ * file chooser, and <code>false</code> otherwise.
+ *
+ * @return A boolean.
+ *
+ * @see #setMultiSelectionEnabled(boolean)
+ */
+ public boolean isMultiSelectionEnabled()
+ {
+ return multiSelection;
+ }
+
+ /**
+ * Returns <code>true</code> if hidden files are to be hidden, and
+ * <code>false</code> otherwise.
+ *
+ * @return A boolean.
+ *
+ * @see #setFileHidingEnabled(boolean)
+ */
+ public boolean isFileHidingEnabled()
+ {
+ return fileHiding;
+ }
+
+ /**
+ * Sets the flag that controls whether or not hidden files are displayed,
+ * and sends a {@link PropertyChangeEvent} (with the property name
+ * {@link #FILE_HIDING_CHANGED_PROPERTY}) to all registered listeners.
+ *
+ * @param b the new value of the flag.
+ */
+ public void setFileHidingEnabled(boolean b)
+ {
+ if (fileHiding != b)
+ {
+ fileHiding = b;
+ firePropertyChange(FILE_HIDING_CHANGED_PROPERTY, ! fileHiding,
+ fileHiding);
+ }
+ }
+
+ /**
+ * Sets the file filter and sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #FILE_FILTER_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param filter the filter (<code>null</code> permitted).
+ */
+ public void setFileFilter(FileFilter filter)
+ {
+ if (currentFilter != filter)
+ {
+ if (filter != null && !choosableFilters.contains(filter))
+ addChoosableFileFilter(filter);
+ FileFilter old = currentFilter;
+ currentFilter = filter;
+ firePropertyChange(FILE_FILTER_CHANGED_PROPERTY, old, currentFilter);
+ }
+ }
+
+ /**
+ * Returns the file filter.
+ *
+ * @return The file filter.
+ *
+ * @see #setFileFilter(FileFilter)
+ */
+ public FileFilter getFileFilter()
+ {
+ return currentFilter;
+ }
+
+ /**
+ * Sets a custom {@link FileView} for the file chooser and sends a
+ * {@link PropertyChangeEvent} to all registered listeners. The property
+ * name is {@link #FILE_VIEW_CHANGED_PROPERTY}.
+ *
+ * @param fileView the file view (<code>null</code> permitted).
+ *
+ * @see #getFileView()
+ */
+ public void setFileView(FileView fileView)
+ {
+ if (fv != fileView)
+ {
+ FileView old = fv;
+ fv = fileView;
+ firePropertyChange(FILE_VIEW_CHANGED_PROPERTY, old, fv);
+ }
+ }
+
+ /**
+ * Returns the custom {@link FileView} for the file chooser.
+ *
+ * @return The file view (possibly <code>null</code>).
+ */
+ public FileView getFileView()
+ {
+ return fv;
+ }
+
+ /**
+ * Returns the name of the file, generated by the current (or default)
+ * {@link FileView}.
+ *
+ * @param f the file.
+ *
+ * @return The file name.
+ */
+ public String getName(File f)
+ {
+ String name = null;
+ if (fv != null)
+ name = fv.getName(f);
+ if (name == null)
+ name = getUI().getFileView(this).getName(f);
+ return name;
+ }
+
+ /**
+ * Returns the description of the file, generated by the current (or default)
+ * {@link FileView}.
+ *
+ * @param f the file.
+ *
+ * @return The file description.
+ */
+ public String getDescription(File f)
+ {
+ String result = null;
+ if (fv != null)
+ result = fv.getDescription(f);
+ if (result == null)
+ result = getUI().getFileView(this).getDescription(f);
+ return result;
+ }
+
+ /**
+ * Returns the type description for the file, generated by the current (or
+ * default) {@link FileView}.
+ *
+ * @param f the file.
+ *
+ * @return The file type description.
+ */
+ public String getTypeDescription(File f)
+ {
+ String result = null;
+ if (fv != null)
+ result = getFileView().getTypeDescription(f);
+ if (result == null)
+ result = getUI().getFileView(this).getTypeDescription(f);
+ return result;
+ }
+
+ /**
+ * Returns the icon provided by the current (or default) {@link FileView}.
+ *
+ * @param f the file.
+ *
+ * @return An icon representing the file.
+ */
+ public Icon getIcon(File f)
+ {
+ Icon result = null;
+ if (fv != null)
+ result = fv.getIcon(f);
+ if (result == null)
+ result = getUI().getFileView(this).getIcon(f);
+ return result;
+ }
+
+ /**
+ * Returns <code>true</code> if the file is traversable, and
+ * <code>false</code> otherwise.
+ *
+ * @param f the file or directory.
+ *
+ * @return A boolean.
+ */
+ public boolean isTraversable(File f)
+ {
+ return getFileSystemView().isTraversable(f).booleanValue();
+ }
+
+ /**
+ * Returns <code>true</code> if the file is accepted by the current
+ * file filter.
+ *
+ * @param f the file.
+ *
+ * @return A boolean.
+ */
+ public boolean accept(File f)
+ {
+ if (f == null)
+ return true;
+ FileFilter ff = getFileFilter();
+ if (ff != null)
+ return ff.accept(f);
+ else
+ return true;
+ }
+
+ /**
+ * Sets the file system view for the file chooser and sends a
+ * {@link PropertyChangeEvent} to all registered listeners.
+ *
+ * @param fsv the file system view.
+ */
+ public void setFileSystemView(FileSystemView fsv)
+ {
+ if (this.fsv != fsv)
+ {
+ FileSystemView old = this.fsv;
+ this.fsv = fsv;
+ firePropertyChange(FILE_SYSTEM_VIEW_CHANGED_PROPERTY, old, this.fsv);
+ }
+ }
+
+ /**
+ * Returns the file system view being used by this file chooser.
+ *
+ * @return The file system view.
+ *
+ * @see #setFileSystemView(FileSystemView)
+ */
+ public FileSystemView getFileSystemView()
+ {
+ return fsv;
+ }
+
+ /**
+ * Approves the selection. An {@link ActionEvent} is sent to all registered
+ * listeners.
+ */
+ public void approveSelection()
+ {
+ retval = APPROVE_OPTION;
+ fireActionPerformed(APPROVE_SELECTION);
+ }
+
+ /**
+ * Cancels the selection. An {@link ActionEvent} is sent to all registered
+ * listeners.
+ */
+ public void cancelSelection()
+ {
+ retval = CANCEL_OPTION;
+ fireActionPerformed(CANCEL_SELECTION);
+ }
+
+ /**
+ * Adds an {@link ActionListener} to the file chooser.
+ *
+ * @param l the listener.
+ */
+ public void addActionListener(ActionListener l)
+ {
+ listenerList.add(ActionListener.class, l);
+ }
+
+ /**
+ * Removes an {@link ActionListener} from this file chooser.
+ *
+ * @param l the listener.
+ */
+ public void removeActionListener(ActionListener l)
+ {
+ try
+ {
+ listenerList.remove(ActionListener.class, l);
+ }
+ catch (IllegalArgumentException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns the action listeners registered with this file chooser.
+ *
+ * @return An array of listeners.
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) getListeners(ActionListener.class);
+ }
+
+ /**
+ * Sends an @link {ActionEvent} to all registered listeners.
+ *
+ * @param command the action command.
+ */
+ protected void fireActionPerformed(String command)
+ {
+ ActionListener[] list = getActionListeners();
+ ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ command);
+
+ for (int i = 0; i < list.length; i++)
+ list[i].actionPerformed(event);
+ }
+
+ /**
+ * Installs the UI delegate for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((FileChooserUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the UI delegate class identifier.
+ *
+ * @return <code>FileChooserUI</code>.
+ */
+ public String getUIClassID()
+ {
+ return "FileChooserUI";
+ }
+
+ /**
+ * Returns the UI delegate for the component.
+ *
+ * @return The UI delegate.
+ */
+ public FileChooserUI getUI()
+ {
+ return (FileChooserUI) ui;
+ }
+
+ /**
+ * Returns a string describing the attributes for the
+ * <code>JFileChooser</code> component, for use in debugging. The return
+ * value is guaranteed to be non-<code>null</code>, but the format of the
+ * string may vary between implementations.
+ *
+ * @return A string describing the attributes of the
+ * <code>JFileChooser</code>.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",approveButtonText=");
+ if (approveButtonText != null)
+ sb.append(approveButtonText);
+ sb.append(",currentDirectory=");
+ if (currentDir != null)
+ sb.append(currentDir);
+ sb.append(",dialogTitle=");
+ if (dialogTitle != null)
+ sb.append(dialogTitle);
+ sb.append(",dialogType=");
+ if (dialogType == OPEN_DIALOG)
+ sb.append("OPEN_DIALOG");
+ if (dialogType == SAVE_DIALOG)
+ sb.append("SAVE_DIALOG");
+ if (dialogType == CUSTOM_DIALOG)
+ sb.append("CUSTOM_DIALOG");
+ sb.append(",fileSelectionMode=");
+ if (fileSelectionMode == FILES_ONLY)
+ sb.append("FILES_ONLY");
+ if (fileSelectionMode == DIRECTORIES_ONLY)
+ sb.append("DIRECTORIES_ONLY");
+ if (fileSelectionMode == FILES_AND_DIRECTORIES)
+ sb.append("FILES_AND_DIRECTORIES");
+ sb.append(",returnValue=");
+ if (retval == APPROVE_OPTION)
+ sb.append("APPROVE_OPTION");
+ if (retval == CANCEL_OPTION)
+ sb.append("CANCEL_OPTION");
+ if (retval == ERROR_OPTION)
+ sb.append("ERROR_OPTION");
+ sb.append(",selectedFile=");
+ if (selectedFile != null)
+ sb.append(selectedFile);
+ sb.append(",useFileHiding=").append(fileHiding);
+ return sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JFileChooser</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJFileChooser}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJFileChooser();
+ return accessibleContext;
+ }
+
+ /**
+ * Provides the accessibility features for the <code>JFileChooser</code>
+ * component.
+ */
+ protected class AccessibleJFileChooser
+ extends JComponent.AccessibleJComponent
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJFileChooser</code>.
+ */
+ protected AccessibleJFileChooser()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the <code>JFileChooser</code>
+ * component.
+ *
+ * @return {@link AccessibleRole#FILE_CHOOSER}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.FILE_CHOOSER;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JFormattedTextField.java b/libjava/classpath/javax/swing/JFormattedTextField.java
new file mode 100644
index 000000000..e4b6fec79
--- /dev/null
+++ b/libjava/classpath/javax/swing/JFormattedTextField.java
@@ -0,0 +1,648 @@
+/* JFormattedTextField.java --
+ Copyright (C) 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.event.FocusEvent;
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Date;
+
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.DateFormatter;
+import javax.swing.text.DefaultFormatter;
+import javax.swing.text.DefaultFormatterFactory;
+import javax.swing.text.Document;
+import javax.swing.text.DocumentFilter;
+import javax.swing.text.InternationalFormatter;
+import javax.swing.text.NavigationFilter;
+import javax.swing.text.NumberFormatter;
+
+/**
+ * A text field that makes use of a formatter to display and edit a specific
+ * type of data. The value that is displayed can be an arbitrary object. The
+ * formatter is responsible for displaying the value in a textual form and
+ * it may allow editing of the value.
+ *
+ * Formatters are usually obtained using an instance of
+ * {@link AbstractFormatterFactory}. This factory is responsible for providing
+ * an instance of {@link AbstractFormatter} that is able to handle the
+ * formatting of the value of the JFormattedTextField.
+ *
+ * @author Michael Koch
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ * @since 1.4
+ */
+public class JFormattedTextField extends JTextField
+{
+ private static final long serialVersionUID = 5464657870110180632L;
+
+ /**
+ * An abstract base implementation for a formatter that can be used by
+ * a JTextField. A formatter can display a specific type of object and
+ * may provide a way to edit this value.
+ */
+ public abstract static class AbstractFormatter implements Serializable
+ {
+ private static final long serialVersionUID = -5193212041738979680L;
+
+ private JFormattedTextField textField;
+
+ public AbstractFormatter ()
+ {
+ //Do nothing here.
+ }
+
+ /**
+ * Clones the AbstractFormatter and removes the association to any
+ * particular JFormattedTextField.
+ *
+ * @return a clone of this formatter with no association to any particular
+ * JFormattedTextField
+ * @throws CloneNotSupportedException if the Object's class doesn't support
+ * the {@link Cloneable} interface
+ */
+ protected Object clone()
+ throws CloneNotSupportedException
+ {
+ // Clone this formatter.
+ AbstractFormatter newFormatter = (AbstractFormatter) super.clone();
+
+ // And remove the association to the JFormattedTextField.
+ newFormatter.textField = null;
+ return newFormatter;
+ }
+
+ /**
+ * Returns a custom set of Actions that this formatter supports. Should
+ * be subclassed by formatters that have a custom set of Actions.
+ *
+ * @return <code>null</code>. Should be subclassed by formatters that want
+ * to install custom Actions on the JFormattedTextField.
+ */
+ protected Action[] getActions()
+ {
+ return null;
+ }
+
+ /**
+ * Gets the DocumentFilter for this formatter. Should be subclassed
+ * by formatters wishing to install a filter that oversees Document
+ * mutations.
+ *
+ * @return <code>null</code>. Should be subclassed by formatters
+ * that want to restrict Document mutations.
+ */
+ protected DocumentFilter getDocumentFilter()
+ {
+ // Subclasses should override this if they want to install a
+ // DocumentFilter.
+ return null;
+ }
+
+ /**
+ * Returns the JFormattedTextField on which this formatter is
+ * currently installed.
+ *
+ * @return the JFormattedTextField on which this formatter is currently
+ * installed
+ */
+ protected JFormattedTextField getFormattedTextField()
+ {
+ return textField;
+ }
+
+ /**
+ * Gets the NavigationFilter for this formatter. Should be subclassed
+ * by formatters (such as {@link DefaultFormatter}) that wish to
+ * restrict where the cursor can be placed within the text field.
+ *
+ * @return <code>null</code>. Subclassed by formatters that want to restrict
+ * cursor location within the JFormattedTextField.
+ */
+ protected NavigationFilter getNavigationFilter()
+ {
+ // This should be subclassed if the formatter wants to install
+ // a NavigationFilter on the JFormattedTextField.
+ return null;
+ }
+
+ /**
+ * Installs this formatter on the specified JFormattedTextField. This
+ * converts the current value to a displayable String and displays it,
+ * and installs formatter specific Actions from <code>getActions</code>.
+ * It also installs a DocumentFilter and NavigationFilter on the
+ * JFormattedTextField.
+ * <p>
+ * If there is a <code>ParseException</code> this sets the text to an
+ * empty String and marks the text field in an invalid state.
+ *
+ * @param textField the JFormattedTextField on which to install this
+ * formatter
+ */
+ public void install(JFormattedTextField textField)
+ {
+ // Uninstall the current textfield.
+ if (this.textField != null)
+ uninstall();
+
+ this.textField = textField;
+
+ // Install some state on the text field, including display text,
+ // DocumentFilter, NavigationFilter, and formatter specific Actions.
+ if (textField != null)
+ {
+ try
+ {
+ // Set the text of the field.
+ textField.setText(valueToString(textField.getValue()));
+ Document doc = textField.getDocument();
+
+ // Set the DocumentFilter for the field's Document.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).setDocumentFilter(getDocumentFilter());
+
+ // Set the NavigationFilter.
+ textField.setNavigationFilter(getNavigationFilter());
+
+ // Set the Formatter Actions
+ // FIXME: Have to add the actions from getActions()
+ }
+ catch (ParseException pe)
+ {
+ // Set the text to an empty String and mark the field as invalid.
+ textField.setText("");
+ setEditValid(false);
+ }
+ }
+ }
+
+ /**
+ * Clears the state installed on the JFormattedTextField by the formatter.
+ * This resets the DocumentFilter, NavigationFilter, and any additional
+ * Actions (returned by <code>getActions()</code>).
+ */
+ public void uninstall()
+ {
+ // Set the DocumentFilter for the field's Document.
+ Document doc = textField.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).setDocumentFilter(null);
+ textField.setNavigationFilter(null);
+ // FIXME: Have to remove the Actions from getActions()
+ this.textField = null;
+ }
+
+ /**
+ * Invoke this method when invalid values are entered. This forwards the
+ * call to the JFormattedTextField.
+ */
+ protected void invalidEdit()
+ {
+ textField.invalidEdit();
+ }
+
+ /**
+ * This method updates the <code>editValid</code> property of
+ * JFormattedTextField.
+ *
+ * @param valid the new state for the <code>editValid</code> property
+ */
+ protected void setEditValid(boolean valid)
+ {
+ textField.editValid = valid;
+ }
+
+ /**
+ * Parses <code>text</code> to return a corresponding Object.
+ *
+ * @param text the String to parse
+ * @return an Object that <code>text</code> represented
+ * @throws ParseException if there is an error in the conversion
+ */
+ public abstract Object stringToValue(String text)
+ throws ParseException;
+
+ /**
+ * Returns a String to be displayed, based on the Object
+ * <code>value</code>.
+ *
+ * @param value the Object from which to generate a String
+ * @return a String to be displayed
+ * @throws ParseException if there is an error in the conversion
+ */
+ public abstract String valueToString(Object value)
+ throws ParseException;
+ }
+
+ /**
+ * Delivers instances of an {@link AbstractFormatter} for
+ * a specific value type for a JFormattedTextField.
+ */
+ public abstract static class AbstractFormatterFactory
+ {
+ public AbstractFormatterFactory()
+ {
+ // Do nothing here.
+ }
+
+ public abstract AbstractFormatter getFormatter(JFormattedTextField tf);
+ }
+
+ /** The possible focusLostBehavior options **/
+ public static final int COMMIT = 0;
+ public static final int COMMIT_OR_REVERT = 1;
+ public static final int REVERT = 2;
+ public static final int PERSIST = 3;
+
+ /** The most recent valid and committed value **/
+ private Object value;
+
+ /** The behaviour for when this text field loses focus **/
+ private int focusLostBehavior = COMMIT_OR_REVERT;
+
+ /** The formatter factory currently being used **/
+ private AbstractFormatterFactory formatterFactory;
+
+ /** The formatter currently being used **/
+ private AbstractFormatter formatter;
+
+ // Package-private to avoid an accessor method.
+ boolean editValid = true;
+
+ /**
+ * Creates a JFormattedTextField with no formatter factory.
+ * <code>setValue</code> or <code>setFormatterFactory</code> will
+ * properly configure this text field to edit a particular type
+ * of value.
+ */
+ public JFormattedTextField()
+ {
+ this((AbstractFormatterFactory) null, null);
+ }
+
+ /**
+ * Creates a JFormattedTextField that can handle the specified Format.
+ * An appopriate AbstractFormatter and AbstractFormatterFactory will
+ * be created for the specified Format.
+ *
+ * @param format the Format that this JFormattedTextField should be able
+ * to handle
+ */
+ public JFormattedTextField(Format format)
+ {
+ this ();
+ setFormatterFactory(getAppropriateFormatterFactory(format));
+ }
+
+ /**
+ * Creates a JFormattedTextField with the specified formatter. This will
+ * create a {@link DefaultFormatterFactory} with this formatter as the default
+ * formatter.
+ *
+ * @param formatter the formatter to use for this JFormattedTextField
+ */
+ public JFormattedTextField(AbstractFormatter formatter)
+ {
+ this(new DefaultFormatterFactory(formatter));
+ }
+
+ /**
+ * Creates a JFormattedTextField with the specified formatter factory.
+ *
+ * @param factory the formatter factory to use for this JFormattedTextField
+ */
+ public JFormattedTextField(AbstractFormatterFactory factory)
+ {
+ setFormatterFactory(factory);
+ }
+
+ /**
+ * Creates a JFormattedTextField with the specified formatter factory and
+ * initial value.
+ *
+ * @param factory the initial formatter factory for this JFormattedTextField
+ * @param value the initial value for the text field
+ */
+ public JFormattedTextField(AbstractFormatterFactory factory, Object value)
+ {
+ setFormatterFactory(factory);
+ setValue(value);
+ }
+
+ /**
+ * Creates a JFormattedTextField with the specified value. This creates a
+ * formatter and formatterFactory that are appropriate for the value.
+ *
+ * @param value the initial value for this JFormattedTextField
+ */
+ public JFormattedTextField(Object value)
+ {
+ setValue(value);
+ }
+
+ /**
+ * Returns an AbstractFormatterFactory that will give an appropriate
+ * AbstractFormatter for the given Format.
+ * @param format the Format to match with an AbstractFormatter.
+ * @return a DefaultFormatterFactory whose defaultFormatter is appropriate
+ * for the given Format.
+ */
+ private AbstractFormatterFactory getAppropriateFormatterFactory(Format format)
+ {
+ AbstractFormatter newFormatter;
+ if (format instanceof DateFormat)
+ newFormatter = new DateFormatter((DateFormat) format);
+ else if (format instanceof NumberFormat)
+ newFormatter = new NumberFormatter ((NumberFormat) format);
+ else
+ newFormatter = new InternationalFormatter(format);
+
+ return new DefaultFormatterFactory(newFormatter);
+ }
+
+ /**
+ * Forces the current value from the editor to be set as the current
+ * value. If there is no current formatted this has no effect.
+ *
+ * @throws ParseException if the formatter cannot format the current value
+ */
+ public void commitEdit()
+ throws ParseException
+ {
+ if (formatter == null)
+ return;
+ // Note: this code is a lot like setValue except that we don't want
+ // to create a new formatter.
+ Object oldValue = this.value;
+
+ this.value = formatter.stringToValue(getText());
+ editValid = true;
+
+ firePropertyChange("value", oldValue, this.value);
+ }
+
+ /**
+ * Gets the command list supplied by the UI augmented by the specific
+ * Actions for JFormattedTextField.
+ *
+ * @return an array of Actions that this text field supports
+ */
+ public Action[] getActions()
+ {
+ // FIXME: Add JFormattedTextField specific actions
+ // These are related to committing or cancelling edits.
+ return super.getActions();
+ }
+
+ /**
+ * Returns the behaviour of this JFormattedTextField upon losing focus. This
+ * is one of <code>COMMIT</code>, <code>COMMIT_OR_REVERT</code>,
+ * <code>PERSIST</code>, or <code>REVERT</code>.
+ * @return the behaviour upon losing focus
+ */
+ public int getFocusLostBehavior()
+ {
+ return focusLostBehavior;
+ }
+
+ /**
+ * Returns the current formatter used for this JFormattedTextField.
+ * @return the current formatter used for this JFormattedTextField
+ */
+ public AbstractFormatter getFormatter()
+ {
+ return formatter;
+ }
+
+ /**
+ * Returns the factory currently used to generate formatters for this
+ * JFormattedTextField.
+ * @return the factory currently used to generate formatters
+ */
+ public AbstractFormatterFactory getFormatterFactory()
+ {
+ return formatterFactory;
+ }
+
+ public String getUIClassID()
+ {
+ return "FormattedTextFieldUI";
+ }
+
+ /**
+ * Returns the last valid value. This may not be the value currently shown
+ * in the text field depending on whether or not the formatter commits on
+ * valid edits and allows invalid input to be temporarily displayed.
+ * @return the last committed valid value
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This method is used to provide feedback to the user when an invalid value
+ * is input during editing.
+ */
+ protected void invalidEdit()
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(this);
+ }
+
+ /**
+ * Returns true if the current value being edited is valid. This property is
+ * managed by the current formatted.
+ * @return true if the value being edited is valid.
+ */
+ public boolean isEditValid()
+ {
+ return editValid;
+ }
+
+ /**
+ * Processes focus events. This is overridden because we may want to
+ * change the formatted depending on whether or not this field has
+ * focus.
+ *
+ * @param evt the FocusEvent
+ */
+ protected void processFocusEvent(FocusEvent evt)
+ {
+ super.processFocusEvent(evt);
+ // Let the formatterFactory change the formatter for this text field
+ // based on whether or not it has focus.
+ setFormatter (formatterFactory.getFormatter(this));
+ }
+
+ /**
+ * Associates this JFormattedTextField with a Document and propagates
+ * a PropertyChange event to each listener.
+ *
+ * @param newDocument the Document to associate with this text field
+ */
+ public void setDocument(Document newDocument)
+ {
+ // FIXME: This method should do more than this. Must do some handling
+ // of the DocumentListeners.
+ Document oldDocument = getDocument();
+
+ if (oldDocument == newDocument)
+ return;
+
+ super.setDocument(newDocument);
+ }
+
+ /**
+ * Sets the behaviour of this JFormattedTextField upon losing focus.
+ * This must be <code>COMMIT</code>, <code>COMMIT_OR_REVERT</code>,
+ * <code>PERSIST</code>, or <code>REVERT</code> or an
+ * IllegalArgumentException will be thrown.
+ *
+ * @param behavior
+ * @throws IllegalArgumentException if <code>behaviour</code> is not
+ * one of the above
+ */
+ public void setFocusLostBehavior(int behavior)
+ {
+ if (behavior != COMMIT
+ && behavior != COMMIT_OR_REVERT
+ && behavior != PERSIST
+ && behavior != REVERT)
+ throw new IllegalArgumentException("invalid behavior");
+
+ this.focusLostBehavior = behavior;
+ }
+
+ /**
+ * Sets the formatter for this JFormattedTextField. Normally the formatter
+ * factory will take care of this, or calls to setValue will also make sure
+ * that the formatter is set appropriately.
+ *
+ * @param formatter the AbstractFormatter to use for formatting the value for
+ * this JFormattedTextField
+ */
+ protected void setFormatter(AbstractFormatter formatter)
+ {
+ AbstractFormatter oldFormatter = null;
+
+ oldFormatter = this.formatter;
+
+ if (oldFormatter != null)
+ oldFormatter.uninstall();
+
+ this.formatter = formatter;
+
+ if (formatter != null)
+ formatter.install(this);
+
+ firePropertyChange("formatter", oldFormatter, formatter);
+ }
+
+ /**
+ * Sets the factory from which this JFormattedTextField should obtain
+ * its formatters.
+ *
+ * @param factory the AbstractFormatterFactory that will be used to generate
+ * formatters for this JFormattedTextField
+ */
+ public void setFormatterFactory(AbstractFormatterFactory factory)
+ {
+ if (formatterFactory == factory)
+ return;
+
+ AbstractFormatterFactory oldFactory = formatterFactory;
+ formatterFactory = factory;
+ firePropertyChange("formatterFactory", oldFactory, factory);
+
+ // Now set the formatter according to our new factory.
+ if (formatterFactory != null)
+ setFormatter(formatterFactory.getFormatter(this));
+ else
+ setFormatter(null);
+ }
+
+ /**
+ * Sets the value that will be formatted and displayed.
+ *
+ * @param newValue the value to be formatted and displayed
+ */
+ public void setValue(Object newValue)
+ {
+ if (value == newValue)
+ return;
+
+ Object oldValue = value;
+ value = newValue;
+
+ // If there is no formatterFactory then make one.
+ if (formatterFactory == null)
+ setFormatterFactory(createFormatterFactory(newValue));
+
+ // Set the formatter appropriately. This is because there may be a new
+ // formatterFactory from the line above, or we may want a new formatter
+ // depending on the type of newValue (or if newValue is null).
+ setFormatter (formatterFactory.getFormatter(this));
+ firePropertyChange("value", oldValue, newValue);
+ }
+
+ /**
+ * A helper method that attempts to create a formatter factory that is
+ * suitable to format objects of the type like <code>value</code>.
+ *
+ * @param value an object which should be formatted by the formatter factory.
+ *
+ * @return a formatter factory able to format objects of the class of
+ * <code>value</code>
+ */
+ AbstractFormatterFactory createFormatterFactory(Object value)
+ {
+ AbstractFormatter formatter = null;
+ if (value instanceof Date)
+ formatter = new DateFormatter();
+ else if (value instanceof Number)
+ formatter = new NumberFormatter();
+ else
+ formatter = new DefaultFormatter();
+ return new DefaultFormatterFactory(formatter);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JFrame.java b/libjava/classpath/javax/swing/JFrame.java
new file mode 100644
index 000000000..074d1c7d3
--- /dev/null
+++ b/libjava/classpath/javax/swing/JFrame.java
@@ -0,0 +1,410 @@
+/* JFrame.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.AWTEvent;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.LayoutManager;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+/**
+ * A window that supports window decorations (titlebar and borders).
+ * This is an extension of {@link java.awt.Frame} that provides support
+ * for the Swing architecture. Most importantly it contains a {@link JRootPane}
+ * as it's only top-level child, that manages the content pane, the menu and
+ * a glass pane.
+ *
+ * Also, unlike <code>java.awt.Frame</code>s, JFrames support the
+ * Swing Pluggable Look &amp; Feel architecture.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JFrame extends Frame
+ implements WindowConstants, RootPaneContainer, Accessible
+{
+ /**
+ * Provides accessibility support for <code>JFrame</code>s.
+ */
+ protected class AccessibleJFrame extends Frame.AccessibleAWTFrame
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJFrame</code>.
+ */
+ protected AccessibleJFrame()
+ {
+ super();
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A flag for {@link #setDefaultCloseOperation(int)}, indicating that the
+ * application should be exited, when this <code>JFrame</code> is closed.
+ * Note that in version 1.4, the equivalent constant has been added to
+ * {@link WindowConstants}.
+ *
+ * @since 1.3
+ */
+ public static final int EXIT_ON_CLOSE = 3;
+
+ private static final long serialVersionUID = -3362141868504252139L;
+ private static boolean defaultLookAndFeelDecorated;
+ private int closeAction = HIDE_ON_CLOSE;
+ protected AccessibleContext accessibleContext;
+ protected JRootPane rootPane;
+
+ /**
+ * @specnote rootPaneCheckingEnabled is false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ /**
+ * Creates a new frame with an empty string for the title.
+ */
+ public JFrame()
+ {
+ super("");
+ frameInit();
+ }
+
+ /**
+ * Creates a new <code>JFrame</code> with the specified title.
+ *
+ * @param title the frame title (<code>null</code> permitted).
+ */
+ public JFrame(String title)
+ {
+ super(title);
+ frameInit();
+ }
+
+ /**
+ * Creates a new JFrame in the specified {@link GraphicsConfiguration}
+ * and with an empty title.
+ *
+ * @param gc the <code>GraphicsConfiguration</code> that is used for
+ * the new <code>JFrame</code>
+ *
+ * @see Frame#Frame(GraphicsConfiguration)
+ */
+ public JFrame(GraphicsConfiguration gc)
+ {
+ super(gc);
+ frameInit();
+ }
+
+ /**
+ * Creates a new JFrame in the specified {@link GraphicsConfiguration}
+ * and with the specified title.
+ *
+ * @param title the title for the new <code>JFrame</code>
+ * @param gc the <code>GraphicsConfiguration</code> that is used for
+ * the new <code>JFrame</code>
+ *
+ * @see Frame#Frame(String, GraphicsConfiguration)
+ */
+ public JFrame(String title, GraphicsConfiguration gc)
+ {
+ super(title, gc);
+ frameInit();
+ }
+
+ protected void frameInit()
+ {
+ // We need to explicitly enable events here so that our processKeyEvent()
+ // and processWindowEvent() gets called.
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
+
+ super.setLayout(new BorderLayout());
+ setBackground(UIManager.getDefaults().getColor("control"));
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK);
+ getRootPane(); // will do set/create
+
+ // Setup the defaultLookAndFeelDecoration if requested.
+ if (isDefaultLookAndFeelDecorated()
+ && UIManager.getLookAndFeel().getSupportsWindowDecorations())
+ {
+ setUndecorated(true);
+ getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
+ }
+
+ // We're now done the init stage.
+ setRootPaneCheckingEnabled(true);
+ }
+
+ public Dimension getPreferredSize()
+ {
+ return super.getPreferredSize();
+ }
+
+ public JMenuBar getJMenuBar()
+ {
+ return getRootPane().getJMenuBar();
+ }
+
+ public void setJMenuBar(JMenuBar menubar)
+ {
+ getRootPane().setJMenuBar(menubar);
+ }
+
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ public void setLayeredPane(JLayeredPane layeredPane)
+ {
+ getRootPane().setLayeredPane(layeredPane);
+ }
+
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ public JRootPane getRootPane()
+ {
+ if (rootPane == null)
+ setRootPane(createRootPane());
+ return rootPane;
+ }
+
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ rootPane = root;
+ add(rootPane, BorderLayout.CENTER);
+ }
+
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ public void setContentPane(Container contentPane)
+ {
+ getRootPane().setContentPane(contentPane);
+ }
+
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ public void setGlassPane(Component glassPane)
+ {
+ getRootPane().setGlassPane(glassPane);
+ }
+
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're adding in the initialization stage use super.add.
+ // Otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled() && comp != rootPane)
+ getContentPane().add(comp,constraints,index);
+ else
+ super.addImpl(comp, constraints, index);
+ }
+
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead.
+ if (comp==rootPane)
+ super.remove(rootPane);
+ else
+ getContentPane().remove(comp);
+ }
+
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+ protected void processKeyEvent(KeyEvent e)
+ {
+ super.processKeyEvent(e);
+ }
+
+ public static void setDefaultLookAndFeelDecorated(boolean decorated)
+ {
+ defaultLookAndFeelDecorated = decorated;
+ }
+
+ public static boolean isDefaultLookAndFeelDecorated()
+ {
+ return defaultLookAndFeelDecorated;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JFrame</code>.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJFrame}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJFrame();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns a code for the default operation when the frame is closed. The
+ * default value is {@link WindowConstants#HIDE_ON_CLOSE}.
+ *
+ * @return One of: {@link WindowConstants#DO_NOTHING_ON_CLOSE},
+ * {@link WindowConstants#HIDE_ON_CLOSE},
+ * {@link WindowConstants#DISPOSE_ON_CLOSE}, {@link #EXIT_ON_CLOSE}.
+ *
+ * @see #setDefaultCloseOperation(int)
+ */
+ public int getDefaultCloseOperation()
+ {
+ return closeAction;
+ }
+
+ /**
+ * Returns a string describing the attributes for the <code>JFrame</code>,
+ * for use in debugging. The return value is guaranteed to be
+ * non-<code>null</code>, but the format may vary between implementations.
+ *
+ * @return A string describing the attributes of the <code>JFrame</code>.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",defaultCloseOperation=");
+ sb.append(SwingUtilities.convertWindowConstantToString(
+ getDefaultCloseOperation()));
+ sb.append(",rootPane=");
+ if (rootPane != null)
+ sb.append(rootPane);
+ sb.append(",rootPaneCheckingEnabled=").append(rootPaneCheckingEnabled);
+ return sb.toString();
+ }
+
+ protected void processWindowEvent(WindowEvent e)
+ {
+ super.processWindowEvent(e);
+ if (e.getID() == WindowEvent.WINDOW_CLOSING)
+ {
+ switch (closeAction)
+ {
+ case EXIT_ON_CLOSE:
+ System.exit(0);
+ break;
+ case DISPOSE_ON_CLOSE:
+ dispose();
+ break;
+ case HIDE_ON_CLOSE:
+ setVisible(false);
+ break;
+ case DO_NOTHING_ON_CLOSE:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Sets the default operation that is performed when this frame is closed.
+ * The default is <code>HIDE_ON_CLOSE</code>. When
+ * <code>EXIT_ON_CLOSE</code> is specified this method calls
+ * <code>SecurityManager.checkExit(0)</code> which might throw a
+ * <code>SecurityException</code>.
+ *
+ * @param operation a code for the operation (one of:
+ * {@link WindowConstants#DO_NOTHING_ON_CLOSE},
+ * {@link WindowConstants#HIDE_ON_CLOSE},
+ * {@link WindowConstants#DISPOSE_ON_CLOSE} and
+ * {@link WindowConstants#EXIT_ON_CLOSE}).
+ *
+ * @throws IllegalArgumentException if <code>operation</code> is not one of
+ * the specified codes.
+ *
+ * @see #getDefaultCloseOperation()
+ */
+ public void setDefaultCloseOperation(int operation)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null && operation == EXIT_ON_CLOSE)
+ sm.checkExit(0);
+
+ if (operation != EXIT_ON_CLOSE && operation != DISPOSE_ON_CLOSE
+ && operation != HIDE_ON_CLOSE && operation != DO_NOTHING_ON_CLOSE)
+ throw new IllegalArgumentException("operation must be EXIT_ON_CLOSE, "
+ + "HIDE_ON_CLOSE, DISPOSE_ON_CLOSE, or DO_NOTHING_ON_CLOSE");
+
+ closeAction = operation;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JInternalFrame.java b/libjava/classpath/javax/swing/JInternalFrame.java
new file mode 100644
index 000000000..511bc6ed4
--- /dev/null
+++ b/libjava/classpath/javax/swing/JInternalFrame.java
@@ -0,0 +1,1820 @@
+/* JInternalFrame.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Graphics;
+import java.awt.IllegalComponentStateException;
+import java.awt.KeyboardFocusManager;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyVetoException;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
+import javax.swing.plaf.DesktopIconUI;
+import javax.swing.plaf.InternalFrameUI;
+
+/**
+ * This class implements a Swing widget that looks and acts like a native
+ * frame. The frame can be dragged, resized, closed, etc. Typically,
+ * JInternalFrames are placed in JDesktopPanes. The actions that the
+ * JInternalFrame performs (maximizing, minimizing, etc.) are performed by a
+ * DesktopManager. As with regular frames, components are added by calling
+ * frame.getContentPane().add.
+ */
+public class JInternalFrame extends JComponent implements Accessible,
+ WindowConstants,
+ RootPaneContainer
+{
+
+ private static final long serialVersionUID = -5425177187760785402L;
+
+ /**
+ * Provides the accessibility features for the <code>JInternalFrame</code>
+ * component.
+ */
+ protected class AccessibleJInternalFrame extends AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = 5931936924175476797L;
+
+ /**
+ * Creates a new <code>AccessibleJInternalFrame</code> instance.
+ */
+ protected AccessibleJInternalFrame()
+ {
+ super();
+ }
+
+ /**
+ * Returns the frame title.
+ *
+ * @return The frame title.
+ */
+ public String getAccessibleName()
+ {
+ return getTitle();
+ }
+
+ /**
+ * Returns the accessible role for the <code>JInternalFrame</code>
+ * component.
+ *
+ * @return {@link AccessibleRole#INTERNAL_FRAME}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.INTERNAL_FRAME;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values for the {@link JInternalFrame}. Since this class
+ * implements {@link AccessibleValue}, it returns itself.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current layer for the {@link JInternalFrame} component,
+ * as an {@link Integer}.
+ *
+ * @return The layer for the {@link JInternalFrame} component.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getLayer());
+ }
+
+ /**
+ * Returns the maximum permitted accessible value.
+ *
+ * @return <code>Integer(Integer.MAX_VALUE)</code>.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the minimum permitted accessible value.
+ *
+ * @return <code>Integer(Integer.MIN_VALUE)</code>.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(Integer.MIN_VALUE);
+ }
+
+ /**
+ * Sets the layer for the internal frame.
+ *
+ * @param n the layer (see the constants defined in {@link JLayeredPane}).
+ *
+ * @return <code>true</code> if the value is set, and <code>false</code>
+ * if it was not set.
+ */
+ public boolean setCurrentAccessibleValue(Number n)
+ {
+ if (n == null)
+ return false;
+ setLayer(n.intValue());
+ return true;
+ }
+ }
+
+ /**
+ * This class represents the JInternalFrame while it is iconified.
+ */
+ public static class JDesktopIcon extends JComponent implements Accessible
+ {
+ /**
+ * Provides the accessibility features for the <code>JDesktopIcon</code>
+ * component.
+ */
+ protected class AccessibleJDesktopIcon extends AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = 5035560458941637802L;
+
+ /**
+ * Creates a new <code>AccessibleJDesktopIcon</code> instance.
+ */
+ protected AccessibleJDesktopIcon()
+ {
+ super();
+ }
+
+ /**
+ * Returns the accessible role for the <code>JDesktopIcon</code>
+ * component.
+ *
+ * @return {@link AccessibleRole#DESKTOP_ICON}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.DESKTOP_ICON;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values for the {@link JDesktopIcon}. Since this class
+ * implements {@link AccessibleValue}, it returns itself.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current layer for the {@link JInternalFrame} component
+ * represented by this <code>JDesktopIcon</code>, as an {@link Integer}.
+ *
+ * @return The layer.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(frame.getLayer());
+ }
+
+ /**
+ * Returns the maximum permitted accessible value.
+ *
+ * @return <code>Integer(Integer.MAX_VALUE)</code>.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the minimum permitted accessible value.
+ *
+ * @return <code>Integer(Integer.MIN_VALUE)</code>.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(Integer.MIN_VALUE);
+ }
+
+ /**
+ * Sets the layer for the internal frame represented by this
+ * <code>JDesktopIcon</code> component.
+ *
+ * @param n the layer (see the constants defined in
+ * {@link JLayeredPane}).
+ *
+ * @return <code>true</code> if the value is set, and <code>false</code>
+ * if it was not set.
+ */
+ public boolean setCurrentAccessibleValue(Number n)
+ {
+ if (n == null)
+ return false;
+ frame.setLayer(n.intValue());
+ return true;
+ }
+ }
+
+ private static final long serialVersionUID = 4672973344731387687L;
+
+ /** The JInternalFrame this DesktopIcon represents. */
+ JInternalFrame frame;
+
+ /**
+ * Creates a new JDesktopIcon object for representing the given frame.
+ *
+ * @param f The JInternalFrame to represent.
+ */
+ public JDesktopIcon(JInternalFrame f)
+ {
+ frame = f;
+ updateUI();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JDesktopIcon</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJDesktopIcon}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJDesktopIcon();
+ return accessibleContext;
+ }
+
+ /**
+ * This method returns the JDesktopPane this JDesktopIcon is in.
+ *
+ * @return The JDesktopPane this JDesktopIcon is in.
+ */
+ public JDesktopPane getDesktopPane()
+ {
+ JDesktopPane p = (JDesktopPane) SwingUtilities.getAncestorOfClass(JDesktopPane.class,
+ this);
+ return p;
+ }
+
+ /**
+ * This method returns the JInternalFrame this JDesktopIcon represents.
+ *
+ * @return The JInternalFrame this JDesktopIcon represents.
+ */
+ public JInternalFrame getInternalFrame()
+ {
+ return frame;
+ }
+
+ /**
+ * This method returns the UI that is responsible for the JDesktopIcon.
+ *
+ * @return The UI that is responsible for the JDesktopIcon.
+ */
+ public DesktopIconUI getUI()
+ {
+ return (DesktopIconUI) ui;
+ }
+
+ /**
+ * This method returns the String identifier that is used to determine
+ * which class is used for JDesktopIcon's UI.
+ *
+ * @return A String identifier for the UI class.
+ */
+ public String getUIClassID()
+ {
+ return "DesktopIconUI";
+ }
+
+ /**
+ * This method sets the JInternalFrame that this JDesktopIcon represents.
+ *
+ * @param f The JInternalFrame that this JDesktopIcon represents.
+ */
+ public void setInternalFrame(JInternalFrame f)
+ {
+ frame = f;
+ }
+
+ /**
+ * This method sets the UI used for this JDesktopIcon.
+ *
+ * @param ui The UI to use.
+ */
+ public void setUI(DesktopIconUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method restores the UI property to the defaults.
+ */
+ public void updateUI()
+ {
+ setUI((DesktopIconUI) UIManager.getUI(this));
+ }
+ }
+
+ /**
+ * The property fired in a PropertyChangeEvent when the contentPane property
+ * changes.
+ */
+ public static final String CONTENT_PANE_PROPERTY = "contentPane";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the frameIcon property
+ * changes.
+ */
+ public static final String FRAME_ICON_PROPERTY = "frameIcon";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the glassPane property
+ * changes.
+ */
+ public static final String GLASS_PANE_PROPERTY = "glassPane";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the closed property
+ * changes.
+ */
+ public static final String IS_CLOSED_PROPERTY = "closed";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the icon property
+ * changes.
+ */
+ public static final String IS_ICON_PROPERTY = "icon";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the maximum property
+ * changes.
+ */
+ public static final String IS_MAXIMUM_PROPERTY = "maximum";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the selected property
+ * changes.
+ */
+ public static final String IS_SELECTED_PROPERTY = "selected";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the layeredPane property
+ * changes.
+ */
+ public static final String LAYERED_PANE_PROPERTY = "layeredPane";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the jMenuBar property
+ * changes.
+ */
+ public static final String MENU_BAR_PROPERTY = "JMenuBar";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the rootPane property
+ * changes.
+ */
+ public static final String ROOT_PANE_PROPERTY = "rootPane";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the title property
+ * changes.
+ */
+ public static final String TITLE_PROPERTY = "title";
+
+ /** Whether the JInternalFrame is closable. */
+ protected boolean closable;
+
+ /** Whether the JInternalFrame can be iconified. */
+ protected boolean iconable;
+
+ /** Whether the JInternalFrame is closed. */
+ protected boolean isClosed;
+
+ /** Whether the JInternalFrame has been iconified. */
+ protected boolean isIcon;
+
+ /** Whether the JInternalFrame has been maximized. */
+ protected boolean isMaximum;
+
+ /** Whether the JInternalFrame is the active frame. */
+ protected boolean isSelected;
+
+ /** Whether the JInternalFrame can be maximized. */
+ protected boolean maximizable;
+
+ /**
+ * Whether the JInternalFrame has rootPaneChecking enabled.
+ *
+ * @specnote Should be false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ /** Whether the JInternalFrame is resizable. */
+ protected boolean resizable;
+
+ /**
+ * The JDesktopIcon that represents the JInternalFrame while it is
+ * iconified.
+ */
+ protected JDesktopIcon desktopIcon;
+
+ /** The icon used in the JMenuBar in the TitlePane. */
+ protected Icon frameIcon;
+
+ /** The rootPane of the JInternalFrame. */
+ protected JRootPane rootPane;
+
+ /** The title on the TitlePane of the JInternalFrame. */
+ protected String title;
+
+ /** The bounds of the JInternalFrame before it was maximized. */
+ private transient Rectangle storedBounds;
+
+ /** The Component that receives focus by default. */
+ private transient Component defaultFocus;
+
+ /** The default close action taken, */
+ private transient int defaultCloseOperation = DISPOSE_ON_CLOSE;
+
+ /** Whether the JInternalFrame has become visible for the very first time. */
+ private transient boolean isFirstTimeVisible = true;
+
+ /** DOCUMENT ME! */
+ private transient boolean wasIcon = false;
+
+ /**
+ * Creates a new JInternalFrame object that has an empty string for its
+ * title, and is non-resizable, non-maximizable, non-iconifiable, and
+ * non-closable.
+ */
+ public JInternalFrame()
+ {
+ this("", false, false, false, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title and is
+ * non-resizable, non-maximizable, non-iconifiable, and non-closable.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ */
+ public JInternalFrame(String title)
+ {
+ this(title, false, false, false, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title and resizable
+ * properties. The JInternalFrame is non-maximizable, non-iconifiable, and
+ * non-closable.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ * @param resizable Whether the JInternalFrame is resizable.
+ */
+ public JInternalFrame(String title, boolean resizable)
+ {
+ this(title, resizable, false, false, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title, resizable, and
+ * closable properties. The JInternalFrame is non-maximizable and
+ * non-iconifiable.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ * @param resizable Whether the JInternalFrame is resizable.
+ * @param closable Whether the JInternalFrame is closable.
+ */
+ public JInternalFrame(String title, boolean resizable, boolean closable)
+ {
+ this(title, resizable, closable, false, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title, resizable,
+ * closable and maximizable properties. The JInternalFrame is
+ * non-iconifiable.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ * @param resizable Whether the JInternalFrame is resizable.
+ * @param closable Whether the JInternalFrame is closable.
+ * @param maximizable Whether the JInternalFrame is maximizable.
+ */
+ public JInternalFrame(String title, boolean resizable, boolean closable,
+ boolean maximizable)
+ {
+ this(title, resizable, closable, maximizable, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title, resizable,
+ * closable, maximizable and iconifiable properties.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ * @param resizable Whether the JInternalFrame is resizable.
+ * @param closable Whether the JInternalFrame is closable.
+ * @param maximizable Whether the JInternalFrame is maximizable.
+ * @param iconifiable Whether the JInternalFrame is iconifiable.
+ */
+ public JInternalFrame(String title, boolean resizable, boolean closable,
+ boolean maximizable, boolean iconifiable)
+ {
+ this.title = title;
+ this.resizable = resizable;
+ this.closable = closable;
+ this.maximizable = maximizable;
+ this.iconable = iconifiable;
+ isMaximum = false;
+ setRootPane(createRootPane());
+ // JInternalFrames are invisible and opaque by default.
+ setVisible(false);
+ setOpaque(true);
+ desktopIcon = new JDesktopIcon(this);
+ updateUI();
+ setRootPaneCheckingEnabled(true); // Done the init stage, now adds go to content pane.
+ }
+
+ /**
+ * This method adds Components to this Container. For JInternalFrames,
+ * instead of calling add directly on the JInternalFrame, it should be
+ * called with JInternalFrame.getContentPane().add. If root pane checking
+ * is enabled, calling this method will cause an exception to be thrown.
+ *
+ * @param comp The Component to add.
+ * @param constraints The constraints on the Component added.
+ * @param index The position to place the Component.
+ *
+ * @throws Error DOCUMENT ME!
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're in the initialization stage use super.add. Here we add the
+ // rootPane as well as the title bar and other stuff.
+ // Otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().add(comp, constraints, index);
+ else
+ super.addImpl(comp,constraints, index);
+ }
+
+ /**
+ * This method adds an InternalFrameListener to this JInternalFrame.
+ *
+ * @param l The listener to add.
+ */
+ public void addInternalFrameListener(InternalFrameListener l)
+ {
+ listenerList.add(InternalFrameListener.class, l);
+ }
+
+ /**
+ * This method is used to create a root pane for the JInternalFrame. This
+ * method is called by the constructors.
+ *
+ * @return A root pane for the JInternalFrame to use.
+ */
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ /**
+ * This method makes this JInternalFrame invisible, unselected and closed.
+ * If this JInternalFrame is not closed already, it will fire an
+ * INTERNAL_FRAME_CLoSED event. This method is similar to setClosed but it
+ * doesn't give vetoable listeners a chance to veto and it will not fire an
+ * INTERNAL_FRAME_CLOSING event.
+ */
+ public void dispose()
+ {
+ if (isVisible())
+ setVisible(false);
+ if (isSelected())
+ {
+ try
+ {
+ setSelected(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing if they don't want to be unselected.
+ }
+ }
+ if (! isClosed)
+ {
+ firePropertyChange(IS_CLOSED_PROPERTY, Boolean.FALSE, Boolean.TRUE);
+ isClosed = true;
+ }
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED);
+ }
+
+ /**
+ * This method is used for closing this JInternalFrame. It fires an
+ * INTERNAL_FRAME_CLOSING event and then performs the action specified by
+ * the default close operation.
+ */
+ public void doDefaultCloseAction()
+ {
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSING);
+ switch (getDefaultCloseOperation())
+ {
+ case HIDE_ON_CLOSE:
+ setVisible(false);
+ break;
+ case DISPOSE_ON_CLOSE:
+ dispose();
+ break;
+ }
+ }
+
+ /**
+ * This method fires an InternalFrameEvent to the listeners.
+ *
+ * @param id The type of event being fired. See InternalFrameEvent.
+ */
+ protected void fireInternalFrameEvent(int id)
+ {
+ Object[] ifListeners = listenerList.getListenerList();
+ InternalFrameEvent evt = new InternalFrameEvent(this, id);
+ switch (id)
+ {
+ case InternalFrameEvent.INTERNAL_FRAME_CLOSING:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameClosing(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_ACTIVATED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameActivated(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_CLOSED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1]).internalFrameClosed(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_DEACTIVATED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameDeactivated(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_DEICONIFIED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameDeiconified(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_ICONIFIED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameIconified(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_OPENED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1]).internalFrameOpened(evt);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JInternalFrame</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJInternalFrame}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJInternalFrame();
+ return accessibleContext;
+ }
+
+ /**
+ * This method returns the Content Pane for this JInternalFrame.
+ *
+ * @return The Content Pane for this JInternalFrame.
+ */
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ /**
+ * Returns a code for the default action taken when this
+ * <code>JInternalFrame</code> is closed.
+ *
+ * @return The action code (usually one of
+ * {@link WindowConstants#DO_NOTHING_ON_CLOSE},
+ * {@link WindowConstants#HIDE_ON_CLOSE}, or
+ * {@link WindowConstants#DISPOSE_ON_CLOSE}).
+ *
+ * @see #setDefaultCloseOperation(int)
+ * @see #doDefaultCloseAction()
+ */
+ public int getDefaultCloseOperation()
+ {
+ return defaultCloseOperation;
+ }
+
+ /**
+ * Returns the <code>JDesktopIcon</code> that represents this
+ * <code>JInternalFrame</code> while it is iconified.
+ *
+ * @return The desktop icon component.
+ */
+ public JDesktopIcon getDesktopIcon()
+ {
+ return desktopIcon;
+ }
+
+ /**
+ * This method searches this JInternalFrame ancestors for an instance of
+ * JDesktopPane. If one is found, it is returned. If none is found, then it
+ * will search the JDesktopIcon for a JDesktopPane.
+ *
+ * @return The JDesktopPane that this JInternalFrame belongs to.
+ */
+ public JDesktopPane getDesktopPane()
+ {
+ JDesktopPane value = (JDesktopPane) SwingUtilities.getAncestorOfClass(JDesktopPane.class,
+ this);
+ if (value == null && desktopIcon != null)
+ value = desktopIcon.getDesktopPane();
+ return value;
+ }
+
+ /**
+ * This method returns null because this must always be the root of a focus
+ * traversal.
+ *
+ * @return always null
+ *
+ * @since 1.4
+ */
+ public final Container getFocusCycleRootAncestor()
+ {
+ // as defined.
+ return null;
+ }
+
+ /**
+ * This method returns the child Component that will receive focus if this
+ * JInternalFrame is selected.
+ *
+ * @return The child Component that will receive focus.
+ */
+ public Component getFocusOwner()
+ {
+ if (isSelected())
+ {
+ Component focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
+ if (SwingUtilities.isDescendingFrom(focus, this))
+ {
+ defaultFocus = focus;
+ return focus;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This method returns the Frame Icon (the icon used in the JInternalFrame
+ * TitlePane and iconified frame).
+ *
+ * @return The Frame Icon.
+ */
+ public Icon getFrameIcon()
+ {
+ return frameIcon;
+ }
+
+ /**
+ * This method returns the Glass Pane used with this JInternalFrame.
+ *
+ * @return The Glass Pane used with this JInternalFrame.
+ */
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ /**
+ * This method returns an array of InternalFrameListeners that are listening
+ * to this JInternalFrame.
+ *
+ * @return An array of InternalFrameListeners that are listening to this
+ * JInternalFrame.
+ */
+ public InternalFrameListener[] getInternalFrameListeners()
+ {
+ return (InternalFrameListener[]) listenerList.getListeners(InternalFrameListener.class);
+ }
+
+ /**
+ * This method returns the JMenuBar for this JInternalFrame.
+ *
+ * @return The JMenuBar for this JInternalFrame.
+ */
+ public JMenuBar getJMenuBar()
+ {
+ return getRootPane().getJMenuBar();
+ }
+
+ /**
+ * This method returns the layer that this JInternalFrame resides in.
+ *
+ * @return The layer that this JInternalFrame resides in.
+ */
+ public int getLayer()
+ {
+ return JLayeredPane.getLayer(this);
+ }
+
+ /**
+ * This method returns the LayeredPane for this JInternalFrame.
+ *
+ * @return The LayeredPane for this JInternalFrame.
+ */
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ /**
+ * This method is deprecated. This method returns the JMenuBar for this
+ * JInternalFrame.
+ *
+ * @return The JMenuBar for this JInternalFrame.
+ *
+ * @deprecated 1.0.3
+ */
+ public JMenuBar getMenuBar()
+ {
+ return getJMenuBar();
+ }
+
+ /**
+ * This method returns the child Component that will receive focus when the
+ * JInternalFrame is selected. If the JInternalFrame is selected, this
+ * method returns getFocusOwner(). Otherwise, it will return the child
+ * Component that most recently requested focus. If that is null, then the
+ * initial focus Component is returned. If that is null, then the default
+ * focus component is returned.
+ *
+ * @return The most recent focus owner.
+ */
+ public Component getMostRecentFocusOwner()
+ {
+ if (isSelected())
+ return getFocusOwner();
+ else
+ return defaultFocus;
+ }
+
+ /**
+ * This method returns the bounds of the JInternalFrame if it is not
+ * maximized. If it is maximized, it returns the bounds of the
+ * JInternalFrame before it was maximized (the bounds that it will be
+ * restored to).
+ *
+ * @return A Rectangle that contains this JInternalFrame's normal bounds (or
+ * just its bounds if it is not maximized).
+ */
+ public Rectangle getNormalBounds()
+ {
+ if (storedBounds == null)
+ return getBounds();
+ else
+ return storedBounds;
+ }
+
+ /**
+ * This method returns the Root Pane for this JInternalFrame.
+ *
+ * @return The Root Pane for this JInternalFrame.
+ */
+ public JRootPane getRootPane()
+ {
+ return rootPane;
+ }
+
+ /**
+ * Returns the frame's title.
+ *
+ * @return The frame's title (can be <code>null</code>).
+ *
+ * @see #setTitle(String)
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ /**
+ * This method returns the UI used to represent the JInternalFrame.
+ *
+ * @return The UI used to represent the JInternalFrame.
+ */
+ public InternalFrameUI getUI()
+ {
+ return (InternalFrameUI) ui;
+ }
+
+ /**
+ * This method returns a String identifier that is used to determine which
+ * class acts as the JInternalFrame's UI.
+ *
+ * @return A String identifier to determine a UI class.
+ */
+ public String getUIClassID()
+ {
+ return "InternalFrameUI";
+ }
+
+ /**
+ * This method returns null.
+ *
+ * @return null.
+ */
+ public final String getWarningString()
+ {
+ // as defined.
+ return null;
+ }
+
+ /**
+ * This method deselects this JInternalFrame and hides it.
+ */
+ public void hide()
+ {
+ if (isIcon())
+ getDesktopIcon().hide();
+ super.hide();
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is closable.
+ *
+ * @return Whether this JInternalFrame is closable.
+ */
+ public boolean isClosable()
+ {
+ return closable;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame has been closed.
+ *
+ * @return Whether this JInternalFrame is closed.
+ */
+ public boolean isClosed()
+ {
+ return isClosed;
+ }
+
+ /**
+ * This must always return true.
+ *
+ * @return always true
+ *
+ * @since 1.4
+ */
+ public final boolean isFocusCycleRoot()
+ {
+ return true;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is currently iconified.
+ *
+ * @return Whether this JInternalFrame is currently iconified.
+ */
+ public boolean isIcon()
+ {
+ return isIcon;
+ }
+
+ /**
+ * This method returns whether the JInternalFrame can be iconified.
+ *
+ * @return Whether the JInternalFrame can be iconified.
+ */
+ public boolean isIconifiable()
+ {
+ return iconable;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame can be maximized.
+ *
+ * @return Whether this JInternalFrame can be maximized.
+ */
+ public boolean isMaximizable()
+ {
+ return maximizable;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is currently maximized.
+ *
+ * @return Whether this JInternalFrame is maximized.
+ */
+ public boolean isMaximum()
+ {
+ return isMaximum;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is resizable.
+ *
+ * @return Whether this JInternalFrame is resizable.
+ */
+ public boolean isResizable()
+ {
+ return resizable;
+ }
+
+ /**
+ * This method returns whether root pane checking is enabled. If root pane
+ * checking is enabled, then calls to addImpl and setLayout will throw
+ * exceptions.
+ *
+ * @return Whether root pane checking is enabled.
+ */
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is selected.
+ *
+ * @return Whether this JInternalFrame is selected.
+ */
+ public boolean isSelected()
+ {
+ return isSelected;
+ }
+
+ /**
+ * A helper method that moves this JInternalFrame to the back if the parent
+ * is a JLayeredPane.
+ */
+ public void moveToBack()
+ {
+ Container p = getParent();
+ if (p instanceof JLayeredPane)
+ ((JLayeredPane) p).moveToBack(this);
+ }
+
+ /**
+ * A helper method that moves this JInternalFrame to the front if the parent
+ * is a JLayeredPane.
+ */
+ public void moveToFront()
+ {
+ Container p = getParent();
+ if (p != null && p instanceof JLayeredPane)
+ ((JLayeredPane) p).moveToFront(this);
+ }
+
+ /**
+ * This method causes the children of this JInternalFrame to be laid out.
+ * Before it begins, if this JInternalFrame is an icon, then it will be
+ * deiconified. If it is maximized, then it will be restored. If either
+ * operation fails, then this method will return.
+ */
+ public void pack()
+ {
+ try
+ {
+ if (isIcon())
+ setIcon(false);
+ else if (isMaximum())
+ setMaximum(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing if they don't want to be restored first.
+ }
+ setSize(getPreferredSize());
+ validate();
+ }
+
+ /**
+ * This method is overridden to allow for speedier painting while this
+ * JInternalFramme is being dragged.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+ }
+
+ /**
+ * An implementation dependent string describing the current state of this
+ * <code>JInternalFrame</code> instance.
+ *
+ * @return A string describing the current state of this
+ * <code>JInternalFrame</code> instance.
+ */
+ protected String paramString()
+ {
+ return super.paramString() + ",title=" + getTitle();
+ }
+
+ /**
+ * This method removes the given Component from the Container.
+ *
+ * @param comp The Component to remove.
+ */
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead.
+ if (comp==rootPane || ! isRootPaneCheckingEnabled())
+ super.remove(comp);
+ else
+ getContentPane().remove(comp);
+ }
+
+ /**
+ * This method removes an InternalFrameListener from this JInternalFrame.
+ *
+ * @param l The listener to remove.
+ */
+ public void removeInternalFrameListener(InternalFrameListener l)
+ {
+ listenerList.remove(InternalFrameListener.class, l);
+ }
+
+ /**
+ * This method resizes and positions this JInternalFrame. It also forces a
+ * relayout of the Container.
+ *
+ * @param x The x position of this JInternalFrame.
+ * @param y The y position of this JInternalFrame.
+ * @param width The width of this JInternalFrame.
+ * @param height The height of this JInternalFrame.
+ */
+ public void reshape(int x, int y, int width, int height)
+ {
+ super.reshape(x, y, width, height);
+ revalidate();
+ }
+
+ /**
+ * This method gives focus to the last child Component that had focus. This
+ * is used by the UI when this JInternalFrame is activated.
+ */
+ public void restoreSubcomponentFocus()
+ {
+ Component c = getMostRecentFocusOwner();
+ if (c != null)
+ c.requestFocus();
+ }
+
+ /**
+ * This method sets whether this JInternalFrame can be closed.
+ *
+ * @param b Whether this JInternalFrame can be closed.
+ */
+ public void setClosable(boolean b)
+ {
+ if (closable != b)
+ {
+ closable = b;
+ firePropertyChange("closable", ! closable, closable);
+ }
+ }
+
+ /**
+ * This method closes the JInternalFrame if the given boolean is true. If it
+ * is false, then the result of this method is unspecified. If the
+ * JInternalFrame is closed, this method does nothing. This method will
+ * first fire an INTERNAL_FRAME_CLOSING event and give a chance for veto
+ * listeners to cancel the close. If no listener vetoes the change, the
+ * closed property is set to true and the JInternalFrame is hidden and
+ * unselected. The method will finish by firing an INTERNAL_FRAME_CLOSED
+ * event.
+ *
+ * @param b Whether the JInternalFrame will be closed.
+ *
+ * @throws PropertyVetoException If a VetoableChangeListener vetoes the change.
+ */
+ public void setClosed(boolean b) throws PropertyVetoException
+ {
+ if (b && ! isClosed())
+ {
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSING);
+ fireVetoableChange(IS_CLOSED_PROPERTY, false, true);
+
+ isClosed = b;
+ dispose();
+
+ firePropertyChange(IS_CLOSED_PROPERTY, false, true);
+ }
+ }
+
+ /**
+ * This method sets the Container to be used as a Content Pane for this
+ * JInternalFrame.
+ *
+ * @param c The Container to use as a Content Pane.
+ */
+ public void setContentPane(Container c)
+ {
+ if (c != getContentPane())
+ {
+ Container old = getContentPane();
+ getRootPane().setContentPane(c);
+ firePropertyChange(CONTENT_PANE_PROPERTY, old, c);
+ }
+ }
+
+ /**
+ * Sets a code for the action to be taken when this
+ * <code>JInternalFrame</code> is closed. Note that no validation is
+ * performed on the <code>operation</code> code, any integer will be
+ * accepted (nevertheless, you should pass in one of the listed values).
+ *
+ * @param operation one of {@link WindowConstants#DO_NOTHING_ON_CLOSE},
+ * {@link WindowConstants#HIDE_ON_CLOSE} or
+ * {@link WindowConstants#DISPOSE_ON_CLOSE}.
+ *
+ * @see #getDefaultCloseOperation()
+ * @see #doDefaultCloseAction()
+ */
+ public void setDefaultCloseOperation(int operation)
+ {
+ /* Reference implementation allows invalid operations to be specified.
+ In that case, behaviour defaults to DO_NOTHING_ON_CLOSE.
+ processWindowEvent handles the behaviour. getDefaultCloseOperation
+ must return the invalid operator code. */
+ defaultCloseOperation = operation;
+ }
+
+ /**
+ * Sets the <code>JDesktopIcon</code> instance that represents this
+ * <code>JInternalFrame</code> while it is iconified and, if the new icon is
+ * not the same instance as the existing icon, sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * <code>"desktopIcon"</code>) to all registered listeners..
+ *
+ * @param d the icon.
+ *
+ * @see #getDesktopIcon()
+ */
+ public void setDesktopIcon(JDesktopIcon d)
+ {
+ if (desktopIcon != d)
+ {
+ JDesktopIcon oldIcon = desktopIcon;
+ desktopIcon = d;
+ firePropertyChange("desktopIcon", oldIcon, d);
+ }
+ }
+
+ /**
+ * This method does nothing because this must be the root of a focus
+ * traversal cycle.
+ *
+ * @param focusCycleRoot Not used.
+ */
+ public final void setFocusCycleRoot(boolean focusCycleRoot)
+ {
+ // Do nothing
+ }
+
+ /**
+ * This method sets the Icon to be used in two places. The first is icon
+ * that is painted at the top left corner of the JInternalFrame when it is
+ * not iconified (clicking on that icon will activate the TitlePane
+ * JMenuBar). When the JInternalFrame is iconified, it will be the icon
+ * displayed in the JDesktopIcon. If no icon is set, the JInternalFrame
+ * will use a Look and Feel default.
+ *
+ * @param icon The Icon used in the TitlePane JMenuBar and iconified frames.
+ */
+ public void setFrameIcon(Icon icon)
+ {
+ if (icon != frameIcon)
+ {
+ Icon old = frameIcon;
+ frameIcon = icon;
+ firePropertyChange(FRAME_ICON_PROPERTY, old, frameIcon);
+ }
+ }
+
+ /**
+ * This method sets the Glass Pane used with this JInternalFrame.
+ *
+ * @param glass The Glass Pane to use with this JInternalFrame.
+ */
+ public void setGlassPane(Component glass)
+ {
+ if (glass != getGlassPane())
+ {
+ Component old = getGlassPane();
+ getRootPane().setGlassPane(glass);
+ firePropertyChange(GLASS_PANE_PROPERTY, old, glass);
+ }
+ }
+
+ /**
+ * This method iconifies or deiconifies this JInternalFrame given the
+ * boolean argument. If the JInternalFrame becomes iconified, it will fire
+ * an INTERNAL_FRAME_ICONIFIED event. If the JInternalFrame becomes
+ * deiconified, it will fire anINTERNAL_FRAME_DEICONIFIED event.
+ *
+ * @param b Whether this JInternalFrame is to be iconified or deiconified.
+ *
+ * @throws PropertyVetoException DOCUMENT ME!
+ */
+ public void setIcon(boolean b) throws PropertyVetoException
+ {
+ if (b != isIcon())
+ {
+ fireVetoableChange(IS_ICON_PROPERTY, b, isIcon);
+
+ isIcon = b;
+
+ firePropertyChange(IS_ICON_PROPERTY, ! isIcon, isIcon);
+ if (b)
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_ICONIFIED);
+ else
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_DEICONIFIED);
+ }
+ }
+
+ /**
+ * This method sets whether the JInternalFrame can be iconified. (This means
+ * that the JInternalFrame can be turned into an icon if minimized).
+ *
+ * @param b Whether the JInternalFrame can be iconified.
+ */
+ public void setIconifiable(boolean b)
+ {
+ if (iconable != b)
+ {
+ iconable = b;
+ firePropertyChange("iconable", ! iconable, iconable);
+ }
+ }
+
+ /**
+ * This method sets the JMenuBar to be used with this JInternalFrame.
+ *
+ * @param b The JMenuBar to be used with this JInternalFrame.
+ */
+ public void setJMenuBar(JMenuBar b)
+ {
+ JMenuBar old = getJMenuBar();
+ getRootPane().setJMenuBar(b);
+ firePropertyChange(MENU_BAR_PROPERTY, old, b);
+ }
+
+ /**
+ * A helper method that set the layer that this JInternalFrame resides in.
+ * Using this version of the method means that the user should not set it
+ * to values that are already defined in JLayeredPane. If predefined values
+ * are to be used, the user should use the setLayer(Integer) version.
+ *
+ * @param layer The layer to place this JInternalFrame in.
+ */
+ public void setLayer(int layer)
+ {
+ setLayer(new Integer(layer));
+ }
+
+ /**
+ * A helper method that sets the layer that this JInternalFrame resides in.
+ * Calling this version of the method should use layer values that are
+ * already defined in JLayeredPane.
+ *
+ * @param layer The layer to place this JInternalFrame in.
+ */
+ public void setLayer(Integer layer)
+ {
+ Container p = getParent();
+ if (p instanceof JLayeredPane)
+ {
+ JLayeredPane lp = (JLayeredPane) p;
+ lp.setLayer(this, layer.intValue(), lp.getPosition(this));
+ }
+ else
+ {
+ JLayeredPane.putLayer(this, layer.intValue());
+ if (p != null)
+ p.repaint(getX(), getY(), getWidth(), getHeight());
+ }
+ }
+
+ /**
+ * This method sets the JLayeredPane to use with this JInternalFrame.
+ *
+ * @param layered The JLayeredPane to use as a layeredPane.
+ */
+ public void setLayeredPane(JLayeredPane layered)
+ {
+ if (layered == null)
+ throw new IllegalComponentStateException("LayeredPane must not be null");
+
+ if (layered != getLayeredPane())
+ {
+ JLayeredPane old = getLayeredPane();
+ getRootPane().setLayeredPane(layered);
+ firePropertyChange(LAYERED_PANE_PROPERTY, old, layered);
+ }
+ }
+
+ /**
+ * This method sets whether the JInternalFrame can be maximized.
+ *
+ * @param b Whether this JInternalFrame can be maximized.
+ */
+ public void setMaximizable(boolean b)
+ {
+ if (maximizable != b)
+ {
+ maximizable = b;
+ firePropertyChange("maximizable", ! maximizable, maximizable);
+ }
+ }
+
+ /**
+ * This method sets the Layout Manager used in the JInternalFrame. SetLayout
+ * should not be called on the JInternalFrame directly. Instead, it should
+ * be called with JInternalFrame.getContentPane().setLayout. Calls to this
+ * method with root pane checking enabled will cause exceptions to be
+ * thrown.
+ *
+ * @param manager The Layout Manager to be used with the JInternalFrame.
+ *
+ * @throws Error If rootPaneChecking is enabled.
+ */
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ /**
+ * This method sets the JInternalFrame to maximized (if the given argument
+ * is true) or restores the JInternalFrame to its normal bounds otherwise.
+ *
+ * @param b Whether this JInteralFrame will be maximized or restored.
+ *
+ * @throws PropertyVetoException If a VetoableChangeListener vetoes the change.
+ */
+ public void setMaximum(boolean b) throws PropertyVetoException
+ {
+ if (b != isMaximum)
+ {
+ fireVetoableChange(IS_MAXIMUM_PROPERTY, isMaximum, b);
+ isMaximum = b;
+ firePropertyChange(IS_MAXIMUM_PROPERTY, ! isMaximum, isMaximum);
+ }
+ }
+
+ /**
+ * This method is deprecated. This method sets the JMenuBar used with this
+ * JInternalFrame.
+ *
+ * @param m The JMenuBar to use with this JInternalFrame.
+ *
+ * @deprecated 1.0.3
+ */
+ public void setMenuBar(JMenuBar m)
+ {
+ setJMenuBar(m);
+ }
+
+ /**
+ * This method sets the bounds that this JInternalFrame will be restored to.
+ *
+ * @param r The bounds that this JInternalFrame will be restored to.
+ */
+ public void setNormalBounds(Rectangle r)
+ {
+ storedBounds = r;
+ }
+
+ /**
+ * This method sets whether the JInternalFrame can be resized by a user
+ * action (like dragging at the frame borders).
+ *
+ * @param b Whether this JInternalFramer can be resized.
+ */
+ public void setResizable(boolean b)
+ {
+ if (b != resizable)
+ {
+ resizable = b;
+ firePropertyChange("resizable", ! resizable, resizable);
+ }
+ }
+
+ /**
+ * This method sets the Root Pane for this JInternalFrame.
+ *
+ * @param root The Root Pane for this JInternalFrame.
+ */
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ JRootPane old = rootPane;
+ rootPane = root;
+
+ if (rootPane != null)
+ {
+ boolean checkingEnabled = isRootPaneCheckingEnabled();
+ try
+ {
+ setRootPaneCheckingEnabled(false);
+ add(rootPane, BorderLayout.CENTER);
+ }
+ finally
+ {
+ setRootPaneCheckingEnabled(checkingEnabled);
+ }
+ }
+ firePropertyChange(ROOT_PANE_PROPERTY, old, rootPane);
+ }
+
+ /**
+ * This method sets whether root pane checking is enabled. If root pane
+ * checking is enabled, then calls to addImpl and setLayout will throw
+ * exceptions.
+ *
+ * @param enabled Whether root pane checking is enabled.
+ */
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ /**
+ * This method sets whether this JInternalFrame is the selected frame in the
+ * JDesktopPane (or other container). When selected, a JInternalFrame will
+ * have focus and paint its TitlePane differently (usually a different
+ * colour). If this method selects the frame, this JInternalFrame will fire
+ * an INTERNAL_FRAME_ACTIVATED event. If it deselects this frame, it will
+ * fire an INTERNAL_FRAME_DEACTIVATED event.
+ *
+ * @param selected Whether this JInternalFrame will become selected or
+ * deselected.
+ *
+ * @throws PropertyVetoException If a VetoableChangeListener vetoes the change.
+ */
+ public void setSelected(boolean selected) throws PropertyVetoException
+ {
+ if (selected != isSelected
+ && (! selected || (isIcon ? desktopIcon.isShowing() : isShowing())))
+ {
+ fireVetoableChange(IS_SELECTED_PROPERTY, isSelected, selected);
+
+ if (! selected)
+ defaultFocus = getMostRecentFocusOwner();
+
+ isSelected = selected;
+ firePropertyChange(IS_SELECTED_PROPERTY, ! isSelected, isSelected);
+
+ if (isSelected)
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_ACTIVATED);
+ else
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_DEACTIVATED);
+
+ if (selected)
+ restoreSubcomponentFocus();
+
+ repaint();
+ }
+ }
+
+ /**
+ * Sets the title for the <code>JInternalFrame</code> and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #TITLE_PROPERTY}) to all registered listeners.
+ *
+ * @param title the new title (<code>null</code> permitted).
+ *
+ * @see #getTitle()
+ */
+ public void setTitle(String title)
+ {
+ String old = this.title;
+ this.title = title;
+ firePropertyChange(TITLE_PROPERTY, old, this.title);
+ }
+
+ /**
+ * This method displays the JInternalFrame. If it is not visible, this
+ * method will bring this JInternalFrame to the front, make it visible and
+ * select it. If this is the first time this JInternalFrame is made
+ * visible, an INTERNAL_FRAME_OPENED event will be fired.
+ */
+ public void show()
+ {
+ if (! isVisible())
+ {
+ if (isFirstTimeVisible)
+ {
+ isFirstTimeVisible = false;
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_OPENED);
+ }
+
+ getDesktopIcon().setVisible(true);
+
+ toFront();
+ super.show();
+
+ if (isIcon())
+ return;
+
+ if (! isSelected())
+ {
+ try
+ {
+ setSelected(true);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing. if they don't want to be selected.
+ }
+ }
+ }
+ }
+
+ /**
+ * This method is used to set the UI responsible for the JInternalFrame.
+ *
+ * @param ui The UI responsible for the JInternalFrame.
+ */
+ public void setUI(InternalFrameUI ui)
+ {
+ // We must temporarily go into init mode so that the UI can directly
+ // manipulate the JInternalFrame.
+ boolean old = isRootPaneCheckingEnabled();
+ setRootPaneCheckingEnabled(false);
+ super.setUI(ui);
+ setRootPaneCheckingEnabled(old);
+ }
+
+ /**
+ * This method causes the JInternalFrame to be brough to back in the
+ * z-order.
+ */
+ public void toBack()
+ {
+ moveToBack();
+ }
+
+ /**
+ * This method causes the JInternalFrame to be brought to front in the
+ * z-order.
+ */
+ public void toFront()
+ {
+ moveToFront();
+ }
+
+ /**
+ * This method resets the UI to the Look and Feel defaults.
+ */
+ public void updateUI()
+ {
+ // We must go into the init stage when updating the UI, so the UI can
+ // set layout and components directly on the internal frame, not its
+ // content pane.
+ boolean old = isRootPaneCheckingEnabled();
+ setRootPaneCheckingEnabled(false);
+ setUI((InternalFrameUI) UIManager.getUI(this));
+ setRootPaneCheckingEnabled(old);
+ }
+
+ /**
+ * This helper method allows JInternalFrames to signal that they were
+ * iconned for the first time.
+ *
+ * @param b Whether the JInternalFrame was iconned.
+ * @param ID The identifier of the property change event to fire if the
+ * JInternalFrame is iconned for the first time.
+ */
+ void setWasIcon(boolean b, String ID)
+ {
+ if (b && ! wasIcon)
+ {
+ wasIcon = b;
+ firePropertyChange(ID, ! b, b);
+ }
+ }
+
+ /**
+ * This helper method returns whether the JInternalFrame has been iconned
+ * once already.
+ *
+ * @return Whether the JInternalFrame has been iconned once already.
+ */
+ boolean getWasIcon()
+ {
+ return wasIcon;
+ }
+
+ /**
+ * This method is a convenience method to fire vetoable property changes.
+ *
+ * @param name The identifier of the property change.
+ * @param oldValue The old value.
+ * @param newValue The new value.
+ *
+ * @throws PropertyVetoException Fired if a vetoable change listener vetoes
+ * the change.
+ */
+ private void fireVetoableChange(String name, boolean oldValue,
+ boolean newValue)
+ throws PropertyVetoException
+ {
+ super.fireVetoableChange(name, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
+ }
+}
diff --git a/libjava/classpath/javax/swing/JLabel.java b/libjava/classpath/javax/swing/JLabel.java
new file mode 100644
index 000000000..72354c56e
--- /dev/null
+++ b/libjava/classpath/javax/swing/JLabel.java
@@ -0,0 +1,1122 @@
+/* JLabel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleExtendedComponent;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleText;
+import javax.swing.plaf.LabelUI;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Position;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.View;
+
+/**
+ * A component that displays a static text message and/or an icon.
+ */
+public class JLabel extends JComponent implements Accessible, SwingConstants
+{
+
+ /**
+ * Provides the accessibility features for the <code>JLabel</code>
+ * component.
+ */
+ protected class AccessibleJLabel
+ extends JComponent.AccessibleJComponent
+ implements AccessibleText, AccessibleExtendedComponent
+ {
+
+ /**
+ * Returns the accessible name.
+ *
+ * @return The accessible name.
+ */
+ public String getAccessibleName()
+ {
+ if (accessibleName != null)
+ return accessibleName;
+ if (text != null)
+ return text;
+ else
+ return super.getAccessibleName();
+ }
+
+ /**
+ * Returns the accessible role for the <code>JLabel</code> component.
+ *
+ * @return {@link AccessibleRole#LABEL}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.LABEL;
+ }
+
+ /**
+ * Returns the selected text. This is null since JLabels
+ * are not selectable.
+ *
+ * @return <code>null</code> because JLabels cannot have selected text
+ */
+ public String getSelectedText()
+ {
+ // We return null here since JLabel's text is not selectable.
+ return null;
+ }
+
+ /**
+ * Returns the start index of the selected text.
+ *
+ * @return the start index of the selected text
+ */
+ public int getSelectionStart()
+ {
+ // JLabel don't have selected text, so we return -1 here.
+ return -1;
+ }
+
+ /**
+ * Returns the end index of the selected text.
+ *
+ * @return the end index of the selected text
+ */
+ public int getSelectionEnd()
+ {
+ // JLabel don't have selected text, so we return -1 here.
+ return -1;
+ }
+
+ /**
+ * Returns an {@link AttributeSet} that reflects the text attributes of
+ * the specified character. We return an empty
+ * <code>AttributeSet</code> here, because JLabels don't support text
+ * attributes (at least not yet).
+ *
+ * @param index the index of the character
+ *
+ * @return an {@link AttributeSet} that reflects the text attributes of
+ * the specified character
+ */
+ public AttributeSet getCharacterAttribute(int index)
+ {
+ // FIXME: Return null here for simple labels, and query the HTML
+ // view for HTML labels.
+ return new SimpleAttributeSet();
+ }
+
+ /**
+ * Returns the character, word or sentence at the specified index. The
+ * <code>part</code> parameter determines what is returned, the character,
+ * word or sentence after the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence after <code>index</code>
+ */
+ public String getAtIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index));
+ break;
+ case AccessibleText.WORD:
+ startIndex = text.lastIndexOf(' ', index);
+ endIndex = text.indexOf(' ', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ startIndex = text.lastIndexOf('.', index);
+ endIndex = text.indexOf('.', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the character, word or sentence after the specified index. The
+ * <code>part</code> parameter determines what is returned, the character,
+ * word or sentence after the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence after <code>index</code>
+ */
+ public String getAfterIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index + 1));
+ break;
+ case AccessibleText.WORD:
+ startIndex = text.indexOf(' ', index);
+ endIndex = text.indexOf(' ', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ startIndex = text.indexOf('.', index);
+ endIndex = text.indexOf('.', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the character, word or sentence before the specified index. The
+ * <code>part</code> parameter determines what is returned, the character,
+ * word or sentence before the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence before <code>index</code>
+ */
+ public String getBeforeIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index - 1));
+ break;
+ case AccessibleText.WORD:
+ endIndex = text.lastIndexOf(' ', index);
+ if (endIndex == -1)
+ endIndex = 0;
+ startIndex = text.lastIndexOf(' ', endIndex - 1);
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ endIndex = text.lastIndexOf('.', index);
+ if (endIndex == -1)
+ endIndex = 0;
+ startIndex = text.lastIndexOf('.', endIndex - 1);
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the caret position. This method returns -1 because JLabel don't
+ * have a caret.
+ *
+ * @return the caret position
+ */
+ public int getCaretPosition()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns the number of characters that are displayed by the JLabel.
+ *
+ * @return the number of characters that are displayed by the JLabel
+ */
+ public int getCharCount()
+ {
+ // FIXME: Query HTML view for HTML labels.
+ return text.length();
+ }
+
+ /**
+ * Returns the bounding box of the character at the specified index.
+ *
+ * @param index the index of the character that we return the
+ * bounds for
+ *
+ * @return the bounding box of the character at the specified index
+ */
+ public Rectangle getCharacterBounds(int index)
+ {
+ Rectangle bounds = null;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Rectangle textR = getTextRectangle();
+ try
+ {
+ Shape s = view.modelToView(index, textR, Position.Bias.Forward);
+ bounds = s.getBounds();
+ }
+ catch (BadLocationException ex)
+ {
+ // Can't return something reasonable in this case.
+ }
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the rectangle inside the JLabel, in which the actual text is
+ * rendered. This method has been adopted from the Mauve testcase
+ * gnu.testlet.javax.swing.JLabel.AccessibleJLabel.getCharacterBounds.
+ *
+ * @return the rectangle inside the JLabel, in which the actual text is
+ * rendered
+ */
+ private Rectangle getTextRectangle()
+ {
+ JLabel l = JLabel.this;
+ Rectangle textR = new Rectangle();
+ Rectangle iconR = new Rectangle();
+ Insets i = l.getInsets();
+ int w = l.getWidth();
+ int h = l.getHeight();
+ Rectangle viewR = new Rectangle(i.left, i.top, w - i.left - i.right,
+ h - i.top - i.bottom);
+ FontMetrics fm = l.getFontMetrics(l.getFont());
+ SwingUtilities.layoutCompoundLabel(l, fm, l.getText(), l.getIcon(),
+ l.getVerticalAlignment(),
+ l.getHorizontalAlignment(),
+ l.getVerticalTextPosition(),
+ l.getHorizontalTextPosition(),
+ viewR, iconR, textR,
+ l.getIconTextGap());
+ return textR;
+ }
+
+ /**
+ * Returns the index of the character that is located at the specified
+ * point.
+ *
+ * @param point the location that we lookup the character for
+ *
+ * @return the index of the character that is located at the specified
+ * point
+ */
+ public int getIndexAtPoint(Point point)
+ {
+ int index = -1;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Rectangle r = getTextRectangle();
+ index = view.viewToModel(point.x, point.y, r, new Position.Bias[0]);
+ }
+ return index;
+ }
+ }
+
+ private static final long serialVersionUID = 5496508283662221534L;
+
+ static final String LABEL_PROPERTY = "labeledBy";
+
+ /**
+ * The Component the label will give focus to when its mnemonic is
+ * activated.
+ */
+ protected Component labelFor;
+
+ /** The label's text. */
+ transient String text;
+
+ /** Where the label will be positioned horizontally. */
+ private transient int horizontalAlignment = LEADING;
+
+ /** Where the label text will be placed horizontally relative to the icon. */
+ private transient int horizontalTextPosition = TRAILING;
+
+ /** Where the label will be positioned vertically. */
+ private transient int verticalAlignment = CENTER;
+
+ /** Where the label text will be place vertically relative to the icon. */
+ private transient int verticalTextPosition = CENTER;
+
+ /** The icon painted when the label is enabled. */
+ private transient Icon icon;
+
+ /** The icon painted when the label is disabled. */
+ private transient Icon disabledIcon;
+
+ /** The label's mnemnonic key. */
+ private transient int displayedMnemonic = KeyEvent.VK_UNDEFINED;
+
+ /** The index of the mnemonic character in the text. */
+ private transient int displayedMnemonicIndex = -1;
+
+ /** The gap between the icon and the text. */
+ private transient int iconTextGap = 4;
+
+ /**
+ * Creates a new vertically centered, horizontally on the leading edge
+ * JLabel object with text and no icon.
+ */
+ public JLabel()
+ {
+ this("", null, LEADING);
+ }
+
+ /**
+ * Creates a new vertically and horizontally centered
+ * JLabel object with no text and the given icon.
+ *
+ * @param image The icon to use with the label, <code>null</code> permitted.
+ */
+ public JLabel(Icon image)
+ {
+ this(null, image, CENTER);
+ }
+
+ /**
+ * Creates a new vertically centered JLabel object with no text and the
+ * given icon and horizontal alignment. By default, the text is TRAILING
+ * the image.
+ *
+ * @param image The icon to use with the label, <code>null</code> premitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>.
+ */
+ public JLabel(Icon image, int horizontalAlignment)
+ {
+ this(null, image, horizontalAlignment);
+ }
+
+ /**
+ * Creates a new horizontally leading and vertically centered JLabel
+ * object with no icon and the given text.
+ *
+ * @param text The text to use with the label, <code>null</code> permitted.
+ */
+ public JLabel(String text)
+ {
+ this(text, null, LEADING);
+ }
+
+ /**
+ * Creates a new vertically centered JLabel object with no icon and the
+ * given text and horizontal alignment.
+ *
+ * @param text The text to use with the label, <code>null</code> permitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>.
+ */
+ public JLabel(String text, int horizontalAlignment)
+ {
+ this(text, null, horizontalAlignment);
+ }
+
+ /**
+ * Creates a new vertically centered JLabel object with the given text,
+ * icon, and horizontal alignment.
+ *
+ * @param text The text to use with the label, <code>null</code> permitted.
+ * @param icon The icon to use with the label, <code>null</code> premitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either <code>CENTER</code>, <code>LEFT</code>, <code>RIGHT</code>,
+ * <code>LEADING</code> or <code>TRAILING</code>.
+ */
+ public JLabel(String text, Icon icon, int horizontalAlignment)
+ {
+ if (horizontalAlignment != SwingConstants.LEFT
+ && horizontalAlignment != SwingConstants.RIGHT
+ && horizontalAlignment != SwingConstants.CENTER
+ && horizontalAlignment != SwingConstants.LEADING
+ && horizontalAlignment != SwingConstants.TRAILING)
+ throw new IllegalArgumentException();
+
+ this.text = text;
+ this.icon = icon;
+ this.horizontalAlignment = horizontalAlignment;
+ setAlignmentX(0.0F);
+ setInheritsPopupMenu(true);
+ updateUI();
+ }
+
+ /**
+ * Returns the label's UI delegate.
+ *
+ * @return The label's UI delegate.
+ */
+ public LabelUI getUI()
+ {
+ return (LabelUI) ui;
+ }
+
+ /**
+ * Sets the label's UI delegate.
+ *
+ * @param ui The label's UI delegate (<code>null</code> not permitted).
+ */
+ public void setUI(LabelUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Resets the label's UI delegate to the default UI for the current look and
+ * feel.
+ */
+ public void updateUI()
+ {
+ setUI((LabelUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns a name to identify which look and feel class will be
+ * the UI delegate for this label.
+ *
+ * @return <code>"LabelUI"</code>
+ */
+ public String getUIClassID()
+ {
+ return "LabelUI";
+ }
+
+ /**
+ * Returns a string describing the attributes for the <code>JLabel</code>
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-<code>null</code>, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the <code>JLabel</code>.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",defaultIcon=");
+ if (icon != null)
+ sb.append(icon);
+ sb.append(",disabledIcon=");
+ if (disabledIcon != null)
+ sb.append(disabledIcon);
+ sb.append(",horizontalAlignment=");
+ sb.append(SwingUtilities.convertHorizontalAlignmentCodeToString(
+ horizontalAlignment));
+ sb.append(",horizontalTextPosition=");
+ sb.append(SwingUtilities.convertHorizontalAlignmentCodeToString(
+ horizontalTextPosition));
+ sb.append(",iconTextGap=").append(iconTextGap);
+ sb.append(",labelFor=");
+ if (labelFor != null)
+ sb.append(labelFor);
+ sb.append(",text=");
+ if (text != null)
+ sb.append(text);
+ sb.append(",verticalAlignment=");
+ sb.append(SwingUtilities.convertVerticalAlignmentCodeToString(
+ verticalAlignment));
+ sb.append(",verticalTextPosition=");
+ sb.append(SwingUtilities.convertVerticalAlignmentCodeToString(
+ verticalTextPosition));
+ return sb.toString();
+ }
+
+ /**
+ * Returns the text displayed by the label.
+ *
+ * @return The label text (possibly <code>null</code>).
+ *
+ * @see #setText(String)
+ */
+ public String getText()
+ {
+ return text;
+ }
+
+ /**
+ * Sets the text for the label and sends a {@link PropertyChangeEvent} (with
+ * the name 'text') to all registered listeners. This method will also
+ * update the <code>displayedMnemonicIndex</code>, if necessary.
+ *
+ * @param newText The text (<code>null</code> permitted).
+ *
+ * @see #getText()
+ * @see #getDisplayedMnemonicIndex()
+ */
+ public void setText(String newText)
+ {
+ if (text == null && newText == null)
+ return;
+ if (text != null && text.equals(newText))
+ return;
+
+ String oldText = text;
+ text = newText;
+ firePropertyChange("text", oldText, newText);
+
+ if (text != null)
+ setDisplayedMnemonicIndex(text.toUpperCase().indexOf(displayedMnemonic));
+ else
+ setDisplayedMnemonicIndex(-1);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Returns the active icon. The active icon is painted when the label is
+ * enabled.
+ *
+ * @return The active icon.
+ *
+ * @see #setIcon(Icon)
+ * @see #getDisabledIcon()
+ */
+ public Icon getIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * Sets the icon for the label (this is a bound property with the name
+ * 'icon'). This icon will be displayed when the label is enabled.
+ *
+ * @param newIcon The icon (<code>null</code> permitted).
+ *
+ * @see #getIcon()
+ * @see #setDisabledIcon(Icon)
+ */
+ public void setIcon(Icon newIcon)
+ {
+ if (icon != newIcon)
+ {
+ Icon oldIcon = icon;
+ icon = newIcon;
+ firePropertyChange("icon", oldIcon, newIcon);
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the disabled icon. The disabled icon is painted when the label is
+ * disabled. If the disabled icon is <code>null</code> and the active icon
+ * is an {@link ImageIcon}, this method returns a grayed version of the icon.
+ * The grayed version of the icon becomes the <code>disabledIcon</code>.
+ *
+ * @return The disabled icon.
+ *
+ * @see #setDisabledIcon(Icon)
+ */
+ public Icon getDisabledIcon()
+ {
+ if (disabledIcon == null && icon instanceof ImageIcon)
+ disabledIcon = new ImageIcon(
+ GrayFilter.createDisabledImage(((ImageIcon) icon).getImage()));
+
+ return disabledIcon;
+ }
+
+ /**
+ * Sets the icon displayed when the label is disabled (this is a bound
+ * property with the name 'disabledIcon').
+ *
+ * @param newIcon The disabled icon (<code>null</code> permitted).
+ *
+ * @see #getDisabledIcon()
+ */
+ public void setDisabledIcon(Icon newIcon)
+ {
+ if (disabledIcon != newIcon)
+ {
+ Icon oldIcon = disabledIcon;
+ disabledIcon = newIcon;
+ firePropertyChange("disabledIcon", oldIcon, newIcon);
+ }
+ }
+
+ /**
+ * Sets the keycode that will be the label's mnemonic (this is a bound
+ * property with the name 'displayedMnemonic'). 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 mnemonic The keycode to use for the mnemonic.
+ *
+ * @see #getDisplayedMnemonic()
+ */
+ public void setDisplayedMnemonic(int mnemonic)
+ {
+ if (displayedMnemonic != mnemonic)
+ {
+ int old = displayedMnemonic;
+ displayedMnemonic = mnemonic;
+ firePropertyChange("displayedMnemonic", old, displayedMnemonic);
+ if (text != null)
+ setDisplayedMnemonicIndex(text.toUpperCase().indexOf(mnemonic));
+ }
+ }
+
+ /**
+ * Sets the character that will be the label's mnemonic. If the
+ * label is used as a label for another component, the label will give
+ * focus to that component when the mnemonic is activated via the keyboard.
+ *
+ * @param mnemonic The character to use for the mnemonic (this will be
+ * converted to the equivalent upper case character).
+ *
+ * @see #getDisplayedMnemonic()
+ */
+ public void setDisplayedMnemonic(char mnemonic)
+ {
+ setDisplayedMnemonic((int) Character.toUpperCase(mnemonic));
+ }
+
+ /**
+ * Returns the keycode that is used for the label's mnemonic.
+ *
+ * @return The keycode that is used for the label's mnemonic.
+ *
+ * @see #setDisplayedMnemonic(int)
+ */
+ public int getDisplayedMnemonic()
+ {
+ return displayedMnemonic;
+ }
+
+ /**
+ * Sets the index of the character in the text that will be underlined to
+ * indicate that it is the mnemonic character for the label. You only need
+ * to call this method if you wish to override the automatically calculated
+ * character index. For instance, for a label "Find Next" with the mnemonic
+ * character 'n', you might wish to underline the second occurrence of 'n'
+ * rather than the first (which is the default).
+ * <br><br>
+ * Note that this method does not validate the character at the specified
+ * index to ensure that it matches the key code returned by
+ * {@link #getDisplayedMnemonic()}.
+ *
+ * @param newIndex The index of the character to underline.
+ *
+ * @throws IllegalArgumentException If index less than -1 or index is greater
+ * than or equal to the label length.
+ *
+ * @see #getDisplayedMnemonicIndex()
+ * @since 1.4
+ */
+ public void setDisplayedMnemonicIndex(int newIndex)
+ throws IllegalArgumentException
+ {
+ int maxValid = -1;
+ if (text != null)
+ maxValid = text.length() - 1;
+ if (newIndex < -1 || newIndex > maxValid)
+ throw new IllegalArgumentException();
+
+ if (newIndex != displayedMnemonicIndex)
+ {
+ int oldIndex = displayedMnemonicIndex;
+ displayedMnemonicIndex = newIndex;
+ firePropertyChange("displayedMnemonicIndex", oldIndex, newIndex);
+ }
+ }
+
+ /**
+ * Returns the index of the character in the label's text that will be
+ * underlined (to indicate that it is the mnemonic character), or -1 if no
+ * character is to be underlined.
+ *
+ * @return The index of the character that will be underlined.
+ *
+ * @see #setDisplayedMnemonicIndex(int)
+ * @since 1.4
+ */
+ public int getDisplayedMnemonicIndex()
+ {
+ return displayedMnemonicIndex;
+ }
+
+ /**
+ * Checks the specified key to ensure that it is valid as a horizontal
+ * alignment, throwing an {@link IllegalArgumentException} if the key is
+ * invalid. Valid keys are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT},
+ * {@link #LEADING} and {@link #TRAILING}.
+ *
+ * @param key The key to check.
+ * @param message The message of the exception to be thrown if the key is
+ * invalid.
+ *
+ * @return The key if it is valid.
+ *
+ * @throws IllegalArgumentException If the key is invalid.
+ */
+ protected int checkHorizontalKey(int key, String message)
+ {
+ if (key != LEFT && key != CENTER && key != RIGHT && key != LEADING
+ && key != TRAILING)
+ throw new IllegalArgumentException(message);
+ else
+ return key;
+ }
+
+ /**
+ * Checks the specified key to ensure that it is valid as a vertical
+ * alignment, throwing an {@link IllegalArgumentException} if the key is
+ * invalid. Valid keys are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
+ *
+ * @param key The key to check.
+ * @param message The message of the exception to be thrown if the key is
+ * invalid.
+ *
+ * @return The key if it is valid.
+ *
+ * @throws IllegalArgumentException If the key is invalid.
+ */
+ protected int checkVerticalKey(int key, String message)
+ {
+ if (key != TOP && key != BOTTOM && key != CENTER)
+ throw new IllegalArgumentException(message);
+ else
+ return key;
+ }
+
+ /**
+ * Returns the gap between the icon and the text.
+ *
+ * @return The gap between the icon and the text.
+ *
+ * @see #setIconTextGap(int)
+ */
+ public int getIconTextGap()
+ {
+ return iconTextGap;
+ }
+
+ /**
+ * Sets the gap between the icon and the text, in the case that both are
+ * visible (this is a bound property with the name 'iconTextGap').
+ *
+ * @param newGap The gap (in pixels).
+ *
+ * @see #getIconTextGap()
+ */
+ public void setIconTextGap(int newGap)
+ {
+ if (iconTextGap != newGap)
+ {
+ firePropertyChange("iconTextGap", iconTextGap, newGap);
+ iconTextGap = newGap;
+ }
+ }
+
+ /**
+ * Returns the vertical alignment of the label (one of
+ * {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}). The default value
+ * depends on the installed look and feel, but is usually {@link #CENTER}.
+ *
+ * @return The vertical alignment.
+ *
+ * @see #setVerticalAlignment(int)
+ */
+ public int getVerticalAlignment()
+ {
+ return verticalAlignment;
+ }
+
+ /**
+ * Sets the vertical alignment for the label (this is a bound property with
+ * the name 'verticalAlignment'). The vertical alignment determines where
+ * the label (icon and text) will be placed vertically within the component
+ * bounds. Valid alignment codes are {@link #TOP}, {@link #CENTER} and
+ * {@link #BOTTOM}.
+ *
+ * @param alignment The vertical alignment of the label.
+ *
+ * @throws IllegalArgumentException if <code>alignment</code> is not one of
+ * the specified values.
+ *
+ * @see #getVerticalAlignment()
+ */
+ public void setVerticalAlignment(int alignment)
+ {
+ if (alignment == verticalAlignment)
+ return;
+
+ int oldAlignment = verticalAlignment;
+ verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
+ firePropertyChange("verticalAlignment", oldAlignment, verticalAlignment);
+ }
+
+ /**
+ * Returns the horizontal alignment of the label (one of {@link #LEFT},
+ * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}).
+ * The default value depends on the installed look and feel, but is usually
+ * {@link #LEFT}.
+ *
+ * @return The horizontal alignment.
+ *
+ * @see #setHorizontalAlignment(int)
+ */
+ public int getHorizontalAlignment()
+ {
+ return horizontalAlignment;
+ }
+
+ /**
+ * Sets the horizontal alignment for the label (this is a bound property with
+ * the name 'horizontalAlignment'). The horizontal alignment determines where
+ * the label (icon and text) will be placed horizontally within the
+ * component bounds. Valid alignment codes are {@link #LEFT},
+ * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}.
+ *
+ * @param alignment The horizontal alignment of the label.
+ *
+ * @throws IllegalArgumentException if <code>alignment</code> is not one of
+ * the specified values.
+ *
+ * @see #getHorizontalAlignment()
+ */
+ public void setHorizontalAlignment(int alignment)
+ {
+ if (horizontalAlignment == alignment)
+ return;
+
+ int oldAlignment = horizontalAlignment;
+ horizontalAlignment = checkHorizontalKey(alignment, "horizontalAlignment");
+ firePropertyChange("horizontalAlignment", oldAlignment,
+ horizontalAlignment);
+ }
+
+ /**
+ * Returns the vertical position of the label's text relative to the icon.
+ * This will be one of {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
+ *
+ * @return The vertical position of the label's text relative to the icon.
+ *
+ * @see #setVerticalTextPosition(int)
+ */
+ public int getVerticalTextPosition()
+ {
+ return verticalTextPosition;
+ }
+
+ /**
+ * Sets the vertical position of the label's text relative to the icon (this
+ * is a bound property with the name 'verticalTextPosition'). Valid
+ * positions are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
+ *
+ * @param textPosition The vertical text position.
+ *
+ * @throws IllegalArgumentException if <code>textPosition</code> is not one
+ * of the specified values.
+ */
+ public void setVerticalTextPosition(int textPosition)
+ {
+ if (textPosition != verticalTextPosition)
+ {
+ int oldPos = verticalTextPosition;
+ verticalTextPosition = checkVerticalKey(textPosition,
+ "verticalTextPosition");
+ firePropertyChange("verticalTextPosition", oldPos,
+ verticalTextPosition);
+ }
+ }
+
+ /**
+ * Returns the horizontal position of the label's text relative to the icon.
+ * This will be one of {@link #LEFT}, {@link #CENTER}, {@link #RIGHT},
+ * {@link #LEADING} and {@link #TRAILING}.
+ *
+ * @return The horizontal position of the label's text relative to the icon.
+ *
+ * @see #setHorizontalTextPosition(int)
+ */
+ public int getHorizontalTextPosition()
+ {
+ return horizontalTextPosition;
+ }
+
+ /**
+ * Sets the horizontal position of the label's text relative to the icon (this
+ * is a bound property with the name 'horizontalTextPosition'). Valid
+ * positions are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT},
+ * {@link #LEADING} and {@link #TRAILING}.
+ *
+ * @param textPosition The horizontal text position.
+ *
+ * @throws IllegalArgumentException if <code>textPosition</code> is not one
+ * of the specified values.
+ */
+ public void setHorizontalTextPosition(int textPosition)
+ {
+ if (textPosition != horizontalTextPosition)
+ {
+ int oldPos = horizontalTextPosition;
+ horizontalTextPosition = checkHorizontalKey(textPosition,
+ "horizontalTextPosition");
+ firePropertyChange("horizontalTextPosition", oldPos,
+ horizontalTextPosition);
+ }
+ }
+
+ /**
+ * Returns false if the current icon image (current icon will depend on
+ * whether the label is enabled) is not equal to the passed in image.
+ *
+ * @param img The image to check.
+ * @param infoflags The bitwise inclusive OR of ABORT, ALLBITS, ERROR,
+ * FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, and WIDTH
+ * @param x The x position
+ * @param y The y position
+ * @param w The width
+ * @param h The height
+ *
+ * @return Whether the current icon image is equal to the image given.
+ */
+ public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
+ int h)
+ {
+ Icon currIcon = isEnabled() ? icon : disabledIcon;
+
+ // XXX: Is this the correct way to check for image equality?
+ if (currIcon != null && currIcon instanceof ImageIcon)
+ return (((ImageIcon) currIcon).getImage() == img);
+
+ return false;
+ }
+
+ /**
+ * Returns the component that this <code>JLabel</code> is providing the label
+ * for. This component will typically receive the focus when the label's
+ * mnemonic key is activated via the keyboard.
+ *
+ * @return The component (possibly <code>null</code>).
+ */
+ public Component getLabelFor()
+ {
+ return labelFor;
+ }
+
+ /**
+ * Sets the component that this <code>JLabel</code> is providing the label
+ * for (this is a bound property with the name 'labelFor'). This component
+ * will typically receive the focus when the label's mnemonic key is
+ * activated via the keyboard.
+ *
+ * @param c the component (<code>null</code> permitted).
+ *
+ * @see #getLabelFor()
+ */
+ public void setLabelFor(Component c)
+ {
+ if (c != labelFor)
+ {
+ Component oldLabelFor = labelFor;
+ labelFor = c;
+ firePropertyChange("labelFor", oldLabelFor, labelFor);
+
+ // We put the label into the client properties for the labeled
+ // component so that it can be read by the AccessibleJComponent.
+ // The other option would be to reserve a default visible field
+ // in JComponent, but since this is relatively seldomly used, it
+ // would be unnecessary waste of memory to do so.
+ if (oldLabelFor instanceof JComponent)
+ {
+ ((JComponent) oldLabelFor).putClientProperty(LABEL_PROPERTY, null);
+ }
+
+ if (labelFor instanceof JComponent)
+ {
+ ((JComponent) labelFor).putClientProperty(LABEL_PROPERTY, this);
+ }
+
+ }
+ }
+
+ /**
+ * Sets the font for the label (this a bound property with the name 'font').
+ *
+ * @param f The font (<code>null</code> permitted).
+ */
+ public void setFont(Font f)
+ {
+ super.setFont(f);
+ repaint();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JLabel</code> component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJLabel}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJLabel();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java
new file mode 100644
index 000000000..9a4e31efe
--- /dev/null
+++ b/libjava/classpath/javax/swing/JLayeredPane.java
@@ -0,0 +1,755 @@
+/* JLayeredPane.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * A container that adds depth to the usual <code>Container</code> semantics.
+ * Each child component of a <code>Layered Pane</code> is placed within one
+ * of several layers. <code>JLayeredPane</code> defines a set of standard
+ * layers. The pre-defined sets are (in the order from button to top):
+ *
+ * <dl>
+ * <dt>{@link #DEFAULT_LAYER}</dt>
+ * <dd>The layer where most of the normal components are placed. This
+ * is the bottommost layer.</dd>
+ *
+ * <dt>{@link #PALETTE_LAYER}</dt>
+ * <dd>Palette windows are placed in this layer.</dd>
+ *
+ * <dt>{@link #MODAL_LAYER}</dt>
+ * <dd>The layer where internal modal dialog windows are placed.</dd>
+ *
+ * <dt>{@link #POPUP_LAYER}</dt>
+ * <dd>The layer for popup menus</dd>
+ *
+ * <dt>{@link #DRAG_LAYER}</dt>
+ * <dd>Components that are beeing dragged are temporarily placed in
+ * this layer.</dd>
+ * </dl>
+ *
+ * <p>A child is in exactly one of these layers at any time, though there may
+ * be other layers if someone creates them.</p>
+ *
+ * <p>You can add a component to a specific layer using the
+ * {@link Container#add(Component, Object)} method. I.e.
+ * <code>layeredPane.add(comp, JLayeredPane.MODAL_LAYER)</code> will add the
+ * component <code>comp</code> to the modal layer of <code>layeredPane</code>.
+ * </p>
+ *
+ * <p>To change the layer of a component that is already a child of
+ * a <code>JLayeredPane</code>, use the {@link #setLayer(Component, int)}
+ * method.</p>
+ *
+ * <p>The purpose of this class is to translate this view of "layers" into a
+ * contiguous array of components: the one held in our ancestor,
+ * {@link java.awt.Container}.</p>
+ *
+ * <p>There is a precise set of words we will use to refer to numbers within
+ * this class:</p>
+ *
+ * <dl>
+ * <dt>Component Index:</dt>
+ * <dd>An offset into the <code>component</code> array held in our ancestor,
+ * {@link java.awt.Container}, from <code>[0 .. component.length)</code>. The drawing
+ * rule with indices is that 0 is drawn last.</dd>
+ *
+ * <dt>Layer Number:</dt>
+ * <dd>A general <code>int</code> specifying a layer within this component. Negative
+ * numbers are drawn first, then layer 0, then positive numbered layers, in
+ * ascending order.</dd>
+ *
+ * <dt>Position:</dt>
+ * <dd>An offset into a layer's "logical drawing order". Layer position 0
+ * is drawn last. Layer position -1 is a synonym for the first layer
+ * position (the logical "bottom").</dd>
+ * </dl>
+ *
+ * <p><b>Note:</b> the layer numbering order is the <em>reverse</em> of the
+ * component indexing and position order</p>
+ *
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class JLayeredPane extends JComponent implements Accessible
+{
+
+ /**
+ * Provides accessibility support for <code>JLayeredPane</code>.
+ */
+ protected class AccessibleJLayeredPane extends AccessibleJComponent
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJLayeredPane</code>.
+ */
+ protected AccessibleJLayeredPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessble role of <code>JLayeredPane</code>,
+ * {@link AccessibleRole#LAYERED_PANE}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.LAYERED_PANE;
+ }
+ }
+
+ private static final long serialVersionUID = 5534920399324590459L;
+
+ public static final String LAYER_PROPERTY = "layeredContainerLayer";
+
+ public static final Integer FRAME_CONTENT_LAYER = new Integer(-30000);
+
+ public static final Integer DEFAULT_LAYER = new Integer(0);
+ public static final Integer PALETTE_LAYER = new Integer(100);
+ public static final Integer MODAL_LAYER = new Integer(200);
+ public static final Integer POPUP_LAYER = new Integer(300);
+ public static final Integer DRAG_LAYER = new Integer(400);
+
+ private Hashtable componentToLayer; // Component -> Layer Number (Integer)
+
+ public JLayeredPane()
+ {
+ componentToLayer = new Hashtable();
+ setLayout(null);
+ }
+
+ /**
+ * Looks up the layer a child component is currently assigned to.
+ *
+ * If <code>c</code> is an instance of {@link JComponent}, then the layer
+ * is fetched from the client property with the key {@link #LAYER_PROPERTY}.
+ * Otherwise it is looked up in an internal hashtable that maps
+ * non-JComponent components to layers. If the components cannot be found
+ * in either way, the {@link #DEFAULT_LAYER} is returned.
+ *
+ * @param c the component to look up.
+ *
+ * @return the layer the component is currently assigned to; if the component
+ * is not in this layered pane, then 0 (DEFAULT_LAYER) is returned
+ */
+ public int getLayer(Component c)
+ {
+ Integer layerObj;
+ if (c instanceof JComponent)
+ {
+ JComponent jc = (JComponent) c;
+ layerObj = (Integer) jc.getClientProperty(LAYER_PROPERTY);
+ }
+ else
+ layerObj = (Integer) componentToLayer.get(c);
+
+ if (layerObj == null)
+ layerObj = DEFAULT_LAYER;
+
+ return layerObj.intValue();
+ }
+
+ /**
+ * Looks up the layer in the client property with the key
+ * {@link #LAYER_PROPERTY} of <code>comp</code>. If no such property can be
+ * found, we return <code>0</code> ({@link #DEFAULT_LAYER}).
+ *
+ * @param comp the component for which the layer is looked up
+ *
+ * @return the layer of <code>comp</code> as stored in the corresponding
+ * client property, or <code>0</code> if there is no such property
+ */
+ public static int getLayer(JComponent comp)
+ {
+ Integer layerObj = (Integer) comp.getClientProperty(LAYER_PROPERTY);
+ if (layerObj == null)
+ layerObj = DEFAULT_LAYER;
+ return layerObj.intValue();
+ }
+
+ /**
+ * Returns the first JLayeredPane that contains the Component
+ * <code>comp</code> or <code>null</code> if <code>comp</code> is
+ * not contained in a JLayeredPane.
+ *
+ * @param comp the component for which we are searching the JLayeredPane
+ * ancestor
+ *
+ * @return the first JLayeredPane that contains the Component
+ * <code>comp</code> or <code>null</code> if <code>comp</code> is
+ * not contained in a JLayeredPane
+ */
+ public static JLayeredPane getLayeredPaneAbove(Component comp)
+ {
+ JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass
+ (JLayeredPane.class, comp);
+ return lp;
+ }
+
+ /**
+ * Return the greatest layer number currently in use, in this container.
+ * This number may legally be positive <em>or</em> negative.
+ *
+ * @return the highest layer number
+ *
+ * @see #lowestLayer()
+ */
+ public int highestLayer()
+ {
+ Component[] components = getComponents();
+ int highest;
+ if (components.length == 0)
+ highest = 0;
+ else
+ {
+ highest = Integer.MIN_VALUE;
+ for (int i = 0; i < components.length; i++)
+ highest = Math.max(highest, getLayer(components[i]));
+ }
+ return highest;
+ }
+
+ /**
+ * Return the least layer number currently in use, in this container.
+ * This number may legally be positive <em>or</em> negative.
+ *
+ * @return the least layer number
+ *
+ * @see #highestLayer()
+ */
+ public int lowestLayer()
+ {
+ Component[] components = getComponents();
+ int lowest;
+ if (components.length == 0)
+ lowest = 0;
+ else
+ {
+ lowest = Integer.MAX_VALUE;
+ for (int i = 0; i < components.length; i++)
+ lowest = Math.max(lowest, getLayer(components[i]));
+ }
+ return lowest;
+ }
+
+ /**
+ * Moves a component to the "front" of its layer. The "front" is a
+ * synonym for position 0, which is also the last position drawn in each
+ * layer, so is usually the component which occludes the most other
+ * components in its layer.
+ *
+ * @param c the component to move to the front of its layer
+ *
+ * @see #moveToBack
+ */
+ public void moveToFront(Component c)
+ {
+ setPosition (c, 0);
+ }
+
+ /**
+ * <p>Moves a component to the "back" of its layer. The "back" is a
+ * synonym for position N-1 (also known as position -1), where N is the
+ * size of the layer.</p>
+ *
+ * <p>The "back" of a layer is the first position drawn, so the component at
+ * the "back" is usually the component which is occluded by the most
+ * other components in its layer.</p>
+ *
+ * @param c the component to move to the back of its layer.
+ *
+ * @see #moveToFront
+ */
+ public void moveToBack(Component c)
+ {
+ setPosition (c, -1);
+ }
+
+ /**
+ * Return the position of a component within its layer. Positions are assigned
+ * from the "front" (position 0) to the "back" (position N-1), and drawn from
+ * the back towards the front.
+ *
+ * @param c the component to get the position of
+ *
+ * @return the position of <code>c</code> within its layer or -1 if
+ * <code>c</code> is not a child of this layered pane
+ *
+ * @see #setPosition
+ */
+ public int getPosition(Component c)
+ {
+ int pos = -1;
+ int index = getIndexOf(c);
+ if (index >= 0)
+ {
+ pos = 0;
+ int layer = getLayer(c);
+ for (int i = index - 1; i >= 0; --i)
+ {
+ if (layer == getLayer(getComponent(i)))
+ pos++;
+ else
+ break;
+ }
+ }
+ return pos;
+ }
+
+ /**
+ * Change the position of a component within its layer. Positions are assigned
+ * from the "front" (position 0) to the "back" (position N-1), and drawn from
+ * the back towards the front.
+ *
+ * @param c the component to change the position of
+ * @param position the position to assign the component to
+ *
+ * @see #getPosition
+ */
+ public void setPosition(Component c, int position)
+ {
+ setLayer(c, getLayer(c), position);
+ }
+
+ /**
+ * Return an array of all components within a layer of this
+ * container. Components are ordered front-to-back, with the "front"
+ * element (which draws last) at position 0 of the returned array.
+ *
+ * @param layer the layer to return components from
+ *
+ * @return the components in the layer
+ */
+ public Component[] getComponentsInLayer(int layer)
+ {
+ Component[] inLayer = new Component[getComponentCountInLayer(layer)];
+ Component[] components = getComponents();
+ int j = 0;
+ for (int i = 0; i < components.length; ++i)
+ {
+ if (layer == getLayer(components[i]))
+ {
+ inLayer[j] = components[i];
+ j++;
+ }
+ }
+ return inLayer;
+ }
+
+ /**
+ * Return the number of components within a layer of this
+ * container.
+ *
+ * @param layer the layer count components in
+ *
+ * @return the number of components in the layer
+ */
+ public int getComponentCountInLayer(int layer)
+ {
+ Component[] components = getComponents();
+ int count = 0;
+ for (int i = components.length - 1; i >= 0; --i)
+ {
+ if (getLayer(components[i]) == layer)
+ count++;
+ }
+ return count;
+ }
+
+ /**
+ * Return a hashtable mapping child components of this container to
+ * Integer objects representing the component's layer assignments.
+ */
+ protected Hashtable<Component, Integer> getComponentToLayer()
+ {
+ return componentToLayer;
+ }
+
+ /**
+ * Return the index of a component within the underlying (contiguous)
+ * array of children. This is a "raw" number which does not represent the
+ * child's position in a layer, but rather its position in the logical
+ * drawing order of all children of the container.
+ *
+ * @param c the component to look up.
+ *
+ * @return the external index of the component or <code>-1</code> if
+ * <code>c</code> is not a child of this layered pane
+ */
+ public int getIndexOf(Component c)
+ {
+ return getComponentZOrder(c);
+ }
+
+ /**
+ * Return an Integer object which holds the same int value as the
+ * parameter. This is strictly an optimization to minimize the number of
+ * identical Integer objects which we allocate.
+ *
+ * @param layer the layer number as an int.
+ *
+ * @return the layer number as an Integer, possibly shared.
+ */
+ protected Integer getObjectForLayer(int layer)
+ {
+ switch (layer)
+ {
+ case -30000:
+ return FRAME_CONTENT_LAYER;
+
+ case 0:
+ return DEFAULT_LAYER;
+
+ case 100:
+ return PALETTE_LAYER;
+
+ case 200:
+ return MODAL_LAYER;
+
+ case 300:
+ return POPUP_LAYER;
+
+ case 400:
+ return DRAG_LAYER;
+
+ default:
+ break;
+ }
+
+ return new Integer(layer);
+ }
+
+ /**
+ * Computes an index at which to request the superclass {@link
+ * java.awt.Container} inserts a component, given an abstract layer and
+ * position number.
+ *
+ * @param layer the layer in which to insert a component.
+ * @param position the position in the layer at which to insert a component.
+ *
+ * @return the index at which to insert the component.
+ */
+ protected int insertIndexForLayer(int layer, int position)
+ {
+ return insertIndexForLayer(null, layer, position);
+ }
+
+ /**
+ * Similar to {@link #insertIndexForLayer(int, int)}, only that it takes a
+ * component parameter, which should be ignored in the search. This is
+ * necessary to support {@link #setLayer(Component, int, int)} which uses
+ * Container.setComponentZOrder(), which doesn't remove the component.
+ *
+ * @param comp the component to ignore
+ * @param layer the layer
+ * @param position the position
+ *
+ * @return the insertion index
+ */
+ private int insertIndexForLayer(Component comp, int layer, int position)
+ {
+ // Create the component list to search through.
+ ArrayList l = new ArrayList();
+ int count = getComponentCount();
+ for (int i = 0; i < count; i++)
+ {
+ Component c = getComponent(i);
+ if (c != comp)
+ l.add(c);
+ }
+
+ count = l.size();
+ int layerStart = -1;
+ int layerEnd = -1;
+ for (int i = 0; i < count; i++)
+ {
+ int layerOfComponent = getLayer((Component) l.get(i));
+ if (layerStart == -1 && layerOfComponent == layer)
+ layerStart = i;
+ if (layerOfComponent < layer)
+ {
+ // We are beyond the layer that we are looking for. Update the
+ // layerStart and layerEnd and exit the loop.
+ if (i == 0)
+ {
+ layerStart = 0;
+ layerEnd = 0;
+ }
+ else
+ layerEnd = i;
+ break;
+ }
+ }
+
+ // No layer found. The requested layer is lower than any existing layer,
+ // put the component at the end.
+ int insertIndex;
+ if (layerStart == -1 && layerEnd == -1)
+ {
+ insertIndex = count;
+ }
+ else
+ {
+ // Corner cases.
+ if (layerStart != -1 && layerEnd == -1)
+ layerEnd = count;
+ if (layerStart == -1 && layerEnd != -1)
+ layerStart = layerEnd;
+
+ // Adding to the bottom of a layer returns the end index
+ // in the layer.
+ if (position == -1)
+ insertIndex = layerEnd;
+ else
+ {
+ // Insert into a layer.
+ if (position > -1 && layerStart + position <= layerEnd)
+ insertIndex = layerStart + position;
+ else
+ insertIndex = layerEnd;
+ }
+ }
+ return insertIndex;
+ }
+
+ /**
+ * Removes a child from this container. The child is specified by
+ * index. After removal, the child no longer occupies a layer.
+ *
+ * @param index the index of the child component to remove.
+ */
+ public void remove(int index)
+ {
+ Component c = getComponent(index);
+ if (! (c instanceof JComponent))
+ componentToLayer.remove(c);
+ super.remove(index);
+ }
+
+ /**
+ * Removes all components from this container.
+ *
+ * @since 1.5
+ */
+ public void removeAll()
+ {
+ componentToLayer.clear();
+ super.removeAll();
+ }
+
+ /**
+ * <p>Set the layer property for a component, within this container. The
+ * component will be implicitly mapped to the bottom-most position in the
+ * layer, but only if added <em>after</em> calling this method.</p>
+ *
+ * <p>Read that carefully: this method should be called <em>before</em> the
+ * component is added to the container.</p>
+ *
+ * @param c the component to set the layer property for.
+ * @param layer the layer number to assign to the component.
+ */
+ public void setLayer(Component c, int layer)
+ {
+ setLayer(c, layer, -1);
+ }
+
+ /**
+ * Set the layer and position of a component, within this container.
+ *
+ * @param c the child component to set the layer property for.
+ * @param layer the layer number to assign to the component.
+ * @param position the position number to assign to the component.
+ */
+ public void setLayer(Component c, int layer, int position)
+ {
+ Integer layerObj = getObjectForLayer(layer);
+
+ // Nothing to do if neither the layer nor the position is
+ // changed.
+ if (layer != getLayer(c) || position != getPosition(c))
+ {
+ // Store the layer either in the JComponent or in the hashtable
+ if (c instanceof JComponent)
+ {
+ JComponent jc = (JComponent) c;
+ jc.putClientProperty(LAYER_PROPERTY, layerObj);
+ }
+ else
+ componentToLayer.put (c, layerObj);
+
+ // Update the component in the Z order of the Container.
+ Container parent = c.getParent();
+ if (parent == this)
+ {
+ int index = insertIndexForLayer(c, layer, position);
+ setComponentZOrder(c, index);
+ }
+ }
+ repaint(c.getX(), c.getY(), c.getWidth(), c.getHeight());
+ }
+
+ /**
+ * Overrides the default implementation from {@link java.awt.Container}
+ * such that <code>layerConstraint</code> is interpreted as an {@link
+ * Integer}, specifying the layer to which the component will be added
+ * (at the bottom position).
+ *
+ * The argument <code>index</code> specifies the position within the layer
+ * at which the component should be added, where <code>0</code> is the top
+ * position greater values specify positions below that and <code>-1</code>
+ * specifies the bottom position.
+ *
+ * @param comp the component to add
+ * @param layerConstraint an integer specifying the layer to add the
+ * component to
+ * @param index the position within the layer
+ */
+ protected void addImpl(Component comp, Object layerConstraint, int index)
+ {
+ int layer;
+ if (layerConstraint != null && layerConstraint instanceof Integer)
+ {
+ layer = ((Integer) layerConstraint).intValue();
+ setLayer(comp, layer);
+ }
+ else
+ layer = getLayer(comp);
+
+ int newIdx = insertIndexForLayer(layer, index);
+ super.addImpl(comp, layerConstraint, newIdx);
+ comp.validate();
+ comp.repaint();
+ }
+
+ /**
+ * Sets the layer property for a JComponent.
+ *
+ * @param component the component for which to set the layer
+ * @param layer the layer property to set
+ */
+ public static void putLayer(JComponent component, int layer)
+ {
+ component.putClientProperty(LAYER_PROPERTY, new Integer(layer));
+ }
+
+ /**
+ * Returns the accessible context for this <code>JLayeredPane</code>.
+ *
+ * @return the accessible context for this <code>JLayeredPane</code>
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJLayeredPane();
+ return accessibleContext;
+ }
+
+ /**
+ * This method is overridden order to provide a reasonable painting
+ * mechanism for <code>JLayeredPane</code>. This is necessary since
+ * <code>JLayeredPane</code>'s do not have an own UI delegate.
+ *
+ * Basically this method clears the background for the
+ * <code>JLayeredPane</code> and then calls <code>super.paint(g)</code>.
+ *
+ * @param g the graphics context to use
+ */
+ public void paint(Graphics g)
+ {
+ if (isOpaque())
+ {
+ Color oldColor = g.getColor();
+ Rectangle clip = g.getClipBounds();
+ g.setColor(getBackground());
+ g.fillRect(clip.x, clip.y, clip.width, clip.height);
+ g.setColor(oldColor);
+ }
+ super.paint(g);
+ }
+
+ /**
+ * Returns <code>false</code> if components in this layered pane can overlap,
+ * otherwise <code>true</code>.
+ *
+ * @return <code>false</code> if components in this layered pane can overlap,
+ * otherwise <code>true</code>
+ */
+ public boolean isOptimizedDrawingEnabled()
+ {
+ int numChildren = getComponentCount();
+ boolean result = true;
+ for (int i = 0; i < numChildren; ++i)
+ {
+ Component c1 = getComponent(i);
+ if (! c1.isVisible())
+ continue;
+ Rectangle r1 = c1.getBounds();
+ if (r1.isEmpty())
+ continue;
+
+ for (int j = i + 1; j < numChildren; ++j)
+ {
+ Component c2 = getComponent(j);
+ if (! c2.isVisible())
+ continue;
+ Rectangle r2 = c2.getBounds();
+ if (r2.isEmpty())
+ continue;
+ if (r1.intersects(r2))
+ {
+ result = false;
+ break;
+ }
+ if (result == false)
+ break;
+ }
+ }
+ return result;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java
new file mode 100644
index 000000000..b12fda365
--- /dev/null
+++ b/libjava/classpath/javax/swing/JList.java
@@ -0,0 +1,2499 @@
+/* JList.java --
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.plaf.ListUI;
+import javax.swing.text.Position;
+
+/**
+ * <p>This class is a facade over three separate objects: {@link
+ * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and
+ * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list"
+ * concept, with independently replacable (possibly client-provided) models
+ * for its contents and its current selection. In addition, each element in
+ * the list is rendered via a strategy class {@link
+ * javax.swing.ListCellRenderer}.</p>
+ *
+ * <p>Lists have many properties, some of which are stored in this class
+ * while others are delegated to the list's model or selection. The
+ * following properties are available:</p>
+ *
+ * <table>
+ * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr>
+ * <tr><td>accessibleContext </td><td>list </td><td>no </td></tr>
+ * <tr><td>anchorSelectionIndex </td><td>selection</td><td>no </td></tr>
+ * <tr><td>cellRenderer </td><td>list </td><td>yes </td></tr>
+ * <tr><td>dragEnabled </td><td>list </td><td>no </td></tr>
+ * <tr><td>firstVisibleIndex </td><td>list </td><td>no </td></tr>
+ * <tr><td>fixedCellHeight </td><td>list </td><td>yes </td></tr>
+ * <tr><td>fixedCellWidth </td><td>list </td><td>yes </td></tr>
+ * <tr><td>lastVisibleIndex </td><td>list </td><td>no </td></tr>
+ * <tr><td>layoutOrientation </td><td>list </td><td>yes </td></tr>
+ * <tr><td>leadSelectionIndex </td><td>selection</td><td>no </td></tr>
+ * <tr><td>maxSelectionIndex </td><td>selection</td><td>no </td></tr>
+ * <tr><td>minSelectionIndex </td><td>selection</td><td>no </td></tr>
+ * <tr><td>model </td><td>list </td><td>yes </td></tr>
+ * <tr><td>opaque </td><td>list </td><td>no </td></tr>
+ * <tr><td>preferredScrollableViewportSize</td><td>list </td><td>no </td></tr>
+ * <tr><td>prototypeCellValue </td><td>list </td><td>yes </td></tr>
+ * <tr><td>scrollableTracksViewportHeight </td><td>list </td><td>no </td></tr>
+ * <tr><td>scrollableTracksViewportWidth </td><td>list </td><td>no </td></tr>
+ * <tr><td>selectedIndex </td><td>selection</td><td>no </td></tr>
+ * <tr><td>selectedIndices </td><td>selection</td><td>no </td></tr>
+ * <tr><td>selectedValue </td><td>model </td><td>no </td></tr>
+ * <tr><td>selectedValues </td><td>model </td><td>no </td></tr>
+ * <tr><td>selectionBackground </td><td>list </td><td>yes </td></tr>
+ * <tr><td>selectionEmpty </td><td>selection</td><td>no </td></tr>
+ * <tr><td>selectionForeground </td><td>list </td><td>yes </td></tr>
+ * <tr><td>selectionMode </td><td>selection</td><td>no </td></tr>
+ * <tr><td>selectionModel </td><td>list </td><td>yes </td></tr>
+ * <tr><td>UI </td><td>list </td><td>yes </td></tr>
+ * <tr><td>UIClassID </td><td>list </td><td>no </td></tr>
+ * <tr><td>valueIsAdjusting </td><td>list </td><td>no </td></tr>
+ * <tr><td>visibleRowCount </td><td>list </td><td>no </td></tr>
+ * </table>
+ *
+ * @author Graydon Hoare (graydon@redhat.com)
+ */
+
+public class JList extends JComponent implements Accessible, Scrollable
+{
+
+ /**
+ * Provides accessibility support for <code>JList</code>.
+ */
+ protected class AccessibleJList extends AccessibleJComponent
+ implements AccessibleSelection, PropertyChangeListener,
+ ListSelectionListener, ListDataListener
+ {
+
+ /**
+ * Provides accessibility support for list elements in <code>JList</code>s.
+ */
+ protected class AccessibleJListChild extends AccessibleContext
+ implements Accessible, AccessibleComponent
+ {
+
+ /**
+ * The parent list.
+ */
+ JList parent;
+
+ /**
+ * The index in the list for that child.
+ */
+ int listIndex;
+
+ /**
+ * The cursor for this list child.
+ */
+ // TODO: Testcases show that this class somehow stores state about the
+ // cursor. I cannot make up though how that could affect
+ // the actual list.
+ Cursor cursor = Cursor.getDefaultCursor();
+
+ /**
+ * Creates a new instance of <code>AccessibleJListChild</code>.
+ *
+ * @param list the list of which this is an accessible child
+ * @param index the list index for this child
+ */
+ public AccessibleJListChild(JList list, int index)
+ {
+ parent = list;
+ listIndex = index;
+ }
+
+ /**
+ * Returns the accessible context of this object. Returns
+ * <code>this</code> since <code>AccessibleJListChild</code>s are their
+ * own accessible contexts.
+ *
+ * @return the accessible context of this object, <code>this</code>
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the background color for this list child. This returns the
+ * background of the <code>JList</code> itself since the background
+ * cannot be set on list children individually
+ *
+ * @return the background color for this list child
+ */
+ public Color getBackground()
+ {
+ return parent.getBackground();
+ }
+
+ /**
+ * Calling this method has no effect, since the background color cannot be
+ * set on list children individually.
+ *
+ * @param color not used here.
+ */
+ public void setBackground(Color color)
+ {
+ // Calling this method has no effect, since the background color cannot
+ // be set on list children individually.
+ }
+
+ /**
+ * Returns the foreground color for this list child. This returns the
+ * background of the <code>JList</code> itself since the foreground
+ * cannot be set on list children individually.
+ *
+ * @return the background color for this list child
+ */
+ public Color getForeground()
+ {
+ return parent.getForeground();
+ }
+
+ /**
+ * Calling this method has no effect, since the foreground color cannot be
+ * set on list children individually.
+ *
+ * @param color not used here.
+ */
+ public void setForeground(Color color)
+ {
+ // Calling this method has no effect, since the foreground color cannot
+ // be set on list children individually.
+ }
+
+ /**
+ * Returns the cursor for this list child.
+ *
+ * @return the cursor for this list child
+ */
+ public Cursor getCursor()
+ {
+ // TODO: Testcases show that this method returns the cursor that has
+ // been set by setCursor. I cannot make up though how that could affect
+ // the actual list.
+ return cursor;
+ }
+
+ /**
+ * Sets the cursor for this list child.
+ */
+ public void setCursor(Cursor cursor)
+ {
+ this.cursor = cursor;
+ // TODO: Testcases show that this method returns the cursor that has
+ // been set by setCursor. I cannot make up though how that could affect
+ // the actual list.
+ }
+
+ /**
+ * Returns the font of the <code>JList</code> since it is not possible to
+ * set fonts for list children individually.
+ *
+ * @return the font of the <code>JList</code>
+ */
+ public Font getFont()
+ {
+ return parent.getFont();
+ }
+
+ /**
+ * Does nothing since it is not possible to set the font on list children
+ * individually.
+ *
+ * @param font not used here
+ */
+ public void setFont(Font font)
+ {
+ // Does nothing since it is not possible to set the font on list
+ // children individually.
+ }
+
+ /**
+ * Returns the font metrics for the specified font. This method forwards
+ * to the parent <code>JList</code>.
+ *
+ * @param font the font for which the font metrics is queried
+ *
+ * @return the font metrics for the specified font
+ */
+ public FontMetrics getFontMetrics(Font font)
+ {
+ return parent.getFontMetrics(font);
+ }
+
+ /**
+ * Returns <code>true</code> if the parent <code>JList</code> is enabled,
+ * <code>false</code> otherwise. The list children cannot have an enabled
+ * flag set individually.
+ *
+ * @return <code>true</code> if the parent <code>JList</code> is enabled,
+ * <code>false</code> otherwise
+ */
+ public boolean isEnabled()
+ {
+ return parent.isEnabled();
+ }
+
+ /**
+ * Does nothing since the enabled flag cannot be set for list children
+ * individually.
+ *
+ * @param b not used here
+ */
+ public void setEnabled(boolean b)
+ {
+ // Does nothing since the enabled flag cannot be set for list children
+ // individually.
+ }
+
+ /**
+ * Returns <code>true</code> if this list child is visible,
+ * <code>false</code> otherwise. The value of this property depends
+ * on {@link JList#getFirstVisibleIndex()} and
+ * {@link JList#getLastVisibleIndex()}.
+ *
+ * @return <code>true</code> if this list child is visible,
+ * <code>false</code> otherwise
+ */
+ public boolean isVisible()
+ {
+ return listIndex >= parent.getFirstVisibleIndex()
+ && listIndex <= parent.getLastVisibleIndex();
+ }
+
+ /**
+ * The value of the visible property cannot be modified, so this method
+ * does nothing.
+ *
+ * @param b not used here
+ */
+ public void setVisible(boolean b)
+ {
+ // The value of the visible property cannot be modified, so this method
+ // does nothing.
+ }
+
+ /**
+ * Returns <code>true</code> if this list child is currently showing on
+ * screen and <code>false</code> otherwise. The list child is showing if
+ * it is visible and if it's parent JList is currently showing.
+ *
+ * @return <code>true</code> if this list child is currently showing on
+ * screen and <code>false</code> otherwise
+ */
+ public boolean isShowing()
+ {
+ return isVisible() && parent.isShowing();
+ }
+
+ /**
+ * Returns <code>true</code> if this list child covers the screen location
+ * <code>point</code> (relative to the <code>JList</code> coordinate
+ * system, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if this list child covers the screen location
+ * <code>point</code> , <code>false</code> otherwise
+ */
+ public boolean contains(Point point)
+ {
+ return getBounds().contains(point);
+ }
+
+ /**
+ * Returns the absolute screen location of this list child.
+ *
+ * @return the absolute screen location of this list child
+ */
+ public Point getLocationOnScreen()
+ {
+ Point loc = getLocation();
+ SwingUtilities.convertPointToScreen(loc, parent);
+ return loc;
+ }
+
+ /**
+ * Returns the screen location of this list child relative to it's parent.
+ *
+ * @return the location of this list child relative to it's parent
+ *
+ * @see JList#indexToLocation(int)
+ */
+ public Point getLocation()
+ {
+ return parent.indexToLocation(listIndex);
+ }
+
+ /**
+ * Does nothing since the screen location cannot be set on list children
+ * explictitly.
+ *
+ * @param point not used here
+ */
+ public void setLocation(Point point)
+ {
+ // Does nothing since the screen location cannot be set on list children
+ // explictitly.
+ }
+
+ /**
+ * Returns the bounds of this list child.
+ *
+ * @return the bounds of this list child
+ *
+ * @see JList#getCellBounds(int, int)
+ */
+ public Rectangle getBounds()
+ {
+ return parent.getCellBounds(listIndex, listIndex);
+ }
+
+ /**
+ * Does nothing since the bounds cannot be set on list children
+ * individually.
+ *
+ * @param rectangle not used here
+ */
+ public void setBounds(Rectangle rectangle)
+ {
+ // Does nothing since the bounds cannot be set on list children
+ // individually.
+ }
+
+ /**
+ * Returns the size of this list child.
+ *
+ * @return the size of this list child
+ */
+ public Dimension getSize()
+ {
+ Rectangle b = getBounds();
+ return b.getSize();
+ }
+
+ /**
+ * Does nothing since the size cannot be set on list children
+ * individually.
+ *
+ * @param dimension not used here
+ */
+ public void setSize(Dimension dimension)
+ {
+ // Does nothing since the size cannot be set on list children
+ // individually.
+ }
+
+ /**
+ * Returns <code>null</code> because list children do not have children
+ * themselves
+ *
+ * @return <code>null</code>
+ */
+ public Accessible getAccessibleAt(Point point)
+ {
+ return null;
+ }
+
+ /**
+ * Returns <code>true</code> since list children are focus traversable.
+ *
+ * @return true
+ */
+ public boolean isFocusTraversable()
+ {
+ // TODO: Is this 100% ok?
+ return true;
+ }
+
+ /**
+ * Requests focus on the parent list. List children cannot request focus
+ * individually.
+ */
+ public void requestFocus()
+ {
+ // TODO: Is this 100% ok?
+ parent.requestFocus();
+ }
+
+ /**
+ * Adds a focus listener to the parent list. List children do not have
+ * their own focus management.
+ *
+ * @param listener the focus listener to add
+ */
+ public void addFocusListener(FocusListener listener)
+ {
+ // TODO: Is this 100% ok?
+ parent.addFocusListener(listener);
+ }
+
+ /**
+ * Removes a focus listener from the parent list. List children do not
+ * have their own focus management.
+ *
+ * @param listener the focus listener to remove
+ */
+ public void removeFocusListener(FocusListener listener)
+ {
+ // TODO: Is this 100%
+ parent.removeFocusListener(listener);
+ }
+
+ /**
+ * Returns the accessible role of this list item, which is
+ * {@link AccessibleRole#LABEL}.
+ *
+ * @return {@link AccessibleRole#LABEL}
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.LABEL;
+ }
+
+ /**
+ * Returns the accessible state set of this list item.
+ *
+ * @return the accessible state set of this list item
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet states = new AccessibleStateSet();
+ if (isVisible())
+ states.add(AccessibleState.VISIBLE);
+ if (isShowing())
+ states.add(AccessibleState.SHOWING);
+ if (isFocusTraversable())
+ states.add(AccessibleState.FOCUSABLE);
+ // TODO: How should the active state be handled? The API docs
+ // suggest that this state is set on the activated list child,
+ // that is the one that is drawn with a box. However, I don't know how
+ // to implement this.
+
+ // TODO: We set the selectable state here because list children are
+ // selectable. Is there a way to disable single children?
+ if (parent.isEnabled())
+ states.add(AccessibleState.SELECTABLE);
+
+ if (parent.isSelectedIndex(listIndex))
+ states.add(AccessibleState.SELECTED);
+
+ // TODO: Handle more states here?
+ return states;
+ }
+
+ /**
+ * Returns the index of this list child within it's parent list.
+ *
+ * @return the index of this list child within it's parent list
+ */
+ public int getAccessibleIndexInParent()
+ {
+ return listIndex;
+ }
+
+ /**
+ * Returns <code>0</code> since list children don't have children
+ * themselves.
+ *
+ * @return <code>0</code>
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns <code>null</code> since list children don't have children
+ * themselves.
+ *
+ * @return <code>null</code>
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the locale of this component. This call is forwarded to the
+ * parent list since list children don't have a separate locale setting.
+ *
+ * @return the locale of this component
+ */
+ public Locale getLocale()
+ {
+ return parent.getLocale();
+ }
+
+ /**
+ * This method does
+ * nothing, list children are transient accessible objects which means
+ * that they don't fire property change events.
+ *
+ * @param l not used here
+ */
+ public void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * This method does
+ * nothing, list children are transient accessible objects which means
+ * that they don't fire property change events.
+ *
+ * @param l not used here
+ */
+ public void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ // Do nothing here.
+ }
+
+ // TODO: Implement the remaining methods of this class.
+ }
+
+ /**
+ * Create a new AccessibleJList.
+ */
+ public AccessibleJList()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the number of selected accessible children.
+ *
+ * @return the number of selected accessible children
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return getSelectedIndices().length;
+ }
+
+ /**
+ * Returns the n-th selected accessible child.
+ *
+ * @param n the index of the selected child to return
+ *
+ * @return the n-th selected accessible child
+ */
+ public Accessible getAccessibleSelection(int n)
+ {
+ return new AccessibleJListChild(JList.this, getSelectedIndices()[n]);
+ }
+
+ /**
+ * Returns <code>true</code> if the n-th child is selected,
+ * <code>false</code> otherwise.
+ *
+ * @param n the index of the child of which the selected state is queried
+ *
+ * @return <code>true</code> if the n-th child is selected,
+ * <code>false</code> otherwise
+ */
+ public boolean isAccessibleChildSelected(int n)
+ {
+ return isSelectedIndex(n);
+ }
+
+ /**
+ * Adds the accessible item with the specified index to the selected items.
+ * If multiple selections are supported, the item is added to the selection,
+ * otherwise the item replaces the current selection.
+ *
+ * @param i the index of the item to add to the selection
+ */
+ public void addAccessibleSelection(int i)
+ {
+ addSelectionInterval(i, i);
+ }
+
+ /**
+ * Removes the accessible item with the specified index to the selection.
+ *
+ * @param i the index of the item to be removed from the selection
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ removeSelectionInterval(i, i);
+ }
+
+ /**
+ * Remove all selection items from the selection.
+ */
+ public void clearAccessibleSelection()
+ {
+ clearSelection();
+ }
+
+ /**
+ * Selects all items if multiple selections are supported.
+ * Otherwise do nothing.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ addSelectionInterval(0, getModel().getSize());
+ }
+
+ /**
+ * Receices notification when the list selection is changed. This method
+ * fires two property change events, the first with
+ * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY} and the second
+ * with {@link AccessibleContext#ACCESSIBLE_SELECTION_PROPERTY}.
+ *
+ * @param event the list selection event
+ */
+ public void valueChanged(ListSelectionEvent event)
+ {
+ firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ }
+
+ /**
+ * Receives notification when items have changed in the
+ * <code>JList</code>. This method fires a property change event with
+ * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+ *
+ * @param event the list data event
+ */
+ public void contentsChanged(ListDataEvent event)
+ {
+ firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ }
+
+ /**
+ * Receives notification when items are inserted into the
+ * <code>JList</code>. This method fires a property change event with
+ * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+ *
+ * @param event the list data event
+ */
+ public void intervalAdded(ListDataEvent event)
+ {
+ firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ }
+
+ /**
+ * Receives notification when items are removed from the
+ * <code>JList</code>. This method fires a property change event with
+ * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+ *
+ * @param event the list data event
+ */
+ public void intervalRemoved(ListDataEvent event)
+ {
+ firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ }
+
+
+ /**
+ * Receives notification about changes of the <code>JList</code>'s
+ * properties. This is used to re-register this object as listener to
+ * the data model and selection model when the data model or selection model
+ * changes.
+ *
+ * @param e the property change event
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String propertyName = e.getPropertyName();
+ if (propertyName.equals("model"))
+ {
+ ListModel oldModel = (ListModel) e.getOldValue();
+ oldModel.removeListDataListener(this);
+ ListModel newModel = (ListModel) e.getNewValue();
+ newModel.addListDataListener(this);
+ }
+ else if (propertyName.equals("selectionModel"))
+ {
+ ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue();
+ oldModel.removeListSelectionListener(this);
+ ListSelectionModel newModel = (ListSelectionModel) e.getNewValue();
+ oldModel.addListSelectionListener(this);
+ }
+ }
+
+ /**
+ * Return the state set of the <code>JList</code>.
+ *
+ * @return the state set of the <code>JList</code>
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // TODO: Figure out if there is possibly more state that must be
+ // handled here.
+ AccessibleStateSet s = super.getAccessibleStateSet();
+ if (getSelectionMode() != ListSelectionModel.SINGLE_SELECTION)
+ s.add(AccessibleState.MULTISELECTABLE);
+ return s;
+ }
+
+ /**
+ * Returns the accessible role for <code>JList</code>,
+ * {@link AccessibleRole#LIST}.
+ *
+ * @return the accessible role for <code>JList</code>
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.LIST;
+ }
+
+ /**
+ * Returns the accessible child at the visual location <code>p</code>
+ * (relative to the upper left corner of the <code>JList</code>). If there
+ * is no child at that location, this returns <code>null</code>.
+ *
+ * @param p the screen location for which to return the accessible child
+ *
+ * @return the accessible child at the specified location, or
+ * <code>null</code> if there is no child at that location
+ */
+ public Accessible getAccessibleAt(Point p)
+ {
+ int childIndex = locationToIndex(p);
+ return getAccessibleChild(childIndex);
+ }
+
+ /**
+ * Returns the number of accessible children in the <code>JList</code>.
+ *
+ * @return the number of accessible children in the <code>JList</code>
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return getModel().getSize();
+ }
+
+ /**
+ * Returns the n-th accessible child of this <code>JList</code>. This will
+ * be an instance of {@link AccessibleJListChild}. If there is no child
+ * at that index, <code>null</code> is returned.
+ *
+ * @param n the index of the child to return
+ *
+ * @return the n-th accessible child of this <code>JList</code>
+ */
+ public Accessible getAccessibleChild(int n)
+ {
+ if (getModel().getSize() <= n)
+ return null;
+ return new AccessibleJListChild(JList.this, n);
+ }
+ }
+
+ private static final long serialVersionUID = 4406629526391098046L;
+
+ /**
+ * Constant value used in "layoutOrientation" property. This value means
+ * that cells are laid out in a single vertical column. This is the default.
+ */
+ public static final int VERTICAL = 0;
+
+ /**
+ * Constant value used in "layoutOrientation" property. This value means
+ * that cells are laid out in multiple columns "newspaper style", filling
+ * vertically first, then horizontally.
+ */
+ public static final int VERTICAL_WRAP = 1;
+
+ /**
+ * Constant value used in "layoutOrientation" property. This value means
+ * that cells are laid out in multiple columns "newspaper style",
+ * filling horizontally first, then vertically.
+ */
+ public static final int HORIZONTAL_WRAP = 2;
+
+ /**
+ * This property indicates whether "drag and drop" functions are enabled
+ * on the list.
+ */
+ boolean dragEnabled;
+
+ /** This property provides a strategy for rendering cells in the list. */
+ ListCellRenderer cellRenderer;
+
+ /**
+ * This property indicates an fixed width to assign to all cells in the
+ * list. If its value is <code>-1</code>, no width has been
+ * assigned. This value can be set explicitly, or implicitly by setting
+ * the {@link #prototypeCellValue} property.
+ */
+ int fixedCellWidth;
+
+ /**
+ * This property indicates an fixed height to assign to all cells in the
+ * list. If its value is <code>-1</code>, no height has been
+ * assigned. This value can be set explicitly, or implicitly by setting
+ * the {@link #prototypeCellValue} property.
+ */
+ int fixedCellHeight;
+
+ /**
+ * This property holds the current layout orientation of the list, which
+ * is one of the integer constants {@link #VERTICAL}, {@link
+ * #VERTICAL_WRAP}, or {@link #HORIZONTAL_WRAP}.
+ */
+ int layoutOrientation;
+
+ /** This property holds the data elements displayed by the list. */
+ ListModel model;
+
+ /**
+ * <p>This property holds a reference to a "prototype" data value --
+ * typically a String -- which is used to calculate the {@link
+ * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
+ * {@link #cellRenderer} property to acquire a component to render the
+ * prototype.</p>
+ *
+ * <p>It is important that you <em>not</em> set this value to a
+ * component. It has to be a <em>data value</em> such as the objects you
+ * would find in the list's model. Setting it to a component will have
+ * undefined (and undesirable) affects. </p>
+ */
+ Object prototypeCellValue;
+
+ /**
+ * This property specifies a foreground color for the selected cells in
+ * the list. When {@link ListCellRenderer#getListCellRendererComponent}
+ * is called with a selected cell object, the component returned will
+ * have its "foreground" set to this color.
+ */
+ Color selectionBackground;
+
+ /**
+ * This property specifies a background color for the selected cells in
+ * the list. When {@link ListCellRenderer#getListCellRendererComponent}
+ * is called with a selected cell object, the component returned will
+ * have its "background" property set to this color.
+ */
+ Color selectionForeground;
+
+ /**
+ * This property holds a description of which data elements in the {@link
+ * #model} property should be considered "selected", when displaying and
+ * interacting with the list.
+ */
+ ListSelectionModel selectionModel;
+
+ /**
+ * This property indicates a <em>preference</em> for the number of rows
+ * displayed in the list, and will scale the
+ * {@link #getPreferredScrollableViewportSize} property accordingly. The actual
+ * number of displayed rows, when the list is placed in a real {@link
+ * JViewport} or other component, may be greater or less than this number.
+ */
+ int visibleRowCount;
+
+ /**
+ * Fire a {@link ListSelectionEvent} to all the registered
+ * ListSelectionListeners.
+ *
+ * @param firstIndex the lowest index covering the selection change.
+ * @param lastIndex the highest index covering the selection change.
+ * @param isAdjusting a flag indicating if this event is one in a series
+ * of events updating the selection.
+ */
+ protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
+ boolean isAdjusting)
+ {
+ ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex,
+ lastIndex, isAdjusting);
+ ListSelectionListener listeners[] = getListSelectionListeners();
+ for (int i = 0; i < listeners.length; ++i)
+ {
+ listeners[i].valueChanged(evt);
+ }
+ }
+
+ /**
+ * This private listener propagates {@link ListSelectionEvent} events
+ * from the list's "selectionModel" property to the list's {@link
+ * ListSelectionListener} listeners. It also listens to {@link
+ * ListDataEvent} events from the list's {@link #model} property. If this
+ * class receives either type of event, it triggers repainting of the
+ * list.
+ */
+ private class ListListener
+ implements ListSelectionListener, ListDataListener
+ {
+ // ListDataListener events
+ public void contentsChanged(ListDataEvent event)
+ {
+ JList.this.revalidate();
+ JList.this.repaint();
+ }
+ public void intervalAdded(ListDataEvent event)
+ {
+ JList.this.revalidate();
+ JList.this.repaint();
+ }
+ public void intervalRemoved(ListDataEvent event)
+ {
+ JList.this.revalidate();
+ JList.this.repaint();
+ }
+ // ListSelectionListener events
+ public void valueChanged(ListSelectionEvent event)
+ {
+ JList.this.fireSelectionValueChanged(event.getFirstIndex(),
+ event.getLastIndex(),
+ event.getValueIsAdjusting());
+ JList.this.repaint();
+ }
+ }
+
+ /**
+ * Shared ListListener instance, subscribed to both the current {@link
+ * #model} and {@link #selectionModel} properties of the list.
+ */
+ ListListener listListener;
+
+
+ /**
+ * Creates a new <code>JList</code> object.
+ */
+ public JList()
+ {
+ init(new DefaultListModel());
+ }
+
+ /**
+ * Creates a new <code>JList</code> object.
+ *
+ * @param items the initial list items.
+ */
+ public JList(Object[] items)
+ {
+ init(createListModel(items));
+ }
+
+ /**
+ * Creates a new <code>JList</code> object.
+ *
+ * @param items the initial list items.
+ */
+ public JList(Vector<?> items)
+ {
+ init(createListModel(items));
+ }
+
+ /**
+ * Creates a new <code>JList</code> object.
+ *
+ * @param model a model containing the list items (<code>null</code> not
+ * permitted).
+ *
+ * @throws IllegalArgumentException if <code>model</code> is
+ * <code>null</code>.
+ */
+ public JList(ListModel model)
+ {
+ init(model);
+ }
+
+ /**
+ * Initializes the list.
+ *
+ * @param m the list model (<code>null</code> not permitted).
+ */
+ private void init(ListModel m)
+ {
+ if (m == null)
+ throw new IllegalArgumentException("Null model not permitted.");
+ dragEnabled = false;
+ fixedCellHeight = -1;
+ fixedCellWidth = -1;
+ layoutOrientation = VERTICAL;
+ opaque = true;
+ visibleRowCount = 8;
+
+ cellRenderer = new DefaultListCellRenderer();
+ listListener = new ListListener();
+
+ model = m;
+ if (model != null)
+ model.addListDataListener(listListener);
+
+ selectionModel = createSelectionModel();
+ if (selectionModel != null)
+ {
+ selectionModel.addListSelectionListener(listListener);
+ selectionModel.setSelectionMode
+ (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ }
+ setLayout(null);
+
+ updateUI();
+ }
+
+ /**
+ * Creates the default <code>ListSelectionModel</code>.
+ *
+ * @return the <code>ListSelectionModel</code>
+ */
+ protected ListSelectionModel createSelectionModel()
+ {
+ return new DefaultListSelectionModel();
+ }
+
+ /**
+ * Gets the value of the {@link #fixedCellHeight} property. This property
+ * may be <code>-1</code> to indicate that no cell height has been
+ * set. This property is also set implicitly when the
+ * {@link #prototypeCellValue} property is set.
+ *
+ * @return The current value of the property
+ *
+ * @see #fixedCellHeight
+ * @see #setFixedCellHeight
+ * @see #setPrototypeCellValue
+ */
+ public int getFixedCellHeight()
+ {
+ return fixedCellHeight;
+ }
+
+ /**
+ * Sets the value of the {@link #fixedCellHeight} property. This property
+ * may be <code>-1</code> to indicate that no cell height has been
+ * set. This property is also set implicitly when the {@link
+ * #prototypeCellValue} property is set, but setting it explicitly
+ * overrides the height computed from {@link #prototypeCellValue}.
+ *
+ * @param h the height.
+ *
+ * @see #getFixedCellHeight
+ * @see #getPrototypeCellValue
+ */
+ public void setFixedCellHeight(int h)
+ {
+ if (fixedCellHeight == h)
+ return;
+
+ int old = fixedCellHeight;
+ fixedCellHeight = h;
+ firePropertyChange("fixedCellHeight", old, h);
+ }
+
+
+ /**
+ * Gets the value of the {@link #fixedCellWidth} property. This property
+ * may be <code>-1</code> to indicate that no cell width has been
+ * set. This property is also set implicitly when the {@link
+ * #prototypeCellValue} property is set.
+ *
+ * @return The current value of the property
+ *
+ * @see #setFixedCellWidth
+ * @see #setPrototypeCellValue
+ */
+ public int getFixedCellWidth()
+ {
+ return fixedCellWidth;
+ }
+
+ /**
+ * Sets the value of the {@link #fixedCellWidth} property. This property
+ * may be <code>-1</code> to indicate that no cell width has been
+ * set. This property is also set implicitly when the {@link
+ * #prototypeCellValue} property is set, but setting it explicitly
+ * overrides the width computed from {@link #prototypeCellValue}.
+ *
+ * @param w the width.
+ *
+ * @see #getFixedCellHeight
+ * @see #getPrototypeCellValue
+ */
+ public void setFixedCellWidth(int w)
+ {
+ if (fixedCellWidth == w)
+ return;
+
+ int old = fixedCellWidth;
+ fixedCellWidth = w;
+ firePropertyChange("fixedCellWidth", old, w);
+ }
+
+ /**
+ * Gets the value of the {@link #visibleRowCount} property. The default
+ * value is 8.
+ *
+ * @return the current value of the property.
+ *
+ * @see #setVisibleRowCount(int)
+ */
+ public int getVisibleRowCount()
+ {
+ return visibleRowCount;
+ }
+
+ /**
+ * Sets the value of the {@link #visibleRowCount} property.
+ *
+ * @param vc The new property value
+ *
+ * @see #getVisibleRowCount()
+ */
+ public void setVisibleRowCount(int vc)
+ {
+ if (visibleRowCount != vc)
+ {
+ int oldValue = visibleRowCount;
+ visibleRowCount = Math.max(vc, 0);
+ firePropertyChange("visibleRowCount", oldValue, vc);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Adds a {@link ListSelectionListener} to the listener list for this
+ * list. The listener will be called back with a {@link
+ * ListSelectionEvent} any time the list's {@link #selectionModel}
+ * property changes. The source of such events will be the JList,
+ * not the selection model.
+ *
+ * @param listener The new listener to add
+ */
+ public void addListSelectionListener(ListSelectionListener listener)
+ {
+ listenerList.add (ListSelectionListener.class, listener);
+ }
+
+ /**
+ * Removes a {@link ListSelectionListener} from the listener list for
+ * this list. The listener will no longer be called when the list's
+ * {@link #selectionModel} changes.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeListSelectionListener(ListSelectionListener listener)
+ {
+ listenerList.remove(ListSelectionListener.class, listener);
+ }
+
+ /**
+ * Returns an array of all ListSelectionListeners subscribed to this
+ * list.
+ *
+ * @return The current subscribed listeners
+ *
+ * @since 1.4
+ */
+ public ListSelectionListener[] getListSelectionListeners()
+ {
+ return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
+ }
+
+ /**
+ * Returns the selection mode for the list (one of:
+ * {@link ListSelectionModel#SINGLE_SELECTION},
+ * {@link ListSelectionModel#SINGLE_INTERVAL_SELECTION} and
+ * {@link ListSelectionModel#MULTIPLE_INTERVAL_SELECTION}).
+ *
+ * @return The selection mode.
+ *
+ * @see #setSelectionMode(int)
+ */
+ public int getSelectionMode()
+ {
+ return selectionModel.getSelectionMode();
+ }
+
+ /**
+ * Sets the list's "selectionMode" property, which simply mirrors the
+ * same property on the list's {@link #selectionModel} property. This
+ * property should be one of the integer constants
+ * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code>,
+ * or <code>MULTIPLE_INTERVAL_SELECTION</code> from the {@link
+ * ListSelectionModel} interface.
+ *
+ * @param a The new selection mode
+ */
+ public void setSelectionMode(int a)
+ {
+ selectionModel.setSelectionMode(a);
+ }
+
+ /**
+ * Adds the interval <code>[a,a]</code> to the set of selections managed
+ * by this list's {@link #selectionModel} property. Depending on the
+ * selection mode, this may cause existing selections to become invalid,
+ * or may simply expand the set of selections.
+ *
+ * @param a A number in the half-open range <code>[0, x)</code> where
+ * <code>x = getModel.getSize()</code>, indicating the index of an
+ * element in the list to select. When &lt; 0 the selection is cleared.
+ *
+ * @see #setSelectionMode
+ * @see #selectionModel
+ */
+ public void setSelectedIndex(int a)
+ {
+ if (a < 0)
+ selectionModel.clearSelection();
+ else
+ selectionModel.setSelectionInterval(a, a);
+ }
+
+ /**
+ * For each element <code>a[i]</code> of the provided array
+ * <code>a</code>, calls {@link #setSelectedIndex} on <code>a[i]</code>.
+ *
+ * @param a an array of selected indices (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ * @see #setSelectionMode
+ * @see #selectionModel
+ */
+ public void setSelectedIndices(int [] a)
+ {
+ for (int i = 0; i < a.length; ++i)
+ setSelectedIndex(a[i]);
+ }
+
+ /**
+ * Returns the minimum index of an element in the list which is currently
+ * selected.
+ *
+ * @return A number in the half-open range <code>[0, x)</code> where
+ * <code>x = getModel.getSize()</code>, indicating the minimum index of
+ * an element in the list for which the element is selected, or
+ * <code>-1</code> if no elements are selected
+ */
+ public int getSelectedIndex()
+ {
+ return selectionModel.getMinSelectionIndex();
+ }
+
+ /**
+ * Returns <code>true</code> if the model's selection is empty, otherwise
+ * <code>false</code>.
+ *
+ * @return The return value of {@link ListSelectionModel#isSelectionEmpty}
+ */
+ public boolean isSelectionEmpty()
+ {
+ return selectionModel.isSelectionEmpty();
+ }
+
+ /**
+ * Returns the list index of the upper left or upper right corner of the
+ * visible rectangle of this list, depending on the {@link
+ * Component#getComponentOrientation} property.
+ *
+ * @return The index of the first visible list cell, or <code>-1</code>
+ * if none is visible.
+ */
+ public int getFirstVisibleIndex()
+ {
+ ComponentOrientation or = getComponentOrientation();
+ Rectangle r = getVisibleRect();
+ if (or == ComponentOrientation.RIGHT_TO_LEFT)
+ r.translate((int) r.getWidth() - 1, 0);
+ return getUI().locationToIndex(this, r.getLocation());
+ }
+
+
+ /**
+ * Returns index of the cell to which specified location is closest to. If
+ * the location is outside the bounds of the list, then the greatest index
+ * in the list model is returned. If the list model is empty, then
+ * <code>-1</code> is returned.
+ *
+ * @param location for which to look for in the list
+ *
+ * @return index of the cell to which specified location is closest to.
+ */
+ public int locationToIndex(Point location)
+ {
+ return getUI().locationToIndex(this, location);
+ }
+
+ /**
+ * Returns location of the cell located at the specified index in the list.
+ * @param index of the cell for which location will be determined
+ *
+ * @return location of the cell located at the specified index in the list.
+ */
+ public Point indexToLocation(int index)
+ {
+ return getUI().indexToLocation(this, index);
+ }
+
+ /**
+ * Returns the list index of the lower right or lower left corner of the
+ * visible rectangle of this list, depending on the {@link
+ * Component#getComponentOrientation} property.
+ *
+ * @return The index of the last visible list cell, or <code>-1</code>
+ * if none is visible.
+ */
+ public int getLastVisibleIndex()
+ {
+ ComponentOrientation or = getComponentOrientation();
+ Rectangle r = getVisibleRect();
+ r.translate(0, (int) r.getHeight() - 1);
+ if (or == ComponentOrientation.LEFT_TO_RIGHT)
+ r.translate((int) r.getWidth() - 1, 0);
+ if (getUI().locationToIndex(this, r.getLocation()) == -1
+ && indexToLocation(getModel().getSize() - 1).y < r.y)
+ return getModel().getSize() - 1;
+ return getUI().locationToIndex(this, r.getLocation());
+ }
+
+ /**
+ * Returns the indices of values in the {@link #model} property which are
+ * selected.
+ *
+ * @return An array of model indices, each of which is selected according
+ * to the {@link #getSelectedValues} property
+ */
+ public int[] getSelectedIndices()
+ {
+ int lo, hi, n, i, j;
+ if (selectionModel.isSelectionEmpty())
+ return new int[0];
+ lo = selectionModel.getMinSelectionIndex();
+ hi = selectionModel.getMaxSelectionIndex();
+ n = 0;
+ for (i = lo; i <= hi; ++i)
+ if (selectionModel.isSelectedIndex(i))
+ n++;
+ int [] v = new int[n];
+ j = 0;
+ for (i = lo; i <= hi; ++i)
+ if (selectionModel.isSelectedIndex(i))
+ v[j++] = i;
+ return v;
+ }
+
+ /**
+ * Indicates whether the list element at a given index value is
+ * currently selected.
+ *
+ * @param a The index to check
+ * @return <code>true</code> if <code>a</code> is the index of a selected
+ * list element
+ */
+ public boolean isSelectedIndex(int a)
+ {
+ return selectionModel.isSelectedIndex(a);
+ }
+
+ /**
+ * Returns the first value in the list's {@link #model} property which is
+ * selected, according to the list's {@link #selectionModel} property.
+ * This is equivalent to calling
+ * <code>getModel()getElementAt(getSelectedIndex())</code>, with a check
+ * for the special index value of <code>-1</code> which returns null
+ * <code>null</code>.
+ *
+ * @return The first selected element, or <code>null</code> if no element
+ * is selected.
+ *
+ * @see #getSelectedValues
+ */
+ public Object getSelectedValue()
+ {
+ int index = getSelectedIndex();
+ if (index == -1)
+ return null;
+ return getModel().getElementAt(index);
+ }
+
+ /**
+ * Returns all the values in the list's {@link #model} property which are
+ * selected, according to the list's {@link #selectionModel} property.
+ *
+ * @return An array containing all the selected values
+ * @see #setSelectedValue
+ */
+ public Object[] getSelectedValues()
+ {
+ int[] idx = getSelectedIndices();
+ Object[] v = new Object[idx.length];
+ for (int i = 0; i < idx.length; ++i)
+ v[i] = getModel().getElementAt(idx[i]);
+ return v;
+ }
+
+ /**
+ * Gets the value of the {@link #selectionBackground} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getSelectionBackground()
+ {
+ return selectionBackground;
+ }
+
+ /**
+ * Sets the value of the {@link #selectionBackground} property.
+ *
+ * @param c The new value of the property
+ */
+ public void setSelectionBackground(Color c)
+ {
+ if (selectionBackground == c)
+ return;
+
+ Color old = selectionBackground;
+ selectionBackground = c;
+ firePropertyChange("selectionBackground", old, c);
+ repaint();
+ }
+
+ /**
+ * Gets the value of the {@link #selectionForeground} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getSelectionForeground()
+ {
+ return selectionForeground;
+ }
+
+ /**
+ * Sets the value of the {@link #selectionForeground} property.
+ *
+ * @param c The new value of the property
+ */
+ public void setSelectionForeground(Color c)
+ {
+ if (selectionForeground == c)
+ return;
+
+ Color old = selectionForeground;
+ selectionForeground = c;
+ firePropertyChange("selectionForeground", old, c);
+ }
+
+ /**
+ * Sets the selection to cover only the specified value, if it
+ * exists in the model.
+ *
+ * @param obj The object to select
+ * @param scroll Whether to scroll the list to make the newly selected
+ * value visible
+ *
+ * @see #ensureIndexIsVisible
+ */
+
+ public void setSelectedValue(Object obj, boolean scroll)
+ {
+ for (int i = 0; i < model.getSize(); ++i)
+ {
+ if (model.getElementAt(i).equals(obj))
+ {
+ setSelectedIndex(i);
+ if (scroll)
+ ensureIndexIsVisible(i);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Scrolls this list to make the specified cell visible. This
+ * only works if the list is contained within a viewport.
+ *
+ * @param i The list index to make visible
+ *
+ * @see JComponent#scrollRectToVisible
+ */
+ public void ensureIndexIsVisible(int i)
+ {
+ Rectangle r = getUI().getCellBounds(this, i, i);
+ if (r != null)
+ scrollRectToVisible(r);
+ }
+
+ /**
+ * Sets the {@link #model} property of the list to a new anonymous
+ * {@link AbstractListModel} subclass which accesses the provided Object
+ * array directly.
+ *
+ * @param listData The object array to build a new list model on
+ * @see #setModel
+ */
+ public void setListData(Object[] listData)
+ {
+ setModel(createListModel(listData));
+ }
+
+ /**
+ * Returns a {@link ListModel} backed by the specified array.
+ *
+ * @param items the list items (don't use <code>null</code>).
+ *
+ * @return A list model containing the specified items.
+ */
+ private ListModel createListModel(final Object[] items)
+ {
+ return new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return items.length;
+ }
+ public Object getElementAt(int i)
+ {
+ return items[i];
+ }
+ };
+ }
+
+ /**
+ * Returns a {@link ListModel} backed by the specified vector.
+ *
+ * @param items the list items (don't use <code>null</code>).
+ *
+ * @return A list model containing the specified items.
+ */
+ private ListModel createListModel(final Vector items)
+ {
+ return new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return items.size();
+ }
+ public Object getElementAt(int i)
+ {
+ return items.get(i);
+ }
+ };
+ }
+
+ /**
+ * Sets the {@link #model} property of the list to a new anonymous {@link
+ * AbstractListModel} subclass which accesses the provided vector
+ * directly.
+ *
+ * @param listData The object array to build a new list model on
+ * @see #setModel
+ */
+ public void setListData(final Vector<?> listData)
+ {
+ setModel(new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return listData.size();
+ }
+
+ public Object getElementAt(int i)
+ {
+ return listData.elementAt(i);
+ }
+ });
+ }
+
+ /**
+ * Gets the value of the {@link #cellRenderer} property.
+ *
+ * @return The current value of the property
+ */
+ public ListCellRenderer getCellRenderer()
+ {
+ return cellRenderer;
+ }
+
+ /**
+ * Sets the value of the {@link #getCellRenderer} property.
+ *
+ * @param renderer The new property value
+ */
+ public void setCellRenderer(ListCellRenderer renderer)
+ {
+ if (cellRenderer == renderer)
+ return;
+
+ ListCellRenderer old = cellRenderer;
+ cellRenderer = renderer;
+ firePropertyChange("cellRenderer", old, renderer);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Gets the value of the {@link #model} property.
+ *
+ * @return The current value of the property
+ */
+ public ListModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Sets the value of the {@link #model} property. The list's {@link
+ * #listListener} is unsubscribed from the existing model, if it exists,
+ * and re-subscribed to the new model.
+ *
+ * @param model the new model (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>model</code> is
+ * <code>null</code>.
+ */
+ public void setModel(ListModel model)
+ {
+ if (model == null)
+ throw new IllegalArgumentException("Null 'model' argument.");
+ if (this.model == model)
+ return;
+
+ if (this.model != null)
+ this.model.removeListDataListener(listListener);
+
+ ListModel old = this.model;
+ this.model = model;
+
+ if (this.model != null)
+ this.model.addListDataListener(listListener);
+
+ firePropertyChange("model", old, model);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Returns the selection model for the {@link JList} component. Note that
+ * this class contains a range of convenience methods for configuring the
+ * selection model:<br>
+ * <ul>
+ * <li>{@link #clearSelection()};</li>
+ * <li>{@link #setSelectionMode(int)};</li>
+ * <li>{@link #addSelectionInterval(int, int)};</li>
+ * <li>{@link #setSelectedIndex(int)};</li>
+ * <li>{@link #setSelectedIndices(int[])};</li>
+ * <li>{@link #setSelectionInterval(int, int)}.</li>
+ * </ul>
+ *
+ * @return The selection model.
+ */
+ public ListSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ /**
+ * Sets the value of the {@link #selectionModel} property. The list's
+ * {@link #listListener} is unsubscribed from the existing selection
+ * model, if it exists, and re-subscribed to the new selection model.
+ *
+ * @param model The new property value
+ */
+ public void setSelectionModel(ListSelectionModel model)
+ {
+ if (selectionModel == model)
+ return;
+
+ if (selectionModel != null)
+ selectionModel.removeListSelectionListener(listListener);
+
+ ListSelectionModel old = selectionModel;
+ selectionModel = model;
+
+ if (selectionModel != null)
+ selectionModel.addListSelectionListener(listListener);
+
+ firePropertyChange("selectionModel", old, model);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Gets the value of the UI property.
+ *
+ * @return The current property value
+ */
+ public ListUI getUI()
+ {
+ return (ListUI) ui;
+ }
+
+ /**
+ * Sets the value of the UI property.
+ *
+ * @param ui The new property value
+ */
+ public void setUI(ListUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Calls {@link #setUI} with the {@link ListUI} subclass
+ * returned from calling {@link UIManager#getUI}.
+ */
+ public void updateUI()
+ {
+ setUI((ListUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Return the class identifier for the list's UI property. This should
+ * be the constant string <code>"ListUI"</code>, and map to an
+ * appropriate UI class in the {@link UIManager}.
+ *
+ * @return The class identifier
+ */
+ public String getUIClassID()
+ {
+ return "ListUI";
+ }
+
+
+ /**
+ * Returns the current value of the {@link #prototypeCellValue}
+ * property. This property holds a reference to a "prototype" data value
+ * -- typically a String -- which is used to calculate the {@link
+ * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
+ * {@link #cellRenderer} property to acquire a component to render the
+ * prototype.
+ *
+ * @return The current prototype cell value
+ * @see #setPrototypeCellValue
+ */
+ public Object getPrototypeCellValue()
+ {
+ return prototypeCellValue;
+ }
+
+ /**
+ * <p>Set the {@link #prototypeCellValue} property. This property holds a
+ * reference to a "prototype" data value -- typically a String -- which
+ * is used to calculate the {@link #fixedCellWidth} and {@link
+ * #fixedCellHeight} properties, using the {@link #cellRenderer} property
+ * to acquire a component to render the prototype.</p>
+ *
+ * <p>It is important that you <em>not</em> set this value to a
+ * component. It has to be a <em>data value</em> such as the objects you
+ * would find in the list's model. Setting it to a component will have
+ * undefined (and undesirable) affects. </p>
+ *
+ * @param obj The new prototype cell value
+ * @see #getPrototypeCellValue
+ */
+ public void setPrototypeCellValue(Object obj)
+ {
+ if (prototypeCellValue == obj)
+ return;
+
+ Object old = prototypeCellValue;
+ Component comp = getCellRenderer()
+ .getListCellRendererComponent(this, obj, 0, false, false);
+ Dimension d = comp.getPreferredSize();
+ fixedCellWidth = d.width;
+ fixedCellHeight = d.height;
+ prototypeCellValue = obj;
+ firePropertyChange("prototypeCellValue", old, obj);
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ return new AccessibleJList();
+ }
+
+ /**
+ * Returns a size indicating how much space this list would like to
+ * consume, when contained in a scrollable viewport. This is part of the
+ * {@link Scrollable} interface, which interacts with {@link
+ * ScrollPaneLayout} and {@link JViewport} to define scrollable objects.
+ *
+ * @return The preferred size
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ //If the layout orientation is not VERTICAL, then this will
+ //return the value from getPreferredSize. The current ListUI is
+ //expected to override getPreferredSize to return an appropriate value.
+ if (getLayoutOrientation() != VERTICAL)
+ return getPreferredSize();
+
+ int size = getModel().getSize();
+
+ // Trivial case: if fixedCellWidth and fixedCellHeight were set
+ // just use them
+ if (fixedCellHeight != -1 && fixedCellWidth != -1)
+ return new Dimension(fixedCellWidth, size * fixedCellHeight);
+
+ // If the model is empty we use 16 * the number of visible rows
+ // for the height and either fixedCellWidth (if set) or 256
+ // for the width
+ if (size == 0)
+ {
+ if (fixedCellWidth == -1)
+ return new Dimension(256, 16 * getVisibleRowCount());
+ else
+ return new Dimension(fixedCellWidth, 16 * getVisibleRowCount());
+ }
+
+ // Calculate the width: if fixedCellWidth was set use that, otherwise
+ // use the preferredWidth
+ int prefWidth;
+ if (fixedCellWidth != -1)
+ prefWidth = fixedCellWidth;
+ else
+ prefWidth = getPreferredSize().width;
+
+ // Calculate the height: if fixedCellHeight was set use that, otherwise
+ // use the height of the first row multiplied by the number of visible
+ // rows
+ int prefHeight;
+ if (fixedCellHeight != -1)
+ prefHeight = fixedCellHeight;
+ else
+ prefHeight = getVisibleRowCount() * getCellBounds(0, 0).height;
+
+ return new Dimension (prefWidth, prefHeight);
+ }
+
+ /**
+ * <p>Return the number of pixels the list must scroll in order to move a
+ * "unit" of the list into the provided visible rectangle. When the
+ * provided direction is positive, the call describes a "downwards"
+ * scroll, which will be exposing a cell at a <em>greater</em> index in
+ * the list than those elements currently showing. Then the provided
+ * direction is negative, the call describes an "upwards" scroll, which
+ * will be exposing a cell at a <em>lesser</em> index in the list than
+ * those elements currently showing.</p>
+ *
+ * <p>If the provided orientation is <code>HORIZONTAL</code>, the above
+ * comments refer to "rightwards" for positive direction, and "leftwards"
+ * for negative.</p>
+ *
+ *
+ * @param visibleRect The rectangle to scroll an element into
+ * @param orientation One of the numeric consants <code>VERTICAL</code>
+ * or <code>HORIZONTAL</code>
+ * @param direction An integer indicating the scroll direction: positive means
+ * forwards (down, right), negative means backwards (up, left)
+ *
+ * @return The scrollable unit increment, in pixels
+ */
+ public int getScrollableUnitIncrement(Rectangle visibleRect,
+ int orientation, int direction)
+ {
+ int unit = -1;
+ if (orientation == SwingConstants.VERTICAL)
+ {
+ int row = getFirstVisibleIndex();
+ if (row == -1)
+ unit = 0;
+ else if (direction > 0)
+ {
+ // Scrolling down.
+ Rectangle bounds = getCellBounds(row, row);
+ if (bounds != null)
+ unit = bounds.height - (visibleRect.y - bounds.y);
+ else
+ unit = 0;
+ }
+ else
+ {
+ // Scrolling up.
+ Rectangle bounds = getCellBounds(row, row);
+ // First row.
+ if (row == 0 && bounds.y == visibleRect.y)
+ unit = 0; // No need to scroll.
+ else if (bounds.y == visibleRect.y)
+ {
+ // Scroll to previous row.
+ Point loc = bounds.getLocation();
+ loc.y--;
+ int prev = locationToIndex(loc);
+ Rectangle prevR = getCellBounds(prev, prev);
+ if (prevR == null || prevR.y >= bounds.y)
+ unit = 0; // For multicolumn lists.
+ else
+ unit = prevR.height;
+ }
+ else
+ unit = visibleRect.y - bounds.y;
+ }
+ }
+ else if (orientation == SwingConstants.HORIZONTAL && getLayoutOrientation() != VERTICAL)
+ {
+ // Horizontal scrolling.
+ int i = locationToIndex(visibleRect.getLocation());
+ if (i != -1)
+ {
+ Rectangle b = getCellBounds(i, i);
+ if (b != null)
+ {
+ if (b.x != visibleRect.x)
+ {
+ if (direction < 0)
+ unit = Math.abs(b.x - visibleRect.x);
+ else
+ unit = b.width + b.x - visibleRect.x;
+ }
+ else
+ unit = b.width;
+ }
+ }
+ }
+
+ if (unit == -1)
+ {
+ // This fallback seems to be used by the RI for the degenerate cases
+ // not covered above.
+ Font f = getFont();
+ unit = f != null ? f.getSize() : 1;
+ }
+ return unit;
+ }
+
+ /**
+ * <p>Return the number of pixels the list must scroll in order to move a
+ * "block" of the list into the provided visible rectangle. When the
+ * provided direction is positive, the call describes a "downwards"
+ * scroll, which will be exposing a cell at a <em>greater</em> index in
+ * the list than those elements currently showing. Then the provided
+ * direction is negative, the call describes an "upwards" scroll, which
+ * will be exposing a cell at a <em>lesser</em> index in the list than
+ * those elements currently showing.</p>
+ *
+ * <p>If the provided orientation is <code>HORIZONTAL</code>, the above
+ * comments refer to "rightwards" for positive direction, and "leftwards"
+ * for negative.</p>
+ *
+ *
+ * @param visibleRect The rectangle to scroll an element into
+ * @param orientation One of the numeric consants <code>VERTICAL</code>
+ * or <code>HORIZONTAL</code>
+ * @param direction An integer indicating the scroll direction: positive means
+ * forwards (down, right), negative means backwards (up, left)
+ *
+ * @return The scrollable unit increment, in pixels
+ */
+ public int getScrollableBlockIncrement(Rectangle visibleRect,
+ int orientation, int direction)
+ {
+ int block = -1;
+ if (orientation == SwingConstants.VERTICAL)
+ {
+ // Default block scroll. Special cases are handled below for
+ // better usability.
+ block = visibleRect.height;
+ if (direction > 0)
+ {
+ // Scroll down.
+ // Scroll so that after scrolling the last line aligns with
+ // the lower boundary of the visible area.
+ Point p = new Point(visibleRect.x,
+ visibleRect.y + visibleRect.height - 1);
+ int last = locationToIndex(p);
+ if (last != -1)
+ {
+ Rectangle lastR = getCellBounds(last, last);
+ if (lastR != null)
+ {
+ block = lastR.y - visibleRect.y;
+ if (block == 0&& last < getModel().getSize() - 1)
+ block = lastR.height;
+ }
+ }
+ }
+ else
+ {
+ // Scroll up.
+ // Scroll so that after scrolling the first line aligns with
+ // the upper boundary of the visible area.
+ Point p = new Point(visibleRect.x,
+ visibleRect.y - visibleRect.height);
+ int newFirst = locationToIndex(p);
+ if (newFirst != -1)
+ {
+ int first = getFirstVisibleIndex();
+ if (first == -1)
+ first = locationToIndex(visibleRect.getLocation());
+ Rectangle newFirstR = getCellBounds(newFirst, newFirst);
+ Rectangle firstR = getCellBounds(first, first);
+ if (newFirstR != null && firstR != null)
+ {
+ // Search first item that would left the current first
+ // item visible when scrolled to.
+ while (newFirstR.y + visibleRect.height
+ < firstR.y + firstR.height
+ && newFirstR.y < firstR.y)
+ {
+ newFirst++;
+ newFirstR = getCellBounds(newFirst, newFirst);
+ }
+ block = visibleRect.y - newFirstR.y;
+ if (block <= 0 && newFirstR.y > 0)
+ {
+ newFirst--;
+ newFirstR = getCellBounds(newFirst, newFirst);
+ if (newFirstR != null)
+ block = visibleRect.y - newFirstR.y;
+ }
+ }
+ }
+ }
+ }
+ else if (orientation == SwingConstants.HORIZONTAL
+ && getLayoutOrientation() != VERTICAL)
+ {
+ // Default block increment. Special cases are handled below for
+ // better usability.
+ block = visibleRect.width;
+ if (direction > 0)
+ {
+ // Scroll right.
+ Point p = new Point(visibleRect.x + visibleRect.width + 1,
+ visibleRect.y);
+ int last = locationToIndex(p);
+ if (last != -1)
+ {
+ Rectangle lastR = getCellBounds(last, last);
+ if (lastR != null)
+ {
+ block = lastR.x - visibleRect.x;
+ if (block < 0)
+ block += lastR.width;
+ else if (block == 0 && last < getModel().getSize() - 1)
+ block = lastR.width;
+ }
+ }
+ }
+ else
+ {
+ // Scroll left.
+ Point p = new Point(visibleRect.x - visibleRect.width,
+ visibleRect.y);
+ int first = locationToIndex(p);
+ if (first != -1)
+ {
+ Rectangle firstR = getCellBounds(first, first);
+ if (firstR != null)
+ {
+ if (firstR.x < visibleRect.x - visibleRect.width)
+ {
+ if (firstR.x + firstR.width > visibleRect.x)
+ block = visibleRect.x - firstR.x;
+ else
+ block = visibleRect.x - firstR.x - firstR.width;
+ }
+ else
+ block = visibleRect.x - firstR.x;
+ }
+ }
+ }
+ }
+
+ return block;
+ }
+
+ /**
+ * Gets the value of the <code>scrollableTracksViewportWidth</code> property.
+ *
+ * @return <code>true</code> if the viewport is larger (horizontally)
+ * than the list and the list should be expanded to fit the viewport;
+ * <code>false</code> if the viewport is smaller than the list and the
+ * list should scroll (horizontally) within the viewport
+ */
+ public boolean getScrollableTracksViewportWidth()
+ {
+ Component parent = getParent();
+ boolean retVal = false;
+ if (parent instanceof JViewport)
+ {
+ JViewport viewport = (JViewport) parent;
+ Dimension pref = getPreferredSize();
+ if (viewport.getSize().width > pref.width)
+ retVal = true;
+ if ((getLayoutOrientation() == HORIZONTAL_WRAP)
+ && (getVisibleRowCount() <= 0))
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * Gets the value of the </code>scrollableTracksViewportWidth</code> property.
+ *
+ * @return <code>true</code> if the viewport is larger (vertically)
+ * than the list and the list should be expanded to fit the viewport;
+ * <code>false</code> if the viewport is smaller than the list and the
+ * list should scroll (vertically) within the viewport
+ */
+ public boolean getScrollableTracksViewportHeight()
+ {
+ Component parent = getParent();
+ boolean retVal = false;
+ if (parent instanceof JViewport)
+ {
+ JViewport viewport = (JViewport) parent;
+ Dimension pref = getPreferredSize();
+ if (viewport.getSize().height > pref.height)
+ retVal = true;
+ if ((getLayoutOrientation() == VERTICAL_WRAP)
+ && (getVisibleRowCount() <= 0))
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the index of the anchor item in the current selection, or
+ * <code>-1</code> if there is no anchor item.
+ *
+ * @return The item index.
+ */
+ public int getAnchorSelectionIndex()
+ {
+ return selectionModel.getAnchorSelectionIndex();
+ }
+
+ /**
+ * Returns the index of the lead item in the current selection, or
+ * <code>-1</code> if there is no lead item.
+ *
+ * @return The item index.
+ */
+ public int getLeadSelectionIndex()
+ {
+ return selectionModel.getLeadSelectionIndex();
+ }
+
+ /**
+ * Returns the lowest item index in the current selection, or <code>-1</code>
+ * if there is no selection.
+ *
+ * @return The index.
+ *
+ * @see #getMaxSelectionIndex()
+ */
+ public int getMinSelectionIndex()
+ {
+ return selectionModel.getMinSelectionIndex();
+ }
+
+ /**
+ * Returns the highest item index in the current selection, or
+ * <code>-1</code> if there is no selection.
+ *
+ * @return The index.
+ *
+ * @see #getMinSelectionIndex()
+ */
+ public int getMaxSelectionIndex()
+ {
+ return selectionModel.getMaxSelectionIndex();
+ }
+
+ /**
+ * Clears the current selection.
+ */
+ public void clearSelection()
+ {
+ selectionModel.clearSelection();
+ }
+
+ /**
+ * Sets the current selection to the items in the specified range (inclusive).
+ * Note that <code>anchor</code> can be less than, equal to, or greater than
+ * <code>lead</code>.
+ *
+ * @param anchor the index of the anchor item.
+ * @param lead the index of the anchor item.
+ */
+ public void setSelectionInterval(int anchor, int lead)
+ {
+ selectionModel.setSelectionInterval(anchor, lead);
+ }
+
+ /**
+ * Adds the specified interval to the current selection. Note that
+ * <code>anchor</code> can be less than, equal to, or greater than
+ * <code>lead</code>.
+ *
+ * @param anchor the index of the anchor item.
+ * @param lead the index of the lead item.
+ */
+ public void addSelectionInterval(int anchor, int lead)
+ {
+ selectionModel.addSelectionInterval(anchor, lead);
+ }
+
+ /**
+ * Removes the specified interval from the current selection. Note that
+ * <code>index0</code> can be less than, equal to, or greater than
+ * <code>index1</code>.
+ *
+ * @param index0 an index for one end of the range.
+ * @param index1 an index for the other end of the range.
+ */
+ public void removeSelectionInterval(int index0, int index1)
+ {
+ selectionModel.removeSelectionInterval(index0, index1);
+ }
+
+ /**
+ * Returns the <code>valueIsAdjusting</code> flag from the list's selection
+ * model.
+ *
+ * @return the value
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return selectionModel.getValueIsAdjusting();
+ }
+
+ /**
+ * Sets the <code>valueIsAdjusting</code> flag in the list's selection
+ * model.
+ *
+ * @param isAdjusting the new value
+ */
+ public void setValueIsAdjusting(boolean isAdjusting)
+ {
+ selectionModel.setValueIsAdjusting(isAdjusting);
+ }
+
+ /**
+ * Return the value of the <code>dragEnabled</code> property.
+ *
+ * @return the value
+ *
+ * @since 1.4
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * Set the <code>dragEnabled</code> property.
+ *
+ * @param enabled new value
+ *
+ * @since 1.4
+ */
+ public void setDragEnabled(boolean enabled)
+ {
+ dragEnabled = enabled;
+ }
+
+ /**
+ * Returns the layout orientation, which will be one of {@link #VERTICAL},
+ * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. The default value
+ * is {@link #VERTICAL}.
+ *
+ * @return the orientation.
+ *
+ * @see #setLayoutOrientation(int)
+ * @since 1.4
+ */
+ public int getLayoutOrientation()
+ {
+ return layoutOrientation;
+ }
+
+ /**
+ * Sets the layout orientation (this is a bound property with the name
+ * 'layoutOrientation'). Valid orientations are {@link #VERTICAL},
+ * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}.
+ *
+ * @param orientation the orientation.
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not one
+ * of the specified values.
+ * @since 1.4
+ * @see #getLayoutOrientation()
+ */
+ public void setLayoutOrientation(int orientation)
+ {
+ if (orientation < JList.VERTICAL || orientation > JList.HORIZONTAL_WRAP)
+ throw new IllegalArgumentException();
+ if (layoutOrientation == orientation)
+ return;
+
+ int old = layoutOrientation;
+ layoutOrientation = orientation;
+ firePropertyChange("layoutOrientation", old, orientation);
+ }
+
+ /**
+ * Returns the bounds of the rectangle that encloses both list cells
+ * with index0 and index1.
+ *
+ * @param index0 the index of the first cell
+ * @param index1 the index of the second cell
+ *
+ * @return the bounds of the rectangle that encloses both list cells
+ * with index0 and index1, <code>null</code> if one of the indices is
+ * not valid
+ */
+ public Rectangle getCellBounds(int index0, int index1)
+ {
+ ListUI ui = getUI();
+ Rectangle bounds = null;
+ if (ui != null)
+ {
+ bounds = ui.getCellBounds(this, index0, index1);
+ }
+ // When the UI is null, this method also returns null in the RI.
+ return bounds;
+ }
+
+ /**
+ * Returns the index of the next list element (beginning at
+ * <code>startIndex</code> and moving in the specified direction through the
+ * list, looping around if necessary) that starts with <code>prefix</code>
+ * (ignoring case).
+ *
+ * @param prefix the prefix to search for in the cell values
+ * @param startIndex the index where to start searching from
+ * @param direction the search direction, either {@link Position.Bias#Forward}
+ * or {@link Position.Bias#Backward} (<code>null</code> is interpreted
+ * as {@link Position.Bias#Backward}.
+ *
+ * @return the index of the found element or -1 if no such element has
+ * been found
+ *
+ * @throws IllegalArgumentException if prefix is <code>null</code> or
+ * startIndex is not valid
+ *
+ * @since 1.4
+ */
+ public int getNextMatch(String prefix, int startIndex,
+ Position.Bias direction)
+ {
+ if (prefix == null)
+ throw new IllegalArgumentException("The argument 'prefix' must not be"
+ + " null.");
+ if (startIndex < 0)
+ throw new IllegalArgumentException("The argument 'startIndex' must not"
+ + " be less than zero.");
+
+ int size = model.getSize();
+ if (startIndex >= model.getSize())
+ throw new IllegalArgumentException("The argument 'startIndex' must not"
+ + " be greater than the number of"
+ + " elements in the ListModel.");
+
+ int result = -1;
+ int current = startIndex;
+ int delta = -1;
+ int itemCount = model.getSize();
+ boolean finished = false;
+ prefix = prefix.toUpperCase();
+
+ if (direction == Position.Bias.Forward)
+ delta = 1;
+ while (!finished)
+ {
+ String itemStr = model.getElementAt(current).toString().toUpperCase();
+ if (itemStr.startsWith(prefix))
+ return current;
+ current = (current + delta);
+ if (current == -1)
+ current += itemCount;
+ else
+ current = current % itemCount;
+ finished = current == startIndex;
+ }
+ return result;
+ }
+
+ /**
+ * Returns a string describing the attributes for the <code>JList</code>
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-<code>null</code>, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the <code>JList</code>.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",fixedCellHeight=").append(getFixedCellHeight());
+ sb.append(",fixedCellWidth=").append(getFixedCellWidth());
+ sb.append(",selectionBackground=");
+ if (getSelectionBackground() != null)
+ sb.append(getSelectionBackground());
+ sb.append(",selectionForeground=");
+ if (getSelectionForeground() != null)
+ sb.append(getSelectionForeground());
+ sb.append(",visibleRowCount=").append(getVisibleRowCount());
+ sb.append(",layoutOrientation=").append(getLayoutOrientation());
+ return sb.toString();
+ }
+}
diff --git a/libjava/classpath/javax/swing/JMenu.java b/libjava/classpath/javax/swing/JMenu.java
new file mode 100644
index 000000000..e715ff98d
--- /dev/null
+++ b/libjava/classpath/javax/swing/JMenu.java
@@ -0,0 +1,1290 @@
+/* JMenu.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.EventListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
+import javax.swing.plaf.MenuItemUI;
+
+/**
+ * This class represents a menu that can be added to a menu bar or
+ * can be a submenu in some other menu. When JMenu is selected it
+ * displays JPopupMenu containing its menu items.
+ *
+ * <p>
+ * JMenu's fires MenuEvents when this menu's selection changes. If this menu
+ * is selected, then fireMenuSelectedEvent() is invoked. In case when menu is
+ * deselected or cancelled, then fireMenuDeselectedEvent() or
+ * fireMenuCancelledEvent() is invoked, respectivelly.
+ * </p>
+ */
+public class JMenu extends JMenuItem implements Accessible, MenuElement
+{
+ /**
+ * Receives notifications when the JMenu's ButtonModel is changed and
+ * fires menuSelected or menuDeselected events when appropriate.
+ */
+ private class MenuChangeListener
+ implements ChangeListener
+ {
+ /**
+ * Indicates the last selected state.
+ */
+ private boolean selected;
+
+ /**
+ * Receives notification when the JMenu's ButtonModel changes.
+ */
+ public void stateChanged(ChangeEvent ev)
+ {
+ ButtonModel m = (ButtonModel) ev.getSource();
+ boolean s = m.isSelected();
+ if (s != selected)
+ {
+ if (s)
+ fireMenuSelected();
+ else
+ fireMenuDeselected();
+ selected = s;
+ }
+ }
+ }
+
+ private static final long serialVersionUID = 4227225638931828014L;
+
+ /** A Popup menu associated with this menu, which pops up when menu is selected */
+ private JPopupMenu popupMenu = null;
+
+ /** Whenever menu is selected or deselected the MenuEvent is fired to
+ menu's registered listeners. */
+ private MenuEvent menuEvent = new MenuEvent(this);
+
+ /*Amount of time, in milliseconds, that should pass before popupMenu
+ associated with this menu appears or disappers */
+ private int delay;
+
+ /* PopupListener */
+ protected WinListener popupListener;
+
+ /**
+ * Location at which popup menu associated with this menu will be
+ * displayed
+ */
+ private Point menuLocation;
+
+ /**
+ * The ChangeListener for the ButtonModel.
+ *
+ * @see MenuChangeListener
+ */
+ private ChangeListener menuChangeListener;
+
+ /**
+ * Creates a new JMenu object.
+ */
+ public JMenu()
+ {
+ super();
+ setOpaque(false);
+ }
+
+ /**
+ * Creates a new <code>JMenu</code> with the specified label.
+ *
+ * @param text label for this menu
+ */
+ public JMenu(String text)
+ {
+ super(text);
+ popupMenu = new JPopupMenu();
+ popupMenu.setInvoker(this);
+ setOpaque(false);
+ }
+
+ /**
+ * Creates a new <code>JMenu</code> object.
+ *
+ * @param action Action that is used to create menu item tha will be
+ * added to the menu.
+ */
+ public JMenu(Action action)
+ {
+ super(action);
+ createActionChangeListener(this);
+ popupMenu = new JPopupMenu();
+ popupMenu.setInvoker(this);
+ setOpaque(false);
+ }
+
+ /**
+ * Creates a new <code>JMenu</code> with specified label and an option
+ * for this menu to be tear-off menu.
+ *
+ * @param text label for this menu
+ * @param tearoff true if this menu should be tear-off and false otherwise
+ */
+ public JMenu(String text, boolean tearoff)
+ {
+ // FIXME: tearoff not implemented
+ this(text);
+ }
+
+ /**
+ * Adds specified menu item to this menu
+ *
+ * @param item Menu item to add to this menu
+ *
+ * @return Menu item that was added
+ */
+ public JMenuItem add(JMenuItem item)
+ {
+ return getPopupMenu().add(item);
+ }
+
+ /**
+ * Adds specified component to this menu.
+ *
+ * @param component Component to add to this menu
+ *
+ * @return Component that was added
+ */
+ public Component add(Component component)
+ {
+ getPopupMenu().insert(component, -1);
+ return component;
+ }
+
+ /**
+ * Adds specified component to this menu at the given index
+ *
+ * @param component Component to add
+ * @param index Position of this menu item in the menu
+ *
+ * @return Component that was added
+ */
+ public Component add(Component component, int index)
+ {
+ return getPopupMenu().add(component, index);
+ }
+
+ /**
+ * Adds JMenuItem constructed with the specified label to this menu
+ *
+ * @param text label for the menu item that will be added
+ *
+ * @return Menu Item that was added to this menu
+ */
+ public JMenuItem add(String text)
+ {
+ return add(new JMenuItem(text));
+ }
+
+ /**
+ * Adds JMenuItem constructed using properties from specified action.
+ *
+ * @param action action to construct the menu item with
+ *
+ * @return Menu Item that was added to this menu
+ */
+ public JMenuItem add(Action action)
+ {
+ JMenuItem i = createActionComponent(action);
+ i.setAction(action);
+ add(i);
+ return i;
+ }
+
+ /**
+ * Removes given menu item from this menu. Nothing happens if
+ * this menu doesn't contain specified menu item.
+ *
+ * @param item Menu Item which needs to be removed
+ */
+ public void remove(JMenuItem item)
+ {
+ getPopupMenu().remove(item);
+ }
+
+ /**
+ * Removes component at the specified index from this menu
+ *
+ * @param index Position of the component that needs to be removed in the menu
+ */
+ public void remove(int index)
+ {
+ if (index < 0 || (index > 0 && getMenuComponentCount() == 0))
+ throw new IllegalArgumentException();
+
+ if (getMenuComponentCount() > 0)
+ popupMenu.remove(index);
+ }
+
+ /**
+ * Removes given component from this menu.
+ *
+ * @param component Component to remove
+ */
+ public void remove(Component component)
+ {
+ int index = getPopupMenu().getComponentIndex(component);
+ if (index >= 0)
+ getPopupMenu().remove(index);
+ }
+
+ /**
+ * Removes all menu items from the menu
+ */
+ public void removeAll()
+ {
+ if (popupMenu != null)
+ popupMenu.removeAll();
+ }
+
+ /**
+ * Creates JMenuItem with the specified text and inserts it in the
+ * at the specified index
+ *
+ * @param text label for the new menu item
+ * @param index index at which to insert newly created menu item.
+ */
+ public void insert(String text, int index)
+ {
+ this.insert(new JMenuItem(text), index);
+ }
+
+ /**
+ * Creates JMenuItem with the specified text and inserts it in the
+ * at the specified index. IllegalArgumentException is thrown
+ * if index is less than 0
+ *
+ * @param item menu item to insert
+ * @param index index at which to insert menu item.
+ * @return Menu item that was added to the menu
+ */
+ public JMenuItem insert(JMenuItem item, int index)
+ {
+ if (index < 0)
+ throw new IllegalArgumentException("index less than zero");
+
+ getPopupMenu().insert(item, index);
+ return item;
+ }
+
+ /**
+ * Creates JMenuItem with the associated action and inserts it to the menu
+ * at the specified index. IllegalArgumentException is thrown
+ * if index is less than 0
+ *
+ * @param action Action for the new menu item
+ * @param index index at which to insert newly created menu item.
+ * @return Menu item that was added to the menu
+ */
+ public JMenuItem insert(Action action, int index)
+ {
+ JMenuItem item = new JMenuItem(action);
+ this.insert(item, index);
+
+ return item;
+ }
+
+ /**
+ * This method sets this menuItem's UI to the UIManager's default for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((MenuItemUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menu.
+ *
+ * @return The Look and Feel classID. "MenuUI"
+ */
+ public String getUIClassID()
+ {
+ return "MenuUI";
+ }
+
+ /**
+ * Sets model for this menu.
+ *
+ * @param model model to set
+ */
+ public void setModel(ButtonModel model)
+ {
+ ButtonModel oldModel = getModel();
+ if (oldModel != null && menuChangeListener != null)
+ oldModel.removeChangeListener(menuChangeListener);
+
+ super.setModel(model);
+
+ if (model != null)
+ {
+ if (menuChangeListener == null)
+ menuChangeListener = new MenuChangeListener();
+ model.addChangeListener(menuChangeListener);
+ }
+ }
+
+ /**
+ * Returns true if the menu is selected and false otherwise
+ *
+ * @return true if the menu is selected and false otherwise
+ */
+ public boolean isSelected()
+ {
+ return super.isSelected();
+ }
+
+ /**
+ * Changes this menu selected state if selected is true and false otherwise
+ * This method fires menuEvents to menu's registered listeners.
+ *
+ * @param selected true if the menu should be selected and false otherwise
+ */
+ public void setSelected(boolean selected)
+ {
+ ButtonModel m = getModel();
+ if (selected != m.isSelected())
+ m.setSelected(selected);
+ }
+
+ /**
+ * Checks if PopupMenu associated with this menu is visible
+ *
+ * @return true if the popup associated with this menu is currently visible
+ * on the screen and false otherwise.
+ */
+ public boolean isPopupMenuVisible()
+ {
+ return getPopupMenu().isVisible();
+ }
+
+ /**
+ * Sets popup menu visibility
+ *
+ * @param popup true if popup should be visible and false otherwise
+ */
+ public void setPopupMenuVisible(boolean popup)
+ {
+ if (popup != isPopupMenuVisible() && (isEnabled() || ! popup))
+ {
+ if (popup && isShowing())
+ {
+ // Set location as determined by getPopupLocation().
+ Point loc = menuLocation == null ? getPopupMenuOrigin()
+ : menuLocation;
+ getPopupMenu().show(this, loc.x, loc.y);
+ }
+ else
+ getPopupMenu().setVisible(false);
+ }
+ }
+
+ /**
+ * Returns origin point of the popup menu. This takes the screen bounds
+ * into account and places the popup where it fits best.
+ *
+ * @return the origin of the popup menu
+ */
+ protected Point getPopupMenuOrigin()
+ {
+ // The menu's screen location and size.
+ Point screenLoc = getLocationOnScreen();
+ Dimension size = getSize();
+
+ // Determine the popup's size.
+ JPopupMenu popup = getPopupMenu();
+ Dimension popupSize = popup.getSize();
+ if (popupSize.width == 0 || popupSize.height == 0)
+ popupSize = popup.getPreferredSize();
+
+ // Determine screen bounds.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ Rectangle screenBounds = new Rectangle(tk.getScreenSize());
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice gd = ge.getDefaultScreenDevice();
+ GraphicsConfiguration gc = gd.getDefaultConfiguration();
+ Insets screenInsets = tk.getScreenInsets(gc);
+ screenBounds.x -= screenInsets.left;
+ screenBounds.width -= screenInsets.left + screenInsets.right;
+ screenBounds.y -= screenInsets.top;
+ screenBounds.height -= screenInsets.top + screenInsets.bottom;
+ screenLoc.x -= screenInsets.left;
+ screenLoc.y -= screenInsets.top;
+
+ Point point = new Point();
+ if (isTopLevelMenu())
+ {
+ // If menu in the menu bar.
+ int xOffset = UIManager.getInt("Menu.menuPopupOffsetX");
+ int yOffset = UIManager.getInt("Menu.menuPopupOffsetY");
+ // Determine X location.
+ if (getComponentOrientation().isLeftToRight())
+ {
+ // Prefer popup to the right.
+ point.x = xOffset;
+ // Check if it fits, otherwise place popup wherever it fits.
+ if (screenLoc.x + point.x + popupSize.width
+ > screenBounds.width + screenBounds.width
+ && screenBounds.width - size.width
+ < 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the right if there's not enough room.
+ point.x = size.width - xOffset - popupSize.width;
+ }
+ else
+ {
+ // Prefer popup to the left.
+ point.x = size.width - xOffset - popupSize.width;
+ if (screenLoc.x + point.x < screenBounds.x
+ && screenBounds.width - size.width
+ > 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the left if there's not enough room.
+ point.x = xOffset;
+ }
+ // Determine Y location. Prefer popping down.
+ point.y = size.height + yOffset;
+ if (screenLoc.y + point.y + popupSize.height >= screenBounds.height
+ && screenBounds.height - size.height
+ < 2 * (screenLoc.y - screenBounds.y))
+ // Position above if there's not enough room below.
+ point.y = - yOffset - popupSize.height;
+ }
+ else
+ {
+ // If submenu.
+ int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX");
+ int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY");
+ // Determine X location.
+ if (getComponentOrientation().isLeftToRight())
+ {
+ // Prefer popup to the right.
+ point.x = size.width + xOffset;
+ if (screenLoc.x + point.x + popupSize.width
+ >= screenBounds.x + screenBounds.width
+ && screenBounds.width - size.width
+ < 2 * (screenLoc.x - screenBounds.x))
+ // Position to the left if there's not enough room on the right.
+ point.x = - xOffset - popupSize.width;
+ }
+ else
+ {
+ // Prefer popup on the left side.
+ point.x = - xOffset - popupSize.width;
+ if (screenLoc.x + point.x < screenBounds.x
+ && screenBounds.width - size.width
+ > 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the right if there's not enough room.
+ point.x = size.width + xOffset;
+ }
+ // Determine Y location. Prefer popping down.
+ point.y = yOffset;
+ if (screenLoc.y + point.y + popupSize.height
+ >= screenBounds.y + screenBounds.height
+ && screenBounds.height - size.height
+ < 2 * (screenLoc.y - screenBounds.y))
+ // Pop up if there's not enough room below.
+ point.y = size.height - yOffset - popupSize.height;
+ }
+ return point;
+ }
+
+ /**
+ * Returns delay property.
+ *
+ * @return delay property, indicating number of milliseconds before
+ * popup menu associated with the menu appears or disappears after
+ * menu was selected or deselected respectively
+ */
+ public int getDelay()
+ {
+ return delay;
+ }
+
+ /**
+ * Sets delay property for this menu. If given time for the delay
+ * property is negative, then IllegalArgumentException is thrown
+ *
+ * @param delay number of milliseconds before
+ * popup menu associated with the menu appears or disappears after
+ * menu was selected or deselected respectively
+ */
+ public void setDelay(int delay)
+ {
+ if (delay < 0)
+ throw new IllegalArgumentException("delay less than 0");
+ this.delay = delay;
+ }
+
+ /**
+ * Sets location at which popup menu should be displayed
+ * The location given is relative to this menu item
+ *
+ * @param x x-coordinate of the menu location
+ * @param y y-coordinate of the menu location
+ */
+ public void setMenuLocation(int x, int y)
+ {
+ menuLocation = new Point(x, y);
+ if (popupMenu != null)
+ popupMenu.setLocation(x, y);
+ }
+
+ /**
+ * Creates and returns JMenuItem associated with the given action
+ *
+ * @param action Action to use for creation of JMenuItem
+ *
+ * @return JMenuItem that was creted with given action
+ */
+ protected JMenuItem createActionComponent(Action action)
+ {
+ return new JMenuItem(action);
+ }
+
+ /**
+ * Creates ActionChangeListener to listen for PropertyChangeEvents occuring
+ * in the action that is associated with this menu
+ *
+ * @param item menu that contains action to listen to
+ *
+ * @return The PropertyChangeListener
+ */
+ protected PropertyChangeListener createActionChangeListener(JMenuItem item)
+ {
+ return new ActionChangedListener(item);
+ }
+
+ /**
+ * Adds separator to the end of the menu items in the menu.
+ */
+ public void addSeparator()
+ {
+ getPopupMenu().addSeparator();
+ }
+
+ /**
+ * Inserts separator in the menu at the specified index.
+ *
+ * @param index Index at which separator should be inserted
+ */
+ public void insertSeparator(int index)
+ {
+ if (index < 0)
+ throw new IllegalArgumentException("index less than 0");
+
+ getPopupMenu().insert(new JPopupMenu.Separator(), index);
+ }
+
+ /**
+ * Returns menu item located at the specified index in the menu
+ *
+ * @param index Index at which to look for the menu item
+ *
+ * @return menu item located at the specified index in the menu
+ */
+ public JMenuItem getItem(int index)
+ {
+ if (index < 0)
+ throw new IllegalArgumentException("index less than 0");
+
+ if (getItemCount() == 0)
+ return null;
+
+ Component c = popupMenu.getComponentAtIndex(index);
+
+ if (c instanceof JMenuItem)
+ return (JMenuItem) c;
+ else
+ return null;
+ }
+
+ /**
+ * Returns number of items in the menu including separators.
+ *
+ * @return number of items in the menu
+ *
+ * @see #getMenuComponentCount()
+ */
+ public int getItemCount()
+ {
+ return getMenuComponentCount();
+ }
+
+ /**
+ * Checks if this menu is a tear-off menu.
+ *
+ * @return true if this menu is a tear-off menu and false otherwise
+ */
+ public boolean isTearOff()
+ {
+ // NOT YET IMPLEMENTED
+ throw new Error("The method isTearOff() has not yet been implemented.");
+ }
+
+ /**
+ * Returns number of menu components in this menu
+ *
+ * @return number of menu components in this menu
+ */
+ public int getMenuComponentCount()
+ {
+ return getPopupMenu().getComponentCount();
+ }
+
+ /**
+ * Returns menu component located at the givent index
+ * in the menu
+ *
+ * @param index index at which to get the menu component in the menu
+ *
+ * @return Menu Component located in the menu at the specified index
+ */
+ public Component getMenuComponent(int index)
+ {
+ if (getPopupMenu() == null || getMenuComponentCount() == 0)
+ return null;
+
+ return popupMenu.getComponentAtIndex(index);
+ }
+
+ /**
+ * Return components belonging to this menu
+ *
+ * @return components belonging to this menu
+ */
+ public Component[] getMenuComponents()
+ {
+ return getPopupMenu().getComponents();
+ }
+
+ /**
+ * Checks if this menu is a top level menu. The menu is top
+ * level menu if it is inside the menu bar. While if the menu
+ * inside some other menu, it is considered to be a pull-right menu.
+ *
+ * @return true if this menu is top level menu, and false otherwise
+ */
+ public boolean isTopLevelMenu()
+ {
+ return getParent() instanceof JMenuBar;
+ }
+
+ /**
+ * Checks if given component exists in this menu. The submenus of
+ * this menu are checked as well
+ *
+ * @param component Component to look for
+ *
+ * @return true if the given component exists in this menu, and false otherwise
+ */
+ public boolean isMenuComponent(Component component)
+ {
+ return false;
+ }
+
+ /**
+ * Returns popup menu associated with the menu.
+ *
+ * @return popup menu associated with the menu.
+ */
+ public JPopupMenu getPopupMenu()
+ {
+ if (popupMenu == null)
+ {
+ popupMenu = new JPopupMenu();
+ popupMenu.setInvoker(this);
+ }
+ return popupMenu;
+ }
+
+ /**
+ * Adds MenuListener to the menu
+ *
+ * @param listener MenuListener to add
+ */
+ public void addMenuListener(MenuListener listener)
+ {
+ listenerList.add(MenuListener.class, listener);
+ }
+
+ /**
+ * Removes MenuListener from the menu
+ *
+ * @param listener MenuListener to remove
+ */
+ public void removeMenuListener(MenuListener listener)
+ {
+ listenerList.remove(MenuListener.class, listener);
+ }
+
+ /**
+ * Returns all registered <code>MenuListener</code> objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public MenuListener[] getMenuListeners()
+ {
+ return (MenuListener[]) listenerList.getListeners(MenuListener.class);
+ }
+
+ /**
+ * This method fires MenuEvents to all menu's MenuListeners. In this case
+ * menuSelected() method of MenuListeners is called to indicated that the menu
+ * was selected.
+ */
+ protected void fireMenuSelected()
+ {
+ MenuListener[] listeners = getMenuListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].menuSelected(menuEvent);
+ }
+
+ /**
+ * This method fires MenuEvents to all menu's MenuListeners. In this case
+ * menuDeselected() method of MenuListeners is called to indicated that the menu
+ * was deselected.
+ */
+ protected void fireMenuDeselected()
+ {
+ EventListener[] ll = listenerList.getListeners(MenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuListener) ll[i]).menuDeselected(menuEvent);
+ }
+
+ /**
+ * This method fires MenuEvents to all menu's MenuListeners. In this case
+ * menuSelected() method of MenuListeners is called to indicated that the menu
+ * was cancelled. The menu is cancelled when it's popup menu is close without selection.
+ */
+ protected void fireMenuCanceled()
+ {
+ EventListener[] ll = listenerList.getListeners(MenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuListener) ll[i]).menuCanceled(menuEvent);
+ }
+
+ /**
+ * Creates WinListener that listens to the menu;s popup menu.
+ *
+ * @param popup JPopupMenu to listen to
+ *
+ * @return The WinListener
+ */
+ protected WinListener createWinListener(JPopupMenu popup)
+ {
+ return new WinListener(popup);
+ }
+
+ /**
+ * Method of the MenuElementInterface. It reacts to the selection
+ * changes in the menu. If this menu was selected, then it
+ * displayes popup menu associated with it and if this menu was
+ * deselected it hides the popup menu.
+ *
+ * @param changed true if the menu was selected and false otherwise
+ */
+ public void menuSelectionChanged(boolean changed)
+ {
+ // if this menu selection is true, then activate this menu and
+ // display popup associated with this menu
+ setSelected(changed);
+ }
+
+ /**
+ * Method of MenuElement interface. Returns sub components of
+ * this menu.
+ *
+ * @return array containing popupMenu that is associated with this menu
+ */
+ public MenuElement[] getSubElements()
+ {
+ return new MenuElement[] { popupMenu };
+ }
+
+ /**
+ * @return Returns reference to itself
+ */
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ /**
+ * This method is overriden with empty implementation, s.t the
+ * accelerator couldn't be set for the menu. The mnemonic should
+ * be used for the menu instead.
+ *
+ * @param keystroke accelerator for this menu
+ */
+ public void setAccelerator(KeyStroke keystroke)
+ {
+ throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead.");
+ }
+
+ /**
+ * This method process KeyEvent occuring when the menu is visible
+ *
+ * @param event The KeyEvent
+ */
+ protected void processKeyEvent(KeyEvent event)
+ {
+ MenuSelectionManager.defaultManager().processKeyEvent(event);
+ }
+
+ /**
+ * Programatically performs click
+ *
+ * @param time Number of milliseconds for which this menu stays pressed
+ */
+ public void doClick(int time)
+ {
+ getModel().setArmed(true);
+ getModel().setPressed(true);
+ try
+ {
+ java.lang.Thread.sleep(time);
+ }
+ catch (java.lang.InterruptedException e)
+ {
+ // probably harmless
+ }
+
+ getModel().setPressed(false);
+ getModel().setArmed(false);
+ popupMenu.show(this, this.getWidth(), 0);
+ }
+
+ /**
+ * A string that describes this JMenu. Normally only used
+ * for debugging.
+ *
+ * @return A string describing this JMenu
+ */
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJMenu();
+
+ return accessibleContext;
+ }
+
+ /**
+ * Implements support for assisitive technologies for <code>JMenu</code>.
+ */
+ protected class AccessibleJMenu extends AccessibleJMenuItem
+ implements AccessibleSelection
+ {
+ private static final long serialVersionUID = -8131864021059524309L;
+
+ protected AccessibleJMenu()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the number of accessible children of this object.
+ *
+ * @return the number of accessible children of this object
+ */
+ public int getAccessibleChildrenCount()
+ {
+ Component[] children = getMenuComponents();
+ int count = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ if (children[i] instanceof Accessible)
+ count++;
+ }
+ return count;
+ }
+
+ /**
+ * Returns the accessible child with the specified <code>index</code>.
+ *
+ * @param index the index of the child to fetch
+ *
+ * @return the accessible child with the specified <code>index</code>
+ */
+ public Accessible getAccessibleChild(int index)
+ {
+ Component[] children = getMenuComponents();
+ int count = 0;
+ Accessible found = null;
+ for (int i = 0; i < children.length; i++)
+ {
+ if (children[i] instanceof Accessible)
+ {
+ if (count == index)
+ {
+ found = (Accessible) children[i];
+ break;
+ }
+ count++;
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Returns the accessible selection of this object. AccessibleJMenus handle
+ * their selection themselves, so we always return <code>this</code> here.
+ *
+ * @return the accessible selection of this object
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the selected accessible child with the specified
+ * <code>index</code>.
+ *
+ * @param index the index of the accessible selected child to return
+ *
+ * @return the selected accessible child with the specified
+ * <code>index</code>
+ */
+ public Accessible getAccessibleSelection(int index)
+ {
+ Accessible selected = null;
+ // Only one item can be selected, which must therefore have index == 0.
+ if (index == 0)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] me = msm.getSelectedPath();
+ if (me != null)
+ {
+ for (int i = 0; i < me.length; i++)
+ {
+ if (me[i] == JMenu.this)
+ {
+ // This JMenu is selected, find and return the next
+ // JMenuItem in the path.
+ do
+ {
+ if (me[i] instanceof Accessible)
+ {
+ selected = (Accessible) me[i];
+ break;
+ }
+ i++;
+ } while (i < me.length);
+ }
+ if (selected != null)
+ break;
+ }
+ }
+ }
+ return selected;
+ }
+
+ /**
+ * Returns <code>true</code> if the accessible child with the specified
+ * index is selected, <code>false</code> otherwise.
+ *
+ * @param index the index of the accessible child to check
+ *
+ * @return <code>true</code> if the accessible child with the specified
+ * index is selected, <code>false</code> otherwise
+ */
+ public boolean isAccessibleChildSelected(int index)
+ {
+ boolean selected = false;
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] me = msm.getSelectedPath();
+ if (me != null)
+ {
+ Accessible toBeFound = getAccessibleChild(index);
+ for (int i = 0; i < me.length; i++)
+ {
+ if (me[i] == toBeFound)
+ {
+ selected = true;
+ break;
+ }
+ }
+ }
+ return selected;
+ }
+
+ /**
+ * Returns the accessible role of this object, which is
+ * {@link AccessibleRole#MENU} for the AccessibleJMenu.
+ *
+ * @return the accessible role of this object
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.MENU;
+ }
+
+ /**
+ * Returns the number of selected accessible children. This will be
+ * <code>0</code> if no item is selected, or <code>1</code> if an item
+ * is selected. AccessibleJMenu can have maximum 1 selected item.
+ *
+ * @return the number of selected accessible children
+ */
+ public int getAccessibleSelectionCount()
+ {
+ int count = 0;
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] me = msm.getSelectedPath();
+ if (me != null)
+ {
+ for (int i = 0; i < me.length; i++)
+ {
+ if (me[i] == JMenu.this)
+ {
+ if (i + 1 < me.length)
+ {
+ count = 1;
+ break;
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Selects the accessible child with the specified index.
+ *
+ * @param index the index of the accessible child to select
+ */
+ public void addAccessibleSelection(int index)
+ {
+ Accessible child = getAccessibleChild(index);
+ if (child != null && child instanceof JMenuItem)
+ {
+ JMenuItem mi = (JMenuItem) child;
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ msm.setSelectedPath(createPath(JMenu.this));
+ }
+ }
+
+ /**
+ * Removes the item with the specified index from the selection.
+ *
+ * @param index the index of the selected item to remove from the selection
+ */
+ public void removeAccessibleSelection(int index)
+ {
+ Accessible child = getAccessibleChild(index);
+ if (child != null)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] oldSelection = msm.getSelectedPath();
+ for (int i = 0; i < oldSelection.length; i++)
+ {
+ if (oldSelection[i] == child)
+ {
+ // Found the specified child in the selection. Remove it
+ // from the selection.
+ MenuElement[] newSel = new MenuElement[i - 1];
+ System.arraycopy(oldSelection, 0, newSel, 0, i - 1);
+ msm.setSelectedPath(newSel);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes all possibly selected accessible children of this object from
+ * the selection.
+ */
+ public void clearAccessibleSelection()
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] oldSelection = msm.getSelectedPath();
+ for (int i = 0; i < oldSelection.length; i++)
+ {
+ if (oldSelection[i] == JMenu.this)
+ {
+ // Found this menu in the selection. Remove all children from
+ // the selection.
+ MenuElement[] newSel = new MenuElement[i];
+ System.arraycopy(oldSelection, 0, newSel, 0, i);
+ msm.setSelectedPath(newSel);
+ break;
+ }
+ }
+ }
+
+ /**
+ * AccessibleJMenu don't support multiple selection, so this method
+ * does nothing.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ protected class WinListener extends WindowAdapter implements Serializable
+ {
+ private static final long serialVersionUID = -6415815570638474823L;
+
+ /**
+ * Creates a new <code>WinListener</code>.
+ *
+ * @param popup the popup menu which is observed
+ */
+ public WinListener(JPopupMenu popup)
+ {
+ // TODO: What should we do with the popup argument?
+ }
+
+ /**
+ * Receives notification when the popup menu is closing and deselects
+ * the menu.
+ *
+ * @param event the window event
+ */
+ public void windowClosing(WindowEvent event)
+ {
+ setSelected(false);
+ }
+ }
+
+ /**
+ * This class listens to PropertyChangeEvents occuring in menu's action
+ */
+ private class ActionChangedListener implements PropertyChangeListener
+ {
+ /** menu item associated with the action */
+ private JMenuItem menuItem;
+
+ /** Creates new ActionChangedListener and adds it to menuItem's action */
+ public ActionChangedListener(JMenuItem menuItem)
+ {
+ this.menuItem = menuItem;
+
+ Action a = menuItem.getAction();
+ if (a != null)
+ a.addPropertyChangeListener(this);
+ }
+
+ /**This method is invoked when some change occures in menuItem's action*/
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // FIXME: Need to implement
+ }
+ }
+
+ /**
+ * Creates an array to be feeded as argument to
+ * {@link MenuSelectionManager#setSelectedPath(MenuElement[])} for the
+ * specified element. This has the effect of selecting the specified element
+ * and all its parents.
+ *
+ * @param leaf the leaf element for which to create the selected path
+ *
+ * @return the selected path array
+ */
+ MenuElement[] createPath(JMenu leaf)
+ {
+ ArrayList path = new ArrayList();
+ MenuElement[] array = null;
+ Component current = leaf.getPopupMenu();
+ while (true)
+ {
+ if (current instanceof JPopupMenu)
+ {
+ JPopupMenu popupMenu = (JPopupMenu) current;
+ path.add(0, popupMenu);
+ current = popupMenu.getInvoker();
+ }
+ else if (current instanceof JMenu)
+ {
+ JMenu menu = (JMenu) current;
+ path.add(0, menu);
+ current = menu.getParent();
+ }
+ else if (current instanceof JMenuBar)
+ {
+ JMenuBar menuBar = (JMenuBar) current;
+ path.add(0, menuBar);
+ array = new MenuElement[path.size()];
+ array = (MenuElement[]) path.toArray(array);
+ break;
+ }
+ }
+ return array;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JMenuBar.java b/libjava/classpath/javax/swing/JMenuBar.java
new file mode 100644
index 000000000..1c5a448a0
--- /dev/null
+++ b/libjava/classpath/javax/swing/JMenuBar.java
@@ -0,0 +1,692 @@
+/* JMenuBar.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.plaf.MenuBarUI;
+
+import javax.swing.border.Border;
+
+/**
+ * JMenuBar is a container for menu's. For a menu bar to be seen on the
+ * screen, at least one menu should be added to it. Just like adding
+ * components to container, one can use add() to add menu's to the menu bar.
+ * Menu's will be displayed in the menu bar in the order they were added.
+ * The JMenuBar uses selectionModel to keep track of selected menu index.
+ * JMenuBar's selectionModel will fire ChangeEvents to its registered
+ * listeners when the selected index changes.
+ */
+public class JMenuBar extends JComponent implements Accessible, MenuElement
+{
+ /**
+ * Provides accessibility support for <code>JMenuBar</code>.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJMenuBar extends AccessibleJComponent
+ implements AccessibleSelection
+ {
+
+ /**
+ * Returns the number of selected items in the menu bar. Possible values
+ * are <code>0</code> if nothing is selected, or <code>1</code> if one
+ * item is selected.
+ *
+ * @return the number of selected items in the menu bar
+ */
+ public int getAccessibleSelectionCount()
+ {
+ int count = 0;
+ if (getSelectionModel().getSelectedIndex() != -1)
+ count = 1;
+ return count;
+ }
+
+ /**
+ * Returns the selected with index <code>i</code> menu, or
+ * <code>null</code> if the specified menu is not selected.
+ *
+ * @param i the index of the menu to return
+ *
+ * @return the selected with index <code>i</code> menu, or
+ * <code>null</code> if the specified menu is not selected
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ if (getSelectionModel().getSelectedIndex() != i)
+ return null;
+ return getMenu(i);
+ }
+
+ /**
+ * Returns <code>true</code> if the specified menu is selected,
+ * <code>false</code> otherwise.
+ *
+ * @param i the index of the menu to check
+ *
+ *@return <code>true</code> if the specified menu is selected,
+ * <code>false</code> otherwise
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ return getSelectionModel().getSelectedIndex() == i;
+ }
+
+ /**
+ * Selects the menu with index <code>i</code>. If another menu is already
+ * selected, this will be deselected.
+ *
+ * @param i the menu to be selected
+ */
+ public void addAccessibleSelection(int i)
+ {
+ getSelectionModel().setSelectedIndex(i);
+ }
+
+ /**
+ * Deselects the menu with index <code>i</code>.
+ *
+ * @param i the menu index to be deselected
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ if (getSelectionModel().getSelectedIndex() == i)
+ getSelectionModel().clearSelection();
+ }
+
+ /**
+ * Deselects all possibly selected menus.
+ */
+ public void clearAccessibleSelection()
+ {
+ getSelectionModel().clearSelection();
+ }
+
+ /**
+ * In menu bars it is not possible to select all items, so this method
+ * does nothing.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ // In menu bars it is not possible to select all items, so this method
+ // does nothing.
+ }
+
+ /**
+ * Returns the accessible role of <code>JMenuBar</code>, which is
+ * {@link AccessibleRole#MENU_BAR}.
+ *
+ * @return the accessible role of <code>JMenuBar</code>, which is
+ * {@link AccessibleRole#MENU_BAR}
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.MENU_BAR;
+ }
+
+ /**
+ * Returns the <code>AccessibleSelection</code> for this object. This
+ * method returns <code>this</code>, since the
+ * <code>AccessibleJMenuBar</code> manages its selection itself.
+ *
+ * @return the <code>AccessibleSelection</code> for this object
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the state of this <code>AccessibleJMenuBar</code>.
+ *
+ * @return the state of this <code>AccessibleJMenuBar</code>.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet stateSet = super.getAccessibleStateSet();
+ // TODO: Figure out what state must be added to the super state set.
+ return stateSet;
+ }
+ }
+
+ private static final long serialVersionUID = -8191026883931977036L;
+
+ /** JMenuBar's model. It keeps track of selected menu's index */
+ private transient SingleSelectionModel selectionModel;
+
+ /* borderPainted property indicating if the menuBar's border will be painted*/
+ private boolean borderPainted;
+
+ /* margin between menu bar's border and its menues*/
+ private Insets margin;
+
+ /**
+ * Creates a new JMenuBar object.
+ */
+ public JMenuBar()
+ {
+ selectionModel = new DefaultSingleSelectionModel();
+ borderPainted = true;
+ updateUI();
+ }
+
+ /**
+ * Adds menu to the menu bar
+ *
+ * @param c menu to add
+ *
+ * @return reference to the added menu
+ */
+ public JMenu add(JMenu c)
+ {
+ c.setAlignmentX(Component.LEFT_ALIGNMENT);
+ super.add(c);
+ return c;
+ }
+
+ /**
+ * This method overrides addNotify() in the Container to register
+ * this menu bar with the current keyboard manager.
+ */
+ public void addNotify()
+ {
+ super.addNotify();
+ KeyboardManager.getManager().registerJMenuBar(this);
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJMenuBar();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns reference to this menu bar
+ *
+ * @return reference to this menu bar
+ */
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Returns component at the specified index.
+ *
+ * @param i index of the component to get
+ *
+ * @return component at the specified index. Null is returned if
+ * component at the specified index doesn't exist.
+ * @deprecated Replaced by getComponent(int)
+ */
+ public Component getComponentAtIndex(int i)
+ {
+ return getComponent(i);
+ }
+
+ /**
+ * Returns index of the specified component
+ *
+ * @param c Component to search for
+ *
+ * @return index of the specified component. -1 is returned if
+ * specified component doesnt' exist in the menu bar.
+ */
+ public int getComponentIndex(Component c)
+ {
+ Component[] comps = getComponents();
+
+ int index = -1;
+
+ for (int i = 0; i < comps.length; i++)
+ {
+ if (comps[i].equals(c))
+ {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * This method is not implemented and will throw an {@link Error} if called.
+ *
+ * @return This method never returns anything, it throws an exception.
+ */
+ public JMenu getHelpMenu()
+ {
+ // the following error matches the behaviour of the reference
+ // implementation...
+ throw new Error("getHelpMenu() is not implemented");
+ }
+
+ /**
+ * Returns the margin between the menu bar's border and its menus. If the
+ * margin is <code>null</code>, this method returns
+ * <code>new Insets(0, 0, 0, 0)</code>.
+ *
+ * @return The margin (never <code>null</code>).
+ *
+ * @see #setMargin(Insets)
+ */
+ public Insets getMargin()
+ {
+ if (margin == null)
+ return new Insets(0, 0, 0, 0);
+ else
+ return margin;
+ }
+
+ /**
+ * Return menu at the specified index. If component at the
+ * specified index is not a menu, then null is returned.
+ *
+ * @param index index to look for the menu
+ *
+ * @return menu at specified index, or null if menu doesn't exist
+ * at the specified index.
+ */
+ public JMenu getMenu(int index)
+ {
+ if (getComponentAtIndex(index) instanceof JMenu)
+ return (JMenu) getComponentAtIndex(index);
+ else
+ return null;
+ }
+
+ /**
+ * Returns number of menu's in this menu bar
+ *
+ * @return number of menu's in this menu bar
+ */
+ public int getMenuCount()
+ {
+ return getComponentCount();
+ }
+
+ /**
+ * Returns selection model for this menu bar. SelectionModel
+ * keeps track of the selected menu in the menu bar. Whenever
+ * selected property of selectionModel changes, the ChangeEvent
+ * will be fired its ChangeListeners.
+ *
+ * @return selection model for this menu bar.
+ */
+ public SingleSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ /**
+ * Method of MenuElement interface. It returns subcomponents
+ * of the menu bar, which are all the menues that it contains.
+ *
+ * @return MenuElement[] array containing menues in this menu bar
+ */
+ public MenuElement[] getSubElements()
+ {
+ MenuElement[] subElements = new MenuElement[getComponentCount()];
+
+ int j = 0;
+ boolean doResize = false;
+ MenuElement menu;
+ for (int i = 0; i < getComponentCount(); i++)
+ {
+ menu = getMenu(i);
+ if (menu != null)
+ {
+ subElements[j++] = (MenuElement) menu;
+ }
+ else
+ doResize = true;
+ }
+
+ if (! doResize)
+ return subElements;
+ else
+ {
+ MenuElement[] subElements2 = new MenuElement[j];
+ for (int i = 0; i < j; i++)
+ subElements2[i] = subElements[i];
+
+ return subElements2;
+ }
+ }
+
+ /**
+ * Set the "UI" property of the menu bar, which is a look and feel class
+ * responsible for handling the menuBar's input events and painting it.
+ *
+ * @return The current "UI" property
+ */
+ public MenuBarUI getUI()
+ {
+ return (MenuBarUI) ui;
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menu bar.
+ *
+ * @return The Look and Feel classID. "MenuBarUI"
+ */
+ public String getUIClassID()
+ {
+ return "MenuBarUI";
+ }
+
+ /**
+ * Returns true if menu bar paints its border and false otherwise
+ *
+ * @return true if menu bar paints its border and false otherwise
+ */
+ public boolean isBorderPainted()
+ {
+ return borderPainted;
+ }
+
+ /**
+ * Returns true if some menu in menu bar is selected.
+ *
+ * @return true if some menu in menu bar is selected and false otherwise
+ */
+ public boolean isSelected()
+ {
+ return selectionModel.isSelected();
+ }
+
+ /**
+ * This method does nothing by default. This method is need for the
+ * MenuElement interface to be implemented.
+ *
+ * @param isIncluded true if menuBar is included in the selection
+ * and false otherwise
+ */
+ public void menuSelectionChanged(boolean isIncluded)
+ {
+ // Do nothing - needed for implementation of MenuElement interface
+ }
+
+ /**
+ * Paints border of the menu bar, if its borderPainted property is set to
+ * true.
+ *
+ * @param g The graphics context with which to paint the border
+ */
+ protected void paintBorder(Graphics g)
+ {
+ if (borderPainted)
+ {
+ Border border = getBorder();
+ if (border != null)
+ getBorder().paintBorder(this, g, 0, 0, getSize(null).width,
+ getSize(null).height);
+ }
+ }
+
+ /**
+ * A string that describes this JMenuBar. Normally only used
+ * for debugging.
+ *
+ * @return A string describing this JMenuBar
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(super.paramString());
+ sb.append(",margin=");
+ if (getMargin() != null)
+ sb.append(getMargin());
+ sb.append(",paintBorder=").append(isBorderPainted());
+ return sb.toString();
+ }
+
+ /**
+ * Process key events forwarded from MenuSelectionManager. This method
+ * doesn't do anything. It is here to conform to the MenuElement interface.
+ *
+ * @param e event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ *
+ */
+ public void processKeyEvent(KeyEvent e, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // Do nothing - needed for implementation of MenuElement interface
+ }
+
+ /**
+ * This method overrides JComponent.processKeyBinding to allow the
+ * JMenuBar to check all the child components (recursiveley) to see
+ * if they'll consume the event.
+ *
+ * @param ks the KeyStroke for the event
+ * @param e the KeyEvent for the event
+ * @param condition the focus condition for the binding
+ * @param pressed true if the key is pressed
+ */
+ protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition,
+ boolean pressed)
+ {
+ // See if the regular JComponent behavior consumes the event
+ if (super.processKeyBinding(ks, e, condition, pressed))
+ return true;
+
+ // If not, have to recursively check all the child menu elements to see
+ // if they want it
+ MenuElement[] children = getSubElements();
+ for (int i = 0; i < children.length; i++)
+ if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
+ return true;
+ return false;
+ }
+
+ /**
+ * This is a helper method to recursively check the children of this
+ * JMenuBar to see if they will consume a key event via key bindings.
+ * This is used for menu accelerators.
+ * @param menuElement the menuElement to check (and check all its children)
+ * @param ks the KeyStroke for the event
+ * @param e the KeyEvent that may be consumed
+ * @param condition the focus condition for the binding
+ * @param pressed true if the key was pressed
+ * @return true <code>menuElement</code> or one of its children consume
+ * the event (processKeyBinding returns true for menuElement or one of
+ * its children).
+ */
+ static boolean processKeyBindingHelper(MenuElement menuElement, KeyStroke ks,
+ KeyEvent e, int condition,
+ boolean pressed)
+ {
+ if (menuElement == null)
+ return false;
+
+ // First check the menuElement itself, if it's a JComponent
+ if (menuElement instanceof JComponent
+ && ((JComponent) menuElement).processKeyBinding(ks, e, condition,
+ pressed))
+ return true;
+
+ // If that didn't consume it, check all the children recursively
+ MenuElement[] children = menuElement.getSubElements();
+ for (int i = 0; i < children.length; i++)
+ if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
+ return true;
+ return false;
+ }
+
+ /**
+ * Process mouse events forwarded from MenuSelectionManager. This method
+ * doesn't do anything. It is here to conform to the MenuElement interface.
+ *
+ * @param event event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ *
+ */
+ public void processMouseEvent(MouseEvent event, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // Do nothing - needed for implementation of MenuElement interface
+ }
+
+ /**
+ * This method overrides removeNotify() in the Container to
+ * unregister this menu bar from the current keyboard manager.
+ */
+ public void removeNotify()
+ {
+ KeyboardManager.getManager().unregisterJMenuBar(this);
+ super.removeNotify();
+ }
+
+ /**
+ * Sets painting status of the border. If 'b' is true then menu bar's
+ * border will be painted, and it will not be painted otherwise.
+ *
+ * @param b indicates if menu bar's border should be painted.
+ */
+ public void setBorderPainted(boolean b)
+ {
+ if (b != borderPainted)
+ {
+ boolean old = borderPainted;
+ borderPainted = b;
+ firePropertyChange("borderPainted", old, b);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Sets help menu for this menu bar
+ *
+ * @param menu help menu
+ *
+ * @specnote The specification states that this method is not yet implemented
+ * and should throw an exception.
+ */
+ public void setHelpMenu(JMenu menu)
+ {
+ // We throw an Error here, just as Sun's JDK does.
+ throw new Error("setHelpMenu() not yet implemented.");
+ }
+
+ /**
+ * Sets the margin between the menu bar's border and its menus (this is a
+ * bound property with the name 'margin').
+ *
+ * @param m the margin (<code>null</code> permitted).
+ *
+ * @see #getMargin()
+ */
+ public void setMargin(Insets m)
+ {
+ if (m != margin)
+ {
+ Insets oldMargin = margin;
+ margin = m;
+ firePropertyChange("margin", oldMargin, margin);
+ }
+ }
+
+ /**
+ * Changes menu bar's selection to the specified menu.
+ * This method updates selected index of menu bar's selection model,
+ * which results in a model firing change event.
+ *
+ * @param sel menu to select
+ */
+ public void setSelected(Component sel)
+ {
+ int index = getComponentIndex(sel);
+ selectionModel.setSelectedIndex(index);
+ }
+
+ /**
+ * Sets menuBar's selection model to the one specified
+ *
+ * @param model SingleSelectionModel that needs to be set for this menu bar
+ */
+ public void setSelectionModel(SingleSelectionModel model)
+ {
+ if (selectionModel != model)
+ {
+ SingleSelectionModel oldModel = selectionModel;
+ selectionModel = model;
+ firePropertyChange("model", oldModel, selectionModel);
+ }
+ }
+
+ /**
+ * Set the "UI" property of the menu bar, which is a look and feel class
+ * responsible for handling menuBar's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(MenuBarUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Set the "UI" property to a class constructed, via the {@link
+ * UIManager}, from the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((MenuBarUI) UIManager.getUI(this));
+ }
+}
diff --git a/libjava/classpath/javax/swing/JMenuItem.java b/libjava/classpath/javax/swing/JMenuItem.java
new file mode 100644
index 000000000..fea3166a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/JMenuItem.java
@@ -0,0 +1,809 @@
+/* JMenuItem.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.EventListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MenuDragMouseEvent;
+import javax.swing.event.MenuDragMouseListener;
+import javax.swing.event.MenuKeyEvent;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.plaf.MenuItemUI;
+
+/**
+ * JMenuItem represents element in the menu. It inherits most of
+ * its functionality from AbstractButton, however its behavior somewhat
+ * varies from it. JMenuItem fire different kinds of events.
+ * PropertyChangeEvents are fired when menuItems properties are modified;
+ * ChangeEvents are fired when menuItem's state changes and actionEvents are
+ * fired when menu item is selected. In addition to this events menuItem also
+ * fire MenuDragMouseEvent and MenuKeyEvents when mouse is dragged over
+ * the menu item or associated key with menu item is invoked respectively.
+ */
+public class JMenuItem extends AbstractButton implements Accessible,
+ MenuElement
+{
+ private static final long serialVersionUID = -1681004643499461044L;
+
+ /** Combination of keyboard keys that can be used to activate this menu item */
+ private KeyStroke accelerator;
+
+ /**
+ * Indicates if we are currently dragging the mouse.
+ */
+ private boolean isDragging;
+
+ /**
+ * Creates a new JMenuItem object.
+ */
+ public JMenuItem()
+ {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new JMenuItem with the given icon.
+ *
+ * @param icon Icon that will be displayed on the menu item
+ */
+ public JMenuItem(Icon icon)
+ {
+ // FIXME: The requestedFocusEnabled property should
+ // be set to false, when only icon is set for menu item.
+ this(null, icon);
+ }
+
+ /**
+ * Creates a new JMenuItem with the given label.
+ *
+ * @param text label for the menu item
+ */
+ public JMenuItem(String text)
+ {
+ this(text, null);
+ }
+
+ /**
+ * Creates a new JMenuItem associated with the specified action.
+ *
+ * @param action action for this menu item
+ */
+ public JMenuItem(Action action)
+ {
+ super();
+ super.setAction(action);
+ setModel(new DefaultButtonModel());
+ init(null, null);
+ if (action != null)
+ {
+ String name = (String) action.getValue(Action.NAME);
+ if (name != null)
+ setName(name);
+
+ KeyStroke accel = (KeyStroke) action.getValue(Action.ACCELERATOR_KEY);
+ if (accel != null)
+ setAccelerator(accel);
+
+ Integer mnemonic = (Integer) action.getValue(Action.MNEMONIC_KEY);
+ if (mnemonic != null)
+ setMnemonic(mnemonic.intValue());
+
+ String command = (String) action.getValue(Action.ACTION_COMMAND_KEY);
+ if (command != null)
+ setActionCommand(command);
+ }
+ }
+
+ /**
+ * Creates a new JMenuItem with specified text and icon.
+ * Text is displayed to the left of icon by default.
+ *
+ * @param text label for this menu item
+ * @param icon icon that will be displayed on this menu item
+ */
+ public JMenuItem(String text, Icon icon)
+ {
+ super();
+ setModel(new DefaultButtonModel());
+ init(text, icon);
+ }
+
+ /**
+ * Creates a new JMenuItem object.
+ *
+ * @param text label for this menu item
+ * @param mnemonic - Single key that can be used with a
+ * look-and-feel meta key to activate this menu item. However
+ * menu item should be visible on the screen when mnemonic is used.
+ */
+ public JMenuItem(String text, int mnemonic)
+ {
+ this(text, null);
+ setMnemonic(mnemonic);
+ }
+
+ /**
+ * Initializes this menu item
+ *
+ * @param text label for this menu item
+ * @param icon icon to be displayed for this menu item
+ */
+ protected void init(String text, Icon icon)
+ {
+ super.init(text, icon);
+
+ // Initializes properties for this menu item, that are different
+ // from Abstract button properties.
+ /* NOTE: According to java specifications paint_border should be set to false,
+ since menu item should not have a border. However running few java programs
+ it seems that menu items and menues can have a border. Commenting
+ out statement below for now. */
+ //borderPainted = false;
+ focusPainted = false;
+ horizontalAlignment = JButton.LEADING;
+ horizontalTextPosition = JButton.TRAILING;
+ }
+
+ /**
+ * Set the "UI" property of the menu item, which is a look and feel class
+ * responsible for handling menuItem's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(MenuItemUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets this menuItem's UI to the UIManager's default for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((MenuItemUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menuItem.
+ *
+ * @return The Look and Feel classID. "MenuItemUI"
+ */
+ public String getUIClassID()
+ {
+ return "MenuItemUI";
+ }
+
+ /**
+ * Returns true if button's model is armed and false otherwise. The
+ * button model is armed if menu item has focus or it is selected.
+ *
+ * @return $boolean$ true if button's model is armed and false otherwise
+ */
+ public boolean isArmed()
+ {
+ return getModel().isArmed();
+ }
+
+ /**
+ * Sets menuItem's "ARMED" property
+ *
+ * @param armed DOCUMENT ME!
+ */
+ public void setArmed(boolean armed)
+ {
+ getModel().setArmed(armed);
+ }
+
+ /**
+ * Enable or disable menu item. When menu item is disabled,
+ * its text and icon are grayed out if they exist.
+ *
+ * @param enabled if true enable menu item, and disable otherwise.
+ */
+ public void setEnabled(boolean enabled)
+ {
+ super.setEnabled(enabled);
+ }
+
+ /**
+ * Return accelerator for this menu item.
+ *
+ * @return $KeyStroke$ accelerator for this menu item.
+ */
+ public KeyStroke getAccelerator()
+ {
+ return accelerator;
+ }
+
+ /**
+ * Sets the key combination which invokes the menu item's action
+ * listeners without navigating the menu hierarchy. Note that when the
+ * keyboard accelerator is typed, it will work whether or not the
+ * menu is currently displayed.
+ *
+ * @param keystroke accelerator for this menu item.
+ */
+ public void setAccelerator(KeyStroke keystroke)
+ {
+ KeyStroke old = this.accelerator;
+ this.accelerator = keystroke;
+ firePropertyChange ("accelerator", old, keystroke);
+ }
+
+ /**
+ * Configures menu items' properties from properties of the specified action.
+ * This method overrides configurePropertiesFromAction from AbstractButton
+ * to also set accelerator property.
+ *
+ * @param action action to configure properties from
+ */
+ protected void configurePropertiesFromAction(Action action)
+ {
+ super.configurePropertiesFromAction(action);
+
+ if (! (this instanceof JMenu) && action != null)
+ {
+ setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY)));
+ if (accelerator != null)
+ super.registerKeyboardAction(action, accelerator,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ }
+ }
+
+ /**
+ * Creates PropertyChangeListener to listen for the changes in action
+ * properties.
+ *
+ * @param action action to listen to for property changes
+ *
+ * @return $PropertyChangeListener$ Listener that listens to changes in
+ * action properties.
+ */
+ protected PropertyChangeListener createActionPropertyChangeListener(Action action)
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ Action act = (Action) (e.getSource());
+ configurePropertiesFromAction(act);
+ }
+ };
+ }
+
+ /**
+ * Process mouse events forwarded from MenuSelectionManager.
+ *
+ * @param ev event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ */
+ public void processMouseEvent(MouseEvent ev, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ MenuDragMouseEvent e = new MenuDragMouseEvent(ev.getComponent(),
+ ev.getID(), ev.getWhen(),
+ ev.getModifiers(), ev.getX(),
+ ev.getY(),
+ ev.getClickCount(),
+ ev.isPopupTrigger(), path,
+ manager);
+ processMenuDragMouseEvent(e);
+ }
+
+ /**
+ * Process key events forwarded from MenuSelectionManager.
+ *
+ * @param event event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ */
+ public void processKeyEvent(KeyEvent event, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ MenuKeyEvent e = new MenuKeyEvent(event.getComponent(), event.getID(),
+ event.getWhen(), event.getModifiers(),
+ event.getKeyCode(), event.getKeyChar(),
+ path, manager);
+ processMenuKeyEvent(e);
+
+ // Consume original key event, if the menu key event has been consumed.
+ if (e.isConsumed())
+ event.consume();
+ }
+
+ /**
+ * This method fires MenuDragMouseEvents to registered listeners.
+ * Different types of MenuDragMouseEvents are fired depending
+ * on the observed mouse event.
+ *
+ * @param event Mouse
+ */
+ public void processMenuDragMouseEvent(MenuDragMouseEvent event)
+ {
+ switch (event.getID())
+ {
+ case MouseEvent.MOUSE_ENTERED:
+ isDragging = false;
+ fireMenuDragMouseEntered(event);
+ break;
+ case MouseEvent.MOUSE_EXITED:
+ isDragging = false;
+ fireMenuDragMouseExited(event);
+ break;
+ case MouseEvent.MOUSE_DRAGGED:
+ isDragging = true;
+ fireMenuDragMouseDragged(event);
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ if (isDragging)
+ fireMenuDragMouseReleased(event);
+ break;
+ }
+ }
+
+ /**
+ * This method fires MenuKeyEvent to registered listeners.
+ * Different types of MenuKeyEvents are fired depending
+ * on the observed key event.
+ *
+ * @param event DOCUMENT ME!
+ */
+ public void processMenuKeyEvent(MenuKeyEvent event)
+ {
+ switch (event.getID())
+ {
+ case KeyEvent.KEY_PRESSED:
+ fireMenuKeyPressed(event);
+ break;
+ case KeyEvent.KEY_RELEASED:
+ fireMenuKeyReleased(event);
+ break;
+ case KeyEvent.KEY_TYPED:
+ fireMenuKeyTyped(event);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
+ *
+ * @param event The event signifying that mouse entered menuItem while it was dragged
+ */
+ protected void fireMenuDragMouseEntered(MenuDragMouseEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuDragMouseListener) ll[i]).menuDragMouseEntered(event);
+ }
+
+ /**
+ * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
+ *
+ * @param event The event signifying that mouse has exited menu item, while it was dragged
+ */
+ protected void fireMenuDragMouseExited(MenuDragMouseEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuDragMouseListener) ll[i]).menuDragMouseExited(event);
+ }
+
+ /**
+ * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
+ *
+ * @param event The event signifying that mouse is being dragged over the menuItem
+ */
+ protected void fireMenuDragMouseDragged(MenuDragMouseEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuDragMouseListener) ll[i]).menuDragMouseDragged(event);
+ }
+
+ /**
+ * This method fires a MenuDragMouseEvent to all the MenuItem's MouseInputListeners.
+ *
+ * @param event The event signifying that mouse was released while it was dragged over the menuItem
+ */
+ protected void fireMenuDragMouseReleased(MenuDragMouseEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuDragMouseListener) ll[i]).menuDragMouseReleased(event);
+ }
+
+ /**
+ * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
+ *
+ * @param event The event signifying that key associated with this menu was pressed
+ */
+ protected void fireMenuKeyPressed(MenuKeyEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuKeyListener) ll[i]).menuKeyPressed(event);
+ }
+
+ /**
+ * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
+ *
+ * @param event The event signifying that key associated with this menu was released
+ */
+ protected void fireMenuKeyReleased(MenuKeyEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuKeyListener) ll[i]).menuKeyTyped(event);
+ }
+
+ /**
+ * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
+ *
+ * @param event The event signifying that key associated with this menu was typed.
+ * The key is typed when it was pressed and then released
+ */
+ protected void fireMenuKeyTyped(MenuKeyEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuKeyListener) ll[i]).menuKeyTyped(event);
+ }
+
+ /**
+ * Method of the MenuElement interface.
+ * This method is invoked by MenuSelectionManager when selection of
+ * this menu item has changed. If this menu item was selected then
+ * arm it's model, and disarm the model otherwise. The menu item
+ * is considered to be selected, and thus highlighted when its model
+ * is armed.
+ *
+ * @param changed indicates selection status of this menu item. If changed is
+ * true then menu item is selected and deselected otherwise.
+ */
+ public void menuSelectionChanged(boolean changed)
+ {
+ Component parent = this.getParent();
+ if (changed)
+ {
+ model.setArmed(true);
+
+ if (parent != null && parent instanceof JPopupMenu)
+ ((JPopupMenu) parent).setSelected(this);
+ }
+ else
+ {
+ model.setArmed(false);
+
+ if (parent != null && parent instanceof JPopupMenu)
+ ((JPopupMenu) parent).getSelectionModel().clearSelection();
+ }
+ }
+
+ /**
+ * Method of the MenuElement interface.
+ *
+ * @return $MenuElement[]$ Returns array of sub-components for this menu
+ * item. By default menuItem doesn't have any subcomponents and so
+ * empty array is returned instead.
+ */
+ public MenuElement[] getSubElements()
+ {
+ return new MenuElement[0];
+ }
+
+ /**
+ * Returns reference to the component that will paint this menu item.
+ *
+ * @return $Component$ Component that will paint this menu item.
+ * Simply returns reference to this menu item.
+ */
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Adds a MenuDragMouseListener to this menu item. When mouse
+ * is dragged over the menu item the MenuDragMouseEvents will be
+ * fired, and these listeners will be called.
+ *
+ * @param listener The new listener to add
+ */
+ public void addMenuDragMouseListener(MenuDragMouseListener listener)
+ {
+ listenerList.add(MenuDragMouseListener.class, listener);
+ }
+
+ /**
+ * Removes a MenuDragMouseListener from the menuItem's listener list.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeMenuDragMouseListener(MenuDragMouseListener listener)
+ {
+ listenerList.remove(MenuDragMouseListener.class, listener);
+ }
+
+ /**
+ * Returns all added MenuDragMouseListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public MenuDragMouseListener[] getMenuDragMouseListeners()
+ {
+ return (MenuDragMouseListener[]) listenerList.getListeners(MenuDragMouseListener.class);
+ }
+
+ /**
+ * Adds an MenuKeyListener to this menu item. This listener will be
+ * invoked when MenuKeyEvents will be fired by this menu item.
+ *
+ * @param listener The new listener to add
+ */
+ public void addMenuKeyListener(MenuKeyListener listener)
+ {
+ listenerList.add(MenuKeyListener.class, listener);
+ }
+
+ /**
+ * Removes an MenuKeyListener from the menuItem's listener list.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeMenuKeyListener(MenuKeyListener listener)
+ {
+ listenerList.remove(MenuKeyListener.class, listener);
+ }
+
+ /**
+ * Returns all added MenuKeyListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public MenuKeyListener[] getMenuKeyListeners()
+ {
+ return (MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class);
+ }
+
+ /**
+ * Returns a string describing the attributes for the <code>JMenuItem</code>
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-<code>null</code>, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the <code>JMenuItem</code>.
+ */
+ protected String paramString()
+ {
+ // calling super seems to be sufficient here...
+ return super.paramString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JMenuItem</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJMenuItem}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ {
+ AccessibleJMenuItem ctx = new AccessibleJMenuItem();
+ addChangeListener(ctx);
+ accessibleContext = ctx;
+ }
+
+ return accessibleContext;
+ }
+
+ /**
+ * Provides the accessibility features for the <code>JMenuItem</code>
+ * component.
+ *
+ * @see JMenuItem#getAccessibleContext()
+ */
+ protected class AccessibleJMenuItem extends AccessibleAbstractButton
+ implements ChangeListener
+ {
+ private static final long serialVersionUID = 6748924232082076534L;
+
+ private boolean armed;
+ private boolean focusOwner;
+ private boolean pressed;
+ private boolean selected;
+
+ /**
+ * Creates a new <code>AccessibleJMenuItem</code> instance.
+ */
+ AccessibleJMenuItem()
+ {
+ //super(component);
+ }
+
+ /**
+ * Receives notification when the menu item's state changes and fires
+ * appropriate property change events to registered listeners.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ // This is fired in all cases.
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ Boolean.FALSE, Boolean.TRUE);
+
+ ButtonModel model = getModel();
+
+ // Handle the armed property.
+ if (model.isArmed())
+ {
+ if (! armed)
+ {
+ armed = true;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.ARMED, null);
+ }
+ }
+ else
+ {
+ if (armed)
+ {
+ armed = false;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ null, AccessibleState.ARMED);
+ }
+ }
+
+ // Handle the pressed property.
+ if (model.isPressed())
+ {
+ if (! pressed)
+ {
+ pressed = true;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.PRESSED, null);
+ }
+ }
+ else
+ {
+ if (pressed)
+ {
+ pressed = false;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ null, AccessibleState.PRESSED);
+ }
+ }
+
+ // Handle the selected property.
+ if (model.isSelected())
+ {
+ if (! selected)
+ {
+ selected = true;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.SELECTED, null);
+ }
+ }
+ else
+ {
+ if (selected)
+ {
+ selected = false;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ null, AccessibleState.SELECTED);
+ }
+ }
+
+ // Handle the focusOwner property.
+ if (isFocusOwner())
+ {
+ if (! focusOwner)
+ {
+ focusOwner = true;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.FOCUSED, null);
+ }
+ }
+ else
+ {
+ if (focusOwner)
+ {
+ focusOwner = false;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ null, AccessibleState.FOCUSED);
+ }
+ }
+ }
+
+ /**
+ * Returns the accessible role for the <code>JMenuItem</code> component.
+ *
+ * @return {@link AccessibleRole#MENU_ITEM}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.MENU_ITEM;
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return SwingUtilities.getAncestorOfClass(JInternalFrame.class, this)
+ == null;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JOptionPane.java b/libjava/classpath/javax/swing/JOptionPane.java
new file mode 100644
index 000000000..8c765ed74
--- /dev/null
+++ b/libjava/classpath/javax/swing/JOptionPane.java
@@ -0,0 +1,1637 @@
+/* JOptionPane.java
+ Copyright (C) 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTEvent;
+import java.awt.ActiveEvent;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.EventQueue;
+import java.awt.Frame;
+import java.awt.MenuComponent;
+import java.awt.Toolkit;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseMotionAdapter;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.OptionPaneUI;
+
+/**
+ * This class creates different types of JDialogs and JInternalFrames that can
+ * ask users for input or pass on information. JOptionPane can be used by
+ * calling one of the show static methods or by creating an instance of
+ * JOptionPane and calling createDialog or createInternalFrame.
+ */
+public class JOptionPane extends JComponent implements Accessible
+{
+ /**
+ * Provides the accessibility features for the <code>JOptionPane</code>
+ * component.
+ */
+ protected class AccessibleJOptionPane extends JComponent.AccessibleJComponent
+ {
+ private static final long serialVersionUID = 686071432213084821L;
+
+ /**
+ * Creates a new <code>AccessibleJOptionPane</code> instance.
+ */
+ protected AccessibleJOptionPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role of this object, which is always
+ * {@link AccessibleRole#OPTION_PANE}.
+ *
+ * @return the accessible role of this object
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.OPTION_PANE;
+ }
+ }
+
+ private static final long serialVersionUID = 5231143276678566796L;
+
+ /** The value returned when cancel option is selected. */
+ public static final int CANCEL_OPTION = 2;
+
+ /** The value returned when the dialog is closed without a selection. */
+ public static final int CLOSED_OPTION = -1;
+
+ /** An option used in confirmation dialog methods. */
+ public static final int DEFAULT_OPTION = -1;
+
+ /** The value returned when the no option is selected. */
+ public static final int NO_OPTION = 1;
+
+ /** An option used in confirmation dialog methods. */
+ public static final int OK_CANCEL_OPTION = 2;
+
+ /** The value returned when the ok option is selected. */
+ public static final int OK_OPTION = 0;
+
+ /** An option used in confirmation dialog methods. */
+ public static final int YES_NO_CANCEL_OPTION = 1;
+
+ /** An option used in confirmation dialog methods. */
+ public static final int YES_NO_OPTION = 0;
+
+ /** The value returned when the yes option is selected. */
+ public static final int YES_OPTION = 0;
+
+ /** Identifier for the error message type. */
+ public static final int ERROR_MESSAGE = 0;
+
+ /** Identifier for the information message type. */
+ public static final int INFORMATION_MESSAGE = 1;
+
+ /** Identifier for the plain message type. */
+ public static final int PLAIN_MESSAGE = -1;
+
+ /** Identifier for the question message type. */
+ public static final int QUESTION_MESSAGE = 3;
+
+ /** Identifier for the warning message type. */
+ public static final int WARNING_MESSAGE = 2;
+
+ /**
+ * The identifier for the propertyChangeEvent when the icon property
+ * changes.
+ */
+ public static final String ICON_PROPERTY = "icon";
+
+ /**
+ * The identifier for the propertyChangeEvent when the initialSelectionValue
+ * property changes.
+ */
+ public static final String INITIAL_SELECTION_VALUE_PROPERTY = "initialSelectionValue";
+
+ /**
+ * The identifier for the propertyChangeEvent when the initialValue property
+ * changes.
+ */
+ public static final String INITIAL_VALUE_PROPERTY = "initialValue";
+
+ /**
+ * The identifier for the propertyChangeEvent when the inputValue property
+ * changes.
+ */
+ public static final String INPUT_VALUE_PROPERTY = "inputValue";
+
+ /**
+ * The identifier for the propertyChangeEvent when the message property
+ * changes.
+ */
+ public static final String MESSAGE_PROPERTY = "message";
+
+ /**
+ * The identifier for the propertyChangeEvent when the messageType property
+ * changes.
+ */
+ public static final String MESSAGE_TYPE_PROPERTY = "messageType";
+
+ /**
+ * The identifier for the propertyChangeEvent when the optionType property
+ * changes.
+ */
+ public static final String OPTION_TYPE_PROPERTY = "optionType";
+
+ /**
+ * The identifier for the propertyChangeEvent when the options property
+ * changes.
+ */
+ public static final String OPTIONS_PROPERTY = "options";
+
+ /**
+ * The identifier for the propertyChangeEvent when the selectionValues
+ * property changes.
+ */
+ public static final String SELECTION_VALUES_PROPERTY = "selectionValues";
+
+ /**
+ * The identifier for the propertyChangeEvent when the value property
+ * changes.
+ */
+ public static final String VALUE_PROPERTY = "value";
+
+ /**
+ * The identifier for the propertyChangeEvent when the wantsInput property
+ * changes.
+ */
+ public static final String WANTS_INPUT_PROPERTY = "wantsInput";
+
+ /** The value returned when the inputValue is uninitialized. */
+ public static final Object UNINITIALIZED_VALUE = "uninitializedValue";
+
+ /** The icon displayed in the dialog/internal frame. */
+ protected Icon icon;
+
+ /** The initial selected value in the input component. */
+ protected Object initialSelectionValue;
+
+ /** The object that is initially selected for options. */
+ protected Object initialValue;
+
+ /** The value the user inputs. */
+ protected Object inputValue = UNINITIALIZED_VALUE;
+
+ /** The message displayed in the dialog/internal frame. */
+ protected Object message;
+
+ /** The type of message displayed. */
+ protected int messageType = PLAIN_MESSAGE;
+
+ /**
+ * The options (usually buttons) aligned at the bottom for the user to
+ * select.
+ */
+ protected Object[] options;
+
+ /** The type of options to display. */
+ protected int optionType = DEFAULT_OPTION;
+
+ /** The input values the user can select. */
+ protected Object[] selectionValues;
+
+ /** The value returned by selecting an option. */
+ protected Object value = UNINITIALIZED_VALUE;
+
+ /** Whether the Dialog/InternalFrame needs input. */
+ protected boolean wantsInput;
+
+ /** The common frame used when no parent is provided. */
+ private static Frame privFrame = (Frame) SwingUtilities.getOwnerFrame(null);
+
+ /**
+ * Creates a new JOptionPane object using a message of "JOptionPane
+ * message", using the PLAIN_MESSAGE type and DEFAULT_OPTION.
+ */
+ public JOptionPane()
+ {
+ this("JOptionPane message", PLAIN_MESSAGE, DEFAULT_OPTION, null, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message using the
+ * PLAIN_MESSAGE type and DEFAULT_OPTION.
+ *
+ * @param message The message to display.
+ */
+ public JOptionPane(Object message)
+ {
+ this(message, PLAIN_MESSAGE, DEFAULT_OPTION, null, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message and messageType
+ * and DEFAULT_OPTION.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ */
+ public JOptionPane(Object message, int messageType)
+ {
+ this(message, messageType, DEFAULT_OPTION, null, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message, messageType and
+ * optionType.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ * @param optionType The type of options.
+ */
+ public JOptionPane(Object message, int messageType, int optionType)
+ {
+ this(message, messageType, optionType, null, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message, messageType,
+ * optionType and icon.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ * @param optionType The type of options.
+ * @param icon The icon to display.
+ */
+ public JOptionPane(Object message, int messageType, int optionType, Icon icon)
+ {
+ this(message, messageType, optionType, icon, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message, messageType,
+ * optionType, icon and options.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ * @param optionType The type of options.
+ * @param icon The icon to display.
+ * @param options The options given.
+ */
+ public JOptionPane(Object message, int messageType, int optionType,
+ Icon icon, Object[] options)
+ {
+ this(message, messageType, optionType, icon, options, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message, messageType,
+ * optionType, icon, options and initialValue. The initialValue will be
+ * focused initially.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ * @param optionType The type of options.
+ * @param icon The icon to display.
+ * @param options The options given.
+ * @param initialValue The component to focus on initially.
+ *
+ * @throws IllegalArgumentException If the messageType or optionType are not
+ * legal values.
+ */
+ public JOptionPane(Object message, int messageType, int optionType,
+ Icon icon, Object[] options, Object initialValue)
+ {
+ this.message = message;
+ if (! validMessageType(messageType))
+ throw new IllegalArgumentException("Message Type not legal value.");
+ this.messageType = messageType;
+ if (! validOptionType(optionType))
+ throw new IllegalArgumentException("Option Type not legal value.");
+ this.optionType = optionType;
+ this.icon = icon;
+ this.options = options;
+ this.initialValue = initialValue;
+
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ updateUI();
+ }
+
+ /**
+ * This method creates a new JDialog that is either centered around the
+ * parent's frame or centered on the screen (if the parent is null). The
+ * JDialog will not be resizable and will be modal. Once the JDialog is
+ * disposed, the inputValue and value properties will be set by the
+ * optionPane.
+ *
+ * @param parentComponent The parent of the Dialog.
+ * @param title The title in the bar of the JDialog.
+ *
+ * @return A new JDialog based on the JOptionPane configuration.
+ */
+ public JDialog createDialog(Component parentComponent, String title)
+ {
+ Frame toUse = getFrameForComponent(parentComponent);
+ if (toUse == null)
+ toUse = getRootFrame();
+
+ JDialog dialog = new JDialog(toUse, title);
+ inputValue = UNINITIALIZED_VALUE;
+ value = UNINITIALIZED_VALUE;
+
+ dialog.getContentPane().add(this);
+ dialog.setModal(true);
+ dialog.setResizable(false);
+ dialog.pack();
+ dialog.setLocationRelativeTo(parentComponent);
+
+ addPropertyChangeListener(new ValuePropertyHandler(dialog));
+ return dialog;
+ }
+
+ /**
+ * Handles changes of the value property. Whenever this property changes,
+ * the JOptionPane dialog should be closed.
+ */
+ private static class ValuePropertyHandler
+ implements PropertyChangeListener
+ {
+ /**
+ * The dialog to close.
+ */
+ JDialog dialog;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param d the dialog to be closed
+ */
+ ValuePropertyHandler(JDialog d)
+ {
+ dialog = d;
+ }
+
+ /**
+ * Receives notification when any of the properties change.
+ */
+ public void propertyChange(PropertyChangeEvent p)
+ {
+ String prop = p.getPropertyName();
+ Object val = p.getNewValue();
+ if (prop.equals(VALUE_PROPERTY) && val != null
+ && val != UNINITIALIZED_VALUE)
+ {
+ dialog.setVisible(false);
+ }
+ }
+ }
+
+ /**
+ * This method creates a new JInternalFrame that is in the JLayeredPane
+ * which contains the parentComponent given. If no suitable JLayeredPane
+ * can be found from the parentComponent given, a RuntimeException will be
+ * thrown.
+ *
+ * @param parentComponent The parent to find a JDesktopPane from.
+ * @param title The title of the JInternalFrame.
+ *
+ * @return A new JInternalFrame based on the JOptionPane configuration.
+ *
+ * @throws RuntimeException If no suitable JDesktopPane is found.
+ *
+ * @specnote The specification says that the internal frame is placed
+ * in the nearest <code>JDesktopPane</code> that is found in
+ * <code>parent</code>'s ancestors. The behaviour of the JDK
+ * is that it actually looks up the nearest
+ * <code>JLayeredPane</code> in <code>parent</code>'s ancestors.
+ * So do we.
+ */
+ public JInternalFrame createInternalFrame(Component parentComponent,
+ String title)
+ throws RuntimeException
+ {
+ // Try to find a JDesktopPane.
+ JLayeredPane toUse = getDesktopPaneForComponent(parentComponent);
+ // If we don't have a JDesktopPane, we try to find a JLayeredPane.
+ if (toUse == null)
+ toUse = JLayeredPane.getLayeredPaneAbove(parentComponent);
+ // If this still fails, we throw a RuntimeException.
+ if (toUse == null)
+ throw new RuntimeException
+ ("parentComponent does not have a valid parent");
+
+ JInternalFrame frame = new JInternalFrame(title);
+
+ inputValue = UNINITIALIZED_VALUE;
+ value = UNINITIALIZED_VALUE;
+
+ frame.setContentPane(this);
+ frame.setClosable(true);
+
+ toUse.add(frame);
+ frame.setLayer(JLayeredPane.MODAL_LAYER);
+
+ frame.pack();
+ frame.setVisible(true);
+
+ return frame;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JOptionPane</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJOptionPane}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJOptionPane();
+ return accessibleContext;
+ }
+
+ /**
+ * This method returns the JDesktopPane for the given parentComponent or
+ * null if none can be found.
+ *
+ * @param parentComponent The component to look in.
+ *
+ * @return The JDesktopPane for the given component or null if none can be
+ * found.
+ */
+ public static JDesktopPane getDesktopPaneForComponent(Component parentComponent)
+ {
+ return (JDesktopPane) SwingUtilities.getAncestorOfClass(JDesktopPane.class,
+ parentComponent);
+ }
+
+ /**
+ * This method returns the Frame for the given parentComponent or null if
+ * none can be found.
+ *
+ * @param parentComponent The component to look in.
+ *
+ * @return The Frame for the given component or null if none can be found.
+ */
+ public static Frame getFrameForComponent(Component parentComponent)
+ {
+ return (Frame) SwingUtilities.getAncestorOfClass(Frame.class,
+ parentComponent);
+ }
+
+ /**
+ * This method returns the icon displayed.
+ *
+ * @return The icon displayed.
+ */
+ public Icon getIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * This method returns the value initially selected from the list of values
+ * the user can input.
+ *
+ * @return The initial selection value.
+ */
+ public Object getInitialSelectionValue()
+ {
+ return initialSelectionValue;
+ }
+
+ /**
+ * This method returns the value that is focused from the list of options.
+ *
+ * @return The initial value from options.
+ */
+ public Object getInitialValue()
+ {
+ return initialValue;
+ }
+
+ /**
+ * This method returns the value that the user input.
+ *
+ * @return The user's input value.
+ */
+ public Object getInputValue()
+ {
+ if (getValue().equals(new Integer(CANCEL_OPTION)))
+ setInputValue(null);
+ return inputValue;
+ }
+
+ /**
+ * This method returns the maximum characters per line. By default, this is
+ * Integer.MAX_VALUE.
+ *
+ * @return The maximum characters per line.
+ */
+ public int getMaxCharactersPerLineCount()
+ {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * This method returns the message displayed.
+ *
+ * @return The message displayed.
+ */
+ public Object getMessage()
+ {
+ return message;
+ }
+
+ /**
+ * This method returns the message type.
+ *
+ * @return The message type.
+ */
+ public int getMessageType()
+ {
+ return messageType;
+ }
+
+ /**
+ * This method returns the options.
+ *
+ * @return The options.
+ */
+ public Object[] getOptions()
+ {
+ return options;
+ }
+
+ /**
+ * This method returns the option type.
+ *
+ * @return The option type.
+ */
+ public int getOptionType()
+ {
+ return optionType;
+ }
+
+ /**
+ * This method returns the Frame used by JOptionPane dialog's that have no
+ * parent.
+ *
+ * @return The Frame used by dialogs that have no parent.
+ */
+ public static Frame getRootFrame()
+ {
+ return privFrame;
+ }
+
+ /**
+ * This method returns the selection values.
+ *
+ * @return The selection values.
+ */
+ public Object[] getSelectionValues()
+ {
+ return selectionValues;
+ }
+
+ /**
+ * This method returns the UI used by the JOptionPane.
+ *
+ * @return The UI used by the JOptionPane.
+ */
+ public OptionPaneUI getUI()
+ {
+ return (OptionPaneUI) ui;
+ }
+
+ /**
+ * This method returns an identifier to determine which UI class will act as
+ * the UI.
+ *
+ * @return The UI identifier.
+ */
+ public String getUIClassID()
+ {
+ return "OptionPaneUI";
+ }
+
+ /**
+ * This method returns the value that the user selected out of options.
+ *
+ * @return The value that the user selected out of options.
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This method returns whether this JOptionPane wants input.
+ *
+ * @return Whether this JOptionPane wants input.
+ */
+ public boolean getWantsInput()
+ {
+ return wantsInput;
+ }
+
+ /**
+ * This method returns a String that describes this JOptionPane.
+ *
+ * @return A String that describes this JOptionPane.
+ */
+ protected String paramString()
+ {
+ return "JOptionPane";
+ }
+
+ /**
+ * This method requests focus for the initial value.
+ */
+ public void selectInitialValue()
+ {
+ if (ui != null)
+ ((OptionPaneUI) ui).selectInitialValue(this);
+ }
+
+ /**
+ * This method changes the icon property.
+ *
+ * @param newIcon The new icon to use.
+ */
+ public void setIcon(Icon newIcon)
+ {
+ if (icon != newIcon)
+ {
+ Icon old = icon;
+ icon = newIcon;
+ firePropertyChange(ICON_PROPERTY, old, icon);
+ }
+ }
+
+ /**
+ * This method changes the initial selection property.
+ *
+ * @param newValue The new initial selection.
+ */
+ public void setInitialSelectionValue(Object newValue)
+ {
+ if (initialSelectionValue != newValue)
+ {
+ Object old = initialSelectionValue;
+ initialSelectionValue = newValue;
+ firePropertyChange(INITIAL_SELECTION_VALUE_PROPERTY, old,
+ initialSelectionValue);
+ }
+ }
+
+ /**
+ * This method changes the initial value property.
+ *
+ * @param newValue The new initial value.
+ */
+ public void setInitialValue(Object newValue)
+ {
+ if (initialValue != newValue)
+ {
+ Object old = initialValue;
+ initialValue = newValue;
+ firePropertyChange(INITIAL_VALUE_PROPERTY, old, initialValue);
+ }
+ }
+
+ /**
+ * This method changes the inputValue property.
+ *
+ * @param newValue The new inputValue.
+ */
+ public void setInputValue(Object newValue)
+ {
+ if (inputValue != newValue)
+ {
+ Object old = inputValue;
+ inputValue = newValue;
+ firePropertyChange(INPUT_VALUE_PROPERTY, old, inputValue);
+ }
+ }
+
+ /**
+ * This method changes the message property.
+ *
+ * @param newMessage The new message.
+ */
+ public void setMessage(Object newMessage)
+ {
+ if (message != newMessage)
+ {
+ Object old = message;
+ message = newMessage;
+ firePropertyChange(MESSAGE_PROPERTY, old, message);
+ }
+ }
+
+ /**
+ * This method changes the messageType property.
+ *
+ * @param newType The new messageType.
+ *
+ * @throws IllegalArgumentException If the messageType is not valid.
+ */
+ public void setMessageType(int newType)
+ {
+ if (! validMessageType(newType))
+ throw new IllegalArgumentException("Message Type not legal value.");
+ if (newType != messageType)
+ {
+ int old = messageType;
+ messageType = newType;
+ firePropertyChange(MESSAGE_TYPE_PROPERTY, old, messageType);
+ }
+ }
+
+ /**
+ * This method changes the options property.
+ *
+ * @param newOptions The new options.
+ */
+ public void setOptions(Object[] newOptions)
+ {
+ if (options != newOptions)
+ {
+ Object[] old = options;
+ options = newOptions;
+ firePropertyChange(OPTIONS_PROPERTY, old, options);
+ }
+ }
+
+ /**
+ * This method changes the optionType property.
+ *
+ * @param newType The new optionType.
+ *
+ * @throws IllegalArgumentException If the optionType is not valid.
+ */
+ public void setOptionType(int newType)
+ {
+ if (! validOptionType(newType))
+ throw new IllegalArgumentException("Option Type not legal value.");
+ if (newType != optionType)
+ {
+ int old = optionType;
+ optionType = newType;
+ firePropertyChange(OPTION_TYPE_PROPERTY, old, optionType);
+ }
+ }
+
+ /**
+ * This method changes the Frame used for JOptionPane dialogs that have no
+ * parent.
+ *
+ * @param newRootFrame The Frame to use for dialogs that have no parent.
+ */
+ public static void setRootFrame(Frame newRootFrame)
+ {
+ privFrame = newRootFrame;
+ }
+
+ /**
+ * This method changes the selectionValues property.
+ *
+ * @param newValues The new selectionValues.
+ */
+ public void setSelectionValues(Object[] newValues)
+ {
+ if (newValues != selectionValues)
+ {
+ if (newValues != null)
+ wantsInput = true;
+ Object[] old = selectionValues;
+ selectionValues = newValues;
+ firePropertyChange(SELECTION_VALUES_PROPERTY, old, selectionValues);
+ }
+ }
+
+ /**
+ * This method sets the UI used with the JOptionPane.
+ *
+ * @param ui The UI used with the JOptionPane.
+ */
+ public void setUI(OptionPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets the value has been selected out of options.
+ *
+ * @param newValue The value that has been selected out of options.
+ */
+ public void setValue(Object newValue)
+ {
+ if (value != newValue)
+ {
+ Object old = value;
+ value = newValue;
+ firePropertyChange(VALUE_PROPERTY, old, value);
+ }
+ }
+
+ /**
+ * This method changes the wantsInput property.
+ *
+ * @param newValue Whether this JOptionPane requires input.
+ */
+ public void setWantsInput(boolean newValue)
+ {
+ if (wantsInput != newValue)
+ {
+ boolean old = wantsInput;
+ wantsInput = newValue;
+ firePropertyChange(WANTS_INPUT_PROPERTY, old, wantsInput);
+ }
+ }
+
+ /**
+ * This method shows a confirmation dialog with the title "Select an Option"
+ * and displays the given message. The parent frame will be the same as the
+ * parent frame of the given parentComponent. This method returns the
+ * option chosen by the user.
+ *
+ * @param parentComponent The parentComponent to find a frame in.
+ * @param message The message to display.
+ *
+ * @return The option that was selected.
+ */
+ public static int showConfirmDialog(Component parentComponent, Object message)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ JDialog dialog = pane.createDialog(parentComponent, "Select an Option");
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows a confirmation dialog with the given message,
+ * optionType and title. The frame that owns the dialog will be the same
+ * frame that holds the given parentComponent. This method returns the
+ * option that was chosen.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param optionType The optionType.
+ *
+ * @return The option that was chosen.
+ */
+ public static int showConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType)
+ {
+ JOptionPane pane = new JOptionPane(message, PLAIN_MESSAGE, optionType);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows a confirmation dialog with the given message, title,
+ * messageType and optionType. The frame owner will be the same frame as
+ * the one that holds the given parentComponent. This method returns the
+ * option selected by the user.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param optionType The optionType.
+ * @param messageType The messageType.
+ *
+ * @return The selected option.
+ */
+ public static int showConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows a confirmation dialog with the given message, title,
+ * optionType, messageType and icon. The frame owner will be the same as
+ * the one that holds the given parentComponent. This method returns the
+ * option selected by the user.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param optionType The optionType.
+ * @param messageType The messsageType.
+ * @param icon The icon displayed.
+ *
+ * @return The selected option.
+ */
+ public static int showConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType,
+ Icon icon)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType, icon);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method will show a QUESTION_MESSAGE input dialog with the given
+ * message. No selectionValues is set so the Look and Feel will usually
+ * give the user a TextField to fill out. The frame owner will be the same
+ * frame that holds the given parentComponent. This method will return the
+ * value entered by the user.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ *
+ * @return The value entered by the user.
+ */
+ public static String showInputDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ pane.setWantsInput(true);
+ JDialog dialog = pane.createDialog(parentComponent, null);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method will show a QUESTION_MESSAGE type input dialog with the given
+ * message and initialSelectionValue. Since there is no selectionValues
+ * set, the Look and Feel will usually give a TextField to fill out. The
+ * frame owner will be the same as the one that holds the given
+ * parentComponent. This method will return the value entered by the user.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message to display.
+ * @param initialSelectionValue The initially selected value.
+ *
+ * @return The value the user input.
+ */
+ public static String showInputDialog(Component parentComponent,
+ Object message,
+ Object initialSelectionValue)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ pane.setInitialSelectionValue(initialSelectionValue);
+ pane.setWantsInput(true);
+ JDialog dialog = pane.createDialog(parentComponent, null);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method displays a new input dialog with the given message, title and
+ * messageType. Since no selectionValues value is given, the Look and Feel
+ * will usually give the user a TextField to input data to. This method
+ * returns the value the user inputs.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message to display.
+ * @param title The title of the dialog.
+ * @param messageType The messageType.
+ *
+ * @return The value the user input.
+ */
+ public static String showInputDialog(Component parentComponent,
+ Object message, String title,
+ int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setWantsInput(true);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows an input dialog with the given message, title,
+ * messageType, icon, selectionValues, and initialSelectionValue. This
+ * method returns the value that the user selects.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param messageType The messageType.
+ * @param icon The icon displayed.
+ * @param selectionValues The list of values to select from.
+ * @param initialSelectionValue The initially selected value.
+ *
+ * @return The user selected value.
+ */
+ public static Object showInputDialog(Component parentComponent,
+ Object message, String title,
+ int messageType, Icon icon,
+ Object[] selectionValues,
+ Object initialSelectionValue)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setWantsInput(true);
+ pane.setIcon(icon);
+ pane.setSelectionValues(selectionValues);
+ pane.setInitialSelectionValue(initialSelectionValue);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ return pane.getInputValue();
+ }
+
+ /**
+ * This method shows a QUESTION_MESSAGE type input dialog. Since no
+ * selectionValues is set, the Look and Feel will usually give the user a
+ * TextField to input data to. This method returns the value the user
+ * inputs.
+ *
+ * @param message The message to display.
+ *
+ * @return The user selected value.
+ */
+ public static String showInputDialog(Object message)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ pane.setWantsInput(true);
+ JDialog dialog = pane.createDialog(null, null);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows a QUESTION_MESSAGE type input dialog. Since no
+ * selectionValues is set, the Look and Feel will usually give the user a
+ * TextField to input data to. The input component will be initialized with
+ * the initialSelectionValue. This method returns the value the user
+ * inputs.
+ *
+ * @param message The message to display.
+ * @param initialSelectionValue The initialSelectionValue.
+ *
+ * @return The user selected value.
+ */
+ public static String showInputDialog(Object message,
+ Object initialSelectionValue)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ pane.setWantsInput(true);
+ pane.setInitialSelectionValue(initialSelectionValue);
+ JDialog dialog = pane.createDialog(null, null);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows an internal confirmation dialog with the given message.
+ * The internal frame dialog will be placed in the first JDesktopPane
+ * ancestor of the given parentComponent. This method will return the value
+ * selected.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ *
+ * @return The value selected.
+ */
+ public static int showInternalConfirmDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, null);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an internal confirmation dialog with the given message,
+ * optionType and title. The internal frame dialog will be placed in the
+ * first JDesktopPane ancestor of the given parentComponent. This method
+ * will return the selected value.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param optionType The option type.
+ *
+ * @return The selected value.
+ */
+ public static int showInternalConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType)
+ {
+ JOptionPane pane = new JOptionPane(message, PLAIN_MESSAGE, optionType);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an internal confirmation dialog with the given message,
+ * title, optionTypes and icon for the given message type. The internal
+ * confirmation dialog will be placed in the first instance of
+ * JDesktopPane ancestor of the given parentComponent.
+ *
+ * @param parentComponent The component to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title of the dialog.
+ * @param optionType The option type.
+ * @param messageType The message type.
+ *
+ * @return The selected value.
+ */
+ public static int showInternalConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an internal confirmation dialog with the given message,
+ * title, option type, message type, and icon. The internal frame dialog
+ * will be placed in the first JDesktopPane ancestor that is found in the
+ * given parentComponent. This method returns the selected value.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param optionType The option type.
+ * @param messageType The message type.
+ * @param icon The icon to display.
+ *
+ * @return The selected value.
+ */
+ public static int showInternalConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType,
+ Icon icon)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType, icon);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an internal input dialog with the given message. The
+ * internal frame dialog will be placed in the first JDesktopPane ancestor
+ * of the given parent component. This method returns the value input by
+ * the user.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ *
+ * @return The user selected value.
+ */
+ public static String showInternalInputDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message);
+ pane.setWantsInput(true);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, null);
+
+ startModal(frame);
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows an internal input dialog with the given message, title
+ * and message type. The internal input dialog will be placed in the first
+ * JDesktopPane ancestor found in the given parent component. This method
+ * will return the input value given by the user.
+ *
+ * @param parentComponent The component to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param messageType The message type.
+ *
+ * @return The user input value.
+ */
+ public static String showInternalInputDialog(Component parentComponent,
+ Object message, String title,
+ int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setWantsInput(true);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows an internal input dialog with the given message, title
+ * message type, icon, selection value list and initial selection value.
+ * The internal frame dialog will be placed in the first JDesktopPane
+ * ancestor found in the given parent component. This method returns the
+ * input value from the user.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param messageType The message type.
+ * @param icon The icon to display.
+ * @param selectionValues The selection value list.
+ * @param initialSelectionValue The initial selection value.
+ *
+ * @return The user input value.
+ */
+ public static Object showInternalInputDialog(Component parentComponent,
+ Object message, String title,
+ int messageType, Icon icon,
+ Object[] selectionValues,
+ Object initialSelectionValue)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setWantsInput(true);
+ pane.setIcon(icon);
+ pane.setSelectionValues(selectionValues);
+ pane.setInitialSelectionValue(initialSelectionValue);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ return pane.getInputValue();
+ }
+
+ /**
+ * This method shows an internal message dialog with the given message. The
+ * internal frame dialog will be placed in the first JDesktopPane ancestor
+ * found in the given parent component.
+ *
+ * @param parentComponent The component to find a JDesktopPane in.
+ * @param message The message to display.
+ */
+ public static void showInternalMessageDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, null);
+
+ startModal(frame);
+ }
+
+ /**
+ * This method shows an internal message dialog with the given message,
+ * title and message type. The internal message dialog is placed in the
+ * first JDesktopPane ancestor found in the given parent component.
+ *
+ * @param parentComponent The parent component to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param messageType The message type.
+ */
+ public static void showInternalMessageDialog(Component parentComponent,
+ Object message, String title,
+ int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+ }
+
+ /**
+ * This method shows an internal message dialog with the given message,
+ * title, message type and icon. The internal message dialog is placed in
+ * the first JDesktopPane ancestor found in the given parent component.
+ *
+ * @param parentComponent The component to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param messageType The message type.
+ * @param icon The icon to display.
+ */
+ public static void showInternalMessageDialog(Component parentComponent,
+ Object message, String title,
+ int messageType, Icon icon)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setIcon(icon);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+ }
+
+ /**
+ * This method displays an internal option dialog with the given message,
+ * title, option type, message type, icon, option list, and initial option
+ * value. The internal option dialog is placed in the first JDesktopPane
+ * ancestor found in the parent component. This method returns the option
+ * selected.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message displayed.
+ * @param title The title displayed.
+ * @param optionType The option type.
+ * @param messageType The message type.
+ * @param icon The icon to display.
+ * @param options The array of options.
+ * @param initialValue The initial value selected.
+ *
+ * @return The option that was selected.
+ */
+ public static int showInternalOptionDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType,
+ Icon icon, Object[] options,
+ Object initialValue)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType, icon,
+ options, initialValue);
+
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an INFORMATION_MESSAGE type message dialog.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ */
+ public static void showMessageDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message, INFORMATION_MESSAGE);
+ JDialog dialog = pane.createDialog(parentComponent, null);
+ dialog.show();
+ }
+
+ /**
+ * This method shows a message dialog with the given message, title and
+ * messageType.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param messageType The messageType.
+ */
+ public static void showMessageDialog(Component parentComponent,
+ Object message, String title,
+ int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+ }
+
+ /**
+ * This method shows a message dialog with the given message, title,
+ * messageType and icon.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param messageType The messageType.
+ * @param icon The icon displayed.
+ */
+ public static void showMessageDialog(Component parentComponent,
+ Object message, String title,
+ int messageType, Icon icon)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setIcon(icon);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+ }
+
+ /**
+ * This method shows an option dialog with the given message, title,
+ * optionType, messageType, icon, options and initialValue. This method
+ * returns the option that was selected.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param optionType The optionType.
+ * @param messageType The messageType.
+ * @param icon The icon displayed.
+ * @param options The options to choose from.
+ * @param initialValue The initial value.
+ *
+ * @return The selected option.
+ */
+ public static int showOptionDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType,
+ Icon icon, Object[] options,
+ Object initialValue)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType, icon,
+ options, initialValue);
+
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method resets the UI to the Look and Feel default.
+ */
+ public void updateUI()
+ {
+ setUI((OptionPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns true if the key is a valid messageType.
+ *
+ * @param key The key to check.
+ *
+ * @return True if key is valid.
+ */
+ private boolean validMessageType(int key)
+ {
+ switch (key)
+ {
+ case ERROR_MESSAGE:
+ case INFORMATION_MESSAGE:
+ case PLAIN_MESSAGE:
+ case QUESTION_MESSAGE:
+ case WARNING_MESSAGE:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method returns true if the key is a valid optionType.
+ *
+ * @param key The key to check.
+ *
+ * @return True if key is valid.
+ */
+ private boolean validOptionType(int key)
+ {
+ switch (key)
+ {
+ case DEFAULT_OPTION:
+ case OK_CANCEL_OPTION:
+ case YES_NO_CANCEL_OPTION:
+ case YES_NO_OPTION:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This helper method makes the JInternalFrame wait until it is notified by
+ * an InternalFrameClosing event. This method also adds the given
+ * JOptionPane to the JInternalFrame and sizes it according to the
+ * JInternalFrame's preferred size.
+ *
+ * @param f The JInternalFrame to make modal.
+ */
+ private static void startModal(JInternalFrame f)
+ {
+ // We need to add an additional glasspane-like component directly
+ // below the frame, which intercepts all mouse events that are not
+ // directed at the frame itself.
+ JPanel modalInterceptor = new JPanel();
+ modalInterceptor.setOpaque(false);
+ JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
+ lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
+ modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
+ modalInterceptor.addMouseListener(new MouseAdapter(){});
+ modalInterceptor.addMouseMotionListener(new MouseMotionAdapter(){});
+ lp.add(modalInterceptor);
+ f.toFront();
+
+ // We need to explicitly dispatch events when we are blocking the event
+ // dispatch thread.
+ EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
+ try
+ {
+ while (! f.isClosed())
+ {
+ if (EventQueue.isDispatchThread())
+ {
+ // The getNextEventMethod() issues wait() when no
+ // event is available, so we don't need do explicitly wait().
+ AWTEvent ev = queue.getNextEvent();
+ // This mimics EventQueue.dispatchEvent(). We can't use
+ // EventQueue.dispatchEvent() directly, because it is
+ // protected, unfortunately.
+ if (ev instanceof ActiveEvent)
+ ((ActiveEvent) ev).dispatch();
+ else if (ev.getSource() instanceof Component)
+ ((Component) ev.getSource()).dispatchEvent(ev);
+ else if (ev.getSource() instanceof MenuComponent)
+ ((MenuComponent) ev.getSource()).dispatchEvent(ev);
+ // Other events are ignored as per spec in
+ // EventQueue.dispatchEvent
+ }
+ else
+ {
+ // Give other threads a chance to become active.
+ Thread.yield();
+ }
+ }
+ }
+ catch (InterruptedException ex)
+ {
+ // If we get interrupted, then leave the modal state.
+ }
+ finally
+ {
+ // Clean up the modal interceptor.
+ lp.remove(modalInterceptor);
+
+ // Remove the internal frame from its parent, so it is no longer
+ // lurking around and clogging memory.
+ Container parent = f.getParent();
+ if (parent != null)
+ parent.remove(f);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JPanel.java b/libjava/classpath/javax/swing/JPanel.java
new file mode 100644
index 000000000..f2ff95690
--- /dev/null
+++ b/libjava/classpath/javax/swing/JPanel.java
@@ -0,0 +1,203 @@
+/* JPanel.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.FlowLayout;
+import java.awt.LayoutManager;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.PanelUI;
+
+/**
+ * An instance of JPanel can be added to a panel, frame etc
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JPanel extends JComponent implements Accessible
+{
+ /**
+ * Provides accessibility support for <code>JPanel</code>.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+ protected class AccessibleJPanel extends AccessibleJComponent
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJPanel</code>.
+ */
+ protected AccessibleJPanel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for <code>JPanel</code>, which is
+ * {@link AccessibleRole#PANEL}.
+ *
+ * @return the accessible role for <code>JPanel</code>
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PANEL;
+ }
+ }
+
+ /**
+ * Creates a new panel with a new instance of {@link FlowLayout} as the
+ * layout manager and double-buffering enabled.
+ */
+ public JPanel()
+ {
+ this(new FlowLayout(), true);
+ }
+
+ /**
+ * Creates a new panel with double-buffering enabled or disabled as
+ * specified. The default layout manager is an instance of
+ * {@link FlowLayout}.
+ *
+ * @param isDoubleBuffered a flag that controls whether or not
+ * double-buffering is enabled.
+ */
+ public JPanel(boolean isDoubleBuffered)
+ {
+ this(new FlowLayout(), isDoubleBuffered);
+ }
+
+ /**
+ * Creates a new panel with the specified layout manager. Double-buffering
+ * is enabled by default.
+ *
+ * @param layout the layout manager (<code>null</code> permitted).
+ */
+ public JPanel(LayoutManager layout)
+ {
+ this(layout, true);
+ }
+
+ /**
+ * Creates a new panel with the specified layout manager and
+ * double-buffering.
+ *
+ * @param layout the layout manager (<code>null</code> permitted).
+ * @param isDoubleBuffered a flag that controls whether or not
+ * double-buffering is enabled.
+ */
+ public JPanel(LayoutManager layout, boolean isDoubleBuffered)
+ {
+ setLayout(layout);
+ setOpaque(true);
+ setDoubleBuffered(isDoubleBuffered);
+ updateUI();
+ }
+
+ /**
+ * Returns the suffix (<code>"PanelUI"</code> in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a <code>JPanel</code>.
+ *
+ * @return <code>"PanelUI"</code>.
+ */
+ public String getUIClassID()
+ {
+ return "PanelUI";
+ }
+
+ /**
+ * Sets the UI delegate for the <code>JPanel</code> component.
+ *
+ * @param ui the UI delegate.
+ *
+ * @since 1.4
+ * @see #getUI()
+ */
+ public void setUI(PanelUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Returns the UI delegate for the <code>JPanel</code> component.
+ *
+ * @return The UI delegate.
+ *
+ * @since 1.4
+ * @see #setUI(PanelUI)
+ */
+ public PanelUI getUI()
+ {
+ return (PanelUI) ui;
+ }
+
+ /**
+ * Sets this panel's UI delegate to the default (obtained from the
+ * {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((PanelUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JPanel</code> component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJPanel}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJPanel();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this <code>JPanel</code>.
+ *
+ * @return A string describing the attributes of this <code>JPanel</code>
+ * (never <code>null</code>).
+ */
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+}
diff --git a/libjava/classpath/javax/swing/JPasswordField.java b/libjava/classpath/javax/swing/JPasswordField.java
new file mode 100644
index 000000000..e73993f62
--- /dev/null
+++ b/libjava/classpath/javax/swing/JPasswordField.java
@@ -0,0 +1,297 @@
+/* JPasswordField.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.IOException;
+import java.io.ObjectOutputStream;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+/**
+ * class JPasswordField
+ *
+ * @author Andrew Selkirk
+ * @author Lillian Angel
+ * @version 1.0
+ */
+public class JPasswordField extends JTextField
+{
+ /**
+ * AccessibleJPasswordField
+ */
+ protected class AccessibleJPasswordField extends AccessibleJTextField
+ {
+ private static final long serialVersionUID = -8477039424200681086L;
+
+ /**
+ * Constructor AccessibleJPasswordField
+ */
+ protected AccessibleJPasswordField()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * getAccessibleRole
+ *
+ * @return AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PASSWORD_TEXT;
+ }
+ }
+
+ /**
+ * echoChar. Default is 0.
+ */
+ private char echoChar = 0;
+
+ /**
+ * Creates a <code>JPasswordField</code> object.
+ */
+ public JPasswordField()
+ {
+ this(null, null, 0);
+ }
+
+ /**
+ * Creates a <code>JPasswordField</code> object.
+ *
+ * @param text the initial text
+ */
+ public JPasswordField(String text)
+ {
+ this(null, text, 0);
+ }
+
+ /**
+ * Creates a <code>JPasswordField</code> object.
+ *
+ * @param columns the number of columns
+ */
+ public JPasswordField(int columns)
+ {
+ this(null, null, columns);
+ }
+
+ /**
+ * Creates a <code>JPasswordField</code> object.
+ *
+ * @param text the initial text
+ * @param columns the number of columns
+ */
+ public JPasswordField(String text, int columns)
+ {
+ this(null, text, columns);
+ }
+
+ /**
+ * Creates a <code>JPasswordField</code> object.
+ *
+ * @param document the document to use
+ * @param text the initial text
+ * @param columns the number of columns
+ */
+ public JPasswordField(Document document, String text, int columns)
+ {
+ super(document, text, columns);
+ }
+
+ /**
+ * writeObject
+ *
+ * @param stream the stream to write to
+ *
+ * @exception IOException if an error occurs
+ */
+ private void writeObject(ObjectOutputStream stream) throws IOException
+ {
+ // TODO: Implement me.
+ }
+
+ /**
+ * Returns the <code>UIClassID</code>
+ *
+ * @return the string "PasswordFieldUI"
+ */
+ public String getUIClassID()
+ {
+ return "PasswordFieldUI";
+ }
+
+ /**
+ * getEchoChar
+ *
+ * @return the echo char
+ */
+ public char getEchoChar()
+ {
+ return echoChar;
+ }
+
+ /**
+ * setEchoChar
+ *
+ * @param echo the echo char
+ */
+ public void setEchoChar(char echo)
+ {
+ this.echoChar = echo;
+ }
+
+ /**
+ * Returns true if this JPasswordField has a character set for echoing.
+ * A character is considered to be set if the echo character is not 0.
+ *
+ * @return <code>true</code> if the echo char is set,
+ * <code>false</code> otherwise.
+ */
+ public boolean echoCharIsSet()
+ {
+ return echoChar != 0;
+ }
+
+ /**
+ * Copies the selected text into the clipboard. This operation is not
+ * allowed in a password input field.
+ */
+ public void copy()
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(this);
+ }
+
+ /**
+ * Cuts the selected text and puts it into the clipboard. This operation
+ * is not allowed in a password input field.
+ */
+ public void cut()
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(this);
+ }
+
+ /**
+ * Returns the text contained in this TextComponent. If the
+ * underlying document is null, will give a NullPointerException.
+ *
+ * @return String
+ *
+ * @deprecated
+ */
+ public String getText()
+ {
+ try
+ {
+ return getDocument().getText(0, getDocument().getLength());
+ }
+ catch (BadLocationException ble)
+ {
+ // This should never happen.
+ throw new AssertionError(ble);
+ }
+ }
+
+ /**
+ * Fetches a portion of the text represented by the component.
+ * Returns an empty string if length is 0. If the
+ * underlying document is null, will give a NullPointerException.
+ *
+ * @param offset TODO
+ * @param length TODO
+ *
+ * @return String
+ *
+ * @exception BadLocationException TODO
+ *
+ * @deprecated
+ */
+ public String getText(int offset, int length) throws BadLocationException
+ {
+ return getDocument().getText(offset, length);
+ }
+
+ /**
+ * Returns the text contained in this TextComponent. If the underlying
+ * document is null, will give a NullPointerException.
+ * For stronger security, it is recommended that the returned character
+ * array be cleared after use by setting each character to zero.
+ *
+ * @return char[]
+ */
+ public char[] getPassword()
+ {
+ return getText().toCharArray();
+ }
+
+ /**
+ * Returns a string representation of this JPasswordField. This method is
+ * intended to be used only for debugging purposes,
+ * and the content and format of the returned string may vary between
+ * implementations. The returned string may be empty but may not be null.
+ *
+ * @return String
+ */
+ protected String paramString()
+ {
+ try
+ {
+ return getText();
+ }
+ catch (NullPointerException npe)
+ {
+ return "";
+ }
+ }
+
+ /**
+ * getAccessibleContext
+ *
+ * @return the <code>AccessibleContext</code> object
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJPasswordField();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JPopupMenu.java b/libjava/classpath/javax/swing/JPopupMenu.java
new file mode 100644
index 000000000..8f16aa2a7
--- /dev/null
+++ b/libjava/classpath/javax/swing/JPopupMenu.java
@@ -0,0 +1,947 @@
+/* JPopupMenu.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.EventListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.plaf.PopupMenuUI;
+
+/**
+ * JPopupMenu is a container that is used to display popup menu's menu
+ * items. By default JPopupMenu is a lightweight container, however if it
+ * is the case that JPopupMenu's bounds are outside of main window, then
+ * heawyweight container will be used to display menu items. It is also
+ * possible to change JPopupMenu's default behavior and set JPopupMenu
+ * to always use heavyweight container.
+ *
+ * JPopupMenu can be displayed anywhere; it is a floating free popup menu.
+ * However before JPopupMenu is diplayed, its invoker property should be set.
+ * JPopupMenu's invoker is a component relative to which popup menu is
+ * displayed.
+ *
+ * JPopupMenu fires PopupMenuEvents to its registered listeners. Whenever
+ * JPopupMenu becomes visible on the screen then PopupMenuEvent indicating
+ * that popup menu became visible will be fired. In the case when
+ * JPopupMenu becomes invisible or cancelled without selection, then
+ * popupMenuBecomeInvisible() or popupMenuCancelled() methods of
+ * PopupMenuListeners will be invoked.
+ *
+ * JPopupMenu also fires PropertyChangeEvents when its bound properties
+ * change.In addittion to inheritted bound properties, JPopupMenu has
+ * 'visible' bound property. When JPopupMenu becomes visible/invisible on
+ * the screen it fires PropertyChangeEvents to its registered
+ * PropertyChangeListeners.
+ */
+public class JPopupMenu extends JComponent implements Accessible, MenuElement
+{
+ private static final long serialVersionUID = -8336996630009646009L;
+
+ /* indicates if popup's menu border should be painted*/
+ private boolean borderPainted = true;
+
+ /** Flag indicating whether lightweight, mediumweight or heavyweight popup
+ is used to display menu items.
+
+ These are the possible cases:
+
+ 1. if DefaultLightWeightPopupEnabled true
+ (i) use lightweight container if popup feets inside top-level window
+ (ii) only use heavyweight container (JDialog) if popup doesn't fit.
+
+ 2. if DefaultLightWeightPopupEnabled false
+ (i) if popup fits, use awt.Panel (mediumWeight)
+ (ii) if popup doesn't fit, use JDialog (heavyWeight)
+ */
+ private static boolean DefaultLightWeightPopupEnabled = true;
+
+ /* Component that invokes popup menu. */
+ transient Component invoker;
+
+ /* Label for this popup menu. It is not used in most of the look and feel themes. */
+ private String label;
+
+ /*Amount of space between menuItem's in JPopupMenu and JPopupMenu's border */
+ private Insets margin;
+
+ /** Indicates whether ligthWeight container can be used to display popup
+ menu. This flag is the same as DefaultLightWeightPopupEnabled, but setting
+ this flag can change popup menu after creation of the object */
+ private boolean lightWeightPopupEnabled;
+
+ /** SelectionModel that keeps track of menu selection. */
+ protected SingleSelectionModel selectionModel;
+
+ /* Popup that is used to display JPopupMenu */
+ private transient Popup popup;
+
+ /**
+ * Location of the popup, X coordinate.
+ */
+ private int popupLocationX;
+
+ /**
+ * Location of the popup, Y coordinate.
+ */
+ private int popupLocationY;
+
+ /* Field indicating if popup menu is visible or not */
+ private boolean visible = false;
+
+ /**
+ * Creates a new JPopupMenu object.
+ */
+ public JPopupMenu()
+ {
+ this(null);
+ }
+
+ /**
+ * Creates a new JPopupMenu with specified label
+ *
+ * @param label Label for popup menu.
+ */
+ public JPopupMenu(String label)
+ {
+ lightWeightPopupEnabled = getDefaultLightWeightPopupEnabled();
+ setLabel(label);
+ setSelectionModel(new DefaultSingleSelectionModel());
+ super.setVisible(false);
+ updateUI();
+ }
+
+ /**
+ * Adds given menu item to the popup menu
+ *
+ * @param item menu item to add to the popup menu
+ *
+ * @return menu item that was added to the popup menu
+ */
+ public JMenuItem add(JMenuItem item)
+ {
+ this.insert(item, -1);
+ return item;
+ }
+
+ /**
+ * Constructs menu item with a specified label and adds it to
+ * popup menu
+ *
+ * @param text label for the menu item to be added
+ *
+ * @return constructed menu item that was added to the popup menu
+ */
+ public JMenuItem add(String text)
+ {
+ JMenuItem item = new JMenuItem(text);
+ return add(item);
+ }
+
+ /**
+ * Constructs menu item associated with the specified action
+ * and adds it to the popup menu
+ *
+ * @param action Action for the new menu item
+ *
+ * @return menu item that was added to the menu
+ */
+ public JMenuItem add(Action action)
+ {
+ JMenuItem item = createActionComponent(action);
+
+ if (action != null)
+ action.addPropertyChangeListener(createActionChangeListener(item));
+
+ return add(item);
+ }
+
+ /**
+ * Revomes component at the given index from the menu.
+ *
+ * @param index index of the component that will be removed in the menu
+ */
+ public void remove(int index)
+ {
+ super.remove(index);
+ revalidate();
+ }
+
+ /**
+ * Create menu item associated with the given action
+ * and inserts it into the popup menu at the specified index
+ *
+ * @param action Action for the new menu item
+ * @param index index in the popup menu at which to insert new menu item.
+ */
+ public void insert(Action action, int index)
+ {
+ JMenuItem item = new JMenuItem(action);
+ this.insert(item, index);
+ }
+
+ /**
+ * Insert given component to the popup menu at the
+ * specified index
+ *
+ * @param component Component to insert
+ * @param index Index at which to insert given component
+ */
+ public void insert(Component component, int index)
+ {
+ super.add(component, index);
+ }
+
+ /**
+ * Returns flag indicating if newly created JPopupMenu will use
+ * heavyweight or lightweight container to display its menu items
+ *
+ * @return true if JPopupMenu will use lightweight container to display
+ * menu items by default, and false otherwise.
+ */
+ public static boolean getDefaultLightWeightPopupEnabled()
+ {
+ return DefaultLightWeightPopupEnabled;
+ }
+
+ /**
+ * Sets whether JPopupMenu should use ligthWeight container to
+ * display it menu items by default
+ *
+ * @param enabled true if JPopupMenu should use lightweight container
+ * for displaying its menu items, and false otherwise.
+ */
+ public static void setDefaultLightWeightPopupEnabled(boolean enabled)
+ {
+ DefaultLightWeightPopupEnabled = enabled;
+ }
+
+ /**
+ * This method returns the UI used to display the JPopupMenu.
+ *
+ * @return The UI used to display the JPopupMenu.
+ */
+ public PopupMenuUI getUI()
+ {
+ return (PopupMenuUI) ui;
+ }
+
+ /**
+ * Set the "UI" property of the menu item, which is a look and feel class
+ * responsible for handling popupMenu's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(PopupMenuUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets this menuItem's UI to the UIManager's default for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((PopupMenuUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menuItem.
+ *
+ * @return The Look and Feel classID. "PopupMenuUI"
+ */
+ public String getUIClassID()
+ {
+ return "PopupMenuUI";
+ }
+
+ /**
+ * Returns selectionModel used by this popup menu to keep
+ * track of the selection.
+ *
+ * @return popup menu's selection model
+ */
+ public SingleSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ /**
+ * Sets selection model for this popup menu
+ *
+ * @param model new selection model of this popup menu
+ */
+ public void setSelectionModel(SingleSelectionModel model)
+ {
+ selectionModel = model;
+ }
+
+ /**
+ * Creates new menu item associated with a given action.
+ *
+ * @param action Action used to create new menu item
+ *
+ * @return new created menu item associated with a given action.
+ */
+ protected JMenuItem createActionComponent(Action action)
+ {
+ return new JMenuItem(action);
+ }
+
+ /**
+ * Creates PropertyChangeListener that listens to PropertyChangeEvents
+ * occuring in the Action associated with given menu item in this popup menu.
+ *
+ * @param item MenuItem
+ *
+ * @return The PropertyChangeListener
+ */
+ protected PropertyChangeListener createActionChangeListener(JMenuItem item)
+ {
+ return new ActionChangeListener();
+ }
+
+ /**
+ * Returns true if this popup menu will display its menu item in
+ * a lightweight container and false otherwise.
+ *
+ * @return true if this popup menu will display its menu items
+ * in a lightweight container and false otherwise.
+ */
+ public boolean isLightWeightPopupEnabled()
+ {
+ return lightWeightPopupEnabled;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param enabled DOCUMENT ME!
+ */
+ public void setLightWeightPopupEnabled(boolean enabled)
+ {
+ lightWeightPopupEnabled = enabled;
+ }
+
+ /**
+ * Returns label for this popup menu
+ *
+ * @return label for this popup menu
+ */
+ public String getLabel()
+ {
+ return label;
+ }
+
+ /**
+ * Sets label for this popup menu. This method fires PropertyChangeEvent
+ * when the label property is changed. Please note that most
+ * of the Look &amp; Feel will ignore this property.
+ *
+ * @param label label for this popup menu
+ */
+ public void setLabel(String label)
+ {
+ if (label != this.label)
+ {
+ String oldLabel = this.label;
+ this.label = label;
+ firePropertyChange("label", oldLabel, label);
+ }
+ }
+
+ /**
+ * Adds separator to this popup menu
+ */
+ public void addSeparator()
+ {
+ // insert separator at the end of the list of menu items
+ this.insert(new Separator(), -1);
+ }
+
+ /**
+ * Adds a MenuKeyListener to the popup.
+ *
+ * @param l - the listener to add.
+ */
+ public void addMenuKeyListener(MenuKeyListener l)
+ {
+ listenerList.add(MenuKeyListener.class, l);
+ }
+
+ /**
+ * Removes a MenuKeyListener from the popup.
+ *
+ * @param l - the listener to remove.
+ */
+ public void removeMenuKeyListener(MenuKeyListener l)
+ {
+ listenerList.remove(MenuKeyListener.class, l);
+ }
+
+ /**
+ * Returns array of getMenuKeyListeners that are listening to JPopupMenu.
+ *
+ * @return array of getMenuKeyListeners that are listening to JPopupMenu
+ */
+ public MenuKeyListener[] getMenuKeyListeners()
+ {
+ return ((MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class));
+ }
+
+ /**
+ * Adds popupMenuListener to listen for PopupMenuEvents fired
+ * by the JPopupMenu
+ *
+ * @param listener PopupMenuListener to add to JPopupMenu
+ */
+ public void addPopupMenuListener(PopupMenuListener listener)
+ {
+ listenerList.add(PopupMenuListener.class, listener);
+ }
+
+ /**
+ * Removes PopupMenuListener from JPopupMenu's list of listeners
+ *
+ * @param listener PopupMenuListener which needs to be removed
+ */
+ public void removePopupMenuListener(PopupMenuListener listener)
+ {
+ listenerList.remove(PopupMenuListener.class, listener);
+ }
+
+ /**
+ * Returns array of PopupMenuListeners that are listening to JPopupMenu
+ *
+ * @return Array of PopupMenuListeners that are listening to JPopupMenu
+ */
+ public PopupMenuListener[] getPopupMenuListeners()
+ {
+ return ((PopupMenuListener[]) listenerList.getListeners(PopupMenuListener.class));
+ }
+
+ /**
+ * This method calls popupMenuWillBecomeVisible() of popup menu's
+ * PopupMenuListeners. This method is invoked just before popup menu
+ * will appear on the screen.
+ */
+ protected void firePopupMenuWillBecomeVisible()
+ {
+ EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((PopupMenuListener) ll[i]).popupMenuWillBecomeVisible(new PopupMenuEvent(this));
+ }
+
+ /**
+ * This method calls popupMenuWillBecomeInvisible() of popup
+ * menu's PopupMenuListeners. This method is invoked just before popup
+ * menu will disappear from the screen
+ */
+ protected void firePopupMenuWillBecomeInvisible()
+ {
+ EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((PopupMenuListener) ll[i]).popupMenuWillBecomeInvisible(new PopupMenuEvent(this));
+ }
+
+ /**
+ * This method calls popupMenuCanceled() of popup menu's PopupMenuListeners.
+ * This method is invoked just before popup menu is cancelled. This happens
+ * when popup menu is closed without selecting any of its menu items. This
+ * usually happens when the top-level window is resized or moved.
+ */
+ protected void firePopupMenuCanceled()
+ {
+ EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((PopupMenuListener) ll[i]).popupMenuCanceled(new PopupMenuEvent(this));
+ }
+
+ /**
+ * This methods sets popup menu's size to its' preferred size. If the
+ * popup menu's size is previously set it will be ignored.
+ */
+ public void pack()
+ {
+ // Hook up this call so that it gets executed on the event thread in order
+ // to avoid synchronization problems when calling the layout manager.
+ if (! SwingUtilities.isEventDispatchThread())
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ show();
+ }
+ });
+ }
+
+ setSize(getPreferredSize());
+ }
+
+ /**
+ * Return visibility of the popup menu
+ *
+ * @return true if popup menu is visible on the screen and false otherwise.
+ */
+ public boolean isVisible()
+ {
+ return visible;
+ }
+
+ /**
+ * Sets visibility property of this popup menu. If the property is
+ * set to true then popup menu will be dispayed and popup menu will
+ * hide itself if visible property is set to false.
+ *
+ * @param visible true if popup menu will become visible and false otherwise.
+ */
+ public void setVisible(final boolean visible)
+ {
+ // Hook up this call so that it gets executed on the event thread in order
+ // to avoid synchronization problems when calling the layout manager.
+ if (! SwingUtilities.isEventDispatchThread())
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ setVisible(visible);
+ }
+ });
+ }
+
+ if (visible == isVisible())
+ return;
+
+ boolean old = isVisible();
+ this.visible = visible;
+ if (old != isVisible())
+ {
+ if (visible)
+ {
+ if (invoker != null && !(invoker instanceof JMenu))
+ {
+ MenuElement[] menuEls;
+ if (getSubElements().length > 0)
+ {
+ menuEls = new MenuElement[2];
+ menuEls[0] = this;
+ menuEls[1] = getSubElements()[0];
+ }
+ else
+ {
+ menuEls = new MenuElement[1];
+ menuEls[0] = this;
+ }
+ MenuSelectionManager.defaultManager().setSelectedPath(menuEls);
+ }
+ firePopupMenuWillBecomeVisible();
+ PopupFactory pf = PopupFactory.getSharedInstance();
+ pack();
+ popup = pf.getPopup(invoker, this, popupLocationX, popupLocationY);
+ popup.show();
+ }
+ else
+ {
+ getSelectionModel().clearSelection();
+ firePopupMenuWillBecomeInvisible();
+ popup.hide();
+ }
+ firePropertyChange("visible", old, isVisible());
+ }
+ }
+
+ /**
+ * Sets location of the popup menu.
+ *
+ * @param x X coordinate of the popup menu's location
+ * @param y Y coordinate of the popup menu's location
+ */
+ public void setLocation(int x, int y)
+ {
+ popupLocationX = x;
+ popupLocationY = y;
+ // Handle the case when the popup is already showing. In this case we need
+ // to fetch a new popup from PopupFactory and use this. See the general
+ // contract of the PopupFactory.
+ }
+
+ /**
+ * Returns popup menu's invoker.
+ *
+ * @return popup menu's invoker
+ */
+ public Component getInvoker()
+ {
+ return invoker;
+ }
+
+ /**
+ * Sets popup menu's invoker.
+ *
+ * @param component The new invoker of this popup menu
+ */
+ public void setInvoker(Component component)
+ {
+ invoker = component;
+ }
+
+ /**
+ * This method displays JPopupMenu on the screen at the specified
+ * location. Note that x and y coordinates given to this method
+ * should be expressed in terms of the popup menus' invoker.
+ *
+ * @param component Invoker for this popup menu
+ * @param x x-coordinate of the popup menu relative to the specified invoker
+ * @param y y-coordiate of the popup menu relative to the specified invoker
+ */
+ public void show(Component component, int x, int y)
+ {
+ if (component.isShowing())
+ {
+ setInvoker(component);
+ Point p = new Point(x, y);
+ SwingUtilities.convertPointToScreen(p, component);
+ setLocation(p.x, p.y);
+ setVisible(true);
+ }
+ }
+
+ /**
+ * Returns component located at the specified index in the popup menu
+ *
+ * @param index index of the component to return
+ *
+ * @return component located at the specified index in the popup menu
+ *
+ * @deprecated Replaced by getComponent(int)
+ */
+ public Component getComponentAtIndex(int index)
+ {
+ return getComponent(index);
+ }
+
+ /**
+ * Returns index of the specified component in the popup menu
+ *
+ * @param component Component to look for
+ *
+ * @return index of the specified component in the popup menu
+ */
+ public int getComponentIndex(Component component)
+ {
+ Component[] items = getComponents();
+
+ for (int i = 0; i < items.length; i++)
+ {
+ if (items[i].equals(component))
+ return i;
+ }
+
+ return -1;
+ }
+
+ /**
+ * Sets size of the popup
+ *
+ * @param size Dimensions representing new size of the popup menu
+ */
+ public void setPopupSize(Dimension size)
+ {
+ super.setSize(size);
+ }
+
+ /**
+ * Sets size of the popup menu
+ *
+ * @param width width for the new size
+ * @param height height for the new size
+ */
+ public void setPopupSize(int width, int height)
+ {
+ super.setSize(width, height);
+ }
+
+ /**
+ * Selects specified component in this popup menu.
+ *
+ * @param selected component to select
+ */
+ public void setSelected(Component selected)
+ {
+ int index = getComponentIndex(selected);
+ selectionModel.setSelectedIndex(index);
+ }
+
+ /**
+ * Checks if this popup menu paints its border.
+ *
+ * @return true if this popup menu paints its border and false otherwise.
+ */
+ public boolean isBorderPainted()
+ {
+ return borderPainted;
+ }
+
+ /**
+ * Sets if the border of the popup menu should be
+ * painter or not.
+ *
+ * @param painted true if the border should be painted and false otherwise
+ */
+ public void setBorderPainted(boolean painted)
+ {
+ borderPainted = painted;
+ }
+
+ /**
+ * Returns margin for this popup menu.
+ *
+ * @return margin for this popup menu.
+ */
+ public Insets getMargin()
+ {
+ return margin;
+ }
+
+ /**
+ * A string that describes this JPopupMenu. Normally only used
+ * for debugging.
+ *
+ * @return A string describing this JMenuItem
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(super.paramString());
+ sb.append(",label=");
+ if (getLabel() != null)
+ sb.append(getLabel());
+ sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled());
+ sb.append(",margin=");
+ if (getMargin() != null)
+ sb.append(margin);
+ sb.append(",paintBorder=").append(isBorderPainted());
+ return sb.toString();
+ }
+
+ /**
+ * Process mouse events forwarded from MenuSelectionManager. This method
+ * doesn't do anything. It is here to conform to the MenuElement interface.
+ *
+ * @param event event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ */
+ public void processMouseEvent(MouseEvent event, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // Empty Implementation. This method is needed for the implementation
+ // of MenuElement interface
+ }
+
+ /**
+ * Process key events forwarded from MenuSelectionManager. This method
+ * doesn't do anything. It is here to conform to the MenuElement interface.
+ *
+ * @param event event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ *
+ */
+ public void processKeyEvent(KeyEvent event, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // Empty Implementation. This method is needed for the implementation
+ // of MenuElement interface
+ }
+
+ /**
+ * Method of MenuElement Interface. It is invoked when
+ * popupMenu's selection has changed
+ *
+ * @param changed true if this popupMenu is part of current menu
+ * hierarchy and false otherwise.
+ */
+ public void menuSelectionChanged(boolean changed)
+ {
+ if (invoker instanceof JMenu)
+ {
+ // We need to special case this since the JMenu calculates the
+ // position etc of the popup.
+ JMenu menu = (JMenu) invoker;
+ menu.setPopupMenuVisible(changed);
+ }
+ else if (! changed)
+ setVisible(false);
+ }
+
+ /**
+ * Return subcomonents of this popup menu. This method returns only
+ * components that implement the <code>MenuElement</code> interface.
+ *
+ * @return array of menu items belonging to this popup menu
+ */
+ public MenuElement[] getSubElements()
+ {
+ Component[] items = getComponents();
+ ArrayList subElements = new ArrayList();
+
+ for (int i = 0; i < items.length; i++)
+ if (items[i] instanceof MenuElement)
+ subElements.add(items[i]);
+
+ return (MenuElement[])
+ subElements.toArray(new MenuElement[subElements.size()]);
+ }
+
+ /**
+ * Method of the MenuElement interface. Returns reference to itself.
+ *
+ * @return Returns reference to itself
+ */
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Checks if observing mouse event should trigger popup
+ * menu to show on the screen.
+ *
+ * @param event MouseEvent to check
+ *
+ * @return true if the observing mouse event is popup trigger and false otherwise
+ */
+ public boolean isPopupTrigger(MouseEvent event)
+ {
+ return ((PopupMenuUI) getUI()).isPopupTrigger(event);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJPopupMenu();
+
+ return accessibleContext;
+ }
+
+ /**
+ * This is the separator that can be used in popup menu.
+ */
+ public static class Separator extends JSeparator
+ {
+ public Separator()
+ {
+ super();
+ }
+
+ public String getUIClassID()
+ {
+ return "PopupMenuSeparatorUI";
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return true;
+ }
+
+ protected class AccessibleJPopupMenu extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = 7423261328879849768L;
+
+ protected AccessibleJPopupMenu()
+ {
+ // Nothing to do here.
+ }
+
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.POPUP_MENU;
+ }
+ }
+
+ /* This class resizes popup menu and repaints popup menu appropriately if one
+ of item's action has changed */
+ private class ActionChangeListener implements PropertyChangeListener
+ {
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // We used to have a revalidate() and repaint() call here. However I think
+ // this is not needed. Instead, a new Popup has to be fetched from the
+ // PopupFactory and used here.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JProgressBar.java b/libjava/classpath/javax/swing/JProgressBar.java
new file mode 100644
index 000000000..d24a380b0
--- /dev/null
+++ b/libjava/classpath/javax/swing/JProgressBar.java
@@ -0,0 +1,861 @@
+/* JProgressBar.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Graphics;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleValue;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ProgressBarUI;
+
+/**
+ * A component that displays a visual indicator of the progress of a task. The
+ * component has two modes: determinate and indeterminate. In determinate mode,
+ * the <code>JProgressBar</code> fills a percentage of its bar based on its
+ * current value. In indeterminate mode, it creates box and bounces it between
+ * its bounds.
+ * <p>
+ * This component has the following properties:
+ * </p>
+ * <table>
+ * <tr><th> Property </th><th> Stored in </th><th> Bound? </th></tr>
+ * <tr><td> borderPainted </td><td> progressBar </td><td> yes </td></tr>
+ * <tr><td> changeListeners </td><td> progressBar </td><td> no </td></tr>
+ * <tr><td> indeterminate </td><td> progressBar </td><td> yes </td></tr>
+ * <tr><td> maximum </td><td> model </td><td> no </td></tr>
+ * <tr><td> minimum </td><td> model </td><td> no </td></tr>
+ * <tr><td> model </td><td> progressBar </td><td> no </td></tr>
+ * <tr><td> orientation </td><td> progressBar </td><td> yes </td></tr>
+ * <tr><td> percentComplete </td><td> progressBar </td><td> no </td></tr>
+ * <tr><td> string </td><td> progressBar </td><td> yes </td></tr>
+ * <tr><td> stringPainted </td><td> progressBar </td><td> yes </td></tr>
+ * <tr><td> value </td><td> model </td><td> no </td></tr>
+ * </table>
+ */
+public class JProgressBar extends JComponent implements SwingConstants,
+ Accessible
+{
+ /**
+ * Provides the accessibility features for the <code>JProgressBar</code>
+ * component.
+ */
+ protected class AccessibleJProgressBar extends AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = -2938130009392721813L;
+
+ /**
+ * Creates a new <code>AccessibleJProgressBar</code> instance.
+ */
+ protected AccessibleJProgressBar()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JProgressBar}
+ * component.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet result = super.getAccessibleStateSet();
+ if (orientation == JProgressBar.HORIZONTAL)
+ result.add(AccessibleState.HORIZONTAL);
+ else if (orientation == JProgressBar.VERTICAL)
+ result.add(AccessibleState.VERTICAL);
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the <code>JProgressBar</code> component.
+ *
+ * @return {@link AccessibleRole#PROGRESS_BAR}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PROGRESS_BAR;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current value of the {@link JProgressBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The current value of the {@link JProgressBar} component.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getValue());
+ }
+
+ /**
+ * Sets the current value of the {@link JProgressBar} component and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
+ * listeners. If the supplied value is <code>null</code>, this method
+ * does nothing and returns <code>false</code>.
+ *
+ * @param value the new progress bar value (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if the slider value is updated, and
+ * <code>false</code> otherwise.
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ if (value == null)
+ return false;
+ Number oldValue = getCurrentAccessibleValue();
+ setValue(value.intValue());
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue,
+ new Integer(getValue()));
+ return true;
+ }
+
+ /**
+ * Returns the minimum value of the {@link JProgressBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The minimum value of the {@link JProgressBar} component.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(getMinimum());
+ }
+
+ /**
+ * Returns the maximum value of the {@link JProgressBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The maximum value of the {@link JProgressBar} component.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(getMaximum());
+ }
+ }
+
+ private static final long serialVersionUID = 1980046021813598781L;
+
+ /**
+ * A flag that determines the mode (<code>true</code> for indeterminate,
+ * <code>false</code> for determinate).
+ */
+ private transient boolean indeterminate = false;
+
+ /**
+ * The orientation of the <code>JProgressBar</code>
+ * ({@link SwingConstants#HORIZONTAL} or {@link SwingConstants#VERTICAL}).
+ * Defaults to {@link SwingConstants#HORIZONTAL}.
+ * @see #setOrientation(int)
+ */
+ protected int orientation;
+
+ /**
+ * A flag the controls whether or not the component's border is painted.
+ * The default is <code>true</code>.
+ * @see #setBorderPainted(boolean)
+ */
+ protected boolean paintBorder = true;
+
+ /**
+ * The model defining the bounds and current value for the progress bar.
+ * @see #setModel(BoundedRangeModel)
+ */
+ protected BoundedRangeModel model;
+
+ /**
+ * A custom string for display in the progress bar. If this is
+ * <code>null</code>, a default string will be generated.
+ * @see #setString(String)
+ */
+ protected String progressString;
+
+ /**
+ * A flag that controls whether a string is displayed within the progress
+ * bar.
+ * @see #setStringPainted(boolean)
+ */
+ protected boolean paintString = false;
+
+ /**
+ * A single change event reused for all events.
+ * @see #fireStateChanged()
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * The listener that is registered with the model. */
+ protected ChangeListener changeListener;
+
+ /**
+ * Creates a new <code>JProgressBar</code> with default attributes. The
+ * following defaults are used:
+ * <p>
+ * <ul>
+ * <li><code>value</code>: 0;</li>
+ * <li><code>minimum</code>: 0;</li>
+ * <li><code>maximum</code>: 100;</li>
+ * <li><code>orientation</code>: {@link SwingConstants#HORIZONTAL}.</li>
+ * </ul>
+ */
+ public JProgressBar()
+ {
+ this(HORIZONTAL, 0, 100);
+ }
+
+ /**
+ * Creates a new <code>JProgressBar</code> with the specified
+ * <code>orientation</code>. The following defaults are used:
+ * <p>
+ * <ul>
+ * <li><code>value</code>: 0;</li>
+ * <li><code>minimum</code>: 0;</li>
+ * <li><code>maximum</code>: 100;</li>
+ * </ul>
+ *
+ * @param orientation the orientation ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not one of
+ * the specified values.
+ */
+ public JProgressBar(int orientation)
+ {
+ this(orientation, 0, 100);
+ }
+
+ /**
+ * Creates a new <code>JProgressBar</code> with the specified value range.
+ * The following defaults are used:
+ * <p>
+ * <ul>
+ * <li><code>value</code>: <code>minimum</code>;</li>
+ * <li><code>orientation</code>: {@link SwingConstants#HORIZONTAL}.</li>
+ * </ul>
+ *
+ * @param minimum the lower bound of the value range.
+ * @param maximum the upper bound of the value range.
+ */
+ public JProgressBar(int minimum, int maximum)
+ {
+ this(HORIZONTAL, minimum, maximum);
+ }
+
+ /**
+ * Creates a new <code>JProgressBar</code> with the specified range and
+ * orientation. The following defaults are used:
+ * <p>
+ * <ul>
+ * <li><code>value</code>: <code>minimum</code>;</li>
+ * </ul>
+ *
+ * @param minimum the lower bound of the value range.
+ * @param maximum the upper bound of the value range.
+ * @param orientation the orientation ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not one of
+ * the specified values.
+ */
+ public JProgressBar(int orientation, int minimum, int maximum)
+ {
+ model = new DefaultBoundedRangeModel(minimum, 0, minimum, maximum);
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ this.orientation = orientation;
+ changeListener = createChangeListener();
+ model.addChangeListener(changeListener);
+ updateUI();
+ }
+
+ /**
+ * Creates a new <code>JProgressBar</code> with the specified model. The
+ * following defaults are used:
+ * <p>
+ * <ul>
+ * <li><code>orientation</code>: {@link SwingConstants#HORIZONTAL}.</li>
+ * </ul>
+ *
+ * @param model the model (<code>null</code> not permitted).
+ */
+ public JProgressBar(BoundedRangeModel model)
+ {
+ this.model = model;
+ changeListener = createChangeListener();
+ if (model != null)
+ model.addChangeListener(changeListener);
+ updateUI();
+ }
+
+ /**
+ * Returns the current value for the <code>JProgressBar</code>. This value
+ * is fetched from the model.
+ *
+ * @return The current value.
+ *
+ * @see #setValue(int)
+ */
+ public int getValue()
+ {
+ return model.getValue();
+ }
+
+ /**
+ * Sets the current value for the <code>JProgressBar</code>. The value is
+ * stored in the component's <code>model</code> (see {@link #getModel()}).
+ * If the new value is different to the old value, a {@link ChangeEvent} is
+ * sent to the model's registered listeners. In turn, this triggers a call
+ * to {@link #fireStateChanged()} which will send a <code>ChangeEvent</code>
+ * to this component's registered listeners.
+ * <p>
+ * If <code>value</code> is outside the range <code>minimum</code> to
+ * <code>maximum</code>, it will be set to the nearest of those boundary
+ * values.
+ *
+ * @param value the new value.
+ *
+ * @see #getValue()
+ */
+ public void setValue(int value)
+ {
+ model.setValue(value);
+ }
+
+ /**
+ * Paints the component's border, but only if {@link #isBorderPainted()}
+ * returns <code>true</code>.
+ *
+ * @param graphics the graphics object to paint with.
+ *
+ * @see #setBorderPainted(boolean)
+ */
+ protected void paintBorder(Graphics graphics)
+ {
+ Border border = getBorder();
+ if (paintBorder && border != null)
+ border.paintBorder(this, graphics, 0, 0, getWidth(), getHeight());
+ }
+
+ /**
+ * Returns the orientation of the <code>JProgressBar</code> component, which
+ * is either {@link SwingConstants#HORIZONTAL} or
+ * {@link SwingConstants#VERTICAL}. The default orientation is
+ * <code>HORIZONTAL</code>.
+ *
+ * @return The orientation.
+ *
+ * @see #setOrientation(int)
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * Sets the orientation for this <code>JProgressBar</code> component and,
+ * if the value changes, sends a {@link PropertyChangeEvent} (with the
+ * property name <code>"orientation"</code>) to all registered listeners.
+ *
+ * @param orientation the orientation ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not
+ * one of the listed values.
+ *
+ * @see #getOrientation()
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != VERTICAL && orientation != HORIZONTAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ if (this.orientation != orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", oldOrientation, this.orientation);
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not the string returned by
+ * {@link #getString()} is displayed by the <code>JProgressBar</code>
+ * component.
+ *
+ * @return <code>true</code> if the string should be displayed, and
+ * <code>false</code> otherwise.
+ *
+ * @see #setStringPainted(boolean)
+ */
+ public boolean isStringPainted()
+ {
+ return paintString;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the string returned by
+ * {@link #getString()} is displayed by the <code>JProgressBar</code>
+ * component. If the flag value changes, a {@link PropertyChangeEvent} (with
+ * the property name <code>"stringPainted"</code>) is sent to all registered
+ * listeners.
+ *
+ * @param painted the new flag value.
+ *
+ * @see #isStringPainted()
+ * @see #setString(String)
+ */
+ public void setStringPainted(boolean painted)
+ {
+ if (paintString != painted)
+ {
+ boolean oldPainted = paintString;
+ paintString = painted;
+ firePropertyChange("stringPainted", oldPainted, paintString);
+ }
+ }
+
+ /**
+ * Returns the string that is painted on the <code>JProgressBar</code> if
+ * {@link #isStringPainted()} returns <code>true</code>. If no string has
+ * been explicitly set, this method will return a string displaying the
+ * value of {@link #getPercentComplete()}.
+ *
+ * @return The string.
+ *
+ * @see #setString(String)
+ * @see #setStringPainted(boolean)
+ */
+ public String getString()
+ {
+ if (progressString != null)
+ return progressString;
+ else
+ return (int) (getPercentComplete() * 100) + "%";
+ }
+
+ /**
+ * Sets the string to display within the progress bar and, if the new value
+ * is different to the old value, sends a {@link PropertyChangeEvent} (with
+ * the property name <code>"string"</code>) to all registered listeners. If
+ * the string is set to <code>null</code>, {@link #getString()} will return
+ * a default string.
+ *
+ * @param string the string (<code>null</code> permitted).
+ *
+ * @see #getString()
+ * @see #setStringPainted(boolean)
+ */
+ public void setString(String string)
+ {
+ if (((string == null || progressString == null) &&
+ string != progressString) || (string != null &&
+ ! string.equals(progressString)))
+ {
+ String oldString = progressString;
+ progressString = string;
+ firePropertyChange("string", oldString, progressString);
+ }
+ }
+
+ /**
+ * Returns the current value expressed as a percentage. This is calculated
+ * as <code>(value - min) / (max - min)</code>.
+ *
+ * @return The percentage (a value in the range 0.0 to 1.0).
+ */
+ public double getPercentComplete()
+ {
+ if (getMaximum() == getMinimum())
+ return 1.0;
+ else
+ return (double) (model.getValue() - model.getMinimum())
+ / (model.getMaximum() - model.getMinimum());
+ }
+
+ /**
+ * Returns a flag that controls whether or not the component's border is
+ * painted. The default value is <code>true</code>.
+ *
+ * @return <code>true</code> if the component's border should be painted,
+ * and <code>false</code> otherwise.
+ *
+ * @see #setBorderPainted(boolean)
+ */
+ public boolean isBorderPainted()
+ {
+ return paintBorder;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the component's border is
+ * painted. If the flag value is changed, this method sends a
+ * {@link PropertyChangeEvent} (with the property name "borderPainted") to
+ * all registered listeners.
+ *
+ * @param painted the new flag value.
+ *
+ * @see #isBorderPainted()
+ * @see #paintBorder
+ */
+ public void setBorderPainted(boolean painted)
+ {
+ if (painted != paintBorder)
+ {
+ boolean oldPainted = paintBorder;
+ paintBorder = painted;
+ firePropertyChange("borderPainted", oldPainted, paintBorder);
+ }
+ }
+
+ /**
+ * Returns the UI delegate for this <code>JProgressBar</code>.
+ *
+ * @return The UI delegate.
+ */
+ public ProgressBarUI getUI()
+ {
+ return (ProgressBarUI) ui;
+ }
+
+ /**
+ * Sets the UI delegate for this component.
+ *
+ * @param ui the new UI delegate.
+ */
+ public void setUI(ProgressBarUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Sets this <code>JProgressBar</code>'s UI delegate to the default
+ * (obtained from the {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ProgressBarUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the suffix (<code>"ProgressBarUI"</code> in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a <code>JProgressBar</code>.
+ *
+ * @return <code>"ProgressBarUI"</code>.
+ */
+ public String getUIClassID()
+ {
+ return "ProgressBarUI";
+ }
+
+ /**
+ * Creates a new {@link ChangeListener} that calls
+ * {@link #fireStateChanged()} whenever it receives a {@link ChangeEvent}
+ * (typically from the component's <code>model</code>). This listener is
+ * registered with the progress bar's model, so that changes made to the
+ * model directly will automatically result in the progress bar's listeners
+ * being notified also.
+ *
+ * @return A new listener.
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new ChangeListener()
+ {
+ public void stateChanged(ChangeEvent ce)
+ {
+ fireStateChanged();
+ }
+ };
+ }
+
+ /**
+ * Registers a listener with this component so that it will receive
+ * notification of component state changes.
+ *
+ * @param listener the listener.
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Deregisters a listener so that it no longer receives notification of
+ * component state changes.
+ *
+ * @param listener the listener.
+ *
+ * @see #addChangeListener(ChangeListener)
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Returns an array of the listeners that are registered with this component.
+ * The array may be empty, but is never <code>null</code>.
+ *
+ * @return An array of listeners.
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Sends a {@link ChangeEvent} to all registered listeners to indicate that
+ * the state of the <code>JProgressBar</code> has changed.
+ *
+ * @see #createChangeListener()
+ */
+ protected void fireStateChanged()
+ {
+ Object[] changeListeners = listenerList.getListenerList();
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ for (int i = changeListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (changeListeners[i] == ChangeListener.class)
+ ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
+ }
+ }
+
+ /**
+ * Returns the model for the <code>JProgressBar</code>.
+ *
+ * @return The model (never <code>null</code>).
+ *
+ * @see #setModel(BoundedRangeModel)
+ */
+ public BoundedRangeModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Sets the model for the <code>JProgressBar</code> and sends a
+ * {@link ChangeEvent} to all registered listeners.
+ *
+ * @param model the model (<code>null</code> not permitted).
+ *
+ * @see #getModel()
+ */
+ public void setModel(BoundedRangeModel model)
+ {
+ if (model != this.model)
+ {
+ this.model.removeChangeListener(changeListener);
+ this.model = model;
+ this.model.addChangeListener(changeListener);
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the minimum value for the <code>JProgressBar</code>. This defines
+ * the lower bound for the current value, and is stored in the component's
+ * <code>model</code>.
+ *
+ * @return The minimum value.
+ *
+ * @see #setMinimum(int)
+ */
+ public int getMinimum()
+ {
+ return model.getMinimum();
+ }
+
+ /**
+ * Sets the minimum value for the <code>JProgressBar</code>. The value is
+ * stored in the component's <code>model</code> (see {@link #getModel()}).
+ * If the new value is different to the old value, a {@link ChangeEvent} is
+ * sent to the model's registered listeners. In turn, this triggers a call
+ * to {@link #fireStateChanged()} which will send a <code>ChangeEvent</code>
+ * to this component's registered listeners.
+ *
+ * @param minimum the minimum value.
+ *
+ * @see #getMinimum()
+ */
+ public void setMinimum(int minimum)
+ {
+ model.setMinimum(minimum);
+ }
+
+ /**
+ * Returns the maximum value for the <code>JProgressBar</code>. This defines
+ * the upper bound for the current value, and is stored in the component's
+ * <code>model</code>.
+ *
+ * @return The maximum value.
+ *
+ * @see #setMaximum(int)
+ */
+ public int getMaximum()
+ {
+ return model.getMaximum();
+ }
+
+ /**
+ * Sets the maximum value for the <code>JProgressBar</code>. The value is
+ * stored in the component's <code>model</code> (see {@link #getModel()}).
+ * If the new value is different to the old value, a {@link ChangeEvent} is
+ * sent to the model's registered listeners. In turn, this triggers a call
+ * to {@link #fireStateChanged()} which will send a <code>ChangeEvent</code>
+ * to this component's registered listeners.
+ *
+ * @param maximum the maximum value.
+ *
+ * @see #getMaximum()
+ */
+ public void setMaximum(int maximum)
+ {
+ model.setMaximum(maximum);
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this <code>JProgressBar</code>.
+ *
+ * @return A string describing the attributes of this
+ * <code>JProgressBar</code> (never <code>null</code>).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",orientation=");
+ if (orientation == HORIZONTAL)
+ sb.append("HORIZONTAL");
+ else
+ sb.append("VERTICAL");
+ sb.append(",paintBorder=").append(isBorderPainted());
+ sb.append(",paintString=").append(isStringPainted());
+ sb.append(",progressString=");
+ if (progressString != null)
+ sb.append(progressString);
+ sb.append(",indeterminateString=").append(isIndeterminate());
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * Sets the flag that controls the mode for this <code>JProgressBar</code>
+ * (<code>true</code> for indeterminate mode, and <code>false</code> for
+ * determinate mode). If the flag value changes, this method sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * <code>"indeterminate"</code>) to all registered listeners.
+ * <p>
+ * If the <code>JProgressBar</code> is determinate, it paints a percentage
+ * of the bar described by its value. If it is indeterminate, it simply
+ * bounces a box between the ends of the bar; the value of the
+ * <code>JProgressBar</code> is ignored.
+ *
+ * @param flag the new flag value.
+ *
+ * @see #isIndeterminate()
+ * @since 1.4
+ */
+ public void setIndeterminate(boolean flag)
+ {
+ if (indeterminate != flag)
+ {
+ indeterminate = flag;
+ firePropertyChange("indeterminate", !flag, indeterminate);
+ }
+ }
+
+ /**
+ * Returns a flag that indicates the mode for this <code>JProgressBar</code>
+ * (<code>true</code> for indeterminate mode, and <code>false</code> for
+ * determinate mode).
+ *
+ * @return A flag indicating the mode for the <code>JProgressBar</code>.
+ *
+ * @see #setIndeterminate(boolean)
+ * @since 1.4
+ */
+ public boolean isIndeterminate()
+ {
+ return indeterminate;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JProgressBar</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJProgressBar}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJProgressBar();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JRadioButton.java b/libjava/classpath/javax/swing/JRadioButton.java
new file mode 100644
index 000000000..ae42f2c01
--- /dev/null
+++ b/libjava/classpath/javax/swing/JRadioButton.java
@@ -0,0 +1,256 @@
+/* JRadioButton.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.ButtonUI;
+
+/**
+ * The <code>JRadioButton</code> component provides a visually selectable
+ * button with mutually exclusive behaviour within a <code>ButtonGroup</code>.
+ * A series of radio buttons can be used to provide options to the user,
+ * where the user can only select one of the available options. The state
+ * of the button is provided by the superclass, <code>JToggleButton</code>.
+ * <code>JRadioButton</code> adds the additional behaviour, that if two
+ * or more radio buttons are grouped together, the selection of one implies
+ * the deselection of the other buttons within the group.
+ * <p>
+ *
+ * Buttons are grouped by adding each instance to a <code>ButtonGroup</code>.
+ * The existence of such a grouping is not reflected visually, so other means
+ * should be used to denote this. For instance, the grouped buttons can be placed
+ * within the same panel, possibly with an appropriate border to denote
+ * the connection between the components.
+ *
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see JToggleButton
+ * @see ButtonGroup
+ * @since 1.2
+ */
+public class JRadioButton extends JToggleButton
+{
+ /**
+ * Compatible with Sun's JDK.
+ */
+ private static final long serialVersionUID = 7751949583255506856L;
+
+ /**
+ * This class provides accessibility support for the toggle button.
+ */
+ protected class AccessibleJRadioButton
+ extends AccessibleJToggleButton
+ {
+ private static final long serialVersionUID = 4850967637026120674L;
+
+ /**
+ * Constructor for the accessible toggle button.
+ */
+ protected AccessibleJRadioButton()
+ {
+ /* Call the superclass to register for events */
+ super();
+ }
+
+ /**
+ * Returns the accessible role for the toggle button.
+ *
+ * @return An instance of <code>AccessibleRole</code>, describing
+ * the role of the toggle button.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.RADIO_BUTTON;
+ }
+
+ }
+
+ /**
+ * Constructs an unselected radio button with no text or icon.
+ */
+ public JRadioButton()
+ {
+ this(null, null, false);
+ }
+
+ /**
+ * Constructs a radio button using the labelling, state
+ * and icon specified by the supplied action.
+ *
+ * @param a the action to use to define the properties of the button.
+ */
+ public JRadioButton(Action a)
+ {
+ this();
+ setAction(a);
+ }
+
+ /**
+ * Constructs an unselected radio button with the supplied icon
+ * and no text.
+ *
+ * @param icon the icon to use.
+ */
+ public JRadioButton(Icon icon)
+ {
+ this(null, icon, false);
+ }
+
+ /**
+ * Constructs a radio button with the supplied icon and state.
+ *
+ * @param icon the icon to use.
+ * @param selected if true, the radio button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JRadioButton(Icon icon, boolean selected)
+ {
+ this(null, icon, selected);
+ }
+
+ /**
+ * Constructs an unselected radio button using the supplied text
+ * and no icon.
+ *
+ * @param text the text to use.
+ */
+ public JRadioButton(String text)
+ {
+ this(text, null, false);
+ }
+
+ /**
+ * Constructs a radio button with the supplied text and state.
+ *
+ * @param text the text to use.
+ * @param selected if true, the radio button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JRadioButton(String text, boolean selected)
+ {
+ this(text, null, selected);
+ }
+
+ /**
+ * Constructs an unselected radio button with the supplied text
+ * and icon.
+ *
+ * @param text the text to use.
+ * @param icon the icon to use.
+ */
+ public JRadioButton(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ /**
+ * Constructs a radio button with the supplied text, icon and state.
+ *
+ * @param text the text to use.
+ * @param icon the icon to use.
+ * @param selected if true, the radio button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JRadioButton(String text, Icon icon, boolean selected)
+ {
+ super(text, icon, selected);
+ setBorderPainted(false);
+ setHorizontalAlignment(LEADING);
+ }
+
+ /**
+ * Returns the accessible context for this <code>JRadioButton</code>,
+ * in the form of an instance of <code>AccessibleJRadioButton</code>.
+ * The context is created, if necessary.
+ *
+ * @return the associated context
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ /* Create the context if this is the first request */
+ if (accessibleContext == null)
+ {
+ /* Create the context */
+ accessibleContext = new AccessibleJRadioButton();
+ }
+ return accessibleContext;
+ }
+
+ /**
+ * Returns a string specifying the name of the Look and Feel UI class
+ * that renders this component.
+ *
+ * @return the Look and Feel UI class for <code>JRadioButton</code>s
+ * as a <code>String</code>.
+ */
+ public String getUIClassID()
+ {
+ return "RadioButtonUI";
+ }
+
+ /**
+ * Returns a string representation of this component for debugging use.
+ * Users should not depend on anything as regards the content or formatting
+ * of this string, except for the fact that the returned string may never be
+ * null (only empty).
+ *
+ * @return the component in <code>String</code> form for debugging.
+ */
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+
+ /**
+ * This method resets the radio button's UI delegate to the default UI for
+ * the current look and feel.
+ */
+ public void updateUI()
+ {
+ /*
+ I can't see any difference between this and the superclass one,
+ but Sun reimplements it... there is no RadioButtonUI class for it
+ to be cast to.
+ */
+ setUI((ButtonUI) UIManager.getUI(this));
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java
new file mode 100644
index 000000000..48fc9aeb6
--- /dev/null
+++ b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java
@@ -0,0 +1,230 @@
+/* JRadioButtonMenuItem.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * This class represents JRadioButtonMenuItem. Its behaviour is very similar
+ * to JRadioButton. Just like JRadioButton, user can check and uncheck this
+ * menu item by clicking on it. JRadioButtonMenuItem uses ToggleButtonModel
+ * to keep track of its selection. If the JRadioButtonMenuItem is included in
+ * the button group, then only one JRadioButtonMenuItem can be selected at
+ * one time.
+ */
+public class JRadioButtonMenuItem extends JMenuItem implements Accessible
+{
+ private static final long serialVersionUID = 8482658191548521743L;
+
+ /** name for the UI delegate for this radio button menu item. */
+ private static final String uiClassID = "RadioButtonMenuItemUI";
+
+ /**
+ * Creates a new JRadioButtonMenuItem object.
+ */
+ public JRadioButtonMenuItem()
+ {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified icon
+ *
+ * @param icon Icon to be used for this menu item
+ */
+ public JRadioButtonMenuItem(Icon icon)
+ {
+ this(null, icon);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified label
+ *
+ * @param text Label for this menu item
+ */
+ public JRadioButtonMenuItem(String text)
+ {
+ this(text, null);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem using specified action
+ *
+ * @param action Action for this menu item
+ */
+ public JRadioButtonMenuItem(Action action)
+ {
+ this();
+ setAction(action);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified label and icon
+ *
+ * @param text Label for this menu item
+ * @param icon Icon for this menu item
+ */
+ public JRadioButtonMenuItem(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified label
+ * and marked selected if 'selected' is true.
+ *
+ * @param text Text for this menu item
+ * @param selected Selected state of this menu item
+ */
+ public JRadioButtonMenuItem(String text, boolean selected)
+ {
+ this(text, null, selected);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified icon
+ * and given selected state
+ *
+ * @param icon Icon for this menu item
+ * @param selected Selected state for this menu item
+ */
+ public JRadioButtonMenuItem(Icon icon, boolean selected)
+ {
+ this(null, icon, selected);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified label,
+ * icon and selected state.
+ *
+ * @param text Label for this menu item
+ * @param icon Icon to be use for this menu item
+ * @param selected selected state of this menu item
+ */
+ public JRadioButtonMenuItem(String text, Icon icon, boolean selected)
+ {
+ super(text, icon);
+ setModel(new JToggleButton.ToggleButtonModel());
+ model.setSelected(selected);
+ setFocusable(false);
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menuItem.
+ *
+ * @return The Look and Feel classID. "JRadioButtonMenuItemUI"
+ */
+ public String getUIClassID()
+ {
+ return uiClassID;
+ }
+
+ /**
+ * This method overrides JComponent.requestFocus with an empty
+ * implementation, since JRadioButtonMenuItems should not
+ * receve focus in general.
+ */
+ public void requestFocus()
+ {
+ // Should do nothing here
+ }
+
+ /**
+ * Returns a string describing the attributes for the
+ * <code>JRadioButtonMenuItem</code> component, for use in debugging. The
+ * return value is guaranteed to be non-<code>null</code>, but the format of
+ * the string may vary between implementations.
+ *
+ * @return A string describing the attributes of the
+ * <code>JRadioButtonMenuItem</code>.
+ */
+ protected String paramString()
+ {
+ // calling super seems to be sufficient here...
+ return super.paramString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JRadioButtonMenuItem</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJRadioButtonMenuItem}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJRadioButtonMenuItem();
+
+ return accessibleContext;
+ }
+
+ /**
+ * Provides the accessibility features for the
+ * <code>JRadioButtonMenuItem</code> component.
+ *
+ * @see JRadioButtonMenuItem#getAccessibleContext()
+ */
+ protected class AccessibleJRadioButtonMenuItem extends AccessibleJMenuItem
+ {
+ private static final long serialVersionUID = 4381471510145292179L;
+
+ /**
+ * Creates a new <code>AccessibleJRadioButtonMenuItem</code> instance.
+ */
+ protected AccessibleJRadioButtonMenuItem()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the <code>JRadioButtonMenuItem</code>
+ * component.
+ *
+ * @return {@link AccessibleRole#RADIO_BUTTON}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.RADIO_BUTTON;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JRootPane.java b/libjava/classpath/javax/swing/JRootPane.java
new file mode 100644
index 000000000..c74f4f520
--- /dev/null
+++ b/libjava/classpath/javax/swing/JRootPane.java
@@ -0,0 +1,700 @@
+/* JRootPane.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.IllegalComponentStateException;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.LayoutManager2;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.RootPaneUI;
+
+/**
+ * This class is where JComponents are added to. Unlike awt where you could
+ * just say frame.add(), with swing you need to say frame.getRootPane()
+ * (which delivers an instance of this class) and add your components to
+ * that. It is implemented by several 'layers' (pane() should be read as
+ * plane()) each on top of the others where you can add components to.
+ * (getContentPane(), getGlassPane(), getLayeredPane())
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JRootPane extends JComponent implements Accessible
+{
+ // The class used to obtain the accessible role for this object.
+ protected class AccessibleJRootPane extends AccessibleJComponent
+ {
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 1082432482784468088L;
+
+ /**
+ * Creates a new <code>AccessibleJRootPane</code> object.
+ */
+ protected AccessibleJRootPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.ROOT_PANE;
+ }
+ }
+
+ // Custom Layout Manager for JRootPane. It positions contentPane and
+ // menuBar withing its layeredPane.
+ protected class RootLayout implements LayoutManager2, Serializable
+ {
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -4100116998559815027L;
+
+ /**
+ * The cached layout info for the glass pane.
+ */
+ private Rectangle glassPaneBounds;
+
+ /**
+ * The cached layout info for the layered pane.
+ */
+ private Rectangle layeredPaneBounds;
+
+ /**
+ * The cached layout info for the content pane.
+ */
+ private Rectangle contentPaneBounds;
+
+ /**
+ * The cached layout info for the menu bar.
+ */
+ private Rectangle menuBarBounds;
+
+ /**
+ * Creates a new <code>RootLayout</code> object.
+ */
+ protected RootLayout()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param comp DOCUMENT ME!
+ * @param constraints DOCUMENT ME!
+ */
+ public void addLayoutComponent(Component comp, Object constraints)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param name DOCUMENT ME!
+ * @param comp DOCUMENT ME!
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public float getLayoutAlignmentX(Container target)
+ {
+ return 0.0F;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public float getLayoutAlignmentY(Container target)
+ {
+ return 0.0F;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ */
+ public void invalidateLayout(Container target)
+ {
+ synchronized (this)
+ {
+ glassPaneBounds = null;
+ layeredPaneBounds = null;
+ contentPaneBounds = null;
+ menuBarBounds = null;
+ }
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param c DOCUMENT ME!
+ */
+ public void layoutContainer(Container c)
+ {
+ if (glassPaneBounds == null || layeredPaneBounds == null
+ || contentPaneBounds == null || menuBarBounds == null)
+ {
+ Insets i = getInsets();
+ int containerWidth = c.getBounds().width - i.left - i.right;
+ int containerHeight = c.getBounds().height - i.top - i.bottom;
+
+ // 1. the glassPane fills entire viewable region (bounds - insets).
+ // 2. the layeredPane filles entire viewable region.
+ // 3. the menuBar is positioned at the upper edge of layeredPane.
+ // 4. the contentPane fills viewable region minus menuBar, if present.
+
+
+ // +-------------------------------+
+ // | JLayeredPane |
+ // | +--------------------------+ |
+ // | | menuBar | |
+ // | +--------------------------+ |
+ // | +--------------------------+ |
+ // | |contentPane | |
+ // | | | |
+ // | | | |
+ // | | | |
+ // | +--------------------------+ |
+ // +-------------------------------+
+
+ if (menuBar != null)
+ {
+ Dimension menuBarSize = menuBar.getPreferredSize();
+ if (menuBarSize.height > containerHeight)
+ menuBarSize.height = containerHeight;
+ menuBarBounds = new Rectangle(0, 0, containerWidth,
+ menuBarSize.height);
+ contentPaneBounds = new Rectangle(0, menuBarSize.height,
+ containerWidth,
+ containerHeight - menuBarSize.height);
+ }
+ else
+ contentPaneBounds = new Rectangle(0, 0, containerWidth,
+ containerHeight);
+
+ glassPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
+ layeredPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
+ }
+
+ glassPane.setBounds(glassPaneBounds);
+ layeredPane.setBounds(layeredPaneBounds);
+ if (menuBar != null)
+ menuBar.setBounds(menuBarBounds);
+ getContentPane().setBounds(contentPaneBounds);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension maximumLayoutSize(Container target)
+ {
+ return preferredLayoutSize(target);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension minimumLayoutSize(Container target)
+ {
+ return preferredLayoutSize(target);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param c DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ Dimension prefSize = new Dimension();
+ Insets i = getInsets();
+ prefSize = new Dimension(i.left + i.right, i.top + i.bottom);
+ Dimension contentPrefSize = getContentPane().getPreferredSize();
+ prefSize.width += contentPrefSize.width;
+ prefSize.height += contentPrefSize.height;
+ if (menuBar != null)
+ {
+ Dimension menuBarSize = menuBar.getPreferredSize();
+ if (menuBarSize.width > contentPrefSize.width)
+ prefSize.width += menuBarSize.width - contentPrefSize.width;
+ prefSize.height += menuBarSize.height;
+ }
+ return prefSize;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param comp DOCUMENT ME!
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = 8690748000348575668L;
+
+ public static final int NONE = 0;
+ public static final int FRAME = 1;
+ public static final int PLAIN_DIALOG = 2;
+ public static final int INFORMATION_DIALOG = 3;
+ public static final int ERROR_DIALOG = 4;
+ public static final int COLOR_CHOOSER_DIALOG = 5;
+ public static final int FILE_CHOOSER_DIALOG = 6;
+ public static final int QUESTION_DIALOG = 7;
+ public static final int WARNING_DIALOG = 8;
+
+ /** DOCUMENT ME! */
+ protected Component glassPane;
+
+ /** DOCUMENT ME! */
+ protected JLayeredPane layeredPane;
+
+ /** DOCUMENT ME! */
+ protected JMenuBar menuBar;
+
+ /** DOCUMENT ME! */
+ protected Container contentPane;
+
+ protected JButton defaultButton;
+
+ /**
+ * This field is unused since JDK1.3. To override the default action you
+ * should modify the JRootPane's ActionMap.
+ *
+ * @deprecated since JDK1.3
+ *
+ * @specnote the specs indicate that the type of this field is
+ * a package private inner class
+ * javax.swing.JRootPane.DefaultAction. I assume that the closest
+ * public superclass is javax.swing.Action.
+ */
+ protected Action defaultPressAction;
+
+ /**
+ * This field is unused since JDK1.3. To override the default action you
+ * should modify the JRootPane's ActionMap.
+ *
+ * @deprecated since JDK1.3
+ *
+ * @specnote the specs indicate that the type of this field is
+ * a package private inner class
+ * javax.swing.JRootPane.DefaultAction. I assume that the closest
+ * public superclass is javax.swing.Action.
+ */
+ protected Action defaultReleaseAction;
+
+ /**
+ * @since 1.4
+ */
+ private int windowDecorationStyle = NONE;
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param m DOCUMENT ME!
+ */
+ public void setJMenuBar(JMenuBar m)
+ {
+ JLayeredPane jlPane = getLayeredPane();
+ if (menuBar != null)
+ jlPane.remove(menuBar);
+ menuBar = m;
+ if (menuBar != null)
+ jlPane.add(menuBar, JLayeredPane.FRAME_CONTENT_LAYER);
+ }
+
+ /**
+ * @deprecated Replaced by <code>setJMenuBar()</code>
+ */
+ public void setMenuBar(JMenuBar m)
+ {
+ setJMenuBar(m);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public JMenuBar getJMenuBar()
+ {
+ return menuBar;
+ }
+
+ /**
+ * @deprecated Replaced by <code>getJMenuBar()</code>
+ */
+ public JMenuBar getMenuBar()
+ {
+ return getJMenuBar();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public boolean isValidateRoot()
+ {
+ return true;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Container getContentPane()
+ {
+ if (contentPane == null)
+ setContentPane(createContentPane());
+ return contentPane;
+ }
+
+ /**
+ * Sets the JRootPane's content pane. The content pane should typically be
+ * opaque for painting to work properly. This method also
+ * removes the old content pane from the layered pane.
+ *
+ * @param p the Container that will be the content pane
+ * @throws IllegalComponentStateException if p is null
+ */
+ public void setContentPane(Container p)
+ {
+ if (p == null)
+ throw new IllegalComponentStateException ("cannot " +
+ "have a null content pane");
+ else
+ {
+ if (contentPane != null && contentPane.getParent() == layeredPane)
+ layeredPane.remove(contentPane);
+ contentPane = p;
+ getLayeredPane().add(contentPane, JLayeredPane.FRAME_CONTENT_LAYER);
+ }
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param comp DOCUMENT ME!
+ * @param constraints DOCUMENT ME!
+ * @param index DOCUMENT ME!
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ super.addImpl(comp, constraints, index);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Component getGlassPane()
+ {
+ if (glassPane == null)
+ setGlassPane(createGlassPane());
+ return glassPane;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param f DOCUMENT ME!
+ */
+ public void setGlassPane(Component f)
+ {
+ if (glassPane != null)
+ remove(glassPane);
+
+ glassPane = f;
+
+ glassPane.setVisible(false);
+ add(glassPane, 0);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public JLayeredPane getLayeredPane()
+ {
+ if (layeredPane == null)
+ setLayeredPane(createLayeredPane());
+ return layeredPane;
+ }
+
+ /**
+ * Set the layered pane for the root pane.
+ *
+ * @param f The JLayeredPane to be used.
+ *
+ * @throws IllegalComponentStateException if JLayeredPane
+ * parameter is null.
+ */
+ public void setLayeredPane(JLayeredPane f)
+ {
+ if (f == null)
+ throw new IllegalComponentStateException();
+
+ if (layeredPane != null)
+ remove(layeredPane);
+
+ layeredPane = f;
+ add(f, -1);
+ }
+
+ /**
+ * Creates a new <code>JRootPane</code> object.
+ */
+ public JRootPane()
+ {
+ setLayout(createRootLayout());
+ getGlassPane();
+ getLayeredPane();
+ getContentPane();
+ setOpaque(true);
+ updateUI();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected LayoutManager createRootLayout()
+ {
+ return new RootLayout();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected Container createContentPane()
+ {
+ JPanel p = new JPanel();
+ p.setName(this.getName() + ".contentPane");
+ p.setLayout(new BorderLayout());
+ return p;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected Component createGlassPane()
+ {
+ JPanel p = new JPanel();
+ p.setName(this.getName() + ".glassPane");
+ p.setVisible(false);
+ p.setOpaque(false);
+ return p;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected JLayeredPane createLayeredPane()
+ {
+ JLayeredPane l = new JLayeredPane();
+ l.setLayout(null);
+ return l;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public RootPaneUI getUI()
+ {
+ return (RootPaneUI) ui;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param ui DOCUMENT ME!
+ */
+ public void setUI(RootPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ public void updateUI()
+ {
+ setUI((RootPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public String getUIClassID()
+ {
+ return "RootPaneUI";
+ }
+
+ public JButton getDefaultButton()
+ {
+ return defaultButton;
+ }
+
+ public void setDefaultButton(JButton newButton)
+ {
+ // We only change the default button if the new button is defaultCapable
+ // or null and the old and new button are different objects.
+ if (defaultButton != newButton
+ && (newButton == null || newButton.isDefaultCapable()))
+ {
+ JButton oldButton = defaultButton;
+ defaultButton = newButton;
+ firePropertyChange("defaultButton", oldButton, newButton);
+ }
+ }
+
+ /**
+ * @since 1.4
+ */
+ public int getWindowDecorationStyle()
+ {
+ return windowDecorationStyle;
+ }
+
+ /**
+ * @since 1.4
+ */
+ public void setWindowDecorationStyle(int style)
+ {
+ if (style != NONE
+ && style != FRAME
+ && style != INFORMATION_DIALOG
+ && style != ERROR_DIALOG
+ && style != COLOR_CHOOSER_DIALOG
+ && style != FILE_CHOOSER_DIALOG
+ && style != QUESTION_DIALOG
+ && style != WARNING_DIALOG
+ && style != PLAIN_DIALOG)
+ throw new IllegalArgumentException("invalid style");
+
+ int oldStyle = windowDecorationStyle;
+ windowDecorationStyle = style;
+ firePropertyChange("windowDecorationStyle", oldStyle, style);
+ }
+
+ /**
+ * This returns <code>true</code> if the <code>glassPane</code> is not
+ * visible because then the root pane can guarantee to tile its children
+ * (the only other direct child is a JLayeredPane which must figure its
+ * <code>optimizeDrawingEnabled</code> state on its own).
+ *
+ * @return <code>true</code> if the <code>glassPane</code> is not
+ * visible
+ */
+ public boolean isOptimizedDrawingEnable()
+ {
+ return ! glassPane.isVisible();
+ }
+
+ /**
+ * Returns the accessible context for this JRootPane. This will be
+ * an instance of {@link AccessibleJRootPane}.
+ *
+ * @return the accessible context for this JRootPane
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJRootPane();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JScrollBar.java b/libjava/classpath/javax/swing/JScrollBar.java
new file mode 100644
index 000000000..a60031670
--- /dev/null
+++ b/libjava/classpath/javax/swing/JScrollBar.java
@@ -0,0 +1,700 @@
+/* JScrollBar.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Adjustable;
+import java.awt.Dimension;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ScrollBarUI;
+
+/**
+ * The JScrollBar. Two buttons control how the values that the
+ * scroll bar can take. You can also drag the thumb or click the track
+ * to move the scroll bar. Typically, the JScrollBar is used with
+ * other components to translate the value of the bar to the viewable
+ * contents of the other components.
+ */
+public class JScrollBar extends JComponent implements Adjustable, Accessible
+{
+ /**
+ * Provides the accessibility features for the <code>JScrollBar</code>
+ * component.
+ */
+ protected class AccessibleJScrollBar extends JComponent.AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = -7758162392045586663L;
+
+ /**
+ * Creates a new <code>AccessibleJScrollBar</code> instance.
+ */
+ protected AccessibleJScrollBar()
+ {
+ super();
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JScrollBar}
+ * component.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet result = super.getAccessibleStateSet();
+ if (orientation == JScrollBar.HORIZONTAL)
+ result.add(AccessibleState.HORIZONTAL);
+ else if (orientation == JScrollBar.VERTICAL)
+ result.add(AccessibleState.VERTICAL);
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the <code>JScrollBar</code> component.
+ *
+ * @return {@link AccessibleRole#SCROLL_BAR}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SCROLL_BAR;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current value of the {@link JScrollBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The current value of the {@link JScrollBar} component.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getValue());
+ }
+
+ /**
+ * Sets the current value of the {@link JScrollBar} component and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
+ * listeners. If the supplied value is <code>null</code>, this method
+ * does nothing and returns <code>false</code>.
+ *
+ * @param value the new slider value (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if the slider value is updated, and
+ * <code>false</code> otherwise.
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ if (value == null)
+ return false;
+ Number oldValue = getCurrentAccessibleValue();
+ setValue(value.intValue());
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue,
+ new Integer(getValue()));
+ return true;
+ }
+
+ /**
+ * Returns the minimum value of the {@link JScrollBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The minimum value of the {@link JScrollBar} component.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(getMinimum());
+ }
+
+ /**
+ * Returns the maximum value of the {@link JScrollBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The maximum value of the {@link JScrollBar} component.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(getMaximum() - model.getExtent());
+ }
+ }
+
+ /**
+ * Listens for changes on the model and fires them to interested
+ * listeners on the JScrollBar, after re-sourcing them.
+ */
+ private class ScrollBarChangeListener
+ implements ChangeListener
+ {
+
+ public void stateChanged(ChangeEvent event)
+ {
+ Object o = event.getSource();
+ if (o instanceof BoundedRangeModel)
+ {
+ BoundedRangeModel m = (BoundedRangeModel) o;
+ fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
+ AdjustmentEvent.TRACK, m.getValue(),
+ m.getValueIsAdjusting());
+ }
+ }
+
+ }
+
+ private static final long serialVersionUID = -8195169869225066566L;
+
+ /** How much the thumb moves when moving in a block. */
+ protected int blockIncrement = 10;
+
+ /** The model that holds the scroll bar's data. */
+ protected BoundedRangeModel model;
+
+ /** The orientation of the scroll bar. */
+ protected int orientation = SwingConstants.VERTICAL;
+
+ /** How much the thumb moves when moving in a unit. */
+ protected int unitIncrement = 1;
+
+ /**
+ * This ChangeListener forwards events fired from the model and re-sources
+ * them to originate from this JScrollBar.
+ */
+ private ChangeListener sbChangeListener;
+
+ /**
+ * Creates a new horizontal JScrollBar object with a minimum
+ * of 0, a maxmium of 100, a value of 0 and an extent of 10.
+ */
+ public JScrollBar()
+ {
+ this(SwingConstants.VERTICAL, 0, 10, 0, 100);
+ }
+
+ /**
+ * Creates a new JScrollBar object with a minimum of 0, a
+ * maximum of 100, a value of 0, an extent of 10 and the given
+ * orientation.
+ *
+ * @param orientation The orientation of the JScrollBar.
+ */
+ public JScrollBar(int orientation)
+ {
+ this(orientation, 0, 10, 0, 100);
+ }
+
+ /**
+ * Creates a new JScrollBar object with the given orientation,
+ * value, min, max, and extent.
+ *
+ * @param orientation The orientation to use.
+ * @param value The value to use.
+ * @param extent The extent to use.
+ * @param min The minimum value of the scrollbar.
+ * @param max The maximum value of the scrollbar.
+ */
+ public JScrollBar(int orientation, int value, int extent, int min, int max)
+ {
+ model = new DefaultBoundedRangeModel(value, extent, min, max);
+ sbChangeListener = new ScrollBarChangeListener();
+ model.addChangeListener(sbChangeListener);
+ if (orientation != SwingConstants.HORIZONTAL
+ && orientation != SwingConstants.VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ this.orientation = orientation;
+ updateUI();
+ }
+
+ /**
+ * This method sets the UI of this scrollbar to
+ * the given UI.
+ *
+ * @param ui The UI to use with this scrollbar.
+ */
+ public void setUI(ScrollBarUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method returns the UI that is being used
+ * with this scrollbar.
+ *
+ * @return The scrollbar's current UI.
+ */
+ public ScrollBarUI getUI()
+ {
+ return (ScrollBarUI) ui;
+ }
+
+ /**
+ * This method changes the UI to be the
+ * default for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ScrollBarUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns an identifier to
+ * choose the correct UI delegate for the
+ * scrollbar.
+ *
+ * @return The identifer to choose the UI delegate; "ScrollBarUI"
+ */
+ public String getUIClassID()
+ {
+ return "ScrollBarUI";
+ }
+
+ /**
+ * This method returns the orientation of the scrollbar.
+ *
+ * @return The orientation of the scrollbar.
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * This method sets the orientation of the scrollbar.
+ *
+ * @param orientation The orientation of the scrollbar.
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != SwingConstants.HORIZONTAL
+ && orientation != SwingConstants.VERTICAL)
+ throw new IllegalArgumentException("orientation must be one of HORIZONTAL or VERTICAL");
+ if (orientation != this.orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", oldOrientation,
+ this.orientation);
+ }
+ }
+
+ /**
+ * This method returns the model being used with
+ * the scrollbar.
+ *
+ * @return The scrollbar's model.
+ */
+ public BoundedRangeModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * This method sets the model to use with
+ * the scrollbar.
+ *
+ * @param newModel The new model to use with the scrollbar.
+ */
+ public void setModel(BoundedRangeModel newModel)
+ {
+ BoundedRangeModel oldModel = model;
+ if (oldModel != null)
+ oldModel.removeChangeListener(sbChangeListener);
+ model = newModel;
+ if (model != null)
+ model.addChangeListener(sbChangeListener);
+ firePropertyChange("model", oldModel, model);
+ }
+
+ /**
+ * This method returns how much the scrollbar's value
+ * should change for a unit increment depending on the
+ * given direction.
+ *
+ * @param direction The direction to scroll in.
+ *
+ * @return The amount the scrollbar's value will change given the direction.
+ */
+ public int getUnitIncrement(int direction)
+ {
+ return unitIncrement;
+ }
+
+ /**
+ * This method sets the unitIncrement property.
+ *
+ * @param unitIncrement The new unitIncrement.
+ */
+ public void setUnitIncrement(int unitIncrement)
+ {
+ if (unitIncrement != this.unitIncrement)
+ {
+ int oldInc = this.unitIncrement;
+ this.unitIncrement = unitIncrement;
+ firePropertyChange("unitIncrement", oldInc,
+ this.unitIncrement);
+ }
+ }
+
+ /**
+ * The method returns how much the scrollbar's value
+ * should change for a block increment depending on
+ * the given direction.
+ *
+ * @param direction The direction to scroll in.
+ *
+ * @return The amount the scrollbar's value will change given the direction.
+ */
+ public int getBlockIncrement(int direction)
+ {
+ return blockIncrement;
+ }
+
+ /**
+ * This method sets the blockIncrement property.
+ *
+ * @param blockIncrement The new blockIncrement.
+ */
+ public void setBlockIncrement(int blockIncrement)
+ {
+ if (blockIncrement != this.blockIncrement)
+ {
+ int oldInc = this.blockIncrement;
+ this.blockIncrement = blockIncrement;
+ firePropertyChange("blockIncrement", oldInc,
+ this.blockIncrement);
+ }
+ }
+
+ /**
+ * This method returns the unitIncrement.
+ *
+ * @return The unitIncrement.
+ */
+ public int getUnitIncrement()
+ {
+ return unitIncrement;
+ }
+
+ /**
+ * This method returns the blockIncrement.
+ *
+ * @return The blockIncrement.
+ */
+ public int getBlockIncrement()
+ {
+ return blockIncrement;
+ }
+
+ /**
+ * This method returns the value of the scrollbar.
+ *
+ * @return The value of the scrollbar.
+ */
+ public int getValue()
+ {
+ return model.getValue();
+ }
+
+ /**
+ * This method changes the value of the scrollbar.
+ *
+ * @param value The new value of the scrollbar.
+ */
+ public void setValue(int value)
+ {
+ model.setValue(value);
+ }
+
+ /**
+ * This method returns the visible amount (AKA extent).
+ * The visible amount can be used by UI delegates to
+ * determine the size of the thumb.
+ *
+ * @return The visible amount (AKA extent).
+ */
+ public int getVisibleAmount()
+ {
+ return model.getExtent();
+ }
+
+ /**
+ * This method sets the visible amount (AKA extent).
+ *
+ * @param extent The visible amount (AKA extent).
+ */
+ public void setVisibleAmount(int extent)
+ {
+ model.setExtent(extent);
+ }
+
+ /**
+ * This method returns the minimum value of the scrollbar.
+ *
+ * @return The minimum value of the scrollbar.
+ */
+ public int getMinimum()
+ {
+ return model.getMinimum();
+ }
+
+ /**
+ * This method sets the minimum value of the scrollbar.
+ *
+ * @param minimum The minimum value of the scrollbar.
+ */
+ public void setMinimum(int minimum)
+ {
+ model.setMinimum(minimum);
+ }
+
+ /**
+ * This method returns the maximum value of the scrollbar.
+ *
+ * @return The maximum value of the scrollbar.
+ */
+ public int getMaximum()
+ {
+ return model.getMaximum();
+ }
+
+ /**
+ * This method sets the maximum value of the scrollbar.
+ *
+ * @param maximum The maximum value of the scrollbar.
+ */
+ public void setMaximum(int maximum)
+ {
+ model.setMaximum(maximum);
+ }
+
+ /**
+ * This method returns the model's isAjusting value.
+ *
+ * @return The model's isAdjusting value.
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return model.getValueIsAdjusting();
+ }
+
+ /**
+ * This method sets the model's isAdjusting value.
+ *
+ * @param b The new isAdjusting value.
+ */
+ public void setValueIsAdjusting(boolean b)
+ {
+ model.setValueIsAdjusting(b);
+ }
+
+ /**
+ * This method sets the value, extent, minimum and
+ * maximum.
+ *
+ * @param newValue The new value.
+ * @param newExtent The new extent.
+ * @param newMin The new minimum.
+ * @param newMax The new maximum.
+ */
+ public void setValues(int newValue, int newExtent, int newMin, int newMax)
+ {
+ model.setRangeProperties(newValue, newExtent, newMin, newMax,
+ model.getValueIsAdjusting());
+ }
+
+ /**
+ * This method adds an AdjustmentListener to the scroll bar.
+ *
+ * @param listener The listener to add.
+ */
+ public void addAdjustmentListener(AdjustmentListener listener)
+ {
+ listenerList.add(AdjustmentListener.class, listener);
+ }
+
+ /**
+ * This method removes an AdjustmentListener from the scroll bar.
+ *
+ * @param listener The listener to remove.
+ */
+ public void removeAdjustmentListener(AdjustmentListener listener)
+ {
+ listenerList.remove(AdjustmentListener.class, listener);
+ }
+
+ /**
+ * This method returns an arry of all AdjustmentListeners listening to
+ * this scroll bar.
+ *
+ * @return An array of AdjustmentListeners listening to this scroll bar.
+ */
+ public AdjustmentListener[] getAdjustmentListeners()
+ {
+ return (AdjustmentListener[]) listenerList.getListeners(AdjustmentListener.class);
+ }
+
+ /**
+ * This method is called to fired AdjustmentEvents to the listeners
+ * of this scroll bar. All AdjustmentEvents that are fired
+ * will have an ID of ADJUSTMENT_VALUE_CHANGED and a type of
+ * TRACK.
+ *
+ * @param id The ID of the adjustment event.
+ * @param type The Type of change.
+ * @param value The new value for the property that was changed..
+ */
+ protected void fireAdjustmentValueChanged(int id, int type, int value)
+ {
+ fireAdjustmentValueChanged(id, type, value, getValueIsAdjusting());
+ }
+
+ /**
+ * Helper method for firing adjustment events that can have their
+ * isAdjusting field modified.
+ *
+ * This is package private to avoid an accessor method.
+ *
+ * @param id the ID of the event
+ * @param type the type of the event
+ * @param value the value
+ * @param isAdjusting if the scrollbar is adjusting or not
+ */
+ void fireAdjustmentValueChanged(int id, int type, int value,
+ boolean isAdjusting)
+ {
+ Object[] adjustmentListeners = listenerList.getListenerList();
+ AdjustmentEvent adjustmentEvent = new AdjustmentEvent(this, id, type,
+ value, isAdjusting);
+ for (int i = adjustmentListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (adjustmentListeners[i] == AdjustmentListener.class)
+ ((AdjustmentListener) adjustmentListeners[i + 1]).adjustmentValueChanged(adjustmentEvent);
+ }
+ }
+
+ /**
+ * This method returns the minimum size for this scroll bar.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize()
+ {
+ return ui.getMinimumSize(this);
+ }
+
+ /**
+ * This method returns the maximum size for this scroll bar.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize()
+ {
+ return ui.getMaximumSize(this);
+ }
+
+ /**
+ * This method overrides the setEnabled in JComponent.
+ * When the scroll bar is disabled, the knob cannot
+ * be moved.
+ *
+ * @param x Whether the scrollbar is enabled.
+ */
+ public void setEnabled(boolean x)
+ {
+ // nothing special needs to be done here since we
+ // just check the enabled setting before changing the value.
+ super.setEnabled(x);
+ }
+
+ /**
+ * Returns a string describing the attributes for the <code>JScrollBar</code>
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-<code>null</code>, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the <code>JScrollBar</code>.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",blockIncrement=").append(blockIncrement);
+ sb.append(",orientation=");
+ if (this.orientation == JScrollBar.HORIZONTAL)
+ sb.append("HORIZONTAL");
+ else
+ sb.append("VERTICAL");
+ sb.append(",unitIncrement=").append(unitIncrement);
+ return sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JScrollBar</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJScrollBar}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJScrollBar();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JScrollPane.java b/libjava/classpath/javax/swing/JScrollPane.java
new file mode 100644
index 000000000..6d17857d3
--- /dev/null
+++ b/libjava/classpath/javax/swing/JScrollPane.java
@@ -0,0 +1,712 @@
+/* JScrollPane.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ScrollPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A component that embeds another component and enables it to be scrolled
+ * both in horizontal and vertical direction.
+ *
+ * <table>
+ * <tr><th>Property </th><th>Stored in </th><th>Bound?</th></tr>
+ * <tr><td>columnHeader </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>columnHeaderView </td><td>columnHeader </td><td>no </td></tr>
+ * <tr><td>componentOrientation </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>horizontalScrollBar </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>horizontalScrollBarPolicy </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>layout </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>rowHeader </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>rowHeaderView </td><td>rowHeader </td><td>no </td></tr>
+ * <tr><td>validateRoot </td><td>scrollPane </td><td>no </td></tr>
+ * <tr><td>verticalScrollBar </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>verticalScrollBarPolicy </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>viewport </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>viewportBorder </td><td>scrollPane </td><td>yes </td></tr>
+ * <tr><td>viewportBorderBounds </td><td>scrollPane </td><td>no </td></tr>
+ * <tr><td>viewportView </td><td>viewport </td><td>no </td></tr>
+ * <tr><td>wheelScrollingEnabled </td><td>scrollPane </td><td>yes </td></tr>
+ * </table>
+ */
+public class JScrollPane extends JComponent
+ implements Accessible, ScrollPaneConstants
+{
+ /**
+ * Provides accessibility support for the <code>JScrollPane</code>.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJScrollPane extends AccessibleJComponent
+ implements ChangeListener, PropertyChangeListener
+ {
+
+ /**
+ * The viewport of the underlying scrollpane.
+ */
+ protected JViewport viewPort;
+
+ /**
+ * Creates a new <code>AccessibleJScrollPane</code> object. This
+ * initializes the <code>viewport</code> field with the current viewport
+ * from the scrollpane associated with this
+ * <code>AccessibleJScrollPane</code>.
+ */
+ public AccessibleJScrollPane()
+ {
+ viewPort = getViewport();
+ viewPort.addChangeListener(this);
+ viewPort.addPropertyChangeListener(this);
+ }
+
+ /**
+ * Receives notification when the state of the viewport changes.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ // TODO: Figure out what should be done here, if anything.
+ }
+
+ /**
+ * Receives notification if any of the viewport's bound properties changes.
+ *
+ * @param e the propery change event
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // TODO: Figure out what should be done here, if anything.
+ }
+
+ /**
+ * Resets the <code>viewPort</code> field when the scrollpane's viewport
+ * changes. This method is called by
+ * {@link JScrollPane#setViewport(JViewport)} in order to update the
+ * <code>viewPort</code> field and set up the listeners on this viewport
+ * correctly.
+ */
+ public void resetViewPort()
+ {
+ viewPort.removeChangeListener(this);
+ viewPort.removePropertyChangeListener(this);
+ viewPort = getViewport();
+ viewPort.addChangeListener(this);
+ viewPort.addPropertyChangeListener(this);
+ }
+ }
+
+ private static final long serialVersionUID = 5203525440012340014L;
+
+ protected JViewport columnHeader;
+ protected JViewport rowHeader;
+
+ protected Component lowerLeft;
+ protected Component lowerRight;
+ protected Component upperLeft;
+ protected Component upperRight;
+
+ protected JScrollBar horizontalScrollBar;
+ protected int horizontalScrollBarPolicy;
+ protected JScrollBar verticalScrollBar;
+ protected int verticalScrollBarPolicy;
+
+ protected JViewport viewport;
+
+ private Border viewportBorder;
+
+ private boolean wheelScrollingEnabled;
+
+ public JViewport getColumnHeader()
+ {
+ return columnHeader;
+ }
+
+ public Component getCorner(String key)
+ {
+ if (getComponentOrientation()
+ == ComponentOrientation.LEFT_TO_RIGHT)
+ {
+ if (key == LOWER_LEADING_CORNER)
+ key = LOWER_LEFT_CORNER;
+ else if (key == LOWER_TRAILING_CORNER)
+ key = LOWER_RIGHT_CORNER;
+ else if (key == UPPER_LEADING_CORNER)
+ key = UPPER_LEFT_CORNER;
+ else if (key == UPPER_TRAILING_CORNER)
+ key = UPPER_RIGHT_CORNER;
+ }
+ else if (getComponentOrientation()
+ == ComponentOrientation.RIGHT_TO_LEFT)
+ {
+ if (key == LOWER_LEADING_CORNER)
+ key = LOWER_RIGHT_CORNER;
+ else if (key == LOWER_TRAILING_CORNER)
+ key = LOWER_LEFT_CORNER;
+ else if (key == UPPER_LEADING_CORNER)
+ key = UPPER_RIGHT_CORNER;
+ else if (key == UPPER_TRAILING_CORNER)
+ key = UPPER_LEFT_CORNER;
+ }
+
+ if (key == LOWER_RIGHT_CORNER)
+ return lowerRight;
+ else if (key == UPPER_RIGHT_CORNER)
+ return upperRight;
+ else if (key == LOWER_LEFT_CORNER)
+ return lowerLeft;
+ else if (key == UPPER_LEFT_CORNER)
+ return upperLeft;
+ return null;
+ }
+
+ public JScrollBar getHorizontalScrollBar()
+ {
+ return horizontalScrollBar;
+ }
+
+ public int getHorizontalScrollBarPolicy()
+ {
+ return horizontalScrollBarPolicy;
+ }
+
+ public JViewport getRowHeader()
+ {
+ return rowHeader;
+ }
+
+ public JScrollBar getVerticalScrollBar()
+ {
+ return verticalScrollBar;
+ }
+
+ public int getVerticalScrollBarPolicy()
+ {
+ return verticalScrollBarPolicy;
+ }
+
+ public JViewport getViewport()
+ {
+ return viewport;
+ }
+
+ public Border getViewportBorder()
+ {
+ return viewportBorder;
+ }
+
+ public Rectangle getViewportBorderBounds()
+ {
+ if (viewportBorder == null)
+ {
+ if (getViewport() == null)
+ return new Rectangle(0, 0, 0, 0);
+ else
+ return getViewport().getBounds();
+ }
+ else
+ {
+ Insets i = viewportBorder.getBorderInsets(getViewport());
+ if (getViewport() == null)
+ return new Rectangle(0, 0, i.left + i.right, i.top + i.bottom);
+ else
+ {
+ Rectangle b = getViewport().getBounds();
+ return new Rectangle(b.x - i.left,
+ b.y - i.top,
+ b.width + i.left + i.right,
+ b.height + i.top + i.bottom);
+ }
+ }
+ }
+
+ public boolean isWheelScrollingEnabled()
+ {
+ return wheelScrollingEnabled;
+ }
+
+
+
+ private void sync()
+ {
+ LayoutManager m = super.getLayout();
+ if (m != null && m instanceof ScrollPaneLayout)
+ {
+ ScrollPaneLayout sl = (ScrollPaneLayout) m;
+ sl.syncWithScrollPane(this);
+ }
+ }
+
+ private void removeNonNull(Component c)
+ {
+ if (c != null)
+ remove(c);
+ }
+
+ private void addNonNull(Component c, Object constraints)
+ {
+ if (c != null)
+ add(c, constraints);
+ }
+
+ public void setComponentOrientation(ComponentOrientation co)
+ {
+ ComponentOrientation old = super.getComponentOrientation();
+ super.setComponentOrientation(co);
+ firePropertyChange("componentOrientation", old, co);
+ sync();
+ }
+
+ public void setColumnHeader(JViewport h)
+ {
+ if (columnHeader == h)
+ return;
+
+ JViewport old = columnHeader;
+ removeNonNull(old);
+ columnHeader = h;
+ addNonNull(h, JScrollPane.COLUMN_HEADER);
+ firePropertyChange("columnHeader", old, h);
+ sync();
+ }
+
+ public void setColumnHeaderView(Component c)
+ {
+ if (columnHeader == null)
+ setColumnHeader(createViewport());
+ columnHeader.setView(c);
+ sync();
+ }
+
+ public void setCorner(String key, Component c)
+ {
+ if (getComponentOrientation()
+ == ComponentOrientation.LEFT_TO_RIGHT)
+ {
+ if (key == LOWER_LEADING_CORNER)
+ key = LOWER_LEFT_CORNER;
+ else if (key == LOWER_TRAILING_CORNER)
+ key = LOWER_RIGHT_CORNER;
+ else if (key == UPPER_LEADING_CORNER)
+ key = UPPER_LEFT_CORNER;
+ else if (key == UPPER_TRAILING_CORNER)
+ key = UPPER_RIGHT_CORNER;
+ }
+ else if (getComponentOrientation()
+ == ComponentOrientation.RIGHT_TO_LEFT)
+ {
+ if (key == LOWER_LEADING_CORNER)
+ key = LOWER_RIGHT_CORNER;
+ else if (key == LOWER_TRAILING_CORNER)
+ key = LOWER_LEFT_CORNER;
+ else if (key == UPPER_LEADING_CORNER)
+ key = UPPER_RIGHT_CORNER;
+ else if (key == UPPER_TRAILING_CORNER)
+ key = UPPER_LEFT_CORNER;
+ }
+
+ if (key == LOWER_RIGHT_CORNER)
+ {
+ removeNonNull(lowerRight);
+ lowerRight = c;
+ addNonNull(c, JScrollPane.LOWER_RIGHT_CORNER);
+ }
+ else if (key == UPPER_RIGHT_CORNER)
+ {
+ removeNonNull(upperRight);
+ upperRight = c;
+ addNonNull(c, JScrollPane.UPPER_RIGHT_CORNER);
+ }
+ else if (key == LOWER_LEFT_CORNER)
+ {
+ removeNonNull(lowerLeft);
+ lowerLeft = c;
+ addNonNull(c, JScrollPane.LOWER_LEFT_CORNER);
+ }
+ else if (key == UPPER_LEFT_CORNER)
+ {
+ removeNonNull(upperLeft);
+ upperLeft = c;
+ addNonNull(c, JScrollPane.UPPER_LEFT_CORNER);
+ }
+ else
+ throw new IllegalArgumentException("unknown corner " + key);
+ sync();
+ }
+
+ public void setHorizontalScrollBar(JScrollBar h)
+ {
+ if (horizontalScrollBar == h)
+ return;
+
+ JScrollBar old = horizontalScrollBar;
+ removeNonNull(old);
+ horizontalScrollBar = h;
+ addNonNull(h, JScrollPane.HORIZONTAL_SCROLLBAR);
+ firePropertyChange("horizontalScrollBar", old, h);
+ sync();
+
+ }
+
+ public void setHorizontalScrollBarPolicy(int h)
+ {
+ if (horizontalScrollBarPolicy == h)
+ return;
+
+ if (h != HORIZONTAL_SCROLLBAR_AS_NEEDED
+ && h != HORIZONTAL_SCROLLBAR_NEVER
+ && h != HORIZONTAL_SCROLLBAR_ALWAYS)
+ throw new IllegalArgumentException("unknown horizontal scrollbar policy");
+
+ int old = horizontalScrollBarPolicy;
+ horizontalScrollBarPolicy = h;
+ firePropertyChange("horizontalScrollBarPolicy", old, h);
+ sync();
+ revalidate();
+ }
+
+ public void setLayout(LayoutManager l)
+ {
+ LayoutManager old = super.getLayout();
+ ScrollPaneLayout tmp = (ScrollPaneLayout) l;
+ super.setLayout(l);
+ tmp.syncWithScrollPane(this);
+ firePropertyChange("layout", old, l);
+ sync();
+ }
+
+ public void setRowHeader(JViewport v)
+ {
+ if (rowHeader == v)
+ return;
+
+ JViewport old = rowHeader;
+ removeNonNull(old);
+ rowHeader = v;
+ addNonNull(v, JScrollPane.ROW_HEADER);
+ firePropertyChange("rowHeader", old, v);
+ sync();
+ }
+
+ public void setRowHeaderView(Component c)
+ {
+ if (rowHeader == null)
+ setRowHeader(createViewport());
+ rowHeader.setView(c);
+ sync();
+ }
+
+ public void setVerticalScrollBar(JScrollBar v)
+ {
+ if (verticalScrollBar == v)
+ return;
+
+ JScrollBar old = verticalScrollBar;
+ removeNonNull(old);
+ verticalScrollBar = v;
+ addNonNull(v, JScrollPane.VERTICAL_SCROLLBAR);
+ firePropertyChange("verticalScrollBar", old, v);
+ sync();
+ }
+
+ public void setVerticalScrollBarPolicy(int v)
+ {
+ if (verticalScrollBarPolicy == v)
+ return;
+
+ if (v != VERTICAL_SCROLLBAR_AS_NEEDED
+ && v != VERTICAL_SCROLLBAR_NEVER
+ && v != VERTICAL_SCROLLBAR_ALWAYS)
+ throw new IllegalArgumentException("unknown vertical scrollbar policy");
+
+ int old = verticalScrollBarPolicy;
+ verticalScrollBarPolicy = v;
+ firePropertyChange("verticalScrollBarPolicy", old, v);
+ sync();
+ revalidate();
+ }
+
+ public void setWheelScrollingEnabled(boolean b)
+ {
+ if (wheelScrollingEnabled == b)
+ return;
+
+ boolean old = wheelScrollingEnabled;
+ wheelScrollingEnabled = b;
+ firePropertyChange("wheelScrollingEnabled", old, b);
+ sync();
+ }
+
+ public void setViewport(JViewport v)
+ {
+ if (viewport == v)
+ return;
+
+ JViewport old = viewport;
+ removeNonNull(old);
+ viewport = v;
+ addNonNull(v, JScrollPane.VIEWPORT);
+ revalidate();
+ repaint();
+ firePropertyChange("viewport", old, v);
+ sync();
+ if (accessibleContext != null)
+ {
+ AccessibleJScrollPane asp = (AccessibleJScrollPane) accessibleContext;
+ asp.resetViewPort();
+ }
+ }
+
+ public void setViewportBorder(Border b)
+ {
+ if (viewportBorder == b)
+ return;
+
+ Border old = viewportBorder;
+ viewportBorder = b;
+ firePropertyChange("viewportBorder", old, b);
+ sync();
+ }
+
+ public void setViewportView(Component view)
+ {
+ if (getViewport() == null)
+ {
+ setViewport(createViewport());
+ }
+
+ if (view != null)
+ {
+ getViewport().setView(view);
+ }
+ sync();
+ }
+
+ public boolean isValidateRoot()
+ {
+ return true;
+ }
+
+ /**
+ * Creates a new <code>JScrollPane</code> without a view. The scrollbar
+ * policy is set to {@link #VERTICAL_SCROLLBAR_AS_NEEDED} and
+ * {@link #HORIZONTAL_SCROLLBAR_AS_NEEDED}.
+ */
+ public JScrollPane()
+ {
+ this(null);
+ }
+
+ /**
+ * Creates a new <code>JScrollPane</code> that embeds the specified
+ * <code>view</code> component, displaying vertical and horizontal scrollbars
+ * as needed.
+ *
+ * @param view the component that is embedded inside the JScrollPane
+ */
+ public JScrollPane(Component view)
+ {
+ this(view,
+ VERTICAL_SCROLLBAR_AS_NEEDED,
+ HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ }
+
+ /**
+ * Creates a new <code>JScrollPane</code> without a view; The scrollbar
+ * policies are set to <code>vsbPolicy</code> and <code>hsbPolicy</code>.
+ *
+ * @param vsbPolicy the vertical scrollbar policy to set
+ * @param hsbPolicy the vertical scrollbar policy to set
+ *
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_ALWAYS
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_AS_NEEDED
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_NEVER
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_ALWAYS
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_AS_NEEDED
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_NEVER
+ */
+ public JScrollPane(int vsbPolicy, int hsbPolicy)
+ {
+ this(null, vsbPolicy, hsbPolicy);
+ }
+
+ /**
+ * Creates a new <code>JScrollPane</code> that embeds the specified
+ * <code>view</code> component; The scrollbar
+ * policies are set to <code>vsbPolicy</code> and <code>hsbPolicy</code>.
+ *
+ * @param vsbPolicy the vertical scrollbar policy to set
+ * @param hsbPolicy the vertical scrollbar policy to set
+ *
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_ALWAYS
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_AS_NEEDED
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_NEVER
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_ALWAYS
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_AS_NEEDED
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_NEVER
+ */
+ public JScrollPane(Component view, int vsbPolicy, int hsbPolicy)
+ {
+ wheelScrollingEnabled = true;
+ setVerticalScrollBarPolicy(vsbPolicy);
+ setVerticalScrollBar(createVerticalScrollBar());
+ setHorizontalScrollBarPolicy(hsbPolicy);
+ setHorizontalScrollBar(createHorizontalScrollBar());
+ viewport = createViewport();
+ if (view != null)
+ getViewport().setView(view);
+ add(viewport,0);
+ setLayout(new ScrollPaneLayout());
+ setOpaque(false);
+ updateUI();
+ }
+
+
+ public JScrollBar createHorizontalScrollBar()
+ {
+ return new ScrollBar(SwingConstants.HORIZONTAL);
+ }
+
+ public JScrollBar createVerticalScrollBar()
+ {
+ return new ScrollBar(SwingConstants.VERTICAL);
+ }
+
+ protected JViewport createViewport()
+ {
+ return new JViewport();
+ }
+
+ public String getUIClassID()
+ {
+ return "ScrollPaneUI";
+ }
+
+ public void updateUI()
+ {
+ setUI((ScrollPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the scrollpane's UI delegate.
+ *
+ * @return The scrollpane's UI delegate.
+ */
+ public ScrollPaneUI getUI()
+ {
+ return (ScrollPaneUI) ui;
+ }
+
+ /**
+ * This method sets the scrollpane's UI delegate.
+ *
+ * @param ui The scrollpane's UI delegate.
+ */
+ public void setUI(ScrollPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ protected class ScrollBar
+ extends JScrollBar
+ implements UIResource
+ {
+ private static final long serialVersionUID = -42032395320987283L;
+
+ public ScrollBar(int orientation)
+ {
+ super(orientation);
+ }
+
+ public int getBlockIncrement(int direction)
+ {
+ Component view = JScrollPane.this.getViewport().getView();
+ if (view == null || (! (view instanceof Scrollable)))
+ return super.getBlockIncrement(direction);
+ else
+ {
+ Scrollable s = (Scrollable) view;
+ return s.getScrollableBlockIncrement(JScrollPane.this.getViewport().getViewRect(),
+ this.getOrientation(),
+ direction);
+ }
+ }
+
+ public int getUnitIncrement(int direction)
+ {
+ Component view = JScrollPane.this.getViewport().getView();
+ if (view == null || (! (view instanceof Scrollable)))
+ return super.getUnitIncrement(direction);
+ else
+ {
+ Scrollable s = (Scrollable) view;
+ return s.getScrollableUnitIncrement(JScrollPane.this.getViewport().getViewRect(),
+ this.getOrientation(),
+ direction);
+ }
+ }
+ }
+
+ /**
+ * Returns the accessible context associated with this
+ * <code>JScrollPane</code>.
+ *
+ * @return the accessible context associated with this
+ * <code>JScrollPane</code>
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJScrollPane();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JSeparator.java b/libjava/classpath/javax/swing/JSeparator.java
new file mode 100644
index 000000000..15d15fd8b
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSeparator.java
@@ -0,0 +1,218 @@
+/* JSeparator.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.SeparatorUI;
+
+/**
+ * The JSeparator. It is mostly used to divide/space out
+ * components.
+ */
+public class JSeparator extends JComponent implements SwingConstants,
+ Accessible
+{
+ /**
+ * Provides the accessibility features for the <code>JSeparator</code>
+ * component.
+ */
+ protected class AccessibleJSeparator extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = 916332890553201095L;
+
+ /**
+ * Creates a new <code>AccessibleJSeparator</code> instance.
+ */
+ protected AccessibleJSeparator()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the <code>JSeparator</code> component.
+ *
+ * @return {@link AccessibleRole#SEPARATOR}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SEPARATOR;
+ }
+ }
+
+ private static final long serialVersionUID = 125301223445282357L;
+
+ /** The orientation of the JSeparator. */
+ private transient int orientation = HORIZONTAL;
+
+ /**
+ * Creates a new horizontal <code>JSeparator</code> object.
+ */
+ public JSeparator()
+ {
+ this(HORIZONTAL);
+ }
+
+ /**
+ * Creates a new <code>JSeparator</code> object with the given orientation.
+ *
+ * @param orientation the orientation (either {@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not
+ * one of the specified values.
+ */
+ public JSeparator(int orientation)
+ {
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a valid orientation.");
+ this.orientation = orientation;
+ updateUI();
+ }
+
+ /**
+ * Returns the UI delegate being used with the <code>JSeparator</code>.
+ *
+ * @return The JSeparator's UI delegate.
+ */
+ public SeparatorUI getUI()
+ {
+ return (SeparatorUI) ui;
+ }
+
+ /**
+ * Sets the separator's UI delegate.
+ *
+ * @param ui the UI delegate.
+ */
+ public void setUI(SeparatorUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Sets this separator's UI delegate to the default (obtained from the
+ * {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((SeparatorUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the suffix (<code>"SeparatorUI"</code> in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a <code>JSeparator</code>.
+ *
+ * @return <code>"SeparatorUI"</code>.
+ */
+ public String getUIClassID()
+ {
+ return "SeparatorUI";
+ }
+
+ /**
+ * Returns the orientation of the <code>JSeparator</code>.
+ *
+ * @return The orientation (one of {@link #HORIZONTAL} and {@link #VERTICAL}).
+ *
+ * @see #setOrientation(int)
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * Sets the orientation for the <code>JSeparator</code> and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * <code>orientation</code>) to all registered listeners.
+ *
+ * @param orientation the orientation (either {@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not
+ * one of the specified values.
+ *
+ * @see #getOrientation()
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a valid orientation.");
+ int old = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", old, orientation);
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this <code>JSeparator</code>.
+ *
+ * @return A string describing the attributes of this <code>JSeparator</code>
+ * (never <code>null</code>).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ if (orientation == HORIZONTAL)
+ return superParamStr + ",orientation=HORIZONTAL";
+ else
+ return superParamStr + ",orientation=VERTICAL";
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JSeparator</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJSeparator}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJSeparator();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JSlider.java b/libjava/classpath/javax/swing/JSlider.java
new file mode 100644
index 000000000..a6f51138d
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSlider.java
@@ -0,0 +1,1144 @@
+/* JSlider.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.MenuContainer;
+import java.awt.image.ImageObserver;
+import java.beans.PropertyChangeEvent;
+import java.io.Serializable;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.SliderUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A visual component that allows selection of a value within a
+ * range by adjusting a thumb in a track. The values for the minimum,
+ * maximum, extent and value are stored in a {@link
+ * DefaultBoundedRangeModel}.
+ * <p>
+ * A <code>JSlider</code> component has the following properties:
+ * </p>
+ *
+ * <table>
+ * <tr><th> Property </th><th> Stored in </th><th> Bound? </th></tr>
+ * <tr><td> extent </td><td> model </td><td> no </td></tr>
+ * <tr><td> inverted </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> labelTable </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> majorTickSpacing </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> maximum </td><td> model </td><td> yes </td></tr>
+ * <tr><td> minimum </td><td> model </td><td> yes </td></tr>
+ * <tr><td> minorTickSpacing </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> model </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> orientation </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> paintLabels </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> paintTicks </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> snapToTicks </td><td> slider </td><td> yes </td></tr>
+ * <tr><td> value </td><td> model </td><td> no </td></tr>
+ * <tr><td> valueIsAdjusting </td><td> model </td><td> no </td></tr>
+ * </table>
+ *
+ * <p>
+ * The various behavioural aspects of these properties follows:
+ * </p>
+ *
+ * <ul>
+ * <li>
+ * When a non-bound property stored in the slider changes, the slider fires
+ * a {@link ChangeEvent} to its change listeners.
+ * </li>
+ * <li>
+ * When a bound property stored in the slider changes, the slider fires a
+ * {@link PropertyChangeEvent} to its property change listeners.
+ * </li>
+ * <li>
+ * If any of the model's properties change, it fires a {@link ChangeEvent} to
+ * its listeners, which include the slider.
+ * </li>
+ * <li>
+ * If the slider receives a {@link ChangeEvent} from its model, it will
+ * propagate the event to its own change listeners, with the event's "source"
+ * property set to refer to the slider, rather than the model.
+ * </li>
+ * </ul>
+ */
+public class JSlider extends JComponent implements SwingConstants, Accessible,
+ ImageObserver,
+ MenuContainer, Serializable
+{
+
+ /**
+ * A little testing shows that the reference implementation creates
+ * labels from a class named LabelUIResource.
+ */
+ private class LabelUIResource
+ extends JLabel
+ implements UIResource
+ {
+ LabelUIResource(String text, int align)
+ {
+ super(text, align);
+ setName("Slider.label");
+ }
+ }
+
+ private static final long serialVersionUID = -1441275936141218479L;
+
+ /**
+ * Provides the accessibility features for the <code>JSlider</code>
+ * component.
+ */
+ protected class AccessibleJSlider extends JComponent.AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = -6301740148041106789L;
+
+ /**
+ * Creates a new <code>AccessibleJSlider</code> instance.
+ */
+ protected AccessibleJSlider()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JSlider}
+ * component.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet result = super.getAccessibleStateSet();
+ if (orientation == JSlider.HORIZONTAL)
+ result.add(AccessibleState.HORIZONTAL);
+ else if (orientation == JSlider.VERTICAL)
+ result.add(AccessibleState.VERTICAL);
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the <code>JSlider</code> component.
+ *
+ * @return {@link AccessibleRole#SLIDER}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SLIDER;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values for the {@link JSlider}. Since this class implements
+ * {@link AccessibleValue}, it returns itself.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current value of the {@link JSlider} component, as an
+ * {@link Integer}.
+ *
+ * @return The current value of the {@link JSlider} component.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getValue());
+ }
+
+ /**
+ * Sets the current value of the {@link JSlider} component and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
+ * listeners. If the supplied value is <code>null</code>, this method
+ * does nothing and returns <code>false</code>.
+ *
+ * @param value the new slider value (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if the slider value is updated, and
+ * <code>false</code> otherwise.
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ if (value == null)
+ return false;
+ Number oldValue = getCurrentAccessibleValue();
+ setValue(value.intValue());
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue,
+ new Integer(getValue()));
+ return true;
+ }
+
+ /**
+ * Returns the minimum value of the {@link JSlider} component, as an
+ * {@link Integer}.
+ *
+ * @return The minimum value of the {@link JSlider} component.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(getMinimum());
+ }
+
+ /**
+ * Returns the maximum value of the {@link JSlider} component, as an
+ * {@link Integer}.
+ *
+ * @return The maximum value of the {@link JSlider} component.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(getMaximum());
+ }
+ }
+
+ /** Whether or not this slider paints its ticks. */
+ private transient boolean paintTicks;
+
+ /** Whether or not this slider paints its track. */
+ private transient boolean paintTrack = true;
+
+ /** Whether or not this slider paints its labels. */
+ private transient boolean paintLabels;
+
+ /**
+ * A dictionary of (Integer, Component) pairs where each Component is a
+ * JLabel and the Integer determines where the label will be painted.
+ */
+ private transient Dictionary labelTable;
+
+ /** The model used to store the slider's range and current value. */
+ protected BoundedRangeModel sliderModel;
+
+ /** The space/distance between major ticks. */
+ protected int majorTickSpacing;
+
+ /** The space/distance between minor ticks. */
+ protected int minorTickSpacing;
+
+ /** Whether the slider snaps its values to ticks. */
+ protected boolean snapToTicks;
+
+ /** The orientation (horizontal or vertical) of the slider. */
+ protected int orientation = HORIZONTAL;
+
+ /** Whether the slider is inverted. */
+ private transient boolean isInverted;
+
+ /**
+ * The listener that monitors the slider's model and forwards events to the
+ * slider's listeners (see <code>createChangeListener()</code>).
+ */
+ protected ChangeListener changeListener;
+
+ /** The change event that is passed to all listeners of this slider. */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * Creates a new horizontal <code>JSlider</code> instance with a minimum of
+ * 0, a maximum of 100, and a value of 50.
+ */
+ public JSlider()
+ {
+ this(HORIZONTAL, 0, 100, 50);
+ }
+
+ /**
+ * Creates a new <code>JSlider</code> instance with the given orientation
+ * and a minimum of 0, a maximum of 100, and a value of 50.
+ *
+ * @param orientation The orientation of the slider ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not one of
+ * the specified values.
+ */
+ public JSlider(int orientation)
+ {
+ this(orientation, 0, 100, 50);
+ }
+
+ /**
+ * Creates a new horizontal <code>JSlider</code> instance with the given
+ * maximum and minimum and a value that is halfway between the minimum and the
+ * maximum.
+ *
+ * @param minimum The minimum value.
+ * @param maximum The maximum value.
+ *
+ * @throws IllegalArgumentException if <code>minimum</code> is greater than
+ * <code>maximum</code>.
+ */
+ public JSlider(int minimum, int maximum)
+ {
+ this(HORIZONTAL, minimum, maximum, (maximum + minimum) / 2);
+ }
+
+ /**
+ * Creates a new horizontal <code>JSlider</code> instance with the given
+ * minimum, maximum, and value.
+ *
+ * @param minimum The minimum value.
+ * @param maximum The maximum value.
+ * @param value The initial value.
+ *
+ * @throws IllegalArgumentException if <code>value</code> is not in the
+ * specified range.
+ * @throws IllegalArgumentException if <code>minimum</code> is greater than
+ * <code>maximum</code>.
+ */
+ public JSlider(int minimum, int maximum, int value)
+ {
+ this(HORIZONTAL, minimum, maximum, value);
+ }
+
+ /**
+ * Creates a new <code>JSlider</code> instance with the given orientation,
+ * minimum, maximum, and value.
+ *
+ * @param orientation The orientation of the slider ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ * @param minimum The minimum value of the JSlider.
+ * @param maximum The maximum value of the JSlider.
+ * @param value The initial value of the JSlider.
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not one of
+ * the specified values.
+ * @throws IllegalArgumentException if <code>value</code> is not in the
+ * specified range.
+ * @throws IllegalArgumentException if <code>minimum</code> is greater than
+ * <code>maximum</code>.
+ */
+ public JSlider(int orientation, int minimum, int maximum, int value)
+ {
+ sliderModel = new DefaultBoundedRangeModel(value, 0, minimum, maximum);
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ this.orientation = orientation;
+ changeListener = createChangeListener();
+ sliderModel.addChangeListener(changeListener);
+ updateUI();
+ }
+
+ /**
+ * Creates a new horizontal <code>JSlider</code> instance with the given
+ * model.
+ *
+ * @param model The model (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>model</code> is <code>null</code>.
+ */
+ public JSlider(BoundedRangeModel model)
+ {
+ sliderModel = model;
+ changeListener = createChangeListener();
+ sliderModel.addChangeListener(changeListener);
+ updateUI();
+ }
+
+ /**
+ * Returns the slider's value (from the slider's model).
+ *
+ * @return The value of the slider.
+ *
+ * @see #setValue(int)
+ */
+ public int getValue()
+ {
+ return sliderModel.getValue();
+ }
+
+ /**
+ * Sets the slider's value and sends a {@link ChangeEvent} to all
+ * registered listeners. Note that the model will fire a change event to all
+ * of its registered listeners first (with the model as the event source) and
+ * then the slider will fire another change event to all of its registered
+ * listeners (this time with the slider as the event source).
+ *
+ * @param value the new value.
+ *
+ * @see #getValue()
+ */
+ public void setValue(int value)
+ {
+ sliderModel.setValue(value);
+ }
+
+ /**
+ * Returns the slider's UI delegate.
+ *
+ * @return The slider's UI delegate.
+ */
+ public SliderUI getUI()
+ {
+ return (SliderUI) ui;
+ }
+
+ /**
+ * Sets the slider's UI delegate.
+ *
+ * @param ui the UI delegate.
+ */
+ public void setUI(SliderUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Sets this slider's UI delegate to the default (obtained from the
+ * {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ updateLabelUIs();
+ setUI((SliderUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the suffix (<code>"SliderUI"</code> in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a <code>JSlider</code>.
+ *
+ * @return <code>"SliderUI"</code>.
+ */
+ public String getUIClassID()
+ {
+ return "SliderUI";
+ }
+
+ /**
+ * Creates a {@link ChangeListener} that is added to the slider's model and
+ * forwards change events generated by the model to the listeners that are
+ * registered with the <code>JSlider</code> (by calling the
+ * {@link #fireStateChanged} method).
+ *
+ * @return A new listener.
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new ChangeListener()
+ {
+ public void stateChanged(ChangeEvent ce)
+ {
+ // No need to trigger a repaint since the UI listens to the model
+ // as well. All we need to do is pass on the stateChanged event
+ // to our listeners.
+ fireStateChanged();
+ }
+ };
+ }
+
+ /**
+ * Registers a listener with the slider so that it will receive
+ * {@link ChangeEvent} notifications. Note that change events generated
+ * by the slider's model will be forwarded automatically to the slider's
+ * listeners.
+ *
+ * @param listener the listener to register.
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Removes a listener from this slider so that it will no longer receive
+ * {@link ChangeEvent} notifications from the slider.
+ *
+ * @param listener The listener to remove.
+ *
+ * @see #addChangeListener(ChangeListener)
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Sends a {@link ChangeEvent} to all registered listeners, with this slider
+ * as the source.
+ */
+ protected void fireStateChanged()
+ {
+ Object[] changeListeners = listenerList.getListenerList();
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ for (int i = changeListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (changeListeners[i] == ChangeListener.class)
+ ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
+ }
+ }
+
+ /**
+ * Returns an array containing all the {@link ChangeListener} instances
+ * registered with this slider. If no listeners are registered, this method
+ * returns an empty array.
+ *
+ * @return An array array containing all the {@link ChangeListener} instances
+ * registered with this slider (possibly empty, but never
+ * <code>null</code>).
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Returns the slider's model, which stores the minimum, maximum and current
+ * values.
+ *
+ * @return The slider's model.
+ *
+ * @see #setModel(BoundedRangeModel)
+ */
+ public BoundedRangeModel getModel()
+ {
+ return sliderModel;
+ }
+
+ /**
+ * Sets the slider's model and sends a {@link PropertyChangeEvent} (with the
+ * property name "model") to all registered listeners. The change listener
+ * that the slider registered with the original model is removed and added
+ * to the new model (this ensures that {@link ChangeEvent} notifications
+ * generated by the model are automatically forwarded to listeners that are
+ * registered with the slider).
+ *
+ * @param model The model to use with the slider.
+ *
+ * @see #getModel()
+ */
+ public void setModel(BoundedRangeModel model)
+ {
+ // I didn't do the null pointer check on purpose.
+ // If you try it with Sun's, it'll go ahead and set it to null
+ // and bork the next time it tries to access the model.
+ if (model != sliderModel)
+ {
+ BoundedRangeModel oldModel = sliderModel;
+ sliderModel = model;
+ oldModel.removeChangeListener(changeListener);
+ sliderModel.addChangeListener(changeListener);
+ firePropertyChange("model", oldModel, sliderModel);
+ }
+ }
+
+ /**
+ * Returns the minimum value of the slider (from the slider's model).
+ *
+ * @return The minimum value of the slider.
+ *
+ * @see #setMinimum(int)
+ */
+ public int getMinimum()
+ {
+ return sliderModel.getMinimum();
+ }
+
+ /**
+ * Sets the minimum value of the slider and fires a
+ * {@link PropertyChangeEvent} (with the property name "minimum") to all
+ * registered listeners. Note that:
+ * <p>
+ * <ul>
+ * <li>the minimum value is stored in the slider's model (see
+ * {@link #getModel()});</li>
+ * <li>in addition to the property change event, the slider also fires a
+ * {@link ChangeEvent}.</li>
+ * </ul>
+ *
+ * @param minimum The minimum value of the slider.
+ *
+ * @see #getMinimum()
+ */
+ public void setMinimum(int minimum)
+ {
+ int old = sliderModel.getMinimum();
+ sliderModel.setMinimum(minimum);
+ if (minimum != old)
+ firePropertyChange("minimum", old, minimum);
+ }
+
+ /**
+ * Returns the slider's maximum value (obtained from the slider's model).
+ *
+ * @return The maximum value of the slider.
+ *
+ * @see #setMaximum(int)
+ */
+ public int getMaximum()
+ {
+ return sliderModel.getMaximum();
+ }
+
+ /**
+ * Sets the maximum value of the slider and fires a
+ * {@link PropertyChangeEvent} (with the property name "maximum") to all
+ * registered listeners. Note that:
+ * <p>
+ * <ul>
+ * <li>the maximum value is stored in the slider's model (see
+ * {@link #getModel()});</li>
+ * <li>in addition to the property change event, the slider also fires a
+ * {@link ChangeEvent}.</li>
+ * </ul>
+ *
+ * @param maximum The maximum value of the slider.
+ *
+ * @see #getMaximum()
+ */
+ public void setMaximum(int maximum)
+ {
+ int old = sliderModel.getMaximum();
+ sliderModel.setMaximum(maximum);
+ if (maximum != old)
+ firePropertyChange("maximum", old, maximum);
+ }
+
+ /**
+ * Returns the <code>valueIsAdjusting</code> flag from the slider's model.
+ *
+ * @return The <code>valueIsAdjusting</code> flag from the slider's model.
+ *
+ * @see #setValueIsAdjusting(boolean)
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return sliderModel.getValueIsAdjusting();
+ }
+
+ /**
+ * Sets the <code>valueIsAdjusting</code> flag in the slider's model, and
+ * sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param adjusting the new flag value.
+ *
+ * @see #getValueIsAdjusting()
+ */
+ public void setValueIsAdjusting(boolean adjusting)
+ {
+ sliderModel.setValueIsAdjusting(adjusting);
+ }
+
+ /**
+ * Returns the slider's extent value, obtained from the slider's model.
+ *
+ * @return The extent value.
+ *
+ * @see #setExtent(int)
+ */
+ public int getExtent()
+ {
+ return sliderModel.getExtent();
+ }
+
+ /**
+ * Sets the slider's extent value and sends a {@link ChangeEvent} to all
+ * registered listeners. Note that the model will fire a change event to all
+ * of its registered listeners first (with the model as the event source) and
+ * then the slider will fire another change event to all of its registered
+ * listeners (this time with the slider as the event source).
+ *
+ * @param extent The extent value for this slider.
+ *
+ * @see #getExtent()
+ */
+ public void setExtent(int extent)
+ {
+ sliderModel.setExtent(extent);
+ }
+
+ /**
+ * Returns the orientation of the slider, either {@link JSlider#HORIZONTAL}
+ * or {@link JSlider#VERTICAL}.
+ *
+ * @return The orientation of the slider.
+ *
+ * @see #setOrientation(int)
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * Sets the orientation for the slider and sends a
+ * {@link PropertyChangeEvent} (with the property name "orientation") to all
+ * registered listeners.
+ *
+ * @param orientation the orientation (one of {@link JSlider#HORIZONTAL} or
+ * {@link JSlider#VERTICAL}).
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not one of
+ * the permitted values.
+ *
+ * @see #getOrientation()
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != VERTICAL && orientation != HORIZONTAL)
+ throw new IllegalArgumentException(
+ "orientation must be one of: VERTICAL, HORIZONTAL");
+ if (orientation != this.orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", oldOrientation, this.orientation);
+ revalidate();
+ }
+ }
+
+ /**
+ * Returns the label table for the slider.
+ *
+ * @return The label table for the slider (possibly <code>null</code>).
+ *
+ * @see #setLabelTable(Dictionary)
+ */
+ public Dictionary getLabelTable()
+ {
+ return labelTable;
+ }
+
+ /**
+ * Sets the table of labels for the slider and sends a
+ * {@link PropertyChangeEvent} (with the property name "labelTable") to all
+ * registered listeners.
+ *
+ * @param table the table of labels (<code>null</code> permitted).
+ *
+ * @see #getLabelTable()
+ */
+ public void setLabelTable(Dictionary table)
+ {
+ if (table != labelTable)
+ {
+ Dictionary oldTable = labelTable;
+ labelTable = table;
+ updateLabelUIs();
+ firePropertyChange("labelTable", oldTable, labelTable);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Resets the UI delegates for the labels in the <code>labelTable</code> to
+ * the default for the current look and feel.
+ */
+ protected void updateLabelUIs()
+ {
+ if (labelTable != null)
+ {
+ for (Enumeration list = labelTable.elements(); list.hasMoreElements();)
+ {
+ Object o = list.nextElement();
+ if (o instanceof JComponent)
+ {
+ JComponent jc = (JComponent) o;
+ jc.updateUI();
+ jc.setSize(jc.getPreferredSize());
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a hashtable of <code>(Integer, JLabel)</code> pairs that can be
+ * used as a label table for this slider. The labels will start from the
+ * slider's minimum and increase by the increment. Each label will have a text
+ * string indicating its integer value.
+ *
+ * @param increment The increment between labels (must be > 0).
+ *
+ * @return A hashtable containing the labels.
+ *
+ * @throws IllegalArgumentException if <code>increment</code> is not greater
+ * than zero.
+ */
+ public Hashtable createStandardLabels(int increment)
+ {
+ return createStandardLabels(increment, sliderModel.getMinimum());
+ }
+
+ /**
+ * Creates a hashtable of <code>(Integer, JLabel)</code> pairs that can be
+ * used as a label table for this slider. The labels will start from the
+ * given start value and increase by the increment. Each label will have a
+ * text string indicating its integer value.
+ *
+ * @param increment The increment between labels (must be > 0).
+ * @param start The value to start from.
+ *
+ * @return A hashtable with the labels and their keys.
+ *
+ * @throws IllegalArgumentException if <code>increment</code> is not greater
+ * than zero, or <code>start</code> is not within the range of the
+ * model.
+ */
+ public Hashtable createStandardLabels(int increment, int start)
+ {
+ if (increment <= 0)
+ throw new IllegalArgumentException("Requires 'increment' > 0.");
+ if (start < getMinimum() || start > getMaximum())
+ throw new IllegalArgumentException("The 'start' value is out of range.");
+ Hashtable table = new Hashtable();
+ int max = getMaximum();
+ for (int i = start; i <= max; i += increment)
+ {
+ LabelUIResource label = new LabelUIResource(String.valueOf(i),
+ JLabel.CENTER);
+ table.put(new Integer(i), label);
+ }
+ return table;
+ }
+
+ /**
+ * Returns the flag that controls whether or not the value scale for the
+ * slider is inverted (the default value is <code>false</code>).
+ *
+ * @return The flag that controls whether or not the value scale for the
+ * slider is inverted.
+ *
+ * @see #setInverted(boolean)
+ */
+ public boolean getInverted()
+ {
+ return isInverted;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the value scale for the
+ * slider is inverted and, if the new flag value is different to the old flag
+ * value, sends a {@link PropertyChangeEvent} to all registered listeners.
+ * Typically, a horizontal slider will display a scale that increases from
+ * left to right, but this is reversed if the 'inverted' flag is set to
+ * <code>true</code>. Similarly, a vertical slider will display a scale that
+ * increases from bottom to top, and this is reversed if the 'inverted' flag
+ * is set to <code>true</code>.
+ *
+ * @param inverted the new flag value.
+ *
+ * @see #getInverted()
+ */
+ public void setInverted(boolean inverted)
+ {
+ if (isInverted != inverted)
+ {
+ boolean oldInverted = isInverted;
+ isInverted = inverted;
+ firePropertyChange("inverted", oldInverted, isInverted);
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the distance between major tick marks along the slider's value
+ * scale.
+ *
+ * @return The amount of units between each major tick mark.
+ *
+ * @see #setMajorTickSpacing(int)
+ */
+ public int getMajorTickSpacing()
+ {
+ return majorTickSpacing;
+ }
+
+ /**
+ * Sets the distance between major tick marks along the slider's value scale,
+ * and sends a {@link PropertyChangeEvent} (with the property name
+ * "majorTickSpacing") to all registered listeners.
+ *
+ * @param spacing the distance between major tick marks.
+ *
+ * @see #getMajorTickSpacing()
+ */
+ public void setMajorTickSpacing(int spacing)
+ {
+ if (majorTickSpacing != spacing)
+ {
+ int oldSpacing = majorTickSpacing;
+ majorTickSpacing = spacing;
+ if (labelTable == null && majorTickSpacing > 0 && getPaintLabels())
+ setLabelTable(createStandardLabels(majorTickSpacing));
+ firePropertyChange("majorTickSpacing", oldSpacing, majorTickSpacing);
+ if (getPaintTicks())
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the distance between minor tick marks along the slider's value
+ * scale.
+ *
+ * @return The distance between minor tick marks along the slider's value
+ * scale.
+ *
+ * @see #setMinorTickSpacing(int)
+ */
+ public int getMinorTickSpacing()
+ {
+ return minorTickSpacing;
+ }
+
+ /**
+ * Sets the distance between minor tick marks along the slider's value scale,
+ * and sends a {@link PropertyChangeEvent} (with the property name
+ * "minorTickSpacing") to all registered listeners.
+ *
+ * @param spacing the distance between minor tick marks.
+ *
+ * @see #getMinorTickSpacing()
+ */
+ public void setMinorTickSpacing(int spacing)
+ {
+ if (minorTickSpacing != spacing)
+ {
+ int oldSpacing = minorTickSpacing;
+ minorTickSpacing = spacing;
+ firePropertyChange("minorTickSpacing", oldSpacing, minorTickSpacing);
+ if (getPaintTicks())
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether the slider thumb will snap to ticks.
+ * Sliders that snap to ticks will automatically move the thumb to the
+ * nearest tick mark.
+ *
+ * @return <code>true</code> if the slider thumb automatically.
+ *
+ * @see #setSnapToTicks(boolean)
+ */
+ public boolean getSnapToTicks()
+ {
+ return snapToTicks;
+ }
+
+ /**
+ * Sets the flag that controls whether the slider thumb will snap to ticks
+ * and sends a {@link PropertyChangeEvent} (with the property name
+ * 'snapToTicks') to all registered listeners. Sliders that snap to ticks
+ * will automatically move the thumb to the nearest tick mark.
+ *
+ * @param snap the new flag value.
+ *
+ * @see #getSnapToTicks()
+ */
+ public void setSnapToTicks(boolean snap)
+ {
+ if (snap != snapToTicks)
+ {
+ snapToTicks = snap;
+ firePropertyChange("snapToTicks", !snap, snap);
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not tick marks are painted along
+ * the slider's value scale.
+ *
+ * @return <code>true</code> if tick marks should be painted, and
+ * <code>false</code> if tick marks should not be painted.
+ *
+ * @see #setPaintTicks(boolean)
+ */
+ public boolean getPaintTicks()
+ {
+ return paintTicks;
+ }
+
+ /**
+ * Sets the flag that controls whether or not tick marks are painted along
+ * the slider's value scale, and sends a {@link PropertyChangeEvent} (with
+ * the property name "paintTicks") to all registered listeners. In
+ * addition to setting this property to <code>true</code>, one or both of the
+ * minor tick spacing and major tick spacing attributes must be set to a
+ * value greater than 0 in order for ticks to be painted.
+ *
+ * @param paint Whether ticks will be painted.
+ *
+ * @see #getPaintTicks()
+ */
+ public void setPaintTicks(boolean paint)
+ {
+ if (paint != paintTicks)
+ {
+ boolean oldPaintTicks = paintTicks;
+ paintTicks = paint;
+ firePropertyChange("paintTicks", oldPaintTicks, paintTicks);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not the track is painted.
+ *
+ * @return Whether the track will be painted.
+ *
+ * @see #setPaintTrack(boolean)
+ */
+ public boolean getPaintTrack()
+ {
+ return paintTrack;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the track is painted, and
+ * sends a {@link PropertyChangeEvent} (for the "paintTrack" property) to all
+ * registered listeners.
+ *
+ * @param paint Whether the track will be painted.
+ *
+ * @see #getPaintTrack()
+ */
+ public void setPaintTrack(boolean paint)
+ {
+ if (paintTrack != paint)
+ {
+ paintTrack = paint;
+ firePropertyChange("paintTrack", !paint, paint);
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not labels are painted for the
+ * tick marks along the slider.
+ *
+ * @return Whether labels will be painted.
+ *
+ * @see #setPaintLabels(boolean)
+ */
+ public boolean getPaintLabels()
+ {
+ return paintLabels;
+ }
+
+ /**
+ * Sets the flag that controls whether or not labels are painted for the
+ * tick marks along the slider and sends a {@link PropertyChangeEvent} (with
+ * the property name "paintLabels") to all registered listeners.
+ *
+ * @param paint Whether labels will be painted.
+ *
+ * @see #getPaintLabels()
+ */
+ public void setPaintLabels(boolean paint)
+ {
+ if (paint != paintLabels)
+ {
+ paintLabels = paint;
+ if (paint && majorTickSpacing > 0 && labelTable == null)
+ setLabelTable(createStandardLabels(majorTickSpacing));
+ firePropertyChange("paintLabels", !paint, paint);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this <code>JSlider</code>.
+ *
+ * @return A string describing the attributes of this <code>JSlider</code>
+ * (never <code>null</code>).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",isInverted=").append(getInverted());
+ sb.append(",majorTickSpacing=").append(getMajorTickSpacing());
+ sb.append(",minorTickSpacing=").append(getMinorTickSpacing());
+ sb.append(",orientation=");
+ if (orientation == HORIZONTAL)
+ sb.append("HORIZONTAL");
+ else
+ sb.append("VERTICAL");
+ sb.append(",paintLabels=").append(getPaintLabels());
+ sb.append(",paintTicks=").append(getPaintTicks());
+ sb.append(",paintTrack=").append(getPaintTrack());
+ sb.append(",snapToTicks=").append(getSnapToTicks());
+
+ // the following is output by the reference implementation. We don't
+ // strictly need to replicate this. Perhaps it has some meaning, but
+ // I couldn't determine it yet...
+ sb.append(",snapToValue=true");
+
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JSlider</code> component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJSlider}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJSlider();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JSpinner.java b/libjava/classpath/javax/swing/JSpinner.java
new file mode 100644
index 000000000..e942caead
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSpinner.java
@@ -0,0 +1,754 @@
+/* JSpinner.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.SpinnerUI;
+import javax.swing.text.DateFormatter;
+import javax.swing.text.DefaultFormatterFactory;
+import javax.swing.text.NumberFormatter;
+
+/**
+ * A <code>JSpinner</code> is a component that displays a single value from
+ * a sequence of values, and provides a convenient means for selecting the
+ * previous and next values in the sequence. Typically the spinner displays
+ * a numeric value, but it is possible to display dates or arbitrary items
+ * from a list.
+ *
+ * @author Ka-Hing Cheung
+ *
+ * @since 1.4
+ */
+public class JSpinner extends JComponent
+{
+ /**
+ * The base class for the editor used by the {@link JSpinner} component.
+ * The editor is in fact a panel containing a {@link JFormattedTextField}
+ * component.
+ */
+ public static class DefaultEditor
+ extends JPanel
+ implements ChangeListener, PropertyChangeListener, LayoutManager
+ {
+ /** The spinner that the editor is allocated to. */
+ private JSpinner spinner;
+
+ /** The JFormattedTextField that backs the editor. */
+ JFormattedTextField ftf;
+
+ /**
+ * For compatability with Sun's JDK 1.4.2 rev. 5
+ */
+ private static final long serialVersionUID = -5317788736173368172L;
+
+ /**
+ * Creates a new <code>DefaultEditor</code> object. The editor is
+ * registered with the spinner as a {@link ChangeListener} here.
+ *
+ * @param spinner the <code>JSpinner</code> associated with this editor
+ */
+ public DefaultEditor(JSpinner spinner)
+ {
+ super();
+ setLayout(this);
+ this.spinner = spinner;
+ ftf = new JFormattedTextField();
+ add(ftf);
+ ftf.setValue(spinner.getValue());
+ ftf.addPropertyChangeListener(this);
+ if (getComponentOrientation().isLeftToRight())
+ ftf.setHorizontalAlignment(JTextField.RIGHT);
+ else
+ ftf.setHorizontalAlignment(JTextField.LEFT);
+ spinner.addChangeListener(this);
+ }
+
+ /**
+ * Returns the <code>JSpinner</code> component that the editor is assigned
+ * to.
+ *
+ * @return The spinner that the editor is assigned to.
+ */
+ public JSpinner getSpinner()
+ {
+ return spinner;
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ public void commitEdit() throws ParseException
+ {
+ // TODO: Implement this properly.
+ }
+
+ /**
+ * Removes the editor from the {@link ChangeListener} list maintained by
+ * the specified <code>spinner</code>.
+ *
+ * @param spinner the spinner (<code>null</code> not permitted).
+ */
+ public void dismiss(JSpinner spinner)
+ {
+ spinner.removeChangeListener(this);
+ }
+
+ /**
+ * Returns the text field used to display and edit the current value in
+ * the spinner.
+ *
+ * @return The text field.
+ */
+ public JFormattedTextField getTextField()
+ {
+ return ftf;
+ }
+
+ /**
+ * Sets the bounds for the child components in this container. In this
+ * case, the text field is the only component to be laid out.
+ *
+ * @param parent the parent container.
+ */
+ public void layoutContainer(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension size = getSize();
+ ftf.setBounds(insets.left, insets.top,
+ size.width - insets.left - insets.right,
+ size.height - insets.top - insets.bottom);
+ }
+
+ /**
+ * Calculates the minimum size for this component. In this case, the
+ * text field is the only subcomponent, so the return value is the minimum
+ * size of the text field plus the insets of this component.
+ *
+ * @param parent the parent container.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension minSize = ftf.getMinimumSize();
+ return new Dimension(minSize.width + insets.left + insets.right,
+ minSize.height + insets.top + insets.bottom);
+ }
+
+ /**
+ * Calculates the preferred size for this component. In this case, the
+ * text field is the only subcomponent, so the return value is the
+ * preferred size of the text field plus the insets of this component.
+ *
+ * @param parent the parent container.
+ *
+ * @return The preferred size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension prefSize = ftf.getPreferredSize();
+ return new Dimension(prefSize.width + insets.left + insets.right,
+ prefSize.height + insets.top + insets.bottom);
+ }
+
+ /**
+ * Receives notification of property changes. If the text field's 'value'
+ * property changes, the spinner's model is updated accordingly.
+ *
+ * @param event the event.
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getSource() == ftf)
+ {
+ if (event.getPropertyName().equals("value"))
+ spinner.getModel().setValue(event.getNewValue());
+ }
+ }
+
+ /**
+ * Receives notification of changes in the state of the {@link JSpinner}
+ * that the editor belongs to - the content of the text field is updated
+ * accordingly.
+ *
+ * @param event the change event.
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ ftf.setValue(spinner.getValue());
+ }
+
+ /**
+ * This method does nothing. It is required by the {@link LayoutManager}
+ * interface, but since this component has a single child, there is no
+ * need to use this method.
+ *
+ * @param child the child component to remove.
+ */
+ public void removeLayoutComponent(Component child)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method does nothing. It is required by the {@link LayoutManager}
+ * interface, but since this component has a single child, there is no
+ * need to use this method.
+ *
+ * @param name the name.
+ * @param child the child component to add.
+ */
+ public void addLayoutComponent(String name, Component child)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A panel containing a {@link JFormattedTextField} that is configured for
+ * displaying and editing numbers. The panel is used as a subcomponent of
+ * a {@link JSpinner}.
+ *
+ * @see JSpinner#createEditor(SpinnerModel)
+ */
+ public static class NumberEditor extends DefaultEditor
+ {
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 3791956183098282942L;
+
+ /**
+ * Creates a new <code>NumberEditor</code> object for the specified
+ * <code>spinner</code>. The editor is registered with the spinner as a
+ * {@link ChangeListener}.
+ *
+ * @param spinner the component the editor will be used with.
+ */
+ public NumberEditor(JSpinner spinner)
+ {
+ super(spinner);
+ NumberEditorFormatter nef = new NumberEditorFormatter();
+ nef.setMinimum(getModel().getMinimum());
+ nef.setMaximum(getModel().getMaximum());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Creates a new <code>NumberEditor</code> object.
+ *
+ * @param spinner the spinner.
+ * @param decimalFormatPattern the number format pattern.
+ */
+ public NumberEditor(JSpinner spinner, String decimalFormatPattern)
+ {
+ super(spinner);
+ NumberEditorFormatter nef
+ = new NumberEditorFormatter(decimalFormatPattern);
+ nef.setMinimum(getModel().getMinimum());
+ nef.setMaximum(getModel().getMaximum());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Returns the format used by the text field.
+ *
+ * @return The format used by the text field.
+ */
+ public DecimalFormat getFormat()
+ {
+ NumberFormatter formatter = (NumberFormatter) ftf.getFormatter();
+ return (DecimalFormat) formatter.getFormat();
+ }
+
+ /**
+ * Returns the model used by the editor's {@link JSpinner} component,
+ * cast to a {@link SpinnerNumberModel}.
+ *
+ * @return The model.
+ */
+ public SpinnerNumberModel getModel()
+ {
+ return (SpinnerNumberModel) getSpinner().getModel();
+ }
+ }
+
+ static class NumberEditorFormatter
+ extends NumberFormatter
+ {
+ public NumberEditorFormatter()
+ {
+ super(NumberFormat.getInstance());
+ }
+ public NumberEditorFormatter(String decimalFormatPattern)
+ {
+ super(new DecimalFormat(decimalFormatPattern));
+ }
+ }
+
+ /**
+ * A <code>JSpinner</code> editor used for the {@link SpinnerListModel}.
+ * This editor uses a <code>JFormattedTextField</code> to edit the values
+ * of the spinner.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public static class ListEditor extends DefaultEditor
+ {
+ /**
+ * Creates a new instance of <code>ListEditor</code>.
+ *
+ * @param spinner the spinner for which this editor is used
+ */
+ public ListEditor(JSpinner spinner)
+ {
+ super(spinner);
+ }
+
+ /**
+ * Returns the spinner's model cast as a {@link SpinnerListModel}.
+ *
+ * @return The spinner's model.
+ */
+ public SpinnerListModel getModel()
+ {
+ return (SpinnerListModel) getSpinner().getModel();
+ }
+ }
+
+ /**
+ * An editor class for a <code>JSpinner</code> that is used
+ * for displaying and editing dates (e.g. that uses
+ * <code>SpinnerDateModel</code> as model).
+ *
+ * The editor uses a {@link JTextField} with the value
+ * displayed by a {@link DateFormatter} instance.
+ */
+ public static class DateEditor extends DefaultEditor
+ {
+
+ /** The serialVersionUID. */
+ private static final long serialVersionUID = -4279356973770397815L;
+
+ /**
+ * Creates a new instance of DateEditor for the specified
+ * <code>JSpinner</code>.
+ *
+ * @param spinner the <code>JSpinner</code> for which to
+ * create a <code>DateEditor</code> instance
+ */
+ public DateEditor(JSpinner spinner)
+ {
+ super(spinner);
+ DateEditorFormatter nef = new DateEditorFormatter();
+ nef.setMinimum(getModel().getStart());
+ nef.setMaximum(getModel().getEnd());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Creates a new instance of DateEditor for the specified
+ * <code>JSpinner</code> using the specified date format
+ * pattern.
+ *
+ * @param spinner the <code>JSpinner</code> for which to
+ * create a <code>DateEditor</code> instance
+ * @param dateFormatPattern the date format to use
+ *
+ * @see SimpleDateFormat#SimpleDateFormat(String)
+ */
+ public DateEditor(JSpinner spinner, String dateFormatPattern)
+ {
+ super(spinner);
+ DateEditorFormatter nef = new DateEditorFormatter(dateFormatPattern);
+ nef.setMinimum(getModel().getStart());
+ nef.setMaximum(getModel().getEnd());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Returns the <code>SimpleDateFormat</code> instance that is used to
+ * format the date value.
+ *
+ * @return the <code>SimpleDateFormat</code> instance that is used to
+ * format the date value
+ */
+ public SimpleDateFormat getFormat()
+ {
+ DateFormatter formatter = (DateFormatter) ftf.getFormatter();
+ return (SimpleDateFormat) formatter.getFormat();
+ }
+
+ /**
+ * Returns the {@link SpinnerDateModel} that is edited by this editor.
+ *
+ * @return the <code>SpinnerDateModel</code> that is edited by this editor
+ */
+ public SpinnerDateModel getModel()
+ {
+ return (SpinnerDateModel) getSpinner().getModel();
+ }
+ }
+
+ static class DateEditorFormatter
+ extends DateFormatter
+ {
+ public DateEditorFormatter()
+ {
+ super(DateFormat.getInstance());
+ }
+ public DateEditorFormatter(String dateFormatPattern)
+ {
+ super(new SimpleDateFormat(dateFormatPattern));
+ }
+ }
+
+ /**
+ * A listener that forwards {@link ChangeEvent} notifications from the model
+ * to the {@link JSpinner}'s listeners.
+ */
+ class ModelListener implements ChangeListener
+ {
+ /**
+ * Creates a new listener.
+ */
+ public ModelListener()
+ {
+ // nothing to do here
+ }
+
+ /**
+ * Receives notification from the model that its state has changed.
+ *
+ * @param event the event (ignored).
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * The model that defines the current value and permitted values for the
+ * spinner.
+ */
+ private SpinnerModel model;
+
+ /** The current editor. */
+ private JComponent editor;
+
+ private static final long serialVersionUID = 3412663575706551720L;
+
+ /**
+ * Creates a new <code>JSpinner</code> with default instance of
+ * {@link SpinnerNumberModel} (that is, a model with value 0, step size 1,
+ * and no upper or lower limit).
+ *
+ * @see javax.swing.SpinnerNumberModel
+ */
+ public JSpinner()
+ {
+ this(new SpinnerNumberModel());
+ }
+
+ /**
+ * Creates a new <code>JSpinner with the specified model. The
+ * {@link #createEditor(SpinnerModel)} method is used to create an editor
+ * that is suitable for the model.
+ *
+ * @param model the model (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>model</code> is <code>null</code>.
+ */
+ public JSpinner(SpinnerModel model)
+ {
+ this.model = model;
+ this.editor = createEditor(model);
+ model.addChangeListener(new ModelListener());
+ updateUI();
+ }
+
+ /**
+ * If the editor is <code>JSpinner.DefaultEditor</code>, then forwards the
+ * call to it, otherwise do nothing.
+ *
+ * @throws ParseException DOCUMENT ME!
+ */
+ public void commitEdit() throws ParseException
+ {
+ if (editor instanceof DefaultEditor)
+ ((DefaultEditor) editor).commitEdit();
+ }
+
+ /**
+ * Gets the current editor
+ *
+ * @return the current editor
+ *
+ * @see #setEditor
+ */
+ public JComponent getEditor()
+ {
+ return editor;
+ }
+
+ /**
+ * Changes the current editor to the new editor. The old editor is
+ * removed from the spinner's {@link ChangeEvent} list.
+ *
+ * @param editor the new editor (<code>null</code> not permitted.
+ *
+ * @throws IllegalArgumentException if <code>editor</code> is
+ * <code>null</code>.
+ *
+ * @see #getEditor
+ */
+ public void setEditor(JComponent editor)
+ {
+ if (editor == null)
+ throw new IllegalArgumentException("editor may not be null");
+
+ JComponent oldEditor = this.editor;
+ if (oldEditor instanceof DefaultEditor)
+ ((DefaultEditor) oldEditor).dismiss(this);
+ else if (oldEditor instanceof ChangeListener)
+ removeChangeListener((ChangeListener) oldEditor);
+
+ this.editor = editor;
+ firePropertyChange("editor", oldEditor, editor);
+ }
+
+ /**
+ * Returns the model used by the {@link JSpinner} component.
+ *
+ * @return The model.
+ *
+ * @see #setModel(SpinnerModel)
+ */
+ public SpinnerModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Sets a new underlying model.
+ *
+ * @param newModel the new model to set
+ *
+ * @exception IllegalArgumentException if newModel is <code>null</code>
+ */
+ public void setModel(SpinnerModel newModel)
+ {
+ if (newModel == null)
+ throw new IllegalArgumentException();
+
+ if (model == newModel)
+ return;
+
+ SpinnerModel oldModel = model;
+ model = newModel;
+ firePropertyChange("model", oldModel, newModel);
+ setEditor(createEditor(model));
+ }
+
+ /**
+ * Gets the next value without changing the current value.
+ *
+ * @return the next value
+ *
+ * @see javax.swing.SpinnerModel#getNextValue
+ */
+ public Object getNextValue()
+ {
+ return model.getNextValue();
+ }
+
+ /**
+ * Gets the previous value without changing the current value.
+ *
+ * @return the previous value
+ *
+ * @see javax.swing.SpinnerModel#getPreviousValue
+ */
+ public Object getPreviousValue()
+ {
+ return model.getPreviousValue();
+ }
+
+ /**
+ * Gets the <code>SpinnerUI</code> that handles this spinner
+ *
+ * @return the <code>SpinnerUI</code>
+ */
+ public SpinnerUI getUI()
+ {
+ return (SpinnerUI) ui;
+ }
+
+ /**
+ * Gets the current value of the spinner, according to the underly model,
+ * not the UI.
+ *
+ * @return the current value
+ *
+ * @see javax.swing.SpinnerModel#getValue
+ */
+ public Object getValue()
+ {
+ return model.getValue();
+ }
+
+ /**
+ * Sets the value in the model.
+ *
+ * @param value the new value.
+ */
+ public void setValue(Object value)
+ {
+ model.setValue(value);
+ }
+
+ /**
+ * Returns the ID that identifies which look and feel class will be
+ * the UI delegate for this spinner.
+ *
+ * @return <code>"SpinnerUI"</code>.
+ */
+ public String getUIClassID()
+ {
+ return "SpinnerUI";
+ }
+
+ /**
+ * This method resets the spinner's UI delegate to the default UI for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((SpinnerUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Sets the UI delegate for the component.
+ *
+ * @param ui The spinner's UI delegate.
+ */
+ public void setUI(SpinnerUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Adds a <code>ChangeListener</code>
+ *
+ * @param listener the listener to add
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Remove a particular listener
+ *
+ * @param listener the listener to remove
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Gets all the <code>ChangeListener</code>s
+ *
+ * @return all the <code>ChangeListener</code>s
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Fires a <code>ChangeEvent</code> to all the <code>ChangeListener</code>s
+ * added to this <code>JSpinner</code>
+ */
+ protected void fireStateChanged()
+ {
+ ChangeEvent evt = new ChangeEvent(this);
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].stateChanged(evt);
+ }
+
+ /**
+ * Creates an editor that is appropriate for the specified <code>model</code>.
+ *
+ * @param model the model.
+ *
+ * @return The editor.
+ */
+ protected JComponent createEditor(SpinnerModel model)
+ {
+ if (model instanceof SpinnerDateModel)
+ return new DateEditor(this);
+ else if (model instanceof SpinnerNumberModel)
+ return new NumberEditor(this);
+ else if (model instanceof SpinnerListModel)
+ return new ListEditor(this);
+ else
+ return new DefaultEditor(this);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JSplitPane.java b/libjava/classpath/javax/swing/JSplitPane.java
new file mode 100644
index 000000000..856b2e5c5
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSplitPane.java
@@ -0,0 +1,942 @@
+/* JSplitPane.java --
+ Copyright (C) 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleValue;
+import javax.swing.plaf.SplitPaneUI;
+
+/**
+ * This class implements JSplitPane. It is used to divide two components. By
+ * dragging the SplitPane's divider, the user can resize the two components.
+ * Note that the divider cannot resize a component to smaller than it's
+ * minimum size.
+ */
+public class JSplitPane extends JComponent implements Accessible
+{
+
+ /**
+ * Provides the accessibility features for the <code>JSplitPane</code>
+ * component.
+ */
+ protected class AccessibleJSplitPane extends JComponent.AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = -1788116871416305366L;
+
+ /**
+ * Creates a new <code>AccessibleJSplitPane</code> instance.
+ */
+ protected AccessibleJSplitPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JSplitPane}
+ * component.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet result = super.getAccessibleStateSet();
+ if (getOrientation() == HORIZONTAL_SPLIT)
+ {
+ result.add(AccessibleState.HORIZONTAL);
+ }
+ else if (getOrientation() == VERTICAL_SPLIT)
+ {
+ result.add(AccessibleState.VERTICAL);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the <code>JSplitPane</code> component.
+ *
+ * @return {@link AccessibleRole#SPLIT_PANE}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SPLIT_PANE;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values for the {@link JSplitPane}. Since this class implements
+ * {@link AccessibleValue}, it returns itself.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current divider location for the {@link JSplitPane}
+ * component, as an {@link Integer}.
+ *
+ * @return The current divider location.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getDividerLocation());
+ }
+
+ /**
+ * Sets the divider location for the {@link JSplitPane} component and sends
+ * a {@link PropertyChangeEvent} (with the property name
+ * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
+ * listeners. If the supplied value is <code>null</code>, this method
+ * does nothing and returns <code>false</code>.
+ *
+ * @param value the new divider location (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if the divider location value is updated, and
+ * <code>false</code> otherwise.
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ if (value == null)
+ return false;
+ Number oldValue = getCurrentAccessibleValue();
+ setDividerLocation(value.intValue());
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue,
+ new Integer(value.intValue()));
+ return true;
+ }
+
+ /**
+ * Returns the minimum divider location for the {@link JSplitPane}
+ * component, as an {@link Integer}.
+ *
+ * @return The minimum divider location.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(getMinimumDividerLocation());
+ }
+
+ /**
+ * Returns the maximum divider location for the {@link JSplitPane}
+ * component, as an {@link Integer}.
+ *
+ * @return The maximum divider location.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(getMaximumDividerLocation());
+ }
+ }
+
+ private static final long serialVersionUID = -5634142046175988380L;
+
+ /** The constraints string used to add components to the bottom. */
+ public static final String BOTTOM = "bottom";
+
+ /** The property fired when the continuousLayout property changes. */
+ public static final String CONTINUOUS_LAYOUT_PROPERTY = "continuousLayout";
+
+ /** The property fired when the divider property changes. */
+ public static final String DIVIDER = "divider";
+
+ /** The property fired when the divider location property changes. */
+ public static final String DIVIDER_LOCATION_PROPERTY = "dividerLocation";
+
+ /** The property fired when the divider size property changes. */
+ public static final String DIVIDER_SIZE_PROPERTY = "dividerSize";
+
+ /**
+ * The value of the orientation when the components are split horizontally.
+ */
+ public static final int HORIZONTAL_SPLIT = 1;
+
+ /** The property fired when the last divider location property changes. */
+ public static final String LAST_DIVIDER_LOCATION_PROPERTY =
+ "lastDividerLocation";
+
+ /** The constraints string used to add components to the left. */
+ public static final String LEFT = "left";
+
+ /** The property fired when the one touch expandable property changes. */
+ public static final String ONE_TOUCH_EXPANDABLE_PROPERTY =
+ "oneTouchExpandable";
+
+ /** The property fired when the orientation property changes. */
+ public static final String ORIENTATION_PROPERTY = "orientation";
+
+ /** The property fired when the resize weight property changes. */
+ public static final String RESIZE_WEIGHT_PROPERTY = "resizeWeight";
+
+ /** The constraints string used to add components to the right. */
+ public static final String RIGHT = "right";
+
+ /** The constraints string used to add components to the top. */
+ public static final String TOP = "top";
+
+ /** The value of the orientation when the components are split vertically. */
+ public static final int VERTICAL_SPLIT = 0;
+
+ /** Whether the JSplitPane uses continuous layout. */
+ protected boolean continuousLayout;
+
+ /** Whether the JSplitPane uses one touch expandable buttons. */
+ protected boolean oneTouchExpandable = false;
+
+ // This is the master dividerSize variable and sets the
+ // BasicSplitPaneDivider one accordingly
+
+ /** The size of the divider. */
+ protected int dividerSize = 10;
+
+ /** The last location of the divider given by the UI. */
+ protected int lastDividerLocation;
+
+ /** The orientation of the JSplitPane. */
+ protected int orientation;
+
+ /** The component on the top or left. */
+ protected Component leftComponent;
+
+ /** The component on the right or bottom. */
+ protected Component rightComponent;
+
+ /**
+ * The divider location.
+ */
+ private int dividerLocation;
+
+ /** Determines how extra space should be allocated. */
+ private transient double resizeWeight;
+
+ /**
+ * Indicates if the dividerSize property has been set by a client program or
+ * by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientDividerSizeSet = false;
+
+ /**
+ * Indicates if the oneTouchExpandable property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientOneTouchExpandableSet = false;
+
+ /**
+ * Creates a new JSplitPane object with the given orientation, layout mode,
+ * and left and right components.
+ *
+ * @param newOrientation The orientation to use.
+ * @param newContinuousLayout The layout mode to use.
+ * @param newLeftComponent The left component.
+ * @param newRightComponent The right component.
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
+ */
+ public JSplitPane(int newOrientation, boolean newContinuousLayout,
+ Component newLeftComponent, Component newRightComponent)
+ {
+ if (newOrientation != HORIZONTAL_SPLIT && newOrientation != VERTICAL_SPLIT)
+ throw new IllegalArgumentException("orientation is invalid.");
+ orientation = newOrientation;
+ continuousLayout = newContinuousLayout;
+ setLeftComponent(newLeftComponent);
+ setRightComponent(newRightComponent);
+ dividerLocation = -1;
+ updateUI();
+ }
+
+ /**
+ * Creates a new JSplitPane object using nonContinuousLayout mode, the given
+ * orientation and left and right components.
+ *
+ * @param newOrientation The orientation to use.
+ * @param newLeftComponent The left component.
+ * @param newRightComponent The right component.
+ */
+ public JSplitPane(int newOrientation, Component newLeftComponent,
+ Component newRightComponent)
+ {
+ this(newOrientation, false, newLeftComponent, newRightComponent);
+ }
+
+ /**
+ * Creates a new JSplitPane object with the given layout mode and
+ * orientation.
+ *
+ * @param newOrientation The orientation to use.
+ * @param newContinuousLayout The layout mode to use.
+ */
+ public JSplitPane(int newOrientation, boolean newContinuousLayout)
+ {
+ this(newOrientation, newContinuousLayout, null, null);
+ }
+
+ /**
+ * Creates a new JSplitPane object using a nonContinuousLayout mode and the
+ * given orientation.
+ *
+ * @param newOrientation The orientation to use.
+ */
+ public JSplitPane(int newOrientation)
+ {
+ this(newOrientation, false, null, null);
+ }
+
+ /**
+ * Creates a new JSplitPane object using HORIZONTAL_SPLIT and a
+ * nonContinuousLayout mode.
+ */
+ public JSplitPane()
+ {
+ this(HORIZONTAL_SPLIT, false, new JButton("left button"),
+ new JButton("right button"));
+ }
+
+ /**
+ * This method adds a component to the JSplitPane. The constraints object is
+ * a string that identifies where this component should go. If the
+ * constraints is not a known one, it will throw an
+ * IllegalArgumentException. The valid constraints are LEFT, TOP, RIGHT,
+ * BOTTOM and DIVIDER.
+ *
+ * @param comp The component to add.
+ * @param constraints The constraints string to use.
+ * @param index Where to place to component in the list of components.
+ *
+ * @throws IllegalArgumentException When the constraints is not a known
+ * identifier.
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ if (constraints == null)
+ {
+ if (leftComponent == null)
+ constraints = LEFT;
+ else if (rightComponent == null)
+ constraints = RIGHT;
+ }
+
+ if (constraints instanceof String)
+ {
+ String placement = (String) constraints;
+
+ if (placement.equals(BOTTOM) || placement.equals(RIGHT))
+ {
+ if (rightComponent != null)
+ remove(rightComponent);
+ rightComponent = comp;
+ }
+ else if (placement.equals(LEFT) || placement.equals(TOP))
+ {
+ if (leftComponent != null)
+ remove(leftComponent);
+ leftComponent = comp;
+ }
+ else if (placement.equals(DIVIDER))
+ constraints = null;
+ else
+ throw new
+ IllegalArgumentException("Constraints is not a known identifier.");
+
+ // If no dividerLocation has been set, then we need to trigger an
+ // initial layout.
+ if (getDividerLocation() != -1)
+ resetToPreferredSizes();
+
+ super.addImpl(comp, constraints, index);
+ }
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JSplitPane</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJSplitPane}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJSplitPane();
+
+ return accessibleContext;
+ }
+
+ /**
+ * This method returns the bottom component.
+ *
+ * @return The bottom component.
+ */
+ public Component getBottomComponent()
+ {
+ return rightComponent;
+ }
+
+ /**
+ * This method returns the location of the divider. This method is passed to
+ * the UI.
+ *
+ * @return The location of the divider.
+ */
+ public int getDividerLocation()
+ {
+ return dividerLocation;
+ }
+
+ /**
+ * This method returns the size of the divider.
+ *
+ * @return The size of the divider.
+ */
+ public int getDividerSize()
+ {
+ return dividerSize;
+ }
+
+ /**
+ * This method returns the last divider location.
+ *
+ * @return The last divider location.
+ */
+ public int getLastDividerLocation()
+ {
+ return lastDividerLocation;
+ }
+
+ /**
+ * This method returns the left component.
+ *
+ * @return The left component.
+ */
+ public Component getLeftComponent()
+ {
+ return leftComponent;
+ }
+
+ /**
+ * This method returns the maximum divider location. This method is passed
+ * to the UI.
+ *
+ * @return DOCUMENT ME!
+ */
+ public int getMaximumDividerLocation()
+ {
+ if (ui != null)
+ return ((SplitPaneUI) ui).getMaximumDividerLocation(this);
+ else
+ return -1;
+ }
+
+ /**
+ * This method returns the minimum divider location. This method is passed
+ * to the UI.
+ *
+ * @return The minimum divider location.
+ */
+ public int getMinimumDividerLocation()
+ {
+ if (ui != null)
+ return ((SplitPaneUI) ui).getMinimumDividerLocation(this);
+ else
+ return -1;
+ }
+
+ /**
+ * This method returns the orientation that the JSplitPane is using.
+ *
+ * @return The current orientation.
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * This method returns the current resize weight.
+ *
+ * @return The current resize weight.
+ */
+ public double getResizeWeight()
+ {
+ return resizeWeight;
+ }
+
+ /**
+ * This method returns the right component.
+ *
+ * @return The right component.
+ */
+ public Component getRightComponent()
+ {
+ return rightComponent;
+ }
+
+ /**
+ * This method returns the top component.
+ *
+ * @return The top component.
+ */
+ public Component getTopComponent()
+ {
+ return leftComponent;
+ }
+
+ /**
+ * This method returns the UI.
+ *
+ * @return The UI.
+ */
+ public SplitPaneUI getUI()
+ {
+ return (SplitPaneUI) ui;
+ }
+
+ /**
+ * This method returns true if the JSplitPane is using a continuousLayout.
+ *
+ * @return True if using a continuousLayout.
+ */
+ public boolean isContinuousLayout()
+ {
+ return continuousLayout;
+ }
+
+ /**
+ * This method returns true if the divider has one touch expandable buttons.
+ *
+ * @return True if one touch expandable is used.
+ */
+ public boolean isOneTouchExpandable()
+ {
+ return oneTouchExpandable;
+ }
+
+ /**
+ * This method returns true.
+ *
+ * @return true.
+ */
+ public boolean isValidateRoot()
+ {
+ return true;
+ }
+
+ /**
+ * This method overrides JComponent's paintChildren so the UI can be
+ * messaged when the children have finished painting.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintChildren(Graphics g)
+ {
+ super.paintChildren(g);
+ if (ui != null)
+ ((SplitPaneUI) ui).finishedPaintingChildren(this, g);
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this <code>JSplitPane</code>.
+ *
+ * @return A string describing the attributes of this <code>JSplitPane</code>
+ * (never <code>null</code>).
+ */
+ protected String paramString()
+ {
+ // FIXME: the next line can be restored once PR27208 is fixed
+ String superParamStr = ""; //super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",continuousLayout=").append(isContinuousLayout());
+ sb.append(",dividerSize=").append(getDividerSize());
+ sb.append(",lastDividerLocation=").append(getLastDividerLocation());
+ sb.append(",oneTouchExpandable=").append(isOneTouchExpandable());
+ sb.append(",orientation=");
+ if (orientation == HORIZONTAL_SPLIT)
+ sb.append("HORIZONTAL_SPLIT");
+ else
+ sb.append("VERTICAL_SPLIT");
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * This method removes the given component from the JSplitPane.
+ *
+ * @param component The Component to remove.
+ */
+ public void remove(Component component)
+ {
+ if (component == leftComponent)
+ leftComponent = null;
+ else if (component == rightComponent)
+ rightComponent = null;
+ super.remove(component);
+ }
+
+ /**
+ * This method removes the component at the given index.
+ *
+ * @param index The index of the component to remove.
+ */
+ public void remove(int index)
+ {
+ Component component = getComponent(index);
+ if (component == leftComponent)
+ leftComponent = null;
+ else if (component == rightComponent)
+ rightComponent = null;
+ super.remove(index);
+ }
+
+ /**
+ * This method removes all components from the JSplitPane.
+ */
+ public void removeAll()
+ {
+ leftComponent = null;
+ rightComponent = null;
+ super.removeAll();
+ }
+
+ /**
+ * This method resets all children of the JSplitPane to their preferred
+ * sizes.
+ */
+ public void resetToPreferredSizes()
+ {
+ if (ui != null)
+ ((SplitPaneUI) ui).resetToPreferredSizes(this);
+ }
+
+ /**
+ * This method sets the bottom component.
+ *
+ * @param comp The Component to be placed at the bottom.
+ */
+ public void setBottomComponent(Component comp)
+ {
+ if (comp != null)
+ add(comp, BOTTOM);
+ else
+ add(new JButton("right button"), BOTTOM);
+ }
+
+ /**
+ * This method sets the layout mode for the JSplitPane.
+ *
+ * @param newContinuousLayout Whether the JSplitPane is in continuousLayout
+ * mode.
+ */
+ public void setContinuousLayout(boolean newContinuousLayout)
+ {
+ if (newContinuousLayout != continuousLayout)
+ {
+ boolean oldValue = continuousLayout;
+ continuousLayout = newContinuousLayout;
+ firePropertyChange(CONTINUOUS_LAYOUT_PROPERTY, oldValue,
+ continuousLayout);
+ }
+ }
+
+ /**
+ * This method sets the location of the divider. A value of 0 sets the
+ * divider to the farthest left. A value of 1 sets the divider to the
+ * farthest right.
+ *
+ * @param proportionalLocation A double that describes the location of the
+ * divider.
+ *
+ * @throws IllegalArgumentException if <code>proportionalLocation</code> is
+ * not in the range from 0.0 to 1.0 inclusive.
+ */
+ public void setDividerLocation(double proportionalLocation)
+ {
+ if (proportionalLocation > 1 || proportionalLocation < 0)
+ throw new IllegalArgumentException
+ ("proportion has to be between 0 and 1.");
+
+ int max = ((orientation == HORIZONTAL_SPLIT) ? getWidth() : getHeight())
+ - getDividerSize();
+ setDividerLocation((int) (proportionalLocation * max));
+ }
+
+ /**
+ * This method sets the location of the divider.
+ *
+ * @param location The location of the divider. The negative value forces to
+ * compute the new location from the preferred sizes of the split
+ * pane components.
+ */
+ public void setDividerLocation(int location)
+ {
+ int oldLocation = dividerLocation;
+ dividerLocation = location;
+ SplitPaneUI ui = getUI();
+ if (ui != null)
+ ui.setDividerLocation(this, location);
+ firePropertyChange(DIVIDER_LOCATION_PROPERTY, oldLocation,
+ location);
+ }
+
+ /**
+ * This method sets the size of the divider.
+ *
+ * @param newSize The size of the divider.
+ */
+ public void setDividerSize(int newSize)
+ {
+ clientDividerSizeSet = true;
+ if (newSize != dividerSize)
+ {
+ int oldSize = dividerSize;
+ dividerSize = newSize;
+ firePropertyChange(DIVIDER_SIZE_PROPERTY, oldSize, dividerSize);
+ }
+ }
+
+ // This doesn't appear to do anything when set from user side.
+ // so it probably is only used from the UI side to change the
+ // lastDividerLocation var.
+
+ /**
+ * This method sets the last location of the divider.
+ *
+ * @param newLastLocation The last location of the divider.
+ */
+ public void setLastDividerLocation(int newLastLocation)
+ {
+ if (newLastLocation != lastDividerLocation)
+ {
+ int oldValue = lastDividerLocation;
+ lastDividerLocation = newLastLocation;
+ firePropertyChange(LAST_DIVIDER_LOCATION_PROPERTY, oldValue,
+ lastDividerLocation);
+ }
+ }
+
+ /**
+ * This method sets the left component.
+ *
+ * @param comp The left component.
+ */
+ public void setLeftComponent(Component comp)
+ {
+ if (comp != null)
+ add(comp, LEFT);
+ else
+ remove (leftComponent);
+ }
+
+ /**
+ * This method sets whether the divider has one touch expandable buttons.
+ * The one touch expandable buttons can expand the size of either component
+ * to the maximum allowed size.
+ *
+ * @param newValue Whether the divider will have one touch expandable
+ * buttons.
+ */
+ public void setOneTouchExpandable(boolean newValue)
+ {
+ clientOneTouchExpandableSet = true;
+ if (newValue != oneTouchExpandable)
+ {
+ boolean oldValue = oneTouchExpandable;
+ oneTouchExpandable = newValue;
+ firePropertyChange(ONE_TOUCH_EXPANDABLE_PROPERTY, oldValue,
+ oneTouchExpandable);
+ }
+ }
+
+ /**
+ * Sets the orientation for the <code>JSplitPane</code> and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #ORIENTATION_PROPERTY}) to all registered listeners.
+ *
+ * @param orientation the orientation (either {@link #HORIZONTAL_SPLIT}
+ * or {@link #VERTICAL_SPLIT}).
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is not one of
+ * the listed values.
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != HORIZONTAL_SPLIT && orientation != VERTICAL_SPLIT)
+ throw new IllegalArgumentException
+ ("orientation must be one of VERTICAL_SPLIT, HORIZONTAL_SPLIT");
+ if (orientation != this.orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange(ORIENTATION_PROPERTY, oldOrientation,
+ this.orientation);
+ }
+ }
+
+ /**
+ * This method determines how extra space will be distributed among the left
+ * and right components. A value of 0 will allocate all extra space to the
+ * right component. A value of 1 indicates that all extra space will go to
+ * the left component. A value in between 1 and 0 will split the space
+ * accordingly.
+ *
+ * @param value The resize weight.
+ */
+ public void setResizeWeight(double value)
+ {
+ if (value < 0.0 || value > 1.0)
+ throw new IllegalArgumentException("Value outside permitted range.");
+ if (this.resizeWeight != value)
+ {
+ double old = resizeWeight;
+ resizeWeight = value;
+ firePropertyChange(RESIZE_WEIGHT_PROPERTY, old, value);
+ }
+ }
+
+ /**
+ * This method sets the right component.
+ *
+ * @param comp The right component.
+ */
+ public void setRightComponent(Component comp)
+ {
+ if (comp != null)
+ add(comp, RIGHT);
+ else
+ remove (rightComponent);
+ }
+
+ /**
+ * This method sets the top component.
+ *
+ * @param comp The top component.
+ */
+ public void setTopComponent(Component comp)
+ {
+ if (comp != null)
+ add(comp, TOP);
+ else
+ add(new JButton("left button"), TOP);
+ }
+
+ /**
+ * This method sets the UI used by the JSplitPane.
+ *
+ * @param ui The UI to use.
+ */
+ public void setUI(SplitPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method resets the UI to the one specified by the current Look and
+ * Feel.
+ */
+ public void updateUI()
+ {
+ setUI((SplitPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a string identifier to determine which UI class it
+ * needs.
+ *
+ * @return A string that identifies it's UI class.
+ */
+ public String getUIClassID()
+ {
+ return "SplitPaneUI";
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if <code>c</code> or
+ * <code>propertyValue</code> is <code>null</code>
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("dividerSize"))
+ {
+ if (! clientDividerSizeSet)
+ {
+ setDividerSize(((Integer) value).intValue());
+ clientDividerSizeSet = false;
+ }
+ }
+ else if (propertyName.equals("oneTouchExpandable"))
+ {
+ if (! clientOneTouchExpandableSet)
+ {
+ setOneTouchExpandable(((Boolean) value).booleanValue());
+ clientOneTouchExpandableSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java
new file mode 100644
index 000000000..18055e8a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTabbedPane.java
@@ -0,0 +1,1728 @@
+/* JTabbedPane.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.io.Serializable;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.TabbedPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * This is a container for components where only one component is displayed at
+ * a given time and the displayed component can be switched by clicking on
+ * tabs.
+ *
+ * <p>
+ * Tabs can be oriented in several ways. They can be above, below, left and
+ * right of the component. Tabs can either wrap around (by creating multiple
+ * rows of tabs) or they can be scrolled (where only a subset of the tabs
+ * can be seen at once). More tabs can be added by calling the
+ * add/addTab/insertTab methods.
+ * </p>
+ */
+public class JTabbedPane extends JComponent implements Serializable,
+ Accessible,
+ SwingConstants
+{
+ /**
+ * Accessibility support for <code>JTabbedPane</code>.
+ */
+ protected class AccessibleJTabbedPane extends JComponent.AccessibleJComponent
+ implements AccessibleSelection, ChangeListener
+ {
+ /**
+ * The serialization UID.
+ */
+ private static final long serialVersionUID = 7610530885966830483L;
+
+ /**
+ * Creates a new AccessibleJTabbedPane object.
+ */
+ public AccessibleJTabbedPane()
+ {
+ super();
+ }
+
+ /**
+ * Receives notification when the selection state of the
+ * <code>JTabbedPane</code> changes and fires appropriate property change
+ * events to interested listeners.
+ *
+ * @param e the change event describing the change
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // I couldn't figure out what else should be done here.
+ Object source = e.getSource();
+ firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
+ null, source);
+ }
+
+ /**
+ * Returns the accessible role of the <code>JTabbedPane</code>, which is
+ * {@link AccessibleRole#PAGE_TAB_LIST}.
+ *
+ * @return the accessible role of the <code>JTabbedPane</code>
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PAGE_TAB_LIST;
+ }
+
+ /**
+ * Returns the number of accessible child components of the
+ * <code>JTabbedPane</code>.
+ *
+ * @return the number of accessible child components of the
+ * <code>JTabbedPane</code>
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return getTabCount();
+ }
+
+ /**
+ * Returns the accessible child component at the specified index.
+ *
+ * @param i the index of the child component to fetch
+ *
+ * @return the accessible child component at the specified index
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ // Testing shows that the reference implementation returns instances
+ // of page here.
+ Accessible child = null;
+ if (i >= 0 && i < tabs.size())
+ child = (Page) tabs.get(i);
+ return child;
+ }
+
+ /**
+ * Returns the current selection state of the <code>JTabbedPane</code>
+ * as AccessibleSelection object.
+ *
+ * @return the current selection state of the <code>JTabbedPane</code>
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible child component at the specified coordinates.
+ * If there is no child component at this location, then return the
+ * currently selected tab.
+ *
+ * @param p the coordinates at which to look up the child component
+ *
+ * @return the accessible child component at the specified coordinates or
+ * the currently selected tab if there is no child component at
+ * this location
+ */
+ public Accessible getAccessibleAt(Point p)
+ {
+ int tabIndex = indexAtLocation(p.x, p.y);
+ if (tabIndex >= 0)
+ return getAccessibleChild(tabIndex);
+ else
+ return getAccessibleSelection(0);
+ }
+
+ /**
+ * Returns the number of selected child components of the
+ * <code>JTabbedPane</code>. The reference implementation appears
+ * to return <code>1</code> always and we do the same.
+ *
+ * @return <code>1</code>
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the selected tab, or <code>null</code> if there is no
+ * selection.
+ *
+ * @param i the selection index (ignored here).
+ *
+ * @return The selected tab, or <code>null</code>.
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ Accessible result = null;
+ int selected = getSelectedIndex();
+ if (selected >= 0)
+ result = (Page) tabs.get(selected);
+ return result;
+ }
+
+ /**
+ * Returns <code>true</code> if the specified child is selected,
+ * and <code>false</code> otherwise.
+ *
+ * @param i the child index.
+ *
+ * @return A boolean.
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ return i == getSelectedIndex();
+ }
+
+ /**
+ * Selects the specified tab.
+ *
+ * @param i the index of the item to select.
+ */
+ public void addAccessibleSelection(int i)
+ {
+ setSelectedIndex(i);
+ }
+
+ /**
+ * Does nothing - it makes no sense to remove a selection for a
+ * tabbed pane, since one tab must always be selected.
+ *
+ * @param i the item index.
+ *
+ * @see #addAccessibleSelection(int)
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ // do nothing
+ }
+
+ /**
+ * Does nothing - it makes no sense to clear the selection for
+ * a tabbed pane, since one tab must always be selected.
+ *
+ * @see #addAccessibleSelection(int)
+ */
+ public void clearAccessibleSelection()
+ {
+ // do nothing
+ }
+
+ /**
+ * Does nothing - it makes no sense to select all for a tabbed
+ * pane, since only one tab can be selected at a time.
+ *
+ * @see #addAccessibleSelection(int)
+ */
+ public void selectAllAccessibleSelection()
+ {
+ // do nothing
+ }
+ }
+
+ /**
+ * A helper class that listens for changes to the model.
+ */
+ protected class ModelListener implements ChangeListener, Serializable
+ {
+ private static final long serialVersionUID = 497359819958114132L;
+
+ /**
+ * Creates a new ModelListener object.
+ */
+ protected ModelListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called whenever the model is changed.
+ *
+ * @param e The ChangeEvent that is passed from the model.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Propagate to our listeners.
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * A private class that holds all the information for each tab.
+ */
+ private class Page
+ extends AccessibleContext
+ implements Accessible
+ {
+ /** The tooltip string. */
+ private String tip;
+
+ /** The component associated with the tab. */
+ private Component component;
+
+ /** The active icon associated with the tab. */
+ private transient Icon icon;
+
+ /** The disabled icon associated with the tab. */
+ private transient Icon disabledIcon;
+
+ /** The tab's enabled status. */
+ private transient boolean enabled = true;
+
+ /** The string painted on the tab. */
+ private transient String title;
+
+ /** The background color of the tab. */
+ private transient Color bg;
+
+ /** The foreground color of the tab. */
+ private transient Color fg;
+
+ /** The mnemonic associated with the tab. */
+ private transient int mnemonicKey;
+
+ /** The index of the underlined character in the string. */
+ private transient int underlinedChar = -1;
+
+ /**
+ * Creates a new data storage for the tab.
+ *
+ * @param title The string displayed on the tab.
+ * @param icon The active icon displayed on the tab.
+ * @param component The component associated with the tab.
+ * @param tip The tooltip associated with the tab.
+ */
+ protected Page(String title, Icon icon, Component component, String tip)
+ {
+ this.title = title;
+ this.icon = icon;
+ this.component = component;
+ this.tip = tip;
+ }
+
+ /**
+ * This method returns the component associated with the tab.
+ *
+ * @return The component associated with the tab.
+ */
+ public Component getComponent()
+ {
+ return component;
+ }
+
+ /**
+ * This method sets the component associated with the tab.
+ *
+ * @param c The component associated with the tab.
+ */
+ public void setComponent(Component c)
+ {
+ int i = indexOfComponent(component);
+ insertTab(title, icon, c, tip, i);
+ component = c;
+ removeTabAt(i);
+ }
+
+ /**
+ * This method returns the tooltip string.
+ *
+ * @return The tooltip string.
+ */
+ public String getTip()
+ {
+ return tip;
+ }
+
+ /**
+ * This method sets the tooltip string.
+ *
+ * @param tip The tooltip string.
+ */
+ public void setTip(String tip)
+ {
+ this.tip = tip;
+ }
+
+ /**
+ * This method returns the background color.
+ *
+ * @return The background color.
+ */
+ public Color getBackground()
+ {
+ Color background;
+ if (bg == null)
+ background = JTabbedPane.this.getBackground();
+ else
+ background = bg;
+ return background;
+ }
+
+ /**
+ * This method sets the background color.
+ *
+ * @param background The background color.
+ */
+ public void setBackground(Color background)
+ {
+ bg = background;
+ }
+
+ /**
+ * This method returns the foreground color.
+ *
+ * @return The foreground color.
+ */
+ public Color getForeground()
+ {
+ Color foreground;
+ if (fg == null)
+ foreground = JTabbedPane.this.getForeground();
+ else
+ foreground = fg;
+ return foreground;
+ }
+
+ /**
+ * This method sets the foreground color.
+ *
+ * @param foreground The foreground color.
+ */
+ public void setForeground(Color foreground)
+ {
+ fg = foreground;
+ }
+
+ /**
+ * This method returns the title associated with the tab.
+ *
+ * @return The title of the tab.
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ private static final long serialVersionUID = 1614381073220130939L;
+
+ /**
+ * This method sets the title of the tab.
+ *
+ * @param text The title of the tab.
+ */
+ public void setTitle(String text)
+ {
+ title = text;
+ if (title != null && title.length() <= underlinedChar)
+ setDisplayedMnemonicIndex(title.length() - 1);
+ }
+
+ /**
+ * This method returns the active icon.
+ *
+ * @return The active icon.
+ */
+ public Icon getIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * This method sets the active icon.
+ *
+ * @param icon The active icon.
+ */
+ public void setIcon(Icon icon)
+ {
+ this.icon = icon;
+ }
+
+ /**
+ * This method returns the disabled icon.
+ *
+ * @return The disabled icon.
+ */
+ public Icon getDisabledIcon()
+ {
+ if (disabledIcon == null && icon instanceof ImageIcon)
+ setDisabledIcon(icon);
+ return disabledIcon;
+ }
+
+ /**
+ * This method sets the disabled icon.
+ *
+ * @param disabledIcon The disabled icon.
+ */
+ public void setDisabledIcon(Icon disabledIcon)
+ {
+ this.disabledIcon = disabledIcon;
+ }
+
+ /**
+ * This method returns whether the tab is enabled.
+ *
+ * @return Whether the tab is enabled.
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * This method sets whether the tab is enabled.
+ *
+ * @param enabled Whether this tab is enabled.
+ */
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ /**
+ * This method returns the mnemonic.
+ *
+ * @return The mnemonic.
+ */
+ public int getMnemonic()
+ {
+ return mnemonicKey;
+ }
+
+ /**
+ * This method sets the mnemonic. If the title is set, it will update the
+ * mnemonicIndex.
+ *
+ * @param key The mnemonic.
+ */
+ public void setMnemonic(int key)
+ {
+ setMnemonic((char) key);
+ }
+
+ /**
+ * This method sets the mnemonic. If the title is set, it will update the
+ * mnemonicIndex.
+ *
+ * @param aChar The mnemonic.
+ */
+ public void setMnemonic(char aChar)
+ {
+ mnemonicKey = aChar;
+ if (title != null)
+ setDisplayedMnemonicIndex(title.indexOf(mnemonicKey));
+ }
+
+ /**
+ * This method returns the mnemonicIndex.
+ *
+ * @return The mnemonicIndex.
+ */
+ public int getDisplayedMnemonicIndex()
+ {
+ return underlinedChar;
+ }
+
+ /**
+ * This method sets the mnemonicIndex.
+ *
+ * @param index The mnemonicIndex.
+ *
+ * @throws IllegalArgumentException If index less than -1 || index greater
+ * or equal to title.length.
+ */
+ public void setDisplayedMnemonicIndex(int index)
+ throws IllegalArgumentException
+ {
+ if (index < -1 || title != null && index >= title.length())
+ throw new IllegalArgumentException();
+
+ if (title == null || mnemonicKey == 0 || (index > -1 && title.charAt(index) != mnemonicKey))
+ index = -1;
+
+ underlinedChar = index;
+ }
+
+ /**
+ * Returns the accessible context, which is this object itself.
+ *
+ * @return the accessible context, which is this object itself
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible name for this tab.
+ *
+ * @return The accessible name.
+ */
+ public String getAccessibleName()
+ {
+ if (accessibleName != null)
+ return accessibleName;
+ else
+ return title;
+ }
+
+ /**
+ * Returns the accessible role of this tab, which is always
+ * {@link AccessibleRole#PAGE_TAB}.
+ *
+ * @return the accessible role of this tab
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PAGE_TAB;
+ }
+
+ /**
+ * Returns the accessible state set of this object.
+ *
+ * @return the accessible state set of this object
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleContext parentCtx = JTabbedPane.this.getAccessibleContext();
+ AccessibleStateSet state = parentCtx.getAccessibleStateSet();
+ state.add(AccessibleState.SELECTABLE);
+ if (component == getSelectedComponent())
+ state.add(AccessibleState.SELECTED);
+ return state;
+ }
+
+ /**
+ * Returns the index of this tab inside its parent.
+ *
+ * @return the index of this tab inside its parent
+ */
+ public int getAccessibleIndexInParent()
+ {
+ // TODO: Not sure if the title is unambiguous, but I can't figure
+ // another way of doing this.
+ return indexOfTab(title);
+ }
+
+ /**
+ * Returns the number of accessible children, which is always one (the
+ * component of this tab).
+ *
+ * @return the number of accessible children
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the accessible child of this tab, which is the component
+ * displayed by the tab.
+ *
+ * @return the accessible child of this tab
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ // A quick test shows that this method always returns the component
+ // displayed by the tab, regardless of the index.
+ return (Accessible) component;
+ }
+
+ /**
+ * Returns the locale of this accessible object.
+ *
+ * @return the locale of this accessible object
+ */
+ public Locale getLocale()
+ {
+ // TODO: Is this ok?
+ return Locale.getDefault();
+ }
+ }
+
+ private static final long serialVersionUID = 1614381073220130939L;
+
+ /** The changeEvent used to fire changes to listeners. */
+ protected ChangeEvent changeEvent;
+
+ /** The listener that listens to the model. */
+ protected ChangeListener changeListener;
+
+ /** The model that describes this JTabbedPane. */
+ protected SingleSelectionModel model;
+
+ /** Indicates that the TabbedPane is in scrolling mode. */
+ public static final int SCROLL_TAB_LAYOUT = 1;
+
+ /** Indicates that the TabbedPane is in wrap mode. */
+ public static final int WRAP_TAB_LAYOUT = 0;
+
+ /** The current tabPlacement of the TabbedPane. */
+ protected int tabPlacement = SwingConstants.TOP;
+
+ /** The current tabLayoutPolicy of the TabbedPane. */
+ private transient int layoutPolicy;
+
+ /** The list of tabs associated with the TabbedPane. */
+ transient Vector tabs = new Vector();
+
+ /**
+ * Creates a new JTabbedPane object with tabs on top and using wrap tab
+ * layout.
+ */
+ public JTabbedPane()
+ {
+ this(SwingConstants.TOP, WRAP_TAB_LAYOUT);
+ }
+
+ /**
+ * Creates a new JTabbedPane object using wrap tab layout and the given
+ * <code>tabPlacement</code>, where <code>tabPlacement</code> can be one
+ * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or
+ * {@link #RIGHT}.
+ *
+ * @param tabPlacement where the tabs will be placed
+ */
+ public JTabbedPane(int tabPlacement)
+ {
+ this(tabPlacement, WRAP_TAB_LAYOUT);
+ }
+
+ /**
+ * Creates a new JTabbedPane object with the given <code>tabPlacement</code>
+ * and <code>tabLayoutPolicy</code>. The <code>tabPlacement</code> can be one
+ * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or
+ * {@link #RIGHT}. The <code>tabLayoutPolicy</code> can be either
+ * {@link #SCROLL_TAB_LAYOUT} or {@link #WRAP_TAB_LAYOUT}.
+ *
+ * @param tabPlacement where the tabs will be placed
+ * @param tabLayoutPolicy the way tabs will be placed
+ *
+ * @throws IllegalArgumentException If tabLayoutPolicy or tabPlacement are
+ * not valid.
+ */
+ public JTabbedPane(int tabPlacement, int tabLayoutPolicy)
+ {
+ if (tabPlacement != TOP && tabPlacement != BOTTOM && tabPlacement != RIGHT
+ && tabPlacement != LEFT)
+ throw new IllegalArgumentException("tabPlacement is not valid.");
+ if (tabLayoutPolicy != SCROLL_TAB_LAYOUT
+ && tabLayoutPolicy != WRAP_TAB_LAYOUT)
+ throw new IllegalArgumentException("tabLayoutPolicy is not valid.");
+ this.tabPlacement = tabPlacement;
+ layoutPolicy = tabLayoutPolicy;
+
+ setModel(new DefaultSingleSelectionModel());
+
+ updateUI();
+ }
+
+ /**
+ * This method returns the UI used to display the JTabbedPane.
+ *
+ * @return The UI used to display the JTabbedPane.
+ */
+ public TabbedPaneUI getUI()
+ {
+ return (TabbedPaneUI) ui;
+ }
+
+ /**
+ * This method sets the UI used to display the JTabbedPane.
+ *
+ * @param ui The UI used to display the JTabbedPane.
+ */
+ public void setUI(TabbedPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method restores the UI to the defaults given by the UIManager.
+ */
+ public void updateUI()
+ {
+ setUI((TabbedPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a string identifier that is used to determine which
+ * UI will be used with the JTabbedPane.
+ *
+ * @return A string identifier for the UI.
+ */
+ public String getUIClassID()
+ {
+ return "TabbedPaneUI";
+ }
+
+ /**
+ * This method creates a ChangeListener that is used to listen to the model
+ * for events.
+ *
+ * @return A ChangeListener to listen to the model.
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new ModelListener();
+ }
+
+ /**
+ * This method adds a ChangeListener to the JTabbedPane.
+ *
+ * @param l The ChangeListener to add.
+ */
+ public void addChangeListener(ChangeListener l)
+ {
+ listenerList.add(ChangeListener.class, l);
+ }
+
+ /**
+ * This method removes a ChangeListener to the JTabbedPane.
+ *
+ * @param l The ChangeListener to remove.
+ */
+ public void removeChangeListener(ChangeListener l)
+ {
+ listenerList.remove(ChangeListener.class, l);
+ }
+
+ /**
+ * This method fires a ChangeEvent to all the JTabbedPane's ChangeListeners.
+ */
+ protected void fireStateChanged()
+ {
+ Object[] changeListeners = listenerList.getListenerList();
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ for (int i = changeListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (changeListeners[i] == ChangeListener.class)
+ ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
+ }
+ }
+
+ /**
+ * This method returns all ChangeListeners registered with the JTabbedPane.
+ *
+ * @return The ChangeListeners registered with the JTabbedPane.
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) super.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * This method returns the model used with the JTabbedPane.
+ *
+ * @return The JTabbedPane's model.
+ */
+ public SingleSelectionModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * This method changes the model property of the JTabbedPane.
+ *
+ * @param m The new model to use with the JTabbedPane.
+ */
+ public void setModel(SingleSelectionModel m)
+ {
+ if (m != model)
+ {
+ SingleSelectionModel oldModel = this.model;
+ if (oldModel != null && changeListener != null)
+ oldModel.removeChangeListener(changeListener);
+
+ model = m;
+
+ if (model != null)
+ {
+ if (changeListener == null)
+ changeListener = createChangeListener();
+ model.addChangeListener(changeListener);
+ }
+ firePropertyChange("model", oldModel, this.model);
+ }
+ }
+
+ /**
+ * This method returns the tabPlacement.
+ *
+ * @return The tabPlacement used with the JTabbedPane.
+ */
+ public int getTabPlacement()
+ {
+ return tabPlacement;
+ }
+
+ /**
+ * This method changes the tabPlacement property of the JTabbedPane.
+ *
+ * @param tabPlacement The tabPlacement to use.
+ *
+ * @throws IllegalArgumentException If tabPlacement is not one of TOP,
+ * BOTTOM, LEFT, or RIGHT.
+ */
+ public void setTabPlacement(int tabPlacement)
+ {
+ if (tabPlacement != TOP && tabPlacement != BOTTOM && tabPlacement != RIGHT
+ && tabPlacement != LEFT)
+ throw new IllegalArgumentException("tabPlacement is not valid.");
+ if (tabPlacement != this.tabPlacement)
+ {
+ int oldPlacement = this.tabPlacement;
+ this.tabPlacement = tabPlacement;
+ firePropertyChange("tabPlacement", oldPlacement, this.tabPlacement);
+ }
+ }
+
+ /**
+ * This method returns the tabLayoutPolicy.
+ *
+ * @return The tabLayoutPolicy.
+ */
+ public int getTabLayoutPolicy()
+ {
+ return layoutPolicy;
+ }
+
+ /**
+ * This method changes the tabLayoutPolicy property of the JTabbedPane.
+ *
+ * @param tabLayoutPolicy The tabLayoutPolicy to use.
+ *
+ * @throws IllegalArgumentException If tabLayoutPolicy is not one of
+ * SCROLL_TAB_LAYOUT or WRAP_TAB_LAYOUT.
+ */
+ public void setTabLayoutPolicy(int tabLayoutPolicy)
+ {
+ if (tabLayoutPolicy != SCROLL_TAB_LAYOUT
+ && tabLayoutPolicy != WRAP_TAB_LAYOUT)
+ throw new IllegalArgumentException("tabLayoutPolicy is not valid.");
+ if (tabLayoutPolicy != layoutPolicy)
+ {
+ int oldPolicy = layoutPolicy;
+ layoutPolicy = tabLayoutPolicy;
+ firePropertyChange("tabLayoutPolicy", oldPolicy, layoutPolicy);
+ }
+ }
+
+ /**
+ * This method returns the index of the tab that is currently selected.
+ *
+ * @return The index of the selected tab.
+ */
+ public int getSelectedIndex()
+ {
+ return model.getSelectedIndex();
+ }
+
+ /**
+ * This method checks the index.
+ *
+ * @param index The index to check.
+ * @param start DOCUMENT ME!
+ * @param end DOCUMENT ME!
+ *
+ * @throws IndexOutOfBoundsException DOCUMENT ME!
+ */
+ private void checkIndex(int index, int start, int end)
+ {
+ if (index < start || index >= end)
+ throw new IndexOutOfBoundsException("Index < " + start + " || Index >= "
+ + end);
+ }
+
+ /**
+ * This method sets the selected index. This method will hide the old
+ * component and show the new component.
+ *
+ * @param index The index to set it at.
+ */
+ public void setSelectedIndex(int index)
+ {
+ checkIndex(index, -1, tabs.size());
+ if (index != getSelectedIndex())
+ {
+ // Hiding and showing the involved components
+ // is done by the JTabbedPane's UI.
+ model.setSelectedIndex(index);
+ }
+ }
+
+ /**
+ * This method returns the component at the selected index.
+ *
+ * @return The component at the selected index.
+ */
+ public Component getSelectedComponent()
+ {
+ int selectedIndex = getSelectedIndex();
+ Component selected = null;
+ if (selectedIndex >= 0)
+ selected = getComponentAt(selectedIndex);
+ return selected;
+ }
+
+ /**
+ * This method sets the component at the selected index.
+ *
+ * @param c The component associated with the selected index.
+ */
+ public void setSelectedComponent(Component c)
+ {
+ if (c.getParent() == this)
+ setSelectedIndex(indexOfComponent(c));
+ else
+ setComponentAt(getSelectedIndex(), c);
+ }
+
+ /**
+ * This method inserts tabs into JTabbedPane. This includes adding the
+ * component to the JTabbedPane and hiding it.
+ *
+ * @param title the title of the tab; may be <code>null</code>
+ * @param icon the tab's icon; may be <code>null</code>
+ * @param component the component associated with the tab
+ * @param tip the tooltip for the tab
+ * @param index the index to insert the tab at
+ */
+ public void insertTab(String title, Icon icon, Component component,
+ String tip, int index)
+ {
+ if (title == null)
+ title = "";
+ Page p = new Page(title, icon, component, tip);
+ tabs.insertElementAt(p, index);
+
+ // Hide the component so we don't see it. Do it before we parent it
+ // so we don't trigger a repaint.
+ if (component != null)
+ {
+ component.hide();
+ super.add(component);
+ }
+
+ if (getSelectedIndex() == -1)
+ {
+ setSelectedIndex(0);
+ fireStateChanged();
+ }
+
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane.
+ *
+ * @param title the title of the tab; may be <code>null</code>
+ * @param icon the icon for the tab; may be <code>null</code>
+ * @param component the associated component
+ * @param tip the associated tooltip
+ */
+ public void addTab(String title, Icon icon, Component component, String tip)
+ {
+ insertTab(title, icon, component, tip, tabs.size());
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane.
+ *
+ * @param title the title of the tab; may be <code>null</code>
+ * @param icon the icon for the tab; may be <code>null</code>
+ * @param component the associated component
+ */
+ public void addTab(String title, Icon icon, Component component)
+ {
+ insertTab(title, icon, component, null, tabs.size());
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane.
+ *
+ * @param title the title of the tab; may be <code>null</code>
+ * @param component the associated component
+ */
+ public void addTab(String title, Component component)
+ {
+ insertTab(title, null, component, null, tabs.size());
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. The title of the tab is the
+ * Component's name. If the Component is an instance of UIResource, it
+ * doesn't add the tab and instead add the component directly to the
+ * JTabbedPane.
+ *
+ * @param component The associated component.
+ *
+ * @return The Component that was added.
+ */
+ public Component add(Component component)
+ {
+ if (component instanceof UIResource)
+ super.add(component);
+ else
+ insertTab(component.getName(), null, component, null, tabs.size());
+
+ return component;
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. If the Component is an
+ * instance of UIResource, it doesn't add the tab and instead add the
+ * component directly to the JTabbedPane.
+ *
+ * @param title the title of the tab; may be <code>null</code>
+ * @param component the associated component
+ *
+ * @return The Component that was added.
+ */
+ public Component add(String title, Component component)
+ {
+ if (component instanceof UIResource)
+ super.add(component);
+ else
+ insertTab(title, null, component, null, tabs.size());
+ return component;
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. If the Component is an
+ * instance of UIResource, it doesn't add the tab and instead add the
+ * component directly to the JTabbedPane.
+ *
+ * @param component The associated component.
+ * @param index The index to insert the tab at.
+ *
+ * @return The Component that was added.
+ */
+ public Component add(Component component, int index)
+ {
+ if (component instanceof UIResource)
+ super.add(component);
+ else
+ insertTab(component.getName(), null, component, null, index);
+ return component;
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. If the Component is an
+ * instance of UIResource, it doesn't add the tab and instead add the
+ * component directly to the JTabbedPane. If the constraints object is an
+ * icon, it will be used as the tab's icon. If the constraints object is a
+ * string, we will use it as the title.
+ *
+ * @param component The associated component.
+ * @param constraints The constraints object.
+ */
+ public void add(Component component, Object constraints)
+ {
+ add(component, constraints, tabs.size());
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. If the Component is an
+ * instance of UIResource, it doesn't add the tab and instead add the
+ * component directly to the JTabbedPane. If the constraints object is an
+ * icon, it will be used as the tab's icon. If the constraints object is a
+ * string, we will use it as the title.
+ *
+ * @param component The associated component.
+ * @param constraints The constraints object.
+ * @param index The index to insert the tab at.
+ */
+ public void add(Component component, Object constraints, int index)
+ {
+ if (component instanceof UIResource)
+ super.add(component);
+ else
+ {
+ if (constraints instanceof String)
+ insertTab((String) constraints, null, component, null, index);
+ else
+ insertTab(component.getName(),
+ (constraints instanceof Icon) ? (Icon) constraints : null,
+ component, null, index);
+ }
+ }
+
+ /**
+ * Removes the tab at index. After the component associated with
+ * index is removed, its visibility is reset to true to ensure it
+ * will be visible if added to other containers.
+ *
+ * @param index The index of the tab to remove.
+ */
+ public void removeTabAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+
+ // We need to adjust the selection if we remove a tab that comes
+ // before the selected tab or if the selected tab is removed.
+ // This decrements the selected index by 1 if any of this is the case.
+ // Note that this covers all cases:
+ // - When the selected tab comes after the removed tab, this simply
+ // adjusts the selection so that after the removal the selected tab
+ // is still the same.
+ // - When we remove the currently selected tab, then the tab before the
+ // selected tab gets selected.
+ // - When the last tab is removed, then we have an index==0, which gets
+ // decremented to -1, which means no selection, which is 100% perfect.
+ int selectedIndex = getSelectedIndex();
+ if (selectedIndex >= index)
+ setSelectedIndex(selectedIndex - 1);
+
+ Component comp = getComponentAt(index);
+
+ // Remove the tab object.
+ tabs.remove(index);
+
+ // Remove the component. I think we cannot assume that the tab order
+ // is equal to the component order, so we iterate over the children
+ // here to find the and remove the correct component.
+ if (comp != null)
+ {
+ Component[] children = getComponents();
+ for (int i = children.length - 1; i >= 0; --i)
+ {
+ if (children[i] == comp)
+ {
+ super.remove(i);
+ comp.setVisible(true);
+ break;
+ }
+ }
+ }
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Removes the specified Component from the JTabbedPane.
+ *
+ * @param component The Component to remove.
+ */
+ public void remove(Component component)
+ {
+ // Since components implementing UIResource
+ // are not added as regular tabs by the add()
+ // methods we have to take special care when
+ // removing these object. Especially
+ // Container.remove(Component) cannot be used
+ // because it will call JTabbedPane.remove(int)
+ // later which is overridden and can only
+ // handle tab components.
+ // This implementation can even cope with a
+ // situation that someone called insertTab()
+ // with a component that implements UIResource.
+ int index = indexOfComponent(component);
+
+ // If the component is not a tab component
+ // find out its Container-given index
+ // and call that class' implementation
+ // directly.
+ if (index == -1)
+ {
+ Component[] cs = getComponents();
+ for (int i = 0; i< cs.length; i++)
+ if (cs[i] == component)
+ super.remove(i);
+ }
+ else
+ removeTabAt(index);
+ }
+
+ /**
+ * Removes the tab and component which corresponds to the specified index.
+ *
+ * @param index The index of the tab to remove.
+ */
+ public void remove(int index)
+ {
+ removeTabAt(index);
+ }
+
+ /**
+ * This method removes all tabs and associated components from the
+ * JTabbedPane.
+ */
+ public void removeAll()
+ {
+ setSelectedIndex(-1);
+ for (int i = getTabCount() - 1; i >= 0; i--)
+ removeTabAt(i);
+ }
+
+ /**
+ * This method returns how many tabs are in the JTabbedPane.
+ *
+ * @return The number of tabs in the JTabbedPane.
+ */
+ public int getTabCount()
+ {
+ return tabs.size();
+ }
+
+ /**
+ * This method returns the number of runs used to paint the JTabbedPane.
+ *
+ * @return The number of runs.
+ */
+ public int getTabRunCount()
+ {
+ return ((TabbedPaneUI) ui).getTabRunCount(this);
+ }
+
+ /**
+ * This method returns the tab title given the index.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The title for the tab.
+ */
+ public String getTitleAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getTitle();
+ }
+
+ /**
+ * This method returns the active icon given the index.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The active icon for the tab.
+ */
+ public Icon getIconAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getIcon();
+ }
+
+ /**
+ * This method returns the disabled icon given the index.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The disabled icon for the tab.
+ */
+ public Icon getDisabledIconAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getDisabledIcon();
+ }
+
+ /**
+ * This method returns the tooltip string for the tab.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The tooltip string for the tab.
+ */
+ public String getToolTipTextAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getTip();
+ }
+
+ /**
+ * This method returns the foreground color for the tab.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The foreground color for the tab.
+ */
+ public Color getForegroundAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getForeground();
+ }
+
+ /**
+ * This method returns the background color for the tab.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The background color for the tab.
+ */
+ public Color getBackgroundAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getBackground();
+ }
+
+ /**
+ * This method returns the component associated with the tab.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The component associated with the tab.
+ */
+ public Component getComponentAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getComponent();
+ }
+
+ /**
+ * This method returns whether this tab is enabled. Disabled tabs cannot be
+ * selected.
+ *
+ * @param index The index of the tab.
+ *
+ * @return Whether the tab is enabled.
+ */
+ public boolean isEnabledAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).isEnabled();
+ }
+
+ /**
+ * This method returns the mnemonic for the tab.
+ *
+ * @param tabIndex The index of the tab.
+ *
+ * @return The mnemonic for the tab.
+ */
+ public int getMnemonicAt(int tabIndex)
+ {
+ checkIndex(tabIndex, 0, tabs.size());
+ return ((Page) tabs.elementAt(tabIndex)).getMnemonic();
+ }
+
+ /**
+ * This method returns the mnemonic index for the tab.
+ *
+ * @param tabIndex The index of the tab.
+ *
+ * @return The mnemonic index for the tab.
+ */
+ public int getDisplayedMnemonicIndexAt(int tabIndex)
+ {
+ checkIndex(tabIndex, 0, tabs.size());
+ return ((Page) tabs.elementAt(tabIndex)).getDisplayedMnemonicIndex();
+ }
+
+ /**
+ * This method returns the bounds of the tab given the index.
+ *
+ * @param index The index of the tab.
+ *
+ * @return A rectangle describing the bounds of the tab.
+ */
+ public Rectangle getBoundsAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((TabbedPaneUI) ui).getTabBounds(this, index);
+ }
+
+ /**
+ * This method sets the title of the tab.
+ *
+ * @param index The index of the tab.
+ * @param title The new title.
+ */
+ public void setTitleAt(int index, String title)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setTitle(title);
+ }
+
+ /**
+ * This method sets the icon of the tab.
+ *
+ * @param index The index of the tab.
+ * @param icon The new icon.
+ */
+ public void setIconAt(int index, Icon icon)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setIcon(icon);
+ }
+
+ /**
+ * This method sets the disabled icon of the tab.
+ *
+ * @param index The index of the tab.
+ * @param disabledIcon The new disabled icon.
+ */
+ public void setDisabledIconAt(int index, Icon disabledIcon)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setDisabledIcon(disabledIcon);
+ }
+
+ /**
+ * This method sets the tooltip text of the tab.
+ *
+ * @param index The index of the tab.
+ * @param toolTipText The tooltip text.
+ */
+ public void setToolTipTextAt(int index, String toolTipText)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setTip(toolTipText);
+ }
+
+ /**
+ * This method sets the background color of the tab.
+ *
+ * @param index The index of the tab.
+ * @param background The background color of the tab.
+ */
+ public void setBackgroundAt(int index, Color background)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setBackground(background);
+ }
+
+ /**
+ * This method sets the foreground color of the tab.
+ *
+ * @param index The index of the tab.
+ * @param foreground The foreground color of the tab.
+ */
+ public void setForegroundAt(int index, Color foreground)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setForeground(foreground);
+ }
+
+ /**
+ * This method sets whether the tab is enabled.
+ *
+ * @param index The index of the tab.
+ * @param enabled Whether the tab is enabled.
+ */
+ public void setEnabledAt(int index, boolean enabled)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setEnabled(enabled);
+ }
+
+ /**
+ * This method sets the component associated with the tab.
+ *
+ * @param index The index of the tab.
+ * @param component The component associated with the tab.
+ */
+ public void setComponentAt(int index, Component component)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setComponent(component);
+ }
+
+ /**
+ * This method sets the displayed mnemonic index of the tab.
+ *
+ * @param tabIndex The index of the tab.
+ * @param mnemonicIndex The mnemonic index.
+ */
+ public void setDisplayedMnemonicIndexAt(int tabIndex, int mnemonicIndex)
+ {
+ checkIndex(tabIndex, 0, tabs.size());
+ ((Page) tabs.elementAt(tabIndex)).setDisplayedMnemonicIndex(mnemonicIndex);
+ }
+
+ /**
+ * This method sets the mnemonic for the tab.
+ *
+ * @param tabIndex The index of the tab.
+ * @param mnemonic The mnemonic.
+ */
+ public void setMnemonicAt(int tabIndex, int mnemonic)
+ {
+ checkIndex(tabIndex, 0, tabs.size());
+ ((Page) tabs.elementAt(tabIndex)).setMnemonic(mnemonic);
+ }
+
+ /**
+ * This method finds the index of a tab given the title.
+ *
+ * @param title The title that belongs to a tab.
+ *
+ * @return The index of the tab that has the title or -1 if not found.
+ */
+ public int indexOfTab(String title)
+ {
+ int index = -1;
+ for (int i = 0; i < tabs.size(); i++)
+ {
+ if (((Page) tabs.elementAt(i)).getTitle().equals(title))
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ /**
+ * This method finds the index of a tab given the icon.
+ *
+ * @param icon The icon that belongs to a tab.
+ *
+ * @return The index of the tab that has the icon or -1 if not found.
+ */
+ public int indexOfTab(Icon icon)
+ {
+ int index = -1;
+ for (int i = 0; i < tabs.size(); i++)
+ {
+ if (((Page) tabs.elementAt(i)).getIcon() == icon)
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ /**
+ * This method finds the index of a tab given the component.
+ *
+ * @param component A component associated with a tab.
+ *
+ * @return The index of the tab that has this component or -1 if not found.
+ */
+ public int indexOfComponent(Component component)
+ {
+ int index = -1;
+ for (int i = 0; i < tabs.size(); i++)
+ {
+ if (((Page) tabs.elementAt(i)).getComponent() == component)
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ /**
+ * This method returns a tab index given an (x,y) location. The origin of
+ * the (x,y) pair will be the JTabbedPane's top left position. The tab
+ * returned will be the one that contains the point. This method is
+ * delegated to the UI.
+ *
+ * @param x The x coordinate of the point.
+ * @param y The y coordinate of the point.
+ *
+ * @return The index of the tab that contains the point.
+ */
+ public int indexAtLocation(int x, int y)
+ {
+ return ((TabbedPaneUI) ui).tabForCoordinate(this, x, y);
+ }
+
+ /**
+ * This method returns the tooltip text given a mouse event.
+ *
+ * @param event The mouse event.
+ *
+ * @return The tool tip text that is associated with this mouse event.
+ */
+ public String getToolTipText(MouseEvent event)
+ {
+ int index = indexAtLocation(event.getX(), event.getY());
+ return ((Page) tabs.elementAt(index)).getTip();
+ }
+
+ /**
+ * Returns a string describing the attributes for the
+ * <code>JTabbedPane</code> component, for use in debugging. The return
+ * value is guaranteed to be non-<code>null</code>, but the format of the
+ * string may vary between implementations.
+ *
+ * @return A string describing the attributes of the
+ * <code>JTabbedPane</code>.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",tabPlacement=");
+ if (tabPlacement == TOP)
+ sb.append("TOP");
+ if (tabPlacement == BOTTOM)
+ sb.append("BOTTOM");
+ if (tabPlacement == LEFT)
+ sb.append("LEFT");
+ if (tabPlacement == RIGHT)
+ sb.append("RIGHT");
+ return sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JTabbedPane</code> component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJTabbedPane}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ {
+ AccessibleJTabbedPane ctx = new AccessibleJTabbedPane();
+ addChangeListener(ctx);
+ accessibleContext = ctx;
+ }
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java
new file mode 100644
index 000000000..b60c67aa3
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTable.java
@@ -0,0 +1,5157 @@
+/* JTable.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.util.Date;
+import java.util.EventObject;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleExtendedTable;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleTable;
+import javax.accessibility.AccessibleTableModelChange;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableColumnModelEvent;
+import javax.swing.event.TableColumnModelListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.plaf.TableUI;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+import javax.swing.table.TableModel;
+
+/**
+ * The table component, displaying information, organized in rows and columns.
+ * The table can be placed in the scroll bar and have the optional header
+ * that is always visible. Cell values may be editable after double clicking
+ * on the cell. Cell columns may have various data types, that are
+ * displayed and edited by the different renderers and editors. It is possible
+ * to set different column width. The columns are also resizeable by
+ * dragging the column boundary in the header.
+ */
+public class JTable
+ extends JComponent
+ implements TableModelListener, Scrollable, TableColumnModelListener,
+ ListSelectionListener, CellEditorListener, Accessible
+{
+ /**
+ * Provides accessibility support for <code>JTable</code>.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJTable
+ extends AccessibleJComponent
+ implements AccessibleSelection, ListSelectionListener, TableModelListener,
+ TableColumnModelListener, CellEditorListener, PropertyChangeListener,
+ AccessibleExtendedTable
+ {
+
+ /**
+ * Provides accessibility support for table cells.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJTableCell
+ extends AccessibleContext
+ implements Accessible, AccessibleComponent
+ {
+
+ /**
+ * The table of this cell.
+ */
+ private JTable table;
+
+ /**
+ * The row index of this cell.
+ */
+ private int row;
+
+ /**
+ * The column index of this cell.
+ */
+ private int column;
+
+ /**
+ * The index of this cell inside the AccessibleJTable parent.
+ */
+ private int index;
+
+ /**
+ * Creates a new <code>AccessibleJTableCell</code>.
+ *
+ * @param t the table
+ * @param r the row
+ * @param c the column
+ * @param i the index of this cell inside the accessible table parent
+ */
+ public AccessibleJTableCell(JTable t, int r, int c, int i)
+ {
+ table = t;
+ row = r;
+ column = c;
+ index = i;
+ }
+
+ /**
+ * Returns the accessible row for the table cell.
+ *
+ * @return the accessible row for the table cell
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ // TODO: What is the role of the table cell?
+ // Seems like the RI returns UNKNOWN here for 'normal' cells, might
+ // be different for special renderers though (not tested yet).
+ return AccessibleRole.UNKNOWN;
+ }
+
+ /**
+ * Returns the accessible state set of this accessible table cell.
+ *
+ * @return the accessible state set of this accessible table cell
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = new AccessibleStateSet();
+
+ // Figure out the SHOWING state.
+ Rectangle visibleRect = getVisibleRect();
+ Rectangle cellRect = getCellRect(row, column, false);
+ if (visibleRect.intersects(cellRect))
+ state.add(AccessibleState.SHOWING);
+
+ // Figure out SELECTED state.
+ if (isCellSelected(row, column))
+ state.add(AccessibleState.SELECTED);
+
+ // Figure out ACTIVE state.
+ if (row == getSelectedRow() && column == getSelectedColumn())
+ state.add(AccessibleState.ACTIVE);
+
+ // TRANSIENT seems to be always set in the RI.
+ state.add(AccessibleState.TRANSIENT);
+
+ // TODO: Any other state to handle here?
+ return state;
+ }
+
+ /**
+ * Returns the index of this cell in the parent object.
+ *
+ * @return the index of this cell in the parent object
+ */
+ public int getAccessibleIndexInParent()
+ {
+ return index;
+ }
+
+ /**
+ * Returns the number of children of this object. Table cells cannot have
+ * children, so we return <code>0</code> here.
+ *
+ * @return <code>0</code>
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the accessible child at index <code>i</code>. Table cells
+ * don't have children, so we return <code>null</code> here.
+ *
+ * @return <code>null</code>
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the locale setting for this accessible table cell.
+ *
+ * @return the locale setting for this accessible table cell
+ */
+ public Locale getLocale()
+ {
+ // TODO: For now, we return english here. This must be fixed as soon
+ // as we have a localized Swing.
+ return Locale.ENGLISH;
+ }
+
+ /**
+ * Returns the accessible context of this table cell. Since accessible
+ * table cells are their own accessible context, we return
+ * <code>this</code>.
+ *
+ * @return the accessible context of this table cell
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the background color of this cell.
+ *
+ * @return the background color of this cell
+ */
+ public Color getBackground()
+ {
+ return table.getBackground();
+ }
+
+ /**
+ * Sets the background of the cell. Since table cells cannot have
+ * individual background colors, this method does nothing. Set the
+ * background directly on the table instead.
+ *
+ * @param color not used
+ */
+ public void setBackground(Color color)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the foreground color of the table cell.
+ *
+ * @return the foreground color of the table cell
+ */
+ public Color getForeground()
+ {
+ return table.getForeground();
+ }
+
+ /**
+ * Sets the foreground of the cell. Since table cells cannot have
+ * individual foreground colors, this method does nothing. Set the
+ * foreground directly on the table instead.
+ *
+ * @param color not used
+ */
+ public void setForeground(Color color)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the cursor for this table cell.
+ *
+ * @return the cursor for this table cell
+ */
+ public Cursor getCursor()
+ {
+ return table.getCursor();
+ }
+
+ /**
+ * Sets the cursor of the cell. Since table cells cannot have
+ * individual cursors, this method does nothing. Set the
+ * cursor directly on the table instead.
+ *
+ * @param cursor not used
+ */
+ public void setCursor(Cursor cursor)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the font of the table cell.
+ *
+ * @return the font of the table cell
+ */
+ public Font getFont()
+ {
+ return table.getFont();
+ }
+
+ /**
+ * Sets the font of the cell. Since table cells cannot have
+ * individual fonts, this method does nothing. Set the
+ * font directly on the table instead.
+ *
+ * @param font not used
+ */
+ public void setFont(Font font)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the font metrics for a specified font.
+ *
+ * @param font the font for which we return the metrics
+ *
+ * @return the font metrics for a specified font
+ */
+ public FontMetrics getFontMetrics(Font font)
+ {
+ return table.getFontMetrics(font);
+ }
+
+ /**
+ * Returns <code>true</code> if this table cell is enabled,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if this table cell is enabled,
+ * <code>false</code> otherwise
+ */
+ public boolean isEnabled()
+ {
+ return table.isEnabled();
+ }
+
+ /**
+ * Table cells cannot be disabled or enabled individually, so this method
+ * does nothing. Set the enabled flag on the table itself.
+ *
+ * @param b not used here
+ */
+ public void setEnabled(boolean b)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns <code>true</code> if this cell is visible, <code>false</code>
+ * otherwise.
+ *
+ * @return <code>true</code> if this cell is visible, <code>false</code>
+ * otherwise
+ */
+ public boolean isVisible()
+ {
+ return table.isVisible();
+ }
+
+ /**
+ * The visibility cannot be set on individual table cells, so this method
+ * does nothing. Set the visibility on the table itself.
+ *
+ * @param b not used
+ */
+ public void setVisible(boolean b)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns <code>true</code> if this table cell is currently showing on
+ * screen.
+ *
+ * @return <code>true</code> if this table cell is currently showing on
+ * screen
+ */
+ public boolean isShowing()
+ {
+ return table.isShowing();
+ }
+
+ /**
+ * Returns <code>true</code> if this table cell contains the location
+ * at <code>point</code>, <code>false</code> otherwise.
+ * <code>point</code> is interpreted as relative to the coordinate system
+ * of the table cell.
+ *
+ * @return <code>true</code> if this table cell contains the location
+ * at <code>point</code>, <code>false</code> otherwise
+ */
+ public boolean contains(Point point)
+ {
+ Rectangle cellRect = table.getCellRect(row, column, true);
+ cellRect.x = 0;
+ cellRect.y = 0;
+ return cellRect.contains(point);
+ }
+
+ /**
+ * Returns the screen location of the table cell.
+ *
+ * @return the screen location of the table cell
+ */
+ public Point getLocationOnScreen()
+ {
+ Point tableLoc = table.getLocationOnScreen();
+ Rectangle cellRect = table.getCellRect(row, column, true);
+ tableLoc.x += cellRect.x;
+ tableLoc.y += cellRect.y;
+ return tableLoc;
+ }
+
+ /**
+ * Returns the location of this cell relative to the table's bounds.
+ *
+ * @return the location of this cell relative to the table's bounds
+ */
+ public Point getLocation()
+ {
+ Rectangle cellRect = table.getCellRect(row, column, true);
+ return new Point(cellRect.x, cellRect.y);
+ }
+
+ /**
+ * The location of the table cells cannot be manipulated directly, so
+ * this method does nothing.
+ *
+ * @param point not used
+ */
+ public void setLocation(Point point)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the bounds of the cell relative to its table.
+ *
+ * @return the bounds of the cell relative to its table
+ */
+ public Rectangle getBounds()
+ {
+ return table.getCellRect(row, column, true);
+ }
+
+ /**
+ * The bounds of the table cells cannot be manipulated directly, so
+ * this method does nothing.
+ *
+ * @param rectangle not used
+ */
+ public void setBounds(Rectangle rectangle)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the size of the table cell.
+ *
+ * @return the size of the table cell
+ */
+ public Dimension getSize()
+ {
+ Rectangle cellRect = table.getCellRect(row, column, true);
+ return new Dimension(cellRect.width, cellRect.height);
+ }
+
+ /**
+ * The size cannot be set on table cells directly, so this method does
+ * nothing.
+ *
+ * @param dimension not used
+ */
+ public void setSize(Dimension dimension)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Table cells have no children, so we return <code>null</code> here.
+ *
+ * @return <code>null</code>
+ */
+ public Accessible getAccessibleAt(Point point)
+ {
+ return null;
+ }
+
+ /**
+ * Returns <code>true</code> if this table cell is focus traversable,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if this table cell is focus traversable,
+ * <code>false</code> otherwise
+ */
+ public boolean isFocusTraversable()
+ {
+ return table.isFocusable();
+ }
+
+ /**
+ * Requests that this table cell gets the keyboard focus.
+ */
+ public void requestFocus()
+ {
+ // We first set the selection models' lead selection to this cell.
+ table.getColumnModel().getSelectionModel()
+ .setLeadSelectionIndex(column);
+ table.getSelectionModel().setLeadSelectionIndex(row);
+ // Now we request that the table receives focus.
+ table.requestFocus();
+ }
+
+ /**
+ * Adds a focus listener to this cell. The focus listener is really
+ * added to the table, so there is no way to find out when an individual
+ * cell changes the focus.
+ *
+ * @param listener the focus listener to add
+ */
+ public void addFocusListener(FocusListener listener)
+ {
+ table.addFocusListener(listener);
+ }
+
+ /**
+ * Removes a focus listener from the cell. The focus listener is really
+ * removed from the table.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeFocusListener(FocusListener listener)
+ {
+ table.removeFocusListener(listener);
+ }
+
+ }
+
+ protected class AccessibleJTableModelChange
+ implements AccessibleTableModelChange
+ {
+ protected int type;
+ protected int firstRow;
+ protected int lastRow;
+ protected int firstColumn;
+ protected int lastColumn;
+
+ protected AccessibleJTableModelChange(int type, int firstRow,
+ int lastRow, int firstColumn,
+ int lastColumn)
+ {
+ this.type = type;
+ this.firstRow = firstRow;
+ this.lastRow = lastRow;
+ this.firstColumn = firstColumn;
+ this.lastColumn = lastColumn;
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public int getFirstRow()
+ {
+ return firstRow;
+ }
+
+ public int getLastRow()
+ {
+ return lastRow;
+ }
+
+ public int getFirstColumn()
+ {
+ return firstColumn;
+ }
+
+ public int getLastColumn()
+ {
+ return lastColumn;
+ }
+ }
+
+ /**
+ * The RI returns an instance with this name in
+ * {@link #getAccessibleColumnHeader()}, this makes sense, so we do the
+ * same.
+ */
+ private class AccessibleTableHeader
+ implements AccessibleTable
+ {
+
+ /**
+ * The JTableHeader wrapped by this class.
+ */
+ private JTableHeader header;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param h the JTableHeader to wrap
+ */
+ private AccessibleTableHeader(JTableHeader h)
+ {
+ header = h;
+ }
+
+ /**
+ * Returns the caption for the table header.
+ *
+ * @return the caption for the table header
+ */
+ public Accessible getAccessibleCaption()
+ {
+ // The RI seems to always return null here, so do we.
+ return null;
+ }
+
+ /**
+ * Sets the caption for the table header.
+ *
+ * @param caption the caption to set
+ */
+ public void setAccessibleCaption(Accessible caption)
+ {
+ // This seems to be a no-op in the RI, so we do the same.
+ }
+
+ /**
+ * Returns the caption for the table header.
+ *
+ * @return the caption for the table header
+ */
+ public Accessible getAccessibleSummary()
+ {
+ // The RI seems to always return null here, so do we.
+ return null;
+ }
+
+ /**
+ * Sets the summary for the table header.
+ *
+ * @param summary the caption to set
+ */
+ public void setAccessibleSummary(Accessible summary)
+ {
+ // This seems to be a no-op in the RI, so we do the same.
+ }
+
+ /**
+ * Returns the number of rows, which is always 1 for the table header.
+ *
+ * @return the number of rows
+ */
+ public int getAccessibleRowCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the number of columns in the table header.
+ *
+ * @return the number of columns in the table header
+ */
+ public int getAccessibleColumnCount()
+ {
+ return header.getColumnModel().getColumnCount();
+ }
+
+ /**
+ * Returns the accessible child at the specified row and column.
+ * The row number is ignored here, and we return an
+ * AccessibleJTableHeaderCell here with the renderer component as
+ * component.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the accessible child at the specified row and column
+ */
+ public Accessible getAccessibleAt(int r, int c)
+ {
+ TableColumn column = header.getColumnModel().getColumn(c);
+ TableCellRenderer rend = column.getHeaderRenderer();
+ if (rend == null)
+ rend = header.getDefaultRenderer();
+ Component comp =
+ rend.getTableCellRendererComponent(header.getTable(),
+ column.getHeaderValue(), false,
+ false, -1, c);
+ return new AccessibleJTableHeaderCell(header, comp, r, c);
+ }
+
+ public int getAccessibleRowExtentAt(int r, int c)
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getAccessibleColumnExtentAt(int r, int c)
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public AccessibleTable getAccessibleRowHeader()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setAccessibleRowHeader(AccessibleTable header)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public AccessibleTable getAccessibleColumnHeader()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setAccessibleColumnHeader(AccessibleTable header)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Accessible getAccessibleRowDescription(int r)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setAccessibleRowDescription(int r, Accessible description)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Accessible getAccessibleColumnDescription(int c)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setAccessibleColumnDescription(int c, Accessible description)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public boolean isAccessibleSelected(int r, int c)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isAccessibleRowSelected(int r)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isAccessibleColumnSelected(int c)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public int[] getSelectedAccessibleRows()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public int[] getSelectedAccessibleColumns()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
+
+ /**
+ * The RI returns an instance of such class for table header cells. This
+ * makes sense so I added this class. This still needs to be fully
+ * implemented, I just don't feel motivated enough to do so just now.
+ */
+ private class AccessibleJTableHeaderCell
+ extends AccessibleContext
+ implements Accessible, AccessibleComponent
+ {
+
+ JTableHeader header;
+
+ int columnIndex;
+
+ /**
+ *
+ * @param h the table header.
+ * @param comp
+ * @param r
+ * @param c the column index.
+ */
+ private AccessibleJTableHeaderCell(JTableHeader h, Component comp, int r,
+ int c)
+ {
+ header = h;
+ columnIndex = c;
+ }
+
+ /**
+ * Returns the header renderer.
+ *
+ * @return The header renderer.
+ */
+ Component getColumnHeaderRenderer()
+ {
+ TableColumn tc = header.getColumnModel().getColumn(columnIndex);
+ TableCellRenderer r = tc.getHeaderRenderer();
+ if (r == null)
+ r = header.getDefaultRenderer();
+ return r.getTableCellRendererComponent(header.getTable(),
+ tc.getHeaderValue(), false, false, -1, columnIndex);
+ }
+
+ /**
+ * Returns the accessible role for the table header cell.
+ *
+ * @return The accessible role.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ Component renderer = getColumnHeaderRenderer();
+ if (renderer instanceof Accessible)
+ {
+ Accessible ac = (Accessible) renderer;
+ return ac.getAccessibleContext().getAccessibleRole();
+ }
+ return null;
+ }
+
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public int getAccessibleIndexInParent()
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getAccessibleChildrenCount()
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public Accessible getAccessibleChild(int i)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Locale getLocale()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Returns the accessible context.
+ *
+ * @return <code>this</code>.
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ public Color getBackground()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setBackground(Color color)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Color getForeground()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setForeground(Color color)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Cursor getCursor()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setCursor(Cursor cursor)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Font getFont()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setFont(Font font)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public FontMetrics getFontMetrics(Font font)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isEnabled()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setEnabled(boolean b)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public boolean isVisible()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setVisible(boolean b)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public boolean isShowing()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean contains(Point point)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public Point getLocationOnScreen()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Point getLocation()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setLocation(Point point)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Rectangle getBounds()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setBounds(Rectangle rectangle)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Dimension getSize()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setSize(Dimension dimension)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Accessible getAccessibleAt(Point point)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isFocusTraversable()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void requestFocus()
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void addFocusListener(FocusListener listener)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeFocusListener(FocusListener listener)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ /**
+ * The last selected row. This is needed to track the selection in
+ * {@link #valueChanged(ListSelectionEvent)}.
+ */
+ private int lastSelectedRow;
+
+ /**
+ * The last selected column. This is needed to track the selection in
+ * {@link #valueChanged(ListSelectionEvent)}.
+ */
+ private int lastSelectedColumn;
+
+ /**
+ * The caption of the table.
+ */
+ private Accessible caption;
+
+ /**
+ * The summary of the table.
+ */
+ private Accessible summary;
+
+ /**
+ * Accessible descriptions for rows.
+ */
+ private Accessible[] rowDescriptions;
+
+ /**
+ * Accessible descriptions for columns.
+ */
+ private Accessible[] columnDescriptions;
+
+ /**
+ * Creates a new <code>AccessibleJTable</code>.
+ *
+ * @since JDK1.5
+ */
+ protected AccessibleJTable()
+ {
+ getModel().addTableModelListener(this);
+ getSelectionModel().addListSelectionListener(this);
+ getColumnModel().addColumnModelListener(this);
+ lastSelectedRow = getSelectedRow();
+ lastSelectedColumn = getSelectedColumn();
+ TableCellEditor editor = getCellEditor();
+ if (editor != null)
+ editor.addCellEditorListener(this);
+ }
+
+ /**
+ * Returns the accessible role for the <code>JTable</code> component.
+ *
+ * @return {@link AccessibleRole#TABLE}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TABLE;
+ }
+
+ /**
+ * Returns the accessible table.
+ *
+ * @return <code>this</code>.
+ */
+ public AccessibleTable getAccessibleTable()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the number of selected items in this table.
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return getSelectedColumnCount();
+ }
+
+ /**
+ * Returns the selected accessible object with the specified index
+ * <code>i</code>. This basically returns the i-th selected cell in the
+ * table when going though it row-wise, and inside the rows, column-wise.
+ *
+ * @param i the index of the selected object to find
+ *
+ * @return the selected accessible object with the specified index
+ * <code>i</code>
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ Accessible found = null;
+
+ int[] selectedRows = getSelectedRows();
+ int[] selectedColumns = getSelectedColumns();
+ int numCols = getColumnCount();
+ int numRows = getRowCount();
+
+ // We have to go through every selected row and column and count until we
+ // find the specified index. This is potentially inefficient, but I can't
+ // think of anything better atm.
+ if (getRowSelectionAllowed() && getColumnSelectionAllowed())
+ {
+ int current = -1;
+ int newIndex = current;
+ int lastSelectedRow = -1;
+ // Go through the selected rows array, don't forget the selected
+ // cells inside the not-selected rows' columns.
+ for (int j = 0; i < selectedRows.length; i++)
+ {
+ // Handle unselected rows between this selected and the last
+ // selected row, if any.
+ int selectedRow = selectedRows[j];
+ int r = -1;
+ int ci = -1;
+ for (r = lastSelectedRow + 1;
+ r < selectedRow && current < i; r++)
+ {
+ for (ci = 0; ci < selectedColumns.length && current < i;
+ ci++)
+ {
+ current++;
+ }
+ }
+ if (current == i)
+ {
+ // We found the cell in the above loops, now get out of here.
+ found = getAccessibleChild(r * numCols
+ + selectedColumns[ci]);
+ break;
+ }
+
+ // If we're still here, handle the current selected row.
+ if (current < i && current + numCols >= i)
+ {
+ // The cell must be in that row, which one is it?
+ found = getAccessibleChild(r * numCols + (i - current));
+ break;
+ }
+ current += numCols;
+ }
+ if (found == null)
+ {
+ // The cell can still be in the last couple of unselected rows.
+ int r = 0;
+ int ci = 0;
+ for (r = lastSelectedRow + 1;
+ r < numRows && current < i; r++)
+ {
+ for (ci = 0; ci < selectedColumns.length && current < i;
+ ci++)
+ {
+ current++;
+ }
+ }
+ if (current == i)
+ {
+ // We found the cell in the above loops, now get out of here.
+ found = getAccessibleChild(r * numCols
+ + selectedColumns[ci]);
+ }
+ }
+ }
+ // One or more rows can be completely selected.
+ else if (getRowSelectionAllowed())
+ {
+ int c = i % numCols;
+ int r = selectedRows[i / numCols];
+ found = getAccessibleChild(r * numCols + c);
+ }
+ // One or more columns can be completely selected.
+ else if (getRowSelectionAllowed())
+ {
+ int numSelectedColumns = selectedColumns.length;
+ int c = selectedColumns[i % numSelectedColumns];
+ int r = i / numSelectedColumns;
+ found = getAccessibleChild(r * numCols + c);
+ }
+
+ return found;
+ }
+
+ /**
+ * Returns <code>true</code> if the accessible child with the index
+ * <code>i</code> is selected, <code>false</code> otherwise.
+ *
+ * @param i the index of the accessible to check
+ *
+ * @return <code>true</code> if the accessible child with the index
+ * <code>i</code> is selected, <code>false</code> otherwise
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ int r = getAccessibleRowAtIndex(i);
+ int c = getAccessibleColumnAtIndex(i);
+ return isCellSelected(r, c);
+ }
+
+ /**
+ * Adds the accessible child with the specified index <code>i</code> to the
+ * selection.
+ *
+ * @param i the index of the accessible child to add to the selection
+ */
+ public void addAccessibleSelection(int i)
+ {
+ int r = getAccessibleRowAtIndex(i);
+ int c = getAccessibleColumnAtIndex(i);
+ changeSelection(r, c, true, false);
+ }
+
+ /**
+ * Removes the accessible child with the specified index <code>i</code>
+ * from the current selection. This will only work on tables that have
+ * cell selection enabled (<code>rowSelectionAllowed == false &&
+ * columnSelectionAllowed == false</code>).
+ *
+ * @param i the index of the accessible to be removed from the selection
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ if (! getRowSelectionAllowed() && ! getColumnSelectionAllowed())
+ {
+ int r = getAccessibleRowAtIndex(i);
+ int c = getAccessibleColumnAtIndex(i);
+ removeRowSelectionInterval(r, r);
+ removeColumnSelectionInterval(c, c);
+ }
+ }
+
+ /**
+ * Deselects all selected accessible children.
+ */
+ public void clearAccessibleSelection()
+ {
+ clearSelection();
+ }
+
+ /**
+ * Selects all accessible children that can be selected. This will only
+ * work on tables that support multiple selections and that have individual
+ * cell selection enabled.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ selectAll();
+ }
+
+ /**
+ * Receives notification when the row selection changes and fires
+ * appropriate property change events.
+ *
+ * @param event the list selection event
+ */
+ public void valueChanged(ListSelectionEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
+ Boolean.FALSE, Boolean.TRUE);
+ int r = getSelectedRow();
+ int c = getSelectedColumn();
+ if (r != lastSelectedRow || c != lastSelectedColumn)
+ {
+ Accessible o = getAccessibleAt(lastSelectedRow,
+ lastSelectedColumn);
+ Accessible n = getAccessibleAt(r, c);
+ firePropertyChange(AccessibleContext
+ .ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, o, n);
+ lastSelectedRow = r;
+ lastSelectedColumn = c;
+ }
+ }
+
+ /**
+ * Receives notification when the table model changes. Depending on the
+ * type of change, this method calls {@link #tableRowsInserted} or
+ * {@link #tableRowsDeleted}.
+ *
+ * @param event the table model event
+ */
+ public void tableChanged(TableModelEvent event)
+ {
+ switch (event.getType())
+ {
+ case TableModelEvent.INSERT:
+ tableRowsInserted(event);
+ break;
+ case TableModelEvent.DELETE:
+ tableRowsDeleted(event);
+ break;
+ }
+ }
+
+ /**
+ * Receives notification when one or more rows have been inserted into the
+ * table and fires appropriate property change events.
+ *
+ * @param event the table model event
+ */
+ public void tableRowsInserted(TableModelEvent event)
+ {
+ handleRowChange(event);
+ }
+
+ /**
+ * Receives notification when one or more rows have been deleted from the
+ * table.
+ *
+ * @param event the table model event
+ */
+ public void tableRowsDeleted(TableModelEvent event)
+ {
+ handleRowChange(event);
+ }
+
+ /**
+ * Fires a PropertyChangeEvent for inserted or deleted rows.
+ *
+ * @param event the table model event
+ */
+ private void handleRowChange(TableModelEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ int firstColumn = event.getColumn();
+ int lastColumn = event.getColumn();
+ if (firstColumn == TableModelEvent.ALL_COLUMNS)
+ {
+ firstColumn = 0;
+ lastColumn = getColumnCount() - 1;
+ }
+ AccessibleJTableModelChange change = new AccessibleJTableModelChange
+ (event.getType(), event.getFirstRow(), event.getLastRow(),
+ firstColumn, lastColumn);
+ firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
+ null, change);
+ }
+
+ public void columnAdded(TableColumnModelEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ handleColumnChange(AccessibleTableModelChange.INSERT,
+ event.getFromIndex(), event.getToIndex());
+ }
+
+ public void columnRemoved(TableColumnModelEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ handleColumnChange(AccessibleTableModelChange.DELETE,
+ event.getFromIndex(), event.getToIndex());
+ }
+
+ public void columnMoved(TableColumnModelEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ handleColumnChange(AccessibleTableModelChange.DELETE,
+ event.getFromIndex(), event.getFromIndex());
+ handleColumnChange(AccessibleTableModelChange.INSERT,
+ event.getFromIndex(), event.getToIndex());
+ }
+
+ /**
+ * Fires a PropertyChangeEvent for inserted or deleted columns.
+ *
+ * @param type the type of change
+ * @param from the start of the change
+ * @param to the target of the change
+ */
+ private void handleColumnChange(int type, int from, int to)
+ {
+ AccessibleJTableModelChange change =
+ new AccessibleJTableModelChange(type, 0, 0, from, to);
+ firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
+ null, change);
+ }
+
+ public void columnMarginChanged(ChangeEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ }
+
+ public void columnSelectionChanged(ListSelectionEvent event)
+ {
+ // AFAICS, nothing is done here.
+ }
+
+ public void editingCanceled(ChangeEvent event)
+ {
+ // AFAICS, nothing is done here.
+ }
+
+ public void editingStopped(ChangeEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ }
+
+ /**
+ * Receives notification when any of the JTable's properties changes. This
+ * is used to replace the listeners on the table's model, selection model,
+ * column model and cell editor.
+ *
+ * @param e the property change event
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String propName = e.getPropertyName();
+ if (propName.equals("tableModel"))
+ {
+ TableModel oldModel = (TableModel) e.getOldValue();
+ oldModel.removeTableModelListener(this);
+ TableModel newModel = (TableModel) e.getNewValue();
+ newModel.addTableModelListener(this);
+ }
+ else if (propName.equals("columnModel"))
+ {
+ TableColumnModel oldModel = (TableColumnModel) e.getOldValue();
+ oldModel.removeColumnModelListener(this);
+ TableColumnModel newModel = (TableColumnModel) e.getNewValue();
+ newModel.addColumnModelListener(this);
+ }
+ else if (propName.equals("selectionModel"))
+ {
+ ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue();
+ oldModel.removeListSelectionListener(this);
+ ListSelectionModel newModel = (ListSelectionModel) e.getNewValue();
+ newModel.addListSelectionListener(this);
+ }
+ else if (propName.equals("cellEditor"))
+ {
+ CellEditor oldEd = (CellEditor) e.getOldValue();
+ oldEd.removeCellEditorListener(this);
+ CellEditor newEd = (CellEditor) e.getNewValue();
+ newEd.addCellEditorListener(this);
+ }
+ }
+
+ /**
+ * Returns the row number of an accessible child (cell) with the specified
+ * index.
+ *
+ * @param index the index of the cell of which the row number is queried
+ *
+ * @return the row number of an accessible child (cell) with the specified
+ * index
+ */
+ public int getAccessibleRow(int index)
+ {
+ return getAccessibleRowAtIndex(index);
+ }
+
+ /**
+ * Returns the column number of an accessible child (cell) with the
+ * specified index.
+ *
+ * @param index the index of the cell of which the column number is queried
+ *
+ * @return the column number of an accessible child (cell) with the
+ * specified index
+ */
+ public int getAccessibleColumn(int index)
+ {
+ return getAccessibleColumnAtIndex(index);
+ }
+
+ /**
+ * Returns the index of the accessible child at the specified row and
+ * column.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the index of the accessible child at the specified row and
+ * column
+ */
+ public int getAccessibleIndex(int r, int c)
+ {
+ return getAccessibleIndexAt(r, c);
+ }
+
+ /**
+ * Returns the caption of the table.
+ *
+ * @return the caption of the table
+ *
+ * @see #setAccessibleCaption(Accessible)
+ */
+ public Accessible getAccessibleCaption()
+ {
+ return caption;
+ }
+
+ /**
+ * Sets the caption for the table.
+ *
+ * @param c the caption to set
+ */
+ public void setAccessibleCaption(Accessible c)
+ {
+ caption = c;
+ }
+
+ /**
+ * Returns the summary for the table.
+ *
+ * @return the summary for the table
+ */
+ public Accessible getAccessibleSummary()
+ {
+ return summary;
+ }
+
+ /**
+ * Sets the summary for the table.
+ *
+ * @param s the summary to set
+ */
+ public void setAccessibleSummary(Accessible s)
+ {
+ summary = s;
+ }
+
+ /**
+ * Returns the number of rows in the table.
+ *
+ * @return the number of rows in the table
+ */
+ public int getAccessibleRowCount()
+ {
+ return getRowCount();
+ }
+
+ /**
+ * Returns the number of columns in the table.
+ *
+ * @return the number of columns in the table
+ */
+ public int getAccessibleColumnCount()
+ {
+ return getColumnCount();
+ }
+
+ /**
+ * Returns the accessible child at the given index.
+ *
+ * @param index the child index.
+ *
+ * @return The accessible child.
+ */
+ public Accessible getAccessibleChild(int index)
+ {
+ int r = getAccessibleRow(index);
+ int c = getAccessibleColumn(index);
+ return getAccessibleAt(r, c);
+ }
+
+ /**
+ * Returns the accessible child (table cell) at the specified row and
+ * column.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the accessible child (table cell) at the specified row and
+ * column
+ */
+ public Accessible getAccessibleAt(int r, int c)
+ {
+ TableCellRenderer cellRenderer = getCellRenderer(r, c);
+ Component renderer = cellRenderer.getTableCellRendererComponent(
+ JTable.this, getValueAt(r, c), isCellSelected(r, c), false, r, c);
+ if (renderer instanceof Accessible)
+ return (Accessible) renderer;
+ return null;
+ }
+
+ /**
+ * Returns the number of rows that the specified cell occupies. The
+ * standard table cells only occupy one row, so we return <code>1</code>
+ * here.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the number of rows that the specified cell occupies
+ */
+ public int getAccessibleRowExtentAt(int r, int c)
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the number of columns that the specified cell occupies. The
+ * standard table cells only occupy one column, so we return <code>1</code>
+ * here.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the number of rows that the specified cell occupies
+ */
+ public int getAccessibleColumnExtentAt(int r, int c)
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the accessible row header.
+ *
+ * @return the accessible row header
+ */
+ public AccessibleTable getAccessibleRowHeader()
+ {
+ // The RI seems to always return null here, so do we.
+ return null;
+ }
+
+ /**
+ * Sets the accessible row header.
+ *
+ * @param header the header to set
+ */
+ public void setAccessibleRowHeader(AccessibleTable header)
+ {
+ // In the RI this seems to be a no-op.
+ }
+
+ /**
+ * Returns the column header.
+ *
+ * @return the column header, or <code>null</code> if there is no column
+ * header
+ */
+ public AccessibleTable getAccessibleColumnHeader()
+ {
+ JTableHeader h = getTableHeader();
+ AccessibleTable header = null;
+ if (h != null)
+ header = new AccessibleTableHeader(h);
+ return header;
+ }
+
+ /**
+ * Sets the accessible column header. The default implementation doesn't
+ * allow changing the header this way, so this is a no-op.
+ *
+ * @param header the accessible column header to set
+ */
+ public void setAccessibleColumnHeader(AccessibleTable header)
+ {
+ // The RI doesn't seem to do anything, so we also do nothing.
+ }
+
+ /**
+ * Returns the accessible description for the row with the specified index,
+ * or <code>null</code> if no description has been set.
+ *
+ * @param r the row for which the description is queried
+ *
+ * @return the accessible description for the row with the specified index,
+ * or <code>null</code> if no description has been set
+ */
+ public Accessible getAccessibleRowDescription(int r)
+ {
+ Accessible descr = null;
+ if (rowDescriptions != null)
+ descr = rowDescriptions[r];
+ return descr;
+ }
+
+ /**
+ * Sets the accessible description for the row with the specified index.
+ *
+ * @param r the row number for which to set the description
+ * @param description the description to set
+ */
+ public void setAccessibleRowDescription(int r, Accessible description)
+ {
+ if (rowDescriptions == null)
+ rowDescriptions = new Accessible[getAccessibleRowCount()];
+ rowDescriptions[r] = description;
+ }
+
+ /**
+ * Returns the accessible description for the column with the specified
+ * index, or <code>null</code> if no description has been set.
+ *
+ * @param c the column for which the description is queried
+ *
+ * @return the accessible description for the column with the specified
+ * index, or <code>null</code> if no description has been set
+ */
+ public Accessible getAccessibleColumnDescription(int c)
+ {
+ Accessible descr = null;
+ if (columnDescriptions != null)
+ descr = columnDescriptions[c];
+ return descr;
+ }
+
+ /**
+ * Sets the accessible description for the column with the specified index.
+ *
+ * @param c the column number for which to set the description
+ * @param description the description to set
+ */
+ public void setAccessibleColumnDescription(int c, Accessible description)
+ {
+ if (columnDescriptions == null)
+ columnDescriptions = new Accessible[getAccessibleRowCount()];
+ columnDescriptions[c] = description;
+ }
+
+ /**
+ * Returns <code>true</code> if the accessible child at the specified
+ * row and column is selected, <code>false</code> otherwise.
+ *
+ * @param r the row number of the child
+ * @param c the column number of the child
+ *
+ * @return <code>true</code> if the accessible child at the specified
+ * row and column is selected, <code>false</code> otherwise
+ */
+ public boolean isAccessibleSelected(int r, int c)
+ {
+ return isCellSelected(r, c);
+ }
+
+ /**
+ * Returns <code>true</code> if the row with the specified index is
+ * selected, <code>false</code> otherwise.
+ *
+ * @param r the row number
+ *
+ * @return <code>true</code> if the row with the specified index is
+ * selected, <code>false</code> otherwise
+ */
+ public boolean isAccessibleRowSelected(int r)
+ {
+ return isRowSelected(r);
+ }
+
+ /**
+ * Returns <code>true</code> if the column with the specified index is
+ * selected, <code>false</code> otherwise.
+ *
+ * @param c the column number
+ *
+ * @return <code>true</code> if the column with the specified index is
+ * selected, <code>false</code> otherwise
+ */
+ public boolean isAccessibleColumnSelected(int c)
+ {
+ return isColumnSelected(c);
+ }
+
+ /**
+ * Returns the indices of all selected rows.
+ *
+ * @return the indices of all selected rows
+ */
+ public int[] getSelectedAccessibleRows()
+ {
+ return getSelectedRows();
+ }
+
+ /**
+ * Returns the indices of all selected columns.
+ *
+ * @return the indices of all selected columns
+ */
+ public int[] getSelectedAccessibleColumns()
+ {
+ return getSelectedColumns();
+ }
+
+ /**
+ * Returns the accessible row at the specified index.
+ *
+ * @param index the index for which to query the row
+ *
+ * @return the row number at the specified table index
+ */
+ public int getAccessibleRowAtIndex(int index)
+ {
+ // TODO: Back this up by a Mauve test and update API docs accordingly.
+ return index / getColumnCount();
+ }
+
+ /**
+ * Returns the accessible column at the specified index.
+ *
+ * @param index the index for which to query the column
+ *
+ * @return the column number at the specified table index
+ */
+ public int getAccessibleColumnAtIndex(int index)
+ {
+ // TODO: Back this up by a Mauve test and update API docs accordingly.
+ return index % getColumnCount();
+ }
+
+ /**
+ * Returns the accessible child index at the specified column and row.
+ *
+ * @param row the row
+ * @param column the column
+ *
+ * @return the index of the accessible child at the specified row and
+ * column
+ */
+ public int getAccessibleIndexAt(int row, int column)
+ {
+ // TODO: Back this up by a Mauve test and update API docs accordingly.
+ return row * getColumnCount() + column;
+ }
+ }
+ /**
+ * Handles property changes from the <code>TableColumn</code>s of this
+ * <code>JTable</code>.
+ *
+ * More specifically, this triggers a {@link #revalidate()} call if the
+ * preferredWidth of one of the observed columns changes.
+ */
+ class TableColumnPropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Receives notification that a property of the observed TableColumns has
+ * changed.
+ *
+ * @param ev the property change event
+ */
+ public void propertyChange(PropertyChangeEvent ev)
+ {
+ if (ev.getPropertyName().equals("preferredWidth"))
+ {
+ JTableHeader header = getTableHeader();
+ if (header != null)
+ // Do nothing if the table is in the resizing mode.
+ if (header.getResizingColumn() == null)
+ {
+ TableColumn col = (TableColumn) ev.getSource();
+ header.setResizingColumn(col);
+ doLayout();
+ header.setResizingColumn(null);
+ }
+ }
+ }
+ }
+
+ /**
+ * A cell renderer for boolean values.
+ */
+ private class BooleanCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * The CheckBox that is used for rendering.
+ */
+ private final JCheckBox checkBox;
+
+ /**
+ * Creates a new checkbox based boolean cell renderer. The checkbox is
+ * centered by default.
+ */
+ BooleanCellRenderer()
+ {
+ checkBox = new JCheckBox();
+ checkBox.setHorizontalAlignment(SwingConstants.CENTER);
+ }
+
+ /**
+ * Get the check box.
+ */
+ JCheckBox getCheckBox()
+ {
+ return checkBox;
+ }
+
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ if (isSelected)
+ {
+ checkBox.setBackground(table.getSelectionBackground());
+ checkBox.setForeground(table.getSelectionForeground());
+ }
+ else
+ {
+ checkBox.setBackground(table.getBackground());
+ checkBox.setForeground(table.getForeground());
+ }
+
+ if (hasFocus)
+ {
+ checkBox.setBorder(
+ UIManager.getBorder("Table.focusCellHighlightBorder"));
+ if (table.isCellEditable(row, column))
+ {
+ checkBox.setBackground(
+ UIManager.getColor("Table.focusCellBackground"));
+ checkBox.setForeground(
+ UIManager.getColor("Table.focusCellForeground"));
+ }
+ }
+ else
+ checkBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
+
+ // Null is rendered as false.
+ if (value == null)
+ checkBox.setSelected(false);
+ else
+ {
+ Boolean boolValue = (Boolean) value;
+ checkBox.setSelected(boolValue.booleanValue());
+ }
+ return checkBox;
+ }
+ }
+
+ /**
+ * A cell renderer for Date values.
+ */
+ private class DateCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
+ row, column);
+ if (value instanceof Date)
+ {
+ Date dateValue = (Date) value;
+ DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
+ setText(df.format(dateValue));
+ }
+ return this;
+ }
+ }
+
+ /**
+ * A cell renderer for Double values.
+ */
+ private class DoubleCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * Creates a new instance of NumberCellRenderer.
+ */
+ public DoubleCellRenderer()
+ {
+ setHorizontalAlignment(JLabel.RIGHT);
+ }
+
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
+ row, column);
+ if (value instanceof Double)
+ {
+ Double doubleValue = (Double) value;
+ NumberFormat nf = NumberFormat.getInstance();
+ setText(nf.format(doubleValue.doubleValue()));
+ }
+ return this;
+ }
+ }
+
+ /**
+ * A cell renderer for Float values.
+ */
+ private class FloatCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * Creates a new instance of NumberCellRenderer.
+ */
+ public FloatCellRenderer()
+ {
+ setHorizontalAlignment(JLabel.RIGHT);
+ }
+
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
+ row, column);
+ if (value instanceof Float)
+ {
+ Float floatValue = (Float) value;
+ NumberFormat nf = NumberFormat.getInstance();
+ setText(nf.format(floatValue.floatValue()));
+ }
+ return this;
+ }
+ }
+
+ /**
+ * A cell renderer for Number values.
+ */
+ private class NumberCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * Creates a new instance of NumberCellRenderer.
+ */
+ public NumberCellRenderer()
+ {
+ setHorizontalAlignment(JLabel.RIGHT);
+ }
+ }
+
+ /**
+ * A cell renderer for Icon values.
+ */
+ private class IconCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ IconCellRenderer()
+ {
+ setHorizontalAlignment(SwingConstants.CENTER);
+ }
+
+
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
+ row, column);
+ if (value instanceof Icon)
+ {
+ Icon iconValue = (Icon) value;
+ setIcon(iconValue);
+ }
+ else
+ {
+ setIcon(null);
+ }
+ setText("");
+ return this;
+ }
+ }
+
+ /**
+ * The JTable text component (used in editing) always has the table
+ * as its parent. The scrollRectToVisible must be adjusted taking the
+ * relative component position.
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+ private class TableTextField extends JTextField
+ {
+ /**
+ * Create the text field without the border.
+ */
+ TableTextField()
+ {
+ setBorder(BorderFactory.createLineBorder(getGridColor(), 2));
+ }
+ }
+
+
+ private static final long serialVersionUID = 3876025080382781659L;
+
+ /**
+ * This table, for referring identically name methods from inner classes.
+ */
+ final JTable this_table = this;
+
+
+ /**
+ * When resizing columns, do not automatically change any columns. In this
+ * case the table should be enclosed in a {@link JScrollPane} in order to
+ * accomodate cases in which the table size exceeds its visible area.
+ */
+ public static final int AUTO_RESIZE_OFF = 0;
+
+ /**
+ * When resizing column <code>i</code>, automatically change only the
+ * single column <code>i+1</code> to provide or absorb excess space
+ * requirements.
+ */
+ public static final int AUTO_RESIZE_NEXT_COLUMN = 1;
+
+ /**
+ * When resizing column <code>i</code> in a table of <code>n</code>
+ * columns, automatically change all columns in the range <code>[i+1,
+ * n)</code>, uniformly, to provide or absorb excess space requirements.
+ */
+ public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2;
+
+ /**
+ * When resizing column <code>i</code> in a table of <code>n</code>
+ * columns, automatically change all columns in the range <code>[0,
+ * n)</code> (with the exception of column i) uniformly, to provide or
+ * absorb excess space requirements.
+ */
+ public static final int AUTO_RESIZE_ALL_COLUMNS = 4;
+
+ /**
+ * When resizing column <code>i</code> in a table of <code>n</code>
+ * columns, automatically change column <code>n-1</code> (the last column
+ * in the table) to provide or absorb excess space requirements.
+ */
+ public static final int AUTO_RESIZE_LAST_COLUMN = 3;
+
+ /**
+ * A table mapping {@link java.lang.Class} objects to
+ * {@link TableCellEditor} objects. This table is consulted by the
+ * FIXME
+ */
+ protected Hashtable defaultEditorsByColumnClass = new Hashtable();
+
+ /**
+ * A table mapping {@link java.lang.Class} objects to
+ * {@link TableCellEditor} objects. This table is consulted by the
+ * FIXME
+ */
+ protected Hashtable defaultRenderersByColumnClass = new Hashtable();
+
+ /**
+ * The column that is edited, -1 if the table is not edited currently.
+ */
+ protected int editingColumn;
+
+ /**
+ * The row that is edited, -1 if the table is not edited currently.
+ */
+ protected int editingRow;
+
+ /**
+ * The component that is used for editing.
+ * <code>null</code> if the table is not editing currently.
+ *
+ */
+ protected transient Component editorComp;
+
+
+ /**
+ * Whether or not the table should automatically compute a matching
+ * {@link TableColumnModel} and assign it to the {@link #columnModel}
+ * property when the {@link #dataModel} property is changed.
+ *
+ * @see #setModel(TableModel)
+ * @see #createDefaultColumnsFromModel()
+ * @see #setColumnModel(TableColumnModel)
+ * @see #setAutoCreateColumnsFromModel(boolean)
+ * @see #getAutoCreateColumnsFromModel()
+ */
+ protected boolean autoCreateColumnsFromModel;
+
+ /**
+ * A numeric code specifying the resizing behavior of the table. Must be
+ * one of {@link #AUTO_RESIZE_ALL_COLUMNS} (the default), {@link
+ * #AUTO_RESIZE_LAST_COLUMN}, {@link #AUTO_RESIZE_NEXT_COLUMN}, {@link
+ * #AUTO_RESIZE_SUBSEQUENT_COLUMNS}, or {@link #AUTO_RESIZE_OFF}.
+ *
+ * @see #doLayout()
+ * @see #setAutoResizeMode(int)
+ * @see #getAutoResizeMode()
+ */
+ protected int autoResizeMode;
+
+ /**
+ * The height in pixels of any row of the table. All rows in a table are
+ * of uniform height. This differs from column width, which varies on a
+ * per-column basis, and is stored in the individual columns of the
+ * {@link #columnModel}.
+ *
+ * @see #getRowHeight()
+ * @see #setRowHeight(int)
+ * @see TableColumn#getWidth()
+ * @see TableColumn#setWidth(int)
+ */
+ protected int rowHeight;
+
+ /**
+ * The height in pixels of the gap left between any two rows of the table.
+ *
+ * @see #setRowMargin(int)
+ * @see #getRowHeight()
+ * @see #getIntercellSpacing()
+ * @see #setIntercellSpacing(Dimension)
+ * @see TableColumnModel#getColumnMargin()
+ * @see TableColumnModel#setColumnMargin(int)
+ */
+ protected int rowMargin;
+
+ /**
+ * Whether or not the table should allow row selection. If the table
+ * allows both row <em>and</em> column selection, it is said to allow
+ * "cell selection". Previous versions of the JDK supported cell
+ * selection as an independent concept, but it is now represented solely
+ * in terms of simultaneous row and column selection.
+ *
+ * @see TableColumnModel#getColumnSelectionAllowed()
+ * @see #setRowSelectionAllowed(boolean)
+ * @see #getRowSelectionAllowed()
+ * @see #getCellSelectionEnabled()
+ * @see #setCellSelectionEnabled(boolean)
+ */
+ protected boolean rowSelectionAllowed;
+
+ /**
+ * Obsolete. Use {@link #rowSelectionAllowed}, {@link
+ * #getColumnSelectionAllowed}, or the combined methods {@link
+ * #getCellSelectionEnabled} and {@link #setCellSelectionEnabled(boolean)}.
+ */
+ protected boolean cellSelectionEnabled;
+
+ /**
+ * The model for data stored in the table. Confusingly, the published API
+ * requires that this field be called <code>dataModel</code>, despite its
+ * property name. The table listens to its model as a {@link
+ * TableModelListener}.
+ *
+ * @see #tableChanged(TableModelEvent)
+ * @see TableModel#addTableModelListener(TableModelListener)
+ */
+ protected TableModel dataModel;
+
+ /**
+ * <p>A model of various aspects of the columns of the table, <em>not
+ * including</em> the data stored in them. The {@link TableColumnModel}
+ * is principally concerned with holding a set of {@link TableColumn}
+ * objects, each of which describes the display parameters of a column
+ * and the numeric index of the column from the data model which the
+ * column is presenting.</p>
+ *
+ * <p>The TableColumnModel also contains a {@link ListSelectionModel} which
+ * indicates which columns are currently selected. This selection model
+ * works in combination with the {@link #selectionModel} of the table
+ * itself to specify a <em>table selection</em>: a combination of row and
+ * column selections.</p>
+ *
+ * <p>Most application programmers do not need to work with this property
+ * at all: setting {@link #autoCreateColumnsFromModel} will construct the
+ * columnModel automatically, and the table acts as a facade for most of
+ * the interesting properties of the columnModel anyways.</p>
+ *
+ * @see #setColumnModel(TableColumnModel)
+ * @see #getColumnModel()
+ */
+ protected TableColumnModel columnModel;
+
+ /**
+ * A model of the rows of this table which are currently selected. This
+ * model is used in combination with the column selection model held as a
+ * member of the {@link #columnModel} property, to represent the rows and
+ * columns (or both: cells) of the table which are currently selected.
+ *
+ * @see #rowSelectionAllowed
+ * @see #setSelectionModel(ListSelectionModel)
+ * @see #getSelectionModel()
+ * @see TableColumnModel#getSelectionModel()
+ * @see ListSelectionModel#addListSelectionListener(ListSelectionListener)
+ */
+ protected ListSelectionModel selectionModel;
+
+ /**
+ * The current cell editor.
+ */
+ protected TableCellEditor cellEditor;
+
+ /**
+ * Whether or not drag-and-drop is enabled on this table.
+ *
+ * @see #setDragEnabled(boolean)
+ * @see #getDragEnabled()
+ */
+ private boolean dragEnabled;
+
+ /**
+ * The color to paint the grid lines of the table, when either {@link
+ * #showHorizontalLines} or {@link #showVerticalLines} is set.
+ *
+ * @see #setGridColor(Color)
+ * @see #getGridColor()
+ */
+ protected Color gridColor;
+
+ /**
+ * The size this table would prefer its viewport assume, if it is
+ * contained in a {@link JScrollPane}.
+ *
+ * @see #setPreferredScrollableViewportSize(Dimension)
+ * @see #getPreferredScrollableViewportSize()
+ */
+ protected Dimension preferredViewportSize;
+
+ /**
+ * The color to paint the background of selected cells. Fires a property
+ * change event with name {@link #SELECTION_BACKGROUND_CHANGED_PROPERTY}
+ * when its value changes.
+ *
+ * @see #setSelectionBackground(Color)
+ * @see #getSelectionBackground()
+ */
+ protected Color selectionBackground;
+
+ /**
+ * The name carried in property change events when the {@link
+ * #selectionBackground} property changes.
+ */
+ private static final String SELECTION_BACKGROUND_CHANGED_PROPERTY = "selectionBackground";
+
+ /**
+ * The color to paint the foreground of selected cells. Fires a property
+ * change event with name {@link #SELECTION_FOREGROUND_CHANGED_PROPERTY}
+ * when its value changes.
+ *
+ * @see #setSelectionForeground(Color)
+ * @see #getSelectionForeground()
+ */
+ protected Color selectionForeground;
+
+ /**
+ * The name carried in property change events when the
+ * {@link #selectionForeground} property changes.
+ */
+ private static final String SELECTION_FOREGROUND_CHANGED_PROPERTY = "selectionForeground";
+
+ /**
+ * The showHorizontalLines property.
+ */
+ protected boolean showHorizontalLines;
+
+ /**
+ * The showVerticalLines property.
+ */
+ protected boolean showVerticalLines;
+
+ /**
+ * The tableHeader property.
+ */
+ protected JTableHeader tableHeader;
+
+ /**
+ * The property handler for this table's columns.
+ */
+ TableColumnPropertyChangeHandler tableColumnPropertyChangeHandler =
+ new TableColumnPropertyChangeHandler();
+
+ /**
+ * Whether cell editors should receive keyboard focus when the table is
+ * activated.
+ */
+ private boolean surrendersFocusOnKeystroke = false;
+
+ /**
+ * A Rectangle object to be reused in {@link #getCellRect}.
+ */
+ private Rectangle rectCache = new Rectangle();
+
+ /**
+ * Indicates if the rowHeight property has been set by a client program or by
+ * the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientRowHeightSet = false;
+
+ /**
+ * Stores the sizes and positions of each row, when using non-uniform row
+ * heights. Initially the height of all rows is equal and stored in
+ * {link #rowHeight}. However, when an application calls
+ * {@link #setRowHeight(int,int)}, the table switches to non-uniform
+ * row height mode which stores the row heights in the SizeSequence
+ * object instead.
+ *
+ * @see #setRowHeight(int)
+ * @see #getRowHeight()
+ * @see #getRowHeight(int)
+ * @see #setRowHeight(int, int)
+ */
+ private SizeSequence rowHeights;
+
+ /**
+ * This editor serves just a marker that the value must be simply changed to
+ * the opposite one instead of starting the editing session.
+ */
+ private transient TableCellEditor booleanInvertingEditor;
+
+ /**
+ * Creates a new <code>JTable</code> instance.
+ */
+ public JTable ()
+ {
+ this(null, null, null);
+ }
+
+ /**
+ * Creates a new <code>JTable</code> instance with the given number
+ * of rows and columns.
+ *
+ * @param numRows an <code>int</code> value
+ * @param numColumns an <code>int</code> value
+ */
+ public JTable (int numRows, int numColumns)
+ {
+ this(new DefaultTableModel(numRows, numColumns));
+ }
+
+ /**
+ * Creates a new <code>JTable</code> instance, storing the given data
+ * array and heaving the given column names. To see the column names,
+ * you must place the JTable into the {@link JScrollPane}.
+ *
+ * @param data an <code>Object[][]</code> the table data
+ * @param columnNames an <code>Object[]</code> the column headers
+ */
+ public JTable(Object[][] data, Object[] columnNames)
+ {
+ this(new DefaultTableModel(data, columnNames));
+ }
+
+ /**
+ * Creates a new <code>JTable</code> instance, using the given data model
+ * object that provides information about the table content. The table model
+ * object is asked for the table size, other features and also receives
+ * notifications in the case when the table has been edited by the user.
+ *
+ * @param model
+ * the table model.
+ */
+ public JTable (TableModel model)
+ {
+ this(model, null, null);
+ }
+
+ /**
+ * Creates a new <code>JTable</code> instance, using the given model object
+ * that provides information about the table content. The table data model
+ * object is asked for the table size, other features and also receives
+ * notifications in the case when the table has been edited by the user. The
+ * table column model provides more detailed control on the table column
+ * related features.
+ *
+ * @param dm
+ * the table data mode
+ * @param cm
+ * the table column model
+ */
+ public JTable (TableModel dm, TableColumnModel cm)
+ {
+ this(dm, cm, null);
+ }
+
+ /**
+ * Creates a new <code>JTable</code> instance, providing data model,
+ * column model and list selection model. The list selection model
+ * manages the selections.
+ *
+ * @param dm data model (manages table data)
+ * @param cm column model (manages table columns)
+ * @param sm list selection model (manages table selections)
+ */
+ public JTable (TableModel dm, TableColumnModel cm, ListSelectionModel sm)
+ {
+ boolean autoCreate = false;
+ TableColumnModel columnModel;
+ if (cm != null)
+ columnModel = cm;
+ else
+ {
+ columnModel = createDefaultColumnModel();
+ autoCreate = true;
+ }
+
+ // Initialise the intercelar spacing before setting the column model to
+ // avoid firing unnecessary events.
+ // The initial incellar spacing is new Dimenstion(1,1).
+ rowMargin = 1;
+ columnModel.setColumnMargin(1);
+ setColumnModel(columnModel);
+
+ setSelectionModel(sm == null ? createDefaultSelectionModel() : sm);
+ setModel(dm == null ? createDefaultDataModel() : dm);
+ setAutoCreateColumnsFromModel(autoCreate);
+ initializeLocalVars();
+
+ // The following four lines properly set the lead selection indices.
+ // After this, the UI will handle the lead selection indices.
+ // FIXME: this should probably not be necessary, if the UI is installed
+ // before the TableModel is set then the UI will handle things on its
+ // own, but certain variables need to be set before the UI can be installed
+ // so we must get the correct order for all the method calls in this
+ // constructor.
+ // These four lines are not needed. A Mauve test that shows this is
+ // gnu.testlet.javax.swing.JTable.constructors(linesNotNeeded).
+ // selectionModel.setAnchorSelectionIndex(-1);
+ // selectionModel.setLeadSelectionIndex(-1);
+ // columnModel.getSelectionModel().setAnchorSelectionIndex(-1);
+ // columnModel.getSelectionModel().setLeadSelectionIndex(-1);
+ updateUI();
+ }
+
+ /**
+ * Creates a new <code>JTable</code> instance that uses data and column
+ * names, stored in {@link Vector}s.
+ *
+ * @param data the table data
+ * @param columnNames the table column names.
+ */
+ public JTable(Vector data, Vector columnNames)
+ {
+ this(new DefaultTableModel(data, columnNames));
+ }
+
+ /**
+ * Initialize local variables to default values.
+ */
+ protected void initializeLocalVars()
+ {
+ setTableHeader(createDefaultTableHeader());
+ if (autoCreateColumnsFromModel)
+ createDefaultColumnsFromModel();
+ this.columnModel.addColumnModelListener(this);
+
+ this.autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
+ setRowHeight(16);
+ this.rowMargin = 1;
+ this.rowSelectionAllowed = true;
+
+ // this.accessibleContext = new AccessibleJTable();
+ this.cellEditor = null;
+
+ // COMPAT: Both Sun and IBM have drag enabled
+ this.dragEnabled = false;
+ this.preferredViewportSize = new Dimension(450,400);
+ this.showHorizontalLines = true;
+ this.showVerticalLines = true;
+ this.editingColumn = -1;
+ this.editingRow = -1;
+ }
+
+ /**
+ * Add the new table column. The table column class allows to specify column
+ * features more precisely, setting the preferred width, column data type
+ * (column class) and table headers.
+ *
+ * There is no need the add columns to the table if the default column
+ * handling is sufficient.
+ *
+ * @param column
+ * the new column to add.
+ */
+ public void addColumn(TableColumn column)
+ {
+ if (column.getHeaderValue() == null)
+ {
+ String name = dataModel.getColumnName(column.getModelIndex());
+ column.setHeaderValue(name);
+ }
+
+ columnModel.addColumn(column);
+ column.addPropertyChangeListener(tableColumnPropertyChangeHandler);
+ }
+
+ /**
+ * Create the default editors for this table. The default method creates
+ * the editor for Booleans.
+ *
+ * Other fields are edited as strings at the moment.
+ */
+ protected void createDefaultEditors()
+ {
+ JCheckBox box = new BooleanCellRenderer().getCheckBox();
+ box.setBorder(BorderFactory.createLineBorder(getGridColor(), 2));
+ box.setBorderPainted(true);
+ booleanInvertingEditor = new DefaultCellEditor(box);
+ setDefaultEditor(Boolean.class, booleanInvertingEditor);
+ }
+
+ /**
+ * Create the default renderers for this table. The default method creates
+ * renderers for Boolean, Number, Double, Date, Icon and ImageIcon.
+ *
+ */
+ protected void createDefaultRenderers()
+ {
+ setDefaultRenderer(Boolean.class, new BooleanCellRenderer());
+ setDefaultRenderer(Number.class, new NumberCellRenderer());
+ setDefaultRenderer(Double.class, new DoubleCellRenderer());
+ setDefaultRenderer(Double.class, new FloatCellRenderer());
+ setDefaultRenderer(Date.class, new DateCellRenderer());
+ setDefaultRenderer(Icon.class, new IconCellRenderer());
+ setDefaultRenderer(ImageIcon.class, new IconCellRenderer());
+ }
+
+ /**
+ * @deprecated 1.0.2, replaced by <code>new JScrollPane(JTable)</code>
+ */
+ public static JScrollPane createScrollPaneForTable(JTable table)
+ {
+ return new JScrollPane(table);
+ }
+
+ /**
+ * Create the default table column model that is used if the user-defined
+ * column model is not provided. The default method creates
+ * {@link DefaultTableColumnModel}.
+ *
+ * @return the created table column model.
+ */
+ protected TableColumnModel createDefaultColumnModel()
+ {
+ return new DefaultTableColumnModel();
+ }
+
+ /**
+ * Create the default table data model that is used if the user-defined
+ * data model is not provided. The default method creates
+ * {@link DefaultTableModel}.
+ *
+ * @return the created table data model.
+ */
+ protected TableModel createDefaultDataModel()
+ {
+ return new DefaultTableModel();
+ }
+
+ /**
+ * Create the default table selection model that is used if the user-defined
+ * selection model is not provided. The default method creates
+ * {@link DefaultListSelectionModel}.
+ *
+ * @return the created table data model.
+ */
+ protected ListSelectionModel createDefaultSelectionModel()
+ {
+ return new DefaultListSelectionModel();
+ }
+
+ /**
+ * Create the default table header, if the user - defined table header is not
+ * provided.
+ *
+ * @return the default table header.
+ */
+ protected JTableHeader createDefaultTableHeader()
+ {
+ return new JTableHeader(columnModel);
+ }
+
+ /**
+ * Invoked when the column is added. Revalidates and repains the table.
+ */
+ public void columnAdded (TableColumnModelEvent event)
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Invoked when the column margin is changed.
+ * Revalidates and repains the table.
+ */
+ public void columnMarginChanged (ChangeEvent event)
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Invoked when the column is moved. Revalidates and repains the table.
+ */
+ public void columnMoved (TableColumnModelEvent event)
+ {
+ if (isEditing())
+ editingCanceled(null);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Invoked when the column is removed. Revalidates and repains the table.
+ */
+ public void columnRemoved (TableColumnModelEvent event)
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Invoked when the the column selection changes, repaints the changed
+ * columns. It is not recommended to override this method, register the
+ * listener instead.
+ */
+ public void columnSelectionChanged (ListSelectionEvent event)
+ {
+ // We must limit the indices to the bounds of the JTable's model, because
+ // we might get values of -1 or greater then columnCount in the case
+ // when columns get removed.
+ int idx0 = Math.max(0, Math.min(getColumnCount() - 1,
+ event.getFirstIndex()));
+ int idxn = Math.max(0, Math.min(getColumnCount() - 1,
+ event.getLastIndex()));
+
+ int minRow = 0;
+ int maxRow = getRowCount() - 1;
+ if (getRowSelectionAllowed())
+ {
+ minRow = selectionModel.getMinSelectionIndex();
+ maxRow = selectionModel.getMaxSelectionIndex();
+ int leadRow = selectionModel.getLeadSelectionIndex();
+ if (minRow == -1 && maxRow == -1)
+ {
+ minRow = leadRow;
+ maxRow = leadRow;
+ }
+ else
+ {
+ // In this case we need to repaint also the range to leadRow, not
+ // only between min and max.
+ if (leadRow != -1)
+ {
+ minRow = Math.min(minRow, leadRow);
+ maxRow = Math.max(maxRow, leadRow);
+ }
+ }
+ }
+ if (minRow != -1 && maxRow != -1)
+ {
+ Rectangle first = getCellRect(minRow, idx0, false);
+ Rectangle last = getCellRect(maxRow, idxn, false);
+ Rectangle dirty = SwingUtilities.computeUnion(first.x, first.y,
+ first.width,
+ first.height, last);
+ repaint(dirty);
+ }
+ }
+
+ /**
+ * Invoked when the editing is cancelled.
+ */
+ public void editingCanceled (ChangeEvent event)
+ {
+ if (editorComp!=null)
+ {
+ remove(editorComp);
+ repaint(editorComp.getBounds());
+ editorComp = null;
+ }
+ }
+
+ /**
+ * Finish the current editing session and update the table with the
+ * new value by calling {@link #setValueAt}.
+ *
+ * @param event the change event
+ */
+ public void editingStopped (ChangeEvent event)
+ {
+ if (editorComp!=null)
+ {
+ remove(editorComp);
+ setValueAt(cellEditor.getCellEditorValue(), editingRow, editingColumn);
+ repaint(editorComp.getBounds());
+ editorComp = null;
+ }
+ requestFocusInWindow();
+ }
+
+ /**
+ * Invoked when the table changes.
+ * <code>null</code> means everything changed.
+ */
+ public void tableChanged (TableModelEvent event)
+ {
+ // update the column model from the table model if the structure has
+ // changed and the flag autoCreateColumnsFromModel is set
+ if (event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW))
+ handleCompleteChange(event);
+ else if (event.getType() == TableModelEvent.INSERT)
+ handleInsert(event);
+ else if (event.getType() == TableModelEvent.DELETE)
+ handleDelete(event);
+ else
+ handleUpdate(event);
+ }
+
+ /**
+ * Handles a request for complete relayout. This is the case when
+ * event.getFirstRow() == TableModelEvent.HEADER_ROW.
+ *
+ * @param ev the table model event
+ */
+ private void handleCompleteChange(TableModelEvent ev)
+ {
+ clearSelection();
+ checkSelection();
+ rowHeights = null;
+ if (getAutoCreateColumnsFromModel())
+ createDefaultColumnsFromModel();
+ else
+ resizeAndRepaint();
+ }
+
+ /**
+ * Handles table model insertions.
+ *
+ * @param ev the table model event
+ */
+ private void handleInsert(TableModelEvent ev)
+ {
+ // Sync selection model with data model.
+ int first = ev.getFirstRow();
+ if (first < 0)
+ first = 0;
+ int last = ev.getLastRow();
+ if (last < 0)
+ last = getRowCount() - 1;
+ selectionModel.insertIndexInterval(first, last - first + 1, true);
+ checkSelection();
+
+ // For variable height rows we must update the SizeSequence thing.
+ if (rowHeights != null)
+ {
+ rowHeights.insertEntries(first, last - first + 1, rowHeight);
+ // TODO: We repaint the whole thing when the rows have variable
+ // heights. We might want to handle this better though.
+ repaint();
+ }
+ else
+ {
+ // Repaint the dirty region and revalidate.
+ int rowHeight = getRowHeight();
+ Rectangle dirty = new Rectangle(0, first * rowHeight,
+ getColumnModel().getTotalColumnWidth(),
+ (getRowCount() - first) * rowHeight);
+ repaint(dirty);
+ }
+ revalidate();
+ }
+
+ /**
+ * Handles table model deletions.
+ *
+ * @param ev the table model event
+ */
+ private void handleDelete(TableModelEvent ev)
+ {
+ // Sync selection model with data model.
+ int first = ev.getFirstRow();
+ if (first < 0)
+ first = 0;
+ int last = ev.getLastRow();
+ if (last < 0)
+ last = getRowCount() - 1;
+
+ selectionModel.removeIndexInterval(first, last);
+
+ checkSelection();
+
+ if (dataModel.getRowCount() == 0)
+ clearSelection();
+
+ // For variable height rows we must update the SizeSequence thing.
+ if (rowHeights != null)
+ {
+ rowHeights.removeEntries(first, last - first + 1);
+ // TODO: We repaint the whole thing when the rows have variable
+ // heights. We might want to handle this better though.
+ repaint();
+ }
+ else
+ {
+ // Repaint the dirty region and revalidate.
+ int rowHeight = getRowHeight();
+ int oldRowCount = getRowCount() + last - first + 1;
+ Rectangle dirty = new Rectangle(0, first * rowHeight,
+ getColumnModel().getTotalColumnWidth(),
+ (oldRowCount - first) * rowHeight);
+ repaint(dirty);
+ }
+ revalidate();
+ }
+
+ /**
+ * Handles table model updates without structural changes.
+ *
+ * @param ev the table model event
+ */
+ private void handleUpdate(TableModelEvent ev)
+ {
+ if (rowHeights == null)
+ {
+ // Some cells have been changed without changing the structure.
+ // Figure out the dirty rectangle and repaint.
+ int firstRow = ev.getFirstRow();
+ int lastRow = ev.getLastRow();
+ int col = ev.getColumn();
+ Rectangle dirty;
+ if (col == TableModelEvent.ALL_COLUMNS)
+ {
+ // All columns changed.
+ dirty = new Rectangle(0, firstRow * getRowHeight(),
+ getColumnModel().getTotalColumnWidth(), 0);
+ }
+ else
+ {
+ // Only one cell or column of cells changed.
+ // We need to convert to view column first.
+ int column = convertColumnIndexToModel(col);
+ dirty = getCellRect(firstRow, column, false);
+ }
+
+ // Now adjust the height of the dirty region.
+ dirty.height = (lastRow + 1) * getRowHeight();
+ // .. and repaint.
+ repaint(dirty);
+ }
+ else
+ {
+ // TODO: We repaint the whole thing when the rows have variable
+ // heights. We might want to handle this better though.
+ repaint();
+ }
+ }
+
+ /**
+ * Helper method for adjusting the lead and anchor indices when the
+ * table structure changed. This sets the lead and anchor to -1 if there's
+ * no more rows, or set them to 0 when they were at -1 and there are actually
+ * some rows now.
+ */
+ private void checkSelection()
+ {
+ TableModel m = getModel();
+ ListSelectionModel sm = selectionModel;
+ if (m != null)
+ {
+ int lead = sm.getLeadSelectionIndex();
+ int c = m.getRowCount();
+ if (c == 0 && lead != -1)
+ {
+ // No rows in the model, reset lead and anchor to -1.
+ sm.setValueIsAdjusting(true);
+ sm.setAnchorSelectionIndex(-1);
+ sm.setLeadSelectionIndex(-1);
+ sm.setValueIsAdjusting(false);
+ }
+ else if (c != 0 && lead == -1)
+ {
+ // We have rows, but no lead/anchor. Set them to 0. We
+ // do a little trick here so that the actual selection is not
+ // touched.
+ if (sm.isSelectedIndex(0))
+ sm.addSelectionInterval(0, 0);
+ else
+ sm.removeSelectionInterval(0, 0);
+ }
+ // Nothing to do in the other cases.
+ }
+ }
+
+ /**
+ * Invoked when another table row is selected. It is not recommended
+ * to override thid method, register the listener instead.
+ */
+ public void valueChanged (ListSelectionEvent event)
+ {
+ // If we are in the editing process, end the editing session.
+ if (isEditing())
+ editingStopped(null);
+
+ // Repaint the changed region.
+ int first = Math.max(0, Math.min(getRowCount() - 1, event.getFirstIndex()));
+ int last = Math.max(0, Math.min(getRowCount() - 1, event.getLastIndex()));
+ Rectangle rect1 = getCellRect(first, 0, false);
+ Rectangle rect2 = getCellRect(last, getColumnCount() - 1, false);
+ Rectangle dirty = SwingUtilities.computeUnion(rect2.x, rect2.y,
+ rect2.width, rect2.height,
+ rect1);
+ repaint(dirty);
+ }
+
+ /**
+ * Returns index of the column that contains specified point
+ * or -1 if this table doesn't contain this point.
+ *
+ * @param point point to identify the column
+ * @return index of the column that contains specified point or
+ * -1 if this table doesn't contain this point.
+ */
+ public int columnAtPoint(Point point)
+ {
+ int ncols = getColumnCount();
+ Dimension gap = getIntercellSpacing();
+ TableColumnModel cols = getColumnModel();
+ int x = point.x;
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ int width = cols.getColumn(i).getWidth()
+ + (gap == null ? 0 : gap.width);
+ if (0 <= x && x < width)
+ return i;
+ x -= width;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns index of the row that contains specified point or -1 if this table
+ * doesn't contain this point.
+ *
+ * @param point point to identify the row
+ * @return index of the row that contains specified point or -1 if this table
+ * doesn't contain this point.
+ */
+ public int rowAtPoint(Point point)
+ {
+ if (point != null)
+ {
+ int nrows = getRowCount();
+ int r;
+ int y = point.y;
+ if (rowHeights == null)
+ {
+ int height = getRowHeight();
+ r = y / height;
+ }
+ else
+ r = rowHeights.getIndex(y);
+
+ if (r < 0 || r >= nrows)
+ return -1;
+ else
+ return r;
+ }
+ else
+ return -1;
+ }
+
+ /**
+ * Calculate the visible rectangle for a particular row and column. The
+ * row and column are specified in visual terms; the column may not match
+ * the {@link #dataModel} column.
+ *
+ * @param row the visible row to get the cell rectangle of
+ *
+ * @param column the visible column to get the cell rectangle of, which may
+ * differ from the {@link #dataModel} column
+ *
+ * @param includeSpacing whether or not to include the cell margins in the
+ * resulting cell. If <code>false</code>, the result will only contain the
+ * inner area of the target cell, not including its margins.
+ *
+ * @return a rectangle enclosing the specified cell
+ */
+ public Rectangle getCellRect(int row,
+ int column,
+ boolean includeSpacing)
+ {
+ Rectangle cellRect = new Rectangle(0, 0, 0, 0);
+
+ // Check for valid range vertically.
+ if (row >= getRowCount())
+ {
+ cellRect.height = getHeight();
+ }
+ else if (row >= 0)
+ {
+ cellRect.height = getRowHeight(row);
+ if (rowHeights == null)
+ cellRect.y = row * cellRect.height;
+ else
+ cellRect.y = rowHeights.getPosition(row);
+
+ if (! includeSpacing)
+ {
+ // The rounding here is important.
+ int rMargin = getRowMargin();
+ cellRect.y += rMargin / 2;
+ cellRect.height -= rMargin;
+ }
+ }
+ // else row < 0, y = height = 0
+
+ // Check for valid range horizontally.
+ if (column < 0)
+ {
+ if (! getComponentOrientation().isLeftToRight())
+ {
+ cellRect.x = getWidth();
+ }
+ }
+ else if (column >= getColumnCount())
+ {
+ if (getComponentOrientation().isLeftToRight())
+ {
+ cellRect.x = getWidth();
+ }
+ }
+ else
+ {
+ TableColumnModel tcm = getColumnModel();
+ if (getComponentOrientation().isLeftToRight())
+ {
+ for (int i = 0; i < column; i++)
+ cellRect.x += tcm.getColumn(i).getWidth();
+ }
+ else
+ {
+ for (int i = tcm.getColumnCount() - 1; i > column; i--)
+ cellRect.x += tcm.getColumn(i).getWidth();
+ }
+ cellRect.width = tcm.getColumn(column).getWidth();
+ if (! includeSpacing)
+ {
+ // The rounding here is important.
+ int cMargin = tcm.getColumnMargin();
+ cellRect.x += cMargin / 2;
+ cellRect.width -= cMargin;
+ }
+ }
+
+ return cellRect;
+ }
+
+ public void clearSelection()
+ {
+ selectionModel.clearSelection();
+ getColumnModel().getSelectionModel().clearSelection();
+ }
+
+ /**
+ * Get the value of the selectedRow property by delegation to
+ * the {@link ListSelectionModel#getMinSelectionIndex} method of the
+ * {@link #selectionModel} field.
+ *
+ * @return The current value of the selectedRow property
+ */
+ public int getSelectedRow ()
+ {
+ return selectionModel.getMinSelectionIndex();
+ }
+
+ /**
+ * Get the value of the {@link #selectionModel} property.
+ *
+ * @return The current value of the property
+ */
+ public ListSelectionModel getSelectionModel()
+ {
+ //Neither Sun nor IBM returns null if rowSelection not allowed
+ return selectionModel;
+ }
+
+ public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
+ {
+ int block;
+ if (orientation == SwingConstants.HORIZONTAL)
+ {
+ block = visibleRect.width;
+ }
+ else
+ {
+ int rowHeight = getRowHeight();
+ if (rowHeight > 0)
+ block = Math.max(rowHeight, // Little hack for useful rounding.
+ (visibleRect.height / rowHeight) * rowHeight);
+ else
+ block = visibleRect.height;
+ }
+ return block;
+ }
+
+ /**
+ * Get the value of the <code>scrollableTracksViewportHeight</code> property.
+ *
+ * @return The constant value <code>false</code>
+ */
+ public boolean getScrollableTracksViewportHeight()
+ {
+ return false;
+ }
+
+ /**
+ * Get the value of the <code>scrollableTracksViewportWidth</code> property.
+ *
+ * @return <code>true</code> unless the {@link #autoResizeMode} property is
+ * <code>AUTO_RESIZE_OFF</code>
+ */
+ public boolean getScrollableTracksViewportWidth()
+ {
+ if (autoResizeMode == AUTO_RESIZE_OFF)
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * Return the preferred scrolling amount (in pixels) for the given scrolling
+ * direction and orientation. This method handles a partially exposed row by
+ * returning the distance required to completely expose the item. When
+ * scrolling the top item is completely exposed.
+ *
+ * @param visibleRect the currently visible part of the component.
+ * @param orientation the scrolling orientation
+ * @param direction the scrolling direction (negative - up, positive -down).
+ * The values greater than one means that more mouse wheel or similar
+ * events were generated, and hence it is better to scroll the longer
+ * distance.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
+ int direction)
+ {
+ int unit;
+ if (orientation == SwingConstants.HORIZONTAL)
+ unit = 100;
+ else
+ {
+ unit = getRowHeight();
+ // The following adjustment doesn't work for variable height rows.
+ // It fully exposes partially visible rows in the scrolling direction.
+ if (rowHeights == null)
+ {
+ if (direction > 0)
+ {
+ // Scroll down.
+ // How much pixles are exposed from the last item?
+ int exposed = (visibleRect.y + visibleRect.height) % unit;
+ if (exposed > 0 && exposed < unit - 1)
+ unit = unit - exposed - 1;
+ }
+ else
+ {
+ // Scroll up.
+ int exposed = visibleRect.y % unit;
+ if (exposed > 0 && exposed < unit)
+ unit = exposed;
+ }
+ }
+ }
+ return unit;
+ }
+
+ /**
+ * Get the cell editor, suitable for editing the given cell. The default
+ * method requests the editor from the column model. If the column model does
+ * not provide the editor, the call is forwarded to the
+ * {@link #getDefaultEditor(Class)} with the parameter, obtained from
+ * {@link TableModel#getColumnClass(int)}.
+ *
+ * @param row the cell row
+ * @param column the cell column
+ * @return the editor to edit that cell
+ */
+ public TableCellEditor getCellEditor(int row, int column)
+ {
+ TableCellEditor editor = columnModel.getColumn(column).getCellEditor();
+
+ if (editor == null)
+ {
+ int mcolumn = convertColumnIndexToModel(column);
+ editor = getDefaultEditor(dataModel.getColumnClass(mcolumn));
+ }
+
+ return editor;
+ }
+
+ /**
+ * Get the default editor for editing values of the given type
+ * (String, Boolean and so on).
+ *
+ * @param columnClass the class of the value that will be edited.
+ *
+ * @return the editor, suitable for editing this data type
+ */
+ public TableCellEditor getDefaultEditor(Class<?> columnClass)
+ {
+ if (defaultEditorsByColumnClass.containsKey(columnClass))
+ return (TableCellEditor) defaultEditorsByColumnClass.get(columnClass);
+ else
+ {
+ JTextField t = new TableTextField();
+ TableCellEditor r = new DefaultCellEditor(t);
+ defaultEditorsByColumnClass.put(columnClass, r);
+ return r;
+ }
+ }
+
+ /**
+ * Get the cell renderer for rendering the given cell.
+ *
+ * @param row the cell row
+ * @param column the cell column
+ * @return the cell renderer to render that cell.
+ */
+ public TableCellRenderer getCellRenderer(int row, int column)
+ {
+ TableCellRenderer renderer = null;
+ if (columnModel.getColumnCount() > 0)
+ renderer = columnModel.getColumn(column).getCellRenderer();
+ if (renderer == null)
+ {
+ int mcolumn = convertColumnIndexToModel(column);
+ renderer = getDefaultRenderer(dataModel.getColumnClass(mcolumn));
+ }
+ return renderer;
+ }
+
+ /**
+ * Set default renderer for rendering the given data type.
+ *
+ * @param columnClass the data type (String, Boolean and so on) that must be
+ * rendered.
+ * @param rend the renderer that will rend this data type
+ */
+ public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer rend)
+ {
+ defaultRenderersByColumnClass.put(columnClass, rend);
+ }
+
+ /**
+ * Get the default renderer for rendering the given data type.
+ *
+ * @param columnClass the data that must be rendered
+ *
+ * @return the appropriate defauld renderer for rendering that data type.
+ */
+ public TableCellRenderer getDefaultRenderer(Class<?> columnClass)
+ {
+ if (defaultRenderersByColumnClass.containsKey(columnClass))
+ return (TableCellRenderer) defaultRenderersByColumnClass.get(columnClass);
+ else
+ {
+ TableCellRenderer r = new DefaultTableCellRenderer();
+ defaultRenderersByColumnClass.put(columnClass, r);
+ return r;
+ }
+ }
+
+ /**
+ * Convert the table model index into the table column number.
+ * The model number need not match the real column position. The columns
+ * may be rearranged by the user with mouse at any time by dragging the
+ * column headers.
+ *
+ * @param vc the column number (0=first).
+ *
+ * @return the table column model index of this column.
+ *
+ * @see TableColumn#getModelIndex()
+ */
+ public int convertColumnIndexToModel(int vc)
+ {
+ if (vc < 0)
+ return vc;
+ else
+ return columnModel.getColumn(vc).getModelIndex();
+ }
+
+ /**
+ * Convert the table column number to the table column model index.
+ * The model number need not match the real column position. The columns
+ * may be rearranged by the user with mouse at any time by dragging the
+ * column headers.
+ *
+ * @param mc the table column index (0=first).
+ *
+ * @return the table column number in the model
+ *
+ * @see TableColumn#getModelIndex()
+ */
+ public int convertColumnIndexToView(int mc)
+ {
+ if (mc < 0)
+ return mc;
+ int ncols = getColumnCount();
+ for (int vc = 0; vc < ncols; ++vc)
+ {
+ if (columnModel.getColumn(vc).getModelIndex() == mc)
+ return vc;
+ }
+ return -1;
+ }
+
+ /**
+ * Prepare the renderer for rendering the given cell.
+ *
+ * @param renderer the renderer being prepared
+ * @param row the row of the cell being rendered
+ * @param column the column of the cell being rendered
+ *
+ * @return the component which .paint() method will paint the cell.
+ */
+ public Component prepareRenderer(TableCellRenderer renderer,
+ int row,
+ int column)
+ {
+ boolean rowSelAllowed = getRowSelectionAllowed();
+ boolean colSelAllowed = getColumnSelectionAllowed();
+ boolean isSel = false;
+ if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed)
+ isSel = isCellSelected(row, column);
+ else
+ isSel = isRowSelected(row) && getRowSelectionAllowed()
+ || isColumnSelected(column) && getColumnSelectionAllowed();
+
+ // Determine the focused cell. The focused cell is the cell at the
+ // leadSelectionIndices of the row and column selection model.
+ ListSelectionModel rowSel = getSelectionModel();
+ ListSelectionModel colSel = getColumnModel().getSelectionModel();
+ boolean hasFocus = hasFocus() && isEnabled()
+ && rowSel.getLeadSelectionIndex() == row
+ && colSel.getLeadSelectionIndex() == column;
+
+ return renderer.getTableCellRendererComponent(this,
+ dataModel.getValueAt(row,
+ convertColumnIndexToModel(column)),
+ isSel,
+ hasFocus,
+ row, column);
+ }
+
+
+ /**
+ * Get the value of the {@link #autoCreateColumnsFromModel} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getAutoCreateColumnsFromModel()
+ {
+ return autoCreateColumnsFromModel;
+ }
+
+ /**
+ * Get the value of the {@link #autoResizeMode} property.
+ *
+ * @return The current value of the property
+ */
+ public int getAutoResizeMode()
+ {
+ return autoResizeMode;
+ }
+
+ /**
+ * Get the value of the {@link #rowHeight} property.
+ *
+ * @return The current value of the property
+ */
+ public int getRowHeight()
+ {
+ return rowHeight;
+ }
+
+ /**
+ * Get the height of the specified row.
+ *
+ * @param row the row whose height to return
+ */
+ public int getRowHeight(int row)
+ {
+ int rh = rowHeight;
+ if (rowHeights != null)
+ rh = rowHeights.getSize(row);
+ return rh;
+ }
+
+
+ /**
+ * Get the value of the {@link #rowMargin} property.
+ *
+ * @return The current value of the property
+ */
+ public int getRowMargin()
+ {
+ return rowMargin;
+ }
+
+ /**
+ * Get the value of the {@link #rowSelectionAllowed} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setRowSelectionAllowed(boolean)
+ */
+ public boolean getRowSelectionAllowed()
+ {
+ return rowSelectionAllowed;
+ }
+
+ /**
+ * Get the value of the {@link #cellSelectionEnabled} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getCellSelectionEnabled()
+ {
+ return getColumnSelectionAllowed() && getRowSelectionAllowed();
+ }
+
+ /**
+ * Get the value of the {@link #dataModel} property.
+ *
+ * @return The current value of the property
+ */
+ public TableModel getModel()
+ {
+ return dataModel;
+ }
+
+ /**
+ * Get the value of the <code>columnCount</code> property by
+ * delegation to the {@link #columnModel} field.
+ *
+ * @return The current value of the columnCount property
+ */
+ public int getColumnCount()
+ {
+ return columnModel.getColumnCount();
+ }
+
+ /**
+ * Get the value of the <code>rowCount</code> property by
+ * delegation to the {@link #dataModel} field.
+ *
+ * @return The current value of the rowCount property
+ */
+ public int getRowCount()
+ {
+ return dataModel.getRowCount();
+ }
+
+ /**
+ * Get the value of the {@link #columnModel} property.
+ *
+ * @return The current value of the property
+ */
+ public TableColumnModel getColumnModel()
+ {
+ return columnModel;
+ }
+
+ /**
+ * Get the value of the <code>selectedColumn</code> property by
+ * delegation to the {@link #columnModel} field.
+ *
+ * @return The current value of the selectedColumn property
+ */
+ public int getSelectedColumn()
+ {
+ return columnModel.getSelectionModel().getMinSelectionIndex();
+ }
+
+ private static int countSelections(ListSelectionModel lsm)
+ {
+ int lo = lsm.getMinSelectionIndex();
+ int hi = lsm.getMaxSelectionIndex();
+ int sum = 0;
+ if (lo != -1 && hi != -1)
+ {
+ switch (lsm.getSelectionMode())
+ {
+ case ListSelectionModel.SINGLE_SELECTION:
+ sum = 1;
+ break;
+
+ case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
+ sum = hi - lo + 1;
+ break;
+
+ case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ if (lsm.isSelectedIndex(i))
+ ++sum;
+ break;
+ }
+ }
+ return sum;
+ }
+
+ private static int[] getSelections(ListSelectionModel lsm)
+ {
+ int sz = countSelections(lsm);
+ int [] ret = new int[sz];
+
+ int lo = lsm.getMinSelectionIndex();
+ int hi = lsm.getMaxSelectionIndex();
+ int j = 0;
+ if (lo != -1 && hi != -1)
+ {
+ switch (lsm.getSelectionMode())
+ {
+ case ListSelectionModel.SINGLE_SELECTION:
+ ret[0] = lo;
+ break;
+
+ case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ ret[j++] = i;
+ break;
+
+ case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ if (lsm.isSelectedIndex(i))
+ ret[j++] = i;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Get the value of the <code>selectedColumnCount</code> property by
+ * delegation to the {@link #columnModel} field.
+ *
+ * @return The current value of the selectedColumnCount property
+ */
+ public int getSelectedColumnCount()
+ {
+ return countSelections(columnModel.getSelectionModel());
+ }
+
+ /**
+ * Get the value of the <code>selectedColumns</code> property by
+ * delegation to the {@link #columnModel} field.
+ *
+ * @return The current value of the selectedColumns property
+ */
+ public int[] getSelectedColumns()
+ {
+ return getSelections(columnModel.getSelectionModel());
+ }
+
+ /**
+ * Get the value of the <code>columnSelectionAllowed</code> property.
+ *
+ * @return The current value of the columnSelectionAllowed property
+ *
+ * @see #setColumnSelectionAllowed(boolean)
+ */
+ public boolean getColumnSelectionAllowed()
+ {
+ return getColumnModel().getColumnSelectionAllowed();
+ }
+
+ /**
+ * Get the value of the <code>selectedRowCount</code> property by
+ * delegation to the {@link #selectionModel} field.
+ *
+ * @return The current value of the selectedRowCount property
+ */
+ public int getSelectedRowCount()
+ {
+ return countSelections(selectionModel);
+ }
+
+ /**
+ * Get the value of the <code>selectedRows</code> property by
+ * delegation to the {@link #selectionModel} field.
+ *
+ * @return The current value of the selectedRows property
+ */
+ public int[] getSelectedRows()
+ {
+ return getSelections(selectionModel);
+ }
+
+ /**
+ * Get the value of the {@link #accessibleContext} property.
+ *
+ * @return The current value of the property
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ {
+ AccessibleJTable ctx = new AccessibleJTable();
+ addPropertyChangeListener(ctx);
+ TableColumnModel tcm = getColumnModel();
+ tcm.addColumnModelListener(ctx);
+ tcm.getSelectionModel().addListSelectionListener(ctx);
+ getSelectionModel().addListSelectionListener(ctx);
+
+ accessibleContext = ctx;
+ }
+ return accessibleContext;
+ }
+
+ /**
+ * Get the value of the {@link #cellEditor} property.
+ *
+ * @return The current value of the property
+ */
+ public TableCellEditor getCellEditor()
+ {
+ return cellEditor;
+ }
+
+ /**
+ * Get the value of the {@link #dragEnabled} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * Get the value of the {@link #gridColor} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getGridColor()
+ {
+ return gridColor;
+ }
+
+ /**
+ * Get the value of the <code>intercellSpacing</code> property.
+ *
+ * @return The current value of the property
+ */
+ public Dimension getIntercellSpacing()
+ {
+ return new Dimension(columnModel.getColumnMargin(), rowMargin);
+ }
+
+ /**
+ * Get the value of the {@link #preferredViewportSize} property.
+ *
+ * @return The current value of the property
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ return preferredViewportSize;
+ }
+
+ /**
+ * Get the value of the {@link #selectionBackground} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getSelectionBackground()
+ {
+ return selectionBackground;
+ }
+
+ /**
+ * Get the value of the {@link #selectionForeground} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getSelectionForeground()
+ {
+ return selectionForeground;
+ }
+
+ /**
+ * Get the value of the {@link #showHorizontalLines} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getShowHorizontalLines()
+ {
+ return showHorizontalLines;
+ }
+
+ /**
+ * Get the value of the {@link #showVerticalLines} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getShowVerticalLines()
+ {
+ return showVerticalLines;
+ }
+
+ /**
+ * Get the value of the {@link #tableHeader} property.
+ *
+ * @return The current value of the property
+ */
+ public JTableHeader getTableHeader()
+ {
+ return tableHeader;
+ }
+
+ /**
+ * Removes specified column from displayable columns of this table.
+ *
+ * @param column column to removed
+ */
+ public void removeColumn(TableColumn column)
+ {
+ columnModel.removeColumn(column);
+ }
+
+ /**
+ * Moves column at the specified index to new given location.
+ *
+ * @param column index of the column to move
+ * @param targetColumn index specifying new location of the column
+ */
+ public void moveColumn(int column,int targetColumn)
+ {
+ columnModel.moveColumn(column, targetColumn);
+ }
+
+ /**
+ * Set the value of the {@link #autoCreateColumnsFromModel} flag. If the
+ * flag changes from <code>false</code> to <code>true</code>, the
+ * {@link #createDefaultColumnsFromModel()} method is called.
+ *
+ * @param autoCreate the new value of the flag.
+ */
+ public void setAutoCreateColumnsFromModel(boolean autoCreate)
+ {
+ if (autoCreateColumnsFromModel != autoCreate)
+ {
+ autoCreateColumnsFromModel = autoCreate;
+ if (autoCreate)
+ createDefaultColumnsFromModel();
+ }
+ }
+
+ /**
+ * Set the value of the {@link #autoResizeMode} property.
+ *
+ * @param a The new value of the autoResizeMode property
+ */
+ public void setAutoResizeMode(int a)
+ {
+ autoResizeMode = a;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Sets the height for all rows in the table. If you want to change the
+ * height of a single row instead, use {@link #setRowHeight(int, int)}.
+ *
+ * @param r the height to set for all rows
+ *
+ * @see #getRowHeight()
+ * @see #setRowHeight(int, int)
+ * @see #getRowHeight(int)
+ */
+ public void setRowHeight(int r)
+ {
+ if (r < 1)
+ throw new IllegalArgumentException();
+
+ clientRowHeightSet = true;
+
+ rowHeight = r;
+ rowHeights = null;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Sets the height of a single row in the table.
+ *
+ * @param rh the new row height
+ * @param row the row to change the height of
+ */
+ public void setRowHeight(int row, int rh)
+ {
+ if (rowHeights == null)
+ {
+ rowHeights = new SizeSequence(getRowCount(), rowHeight);
+ }
+ rowHeights.setSize(row, rh);
+ }
+
+ /**
+ * Set the value of the {@link #rowMargin} property.
+ *
+ * @param r The new value of the rowMargin property
+ */
+ public void setRowMargin(int r)
+ {
+ rowMargin = r;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #rowSelectionAllowed} property.
+ *
+ * @param r The new value of the rowSelectionAllowed property
+ *
+ * @see #getRowSelectionAllowed()
+ */
+ public void setRowSelectionAllowed(boolean r)
+ {
+ if (rowSelectionAllowed != r)
+ {
+ rowSelectionAllowed = r;
+ firePropertyChange("rowSelectionAllowed", !r, r);
+ repaint();
+ }
+ }
+
+ /**
+ * Set the value of the {@link #cellSelectionEnabled} property.
+ *
+ * @param c The new value of the cellSelectionEnabled property
+ */
+ public void setCellSelectionEnabled(boolean c)
+ {
+ setColumnSelectionAllowed(c);
+ setRowSelectionAllowed(c);
+ // for backward-compatibility sake:
+ cellSelectionEnabled = true;
+ }
+
+ /**
+ * <p>Set the value of the {@link #dataModel} property.</p>
+ *
+ * <p>Unregister <code>this</code> as a {@link TableModelListener} from
+ * previous {@link #dataModel} and register it with new parameter
+ * <code>m</code>.</p>
+ *
+ * @param m The new value of the model property
+ */
+ public void setModel(TableModel m)
+ {
+ // Throw exception is m is null.
+ if (m == null)
+ throw new IllegalArgumentException();
+
+ // Don't do anything if setting the current model again.
+ if (dataModel == m)
+ return;
+
+ TableModel oldModel = dataModel;
+
+ // Remove table as TableModelListener from old model.
+ if (dataModel != null)
+ dataModel.removeTableModelListener(this);
+
+ if (m != null)
+ {
+ // Set property.
+ dataModel = m;
+
+ // Add table as TableModelListener to new model.
+ dataModel.addTableModelListener(this);
+
+ // Notify the tableChanged method.
+ tableChanged(new TableModelEvent(dataModel,
+ TableModelEvent.HEADER_ROW));
+
+ // Automatically create columns.
+ if (autoCreateColumnsFromModel)
+ createDefaultColumnsFromModel();
+ }
+
+ // This property is bound, so we fire a property change event.
+ firePropertyChange("model", oldModel, dataModel);
+
+ // Repaint table.
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * <p>Set the value of the {@link #columnModel} property.</p>
+ *
+ * <p>Unregister <code>this</code> as a {@link TableColumnModelListener}
+ * from previous {@link #columnModel} and register it with new parameter
+ * <code>c</code>.</p>
+ *
+ * @param c The new value of the columnModel property
+ */
+ public void setColumnModel(TableColumnModel c)
+ {
+ if (c == null)
+ throw new IllegalArgumentException();
+ TableColumnModel tmp = columnModel;
+ if (tmp != null)
+ tmp.removeColumnModelListener(this);
+ if (c != null)
+ c.addColumnModelListener(this);
+ columnModel = c;
+ if (dataModel != null && columnModel != null)
+ {
+ int ncols = getColumnCount();
+ TableColumn column;
+ for (int i = 0; i < ncols; ++i)
+ {
+ column = columnModel.getColumn(i);
+ if (column.getHeaderValue()==null)
+ column.setHeaderValue(dataModel.getColumnName(i));
+ }
+ }
+
+ // according to Sun's spec we also have to set the tableHeader's
+ // column model here
+ if (tableHeader != null)
+ tableHeader.setColumnModel(c);
+
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the <code>columnSelectionAllowed</code> property.
+ *
+ * @param c The new value of the property
+ *
+ * @see #getColumnSelectionAllowed()
+ */
+ public void setColumnSelectionAllowed(boolean c)
+ {
+ if (columnModel.getColumnSelectionAllowed() != c)
+ {
+ columnModel.setColumnSelectionAllowed(c);
+ firePropertyChange("columnSelectionAllowed", !c, c);
+ repaint();
+ }
+ }
+
+ /**
+ * <p>Set the value of the {@link #selectionModel} property.</p>
+ *
+ * <p>Unregister <code>this</code> as a {@link ListSelectionListener}
+ * from previous {@link #selectionModel} and register it with new
+ * parameter <code>s</code>.</p>
+ *
+ * @param s The new value of the selectionModel property
+ */
+ public void setSelectionModel(ListSelectionModel s)
+ {
+ if (s == null)
+ throw new IllegalArgumentException();
+ ListSelectionModel tmp = selectionModel;
+ if (tmp != null)
+ tmp.removeListSelectionListener(this);
+ if (s != null)
+ s.addListSelectionListener(this);
+ selectionModel = s;
+ checkSelection();
+ }
+
+ /**
+ * Set the value of the <code>selectionMode</code> property by
+ * delegation to the {@link #selectionModel} field. The same selection
+ * mode is set for row and column selection models.
+ *
+ * @param s The new value of the property
+ */
+ public void setSelectionMode(int s)
+ {
+ selectionModel.setSelectionMode(s);
+ columnModel.getSelectionModel().setSelectionMode(s);
+
+ repaint();
+ }
+
+ /**
+ * <p>Set the value of the {@link #cellEditor} property.</p>
+ *
+ * <p>Unregister <code>this</code> as a {@link CellEditorListener} from
+ * previous {@link #cellEditor} and register it with new parameter
+ * <code>c</code>.</p>
+ *
+ * @param c The new value of the cellEditor property
+ */
+ public void setCellEditor(TableCellEditor c)
+ {
+ TableCellEditor tmp = cellEditor;
+ if (tmp != null)
+ tmp.removeCellEditorListener(this);
+ if (c != null)
+ c.addCellEditorListener(this);
+ cellEditor = c;
+ }
+
+ /**
+ * Set the value of the {@link #dragEnabled} property.
+ *
+ * @param d The new value of the dragEnabled property
+ */
+ public void setDragEnabled(boolean d)
+ {
+ dragEnabled = d;
+ }
+
+ /**
+ * Set the value of the {@link #gridColor} property.
+ *
+ * @param g The new value of the gridColor property
+ */
+ public void setGridColor(Color g)
+ {
+ gridColor = g;
+ repaint();
+ }
+
+ /**
+ * Set the value of the <code>intercellSpacing</code> property.
+ *
+ * @param i The new value of the intercellSpacing property
+ */
+ public void setIntercellSpacing(Dimension i)
+ {
+ rowMargin = i.height;
+ columnModel.setColumnMargin(i.width);
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #preferredViewportSize} property.
+ *
+ * @param p The new value of the preferredViewportSize property
+ */
+ public void setPreferredScrollableViewportSize(Dimension p)
+ {
+ preferredViewportSize = p;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * <p>Set the value of the {@link #selectionBackground} property.</p>
+ *
+ * <p>Fire a PropertyChangeEvent with name {@link
+ * #SELECTION_BACKGROUND_CHANGED_PROPERTY} to registered listeners, if
+ * selectionBackground changed.</p>
+ *
+ * @param s The new value of the selectionBackground property
+ */
+ public void setSelectionBackground(Color s)
+ {
+ Color tmp = selectionBackground;
+ selectionBackground = s;
+ if (((tmp == null && s != null)
+ || (s == null && tmp != null)
+ || (tmp != null && s != null && !tmp.equals(s))))
+ firePropertyChange(SELECTION_BACKGROUND_CHANGED_PROPERTY, tmp, s);
+ repaint();
+ }
+
+ /**
+ * <p>Set the value of the {@link #selectionForeground} property.</p>
+ *
+ * <p>Fire a PropertyChangeEvent with name {@link
+ * #SELECTION_FOREGROUND_CHANGED_PROPERTY} to registered listeners, if
+ * selectionForeground changed.</p>
+ *
+ * @param s The new value of the selectionForeground property
+ */
+ public void setSelectionForeground(Color s)
+ {
+ Color tmp = selectionForeground;
+ selectionForeground = s;
+ if (((tmp == null && s != null)
+ || (s == null && tmp != null)
+ || (tmp != null && s != null && !tmp.equals(s))))
+ firePropertyChange(SELECTION_FOREGROUND_CHANGED_PROPERTY, tmp, s);
+ repaint();
+ }
+
+ /**
+ * Set the value of the <code>showGrid</code> property.
+ *
+ * @param s The new value of the showGrid property
+ */
+ public void setShowGrid(boolean s)
+ {
+ setShowVerticalLines(s);
+ setShowHorizontalLines(s);
+ }
+
+ /**
+ * Set the value of the {@link #showHorizontalLines} property.
+ *
+ * @param s The new value of the showHorizontalLines property
+ */
+ public void setShowHorizontalLines(boolean s)
+ {
+ showHorizontalLines = s;
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #showVerticalLines} property.
+ *
+ * @param s The new value of the showVerticalLines property
+ */
+ public void setShowVerticalLines(boolean s)
+ {
+ showVerticalLines = s;
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #tableHeader} property.
+ *
+ * @param t The new value of the tableHeader property
+ */
+ public void setTableHeader(JTableHeader t)
+ {
+ if (tableHeader != null)
+ tableHeader.setTable(null);
+ tableHeader = t;
+ if (tableHeader != null)
+ tableHeader.setTable(this);
+ revalidate();
+ repaint();
+ }
+
+ protected void configureEnclosingScrollPane()
+ {
+ JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this);
+ if (jsp != null && tableHeader != null)
+ {
+ jsp.setColumnHeaderView(tableHeader);
+ }
+ }
+
+ protected void unconfigureEnclosingScrollPane()
+ {
+ JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this);
+ if (jsp != null)
+ {
+ jsp.setColumnHeaderView(null);
+ }
+ }
+
+
+ public void addNotify()
+ {
+ super.addNotify();
+ configureEnclosingScrollPane();
+ }
+
+ public void removeNotify()
+ {
+ super.addNotify();
+ unconfigureEnclosingScrollPane();
+ }
+
+
+ /**
+ * This distributes the superfluous width in a table evenly on its columns.
+ *
+ * The implementation used here is different to that one described in
+ * the JavaDocs. It is much simpler, and seems to work very well.
+ *
+ * TODO: correctly implement the algorithm described in the JavaDoc
+ */
+ private void distributeSpill(TableColumn[] cols, int spill)
+ {
+ int average = spill / cols.length;
+ for (int i = 0; i < cols.length; i++)
+ {
+ if (cols[i] != null)
+ cols[i].setWidth(cols[i].getPreferredWidth() + average);
+ }
+ }
+
+ /**
+ * This distributes the superfluous width in a table, setting the width of the
+ * column being resized strictly to its preferred width.
+ */
+ private void distributeSpillResizing(TableColumn[] cols, int spill,
+ TableColumn resizeIt)
+ {
+ int average = 0;
+ if (cols.length != 1)
+ average = spill / (cols.length-1);
+ for (int i = 0; i < cols.length; i++)
+ {
+ if (cols[i] != null && !cols[i].equals(resizeIt))
+ cols[i].setWidth(cols[i].getPreferredWidth() + average);
+ }
+ resizeIt.setWidth(resizeIt.getPreferredWidth());
+ }
+
+ /**
+ * Set the widths of all columns, taking they preferred widths into
+ * consideration. The excess space, if any, will be distrubuted between
+ * all columns. This method also handles special cases when one of the
+ * collumns is currently being resized.
+ *
+ * @see TableColumn#setPreferredWidth(int)
+ */
+ public void doLayout()
+ {
+ TableColumn resizingColumn = null;
+
+ int ncols = columnModel.getColumnCount();
+ if (ncols < 1)
+ return;
+
+ int prefSum = 0;
+ int rCol = -1;
+
+ if (tableHeader != null)
+ resizingColumn = tableHeader.getResizingColumn();
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ TableColumn col = columnModel.getColumn(i);
+ int p = col.getPreferredWidth();
+ prefSum += p;
+ if (resizingColumn == col)
+ rCol = i;
+ }
+
+ int spill = getWidth() - prefSum;
+
+ if (resizingColumn != null)
+ {
+ TableColumn col;
+ TableColumn [] cols;
+
+ switch (getAutoResizeMode())
+ {
+ case AUTO_RESIZE_LAST_COLUMN:
+ col = columnModel.getColumn(ncols-1);
+ col.setWidth(col.getPreferredWidth() + spill);
+ break;
+
+ case AUTO_RESIZE_NEXT_COLUMN:
+ col = columnModel.getColumn(ncols-1);
+ col.setWidth(col.getPreferredWidth() + spill);
+ break;
+
+ case AUTO_RESIZE_ALL_COLUMNS:
+ cols = new TableColumn[ncols];
+ for (int i = 0; i < ncols; ++i)
+ cols[i] = columnModel.getColumn(i);
+ distributeSpillResizing(cols, spill, resizingColumn);
+ break;
+
+ case AUTO_RESIZE_SUBSEQUENT_COLUMNS:
+
+ // Subtract the width of the non-resized columns from the spill.
+ int w = 0;
+ int wp = 0;
+ TableColumn column;
+ for (int i = 0; i < rCol; i++)
+ {
+ column = columnModel.getColumn(i);
+ w += column.getWidth();
+ wp+= column.getPreferredWidth();
+ }
+
+ // The number of columns right from the column being resized.
+ int n = ncols-rCol-1;
+ if (n>0)
+ {
+ // If there are any columns on the right sied to resize.
+ spill = (getWidth()-w) - (prefSum-wp);
+ int average = spill / n;
+
+ // For all columns right from the column being resized:
+ for (int i = rCol+1; i < ncols; i++)
+ {
+ column = columnModel.getColumn(i);
+ column.setWidth(column.getPreferredWidth() + average);
+ }
+ }
+ resizingColumn.setWidth(resizingColumn.getPreferredWidth());
+ break;
+
+ case AUTO_RESIZE_OFF:
+ default:
+ int prefWidth = resizingColumn.getPreferredWidth();
+ resizingColumn.setWidth(prefWidth);
+ }
+ }
+ else
+ {
+ TableColumn[] cols = new TableColumn[ncols];
+
+ for (int i = 0; i < ncols; ++i)
+ cols[i] = columnModel.getColumn(i);
+
+ distributeSpill(cols, spill);
+ }
+
+ if (editorComp!=null)
+ moveToCellBeingEdited(editorComp);
+
+ int leftBoundary = getLeftResizingBoundary();
+ int width = getWidth() - leftBoundary;
+ repaint(leftBoundary, 0, width, getHeight());
+ if (tableHeader != null)
+ tableHeader.repaint(leftBoundary, 0, width, tableHeader.getHeight());
+ }
+
+ /**
+ * Get the left boundary of the rectangle which changes during the column
+ * resizing.
+ */
+ int getLeftResizingBoundary()
+ {
+ if (tableHeader == null || getAutoResizeMode() == AUTO_RESIZE_ALL_COLUMNS)
+ return 0;
+ else
+ {
+ TableColumn resizingColumn = tableHeader.getResizingColumn();
+ if (resizingColumn == null)
+ return 0;
+
+ int rc = convertColumnIndexToView(resizingColumn.getModelIndex());
+ int p = 0;
+
+ for (int i = 0; i < rc; i++)
+ p += columnModel.getColumn(i).getWidth();
+
+ return p;
+ }
+ }
+
+
+ /**
+ * @deprecated Replaced by <code>doLayout()</code>
+ */
+ public void sizeColumnsToFit(boolean lastColumnOnly)
+ {
+ doLayout();
+ }
+
+ /**
+ * Obsolete since JDK 1.4. Please use <code>doLayout()</code>.
+ */
+ public void sizeColumnsToFit(int resizingColumn)
+ {
+ doLayout();
+ }
+
+ public String getUIClassID()
+ {
+ return "TableUI";
+ }
+
+ /**
+ * This method returns the table's UI delegate.
+ *
+ * @return The table's UI delegate.
+ */
+ public TableUI getUI()
+ {
+ return (TableUI) ui;
+ }
+
+ /**
+ * This method sets the table's UI delegate.
+ *
+ * @param ui The table's UI delegate.
+ */
+ public void setUI(TableUI ui)
+ {
+ super.setUI(ui);
+ // The editors and renderers must be recreated because they constructors
+ // may use the look and feel properties.
+ createDefaultEditors();
+ createDefaultRenderers();
+ }
+
+ public void updateUI()
+ {
+ setUI((TableUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Get the class (datatype) of the column. The cells are rendered and edited
+ * differently, depending from they data type.
+ *
+ * @param column the column (not the model index).
+ *
+ * @return the class, defining data type of that column (String.class for
+ * String, Boolean.class for boolean and so on).
+ */
+ public Class<?> getColumnClass(int column)
+ {
+ return getModel().getColumnClass(convertColumnIndexToModel(column));
+ }
+
+ /**
+ * Get the name of the column. If the column has the column identifier set,
+ * the return value is the result of the .toString() method call on that
+ * identifier. If the identifier is not explicitly set, the returned value
+ * is calculated by
+ * {@link javax.swing.table.AbstractTableModel#getColumnName(int)}.
+ *
+ * @param column the column
+ *
+ * @return the name of that column.
+ */
+ public String getColumnName(int column)
+ {
+ int modelColumn = columnModel.getColumn(column).getModelIndex();
+ return dataModel.getColumnName(modelColumn);
+ }
+
+ /**
+ * Get the column, currently being edited
+ *
+ * @return the column, currently being edited.
+ */
+ public int getEditingColumn()
+ {
+ return editingColumn;
+ }
+
+ /**
+ * Set the column, currently being edited
+ *
+ * @param column the column, currently being edited.
+ */
+ public void setEditingColumn(int column)
+ {
+ editingColumn = column;
+ }
+
+ /**
+ * Get the row currently being edited.
+ *
+ * @return the row, currently being edited.
+ */
+ public int getEditingRow()
+ {
+ return editingRow;
+ }
+
+ /**
+ * Set the row currently being edited.
+ *
+ * @param row the row, that will be edited
+ */
+ public void setEditingRow(int row)
+ {
+ editingRow = row;
+ }
+
+ /**
+ * Get the editor component that is currently editing one of the cells
+ *
+ * @return the editor component or null, if none of the cells is being
+ * edited.
+ */
+ public Component getEditorComponent()
+ {
+ return editorComp;
+ }
+
+ /**
+ * Check if one of the table cells is currently being edited.
+ *
+ * @return true if there is a cell being edited.
+ */
+ public boolean isEditing()
+ {
+ return editorComp != null;
+ }
+
+ /**
+ * Set the default editor for the given column class (column data type).
+ * By default, String is handled by text field and Boolean is handled by
+ * the check box.
+ *
+ * @param columnClass the column data type
+ * @param editor the editor that will edit this data type
+ *
+ * @see TableModel#getColumnClass(int)
+ */
+ public void setDefaultEditor(Class<?> columnClass, TableCellEditor editor)
+ {
+ if (editor != null)
+ defaultEditorsByColumnClass.put(columnClass, editor);
+ else
+ defaultEditorsByColumnClass.remove(columnClass);
+ }
+
+ public void addColumnSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getColumnCount()-1)
+ || index1 < 0 || index1 > (getColumnCount()-1)))
+ throw new IllegalArgumentException("Column index out of range.");
+
+ getColumnModel().getSelectionModel().addSelectionInterval(index0, index1);
+ }
+
+ public void addRowSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getRowCount()-1)
+ || index1 < 0 || index1 > (getRowCount()-1)))
+ throw new IllegalArgumentException("Row index out of range.");
+
+ getSelectionModel().addSelectionInterval(index0, index1);
+ }
+
+ public void setColumnSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getColumnCount()-1)
+ || index1 < 0 || index1 > (getColumnCount()-1)))
+ throw new IllegalArgumentException("Column index out of range.");
+
+ getColumnModel().getSelectionModel().setSelectionInterval(index0, index1);
+ }
+
+ public void setRowSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getRowCount()-1)
+ || index1 < 0 || index1 > (getRowCount()-1)))
+ throw new IllegalArgumentException("Row index out of range.");
+
+ getSelectionModel().setSelectionInterval(index0, index1);
+ }
+
+ public void removeColumnSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getColumnCount()-1)
+ || index1 < 0 || index1 > (getColumnCount()-1)))
+ throw new IllegalArgumentException("Column index out of range.");
+
+ getColumnModel().getSelectionModel().removeSelectionInterval(index0, index1);
+ }
+
+ public void removeRowSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getRowCount()-1)
+ || index1 < 0 || index1 > (getRowCount()-1)))
+ throw new IllegalArgumentException("Row index out of range.");
+
+ getSelectionModel().removeSelectionInterval(index0, index1);
+ }
+
+ /**
+ * Checks if the given column is selected.
+ *
+ * @param column the column
+ *
+ * @return true if the column is selected (as reported by the selection
+ * model, associated with the column model), false otherwise.
+ */
+ public boolean isColumnSelected(int column)
+ {
+ return getColumnModel().getSelectionModel().isSelectedIndex(column);
+ }
+
+ /**
+ * Checks if the given row is selected.
+ *
+ * @param row the row
+ *
+ * @return true if the row is selected (as reported by the selection model),
+ * false otherwise.
+ */
+ public boolean isRowSelected(int row)
+ {
+ return getSelectionModel().isSelectedIndex(row);
+ }
+
+ /**
+ * Checks if the given cell is selected. The cell is selected if both
+ * the cell row and the cell column are selected.
+ *
+ * @param row the cell row
+ * @param column the cell column
+ *
+ * @return true if the cell is selected, false otherwise
+ */
+ public boolean isCellSelected(int row, int column)
+ {
+ return isRowSelected(row) && isColumnSelected(column);
+ }
+
+ /**
+ * Select all table.
+ */
+ public void selectAll()
+ {
+ // The table is empty - nothing to do!
+ if (getRowCount() == 0 || getColumnCount() == 0)
+ return;
+
+ // rowLead and colLead store the current lead selection indices
+ int rowLead = selectionModel.getLeadSelectionIndex();
+ int colLead = getColumnModel().getSelectionModel().getLeadSelectionIndex();
+ // the following calls to setSelectionInterval change the lead selection
+ // indices
+ setColumnSelectionInterval(0, getColumnCount() - 1);
+ setRowSelectionInterval(0, getRowCount() - 1);
+ // the following addSelectionInterval calls restore the lead selection
+ // indices to their previous values
+ addColumnSelectionInterval(colLead,colLead);
+ addRowSelectionInterval(rowLead, rowLead);
+ }
+
+ /**
+ * Get the cell value at the given position.
+ *
+ * @param row the row to get the value
+ * @param column the actual column number (not the model index)
+ * to get the value.
+ *
+ * @return the cell value, as returned by model.
+ */
+ public Object getValueAt(int row, int column)
+ {
+ return dataModel.getValueAt(row, convertColumnIndexToModel(column));
+ }
+
+ /**
+ * Set value for the cell at the given position. The modified cell is
+ * repainted.
+ *
+ * @param value the value to set
+ * @param row the row of the cell being modified
+ * @param column the column of the cell being modified
+ */
+ public void setValueAt(Object value, int row, int column)
+ {
+ dataModel.setValueAt(value, row, convertColumnIndexToModel(column));
+
+ repaint(getCellRect(row, column, true));
+ }
+
+ /**
+ * Get table column with the given identified.
+ *
+ * @param identifier the column identifier
+ *
+ * @return the table column with this identifier
+ *
+ * @throws IllegalArgumentException if <code>identifier</code> is
+ * <code>null</code> or there is no column with that identifier.
+ *
+ * @see TableColumn#setIdentifier(Object)
+ */
+ public TableColumn getColumn(Object identifier)
+ {
+ return columnModel.getColumn(columnModel.getColumnIndex(identifier));
+ }
+
+ /**
+ * Returns <code>true</code> if the specified cell is editable, and
+ * <code>false</code> otherwise.
+ *
+ * @param row the row index.
+ * @param column the column index.
+ *
+ * @return true if the cell is editable, false otherwise.
+ */
+ public boolean isCellEditable(int row, int column)
+ {
+ return dataModel.isCellEditable(row, convertColumnIndexToModel(column));
+ }
+
+ /**
+ * Clears any existing columns from the <code>JTable</code>'s
+ * {@link TableColumnModel} and creates new columns to match the values in
+ * the data ({@link TableModel}) used by the table.
+ *
+ * @see #setAutoCreateColumnsFromModel(boolean)
+ */
+ public void createDefaultColumnsFromModel()
+ {
+ assert columnModel != null : "The columnModel must not be null.";
+
+ // remove existing columns
+ int columnIndex = columnModel.getColumnCount() - 1;
+ while (columnIndex >= 0)
+ {
+ columnModel.removeColumn(columnModel.getColumn(columnIndex));
+ columnIndex--;
+ }
+
+ // add new columns to match the TableModel
+ int columnCount = dataModel.getColumnCount();
+ for (int c = 0; c < columnCount; c++)
+ {
+ TableColumn column = new TableColumn(c);
+ column.setIdentifier(dataModel.getColumnName(c));
+ column.setHeaderValue(dataModel.getColumnName(c));
+ columnModel.addColumn(column);
+ column.addPropertyChangeListener(tableColumnPropertyChangeHandler);
+ }
+ }
+
+ public void changeSelection (int rowIndex, int columnIndex, boolean toggle, boolean extend)
+ {
+ if (toggle && extend)
+ {
+ // Leave the selection state as is, but move the anchor
+ // index to the specified location
+ selectionModel.setAnchorSelectionIndex(rowIndex);
+ getColumnModel().getSelectionModel().setAnchorSelectionIndex(columnIndex);
+ }
+ else if (toggle)
+ {
+ // Toggle the state of the specified cell
+ if (isCellSelected(rowIndex,columnIndex))
+ {
+ selectionModel.removeSelectionInterval(rowIndex,rowIndex);
+ getColumnModel().getSelectionModel().removeSelectionInterval(columnIndex,columnIndex);
+ }
+ else
+ {
+ selectionModel.addSelectionInterval(rowIndex,rowIndex);
+ getColumnModel().getSelectionModel().addSelectionInterval(columnIndex,columnIndex);
+ }
+ }
+ else if (extend)
+ {
+ // Extend the previous selection from the anchor to the
+ // specified cell, clearing all other selections
+ selectionModel.setLeadSelectionIndex(rowIndex);
+ getColumnModel().getSelectionModel().setLeadSelectionIndex(columnIndex);
+ }
+ else
+ {
+ // Clear the previous selection and ensure the new cell
+ // is selected
+ selectionModel.clearSelection();
+ selectionModel.setSelectionInterval(rowIndex,rowIndex);
+ getColumnModel().getSelectionModel().clearSelection();
+ getColumnModel().getSelectionModel().setSelectionInterval(columnIndex, columnIndex);
+
+
+ }
+ }
+
+ /**
+ * Programmatically starts editing the specified cell.
+ *
+ * @param row the row of the cell to edit.
+ * @param column the column of the cell to edit.
+ */
+ public boolean editCellAt(int row, int column)
+ {
+ // Complete the previous editing session, if still active.
+ if (isEditing())
+ editingStopped(new ChangeEvent("editingStopped"));
+
+ TableCellEditor editor = getCellEditor(row, column);
+
+ // The boolean values are inverted by the single click without the
+ // real editing session.
+ if (editor == booleanInvertingEditor && isCellEditable(row, column))
+ {
+ if (Boolean.TRUE.equals(getValueAt(row, column)))
+ setValueAt(Boolean.FALSE, row, column);
+ else
+ setValueAt(Boolean.TRUE, row, column);
+ return false;
+ }
+ else
+ {
+ editingRow = row;
+ editingColumn = column;
+
+ setCellEditor(editor);
+ editorComp = prepareEditor(cellEditor, row, column);
+
+ // Remove the previous editor components, if present. Only one
+ // editor component at time is allowed in the table.
+ removeAll();
+ add(editorComp);
+ moveToCellBeingEdited(editorComp);
+ scrollRectToVisible(editorComp.getBounds());
+ editorComp.requestFocusInWindow();
+
+ // Deliver the should select event.
+ return editor.shouldSelectCell(null);
+ }
+ }
+
+ /**
+ * Move the given component under the cell being edited.
+ * The table must be in the editing mode.
+ *
+ * @param component the component to move.
+ */
+ private void moveToCellBeingEdited(Component component)
+ {
+ Rectangle r = getCellRect(editingRow, editingColumn, true);
+ // Adjust bounding box of the editing component, so that it lies
+ // 'above' the grid on all edges, not only right and bottom.
+ // The table grid is painted only at the right and bottom edge of a cell.
+ r.x -= 1;
+ r.y -= 1;
+ r.width += 1;
+ r.height += 1;
+ component.setBounds(r);
+ }
+
+ /**
+ * Programmatically starts editing the specified cell.
+ *
+ * @param row the row of the cell to edit.
+ * @param column the column of the cell to edit.
+ */
+ public boolean editCellAt (int row, int column, EventObject e)
+ {
+ return editCellAt(row, column);
+ }
+
+ /**
+ * Discards the editor object.
+ */
+ public void removeEditor()
+ {
+ editingStopped(new ChangeEvent(this));
+ }
+
+ /**
+ * Prepares the editor by querying for the value and selection state of the
+ * cell at (row, column).
+ *
+ * @param editor the TableCellEditor to set up
+ * @param row the row of the cell to edit
+ * @param column the column of the cell to edit
+ * @return the Component being edited
+ */
+ public Component prepareEditor (TableCellEditor editor, int row, int column)
+ {
+ return editor.getTableCellEditorComponent
+ (this, getValueAt(row, column), isCellSelected(row, column), row, column);
+ }
+
+ /**
+ * This revalidates the <code>JTable</code> and queues a repaint.
+ */
+ protected void resizeAndRepaint()
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Sets whether cell editors of this table should receive keyboard focus
+ * when the editor is activated by a keystroke. The default setting is
+ * <code>false</code> which means that the table should keep the keyboard
+ * focus until the cell is selected by a mouse click.
+ *
+ * @param value the value to set
+ *
+ * @since 1.4
+ */
+ public void setSurrendersFocusOnKeystroke(boolean value)
+ {
+ // TODO: Implement functionality of this property (in UI impl).
+ surrendersFocusOnKeystroke = value;
+ }
+
+ /**
+ * Returns whether cell editors of this table should receive keyboard focus
+ * when the editor is activated by a keystroke. The default setting is
+ * <code>false</code> which means that the table should keep the keyboard
+ * focus until the cell is selected by a mouse click.
+ *
+ * @return whether cell editors of this table should receive keyboard focus
+ * when the editor is activated by a keystroke
+ *
+ * @since 1.4
+ */
+ public boolean getSurrendersFocusOnKeystroke()
+ {
+ // TODO: Implement functionality of this property (in UI impl).
+ return surrendersFocusOnKeystroke;
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if <code>c</code> or
+ * <code>propertyValue</code> is <code>null</code>
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("rowHeight"))
+ {
+ if (! clientRowHeightSet)
+ {
+ setRowHeight(((Integer) value).intValue());
+ clientRowHeightSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTextArea.java b/libjava/classpath/javax/swing/JTextArea.java
new file mode 100644
index 000000000..51921cc89
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTextArea.java
@@ -0,0 +1,607 @@
+/* JTextArea.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Dimension;
+import java.awt.FontMetrics;
+import java.awt.Rectangle;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.PlainDocument;
+import javax.swing.text.View;
+
+/**
+ * The <code>JTextArea</code> component provides a multi-line area for displaying
+ * and editing plain text. The component is designed to act as a lightweight
+ * replacement for the heavyweight <code>java.awt.TextArea</code> component,
+ * which provides similar functionality using native widgets.
+ * <p>
+ *
+ * This component has additional functionality to the AWT class. It follows
+ * the same design pattern as seen in other text components, such as
+ * <code>JTextField</code>, <code>JTextPane</code> and <code>JEditorPane</code>,
+ * and embodied in <code>JTextComponent</code>. These classes separate the text
+ * (the model) from its appearance within the onscreen component (the view). The
+ * text is held within a <code>javax.swing.text.Document</code> object, which can
+ * also maintain relevant style information where necessary. As a result, it is the
+ * document that should be monitored for textual changes, via
+ * <code>DocumentEvent</code>s delivered to registered
+ * <code>DocumentListener</code>s, rather than this component.
+ * <p>
+ *
+ * Unlike <code>java.awt.TextArea</code>, <code>JTextArea</code> does not
+ * handle scrolling. Instead, this functionality is delegated to a
+ * <code>JScrollPane</code>, which can contain the text area and handle
+ * scrolling when required. Likewise, the word wrapping functionality
+ * of the AWT component is converted to a property of this component
+ * and the <code>rows</code> and <code>columns</code> properties
+ * are used in calculating the preferred size of the scroll pane's
+ * view port.
+ *
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see java.awt.TextArea
+ * @see javax.swing.text.JTextComponent
+ * @see javax.swing.JTextField
+ * @see javax.swing.JTextPane
+ * @see javax.swing.JEditorPane
+ * @see javax.swing.text.Document
+ * @see javax.swing.event.DocumentEvent
+ * @see javax.swing.event.DocumentListener
+ */
+
+public class JTextArea extends JTextComponent
+{
+ /**
+ * Provides accessibility support for <code>JTextArea</code>.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJTextArea extends AccessibleJTextComponent
+ {
+
+ /**
+ * Creates a new <code>AccessibleJTextArea</code> object.
+ */
+ protected AccessibleJTextArea()
+ {
+ super();
+ }
+
+ /**
+ * Returns the accessible state of this <code>AccessibleJTextArea</code>.
+ *
+ * @return the accessible state of this <code>AccessibleJTextArea</code>
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ // TODO: Figure out what state must be added here to the super's state.
+ return state;
+ }
+ }
+
+ /**
+ * Compatible with Sun's JDK
+ */
+ private static final long serialVersionUID = -6141680179310439825L;
+
+ /**
+ * The number of rows used by the component.
+ */
+ private int rows;
+
+ /**
+ * The number of columns used by the component.
+ */
+ private int columns;
+
+ /**
+ * Whether line wrapping is enabled or not.
+ */
+ 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.
+ */
+ public JTextArea()
+ {
+ this(null, null, 0, 0);
+ }
+
+ /**
+ * Creates a new <code>JTextArea</code> object.
+ *
+ * @param text the initial text
+ */
+ public JTextArea(String text)
+ {
+ this(null, text, 0, 0);
+ }
+
+ /**
+ * Creates a new <code>JTextArea</code> object.
+ *
+ * @param rows the number of rows
+ * @param columns the number of cols
+ *
+ * @exception IllegalArgumentException if rows or columns are negative
+ */
+ public JTextArea(int rows, int columns)
+ {
+ this(null, null, rows, columns);
+ }
+
+ /**
+ * Creates a new <code>JTextArea</code> object.
+ *
+ * @param text the initial text
+ * @param rows the number of rows
+ * @param columns the number of cols
+ *
+ * @exception IllegalArgumentException if rows or columns are negative
+ */
+ public JTextArea(String text, int rows, int columns)
+ {
+ this(null, text, rows, columns);
+ }
+
+ /**
+ * Creates a new <code>JTextArea</code> object.
+ *
+ * @param doc the document model to use
+ */
+ public JTextArea(Document doc)
+ {
+ this(doc, null, 0, 0);
+ }
+
+ /**
+ * Creates a new <code>JTextArea</code> object.
+ *
+ * @param doc the document model to use
+ * @param text the initial text
+ * @param rows the number of rows
+ * @param columns the number of cols
+ *
+ * @exception IllegalArgumentException if rows or columns are negative
+ */
+ public JTextArea(Document doc, String text, int rows, int columns)
+ {
+ setDocument(doc == null ? createDefaultModel() : doc);
+ // Only explicitly setText() when there is actual text since
+ // setText() might be overridden and not expected to be called
+ // from the constructor (as in JEdit).
+ if (text != null)
+ setText(text);
+ setRows(rows);
+ setColumns(columns);
+ }
+
+ /**
+ * Appends the supplied text to the current contents
+ * of the document model.
+ *
+ * @param toAppend the text to append
+ */
+ public void append(String toAppend)
+ {
+ try
+ {
+ getDocument().insertString(getText().length(), toAppend, null);
+ }
+ catch (BadLocationException exception)
+ {
+ /* This shouldn't happen in theory -- but, if it does... */
+ throw new RuntimeException("Unexpected exception occurred.", exception);
+ }
+ if (toAppend != null && toAppend.length() > 0)
+ revalidate();
+ }
+
+ /**
+ * Creates the default document model.
+ *
+ * @return a new default model
+ */
+ protected Document createDefaultModel()
+ {
+ return new PlainDocument();
+ }
+
+ /**
+ * Returns true if the width of this component should be forced
+ * to match the width of a surrounding view port. When line wrapping
+ * is turned on, this method returns true.
+ *
+ * @return true if lines are wrapped.
+ */
+ public boolean getScrollableTracksViewportWidth()
+ {
+ return lineWrap ? true : super.getScrollableTracksViewportWidth();
+ }
+
+ /**
+ * Returns the increment that is needed to expose exactly one new line
+ * of text. This is implemented here to return the values of
+ * {@link #getRowHeight} and {@link #getColumnWidth}, depending on
+ * the value of the argument <code>direction</code>.
+ *
+ * @param visibleRect the view area that is visible in the viewport
+ * @param orientation either {@link SwingConstants#VERTICAL} or
+ * {@link SwingConstants#HORIZONTAL}
+ * @param direction less than zero for up/left scrolling, greater
+ * than zero for down/right scrolling
+ *
+ * @return the increment that is needed to expose exactly one new row
+ * or column of text
+ *
+ * @throws IllegalArgumentException if <code>orientation</code> is invalid
+ */
+ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
+ int direction)
+ {
+ if (orientation == SwingConstants.VERTICAL)
+ return getRowHeight();
+ else if (orientation == SwingConstants.HORIZONTAL)
+ return getColumnWidth();
+ else
+ throw new IllegalArgumentException("orientation must be either "
+ + "javax.swing.SwingConstants.VERTICAL "
+ + "or "
+ + "javax.swing.SwingConstants.HORIZONTAL"
+ );
+ }
+
+ /**
+ * Returns the preferred size of that text component in the case
+ * it is embedded within a JScrollPane. This uses the column and
+ * row settings if they are explicitly set, or fall back to
+ * the superclass's behaviour.
+ *
+ * @return the preferred size of that text component in the case
+ * it is embedded within a JScrollPane
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ if ((rows > 0) && (columns > 0))
+ return new Dimension(columns * getColumnWidth(), rows * getRowHeight());
+ else
+ return super.getPreferredScrollableViewportSize();
+ }
+
+ /**
+ * Returns the UI class ID string.
+ *
+ * @return the string "TextAreaUI"
+ */
+ public String getUIClassID()
+ {
+ return "TextAreaUI";
+ }
+
+ /**
+ * Returns the current number of columns.
+ *
+ * @return number of columns
+ */
+ public int getColumns()
+ {
+ return columns;
+ }
+
+ /**
+ * Sets the number of rows.
+ *
+ * @param columns number of columns
+ *
+ * @exception IllegalArgumentException if columns is negative
+ */
+ public void setColumns(int columns)
+ {
+ if (columns < 0)
+ throw new IllegalArgumentException();
+
+ if (columns != this.columns)
+ {
+ this.columns = columns;
+ revalidate();
+ }
+ }
+
+ /**
+ * Returns the current number of rows.
+ *
+ * @return number of rows
+ */
+ public int getRows()
+ {
+ return rows;
+ }
+
+ /**
+ * Sets the number of rows.
+ *
+ * @param rows number of rows
+ *
+ * @exception IllegalArgumentException if rows is negative
+ */
+ public void setRows(int rows)
+ {
+ if (rows < 0)
+ throw new IllegalArgumentException();
+
+ if (rows != this.rows)
+ {
+ this.rows = rows;
+ revalidate();
+ }
+ }
+
+ /**
+ * Checks whether line wrapping is enabled.
+ *
+ * @return <code>true</code> if line wrapping is enabled,
+ * <code>false</code> otherwise
+ */
+ public boolean getLineWrap()
+ {
+ return lineWrap;
+ }
+
+ /**
+ * Enables/disables line wrapping.
+ *
+ * @param flag <code>true</code> to enable line wrapping,
+ * <code>false</code> otherwise
+ */
+ public void setLineWrap(boolean flag)
+ {
+ if (lineWrap == flag)
+ return;
+
+ 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.
+ *
+ * @return the current number of spaces used for a tab.
+ */
+ public int getTabSize()
+ {
+ return tabSize;
+ }
+
+ /**
+ * Sets the number of characters used for a tab to the
+ * supplied value. If a change to the tab size property
+ * occurs (i.e. newSize != tabSize), a property change event
+ * is fired.
+ *
+ * @param newSize The new number of characters to use for a tab.
+ */
+ public void setTabSize(int newSize)
+ {
+ if (tabSize == newSize)
+ return;
+
+ int oldValue = tabSize;
+ tabSize = newSize;
+ firePropertyChange("tabSize", oldValue, tabSize);
+ }
+
+ protected int getColumnWidth()
+ {
+ FontMetrics metrics = getToolkit().getFontMetrics(getFont());
+ return metrics.charWidth('m');
+ }
+
+ public int getLineCount()
+ {
+ return getDocument().getDefaultRootElement().getElementCount();
+ }
+
+ public int getLineStartOffset(int line)
+ throws BadLocationException
+ {
+ int lineCount = getLineCount();
+
+ if (line < 0 || line > lineCount)
+ throw new BadLocationException("Non-existing line number", line);
+
+ Element lineElem = getDocument().getDefaultRootElement().getElement(line);
+ return lineElem.getStartOffset();
+ }
+
+ public int getLineEndOffset(int line)
+ throws BadLocationException
+ {
+ int lineCount = getLineCount();
+
+ if (line < 0 || line > lineCount)
+ throw new BadLocationException("Non-existing line number", line);
+
+ Element lineElem = getDocument().getDefaultRootElement().getElement(line);
+ return lineElem.getEndOffset();
+ }
+
+ public int getLineOfOffset(int offset)
+ throws BadLocationException
+ {
+ Document doc = getDocument();
+
+ if (offset < doc.getStartPosition().getOffset()
+ || offset >= doc.getEndPosition().getOffset())
+ throw new BadLocationException("offset outside of document", offset);
+
+ return doc.getDefaultRootElement().getElementIndex(offset);
+ }
+
+ protected int getRowHeight()
+ {
+ FontMetrics metrics = getToolkit().getFontMetrics(getFont());
+ return metrics.getHeight();
+ }
+
+ /**
+ * Inserts the supplied text at the specified position. Nothing
+ * happens in the case that the model or the supplied string is null
+ * or of zero length.
+ *
+ * @param string The string of text to insert.
+ * @param position The position at which to insert the supplied text.
+ * @throws IllegalArgumentException if the position is &lt; 0 or greater
+ * than the length of the current text.
+ */
+ public void insert(String string, int position)
+ {
+ // Retrieve the document model.
+ Document doc = getDocument();
+
+ // Check the model and string for validity.
+ if (doc == null
+ || string == null
+ || string.length() == 0)
+ return;
+
+ // Insert the text into the model.
+ try
+ {
+ doc.insertString(position, string, null);
+ }
+ catch (BadLocationException e)
+ {
+ throw new IllegalArgumentException("The supplied position, "
+ + position + ", was invalid.");
+ }
+ }
+
+ public void replaceRange(String text, int start, int end)
+ {
+ Document doc = getDocument();
+
+ if (start > end
+ || start < doc.getStartPosition().getOffset()
+ || end >= doc.getEndPosition().getOffset())
+ throw new IllegalArgumentException();
+
+ try
+ {
+ doc.remove(start, end - start);
+ doc.insertString(start, text, null);
+ }
+ catch (BadLocationException e)
+ {
+ // This cannot happen as we check offset above.
+ }
+ }
+
+ /**
+ * Returns the preferred size for the JTextArea. This is the maximum of
+ * the size that is needed to display the content and the requested size
+ * as per {@link #getColumns} and {@link #getRows}.
+ *
+ * @return the preferred size of the JTextArea
+ */
+ public Dimension getPreferredSize()
+ {
+ int reqWidth = getColumns() * getColumnWidth();
+ int reqHeight = getRows() * getRowHeight();
+ View view = getUI().getRootView(this);
+ int neededWidth = (int) view.getPreferredSpan(View.HORIZONTAL);
+ int neededHeight = (int) view.getPreferredSpan(View.VERTICAL);
+ return new Dimension(Math.max(reqWidth, neededWidth),
+ Math.max(reqHeight, neededHeight));
+ }
+
+ /**
+ * Returns the accessible context associated with the <code>JTextArea</code>.
+ *
+ * @return the accessible context associated with the <code>JTextArea</code>
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJTextArea();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTextField.java b/libjava/classpath/javax/swing/JTextField.java
new file mode 100644
index 000000000..69b70b068
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTextField.java
@@ -0,0 +1,570 @@
+/* JTextField.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.text.Document;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.PlainDocument;
+import javax.swing.text.TextAction;
+
+public class JTextField extends JTextComponent
+ implements SwingConstants
+{
+ /**
+ * AccessibleJTextField
+ */
+ protected class AccessibleJTextField extends AccessibleJTextComponent
+ {
+ private static final long serialVersionUID = 8255147276740453036L;
+
+ /**
+ * Constructor AccessibleJTextField
+ */
+ protected AccessibleJTextField()
+ {
+ super();
+ }
+
+ /**
+ * Returns the accessible state of this <code>AccessibleJTextField</code>.
+ *
+ * @return the accessible state of this <code>AccessibleJTextField</code>
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ // TODO: Figure out what state must be added here to the super's state.
+ return state;
+ }
+ }
+
+ private static final long serialVersionUID = 353853209832607592L;
+
+ private static final Action[] actions;
+
+ /**
+ * Name of the action that gets sent when the content of the text field
+ * gets accepted.
+ */
+ public static final String notifyAction = "notify-field-accept";
+
+ static
+ {
+ actions = new Action[1];
+ actions[0] = new TextAction(notifyAction)
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextField textField = (JTextField) event.getSource();
+ textField.fireActionPerformed();
+ }
+ };
+ }
+
+ private int columns;
+ private int align;
+
+ /** @since 1.3 */
+ private Action action;
+
+ /** @since 1.3 */
+ private String actionCommand;
+
+ private PropertyChangeListener actionPropertyChangeListener;
+
+ /**
+ * The horizontal visibility of the textfield.
+ */
+ private BoundedRangeModel horizontalVisibility;
+
+ /**
+ * Creates a new instance of <code>JTextField</code>.
+ */
+ public JTextField()
+ {
+ this(null, null, 0);
+ }
+
+ /**
+ * Creates a new instance of <code>JTextField</code>.
+ *
+ * @param text the initial text
+ */
+ public JTextField(String text)
+ {
+ this(null, text, 0);
+ }
+
+ /**
+ * Creates a new instance of <code>JTextField</code>.
+ *
+ * @param columns the number of columns
+ *
+ * @exception IllegalArgumentException if columns %lt; 0
+ */
+ public JTextField(int columns)
+ {
+ this(null, null, columns);
+ }
+
+ /**
+ * Creates a new instance of <code>JTextField</code>.
+ *
+ * @param text the initial text
+ * @param columns the number of columns
+ *
+ * @exception IllegalArgumentException if columns %lt; 0
+ */
+ public JTextField(String text, int columns)
+ {
+ this(null, text, columns);
+ }
+
+ /**
+ * Creates a new instance of <code>JTextField</code>.
+ *
+ * @param doc the document to use
+ * @param text the initial text
+ * @param columns the number of columns
+ *
+ * @exception IllegalArgumentException if columns %lt; 0
+ */
+ public JTextField(Document doc, String text, int columns)
+ {
+ if (columns < 0)
+ throw new IllegalArgumentException();
+
+ this.columns = columns;
+
+ // Initialize the horizontal visibility model.
+ horizontalVisibility = new DefaultBoundedRangeModel();
+
+ setDocument(doc == null ? createDefaultModel() : doc);
+
+ if (text != null)
+ setText(text);
+
+ // default value for alignment
+ align = LEADING;
+ }
+
+ /**
+ * Creates the default model for this text field.
+ * This implementation returns an instance of <code>PlainDocument</code>.
+ *
+ * @return a new instance of the default model
+ */
+ protected Document createDefaultModel()
+ {
+ return new PlainDocument();
+ }
+
+ /**
+ * Sets the document to be used for this JTextField.
+ *
+ * This sets the document property <code>filterNewlines</code> to
+ * <code>true</code> and then calls the super behaviour to setup a view and
+ * revalidate the text field.
+ *
+ * @param doc the document to set
+ */
+ public void setDocument(Document doc)
+ {
+ doc.putProperty("filterNewlines", Boolean.TRUE);
+ super.setDocument(doc);
+ }
+
+ /**
+ * Returns the class ID for the UI.
+ *
+ * @return "TextFieldUI";
+ */
+ public String getUIClassID()
+ {
+ return "TextFieldUI";
+ }
+
+ /**
+ * Adds a new listener object to this text field.
+ *
+ * @param listener the listener to add
+ */
+ public void addActionListener(ActionListener listener)
+ {
+ listenerList.add(ActionListener.class, listener);
+ }
+
+ /**
+ * Removes a listener object from this text field.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeActionListener(ActionListener listener)
+ {
+ listenerList.remove(ActionListener.class, listener);
+ }
+
+ /**
+ * Returns all registered <code>ActionListener</code> objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) getListeners(ActionListener.class);
+ }
+
+ /**
+ * Sends an action event to all registered
+ * <code>ActionListener</code> objects.
+ */
+ protected void fireActionPerformed()
+ {
+ ActionEvent event = new ActionEvent(this, 0,
+ actionCommand == null ? getText() : actionCommand);
+ ActionListener[] listeners = getActionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].actionPerformed(event);
+ }
+
+ /**
+ * Returns the number of columns of this text field.
+ *
+ * @return the number of columns
+ */
+ public int getColumns()
+ {
+ return columns;
+ }
+
+ /**
+ * Sets the number of columns and then invalidates the layout.
+ * @param columns the number of columns
+ * @throws IllegalArgumentException if columns < 0
+ */
+ public void setColumns(int columns)
+ {
+ if (columns < 0)
+ throw new IllegalArgumentException();
+
+ this.columns = columns;
+ invalidate();
+ //FIXME: do we need this repaint call?
+ repaint();
+ }
+
+ /**
+ * Returns the horizontal alignment, which is one of: JTextField.LEFT,
+ * JTextField.CENTER, JTextField.RIGHT, JTextField.LEADING,
+ * JTextField.TRAILING.
+ * @return the horizontal alignment
+ */
+ public int getHorizontalAlignment()
+ {
+ return align;
+ }
+
+ /**
+ * Sets the horizontal alignment of the text. Calls invalidate and repaint
+ * and fires a property change event.
+ * @param newAlign must be one of: JTextField.LEFT, JTextField.CENTER,
+ * JTextField.RIGHT, JTextField.LEADING, JTextField.TRAILING.
+ * @throws IllegalArgumentException if newAlign is not one of the above.
+ */
+ public void setHorizontalAlignment(int newAlign)
+ {
+ //FIXME: should throw an IllegalArgumentException if newAlign is invalid
+ if (align == newAlign)
+ return;
+
+ int oldAlign = align;
+ align = newAlign;
+ firePropertyChange("horizontalAlignment", oldAlign, newAlign);
+ invalidate();
+ repaint();
+ }
+
+ /**
+ * Sets the current font and revalidates so the font will take effect.
+ */
+ public void setFont(Font newFont)
+ {
+ super.setFont(newFont);
+ revalidate();
+ }
+
+ /**
+ * Returns the preferred size. If there is a non-zero number of columns,
+ * this is the number of columns multiplied by the column width, otherwise
+ * it returns super.getPreferredSize().
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension size = super.getPreferredSize();
+
+ if (columns != 0)
+ {
+ Insets i = getInsets();
+ size.width = columns * getColumnWidth() + i.left + i.right;
+ }
+
+ return size;
+ }
+
+ /**
+ * Returns the scroll offset in pixels.
+ *
+ * @return the scroll offset
+ */
+ public int getScrollOffset()
+ {
+ return horizontalVisibility.getValue();
+ }
+
+ /**
+ * Sets the scroll offset in pixels.
+ *
+ * @param offset the scroll offset
+ */
+ public void setScrollOffset(int offset)
+ {
+ // Automatically sets to the highest possible value if
+ // offset is bigger than that.
+ horizontalVisibility.setValue(
+ Math.min(horizontalVisibility.getMaximum()
+ - horizontalVisibility.getExtent(),
+ offset));
+
+ }
+
+ /**
+ * Returns the set of Actions that are commands for the editor.
+ * This is the actions supported by this editor plus the actions
+ * of the UI (returned by JTextComponent.getActions()).
+ */
+ public Action[] getActions()
+ {
+ return TextAction.augmentList(super.getActions(), actions);
+ }
+
+ public void postActionEvent()
+ {
+ String command = actionCommand != null ? actionCommand : getText();
+ ActionEvent event = new ActionEvent(this, 0, command);
+ ActionListener[] listeners = getActionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].actionPerformed(event);
+ }
+
+ /**
+ * @since 1.3
+ */
+ public Action getAction()
+ {
+ return action;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setAction(Action newAction)
+ {
+ if (action == newAction)
+ return;
+
+ if (action != null)
+ {
+ removeActionListener(action);
+ action.removePropertyChangeListener(actionPropertyChangeListener);
+ actionPropertyChangeListener = null;
+ }
+
+ Action oldAction = action;
+ action = newAction;
+
+ if (action != null)
+ {
+ addActionListener(action);
+ actionPropertyChangeListener = createActionPropertyChangeListener(action);
+ action.addPropertyChangeListener(actionPropertyChangeListener);
+ }
+
+ //FIXME: is this a hack? The horizontal alignment hasn't changed
+ firePropertyChange("horizontalAlignment", oldAction, newAction);
+ }
+
+ /**
+ * Sets the command string used in action events.
+ * @since 1.3
+ */
+ public void setActionCommand(String command)
+ {
+ actionCommand = command;
+ }
+
+ /**
+ * @since 1.3
+ */
+ protected PropertyChangeListener createActionPropertyChangeListener(Action action)
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ // Update properties "action" and "horizontalAlignment".
+ String name = event.getPropertyName();
+
+ if (name.equals("enabled"))
+ {
+ boolean enabled = ((Boolean) event.getNewValue()).booleanValue();
+ JTextField.this.setEnabled(enabled);
+ }
+ else if (name.equals(Action.SHORT_DESCRIPTION))
+ {
+ JTextField.this.setToolTipText((String) event.getNewValue());
+ }
+ }
+ };
+ }
+
+ /**
+ *
+ * @since 1.3
+ */
+ protected void configurePropertiesFromAction(Action action)
+ {
+ if (action != null)
+ {
+ setEnabled(action.isEnabled());
+ setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION));
+ }
+ else
+ {
+ setEnabled(true);
+ setToolTipText(null);
+ }
+ }
+
+ /**
+ * Returns the column width, which is the width of the character m
+ * for the font in use.
+ * @return the width of the character m for the font in use.
+ */
+ protected int getColumnWidth()
+ {
+ FontMetrics metrics = getToolkit().getFontMetrics(getFont());
+ return metrics.charWidth('m');
+ }
+
+ /**
+ * Returns the accessible context associated with the <code>JTextField</code>.
+ *
+ * @return the accessible context associated with the <code>JTextField</code>
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJTextField();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns the bounded range model that describes the horizontal visibility
+ * of the text field in the case when the text does not fit into the
+ * available space. The actual values of this model are managed by the look
+ * and feel implementation.
+ *
+ * @return the bounded range model that describes the horizontal visibility
+ */
+ public BoundedRangeModel getHorizontalVisibility()
+ {
+ return horizontalVisibility;
+ }
+
+ /**
+ * Returns <code>true</code>, unless this is embedded in a
+ * <code>JViewport</code> in which case the viewport takes responsibility of
+ * validating.
+ *
+ * @return <code>true</code>, unless this is embedded in a
+ * <code>JViewport</code> in which case the viewport takes
+ * responsibility of validating
+ */
+ public boolean isValidateRoot()
+ {
+ return ! (getParent() instanceof JViewport);
+ }
+
+ public void scrollRectToVisible(Rectangle r)
+ {
+ int v = horizontalVisibility.getValue();
+
+ // The extent value is the inner width of the text field.
+ int e = horizontalVisibility.getExtent();
+ Insets i = getInsets();
+
+ // The x value in the rectangle (usually) denotes the new location
+ // of the caret. We check whether the location lies inside the left or
+ // right border and scroll into the appropriate direction.
+ // The calculation has to be shifted by the BoundedRangeModel's value
+ // because that value was already used to calculate r.x (this happens
+ // as part of a modelToView() call in FieldView).
+ if (r.x < i.left)
+ setScrollOffset(v + r.x - i.left);
+ else if (r.x > e + i.left)
+ setScrollOffset(r.x + v - e - i.left);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JTextPane.java b/libjava/classpath/javax/swing/JTextPane.java
new file mode 100644
index 000000000..4fef0020b
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTextPane.java
@@ -0,0 +1,424 @@
+/* JTextPane.java -- A powerful text widget supporting styled text
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+import javax.swing.text.StyledEditorKit;
+
+/**
+ * A powerful text component that supports styled content as well as
+ * embedding images and components. It is entirely based on a
+ * {@link StyledDocument} content model and a {@link StyledEditorKit}.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ * @author Andrew Selkirk
+ */
+public class JTextPane
+ extends JEditorPane
+{
+ /**
+ * Creates a new <code>JTextPane</code> with a <code>null</code> document.
+ */
+ public JTextPane()
+ {
+ super();
+ }
+
+ /**
+ * Creates a new <code>JTextPane</code> and sets the specified
+ * <code>document</code>.
+ *
+ * @param document the content model to use
+ */
+ public JTextPane(StyledDocument document)
+ {
+ this();
+ setStyledDocument(document);
+ }
+
+ /**
+ * Returns the UI class ID. This is <code>TextPaneUI</code>.
+ *
+ * @return <code>TextPaneUI</code>
+ */
+ public String getUIClassID()
+ {
+ return "TextPaneUI";
+ }
+
+ /**
+ * Sets the content model for this <code>JTextPane</code>.
+ * <code>JTextPane</code> can only be used with {@link StyledDocument}s,
+ * if you try to set a different type of <code>Document</code>, an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param document the content model to set
+ *
+ * @throws IllegalArgumentException if <code>document</code> is not an
+ * instance of <code>StyledDocument</code>
+ *
+ * @see #setStyledDocument
+ */
+ public void setDocument(Document document)
+ {
+ if (document != null && !(document instanceof StyledDocument))
+ throw new IllegalArgumentException
+ ("JTextPane can only handle StyledDocuments");
+
+ setStyledDocument((StyledDocument) document);
+ }
+
+ /**
+ * Returns the {@link StyledDocument} that is the content model for
+ * this <code>JTextPane</code>. This is a typed wrapper for
+ * {@link #getDocument()}.
+ *
+ * @return the content model of this <code>JTextPane</code>
+ */
+ public StyledDocument getStyledDocument()
+ {
+ return (StyledDocument) super.getDocument();
+ }
+
+ /**
+ * Sets the content model for this <code>JTextPane</code>.
+ *
+ * @param document the content model to set
+ */
+ public void setStyledDocument(StyledDocument document)
+ {
+ super.setDocument(document);
+ }
+
+ /**
+ * Replaces the currently selected text with the specified
+ * <code>content</code>. If there is no selected text, this results
+ * in a simple insertion at the current caret position. If there is
+ * no <code>content</code> specified, this results in the selection
+ * beeing deleted.
+ *
+ * @param content the text with which the selection is replaced
+ */
+ public void replaceSelection(String content)
+ {
+ Caret caret = getCaret();
+ StyledDocument doc = getStyledDocument();
+ AttributeSet a = getInputAttributes().copyAttributes();
+ if (doc == null)
+ return;
+
+ int dot = caret.getDot();
+ int mark = caret.getMark();
+
+ int p0 = Math.min (dot, mark);
+ int p1 = Math.max (dot, mark);
+
+ try
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument)doc).replace(p0, p1 - p0, content, a);
+ else
+ {
+ // Remove selected text.
+ if (dot != mark)
+ doc.remove(p0, p1 - p0);
+ // Insert new text.
+ if (content != null && content.length() > 0)
+ doc.insertString(p0, content, a);
+ }
+ }
+ catch (BadLocationException e)
+ {
+ throw new AssertionError
+ ("No BadLocationException should be thrown here");
+ }
+ }
+
+ /**
+ * Inserts an AWT or Swing component into the text at the current caret
+ * position.
+ *
+ * @param component the component to be inserted
+ */
+ public void insertComponent(Component component)
+ {
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ atts.addAttribute(StyleConstants.ComponentAttribute, component);
+ atts.addAttribute(StyleConstants.NameAttribute,
+ StyleConstants.ComponentElementName);
+ try
+ {
+ getDocument().insertString(getCaret().getDot(), " ", atts);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError("Unexpected bad location");
+ err.initCause(ex);
+ throw err;
+ }
+ }
+
+ /**
+ * Inserts an <code>Icon</code> into the text at the current caret position.
+ *
+ * @param icon the <code>Icon</code> to be inserted
+ */
+ public void insertIcon(Icon icon)
+ {
+ MutableAttributeSet inputAtts = getInputAttributes();
+ inputAtts.removeAttributes(inputAtts);
+ StyleConstants.setIcon(inputAtts, icon);
+ replaceSelection(" ");
+ inputAtts.removeAttributes(inputAtts);
+ }
+
+ /**
+ * Adds a style into the style hierarchy. Unspecified style attributes
+ * can be resolved in the <code>parent</code> style, if one is specified.
+ *
+ * While it is legal to add nameless styles (<code>nm == null</code),
+ * you must be aware that the client application is then responsible
+ * for managing the style hierarchy, since unnamed styles cannot be
+ * looked up by their name.
+ *
+ * @param nm the name of the style or <code>null</code> if the style should
+ * be unnamed
+ * @param parent the parent in which unspecified style attributes are
+ * resolved, or <code>null</code> if that is not necessary
+ *
+ * @return the newly created <code>Style</code>
+ */
+ public Style addStyle(String nm, Style parent)
+ {
+ return getStyledDocument().addStyle(nm, parent);
+ }
+
+ /**
+ * Removes a named <code>Style</code> from the style hierarchy.
+ *
+ * @param nm the name of the <code>Style</code> to be removed
+ */
+ public void removeStyle(String nm)
+ {
+ getStyledDocument().removeStyle(nm);
+ }
+
+ /**
+ * Looks up and returns a named <code>Style</code>.
+ *
+ * @param nm the name of the <code>Style</code>
+ *
+ * @return the found <code>Style</code> of <code>null</code> if no such
+ * <code>Style</code> exists
+ */
+ public Style getStyle(String nm)
+ {
+ return getStyledDocument().getStyle(nm);
+ }
+
+ /**
+ * Returns the logical style of the paragraph at the current caret position.
+ *
+ * @return the logical style of the paragraph at the current caret position
+ */
+ public Style getLogicalStyle()
+ {
+ return getStyledDocument().getLogicalStyle(getCaretPosition());
+ }
+
+ /**
+ * Sets the logical style for the paragraph at the current caret position.
+ *
+ * @param style the style to set for the current paragraph
+ */
+ public void setLogicalStyle(Style style)
+ {
+ getStyledDocument().setLogicalStyle(getCaretPosition(), style);
+ }
+
+ /**
+ * Returns the text attributes for the character at the current caret
+ * position.
+ *
+ * @return the text attributes for the character at the current caret
+ * position
+ */
+ public AttributeSet getCharacterAttributes()
+ {
+ StyledDocument doc = getStyledDocument();
+ Element el = doc.getCharacterElement(getCaretPosition());
+ return el.getAttributes();
+ }
+
+ /**
+ * Sets text attributes for the current selection. If there is no selection
+ * the text attributes are applied to newly inserted text
+ *
+ * @param attribute the text attributes to set
+ * @param replace if <code>true</code>, the attributes of the current
+ * selection are overridden, otherwise they are merged
+ *
+ * @see #getInputAttributes
+ */
+ public void setCharacterAttributes(AttributeSet attribute,
+ boolean replace)
+ {
+ int dot = getCaret().getDot();
+ int start = getSelectionStart();
+ int end = getSelectionEnd();
+ if (start == dot && end == dot)
+ // There is no selection, update insertAttributes instead
+ {
+ MutableAttributeSet inputAttributes =
+ getStyledEditorKit().getInputAttributes();
+ if (replace)
+ inputAttributes.removeAttributes(inputAttributes);
+ inputAttributes.addAttributes(attribute);
+ }
+ else
+ getStyledDocument().setCharacterAttributes(start, end - start, attribute,
+ replace);
+ }
+
+ /**
+ * Returns the text attributes of the paragraph at the current caret
+ * position.
+ *
+ * @return the attributes of the paragraph at the current caret position
+ */
+ public AttributeSet getParagraphAttributes()
+ {
+ StyledDocument doc = getStyledDocument();
+ Element el = doc.getParagraphElement(getCaretPosition());
+ return el.getAttributes();
+ }
+
+ /**
+ * Sets text attributes for the paragraph at the current selection.
+ * If there is no selection the text attributes are applied to
+ * the paragraph at the current caret position.
+ *
+ * @param attribute the text attributes to set
+ * @param replace if <code>true</code>, the attributes of the current
+ * selection are overridden, otherwise they are merged
+ */
+ public void setParagraphAttributes(AttributeSet attribute,
+ boolean replace)
+ {
+ // TODO
+ }
+
+ /**
+ * Returns the attributes that are applied to newly inserted text.
+ * This is a {@link MutableAttributeSet}, so you can easily modify these
+ * attributes.
+ *
+ * @return the attributes that are applied to newly inserted text
+ */
+ public MutableAttributeSet getInputAttributes()
+ {
+ return getStyledEditorKit().getInputAttributes();
+ }
+
+ /**
+ * Returns the {@link StyledEditorKit} that is currently used by this
+ * <code>JTextPane</code>.
+ *
+ * @return the current <code>StyledEditorKit</code> of this
+ * <code>JTextPane</code>
+ */
+ protected final StyledEditorKit getStyledEditorKit()
+ {
+ return (StyledEditorKit) getEditorKit();
+ }
+
+ /**
+ * Creates the default {@link EditorKit} that is used in
+ * <code>JTextPane</code>s. This is an instance of {@link StyledEditorKit}.
+ *
+ * @return the default {@link EditorKit} that is used in
+ * <code>JTextPane</code>s
+ */
+ protected EditorKit createDefaultEditorKit()
+ {
+ return new StyledEditorKit();
+ }
+
+ /**
+ * Sets the {@link EditorKit} to use for this <code>JTextPane</code>.
+ * <code>JTextPane</code>s can only handle {@link StyledEditorKit}s,
+ * if client programs try to set a different type of <code>EditorKit</code>
+ * then an IllegalArgumentException is thrown
+ *
+ * @param editor the <code>EditorKit</code> to set
+ *
+ * @throws IllegalArgumentException if <code>editor</code> is no
+ * <code>StyledEditorKit</code>
+ */
+ public final void setEditorKit(EditorKit editor)
+ {
+ if (!(editor instanceof StyledEditorKit))
+ throw new IllegalArgumentException
+ ("JTextPanes can only handle StyledEditorKits");
+ super.setEditorKit(editor);
+ }
+
+ /**
+ * Returns a param string that can be used for debugging.
+ *
+ * @return a param string that can be used for debugging.
+ */
+ protected String paramString()
+ {
+ return super.paramString(); // TODO
+ }
+}
diff --git a/libjava/classpath/javax/swing/JToggleButton.java b/libjava/classpath/javax/swing/JToggleButton.java
new file mode 100644
index 000000000..2ee4187c3
--- /dev/null
+++ b/libjava/classpath/javax/swing/JToggleButton.java
@@ -0,0 +1,350 @@
+/* JToggleButton.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.swing.plaf.ButtonUI;
+
+/**
+ * The <code>JToggleButton</code> component provides a stateful button,
+ * which can be either selected or unselected. This provides the basis
+ * for the implementations of radio buttons (<code>JRadioButton</code>)
+ * and check boxes (<code>JCheckBox</code>).
+ *
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see JRadioButton
+ * @see JCheckBox
+ * @since 1.2
+ */
+public class JToggleButton extends AbstractButton implements Accessible
+{
+ /**
+ * This class provides accessibility support for the toggle button.
+ */
+ protected class AccessibleJToggleButton
+ extends AccessibleAbstractButton
+ implements ItemListener
+ {
+ private static final long serialVersionUID = -8652952712161229225L;
+
+ /**
+ * Constructor for the accessible toggle button.
+ */
+ public AccessibleJToggleButton()
+ {
+ super();
+ /* Register the accessible toggle button as a listener for item events */
+ addItemListener(this);
+ }
+
+ /**
+ * Returns the accessible role for the toggle button.
+ *
+ * @return An instance of <code>AccessibleRole</code>, describing
+ * the role of the toggle button.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TOGGLE_BUTTON;
+ }
+
+ /**
+ * Monitors the toggle button for state changes and fires accessible
+ * property change events when they occur.
+ *
+ * @param event the event that occurred.
+ */
+ public void itemStateChanged(ItemEvent event)
+ {
+ /* Fire a state property change event as the button's state has changed */
+ if (event.getStateChange() == ItemEvent.SELECTED)
+ {
+ /* State has changed from unselected (null) to selected */
+ firePropertyChange(ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.SELECTED);
+ }
+ else
+ {
+ /* State has changed from selected to unselected (null) */
+ firePropertyChange(ACCESSIBLE_STATE_PROPERTY, AccessibleState.ENABLED, null);
+ }
+ }
+
+ }
+
+ /**
+ * The model handles the storage and maintenance of the state of
+ * the toggle button. This follows the same paradigm (the MVC
+ * or Model-View-Controller design pattern) employed by
+ * other Swing components, where the data associated with a component
+ * is stored separately from the display aspects.
+ */
+ public static class ToggleButtonModel extends DefaultButtonModel
+ {
+ /**
+ * Compatible with Sun's JDK.
+ */
+ private static final long serialVersionUID = -1589950750899943974L;
+
+ /**
+ * Sets the pressed state of the button. The selected state
+ * of the button also changes follwing the button being pressed.
+ *
+ * @param p true if the button is pressed down.
+ */
+ public void setPressed(boolean p)
+ {
+ // cannot change PRESSED state unless button is enabled
+ if (! isEnabled())
+ return;
+
+ // if this call does not represent a CHANGE in state, then return
+ if ((p && isPressed()) || (!p && !isPressed()))
+ return;
+
+ // The JDK first fires events in the following order:
+ // 1. ChangeEvent for selected
+ // 2. ChangeEvent for pressed
+ // 3. ActionEvent
+ // So do we.
+
+ // setPressed(false) == mouse release on us,
+ // if we were armed, we flip the selected state.
+ if (!p && isArmed())
+ {
+ setSelected(! isSelected());
+ }
+
+ // make the change
+ if (p)
+ stateMask = stateMask | PRESSED;
+ else
+ stateMask = stateMask & (~PRESSED);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+
+ if (!p && isArmed())
+ {
+ fireActionPerformed(new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED,
+ actionCommand));
+ }
+ }
+
+ /**
+ * Checks if the button is selected.
+ *
+ * @return <code>true</code> if the button is selected.
+ */
+ public boolean isSelected()
+ {
+ return super.isSelected();
+ }
+
+ /**
+ * Sets the selected state of the button.
+ *
+ * @param b true if button is selected
+ */
+ public void setSelected(boolean b)
+ {
+ super.setSelected(b);
+ }
+ }
+
+ /**
+ * Compatible with Sun's JDK.
+ */
+ private static final long serialVersionUID = -3128248873429850443L;
+
+ /**
+ * Constructs an unselected toggle button with no text or icon.
+ */
+ public JToggleButton()
+ {
+ this(null, null, false);
+ }
+
+ /**
+ * Constructs a toggle button using the labelling, state
+ * and icon specified by the supplied action.
+ *
+ * @param a the action to use to define the properties of the button.
+ */
+ public JToggleButton(Action a)
+ {
+ this();
+ setAction(a);
+ }
+
+ /**
+ * Constructs an unselected toggle button with the supplied icon
+ * and no text.
+ *
+ * @param icon the icon to use.
+ */
+ public JToggleButton(Icon icon)
+ {
+ this(null, icon, false);
+ }
+
+ /**
+ * Constructs a toggle button with the supplied icon and state.
+ *
+ * @param icon the icon to use.
+ * @param selected if true, the toggle button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JToggleButton(Icon icon, boolean selected)
+ {
+ this(null, icon, selected);
+ }
+
+ /**
+ * Constructs an unselected toggle button using the supplied text
+ * and no icon.
+ *
+ * @param text the text to use.
+ */
+ public JToggleButton(String text)
+ {
+ this(text, null, false);
+ }
+
+ /**
+ * Constructs a toggle button with the supplied text and state.
+ *
+ * @param text the text to use.
+ * @param selected if true, the toggle button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JToggleButton(String text, boolean selected)
+ {
+ this(text, null, selected);
+ }
+
+ /**
+ * Constructs an unselected toggle button with the supplied text
+ * and icon.
+ *
+ * @param text the text to use.
+ * @param icon the icon to use.
+ */
+ public JToggleButton(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ /**
+ * Constructs a toggle button with the supplied text, icon and state.
+ *
+ * @param text the text to use.
+ * @param icon the icon to use.
+ * @param selected if true, the toggle button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JToggleButton (String text, Icon icon, boolean selected)
+ {
+ super();
+ setModel(new ToggleButtonModel());
+ init(text, icon);
+ model.setSelected(selected);
+ setAlignmentX(LEFT_ALIGNMENT);
+ }
+
+ /**
+ * Gets the AccessibleContext associated with this <code>JToggleButton</code>.
+ * The context is created, if necessary.
+ *
+ * @return the associated context
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ /* Create the context if this is the first request */
+ if (accessibleContext == null)
+ {
+ /* Create the context */
+ accessibleContext = new AccessibleJToggleButton();
+ }
+ return accessibleContext;
+ }
+
+ /**
+ * Returns a string that specifies the name of the Look and Feel
+ * class that renders this component.
+ *
+ * @return The Look and Feel UI class in <code>String</code> form.
+ */
+ public String getUIClassID()
+ {
+ return "ToggleButtonUI";
+ }
+
+ /**
+ * Returns a textual representation of this component for debugging.
+ * Users should not depend on anything as regards the content or formatting
+ * of this string, except for the fact that the returned string may never be
+ * null (only empty).
+ *
+ * @return the component in <code>String</code> form for debugging.
+ */
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+
+ /**
+ * This method resets the toggle button's UI delegate to the default UI for
+ * the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ButtonUI)UIManager.getUI(this));
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JToolBar.java b/libjava/classpath/javax/swing/JToolBar.java
new file mode 100644
index 000000000..5f4816f04
--- /dev/null
+++ b/libjava/classpath/javax/swing/JToolBar.java
@@ -0,0 +1,798 @@
+/* JToolBar.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.plaf.ToolBarUI;
+
+/**
+ * JToolBar is a component that provides a toolbar to Swing programs. Users
+ * can add buttons (or actions that will be represented by JButtons) as well
+ * as other components to the JToolBar. JToolBars can be dragged in and out
+ * of their parent components. If the JToolBar is dragged out of the parent,
+ * then it will be displayed in its own RootPaneContainer. For dragging to
+ * work properly, JToolBars need to be placed in a Container that has a
+ * BorderLayout. That parent Container cannot have components in the NORTH,
+ * EAST, SOUTH, or WEST components (that is not the JToolBar).
+ */
+public class JToolBar extends JComponent implements SwingConstants, Accessible
+{
+ /**
+ * Provides the accessibility features for the <code>JToolBar</code>
+ * component.
+ */
+ protected class AccessibleJToolBar extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = -5516888265903814215L;
+
+ /**
+ * Creates a new <code>AccessibleJToolBar</code> instance.
+ */
+ protected AccessibleJToolBar()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JToolBar}
+ * component. The current implementation simply calls the superclass.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // running tests against the reference implementation, I was unable
+ // to find any state information that is set specifically by the
+ // tool bar...
+ return super.getAccessibleStateSet();
+ }
+
+ /**
+ * Returns the accessible role for the <code>JToolBar</code> component.
+ *
+ * @return {@link AccessibleRole#TOOL_BAR}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TOOL_BAR;
+ }
+ }
+
+ /**
+ * This is the private JToolBar layout manager.
+ */
+ private class DefaultToolBarLayout implements LayoutManager
+ {
+ /**
+ * This method is called when a new component is added to the container.
+ *
+ * @param name The name of the component added.
+ * @param comp The component that was added.
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the given container to position and
+ * size the child components.
+ *
+ * @param c The container to lay out.
+ *
+ * @throws Error DOCUMENT ME!
+ */
+ public void layoutContainer(Container c)
+ {
+ if (! (c instanceof JToolBar))
+ throw new Error("DefaultToolBarLayout can only be used on JToolBars.");
+ Insets insets = getInsets();
+ Insets margin = getMargin();
+ int middle;
+ if (margin != null)
+ {
+ insets.left += margin.left;
+ insets.top += margin.top;
+ insets.bottom += margin.bottom;
+ insets.right += margin.right;
+ }
+ Component[] components = c.getComponents();
+ Dimension tdims = c.getSize();
+ int start = 0;
+ Dimension pref;
+
+ if (getOrientation() == SwingUtilities.HORIZONTAL)
+ {
+ start += insets.left;
+ for (int i = 0; i < components.length; i++)
+ {
+ if (components[i] != null && components[i].isVisible())
+ {
+ pref = components[i].getPreferredSize();
+ if (pref != null)
+ {
+ middle = (tdims.height - pref.height) / 2;
+ components[i].setBounds(start, middle, pref.width,
+ pref.height);
+ start += pref.width;
+ }
+ }
+ }
+ }
+ else
+ {
+ start += insets.top;
+ for (int i = 0; i < components.length; i++)
+ {
+ if (components[i] != null && components[i].isVisible())
+ {
+ pref = components[i].getPreferredSize();
+ if (pref != null)
+ {
+ middle = (tdims.width - pref.width) / 2;
+ components[i].setBounds(middle, start, pref.width,
+ pref.height);
+ start += pref.height;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This method returns the minimum size of the given container given the
+ * child components.
+ *
+ * @param parent The container to measure.
+ *
+ * @return The minimum size of the given container.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return preferredLayoutSize(parent);
+ }
+
+ /**
+ * This method returns the preferred size of the given container given the
+ * child components.
+ *
+ * @param parent The container to measure.
+ *
+ * @return The preferred size of the given container.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ int orientation = getOrientation();
+ Component[] components = getComponents();
+
+ int limit = 0;
+ int total = 0;
+ Dimension dims;
+
+ int w = 0;
+ int h = 0;
+
+ if (orientation == SwingConstants.HORIZONTAL)
+ {
+ for (int i = 0; i < components.length; i++)
+ {
+ dims = components[i].getPreferredSize();
+ if (dims != null)
+ {
+ if (dims.height > limit)
+ limit = dims.height;
+ total += dims.width;
+ }
+ }
+ w = total;
+ h = limit;
+ }
+ else
+ {
+ for (int i = 0; i < components.length; i++)
+ {
+ dims = components[i].getPreferredSize();
+ if (dims != null)
+ {
+ if (dims.width > limit)
+ limit = dims.width;
+ total += dims.height;
+ }
+ }
+ w = limit;
+ h = total;
+ }
+
+ Insets insets = getInsets();
+ w += insets.left + insets.right;
+ h += insets.top + insets.bottom;
+
+ Insets margin = getMargin();
+ if (margin != null)
+ {
+ w += margin.left + margin.right;
+ h += margin.top + margin.bottom;
+ }
+
+ return new Dimension(w, h);
+ }
+
+ /**
+ * This method is called when the given component is removed from the
+ * container.
+ *
+ * @param comp The component removed.
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * This is an extension of JSeparator used in toolbars. Unlike JSeparator,
+ * nothing is painted for this Separator, it is only blank space that
+ * separates components.
+ */
+ public static class Separator extends JSeparator
+ {
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -1656745644823105219L;
+
+ /**
+ * Creates a new Separator object.
+ */
+ public Separator()
+ {
+ super();
+ } // Separator()
+
+ /**
+ * Creates a new Separator object with the given size.
+ *
+ * @param size The size of the separator.
+ */
+ public Separator(Dimension size)
+ {
+ setPreferredSize(size);
+ } // Separator()
+
+ /**
+ * This method returns the String ID of the UI class of Separator.
+ *
+ * @return The UI class' String ID.
+ */
+ public String getUIClassID()
+ {
+ return "ToolBarSeparatorUI";
+ } // getUIClassID()
+
+ /**
+ * This method returns the preferred size of the Separator.
+ *
+ * @return The preferred size of the Separator.
+ */
+ public Dimension getPreferredSize()
+ {
+ return super.getPreferredSize();
+ } // getPreferredSize()
+
+ /**
+ * This method returns the maximum size of the Separator.
+ *
+ * @return The maximum size of the Separator.
+ */
+ public Dimension getMaximumSize()
+ {
+ return super.getPreferredSize();
+ } // getMaximumSize()
+
+ /**
+ * This method returns the minimum size of the Separator.
+ *
+ * @return The minimum size of the Separator.
+ */
+ public Dimension getMinimumSize()
+ {
+ return super.getPreferredSize();
+ } // getMinimumSize()
+
+ /**
+ * This method returns the size of the Separator.
+ *
+ * @return The size of the Separator.
+ */
+ public Dimension getSeparatorSize()
+ {
+ return super.getPreferredSize();
+ } // getSeparatorSize()
+
+ /**
+ * This method sets the size of the Separator.
+ *
+ * @param size The new size of the Separator.
+ */
+ public void setSeparatorSize(Dimension size)
+ {
+ setPreferredSize(size);
+ } // setSeparatorSize()
+ } // Separator
+
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -1269915519555129643L;
+
+ /** Whether the JToolBar paints its border. */
+ private transient boolean paintBorder = true;
+
+ /** The extra insets around the JToolBar. */
+ private transient Insets margin;
+
+ /** Whether the JToolBar can float (and be dragged around). */
+ private transient boolean floatable = true;
+
+ /** Whether the buttons will have rollover borders. */
+ private transient boolean rollover;
+
+ /** The orientation of the JToolBar. */
+ private int orientation = HORIZONTAL;
+
+ /**
+ * This method creates a new JToolBar object with horizontal orientation
+ * and no name.
+ */
+ public JToolBar()
+ {
+ this(null, HORIZONTAL);
+ } // JToolBar()
+
+ /**
+ * This method creates a new JToolBar with the given orientation and no
+ * name.
+ *
+ * @param orientation JToolBar orientation (HORIZONTAL or VERTICAL)
+ */
+ public JToolBar(int orientation)
+ {
+ this(null, orientation);
+ } // JToolBar()
+
+ /**
+ * This method creates a new JToolBar object with the given name and
+ * horizontal orientation.
+ *
+ * @param name Name assigned to undocked tool bar.
+ */
+ public JToolBar(String name)
+ {
+ this(name, HORIZONTAL);
+ } // JToolBar()
+
+ /**
+ * This method creates a new JToolBar object with the given name and
+ * orientation.
+ *
+ * @param name Name assigned to undocked tool bar.
+ * @param orientation JToolBar orientation (HORIZONTAL or VERTICAL)
+ */
+ public JToolBar(String name, int orientation)
+ {
+ setName(name);
+ setOrientation(orientation);
+ setLayout(new DefaultToolBarLayout());
+ revalidate();
+ setOpaque(true);
+ updateUI();
+ }
+
+ /**
+ * This method adds a new JButton that performs the given Action to the
+ * JToolBar.
+ *
+ * @param action The Action to add to the JToolBar.
+ *
+ * @return The JButton that wraps the Action.
+ */
+ public JButton add(Action action)
+ {
+ JButton b = createActionComponent(action);
+ add(b);
+ return b;
+ } // add()
+
+ /**
+ * This method paints the border if the borderPainted property is true.
+ *
+ * @param graphics The graphics object to paint with.
+ */
+ protected void paintBorder(Graphics graphics)
+ {
+ if (paintBorder && isFloatable())
+ super.paintBorder(graphics);
+ } // paintBorder()
+
+ /**
+ * This method returns the UI class used to paint this JToolBar.
+ *
+ * @return The UI class for this JToolBar.
+ */
+ public ToolBarUI getUI()
+ {
+ return (ToolBarUI) ui;
+ } // getUI()
+
+ /**
+ * This method sets the UI used with the JToolBar.
+ *
+ * @param ui The UI used with the JToolBar.
+ */
+ public void setUI(ToolBarUI ui)
+ {
+ super.setUI(ui);
+ } // setUI()
+
+ /**
+ * This method resets the UI used to the Look and Feel defaults.
+ */
+ public void updateUI()
+ {
+ setUI((ToolBarUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the String identifier for the UI class to the used
+ * with the JToolBar.
+ *
+ * @return The String identifier for the UI class.
+ */
+ public String getUIClassID()
+ {
+ return "ToolBarUI";
+ } // getUIClassID()
+
+ /**
+ * This method sets the rollover property for the JToolBar. In rollover
+ * mode, JButtons inside the JToolBar will only display their borders when
+ * the mouse is moving over them.
+ *
+ * @param b The new rollover property.
+ */
+ public void setRollover(boolean b)
+ {
+ if (b != rollover)
+ {
+ rollover = b;
+ firePropertyChange("rollover", ! rollover, rollover);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * This method returns the rollover property.
+ *
+ * @return The rollover property.
+ */
+ public boolean isRollover()
+ {
+ return rollover;
+ }
+
+ /**
+ * This method returns the index of the given component.
+ *
+ * @param component The component to find.
+ *
+ * @return The index of the given component.
+ */
+ public int getComponentIndex(Component component)
+ {
+ Component[] components = getComponents();
+ if (components == null)
+ return -1;
+
+ for (int i = 0; i < components.length; i++)
+ if (components[i] == component)
+ return i;
+
+ return -1;
+ } // getComponentIndex()
+
+ /**
+ * This method returns the component at the given index.
+ *
+ * @param index The index of the component.
+ *
+ * @return The component at the given index.
+ */
+ public Component getComponentAtIndex(int index)
+ {
+ return getComponent(index);
+ } // getComponentAtIndex()
+
+ /**
+ * This method returns the margin property.
+ *
+ * @return The margin property.
+ */
+ public Insets getMargin()
+ {
+ return margin;
+ } // getMargin()
+
+ /**
+ * This method sets the margin property. The margin property determines the
+ * extra space between the children components of the JToolBar and the
+ * border.
+ *
+ * @param margin The margin property.
+ */
+ public void setMargin(Insets margin)
+ {
+ if ((this.margin != null && margin == null)
+ || (this.margin == null && margin != null)
+ || (margin != null && this.margin != null
+ && (margin.left != this.margin.left
+ || margin.right != this.margin.right || margin.top != this.margin.top
+ || margin.bottom != this.margin.bottom)))
+ {
+ Insets oldMargin = this.margin;
+ this.margin = margin;
+ firePropertyChange("margin", oldMargin, this.margin);
+ revalidate();
+ repaint();
+ }
+ } // setMargin()
+
+ /**
+ * This method returns the borderPainted property.
+ *
+ * @return The borderPainted property.
+ */
+ public boolean isBorderPainted()
+ {
+ return paintBorder;
+ } // isBorderPainted()
+
+ /**
+ * This method sets the borderPainted property. If set to false, the border
+ * will not be painted.
+ *
+ * @param painted Whether the border will be painted.
+ */
+ public void setBorderPainted(boolean painted)
+ {
+ if (painted != paintBorder)
+ {
+ paintBorder = painted;
+ firePropertyChange("borderPainted", ! paintBorder,
+ paintBorder);
+ repaint();
+ }
+ } // setBorderPainted()
+
+ /**
+ * This method returns the floatable property.
+ *
+ * @return The floatable property.
+ */
+ public boolean isFloatable()
+ {
+ return floatable;
+ } // isFloatable()
+
+ /**
+ * This method sets the floatable property. If set to false, the JToolBar
+ * cannot be dragged.
+ *
+ * @param floatable Whether the JToolBar can be dragged.
+ */
+ public void setFloatable(boolean floatable)
+ {
+ if (floatable != this.floatable)
+ {
+ this.floatable = floatable;
+ firePropertyChange("floatable", ! floatable, floatable);
+ }
+ } // setFloatable()
+
+ /**
+ * This method returns the orientation of the JToolBar.
+ *
+ * @return The orientation of the JToolBar.
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ } // getOrientation()
+
+ /**
+ * This method sets the layout manager to be used with the JToolBar.
+ *
+ * @param mgr The Layout Manager used with the JToolBar.
+ */
+ public void setLayout(LayoutManager mgr)
+ {
+ super.setLayout(mgr);
+ revalidate();
+ repaint();
+ } // setLayout()
+
+ /**
+ * This method sets the orientation property for JToolBar.
+ *
+ * @param orientation The new orientation for JToolBar.
+ *
+ * @throws IllegalArgumentException If the orientation is not HORIZONTAL or
+ * VERTICAL.
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ if (orientation != this.orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", oldOrientation, this.orientation);
+ revalidate();
+ repaint();
+ }
+ } // setOrientation()
+
+ /**
+ * This method adds a Separator of default size to the JToolBar.
+ */
+ public void addSeparator()
+ {
+ add(new Separator());
+ } // addSeparator()
+
+ /**
+ * This method adds a Separator with the given size to the JToolBar.
+ *
+ * @param size The size of the Separator.
+ */
+ public void addSeparator(Dimension size)
+ {
+ add(new Separator(size));
+ } // addSeparator()
+
+ /**
+ * This method is used to create JButtons which can be added to the JToolBar
+ * for the given action.
+ *
+ * @param action The action to create a JButton for.
+ *
+ * @return The JButton created from the action.
+ */
+ protected JButton createActionComponent(Action action)
+ {
+ return new JButton(action);
+ } // createActionComponent()
+
+ /**
+ * This method creates a pre-configured PropertyChangeListener which updates
+ * the control as changes are made to the Action. However, this is no
+ * longer the recommended way of adding Actions to Containers. As such,
+ * this method returns null.
+ *
+ * @param button The JButton to configure a PropertyChangeListener for.
+ *
+ * @return null.
+ */
+ protected PropertyChangeListener createActionChangeListener(JButton button)
+ {
+ // XXX: As specified, this returns null. But seems kind of strange, usually deprecated methods don't just return null, verify!
+ return null;
+ } // createActionChangeListener()
+
+ /**
+ * This method overrides Container's addImpl method. If a JButton is added,
+ * it is disabled.
+ *
+ * @param component The Component to add.
+ * @param constraints The Constraints placed on the component.
+ * @param index The index to place the Component at.
+ */
+ protected void addImpl(Component component, Object constraints, int index)
+ {
+ // XXX: Sun says disable button but test cases show otherwise.
+ super.addImpl(component, constraints, index);
+
+ // if we added a Swing Button then adjust this a little
+ if (component instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) component;
+ b.setRolloverEnabled(rollover);
+ }
+
+ } // addImpl()
+
+ /**
+ * Returns a string describing the attributes for the <code>JToolBar</code>
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-<code>null</code>, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the <code>JToolBar</code>.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",floatable=").append(floatable);
+ sb.append(",margin=");
+ if (margin != null)
+ sb.append(margin);
+ sb.append(",orientation=");
+ if (orientation == HORIZONTAL)
+ sb.append("HORIZONTAL");
+ else
+ sb.append(VERTICAL);
+ sb.append(",paintBorder=").append(paintBorder);
+ return sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JToolBar</code> component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJToolBar}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJToolBar();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JToolTip.java b/libjava/classpath/javax/swing/JToolTip.java
new file mode 100644
index 000000000..6f226e780
--- /dev/null
+++ b/libjava/classpath/javax/swing/JToolTip.java
@@ -0,0 +1,244 @@
+/* JToolTip.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.AWTEvent;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.ToolTipUI;
+
+/**
+ * This class is used to display ToolTips. ToolTips are small floating windows
+ * that display text when the mouse comes to rest over a Component. ToolTips
+ * are set for JComponents using JComponent.setToolTipText(String).
+ */
+public class JToolTip extends JComponent implements Accessible
+{
+
+ private static final long serialVersionUID = -1138929898906751643L;
+
+ /**
+ * Provides the accessibility features for the <code>JToolTip</code>
+ * component.
+ */
+ protected class AccessibleJToolTip extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = -6222548177795408476L;
+
+ /**
+ * Creates a new AccessibleJToolTip object.
+ */
+ protected AccessibleJToolTip()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a description for the accessible component.
+ *
+ * @return A description for the accessible component.
+ */
+ public String getAccessibleDescription()
+ {
+ String result = super.getAccessibleDescription();
+ if (result == null)
+ result = text;
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the <code>JToolTip</code> component.
+ *
+ * @return {@link AccessibleRole#TOOL_TIP}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TOOL_TIP;
+ }
+ }
+
+ /** The text to display in the JToolTip. */
+ String text;
+
+ /** The component that the tool tip is associated with. */
+ JComponent component;
+
+ /**
+ * Creates a new <code>JToolTip</code> instance.
+ */
+ public JToolTip()
+ {
+ disableEvents(AWTEvent.MOUSE_EVENT_MASK);
+ updateUI();
+ }
+
+ /**
+ * Returns the text displayed by the tool tip.
+ *
+ * @return The text (possibly <code>null</code>).
+ *
+ * @see #setTipText(String)
+ */
+ public String getTipText()
+ {
+ return text;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * <code>JToolTip</code> component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJToolTip}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJToolTip();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns the component that the tool tip is associated with.
+ *
+ * @return The component (possibly <code>null</code>).
+ *
+ * @see #setComponent(JComponent)
+ */
+ public JComponent getComponent()
+ {
+ return component;
+ }
+
+ /**
+ * Returns the current UI delegate for this component.
+ *
+ * @return The UI delegate.
+ */
+ public ToolTipUI getUI()
+ {
+ return (ToolTipUI) ui;
+ }
+
+ /**
+ * Returns the string suffix used to identify the UI class, in this case
+ * <code>"ToolTipUI"</code>.
+ *
+ * @return <code>"ToolTipUI"</code>.
+ */
+ public String getUIClassID()
+ {
+ return "ToolTipUI";
+ }
+
+ /**
+ * Returns a string describing the attributes for the <code>JToolTip</code>
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-<code>null</code>, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the <code>JToolTip</code>.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",tiptext=");
+ if (text != null)
+ sb.append(text);
+ return sb.toString();
+ }
+
+ /**
+ * Sets the component that the tool tip is associated with and sends a
+ * {@link PropertyChangeEvent} (with the property name 'component') to all
+ * registered listeners.
+ *
+ * @param c the component (<code>null</code> permitted).
+ *
+ * @see #getComponent()
+ */
+ public void setComponent(JComponent c)
+ {
+ JComponent oldValue = component;
+ component = c;
+ firePropertyChange("component", oldValue, c);
+ }
+
+ /**
+ * Sets the text to be displayed by the tool tip and sends a
+ * {@link PropertyChangeEvent} (with the property name 'tiptext') to all
+ * registered listeners.
+ *
+ * @param tipText the text (<code>null</code> permitted).
+ *
+ * @see #getTipText()
+ */
+ public void setTipText(String tipText)
+ {
+ String oldValue = text;
+ text = tipText;
+ firePropertyChange("tiptext", oldValue, tipText);
+ }
+
+ /**
+ * This method resets the UI used to the Look and Feel default.
+ */
+ public void updateUI()
+ {
+ setUI((ToolTipUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return true;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java
new file mode 100644
index 000000000..d89adad5e
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTree.java
@@ -0,0 +1,3186 @@
+/* JTree.java
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeExpansionListener;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.event.TreeWillExpandListener;
+import javax.swing.plaf.TreeUI;
+import javax.swing.text.Position;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.DefaultTreeSelectionModel;
+import javax.swing.tree.ExpandVetoException;
+import javax.swing.tree.TreeCellEditor;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+public class JTree extends JComponent implements Scrollable, Accessible
+{
+
+ /**
+ * This class implements accessibility support for the JTree class. It
+ * provides an implementation of the Java Accessibility API appropriate
+ * to tree user-interface elements.
+ */
+ protected class AccessibleJTree extends JComponent.AccessibleJComponent
+ implements AccessibleSelection, TreeSelectionListener, TreeModelListener,
+ TreeExpansionListener
+ {
+
+ /**
+ * This class implements accessibility support for the JTree child. It provides
+ * an implementation of the Java Accessibility API appropriate to tree nodes.
+ */
+ protected class AccessibleJTreeNode extends AccessibleContext
+ implements Accessible, AccessibleComponent, AccessibleSelection,
+ AccessibleAction
+ {
+
+ private JTree tree;
+ private TreePath tp;
+ private Accessible acc;
+ private AccessibleStateSet states;
+ private Vector selectionList;
+ private Vector actionList;
+ private TreeModel mod;
+ private Cursor cursor;
+
+ /**
+ * Constructs an AccessibleJTreeNode
+ *
+ * @param t - the current tree
+ * @param p - the current path to be dealt with
+ * @param ap - the accessible object to use
+ */
+ public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap)
+ {
+ states = new AccessibleStateSet();
+ selectionList = new Vector();
+ actionList = new Vector();
+ mod = tree.getModel();
+ cursor = JTree.this.getCursor();
+
+ tree = t;
+ tp = p;
+ acc = ap;
+
+ // Add all the children of this path that may already be
+ // selected to the selection list.
+ TreePath[] selected = tree.getSelectionPaths();
+ for (int i = 0; i < selected.length; i++)
+ {
+ TreePath sel = selected[i];
+ if ((sel.getParentPath()).equals(tp))
+ selectionList.add(sel);
+ }
+
+ // Add all the actions available for a node to
+ // the action list.
+ actionList.add("EXPAND");
+ actionList.add("COLLAPSE");
+ actionList.add("EDIT");
+ actionList.add("SELECT");
+ actionList.add("DESELECT");
+ }
+
+ /**
+ * Adds the specified selected item in the object to the object's
+ * selection.
+ *
+ * @param i - the i-th child of this node.
+ */
+ public void addAccessibleSelection(int i)
+ {
+ if (mod != null)
+ {
+ Object child = mod.getChild(tp.getLastPathComponent(), i);
+ if (child != null)
+ {
+ if (!states.contains(AccessibleState.MULTISELECTABLE))
+ clearAccessibleSelection();
+ selectionList.add(child);
+ tree.addSelectionPath(tp.pathByAddingChild(child));
+ }
+ }
+ }
+
+ /**
+ * Adds the specified focus listener to receive focus events
+ * from this component.
+ *
+ * @param l - the new focus listener
+ */
+ public void addFocusListener(FocusListener l)
+ {
+ tree.addFocusListener(l);
+ }
+
+ /**
+ * Add a PropertyChangeListener to the listener list.
+ *
+ * @param l - the new property change listener
+ */
+ public void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Clears the selection in the object, so that nothing in the
+ * object is selected.
+ */
+ public void clearAccessibleSelection()
+ {
+ selectionList.clear();
+ }
+
+ /**
+ * Checks whether the specified point is within this object's
+ * bounds, where the point's x and y coordinates are defined to be
+ * relative to the coordinate system of the object.
+ *
+ * @param p - the point to check
+ * @return true if p is in the bounds
+ */
+ public boolean contains(Point p)
+ {
+ return getBounds().contains(p);
+ }
+
+ /**
+ * Perform the specified Action on the tree node.
+ *
+ * @param i - the i-th action to perform
+ * @return true if the the action was performed; else false.
+ */
+ public boolean doAccessibleAction(int i)
+ {
+ if (i >= actionList.size() || i < 0)
+ return false;
+
+ if (actionList.get(i).equals("EXPAND"))
+ tree.expandPath(tp);
+ else if (actionList.get(i).equals("COLLAPSE"))
+ tree.collapsePath(tp);
+ else if (actionList.get(i).equals("SELECT"))
+ tree.addSelectionPath(tp);
+ else if (actionList.get(i).equals("DESELECT"))
+ tree.removeSelectionPath(tp);
+ else if (actionList.get(i).equals("EDIT"))
+ tree.startEditingAtPath(tp);
+ else
+ return false;
+ return true;
+ }
+
+ /**
+ * Get the AccessibleAction associated with this object.
+ *
+ * @return the action
+ */
+ public AccessibleAction getAccessibleAction()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the number of accessible actions available in this tree node.
+ *
+ * @return the number of actions
+ */
+ public int getAccessibleActionCount()
+ {
+ return actionList.size();
+ }
+
+ /**
+ * Return a description of the specified action of the tree node.
+ *
+ * @param i - the i-th action's description
+ * @return a description of the action
+ */
+ public String getAccessibleActionDescription(int i)
+ {
+ if (i < 0 || i >= actionList.size())
+ return (actionList.get(i)).toString();
+ return super.getAccessibleDescription();
+ }
+
+ /**
+ * Returns the Accessible child, if one exists, contained at the
+ * local coordinate Point.
+ *
+ * @param p - the point of the accessible
+ * @return the accessible at point p if it exists
+ */
+ public Accessible getAccessibleAt(Point p)
+ {
+ TreePath acc = tree.getClosestPathForLocation(p.x, p.y);
+ if (acc != null)
+ return new AccessibleJTreeNode(tree, acc, this);
+ return null;
+ }
+
+ /**
+ * Return the specified Accessible child of the object.
+ *
+ * @param i - the i-th child of the current path
+ * @return the child if it exists
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ if (mod != null)
+ {
+ Object child = mod.getChild(tp.getLastPathComponent(), i);
+ if (child != null)
+ return new AccessibleJTreeNode(tree, tp.pathByAddingChild(child),
+ acc);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the number of accessible children in the object.
+ *
+ * @return the number of children the current node has
+ */
+ public int getAccessibleChildrenCount()
+ {
+ TreeModel mod = getModel();
+ if (mod != null)
+ return mod.getChildCount(tp.getLastPathComponent());
+ return 0;
+ }
+
+ /**
+ * Get the AccessibleComponent associated with this object.
+ *
+ * @return the accessible component if it is supported.
+ */
+ public AccessibleComponent getAccessibleComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Get the AccessibleContext associated with this tree node.
+ *
+ * @return an instance of this class
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Get the accessible description of this object.
+ *
+ * @return the accessible description
+ */
+ public String getAccessibleDescription()
+ {
+ return super.getAccessibleDescription();
+ }
+
+ /**
+ * Get the index of this object in its accessible parent.
+ *
+ * @return the index of this in the parent.
+ */
+ public int getAccessibleIndexInParent()
+ {
+ AccessibleContext parent = getAccessibleParent().getAccessibleContext();
+ if (parent != null)
+ for (int i = 0; i < parent.getAccessibleChildrenCount(); i++)
+ {
+ if ((parent.getAccessibleChild(i)).equals(this))
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Get the accessible name of this object.
+ *
+ * @return the accessible name
+ */
+ public String getAccessibleName()
+ {
+ return super.getAccessibleName();
+ }
+
+ /**
+ * Get the Accessible parent of this object.
+ *
+ * @return the accessible parent if it exists.
+ */
+ public Accessible getAccessibleParent()
+ {
+ return super.getAccessibleParent();
+ }
+
+ /**
+ * Get the role of this object.
+ *
+ * @return the accessible role
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleJTree.this.getAccessibleRole();
+ }
+
+ /**
+ * Get the AccessibleSelection associated with this object if one exists.
+ *
+ * @return the accessible selection for this.
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns an Accessible representing the specified selected item
+ * in the object.
+ *
+ * @return the accessible representing a certain selected item.
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ if (i > 0 && i < getAccessibleSelectionCount())
+ return new AccessibleJTreeNode(tree,
+ tp.pathByAddingChild(selectionList.get(i)), acc);
+ return null;
+ }
+
+ /**
+ * Returns the number of items currently selected.
+ *
+ * @return the number of items selected.
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return selectionList.size();
+ }
+
+ /**
+ * Get the state set of this object.
+ *
+ * @return the state set for this object
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ if (isVisible())
+ states.add(AccessibleState.VISIBLE);
+ if (tree.isCollapsed(tp))
+ states.add(AccessibleState.COLLAPSED);
+ if (tree.isEditable())
+ states.add(AccessibleState.EDITABLE);
+ if (mod != null &&
+ !mod.isLeaf(tp.getLastPathComponent()))
+ states.add(AccessibleState.EXPANDABLE);
+ if (tree.isExpanded(tp))
+ states.add(AccessibleState.EXPANDED);
+ if (isFocusable())
+ states.add(AccessibleState.FOCUSABLE);
+ if (hasFocus())
+ states.add(AccessibleState.FOCUSED);
+ if (tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION)
+ states.add(AccessibleState.MULTISELECTABLE);
+ if (tree.isOpaque())
+ states.add(AccessibleState.OPAQUE);
+ if (tree.isPathSelected(tp))
+ states.add(AccessibleState.SELECTED);
+ if (isShowing())
+ states.add(AccessibleState.SHOWING);
+
+ states.add(AccessibleState.SELECTABLE);
+ return states;
+ }
+
+ /**
+ * Get the AccessibleText associated with this object if one exists.
+ *
+ * @return the accessible text
+ */
+ public AccessibleText getAccessibleText()
+ {
+ return super.getAccessibleText();
+ }
+
+ /**
+ * Get the AccessibleValue associated with this object if one exists.
+ *
+ * @return the accessible value if it exists
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return super.getAccessibleValue();
+ }
+
+ /**
+ * Get the background color of this object.
+ *
+ * @return the color of the background.
+ */
+ public Color getBackground()
+ {
+ return tree.getBackground();
+ }
+
+ /**
+ * Gets the bounds of this object in the form of a Rectangle object.
+ *
+ * @return the bounds of the current node.
+ */
+ public Rectangle getBounds()
+ {
+ return tree.getPathBounds(tp);
+ }
+
+ /**
+ * Gets the Cursor of this object.
+ *
+ * @return the cursor for the current node
+ */
+ public Cursor getCursor()
+ {
+ return cursor;
+ }
+
+ /**
+ * Gets the Font of this object.
+ *
+ * @return the font for the current node
+ */
+ public Font getFont()
+ {
+ return tree.getFont();
+ }
+
+ /**
+ * Gets the FontMetrics of this object.
+ *
+ * @param f - the current font.
+ * @return the font metrics for the given font.
+ */
+ public FontMetrics getFontMetrics(Font f)
+ {
+ return tree.getFontMetrics(f);
+ }
+
+ /**
+ * Get the foreground color of this object.
+ *
+ * @return the foreground for this object.
+ */
+ public Color getForeground()
+ {
+ return tree.getForeground();
+ }
+
+ /**
+ * Gets the locale of the component.
+ *
+ * @return the locale of the component.
+ */
+ public Locale getLocale()
+ {
+ return tree.getLocale();
+ }
+
+ /**
+ * Gets the location of the object relative to the
+ * parent in the form of a point specifying the object's
+ * top-left corner in the screen's coordinate space.
+ *
+ * @return the location of the current node.
+ */
+ public Point getLocation()
+ {
+ return getLocationInJTree();
+ }
+
+ /**
+ * Returns the location in the tree.
+ *
+ * @return the location in the JTree.
+ */
+ protected Point getLocationInJTree()
+ {
+ Rectangle bounds = tree.getPathBounds(tp);
+ return new Point(bounds.x, bounds.y);
+ }
+
+ /**
+ * Returns the location of the object on the screen.
+ *
+ * @return the location of the object on the screen.
+ */
+ public Point getLocationOnScreen()
+ {
+ Point loc = getLocation();
+ SwingUtilities.convertPointToScreen(loc, tree);
+ return loc;
+ }
+
+ /**
+ * Returns the size of this object in the form of a Dimension object.
+ *
+ * @return the size of the object
+ */
+ public Dimension getSize()
+ {
+ Rectangle b = getBounds();
+ return b.getSize();
+ }
+
+ /**
+ * Returns true if the current child of this object is selected.
+ *
+ * @param i - the child of the current node
+ * @return true if the child is selected.
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ Object child = mod.getChild(tp.getLastPathComponent(), i);
+ if (child != null)
+ return tree.isPathSelected(tp.pathByAddingChild(child));
+ return false;
+ }
+
+ /**
+ * Determines if the object is enabled.
+ *
+ * @return true if the tree is enabled
+ */
+ public boolean isEnabled()
+ {
+ return tree.isEnabled();
+ }
+
+ /**
+ * Returns whether this object can accept focus or not.
+ *
+ * @return true, it is always focus traversable
+ */
+ public boolean isFocusTraversable()
+ {
+ return true;
+ }
+
+ /**
+ * Determines if the object is showing.
+ *
+ * @return true if the object is visible and the
+ * parent is visible.
+ */
+ public boolean isShowing()
+ {
+ return isVisible() && tree.isShowing();
+ }
+
+ /**
+ * Determines if the object is visible.
+ *
+ * @return true if the object is visible.
+ */
+ public boolean isVisible()
+ {
+ return tree.isVisible(tp);
+ }
+
+ /**
+ * Removes the specified selected item in the object from the
+ * object's selection.
+ *
+ * @param i - the specified item to remove
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ if (mod != null)
+ {
+ Object child = mod.getChild(tp.getLastPathComponent(), i);
+ if (child != null)
+ {
+ if (!states.contains(AccessibleState.MULTISELECTABLE))
+ clearAccessibleSelection();
+ if (selectionList.contains(child))
+ {
+ selectionList.remove(child);
+ tree.removeSelectionPath(tp.pathByAddingChild(child));
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes the specified focus listener so it no longer receives focus
+ * events from this component.
+ *
+ * @param l - the focus listener to remove
+ */
+ public void removeFocusListener(FocusListener l)
+ {
+ tree.removeFocusListener(l);
+ }
+
+ /**
+ * Remove a PropertyChangeListener from the listener list.
+ *
+ * @param l - the property change listener to remove.
+ */
+ public void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Requests focus for this object.
+ */
+ public void requestFocus()
+ {
+ tree.requestFocus();
+ }
+
+ /**
+ * Causes every selected item in the object to be selected if the object
+ * supports multiple selections.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ Object parent = tp.getLastPathComponent();
+ if (mod != null)
+ {
+ for (int i = 0; i < mod.getChildCount(parent); i++)
+ {
+ Object child = mod.getChild(parent, i);
+ if (child != null)
+ {
+ if (!states.contains(AccessibleState.MULTISELECTABLE))
+ clearAccessibleSelection();
+ if (selectionList.contains(child))
+ {
+ selectionList.add(child);
+ tree.addSelectionPath(tp.pathByAddingChild(child));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the accessible description of this object.
+ *
+ * @param s - the string to set the accessible description to.
+ */
+ public void setAccessibleDescription(String s)
+ {
+ super.setAccessibleDescription(s);
+ }
+
+ /**
+ * Set the localized accessible name of this object.
+ *
+ * @param s - the string to set the accessible name to.
+ */
+ public void setAccessibleName(String s)
+ {
+ super.setAccessibleName(s);
+ }
+
+ /**
+ * Set the background color of this object.
+ *
+ * @param c - the color to set the background to.
+ */
+ public void setBackground(Color c)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the bounds of this object in the form of a Rectangle object.
+ *
+ * @param r - the bounds to set the object o
+ */
+ public void setBounds(Rectangle r)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the Cursor of this object.
+ *
+ * @param c - the new cursor
+ */
+ public void setCursor(Cursor c)
+ {
+ cursor = c;
+ }
+
+ /**
+ * Sets the enabled state of the object.
+ *
+ * @param b - boolean to enable or disable object
+ */
+ public void setEnabled(boolean b)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the Font of this object.
+ *
+ * @param f - the new font.
+ */
+ public void setFont(Font f)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the foreground color of this object.
+ *
+ * @param c - the new foreground color.
+ */
+ public void setForeground(Color c)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the location of the object relative to the parent.
+ *
+ * @param p - the new location for the object.
+ */
+ public void setLocation(Point p)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Resizes this object so that it has width and height.
+ *
+ * @param d - the new size for the object.
+ */
+ public void setSize(Dimension d)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the visible state of the object.
+ *
+ * @param b - sets the objects visibility.
+ */
+ public void setVisible(boolean b)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public AccessibleJTree()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds the specified selected item in the object to the object's selection.
+ *
+ * @param i - the row to add to the tree's selection
+ */
+ public void addAccessibleSelection(int i)
+ {
+ addSelectionInterval(i, i);
+ }
+
+ /**
+ * Clears the selection in the object, so that nothing in the object is selected.
+ */
+ public void clearAccessibleSelection()
+ {
+ clearSelection();
+ }
+
+ /**
+ * Fire a visible data property change notification.
+ */
+ public void fireVisibleDataPropertyChange()
+ {
+ treeDidChange();
+ }
+
+ /**
+ * Returns the Accessible child, if one exists, contained at the local
+ * coordinate Point.
+ *
+ * @param p - the point of the accessible to get.
+ * @return the accessible at point p.
+ */
+ public Accessible getAccessibleAt(Point p)
+ {
+ TreePath tp = getClosestPathForLocation(p.x, p.y);
+ if (tp != null)
+ return new AccessibleJTreeNode(JTree.this, tp, null);
+ return null;
+ }
+
+ /**
+ * Return the nth Accessible child of the object.
+ *
+ * @param i - the accessible child to get
+ * @return the i-th child
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the number of top-level children nodes of this JTree.
+ *
+ * @return the number of top-level children
+ */
+ public int getAccessibleChildrenCount()
+ {
+ TreeModel model = getModel();
+ if (model != null)
+ return model.getChildCount(model.getRoot());
+ return 0;
+ }
+
+ /**
+ * Get the index of this object in its accessible parent.
+ *
+ * @return the index of this object.
+ */
+ public int getAccessibleIndexInParent()
+ {
+ return 0;
+ }
+
+ /**
+ * Get the role of this object.
+ *
+ * @return the role of this object
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TREE;
+ }
+
+ /**
+ * Get the AccessibleSelection associated with this object.
+ *
+ * @return the accessible selection of the tree
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ TreeModel mod = getModel();
+ if (mod != null)
+ return (new AccessibleJTreeNode(JTree.this,
+ new TreePath(mod.getRoot()), null)).getAccessibleSelection();
+ return null;
+ }
+
+ /**
+ * Returns an Accessible representing the specified selected item in the object.
+ *
+ * @return the i-th accessible in the selection
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ TreeModel mod = getModel();
+ if (mod != null)
+ return (new AccessibleJTreeNode(JTree.this,
+ new TreePath(mod.getRoot()), null)).getAccessibleSelection(i);
+ return null;
+ }
+
+ /**
+ * Returns the number of items currently selected.
+ *
+ * @return the number of selected accessibles.
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return getSelectionCount();
+ }
+
+ /**
+ * Returns true if the current child of this object is selected.
+ *
+ * @param i - the child of this object
+ * @return true if the i-th child is selected.
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ // Nothing to do here.
+ return false;
+ }
+
+ /**
+ * Removes the specified selected item in the object from the object's
+ * selection.
+ *
+ * @param i - the i-th selected item to remove
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ removeSelectionInterval(i, i);
+ }
+
+ /**
+ * Causes every selected item in the object to be selected if the object
+ * supports multiple selections.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ if (getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION)
+ addSelectionInterval(0, getVisibleRowCount());
+ }
+
+ /**
+ * Tree Collapsed notification
+ *
+ * @param e - the event
+ */
+ public void treeCollapsed(TreeExpansionEvent e)
+ {
+ fireTreeCollapsed(e.getPath());
+ }
+
+ /**
+ * Tree Model Expansion notification.
+ *
+ * @param e - the event
+ */
+ public void treeExpanded(TreeExpansionEvent e)
+ {
+ fireTreeExpanded(e.getPath());
+ }
+
+ /**
+ * Tree Model Node change notification.
+ *
+ * @param e - the event
+ */
+ public void treeNodesChanged(TreeModelEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Tree Model Node change notification.
+ *
+ * @param e - the event
+ */
+ public void treeNodesInserted(TreeModelEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Tree Model Node change notification.
+ *
+ * @param e - the event
+ */
+ public void treeNodesRemoved(TreeModelEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Tree Model structure change change notification.
+ *
+ * @param e - the event
+ */
+ public void treeStructureChanged(TreeModelEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Tree Selection Listener value change method.
+ *
+ * @param e - the event
+ */
+ public void valueChanged(TreeSelectionEvent e)
+ {
+ fireValueChanged(e);
+ }
+ }
+
+ public static class DynamicUtilTreeNode extends DefaultMutableTreeNode
+ {
+ protected Object childValue;
+
+ protected boolean loadedChildren;
+
+ /**
+ * Currently not set or used by this class. It might be set and used in
+ * later versions of this class.
+ */
+ protected boolean hasChildren;
+
+ public DynamicUtilTreeNode(Object value, Object children)
+ {
+ super(value);
+ childValue = children;
+ loadedChildren = false;
+ }
+
+ public int getChildCount()
+ {
+ loadChildren();
+ return super.getChildCount();
+ }
+
+ protected void loadChildren()
+ {
+ if (!loadedChildren)
+ {
+ createChildren(this, childValue);
+ loadedChildren = true;
+ }
+ }
+
+ public Enumeration children()
+ {
+ loadChildren();
+ return super.children();
+ }
+
+ /**
+ * Returns the child node at position <code>pos</code>. Subclassed
+ * here to load the children if necessary.
+ *
+ * @param pos the position of the child node to fetch
+ *
+ * @return the childnode at the specified position
+ */
+ public TreeNode getChildAt(int pos)
+ {
+ loadChildren();
+ return super.getChildAt(pos);
+ }
+
+ public boolean isLeaf()
+ {
+ return childValue == null || !(childValue instanceof Hashtable
+ || childValue instanceof Vector
+ || childValue.getClass().isArray());
+ }
+
+ public static void createChildren(DefaultMutableTreeNode parent,
+ Object children)
+ {
+ if (children instanceof Hashtable)
+ {
+ Hashtable tab = (Hashtable) children;
+ Enumeration e = tab.keys();
+ while (e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ Object val = tab.get(key);
+ parent.add(new DynamicUtilTreeNode(key, val));
+ }
+ }
+ else if (children instanceof Vector)
+ {
+ Iterator i = ((Vector) children).iterator();
+ while (i.hasNext())
+ {
+ Object n = i.next();
+ parent.add(new DynamicUtilTreeNode(n, n));
+ }
+ }
+ else if (children != null && children.getClass().isArray())
+ {
+ Object[] arr = (Object[]) children;
+ for (int i = 0; i < arr.length; ++i)
+ parent.add(new DynamicUtilTreeNode(arr[i], arr[i]));
+ }
+ }
+ }
+
+ /**
+ * Listens to the model of the JTree and updates the property
+ * <code>expandedState</code> if nodes are removed or changed.
+ */
+ protected class TreeModelHandler implements TreeModelListener
+ {
+
+ /**
+ * Creates a new instance of TreeModelHandler.
+ */
+ protected TreeModelHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notifies when a node has changed in some ways. This does not include
+ * that a node has changed its location or changed it's children. It
+ * only means that some attributes of the node have changed that might
+ * affect its presentation.
+ *
+ * This method is called after the actual change occured.
+ *
+ * @param ev the TreeModelEvent describing the change
+ */
+ public void treeNodesChanged(TreeModelEvent ev)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notifies when a node is inserted into the tree.
+ *
+ * This method is called after the actual change occured.
+ *
+ * @param ev the TreeModelEvent describing the change
+ */
+ public void treeNodesInserted(TreeModelEvent ev)
+ {
+ // nothing to do here
+ }
+
+ /**
+ * Notifies when a node is removed from the tree.
+ *
+ * This method is called after the actual change occured.
+ *
+ * @param ev the TreeModelEvent describing the change
+ */
+ public void treeNodesRemoved(TreeModelEvent ev)
+ {
+ if (ev != null)
+ {
+ TreePath parent = ev.getTreePath();
+ Object[] children = ev.getChildren();
+ TreeSelectionModel sm = getSelectionModel();
+ if (children != null)
+ {
+ TreePath path;
+ Vector toRemove = new Vector();
+ // Collect items that we must remove.
+ for (int i = children.length - 1; i >= 0; i--)
+ {
+ path = parent.pathByAddingChild(children[i]);
+ if (nodeStates.containsKey(path))
+ toRemove.add(path);
+ // Clear selection while we are at it.
+ if (sm != null)
+ removeDescendantSelectedPaths(path, true);
+ }
+ if (toRemove.size() > 0)
+ removeDescendantToggledPaths(toRemove.elements());
+ TreeModel model = getModel();
+ if (model == null || model.isLeaf(parent.getLastPathComponent()))
+ nodeStates.remove(parent);
+ }
+ }
+ }
+
+ /**
+ * Notifies when the structure of the tree is changed.
+ *
+ * This method is called after the actual change occured.
+ *
+ * @param ev the TreeModelEvent describing the change
+ */
+ public void treeStructureChanged(TreeModelEvent ev)
+ {
+ if (ev != null)
+ {
+ TreePath parent = ev.getTreePath();
+ if (parent != null)
+ {
+ if (parent.getPathCount() == 1)
+ {
+ // We have a new root, clear everything.
+ clearToggledPaths();
+ Object root = treeModel.getRoot();
+ if (root != null && treeModel.isLeaf(root))
+ nodeStates.put(parent, Boolean.TRUE);
+ }
+ else if (nodeStates.containsKey(parent))
+ {
+ Vector toRemove = new Vector();
+ boolean expanded = isExpanded(parent);
+ toRemove.add(parent);
+ removeDescendantToggledPaths(toRemove.elements());
+ if (expanded)
+ {
+ TreeModel model = getModel();
+ if (model != null
+ || model.isLeaf(parent.getLastPathComponent()))
+ collapsePath(parent);
+ else
+ nodeStates.put(parent, Boolean.TRUE);
+ }
+ }
+ removeDescendantSelectedPaths(parent, false);
+ }
+ }
+ }
+ }
+
+ /**
+ * This redirects TreeSelectionEvents and rewrites the source of it to be
+ * this JTree. This is typically done when the tree model generates an
+ * event, but the JTree object associated with that model should be listed
+ * as the actual source of the event.
+ */
+ protected class TreeSelectionRedirector implements TreeSelectionListener,
+ Serializable
+ {
+ /** The serial version UID. */
+ private static final long serialVersionUID = -3505069663646241664L;
+
+ /**
+ * Creates a new instance of TreeSelectionRedirector
+ */
+ protected TreeSelectionRedirector()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notifies when the tree selection changes.
+ *
+ * @param ev the TreeSelectionEvent that describes the change
+ */
+ public void valueChanged(TreeSelectionEvent ev)
+ {
+ TreeSelectionEvent rewritten =
+ (TreeSelectionEvent) ev.cloneWithSource(JTree.this);
+ fireValueChanged(rewritten);
+ }
+ }
+
+ /**
+ * A TreeModel that does not allow anything to be selected.
+ */
+ protected static class EmptySelectionModel extends DefaultTreeSelectionModel
+ {
+ /** The serial version UID. */
+ private static final long serialVersionUID = -5815023306225701477L;
+
+ /**
+ * The shared instance of this model.
+ */
+ protected static final EmptySelectionModel sharedInstance =
+ new EmptySelectionModel();
+
+ /**
+ * Creates a new instance of EmptySelectionModel.
+ */
+ protected EmptySelectionModel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the shared instance of EmptySelectionModel.
+ *
+ * @return the shared instance of EmptySelectionModel
+ */
+ public static EmptySelectionModel sharedInstance()
+ {
+ return sharedInstance;
+ }
+
+ /**
+ * This catches attempts to set a selection and sets nothing instead.
+ *
+ * @param paths not used here
+ */
+ public void setSelectionPaths(TreePath[] paths)
+ {
+ // We don't allow selections in this class.
+ }
+
+ /**
+ * This catches attempts to add something to the selection.
+ *
+ * @param paths not used here
+ */
+ public void addSelectionPaths(TreePath[] paths)
+ {
+ // We don't allow selections in this class.
+ }
+
+ /**
+ * This catches attempts to remove something from the selection.
+ *
+ * @param paths not used here
+ */
+ public void removeSelectionPaths(TreePath[] paths)
+ {
+ // We don't allow selections in this class.
+ }
+ }
+
+ private static final long serialVersionUID = 7559816092864483649L;
+
+ public static final String CELL_EDITOR_PROPERTY = "cellEditor";
+
+ public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
+
+ public static final String EDITABLE_PROPERTY = "editable";
+
+ public static final String INVOKES_STOP_CELL_EDITING_PROPERTY =
+ "invokesStopCellEditing";
+
+ public static final String LARGE_MODEL_PROPERTY = "largeModel";
+
+ public static final String ROOT_VISIBLE_PROPERTY = "rootVisible";
+
+ public static final String ROW_HEIGHT_PROPERTY = "rowHeight";
+
+ public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
+
+ public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
+
+ public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
+
+ public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
+
+ public static final String TREE_MODEL_PROPERTY = "model";
+
+ public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
+
+ /** @since 1.3 */
+ public static final String ANCHOR_SELECTION_PATH_PROPERTY =
+ "anchorSelectionPath";
+
+ /** @since 1.3 */
+ public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
+
+ /** @since 1.3 */
+ public static final String EXPANDS_SELECTED_PATHS_PROPERTY =
+ "expandsSelectedPaths";
+
+ private static final Object EXPANDED = Boolean.TRUE;
+
+ private static final Object COLLAPSED = Boolean.FALSE;
+
+ private boolean dragEnabled;
+
+ private boolean expandsSelectedPaths;
+
+ private TreePath anchorSelectionPath;
+
+ /**
+ * This contains the state of all nodes in the tree. Al/ entries map the
+ * TreePath of a note to to its state. Valid states are EXPANDED and
+ * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Hashtable nodeStates = new Hashtable();
+
+ protected transient TreeCellEditor cellEditor;
+
+ protected transient TreeCellRenderer cellRenderer;
+
+ protected boolean editable;
+
+ protected boolean invokesStopCellEditing;
+
+ protected boolean largeModel;
+
+ protected boolean rootVisible;
+
+ protected int rowHeight;
+
+ protected boolean scrollsOnExpand;
+
+ protected transient TreeSelectionModel selectionModel;
+
+ protected boolean showsRootHandles;
+
+ protected int toggleClickCount;
+
+ protected transient TreeModel treeModel;
+
+ protected int visibleRowCount;
+
+ /**
+ * Handles TreeModelEvents to update the expandedState.
+ */
+ protected transient TreeModelListener treeModelListener;
+
+ /**
+ * Redirects TreeSelectionEvents so that the source is this JTree.
+ */
+ protected TreeSelectionRedirector selectionRedirector =
+ new TreeSelectionRedirector();
+
+ /**
+ * Indicates if the rowHeight property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientRowHeightSet = false;
+
+ /**
+ * Indicates if the scrollsOnExpand property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientScrollsOnExpandSet = false;
+
+ /**
+ * Indicates if the showsRootHandles property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientShowsRootHandlesSet = false;
+
+ /**
+ * Creates a new <code>JTree</code> object.
+ */
+ public JTree()
+ {
+ this(getDefaultTreeModel());
+ }
+
+ /**
+ * Creates a new <code>JTree</code> object.
+ *
+ * @param value the initial nodes in the tree
+ */
+ public JTree(Hashtable<?, ?> value)
+ {
+ this(createTreeModel(value));
+ }
+
+ /**
+ * Creates a new <code>JTree</code> object.
+ *
+ * @param value the initial nodes in the tree
+ */
+ public JTree(Object[] value)
+ {
+ this(createTreeModel(value));
+ }
+
+ /**
+ * Creates a new <code>JTree</code> object.
+ *
+ * @param model the model to use
+ */
+ public JTree(TreeModel model)
+ {
+ setRootVisible(true);
+ setSelectionModel( new DefaultTreeSelectionModel() );
+
+ // The root node appears expanded by default.
+ nodeStates = new Hashtable();
+
+ // The cell renderer gets set by the UI.
+ cellRenderer = null;
+
+ // Install the UI before installing the model. This way we avoid double
+ // initialization of lots of UI and model stuff inside the UI and related
+ // classes. The necessary UI updates are performed via property change
+ // events to the UI.
+ updateUI();
+ setModel(model);
+ }
+
+ /**
+ * Creates a new <code>JTree</code> object.
+ *
+ * @param root the root node
+ */
+ public JTree(TreeNode root)
+ {
+ this(root, false);
+ }
+
+ /**
+ * Creates a new <code>JTree</code> object.
+ *
+ * @param root the root node
+ * @param asksAllowChildren if false, all nodes without children are leaf
+ * nodes. If true, only nodes that do not allow children are leaf
+ * nodes.
+ */
+ public JTree(TreeNode root, boolean asksAllowChildren)
+ {
+ this(new DefaultTreeModel(root, asksAllowChildren));
+ }
+
+ /**
+ * Creates a new <code>JTree</code> object.
+ *
+ * @param value the initial nodes in the tree
+ */
+ public JTree(Vector<?> value)
+ {
+ this(createTreeModel(value));
+ }
+
+ public int getRowForPath(TreePath path)
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.getRowForPath(this, path);
+
+ return -1;
+ }
+
+ public TreePath getPathForRow(int row)
+ {
+ TreeUI ui = getUI();
+ return ui != null ? ui.getPathForRow(this, row) : null;
+ }
+
+ /**
+ * Get the pathes that are displayes between the two given rows.
+ *
+ * @param index0 the starting row, inclusive
+ * @param index1 the ending row, inclusive
+ *
+ * @return the array of the tree pathes
+ */
+ protected TreePath[] getPathBetweenRows(int index0, int index1)
+ {
+ TreeUI ui = getUI();
+
+ if (ui == null)
+ return null;
+
+ int minIndex = Math.min(index0, index1);
+ int maxIndex = Math.max(index0, index1);
+ TreePath[] paths = new TreePath[maxIndex - minIndex + 1];
+
+ for (int i = minIndex; i <= maxIndex; ++i)
+ paths[i - minIndex] = ui.getPathForRow(this, i);
+
+ return paths;
+ }
+
+ /**
+ * Creates a new <code>TreeModel</code> object.
+ *
+ * @param value the values stored in the model
+ */
+ protected static TreeModel createTreeModel(Object value)
+ {
+ return new DefaultTreeModel(new DynamicUtilTreeNode(value, value));
+ }
+
+ /**
+ * Return the UI associated with this <code>JTree</code> object.
+ *
+ * @return the associated <code>TreeUI</code> object
+ */
+ public TreeUI getUI()
+ {
+ return (TreeUI) ui;
+ }
+
+ /**
+ * Sets the UI associated with this <code>JTree</code> object.
+ *
+ * @param ui the <code>TreeUI</code> to associate
+ */
+ public void setUI(TreeUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method resets the UI used to the Look and Feel defaults..
+ */
+ public void updateUI()
+ {
+ setUI((TreeUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the String ID of the UI class of Separator.
+ *
+ * @return The UI class' String ID.
+ */
+ public String getUIClassID()
+ {
+ return "TreeUI";
+ }
+
+ /**
+ * Gets the AccessibleContext associated with this
+ * <code>JTree</code>.
+ *
+ * @return the associated context
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return new AccessibleJTree();
+ }
+
+ /**
+ * Returns the preferred viewport size.
+ *
+ * @return the preferred size
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ return getPreferredSize();
+ }
+
+ /**
+ * Return the preferred scrolling amount (in pixels) for the given scrolling
+ * direction and orientation. This method handles a partially exposed row by
+ * returning the distance required to completely expose the item.
+ *
+ * @param visibleRect the currently visible part of the component.
+ * @param orientation the scrolling orientation
+ * @param direction the scrolling direction (negative - up, positive -down).
+ * The values greater than one means that more mouse wheel or similar
+ * events were generated, and hence it is better to scroll the longer
+ * distance.
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
+ int direction)
+ {
+ int delta = 0;
+
+ // Round so that the top would start from the row boundary
+ if (orientation == SwingConstants.VERTICAL)
+ {
+ int row = getClosestRowForLocation(0, visibleRect.y);
+ if (row != -1)
+ {
+ Rectangle b = getRowBounds(row);
+ if (b.y != visibleRect.y)
+ {
+ if (direction < 0)
+ delta = Math.max(0, visibleRect.y - b.y);
+ else
+ delta = b.y + b.height - visibleRect.y;
+ }
+ else
+ {
+ if (direction < 0)
+ {
+ if (row != 0)
+ {
+ b = getRowBounds(row - 1);
+ delta = b.height;
+ }
+ }
+ else
+ delta = b.height;
+ }
+ }
+ }
+ else
+ // The RI always returns 4 for HORIZONTAL scrolling.
+ delta = 4;
+ return delta;
+ }
+
+ public int getScrollableBlockIncrement(Rectangle visibleRect,
+ int orientation, int direction)
+ {
+ int block;
+ if (orientation == SwingConstants.VERTICAL)
+ block = visibleRect.height;
+ else
+ block = visibleRect.width;
+ return block;
+ }
+
+ public boolean getScrollableTracksViewportHeight()
+ {
+ if (getParent() instanceof JViewport)
+ return ((JViewport) getParent()).getHeight() > getPreferredSize().height;
+ return false;
+ }
+
+ public boolean getScrollableTracksViewportWidth()
+ {
+ if (getParent() instanceof JViewport)
+ return ((JViewport) getParent()).getWidth() > getPreferredSize().width;
+ return false;
+ }
+
+ /**
+ * Adds a <code>TreeExpansionListener</code> object to the tree.
+ *
+ * @param listener the listener to add
+ */
+ public void addTreeExpansionListener(TreeExpansionListener listener)
+ {
+ listenerList.add(TreeExpansionListener.class, listener);
+ }
+
+ /**
+ * Removes a <code>TreeExpansionListener</code> object from the tree.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeTreeExpansionListener(TreeExpansionListener listener)
+ {
+ listenerList.remove(TreeExpansionListener.class, listener);
+ }
+
+ /**
+ * Returns all added <code>TreeExpansionListener</code> objects.
+ *
+ * @return an array of listeners
+ */
+ public TreeExpansionListener[] getTreeExpansionListeners()
+ {
+ return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class);
+ }
+
+ /**
+ * Notifies all listeners that the tree was collapsed.
+ *
+ * @param path the path to the node that was collapsed
+ */
+ public void fireTreeCollapsed(TreePath path)
+ {
+ TreeExpansionEvent event = new TreeExpansionEvent(this, path);
+ TreeExpansionListener[] listeners = getTreeExpansionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].treeCollapsed(event);
+ }
+
+ /**
+ * Notifies all listeners that the tree was expanded.
+ *
+ * @param path the path to the node that was expanded
+ */
+ public void fireTreeExpanded(TreePath path)
+ {
+ TreeExpansionEvent event = new TreeExpansionEvent(this, path);
+ TreeExpansionListener[] listeners = getTreeExpansionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].treeExpanded(event);
+ }
+
+ /**
+ * Adds a <code>TreeSelctionListener</code> object to the tree.
+ *
+ * @param listener the listener to add
+ */
+ public void addTreeSelectionListener(TreeSelectionListener listener)
+ {
+ listenerList.add(TreeSelectionListener.class, listener);
+ }
+
+ /**
+ * Removes a <code>TreeSelectionListener</code> object from the tree.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeTreeSelectionListener(TreeSelectionListener listener)
+ {
+ listenerList.remove(TreeSelectionListener.class, listener);
+ }
+
+ /**
+ * Returns all added <code>TreeSelectionListener</code> objects.
+ *
+ * @return an array of listeners
+ */
+ public TreeSelectionListener[] getTreeSelectionListeners()
+ {
+ return (TreeSelectionListener[])
+ getListeners(TreeSelectionListener.class);
+ }
+
+ /**
+ * Notifies all listeners when the selection of the tree changed.
+ *
+ * @param event the event to send
+ */
+ protected void fireValueChanged(TreeSelectionEvent event)
+ {
+ TreeSelectionListener[] listeners = getTreeSelectionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].valueChanged(event);
+ }
+
+ /**
+ * Adds a <code>TreeWillExpandListener</code> object to the tree.
+ *
+ * @param listener the listener to add
+ */
+ public void addTreeWillExpandListener(TreeWillExpandListener listener)
+ {
+ listenerList.add(TreeWillExpandListener.class, listener);
+ }
+
+ /**
+ * Removes a <code>TreeWillExpandListener</code> object from the tree.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeTreeWillExpandListener(TreeWillExpandListener listener)
+ {
+ listenerList.remove(TreeWillExpandListener.class, listener);
+ }
+
+ /**
+ * Returns all added <code>TreeWillExpandListener</code> objects.
+ *
+ * @return an array of listeners
+ */
+ public TreeWillExpandListener[] getTreeWillExpandListeners()
+ {
+ return (TreeWillExpandListener[])
+ getListeners(TreeWillExpandListener.class);
+ }
+
+ /**
+ * Notifies all listeners that the tree will collapse.
+ *
+ * @param path the path to the node that will collapse
+ */
+ public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException
+ {
+ TreeExpansionEvent event = new TreeExpansionEvent(this, path);
+ TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].treeWillCollapse(event);
+ }
+
+ /**
+ * Notifies all listeners that the tree will expand.
+ *
+ * @param path the path to the node that will expand
+ */
+ public void fireTreeWillExpand(TreePath path) throws ExpandVetoException
+ {
+ TreeExpansionEvent event = new TreeExpansionEvent(this, path);
+ TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].treeWillExpand(event);
+ }
+
+ /**
+ * Returns the model of this <code>JTree</code> object.
+ *
+ * @return the associated <code>TreeModel</code>
+ */
+ public TreeModel getModel()
+ {
+ return treeModel;
+ }
+
+ /**
+ * Sets the model to use in <code>JTree</code>.
+ *
+ * @param model the <code>TreeModel</code> to use
+ */
+ public void setModel(TreeModel model)
+ {
+ if (treeModel == model)
+ return;
+
+ // Remove listeners from old model.
+ if (treeModel != null && treeModelListener != null)
+ treeModel.removeTreeModelListener(treeModelListener);
+
+ // add treeModelListener to the new model
+ if (treeModelListener == null)
+ treeModelListener = createTreeModelListener();
+ if (model != null) // as setModel(null) is allowed
+ model.addTreeModelListener(treeModelListener);
+
+ TreeModel oldValue = treeModel;
+ treeModel = model;
+ clearToggledPaths();
+
+ if (treeModel != null)
+ {
+ if (treeModelListener == null)
+ treeModelListener = createTreeModelListener();
+ if (treeModelListener != null)
+ treeModel.addTreeModelListener(treeModelListener);
+ Object root = treeModel.getRoot();
+ if (root != null && !treeModel.isLeaf(root))
+ {
+ nodeStates.put(new TreePath(root), Boolean.TRUE);
+ }
+ }
+
+ firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model);
+ }
+
+ /**
+ * Checks if this <code>JTree</code> object is editable.
+ *
+ * @return <code>true</code> if this tree object is editable,
+ * <code>false</code> otherwise
+ */
+ public boolean isEditable()
+ {
+ return editable;
+ }
+
+ /**
+ * Sets the <code>editable</code> property.
+ *
+ * @param flag <code>true</code> to make this tree object editable,
+ * <code>false</code> otherwise
+ */
+ public void setEditable(boolean flag)
+ {
+ if (editable == flag)
+ return;
+
+ boolean oldValue = editable;
+ editable = flag;
+ firePropertyChange(EDITABLE_PROPERTY, oldValue, editable);
+ }
+
+ /**
+ * Checks if the root element is visible.
+ *
+ * @return <code>true</code> if the root element is visible,
+ * <code>false</code> otherwise
+ */
+ public boolean isRootVisible()
+ {
+ return rootVisible;
+ }
+
+ public void setRootVisible(boolean flag)
+ {
+ if (rootVisible == flag)
+ return;
+
+ // If the root is currently selected, unselect it
+ if (rootVisible && !flag)
+ {
+ TreeSelectionModel model = getSelectionModel();
+ // The root is always shown in the first row
+ TreePath rootPath = getPathForRow(0);
+ model.removeSelectionPath(rootPath);
+ }
+
+ boolean oldValue = rootVisible;
+ rootVisible = flag;
+ firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag);
+
+ }
+
+ public boolean getShowsRootHandles()
+ {
+ return showsRootHandles;
+ }
+
+ public void setShowsRootHandles(boolean flag)
+ {
+ clientShowsRootHandlesSet = true;
+
+ if (showsRootHandles == flag)
+ return;
+
+ boolean oldValue = showsRootHandles;
+ showsRootHandles = flag;
+ firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag);
+ }
+
+ public TreeCellEditor getCellEditor()
+ {
+ return cellEditor;
+ }
+
+ public void setCellEditor(TreeCellEditor editor)
+ {
+ if (cellEditor == editor)
+ return;
+
+ TreeCellEditor oldValue = cellEditor;
+ cellEditor = editor;
+ firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor);
+ }
+
+ public TreeCellRenderer getCellRenderer()
+ {
+ return cellRenderer;
+ }
+
+ public void setCellRenderer(TreeCellRenderer newRenderer)
+ {
+ if (cellRenderer == newRenderer)
+ return;
+
+ TreeCellRenderer oldValue = cellRenderer;
+ cellRenderer = newRenderer;
+ firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer);
+ }
+
+ public TreeSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ public void setSelectionModel(TreeSelectionModel model)
+ {
+ if (selectionModel == model)
+ return;
+
+ if( model == null )
+ model = EmptySelectionModel.sharedInstance();
+
+ if (selectionModel != null)
+ selectionModel.removeTreeSelectionListener(selectionRedirector);
+
+ TreeSelectionModel oldValue = selectionModel;
+ selectionModel = model;
+
+ selectionModel.addTreeSelectionListener(selectionRedirector);
+
+ firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model);
+ revalidate();
+ repaint();
+ }
+
+ public int getVisibleRowCount()
+ {
+ return visibleRowCount;
+ }
+
+ public void setVisibleRowCount(int rows)
+ {
+ if (visibleRowCount == rows)
+ return;
+
+ int oldValue = visibleRowCount;
+ visibleRowCount = rows;
+ firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows);
+ }
+
+ public boolean isLargeModel()
+ {
+ return largeModel;
+ }
+
+ public void setLargeModel(boolean large)
+ {
+ if (largeModel == large)
+ return;
+
+ boolean oldValue = largeModel;
+ largeModel = large;
+ firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large);
+ }
+
+ public int getRowHeight()
+ {
+ return rowHeight;
+ }
+
+ public void setRowHeight(int height)
+ {
+ clientRowHeightSet = true;
+
+ if (rowHeight == height)
+ return;
+
+ int oldValue = rowHeight;
+ rowHeight = height;
+ firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height);
+ }
+
+ public boolean isFixedRowHeight()
+ {
+ return rowHeight > 0;
+ }
+
+ public boolean getInvokesStopCellEditing()
+ {
+ return invokesStopCellEditing;
+ }
+
+ public void setInvokesStopCellEditing(boolean invoke)
+ {
+ if (invokesStopCellEditing == invoke)
+ return;
+
+ boolean oldValue = invokesStopCellEditing;
+ invokesStopCellEditing = invoke;
+ firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY,
+ oldValue, invoke);
+ }
+
+ /**
+ * @since 1.3
+ */
+ public int getToggleClickCount()
+ {
+ return toggleClickCount;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setToggleClickCount(int count)
+ {
+ if (toggleClickCount == count)
+ return;
+
+ int oldValue = toggleClickCount;
+ toggleClickCount = count;
+ firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count);
+ }
+
+ public void scrollPathToVisible(TreePath path)
+ {
+ if (path == null)
+ return;
+ Rectangle rect = getPathBounds(path);
+ scrollRectToVisible(rect);
+ }
+
+ public void scrollRowToVisible(int row)
+ {
+ scrollPathToVisible(getPathForRow(row));
+ }
+
+ public boolean getScrollsOnExpand()
+ {
+ return scrollsOnExpand;
+ }
+
+ public void setScrollsOnExpand(boolean scroll)
+ {
+ clientScrollsOnExpandSet = true;
+ if (scrollsOnExpand == scroll)
+ return;
+
+ boolean oldValue = scrollsOnExpand;
+ scrollsOnExpand = scroll;
+ firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll);
+ }
+
+ public void setSelectionPath(TreePath path)
+ {
+ clearSelectionPathStates();
+ selectionModel.setSelectionPath(path);
+ }
+
+ public void setSelectionPaths(TreePath[] paths)
+ {
+ clearSelectionPathStates();
+ selectionModel.setSelectionPaths(paths);
+ }
+
+ /**
+ * This method, and all calls to it, should be removed once the
+ * DefaultTreeModel fires events properly. Maintenance of the nodeStates
+ * table should really be done in the TreeModelHandler.
+ */
+ private void clearSelectionPathStates()
+ {
+ TreePath[] oldPaths = selectionModel.getSelectionPaths();
+ if (oldPaths != null)
+ for (int i = 0; i < oldPaths.length; i++)
+ nodeStates.remove(oldPaths[i]);
+ }
+
+ public void setSelectionRow(int row)
+ {
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ setSelectionPath(path);
+ }
+
+ public void setSelectionRows(int[] rows)
+ {
+ // Make sure we have an UI so getPathForRow() does not return null.
+ if (rows == null || getUI() == null)
+ return;
+
+ TreePath[] paths = new TreePath[rows.length];
+
+ for (int i = rows.length - 1; i >= 0; --i)
+ paths[i] = getPathForRow(rows[i]);
+
+ setSelectionPaths(paths);
+ }
+
+ public void setSelectionInterval(int index0, int index1)
+ {
+ TreePath[] paths = getPathBetweenRows(index0, index1);
+
+ if (paths != null)
+ setSelectionPaths(paths);
+ }
+
+ public void addSelectionPath(TreePath path)
+ {
+ selectionModel.addSelectionPath(path);
+ }
+
+ public void addSelectionPaths(TreePath[] paths)
+ {
+ selectionModel.addSelectionPaths(paths);
+ }
+
+ public void addSelectionRow(int row)
+ {
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ selectionModel.addSelectionPath(path);
+ }
+
+ public void addSelectionRows(int[] rows)
+ {
+ // Make sure we have an UI so getPathForRow() does not return null.
+ if (rows == null || getUI() == null)
+ return;
+
+ TreePath[] paths = new TreePath[rows.length];
+
+ for (int i = rows.length - 1; i >= 0; --i)
+ paths[i] = getPathForRow(rows[i]);
+
+ addSelectionPaths(paths);
+ }
+
+ /**
+ * Select all rows between the two given indexes, inclusive. The method
+ * will not select the inner leaves and braches of the currently collapsed
+ * nodes in this interval.
+ *
+ * @param index0 the starting row, inclusive
+ * @param index1 the ending row, inclusive
+ */
+ public void addSelectionInterval(int index0, int index1)
+ {
+ TreePath[] paths = getPathBetweenRows(index0, index1);
+
+ if (paths != null)
+ addSelectionPaths(paths);
+ }
+
+ public void removeSelectionPath(TreePath path)
+ {
+ clearSelectionPathStates();
+ selectionModel.removeSelectionPath(path);
+ }
+
+ public void removeSelectionPaths(TreePath[] paths)
+ {
+ clearSelectionPathStates();
+ selectionModel.removeSelectionPaths(paths);
+ }
+
+ public void removeSelectionRow(int row)
+ {
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ removeSelectionPath(path);
+ }
+
+ public void removeSelectionRows(int[] rows)
+ {
+ if (rows == null || getUI() == null)
+ return;
+
+ TreePath[] paths = new TreePath[rows.length];
+
+ for (int i = rows.length - 1; i >= 0; --i)
+ paths[i] = getPathForRow(rows[i]);
+
+ removeSelectionPaths(paths);
+ }
+
+ public void removeSelectionInterval(int index0, int index1)
+ {
+ TreePath[] paths = getPathBetweenRows(index0, index1);
+
+ if (paths != null)
+ removeSelectionPaths(paths);
+ }
+
+ public void clearSelection()
+ {
+ selectionModel.clearSelection();
+ setLeadSelectionPath(null);
+ }
+
+ public TreePath getLeadSelectionPath()
+ {
+ if (selectionModel == null)
+ return null;
+ else
+ return selectionModel.getLeadSelectionPath();
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setLeadSelectionPath(TreePath path)
+ {
+ if (selectionModel != null)
+ {
+ TreePath oldValue = selectionModel.getLeadSelectionPath();
+ if (path == oldValue || path != null && path.equals(oldValue))
+ return;
+
+ // Repaint the previous and current rows with the lead selection path.
+ if (path != null)
+ {
+ repaint(getPathBounds(path));
+ selectionModel.addSelectionPath(path);
+ }
+
+ if (oldValue != null)
+ repaint(getPathBounds(oldValue));
+
+ firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path);
+ }
+ }
+
+ /**
+ * @since 1.3
+ */
+ public TreePath getAnchorSelectionPath()
+ {
+ return anchorSelectionPath;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setAnchorSelectionPath(TreePath path)
+ {
+ if (anchorSelectionPath == path)
+ return;
+
+ TreePath oldValue = anchorSelectionPath;
+ anchorSelectionPath = path;
+ firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path);
+ }
+
+ public int getLeadSelectionRow()
+ {
+ return selectionModel.getLeadSelectionRow();
+ }
+
+ public int getMaxSelectionRow()
+ {
+ return selectionModel.getMaxSelectionRow();
+ }
+
+ public int getMinSelectionRow()
+ {
+ return selectionModel.getMinSelectionRow();
+ }
+
+ public int getSelectionCount()
+ {
+ return selectionModel.getSelectionCount();
+ }
+
+ public TreePath getSelectionPath()
+ {
+ return selectionModel.getSelectionPath();
+ }
+
+ public TreePath[] getSelectionPaths()
+ {
+ return selectionModel.getSelectionPaths();
+ }
+
+ public int[] getSelectionRows()
+ {
+ return selectionModel.getSelectionRows();
+ }
+
+ public boolean isPathSelected(TreePath path)
+ {
+ return selectionModel.isPathSelected(path);
+ }
+
+ /**
+ * Returns <code>true</code> when the specified row is selected,
+ * <code>false</code> otherwise. This call is delegated to the
+ * {@link TreeSelectionModel#isRowSelected(int)} method.
+ *
+ * @param row the row to check
+ *
+ * @return <code>true</code> when the specified row is selected,
+ * <code>false</code> otherwise
+ */
+ public boolean isRowSelected(int row)
+ {
+ return selectionModel.isRowSelected(row);
+ }
+
+ public boolean isSelectionEmpty()
+ {
+ return selectionModel.isSelectionEmpty();
+ }
+
+ /**
+ * Return the value of the <code>dragEnabled</code> property.
+ *
+ * @return the value
+ *
+ * @since 1.4
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * Set the <code>dragEnabled</code> property.
+ *
+ * @param enabled new value
+ *
+ * @since 1.4
+ */
+ public void setDragEnabled(boolean enabled)
+ {
+ dragEnabled = enabled;
+ }
+
+ public int getRowCount()
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.getRowCount(this);
+
+ return 0;
+ }
+
+ public void collapsePath(TreePath path)
+ {
+ try
+ {
+ fireTreeWillCollapse(path);
+ }
+ catch (ExpandVetoException ev)
+ {
+ // We do nothing if attempt has been vetoed.
+ }
+ setExpandedState(path, false);
+ fireTreeCollapsed(path);
+ }
+
+ public void collapseRow(int row)
+ {
+ if (row < 0 || row >= getRowCount())
+ return;
+
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ collapsePath(path);
+ }
+
+ public void expandPath(TreePath path)
+ {
+ // Don't expand if path is null
+ // or is already expanded.
+ if (path == null || isExpanded(path))
+ return;
+
+ try
+ {
+ fireTreeWillExpand(path);
+ }
+ catch (ExpandVetoException ev)
+ {
+ // We do nothing if attempt has been vetoed.
+ }
+
+ setExpandedState(path, true);
+ fireTreeExpanded(path);
+ }
+
+ public void expandRow(int row)
+ {
+ if (row < 0 || row >= getRowCount())
+ return;
+
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ expandPath(path);
+ }
+
+ public boolean isCollapsed(TreePath path)
+ {
+ return !isExpanded(path);
+ }
+
+ public boolean isCollapsed(int row)
+ {
+ if (row < 0 || row >= getRowCount())
+ return false;
+
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ return isCollapsed(path);
+
+ return false;
+ }
+
+ public boolean isExpanded(TreePath path)
+ {
+ if (path == null)
+ return false;
+
+ Object state = nodeStates.get(path);
+
+ if ((state == null) || (state != EXPANDED))
+ return false;
+
+ TreePath parent = path.getParentPath();
+
+ if (parent != null)
+ return isExpanded(parent);
+
+ return true;
+ }
+
+ public boolean isExpanded(int row)
+ {
+ if (row < 0 || row >= getRowCount())
+ return false;
+
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ return isExpanded(path);
+
+ return false;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public boolean getExpandsSelectedPaths()
+ {
+ return expandsSelectedPaths;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setExpandsSelectedPaths(boolean flag)
+ {
+ if (expandsSelectedPaths == flag)
+ return;
+
+ boolean oldValue = expandsSelectedPaths;
+ expandsSelectedPaths = flag;
+ firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag);
+ }
+
+ public Rectangle getPathBounds(TreePath path)
+ {
+ TreeUI ui = getUI();
+
+ if (ui == null)
+ return null;
+
+ return ui.getPathBounds(this, path);
+ }
+
+ public Rectangle getRowBounds(int row)
+ {
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ return getPathBounds(path);
+
+ return null;
+ }
+
+ public boolean isEditing()
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.isEditing(this);
+
+ return false;
+ }
+
+ public boolean stopEditing()
+ {
+ TreeUI ui = getUI();
+
+ if (isEditing())
+ if (ui != null)
+ return ui.stopEditing(this);
+
+ return false;
+ }
+
+ public void cancelEditing()
+ {
+ TreeUI ui = getUI();
+
+ if (isEditing())
+ if (ui != null)
+ ui.cancelEditing(this);
+ }
+
+ public void startEditingAtPath(TreePath path)
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ ui.startEditingAtPath(this, path);
+ }
+
+ public TreePath getEditingPath()
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.getEditingPath(this);
+
+ return null;
+ }
+
+ public TreePath getPathForLocation(int x, int y)
+ {
+ TreePath path = getClosestPathForLocation(x, y);
+
+ if (path != null)
+ {
+ Rectangle rect = getPathBounds(path);
+
+ if ((rect != null) && rect.contains(x, y))
+ return path;
+ }
+
+ return null;
+ }
+
+ public int getRowForLocation(int x, int y)
+ {
+ TreePath path = getPathForLocation(x, y);
+
+ if (path != null)
+ return getRowForPath(path);
+
+ return -1;
+ }
+
+ public TreePath getClosestPathForLocation(int x, int y)
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.getClosestPathForLocation(this, x, y);
+
+ return null;
+ }
+
+ public int getClosestRowForLocation(int x, int y)
+ {
+ TreePath path = getClosestPathForLocation(x, y);
+
+ if (path != null)
+ return getRowForPath(path);
+
+ return -1;
+ }
+
+ public Object getLastSelectedPathComponent()
+ {
+ TreePath path = getSelectionPath();
+
+ if (path != null)
+ return path.getLastPathComponent();
+
+ return null;
+ }
+
+ private void doExpandParents(TreePath path, boolean state)
+ {
+ TreePath parent = path.getParentPath();
+
+ if (!isExpanded(parent) && parent != null)
+ doExpandParents(parent, false);
+
+ nodeStates.put(path, state ? EXPANDED : COLLAPSED);
+ }
+
+ protected void setExpandedState(TreePath path, boolean state)
+ {
+ if (path == null)
+ return;
+
+ doExpandParents(path, state);
+ }
+
+ protected void clearToggledPaths()
+ {
+ nodeStates.clear();
+ }
+
+ protected Enumeration<TreePath> getDescendantToggledPaths(TreePath parent)
+ {
+ if (parent == null)
+ return null;
+
+ Enumeration nodes = nodeStates.keys();
+ Vector result = new Vector();
+
+ while (nodes.hasMoreElements())
+ {
+ TreePath path = (TreePath) nodes.nextElement();
+
+ if (path.isDescendant(parent))
+ result.addElement(path);
+ }
+
+ return result.elements();
+ }
+
+ public boolean hasBeenExpanded(TreePath path)
+ {
+ if (path == null)
+ return false;
+
+ return nodeStates.get(path) != null;
+ }
+
+ public boolean isVisible(TreePath path)
+ {
+ if (path == null)
+ return false;
+
+ TreePath parent = path.getParentPath();
+
+ if (parent == null)
+ return true; // Is root node.
+
+ return isExpanded(parent);
+ }
+
+ public void makeVisible(TreePath path)
+ {
+ if (path == null)
+ return;
+
+ expandPath(path.getParentPath());
+ }
+
+ public boolean isPathEditable(TreePath path)
+ {
+ return isEditable();
+ }
+
+ /**
+ * Creates and returns an instance of {@link TreeModelHandler}.
+ *
+ * @return an instance of {@link TreeModelHandler}
+ */
+ protected TreeModelListener createTreeModelListener()
+ {
+ return new TreeModelHandler();
+ }
+
+ /**
+ * Returns a sample TreeModel that can be used in a JTree. This can be used
+ * in Bean- or GUI-Builders to show something interesting.
+ *
+ * @return a sample TreeModel that can be used in a JTree
+ */
+ protected static TreeModel getDefaultTreeModel()
+ {
+ DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node");
+ DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child node 1");
+ DefaultMutableTreeNode child11 =
+ new DefaultMutableTreeNode("Child node 1.1");
+ DefaultMutableTreeNode child12 =
+ new DefaultMutableTreeNode("Child node 1.2");
+ DefaultMutableTreeNode child13 =
+ new DefaultMutableTreeNode("Child node 1.3");
+ DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child node 2");
+ DefaultMutableTreeNode child21 =
+ new DefaultMutableTreeNode("Child node 2.1");
+ DefaultMutableTreeNode child22 =
+ new DefaultMutableTreeNode("Child node 2.2");
+ DefaultMutableTreeNode child23 =
+ new DefaultMutableTreeNode("Child node 2.3");
+ DefaultMutableTreeNode child24 =
+ new DefaultMutableTreeNode("Child node 2.4");
+
+ DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("Child node 3");
+ root.add(child1);
+ root.add(child2);
+ root.add(child3);
+ child1.add(child11);
+ child1.add(child12);
+ child1.add(child13);
+ child2.add(child21);
+ child2.add(child22);
+ child2.add(child23);
+ child2.add(child24);
+ return new DefaultTreeModel(root);
+ }
+
+ /**
+ * Converts the specified value to a String. This is used by the renderers
+ * of this JTree and its nodes.
+ *
+ * This implementation simply returns <code>value.toString()</code> and
+ * ignores all other parameters. Subclass this method to control the
+ * conversion.
+ *
+ * @param value the value that is converted to a String
+ * @param selected indicates if that value is selected or not
+ * @param expanded indicates if that value is expanded or not
+ * @param leaf indicates if that value is a leaf node or not
+ * @param row the row of the node
+ * @param hasFocus indicates if that node has focus or not
+ */
+ public String convertValueToText(Object value, boolean selected,
+ boolean expanded, boolean leaf, int row, boolean hasFocus)
+ {
+ return value.toString();
+ }
+
+ /**
+ * A String representation of this JTree. This is intended to be used for
+ * debugging. The returned string may be empty but may not be
+ * <code>null</code>.
+ *
+ * @return a String representation of this JTree
+ */
+ protected String paramString()
+ {
+ // TODO: this is completely legal, but it would possibly be nice
+ // to return some more content, like the tree structure, some properties
+ // etc ...
+ return "";
+ }
+
+ /**
+ * Returns all TreePath objects which are a descendants of the given path
+ * and are exapanded at the moment of the execution of this method. If the
+ * state of any node is beeing toggled while this method is executing this
+ * change may be left unaccounted.
+ *
+ * @param path The parent of this request
+ *
+ * @return An Enumeration containing TreePath objects
+ */
+ public Enumeration<TreePath> getExpandedDescendants(TreePath path)
+ {
+ Enumeration paths = nodeStates.keys();
+ Vector relevantPaths = new Vector();
+ while (paths.hasMoreElements())
+ {
+ TreePath nextPath = (TreePath) paths.nextElement();
+ if (nodeStates.get(nextPath) == EXPANDED
+ && path.isDescendant(nextPath))
+ {
+ relevantPaths.add(nextPath);
+ }
+ }
+ return relevantPaths.elements();
+ }
+
+ /**
+ * Returns the next table element (beginning from the row
+ * <code>startingRow</code> that starts with <code>prefix</code>.
+ * Searching is done in the direction specified by <code>bias</code>.
+ *
+ * @param prefix the prefix to search for in the cell values
+ * @param startingRow the index of the row where to start searching from
+ * @param bias the search direction, either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward}
+ *
+ * @return the path to the found element or -1 if no such element has been
+ * found
+ *
+ * @throws IllegalArgumentException if prefix is <code>null</code> or
+ * startingRow is not valid
+ *
+ * @since 1.4
+ */
+ public TreePath getNextMatch(String prefix, int startingRow,
+ Position.Bias bias)
+ {
+ if (prefix == null)
+ throw new IllegalArgumentException("The argument 'prefix' must not be"
+ + " null.");
+ if (startingRow < 0)
+ throw new IllegalArgumentException("The argument 'startingRow' must not"
+ + " be less than zero.");
+
+ int size = getRowCount();
+ if (startingRow > size)
+ throw new IllegalArgumentException("The argument 'startingRow' must not"
+ + " be greater than the number of"
+ + " elements in the TreeModel.");
+
+ TreePath foundPath = null;
+ if (bias == Position.Bias.Forward)
+ {
+ for (int i = startingRow; i < size; i++)
+ {
+ TreePath path = getPathForRow(i);
+ Object o = path.getLastPathComponent();
+ // FIXME: in the following call to convertValueToText the
+ // last argument (hasFocus) should be done right.
+ String item = convertValueToText(o, isRowSelected(i),
+ isExpanded(i), treeModel.isLeaf(o),
+ i, false);
+ if (item.startsWith(prefix))
+ {
+ foundPath = path;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (int i = startingRow; i >= 0; i--)
+ {
+ TreePath path = getPathForRow(i);
+ Object o = path.getLastPathComponent();
+ // FIXME: in the following call to convertValueToText the
+ // last argument (hasFocus) should be done right.
+ String item = convertValueToText(o, isRowSelected(i),
+ isExpanded(i), treeModel.isLeaf(o), i, false);
+ if (item.startsWith(prefix))
+ {
+ foundPath = path;
+ break;
+ }
+ }
+ }
+ return foundPath;
+ }
+
+ /**
+ * Removes any paths in the current set of selected paths that are
+ * descendants of <code>path</code>. If <code>includePath</code> is set
+ * to <code>true</code> and <code>path</code> itself is selected, then
+ * it will be removed too.
+ *
+ * @param path the path from which selected descendants are to be removed
+ * @param includeSelected if <code>true</code> then <code>path</code> itself
+ * will also be remove if it's selected
+ *
+ * @return <code>true</code> if something has been removed,
+ * <code>false</code> otherwise
+ *
+ * @since 1.3
+ */
+ protected boolean removeDescendantSelectedPaths(TreePath path,
+ boolean includeSelected)
+ {
+ boolean removedSomething = false;
+ TreePath[] selected = getSelectionPaths();
+ for (int index = 0; index < selected.length; index++)
+ {
+ if ((selected[index] == path && includeSelected)
+ || (selected[index].isDescendant(path)))
+ {
+ removeSelectionPath(selected[index]);
+ removedSomething = true;
+ }
+ }
+ return removedSomething;
+ }
+
+ /**
+ * Removes any descendants of the TreePaths in toRemove that have been
+ * expanded.
+ *
+ * @param toRemove - Enumeration of TreePaths that need to be removed from
+ * cache of toggled tree paths.
+ */
+ protected void removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
+ {
+ while (toRemove.hasMoreElements())
+ {
+ TreePath current = toRemove.nextElement();
+ Enumeration descendants = getDescendantToggledPaths(current);
+
+ while (descendants.hasMoreElements())
+ {
+ TreePath currentDes = (TreePath) descendants.nextElement();
+ if (isExpanded(currentDes))
+ nodeStates.remove(currentDes);
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Sent when the tree has changed enough that we need to resize the bounds,
+ * but not enough that we need to remove the expanded node set (e.g nodes were
+ * expanded or collapsed, or nodes were inserted into the tree). You should
+ * never have to invoke this, the UI will invoke this as it needs to.
+ * </p>
+ * <p>
+ * If the tree uses {@link DefaultTreeModel}, you must call
+ * {@link DefaultTreeModel#reload(TreeNode)} or
+ * {@link DefaultTreeModel#reload()} after adding or removing nodes. Following
+ * the official Java 1.5 API standard, just calling treeDidChange, repaint()
+ * or revalidate() does <i>not</i> update the tree appearance properly.
+ *
+ * @see DefaultTreeModel#reload()
+ */
+ public void treeDidChange()
+ {
+ repaint();
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if <code>c</code> or
+ * <code>propertyValue</code> is <code>null</code>
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("rowHeight"))
+ {
+ if (! clientRowHeightSet)
+ {
+ setRowHeight(((Integer) value).intValue());
+ clientRowHeightSet = false;
+ }
+ }
+ else if (propertyName.equals("scrollsOnExpand"))
+ {
+ if (! clientScrollsOnExpandSet)
+ {
+ setScrollsOnExpand(((Boolean) value).booleanValue());
+ clientScrollsOnExpandSet = false;
+ }
+ }
+ else if (propertyName.equals("showsRootHandles"))
+ {
+ if (! clientShowsRootHandlesSet)
+ {
+ setShowsRootHandles(((Boolean) value).booleanValue());
+ clientShowsRootHandlesSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JViewport.java b/libjava/classpath/javax/swing/JViewport.java
new file mode 100644
index 000000000..729fac671
--- /dev/null
+++ b/libjava/classpath/javax/swing/JViewport.java
@@ -0,0 +1,948 @@
+/* JViewport.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.io.Serializable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ViewportUI;
+
+/**
+ *
+ * <pre>
+ * _
+ * +-------------------------------+ ...........Y1 \
+ * | view | . \
+ * | (this component's child) | . > VY
+ * | | . / = Y2-Y1
+ * | +------------------------------+ ....Y2_/
+ * | | viewport | | .
+ * | | (this component) | | .
+ * | | | | .
+ * | | | | .
+ * | | | | .
+ * | | | | .
+ * | +------------------------------+ ....Y3
+ * | | .
+ * | . | . .
+ * | . | . .
+ * +---------.---------------------+ ...........Y4
+ * . . . .
+ * . . . .
+ * . . . .
+ * X1.......X2.....................X3.......X4
+ * \____ ___/
+ * \/
+ * VX = X2-X1
+ *</pre>
+ *
+ * <p>A viewport is, like all swing components, located at some position in
+ * the swing component tree; that location is exactly the same as any other
+ * components: the viewport's "bounds".</p>
+ *
+ * <p>But in terms of drawing its child, the viewport thinks of itself as
+ * covering a particular position <em>of the view's coordinate space</em>.
+ * For example, the {@link #getViewPosition} method returns
+ * the position <code>(VX,VY)</code> shown above, which is an position in
+ * "view space", even though this is <em>implemented</em> by positioning
+ * the underlying child at position <code>(-VX,-VY)</code></p>
+ *
+ */
+public class JViewport extends JComponent implements Accessible
+{
+ /**
+ * Provides accessibility support for <code>JViewport</code>.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+ protected class AccessibleJViewport extends AccessibleJComponent
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJViewport</code>.
+ */
+ protected AccessibleJViewport()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role of <code>JViewport</code>, which is
+ * {@link AccessibleRole#VIEWPORT}.
+ *
+ * @return the accessible role of <code>JViewport</code>
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.VIEWPORT;
+ }
+ }
+
+ /**
+ * A {@link java.awt.event.ComponentListener} that listens for
+ * changes of the view's size. This triggers a revalidate() call on the
+ * viewport.
+ */
+ protected class ViewListener extends ComponentAdapter implements Serializable
+ {
+ private static final long serialVersionUID = -2812489404285958070L;
+
+ /**
+ * Creates a new instance of ViewListener.
+ */
+ protected ViewListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification when a component (in this case: the view
+ * component) changes it's size. This simply triggers a revalidate() on the
+ * viewport.
+ *
+ * @param ev the ComponentEvent describing the change
+ */
+ public void componentResized(ComponentEvent ev)
+ {
+ // Fire state change, because resizing the view means changing the
+ // extentSize.
+ fireStateChanged();
+ revalidate();
+ }
+ }
+
+ public static final int SIMPLE_SCROLL_MODE = 0;
+ public static final int BLIT_SCROLL_MODE = 1;
+ public static final int BACKINGSTORE_SCROLL_MODE = 2;
+
+ private static final long serialVersionUID = -6925142919680527970L;
+
+ /**
+ * The default scrollmode to be used by all JViewports as determined by
+ * the system property gnu.javax.swing.JViewport.scrollMode.
+ */
+ private static final int defaultScrollMode;
+
+ protected boolean scrollUnderway;
+ protected boolean isViewSizeSet;
+
+ /**
+ * This flag indicates whether we use a backing store for drawing.
+ *
+ * @deprecated since JDK 1.3
+ */
+ protected boolean backingStore;
+
+ /**
+ * The backingstore image used for the backingstore and blit scroll methods.
+ */
+ protected Image backingStoreImage;
+
+ /**
+ * The position at which the view has been drawn the last time. This is used
+ * to determine the bittable area.
+ */
+ protected Point lastPaintPosition;
+
+ ChangeEvent changeEvent = new ChangeEvent(this);
+
+ int scrollMode;
+
+ /**
+ * The ViewListener instance.
+ */
+ ViewListener viewListener;
+
+ /**
+ * Stores the location from where to blit. This is a cached Point object used
+ * in blitting calculations.
+ */
+ Point cachedBlitFrom;
+
+ /**
+ * Stores the location where to blit to. This is a cached Point object used
+ * in blitting calculations.
+ */
+ Point cachedBlitTo;
+
+ /**
+ * Stores the width of the blitted area. This is a cached Dimension object
+ * used in blitting calculations.
+ */
+ Dimension cachedBlitSize;
+
+ /**
+ * Stores the bounds of the area that needs to be repainted. This is a cached
+ * Rectangle object used in blitting calculations.
+ */
+ Rectangle cachedBlitPaint;
+
+ boolean damaged = true;
+
+ /**
+ * A flag indicating if the size of the viewport has changed since the
+ * last repaint. This is used in double buffered painting to check if we
+ * need a new double buffer, or can reuse the old one.
+ */
+ boolean sizeChanged = true;
+
+ /**
+ * Indicates if this JViewport is the paint root or not. If it is not, then
+ * we may not assume that the offscreen buffer still has the right content
+ * because parent components may have cleared the background already.
+ */
+ private boolean isPaintRoot = false;
+
+ /**
+ * Initializes the default setting for the scrollMode property.
+ */
+ static
+ {
+ String scrollModeProp =
+ SystemProperties.getProperty("gnu.swing.scrollmode", "BACKINGSTORE");
+ if (scrollModeProp.equalsIgnoreCase("simple"))
+ defaultScrollMode = SIMPLE_SCROLL_MODE;
+ else if (scrollModeProp.equalsIgnoreCase("backingstore"))
+ defaultScrollMode = BACKINGSTORE_SCROLL_MODE;
+ else
+ defaultScrollMode = BLIT_SCROLL_MODE;
+ }
+
+ public JViewport()
+ {
+ setOpaque(true);
+ setScrollMode(defaultScrollMode);
+ updateUI();
+ setLayout(createLayoutManager());
+ lastPaintPosition = new Point();
+ cachedBlitFrom = new Point();
+ cachedBlitTo = new Point();
+ cachedBlitSize = new Dimension();
+ cachedBlitPaint = new Rectangle();
+ }
+
+ public Dimension getExtentSize()
+ {
+ return getSize();
+ }
+
+ public Dimension toViewCoordinates(Dimension size)
+ {
+ return size;
+ }
+
+ public Point toViewCoordinates(Point p)
+ {
+ Point pos = getViewPosition();
+ return new Point(p.x + pos.x,
+ p.y + pos.y);
+ }
+
+ public void setExtentSize(Dimension newSize)
+ {
+ Dimension oldExtent = getExtentSize();
+ if (! newSize.equals(oldExtent))
+ {
+ setSize(newSize);
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the viewSize when set, or the preferred size of the set
+ * Component view. If no viewSize and no Component view is set an
+ * empty Dimension is returned.
+ */
+ public Dimension getViewSize()
+ {
+ Dimension size;
+ Component view = getView();
+ if (view != null)
+ {
+ if (isViewSizeSet)
+ size = view.getSize();
+ else
+ size = view.getPreferredSize();
+ }
+ else
+ size = new Dimension(0, 0);
+ return size;
+ }
+
+
+ public void setViewSize(Dimension newSize)
+ {
+ Component view = getView();
+ if (view != null)
+ {
+ if (! newSize.equals(view.getSize()))
+ {
+ scrollUnderway = false;
+ view.setSize(newSize);
+ isViewSizeSet = true;
+ fireStateChanged();
+ }
+ }
+ }
+
+ /**
+ * Get the viewport's position in view space. Despite confusing name,
+ * this really does return the viewport's (0,0) position in view space,
+ * not the view's position.
+ */
+
+ public Point getViewPosition()
+ {
+ Component view = getView();
+ if (view == null)
+ return new Point(0,0);
+ else
+ {
+ Point p = view.getLocation();
+ p.x = -p.x;
+ p.y = -p.y;
+ return p;
+ }
+ }
+
+ public void setViewPosition(Point p)
+ {
+ Component view = getView();
+ if (view != null && ! p.equals(getViewPosition()))
+ {
+ scrollUnderway = true;
+ view.setLocation(-p.x, -p.y);
+ fireStateChanged();
+ }
+ }
+
+ public Rectangle getViewRect()
+ {
+ return new Rectangle(getViewPosition(), getExtentSize());
+ }
+
+ /**
+ * @deprecated 1.4
+ */
+ public boolean isBackingStoreEnabled()
+ {
+ return scrollMode == BACKINGSTORE_SCROLL_MODE;
+ }
+
+ /**
+ * @deprecated 1.4
+ */
+ public void setBackingStoreEnabled(boolean b)
+ {
+ if (b && scrollMode != BACKINGSTORE_SCROLL_MODE)
+ {
+ scrollMode = BACKINGSTORE_SCROLL_MODE;
+ fireStateChanged();
+ }
+ }
+
+ public void setScrollMode(int mode)
+ {
+ scrollMode = mode;
+ fireStateChanged();
+ }
+
+ public int getScrollMode()
+ {
+ return scrollMode;
+ }
+
+ public Component getView()
+ {
+ if (getComponentCount() == 0)
+ return null;
+
+ return getComponents()[0];
+ }
+
+ public void setView(Component v)
+ {
+ Component currView = getView();
+ if (viewListener != null && currView != null)
+ currView.removeComponentListener(viewListener);
+
+ if (v != null)
+ {
+ if (viewListener == null)
+ viewListener = createViewListener();
+ v.addComponentListener(viewListener);
+ add(v);
+ fireStateChanged();
+ }
+ revalidate();
+ repaint();
+ }
+
+ public void reshape(int x, int y, int w, int h)
+ {
+ if (w != getWidth() || h != getHeight())
+ sizeChanged = true;
+ super.reshape(x, y, w, h);
+ if (sizeChanged)
+ {
+ damaged = true;
+ fireStateChanged();
+ }
+ }
+
+ public final Insets getInsets()
+ {
+ return new Insets(0, 0, 0, 0);
+ }
+
+ public final Insets getInsets(Insets insets)
+ {
+ if (insets == null)
+ return getInsets();
+ insets.top = 0;
+ insets.bottom = 0;
+ insets.left = 0;
+ insets.right = 0;
+ return insets;
+ }
+
+
+ /**
+ * Overridden to return <code>false</code>, so the JViewport's paint method
+ * gets called instead of directly calling the children. This is necessary
+ * in order to get a useful clipping and translation on the children.
+ *
+ * @return <code>false</code>
+ */
+ public boolean isOptimizedDrawingEnabled()
+ {
+ return false;
+ }
+
+ public void paint(Graphics g)
+ {
+ Component view = getView();
+
+ if (view == null)
+ return;
+
+ Rectangle viewBounds = view.getBounds();
+ Rectangle portBounds = getBounds();
+
+ if (viewBounds.width == 0
+ || viewBounds.height == 0
+ || portBounds.width == 0
+ || portBounds.height == 0)
+ return;
+
+ switch (getScrollMode())
+ {
+
+ case JViewport.BACKINGSTORE_SCROLL_MODE:
+ paintBackingStore(g);
+ break;
+ case JViewport.BLIT_SCROLL_MODE:
+ paintBlit(g);
+ break;
+ case JViewport.SIMPLE_SCROLL_MODE:
+ default:
+ paintSimple(g);
+ break;
+ }
+ damaged = false;
+ }
+
+ 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[]) getListeners(ChangeListener.class);
+ }
+
+ /**
+ * This method returns the String ID of the UI class of Separator.
+ *
+ * @return The UI class' String ID.
+ */
+ public String getUIClassID()
+ {
+ return "ViewportUI";
+ }
+
+ /**
+ * This method resets the UI used to the Look and Feel defaults..
+ */
+ public void updateUI()
+ {
+ setUI((ViewportUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the viewport's UI delegate.
+ *
+ * @return The viewport's UI delegate.
+ */
+ public ViewportUI getUI()
+ {
+ return (ViewportUI) ui;
+ }
+
+ /**
+ * This method sets the viewport's UI delegate.
+ *
+ * @param ui The viewport's UI delegate.
+ */
+ public void setUI(ViewportUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ public final void setBorder(Border border)
+ {
+ if (border != null)
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Scrolls the view so that contentRect becomes visible.
+ *
+ * @param contentRect the rectangle to make visible within the view
+ */
+ public void scrollRectToVisible(Rectangle contentRect)
+ {
+ Component view = getView();
+ if (view == null)
+ return;
+
+ Point pos = getViewPosition();
+ // We get the contentRect in the viewport coordinates. But we want to
+ // calculate with view coordinates.
+ int contentX = contentRect.x + pos.x;
+ int contentY = contentRect.y + pos.y;
+ Rectangle viewBounds = getView().getBounds();
+ Rectangle portBounds = getBounds();
+
+ if (isShowing())
+ getView().validate();
+
+ // If the bottom boundary of contentRect is below the port
+ // boundaries, scroll up as necessary.
+ if (contentY + contentRect.height + viewBounds.y > portBounds.height)
+ pos.y = contentY + contentRect.height - portBounds.height;
+ // If contentY is above the port boundaries, scroll down to
+ // contentY.
+ if (contentY + viewBounds.y < 0)
+ pos.y = contentY;
+ // If the right boundary of contentRect is right from the port
+ // boundaries, scroll left as necessary.
+ if (contentX + contentRect.width + viewBounds.x > portBounds.width)
+ pos.x = contentX + contentRect.width - portBounds.width;
+ // If contentX is left from the port boundaries, scroll right to
+ // contentRect.x.
+ if (contentX + viewBounds.x < 0)
+ pos.x = contentX;
+ setViewPosition(pos);
+ }
+
+ /**
+ * Returns the accessible context for this <code>JViewport</code>. This
+ * will be an instance of {@link AccessibleJViewport}.
+ *
+ * @return the accessible context for this <code>JViewport</code>
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJViewport();
+ return accessibleContext;
+ }
+
+ /**
+ * Forward repaint to parent to make sure only one paint is performed by the
+ * RepaintManager.
+ *
+ * @param tm number of milliseconds to defer the repaint request
+ * @param x the X coordinate of the upper left corner of the dirty area
+ * @param y the Y coordinate of the upper left corner of the dirty area
+ * @param w the width of the dirty area
+ * @param h the height of the dirty area
+ */
+ public void repaint(long tm, int x, int y, int w, int h)
+ {
+ Component parent = getParent();
+ if (parent != null)
+ parent.repaint(tm, x + getX(), y + getY(), w, h);
+ else
+ super.repaint(tm, x, y, w, h);
+ }
+
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ if (getComponentCount() > 0)
+ remove(getComponents()[0]);
+
+ super.addImpl(comp, constraints, index);
+ }
+
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Creates a {@link ViewListener} that is supposed to listen for
+ * size changes on the view component.
+ *
+ * @return a ViewListener instance
+ */
+ protected ViewListener createViewListener()
+ {
+ return new ViewListener();
+ }
+
+ /**
+ * Creates the LayoutManager that is used for this viewport. Override
+ * this method if you want to use a custom LayoutManager.
+ *
+ * @return a LayoutManager to use for this viewport
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new ViewportLayout();
+ }
+
+ /**
+ * Computes the parameters for the blitting scroll method. <code>dx</code>
+ * and <code>dy</code> specifiy the X and Y offset by which the viewport
+ * is scrolled. All other arguments are output parameters and are filled by
+ * this method.
+ *
+ * <code>blitFrom</code> holds the position of the blit rectangle in the
+ * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea
+ * is copied to.
+ *
+ * <code>blitSize</code> holds the size of the blit area and
+ * <code>blitPaint</code> is the area of the view that needs to be painted.
+ *
+ * This method returns <code>true</code> if blitting is possible and
+ * <code>false</code> if the viewport has to be repainted completetly without
+ * blitting.
+ *
+ * @param dx the horizontal delta
+ * @param dy the vertical delta
+ * @param blitFrom the position from where to blit; set by this method
+ * @param blitTo the position where to blit area is copied to; set by this
+ * method
+ * @param blitSize the size of the blitted area; set by this method
+ * @param blitPaint the area that needs repainting; set by this method
+ *
+ * @return <code>true</code> if blitting is possible,
+ * <code>false</code> otherwise
+ */
+ protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo,
+ Dimension blitSize, Rectangle blitPaint)
+ {
+ if ((dx != 0 && dy != 0) || (dy == 0 && dy == 0) || damaged)
+ // We cannot blit if the viewport is scrolled in both directions at
+ // once. Also, we do not want to blit if the viewport is not scrolled at
+ // all, because that probably means the view component repaints itself
+ // and the buffer needs updating.
+ return false;
+
+ Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds());
+
+ // Compute the blitFrom and blitTo parameters.
+ blitFrom.x = portBounds.x;
+ blitFrom.y = portBounds.y;
+ blitTo.x = portBounds.x;
+ blitTo.y = portBounds.y;
+
+ if (dy > 0)
+ {
+ blitFrom.y = portBounds.y + dy;
+ }
+ else if (dy < 0)
+ {
+ blitTo.y = portBounds.y - dy;
+ }
+ else if (dx > 0)
+ {
+ blitFrom.x = portBounds.x + dx;
+ }
+ else if (dx < 0)
+ {
+ blitTo.x = portBounds.x - dx;
+ }
+
+ // Compute size of the blit area.
+ if (dx != 0)
+ {
+ blitSize.width = portBounds.width - Math.abs(dx);
+ blitSize.height = portBounds.height;
+ }
+ else if (dy != 0)
+ {
+ blitSize.width = portBounds.width;
+ blitSize.height = portBounds.height - Math.abs(dy);
+ }
+
+ // Compute the blitPaint parameter.
+ blitPaint.setBounds(portBounds);
+ if (dy > 0)
+ {
+ blitPaint.y = portBounds.y + portBounds.height - dy;
+ blitPaint.height = dy;
+ }
+ else if (dy < 0)
+ {
+ blitPaint.height = -dy;
+ }
+ if (dx > 0)
+ {
+ blitPaint.x = portBounds.x + portBounds.width - dx;
+ blitPaint.width = dx;
+ }
+ else if (dx < 0)
+ {
+ blitPaint.width = -dx;
+ }
+
+ return true;
+ }
+
+ /**
+ * Paints the viewport in case we have a scrollmode of
+ * {@link #SIMPLE_SCROLL_MODE}.
+ *
+ * This simply paints the view directly on the surface of the viewport.
+ *
+ * @param g the graphics context to use
+ */
+ void paintSimple(Graphics g)
+ {
+ // We need to call this to properly clear the background.
+ paintComponent(g);
+
+ Point pos = getViewPosition();
+ Component view = getView();
+ Shape oldClip = g.getClip();
+ g.clipRect(0, 0, getWidth(), getHeight());
+ boolean translated = false;
+ try
+ {
+ g.translate(-pos.x, -pos.y);
+ translated = true;
+ view.paint(g);
+ }
+ finally
+ {
+ if (translated)
+ g.translate (pos.x, pos.y);
+ g.setClip(oldClip);
+ }
+ }
+
+ /**
+ * Paints the viewport in case we have a scroll mode of
+ * {@link #BACKINGSTORE_SCROLL_MODE}.
+ *
+ * This method uses a backing store image to paint the view to, which is then
+ * subsequently painted on the screen. This should make scrolling more
+ * smooth.
+ *
+ * @param g the graphics context to use
+ */
+ void paintBackingStore(Graphics g)
+ {
+ // If we have no backing store image yet or the size of the component has
+ // changed, we need to rebuild the backing store.
+ if (backingStoreImage == null || sizeChanged)
+ {
+ backingStoreImage = createImage(getWidth(), getHeight());
+ sizeChanged = false;
+ Graphics g2 = backingStoreImage.getGraphics();
+ paintSimple(g2);
+ g2.dispose();
+ }
+ // Otherwise we can perform the blitting on the backing store image:
+ // First we move the part that remains visible after scrolling, then
+ // we only need to paint the bit that becomes newly visible.
+ else
+ {
+ Graphics g2 = backingStoreImage.getGraphics();
+ Point viewPosition = getViewPosition();
+ int dx = viewPosition.x - lastPaintPosition.x;
+ int dy = viewPosition.y - lastPaintPosition.y;
+ boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo,
+ cachedBlitSize, cachedBlitPaint);
+ if (canBlit && isPaintRoot)
+ {
+ // Copy the part that remains visible during scrolling.
+ if (cachedBlitSize.width > 0 && cachedBlitSize.height > 0)
+ {
+ g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y,
+ cachedBlitSize.width, cachedBlitSize.height,
+ cachedBlitTo.x - cachedBlitFrom.x,
+ cachedBlitTo.y - cachedBlitFrom.y);
+ }
+ // Now paint the part that becomes newly visible.
+ g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y,
+ cachedBlitPaint.width, cachedBlitPaint.height);
+ paintSimple(g2);
+ }
+ // If blitting is not possible for some reason, fall back to repainting
+ // everything.
+ else
+ {
+ // If the image has not been scrolled at all, only the changed
+ // clip must be updated in the buffer.
+ if (dx == 0 && dy == 0)
+ g2.setClip(g.getClip());
+
+ paintSimple(g2);
+ }
+ g2.dispose();
+ }
+ // Actually draw the backingstore image to the graphics context.
+ g.drawImage(backingStoreImage, 0, 0, this);
+ // Update the lastPaintPosition so that we know what is already drawn when
+ // we paint the next time.
+ lastPaintPosition.setLocation(getViewPosition());
+ }
+
+ /**
+ * Paints the viewport in case we have a scrollmode of
+ * {@link #BLIT_SCROLL_MODE}.
+ *
+ * This paints the viewport using a backingstore and a blitting algorithm.
+ * Only the newly exposed area of the view is painted from the view painting
+ * methods, the remainder is copied from the backing store.
+ *
+ * @param g the graphics context to use
+ */
+ void paintBlit(Graphics g)
+ {
+ // First we move the part that remains visible after scrolling, then
+ // we only need to paint the bit that becomes newly visible.
+ Point viewPosition = getViewPosition();
+ int dx = viewPosition.x - lastPaintPosition.x;
+ int dy = viewPosition.y - lastPaintPosition.y;
+ boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo,
+ cachedBlitSize, cachedBlitPaint);
+ if (canBlit && isPaintRoot)
+ {
+ // Copy the part that remains visible during scrolling.
+ if (cachedBlitSize.width > 0 && cachedBlitSize.width > 0)
+ {
+ g.copyArea(cachedBlitFrom.x, cachedBlitFrom.y,
+ cachedBlitSize.width, cachedBlitSize.height,
+ cachedBlitTo.x - cachedBlitFrom.x,
+ cachedBlitTo.y - cachedBlitFrom.y);
+ }
+ // Now paint the part that becomes newly visible.
+ Shape oldClip = g.getClip();
+ g.clipRect(cachedBlitPaint.x, cachedBlitPaint.y,
+ cachedBlitPaint.width, cachedBlitPaint.height);
+ try
+ {
+ paintSimple(g);
+ }
+ finally
+ {
+ g.setClip(oldClip);
+ }
+ }
+ // If blitting is not possible for some reason, fall back to repainting
+ // everything.
+ else
+ paintSimple(g);
+ lastPaintPosition.setLocation(getViewPosition());
+ }
+
+ /**
+ * Overridden from JComponent to set the {@link #isPaintRoot} flag.
+ *
+ * @param x the rectangle to paint, X coordinate
+ * @param y the rectangle to paint, Y coordinate
+ * @param w the rectangle to paint, width
+ * @param h the rectangle to paint, height
+ */
+ void paintImmediately2(int x, int y, int w, int h)
+ {
+ isPaintRoot = true;
+ super.paintImmediately2(x, y, w, h);
+ isPaintRoot = false;
+ }
+
+ /**
+ * Returns true when the JViewport is using a backbuffer, so that we
+ * can update our backbuffer correctly.
+ */
+ boolean isPaintRoot()
+ {
+ return scrollMode == BACKINGSTORE_SCROLL_MODE;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JWindow.java b/libjava/classpath/javax/swing/JWindow.java
new file mode 100644
index 000000000..91e0b5069
--- /dev/null
+++ b/libjava/classpath/javax/swing/JWindow.java
@@ -0,0 +1,290 @@
+/* JWindow.java --
+ Copyright (C) 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTEvent;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.LayoutManager;
+import java.awt.Window;
+import java.awt.event.KeyEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+/**
+ * Unlike JComponent derivatives, JWindow inherits from
+ * java.awt.Window. But also lets a look-and-feel component to its work.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JWindow extends Window implements Accessible, RootPaneContainer
+{
+ /**
+ * Provides accessibility support for <code>JWindow</code>.
+ */
+ protected class AccessibleJWindow extends Window.AccessibleAWTWindow
+ {
+ /**
+ * Creates a new instance of <code>AccessibleJWindow</code>.
+ */
+ protected AccessibleJWindow()
+ {
+ super();
+ // Nothing to do here.
+ }
+ }
+
+ private static final long serialVersionUID = 5420698392125238833L;
+
+ protected JRootPane rootPane;
+
+ /**
+ * @specnote rootPaneCheckingEnabled is false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * Creates a new <code>JWindow</code> that has a shared invisible owner frame
+ * as its parent.
+ */
+ public JWindow()
+ {
+ super(SwingUtilities.getOwnerFrame(null));
+ windowInit();
+ }
+
+ /**
+ * Creates a new <code>JWindow</code> that uses the specified graphics
+ * environment. This can be used to open a window on a different screen for
+ * example.
+ *
+ * @param gc the graphics environment to use
+ */
+ public JWindow(GraphicsConfiguration gc)
+ {
+ super(SwingUtilities.getOwnerFrame(null), gc);
+ windowInit();
+ }
+
+ /**
+ * Creates a new <code>JWindow</code> that has the specified
+ * <code>owner</code> frame. If <code>owner</code> is <code>null</code>, then
+ * an invisible shared owner frame is installed as owner frame.
+ *
+ * @param owner the owner frame of this window; if <code>null</code> a shared
+ * invisible owner frame is used
+ */
+ public JWindow(Frame owner)
+ {
+ super(SwingUtilities.getOwnerFrame(owner));
+ windowInit();
+ }
+
+ /**
+ * Creates a new <code>JWindow</code> that has the specified
+ * <code>owner</code> window. If <code>owner</code> is <code>null</code>,
+ * then an invisible shared owner frame is installed as owner frame.
+ *
+ * @param owner the owner window of this window; if <code>null</code> a
+ * shared invisible owner frame is used
+ */
+ public JWindow(Window owner)
+ {
+ super(SwingUtilities.getOwnerFrame(owner));
+ windowInit();
+ }
+
+ /**
+ * Creates a new <code>JWindow</code> for the given graphics configuration
+ * and that has the specified <code>owner</code> window. If
+ * <code>owner</code> is <code>null</code>, then an invisible shared owner
+ * frame is installed as owner frame.
+ *
+ * The <code>gc</code> parameter can be used to open the window on a
+ * different screen for example.
+ *
+ * @param owner the owner window of this window; if <code>null</code> a
+ * shared invisible owner frame is used
+ * @param gc the graphics configuration to use
+ */
+ public JWindow(Window owner, GraphicsConfiguration gc)
+ {
+ super(SwingUtilities.getOwnerFrame(owner), gc);
+ windowInit();
+ }
+
+ protected void windowInit()
+ {
+ // We need to explicitly enable events here so that our processKeyEvent()
+ // and processWindowEvent() gets called.
+ enableEvents(AWTEvent.KEY_EVENT_MASK);
+
+ super.setLayout(new BorderLayout(1, 1));
+ getRootPane(); // will do set/create
+ // Now we're done init stage, adds and layouts go to content pane.
+ setRootPaneCheckingEnabled(true);
+ }
+
+ public Dimension getPreferredSize()
+ {
+ return super.getPreferredSize();
+ }
+
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ public void setLayeredPane(JLayeredPane layeredPane)
+ {
+ getRootPane().setLayeredPane(layeredPane);
+ }
+
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ public JRootPane getRootPane()
+ {
+ if (rootPane == null)
+ setRootPane(createRootPane());
+ return rootPane;
+ }
+
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ rootPane = root;
+ add(rootPane, BorderLayout.CENTER);
+ }
+
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ public void setContentPane(Container contentPane)
+ {
+ getRootPane().setContentPane(contentPane);
+ }
+
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ public void setGlassPane(Component glassPane)
+ {
+ getRootPane().setGlassPane(glassPane);
+ }
+
+
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're adding in the initialization stage use super.add.
+ // otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().add(comp, constraints, index);
+ else
+ super.addImpl(comp, constraints, index);
+ }
+
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead.
+ if (comp == rootPane)
+ super.remove(rootPane);
+ else
+ getContentPane().remove(comp);
+ }
+
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+ protected void processKeyEvent(KeyEvent e)
+ {
+ super.processKeyEvent(e);
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJWindow();
+ return accessibleContext;
+ }
+
+ protected String paramString()
+ {
+ return "JWindow";
+ }
+}
diff --git a/libjava/classpath/javax/swing/KeyStroke.java b/libjava/classpath/javax/swing/KeyStroke.java
new file mode 100644
index 000000000..94bc334b0
--- /dev/null
+++ b/libjava/classpath/javax/swing/KeyStroke.java
@@ -0,0 +1,123 @@
+/* KeyStroke.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTKeyStroke;
+import java.awt.event.KeyEvent;
+import java.io.Serializable;
+
+public class KeyStroke
+ extends AWTKeyStroke
+ implements Serializable
+{
+ private static final long serialVersionUID = -9060180771037902530L;
+
+ // Called by java.awt.AWTKeyStroke.registerSubclass via reflection.
+ private KeyStroke()
+ {
+ // Nothing to do here.
+ }
+
+ private KeyStroke(char keyChar, int keyCode, int modifiers,
+ boolean onKeyRelease)
+ {
+ super(keyChar, keyCode, modifiers, onKeyRelease);
+ }
+
+ static
+ {
+ AWTKeyStroke.registerSubclass(KeyStroke.class);
+ }
+
+ public static KeyStroke getKeyStroke(char keyChar)
+ {
+ return (KeyStroke) getAWTKeyStroke(keyChar);
+ }
+
+ /**
+ * @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)
+ {
+ return new KeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, onKeyRelease);
+ }
+
+ public static KeyStroke getKeyStroke(Character keyChar, int modifiers)
+ {
+ return (KeyStroke) getAWTKeyStroke(keyChar, modifiers);
+ }
+
+ public static KeyStroke getKeyStroke(int keyCode, int modifiers,
+ boolean onKeyRelease)
+ {
+ return (KeyStroke) getAWTKeyStroke(keyCode, modifiers, onKeyRelease);
+ }
+
+ public static KeyStroke getKeyStroke(int keyCode, int modifiers)
+ {
+ return (KeyStroke) getAWTKeyStroke(keyCode, modifiers);
+ }
+
+ /**
+ * Returns the KeyStroke according to <code>getAWTKeyStroke()</code>.
+ * But it returns null instead of throwing
+ * <code>IllegalArugmentException</code> when
+ * the keystoke sequence cannot be parsed from the given string.
+ */
+ public static KeyStroke getKeyStroke(String str)
+ {
+ try
+ {
+ return (KeyStroke) getAWTKeyStroke(str);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ return null;
+ }
+ }
+
+ public static KeyStroke getKeyStrokeForEvent(KeyEvent event)
+ {
+ return (KeyStroke) getAWTKeyStrokeForEvent(event);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/KeyboardManager.java b/libjava/classpath/javax/swing/KeyboardManager.java
new file mode 100644
index 000000000..5c1c09eab
--- /dev/null
+++ b/libjava/classpath/javax/swing/KeyboardManager.java
@@ -0,0 +1,281 @@
+/* KeyboardManager.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.applet.Applet;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Window;
+import java.awt.event.KeyEvent;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.WeakHashMap;
+
+/**
+ * This class maintains a mapping from top-level containers to a
+ * Hashtable. The Hashtable maps KeyStrokes to Components to be used when
+ * Components register keyboard actions with the condition
+ * JComponent.WHEN_IN_FOCUSED_WINDOW.
+ *
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ */
+class KeyboardManager
+{
+ /** Shared instance of KeyboardManager **/
+ static KeyboardManager manager = new KeyboardManager();
+
+ /**
+ * A mapping between top level containers and Hashtables that
+ * map KeyStrokes to Components.
+ */
+ WeakHashMap topLevelLookup = new WeakHashMap();
+
+ /**
+ * A mapping between top level containers and Vectors of JMenuBars
+ * used to allow all the JMenuBars within a top level container
+ * a chance to consume key events.
+ */
+ Hashtable menuBarLookup = new Hashtable();
+ /**
+ * Returns the shared instance of KeyboardManager.
+ * @return the shared instance of KeybaordManager.
+ */
+ public static KeyboardManager getManager()
+ {
+ return manager;
+ }
+ /**
+ * Returns the top-level ancestor for the given JComponent.
+ * @param c the JComponent whose top-level ancestor we want
+ * @return the top-level ancestor for the given JComponent.
+ */
+ static Container findTopLevel (Component c)
+ {
+ Container topLevel = (c instanceof Container) ? (Container) c
+ : c.getParent();
+ while (topLevel != null &&
+ !(topLevel instanceof Window) &&
+ !(topLevel instanceof Applet) &&
+ !(topLevel instanceof JInternalFrame))
+ topLevel = topLevel.getParent();
+ return topLevel;
+ }
+
+ /**
+ * Returns the Hashtable that maps KeyStrokes to Components, for
+ * the specified top-level container c. If no Hashtable exists
+ * we create and register it here and return the newly created
+ * Hashtable.
+ *
+ * @param c the top-level container whose Hashtable we want
+ * @return the Hashtable mapping KeyStrokes to Components for the
+ * specified top-level container
+ */
+ Hashtable getHashtableForTopLevel (Container c)
+ {
+ Hashtable keyToComponent = (Hashtable)topLevelLookup.get(c);
+ if (keyToComponent == null)
+ {
+ keyToComponent = new Hashtable();
+ topLevelLookup.put(c, keyToComponent);
+ }
+ return keyToComponent;
+ }
+
+ /**
+ * Registers a KeyStroke with a Component. This does not register
+ * the KeyStroke to a specific Action. When searching for a
+ * WHEN_IN_FOCUSED_WINDOW binding we will first go up to the focused
+ * top-level Container, then get the Hashtable that maps KeyStrokes
+ * to components for that particular top-level Container, then
+ * call processKeyBindings on that component with the condition
+ * JComponent.WHEN_IN_FOCUSED_WINDOW.
+ * @param comp the JComponent associated with the KeyStroke
+ * @param key the KeyStroke
+ */
+ public void registerBinding(JComponent comp, KeyStroke key)
+ {
+ // This method associates a KeyStroke with a particular JComponent
+ // When the KeyStroke occurs, if this component's top-level ancestor
+ // has focus (one of its children is the focused Component) then
+ // comp.processKeyBindings will be called with condition
+ // JComponent.WHEN_IN_FOCUSED_WINDOW.
+
+ // Look for the JComponent's top-level parent and return if it is null
+ Container topLevel = findTopLevel(comp);
+ if (topLevel == null)
+ return;
+
+ // Now get the Hashtable for this top-level container
+ Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
+
+ // And add the new binding to this Hashtable
+ // FIXME: should allow more than one JComponent to be associated
+ // with a KeyStroke, in case one of them is disabled
+ keyToComponent.put(key, comp);
+ }
+
+ public void clearBindingsForComp(JComponent comp)
+ {
+ // This method clears all the WHEN_IN_FOCUSED_WINDOW bindings associated
+ // with <code>comp</code>. This is used for a terribly ineffcient
+ // strategy in which JComponent.updateComponentInputMap simply clears
+ // all bindings associated with its component and then reloads all the
+ // bindings from the updated ComponentInputMap. This is only a preliminary
+ // strategy and should be improved upon once the WHEN_IN_FOCUSED_WINDOW
+ // bindings work.
+
+ // Find the top-level ancestor
+
+ Container topLevel = findTopLevel(comp);
+ if (topLevel == null)
+ return;
+ // And now get its Hashtable
+ Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
+
+ Enumeration keys = keyToComponent.keys();
+ Object temp;
+
+ // Iterate through the keys and remove any key whose value is comp
+ while (keys.hasMoreElements())
+ {
+ temp = keys.nextElement();
+ if (comp == (JComponent)keyToComponent.get(temp))
+ keyToComponent.remove(temp);
+ }
+ }
+
+ /**
+ * This method registers all the bindings in the given ComponentInputMap.
+ * Rather than call registerBinding on all the keys, we do the work here
+ * so that we don't duplicate finding the top-level container and
+ * getting its Hashtable.
+ *
+ * @param map the ComponentInputMap whose bindings we want to register
+ */
+ public void registerEntireMap (ComponentInputMap map)
+ {
+ if (map == null)
+ return;
+ JComponent comp = map.getComponent();
+ KeyStroke[] keys = map.allKeys();
+ if (keys == null)
+ return;
+ // Find the top-level container associated with this ComponentInputMap
+ Container topLevel = findTopLevel(comp);
+ if (topLevel == null)
+ return;
+
+ // Register the KeyStrokes in the top-level container's Hashtable
+ Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
+ for (int i = 0; i < keys.length; i++)
+ keyToComponent.put(keys[i], comp);
+ }
+
+ public boolean processKeyStroke (Component comp, KeyStroke key, KeyEvent e)
+ {
+ boolean pressed = e.getID() == KeyEvent.KEY_PRESSED;
+
+ // Look for the top-level ancestor
+ Container topLevel = findTopLevel(comp);
+ if (topLevel == null)
+ return false;
+ // Now get the Hashtable for that top-level container
+ Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
+ Enumeration keys = keyToComponent.keys();
+ JComponent target = (JComponent)keyToComponent.get(key);
+ if (target != null && target.processKeyBinding
+ (key, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed))
+ return true;
+
+ // Have to give all the JMenuBars a chance to consume the event
+ Vector menuBars = getVectorForTopLevel(topLevel);
+ for (int i = 0; i < menuBars.size(); i++)
+ if (((JMenuBar)menuBars.elementAt(i)).processKeyBinding(key, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns the Vector of JMenuBars associated with the top-level
+ * @param c the top-level container whose JMenuBar Vector we want
+ * @return the Vector of JMenuBars for this top level container
+ */
+ Vector getVectorForTopLevel(Container c)
+ {
+ Vector result = (Vector) menuBarLookup.get(c);
+ if (result == null)
+ {
+ result = new Vector();
+ menuBarLookup.put (c, result);
+ }
+ return result;
+ }
+
+ /**
+ * In processKeyStroke, KeyManager must give all JMenuBars in the
+ * focused top-level container a chance to process the event. So,
+ * JMenuBars must be registered in KeyManager and associated with a
+ * top-level container. That's what this method is for.
+ * @param menuBar the JMenuBar to register
+ */
+ public void registerJMenuBar (JMenuBar menuBar)
+ {
+ Container topLevel = findTopLevel(menuBar);
+ Vector menuBars = getVectorForTopLevel(topLevel);
+ if (!menuBars.contains(menuBar))
+ menuBars.add(menuBar);
+ }
+
+ /**
+ * Unregisters a JMenuBar from its top-level container. This is
+ * called before the JMenuBar is actually removed from the container
+ * so findTopLevel will still find us the correct top-level container.
+ * @param menuBar the JMenuBar to unregister.
+ */
+ public void unregisterJMenuBar (JMenuBar menuBar)
+ {
+ Container topLevel = findTopLevel(menuBar);
+ Vector menuBars = getVectorForTopLevel(topLevel);
+ if (menuBars.contains(menuBar))
+ menuBars.remove(menuBar);
+ }
+}
diff --git a/libjava/classpath/javax/swing/LayoutFocusTraversalPolicy.java b/libjava/classpath/javax/swing/LayoutFocusTraversalPolicy.java
new file mode 100644
index 000000000..335bc265d
--- /dev/null
+++ b/libjava/classpath/javax/swing/LayoutFocusTraversalPolicy.java
@@ -0,0 +1,89 @@
+/* LayoutFocusTraversalPolicy.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * @author Graydon Hoare
+ * @author Michael Koch
+ *
+ * @since 1.4
+ */
+public class LayoutFocusTraversalPolicy
+ extends SortingFocusTraversalPolicy
+ implements Serializable
+{
+ private static class LayoutComparator
+ implements Comparator
+ {
+ public LayoutComparator()
+ {
+ // Do nothing here.
+ }
+
+ public int compare(Object o1, Object o2)
+ {
+ Component comp1 = (Component) o1;
+ Component comp2 = (Component) o2;
+
+ int x1 = comp1.getX();
+ int y1 = comp1.getY();
+ int x2 = comp2.getX();
+ int y2 = comp2.getY();
+
+ if (x1 == x2 && y1 == y2)
+ return 0;
+
+ if ((y1 < y2) || ((y1 == y2) && (x1 < x2)))
+ return -1;
+
+ return 1;
+ }
+ }
+
+ private static final long serialVersionUID = 4312146927238881442L;
+
+ public LayoutFocusTraversalPolicy()
+ {
+ super(new LayoutComparator());
+ }
+}
diff --git a/libjava/classpath/javax/swing/ListCellRenderer.java b/libjava/classpath/javax/swing/ListCellRenderer.java
new file mode 100644
index 000000000..0e56d5016
--- /dev/null
+++ b/libjava/classpath/javax/swing/ListCellRenderer.java
@@ -0,0 +1,50 @@
+/* ListCellRenderer.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+
+/**
+ * Renders the cells of a {@link JList}.
+ */
+public interface ListCellRenderer
+{
+ Component getListCellRendererComponent(JList list, Object value, int index,
+ boolean isSelected,
+ boolean cellHasFocus);
+}
diff --git a/libjava/classpath/javax/swing/ListModel.java b/libjava/classpath/javax/swing/ListModel.java
new file mode 100644
index 000000000..736627e85
--- /dev/null
+++ b/libjava/classpath/javax/swing/ListModel.java
@@ -0,0 +1,80 @@
+/* ListModel.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.event.ListDataListener;
+
+/**
+ * The data model that is typically used in {@link JList}.
+ *
+ * @author Graydon Hoare (graydon@redhat.com)
+ */
+public interface ListModel
+{
+ /**
+ * Return the number of data elements in the list.
+ *
+ * @return The number of data elements in the list
+ */
+ int getSize();
+
+ /**
+ * Retrieves a data element at a specified index.
+ *
+ * @param index The index of the element to retrieve
+ *
+ * @return The data element at the specified index
+ */
+ Object getElementAt(int index);
+
+ /**
+ * Add a listener object to this model. The listener will be called
+ * any time the set of elements in the model is changed.
+ *
+ * @param l The listener to add
+ */
+ void addListDataListener(ListDataListener l);
+
+ /**
+ * Add a listener object to this model. The listener will no longer be
+ * called when the set of elements in the model is changed.
+ *
+ * @param l The listener to remove
+ */
+ void removeListDataListener(ListDataListener l);
+}
diff --git a/libjava/classpath/javax/swing/ListSelectionModel.java b/libjava/classpath/javax/swing/ListSelectionModel.java
new file mode 100644
index 000000000..1cd67e948
--- /dev/null
+++ b/libjava/classpath/javax/swing/ListSelectionModel.java
@@ -0,0 +1,332 @@
+/* ListSelectionModel.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+/**
+ * A model that tracks the selection status of a list of items. Each item in
+ * the list is identified by a zero-based index only, so the model can be used
+ * to track the selection status of any type of list. The model
+ * supports three modes:
+ * <ul>
+ * <li><code>SINGLE_SELECTION</code> - only one item in the list may be
+ * selected;</li>
+ * <li><code>SINGLE_INTERVAL_SELECTION</code> - only one interval in the list
+ * may be selected;</li>
+ * <li><code>MULTIPLE_INTERVAL_SELECTION</code> - any combination of items in
+ * the list may be selected.</li>
+ * </ul>
+ * The model uses an event notification mechanism to notify listeners (see
+ * {@link ListSelectionListener}) about updates to the selection model.
+ * <p>
+ * This model is used to track row selections in the {@link JList} component,
+ * and row and column selections in the {@link JTable} component.
+ */
+public interface ListSelectionModel
+{
+
+ /**
+ * A selection mode in which only one item can be selected.
+ *
+ * @see #setSelectionMode(int)
+ */
+ int SINGLE_SELECTION = 0;
+
+ /**
+ * A selection mode in which a single interval can be selected (an interval
+ * is a range containing one or more contiguous items).
+ *
+ * @see #setSelectionMode(int)
+ */
+ int SINGLE_INTERVAL_SELECTION = 1;
+
+ /**
+ * A selection mode in which any combination of items can be selected.
+ *
+ * @see #setSelectionMode(int)
+ */
+ int MULTIPLE_INTERVAL_SELECTION = 2;
+
+ /**
+ * Sets the selection mode.
+ * <p>
+ * FIXME: The spec is silent about what happens to existing selections, for
+ * example when changing from an interval selection to single selection.
+ *
+ * @param mode one of {@link #SINGLE_SELECTION},
+ * {@link #SINGLE_INTERVAL_SELECTION} and
+ * {@link #MULTIPLE_INTERVAL_SELECTION}.
+ *
+ * @see #getSelectionMode()
+ *
+ * @throws IllegalArgumentException if <code>mode</code> is not one of the
+ * specified values.
+ */
+ void setSelectionMode(int mode);
+
+ /**
+ * Returns the selection mode, which is one of {@link #SINGLE_SELECTION},
+ * {@link #SINGLE_INTERVAL_SELECTION} and
+ * {@link #MULTIPLE_INTERVAL_SELECTION}.
+ *
+ * @return The selection mode.
+ *
+ * @see #setSelectionMode(int)
+ */
+ int getSelectionMode();
+
+ /**
+ * Clears the current selection from the model. If the selection state
+ * changes (that is, the existing selection is non-empty) a
+ * {@link ListSelectionEvent} should be sent to all registered listeners.
+ * <p>
+ * FIXME: what happens to the anchor and lead selection indices (the spec
+ * is silent about this)? See:
+ * <p>
+ * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4334792
+ */
+ void clearSelection();
+
+ /**
+ * Returns the lowest selected index, or <code>-1</code> if there is no
+ * selection.
+ *
+ * @return The lowest selected index.
+ *
+ * @see #getMaxSelectionIndex()
+ */
+ int getMinSelectionIndex();
+
+ /**
+ * Returns the highest selected index, or <code>-1</code> if there is no
+ * selection.
+ *
+ * @return The highest selected index.
+ *
+ * @see #getMinSelectionIndex()
+ */
+ int getMaxSelectionIndex();
+
+ /**
+ * Returns <code>true</code> if the specified item is selected, and
+ * <code>false</code> otherwise. Special note: if <code>index</code> is
+ * negative, this method should return <code>false</code> (no exception
+ * should be thrown).
+ *
+ * @param index the item index (zero-based).
+ *
+ * @return <code>true</code> if the specified item is selected, and
+ * <code>false</code> otherwise.
+ */
+ boolean isSelectedIndex(int index);
+
+ /**
+ * Returns <code>true</code> if there is no selection, and <code>false</code>
+ * otherwise.
+ *
+ * @return <code>true</code> if there is no selection, and
+ * <code>false</code> otherwise.
+ */
+ boolean isSelectionEmpty();
+
+ /**
+ * Sets the selection interval to the specified range (note that
+ * <code>anchor</code> can be less than, equal to, or greater than
+ * <code>lead</code>). If this results in the selection being changed,
+ * a {@link ListSelectionEvent} is sent to all registered listeners.
+ * <p>
+ * If the selection mode is {@link #SINGLE_SELECTION}, only the
+ * <code>lead</code> item is selected.
+ *
+ * @param anchor the anchor index.
+ * @param lead the lead index.
+ */
+ void setSelectionInterval(int anchor, int lead);
+
+ /**
+ * Marks the items in the specified interval as selected. The behaviour of
+ * this method depends on the selection mode:
+ * <ul>
+ * <li><code>SINGLE_SELECTION</code> - only the <code>lead</code> item is
+ * selected;</li>
+ * <li><code>SINGLE_INTERVAL_SELECTION</code> - the existing selection
+ * interval is replaced by the specified interval;</li>
+ * <li><code>MULTIPLE_INTERVAL_SELECTION</code> - the specified interval is
+ * merged into the currently selected intervals.</li>
+ * </ul>
+ * Note that <code>anchor</code> can be less than, equal to, or greater than
+ * <code>lead</code>.
+ *
+ * @param anchor the index of the anchor item
+ * @param lead the index of the lead item.
+ */
+ void addSelectionInterval(int anchor, int lead);
+
+ /**
+ * Marks the items in the specified interval as not selected. The behaviour
+ * of this method depends on the selection mode:
+ * <ul>
+ * <li><code>SINGLE_SELECTION</code> - XXX;</li>
+ * <li><code>SINGLE_INTERVAL_SELECTION</code> - XXX;</li>
+ * <li><code>MULTIPLE_INTERVAL_SELECTION</code> - XXX.</li>
+ * </ul>
+ * Note that <code>anchor</code> can be less than, equal to, or greater than
+ * <code>lead</code>.
+ *
+ * @param anchor the index of the anchor item
+ * @param lead the index of the lead item.
+ */
+ void removeSelectionInterval(int anchor, int lead);
+
+ /**
+ * Inserts a new interval containing <code>length</code> items at the
+ * specified <code>index</code> (the <code>before</code> flag indicates
+ * whether the range is inserted before or after the existing item at
+ * <code>index</code>).
+ *
+ * FIXME: What is the selection status of the new items? Bug 4870694.
+ * FIXME: What event is generated?
+ *
+ * @param index the index of the item.
+ * @param length the number of items in the interval to be inserted.
+ * @param before if <code>true</code>, the interval should be inserted
+ * before <code>index</code>, otherwise it is inserted after.
+ *
+ * @see #removeIndexInterval(int, int)
+ */
+ void insertIndexInterval(int index, int length, boolean before);
+
+ /**
+ * Removes the items in the specified range (inclusive) from the selection
+ * model. This method should be called when an interval is deleted from
+ * the underlying list.
+ *
+ * FIXME: what happens to the lead and anchor indices if they are part of
+ * the range that is removed?
+ * FIXME: what event is generated
+ *
+ * @param index0 XXX
+ * @param index1 XXX
+ *
+ * @see #insertIndexInterval(int, int, boolean)
+ */
+ void removeIndexInterval(int index0, int index1);
+
+ /**
+ * Returns the index of the anchor item.
+ *
+ * @return The index of the anchor item.
+ *
+ * @see #setAnchorSelectionIndex(int)
+ */
+ int getAnchorSelectionIndex();
+
+ /**
+ * Sets the index of the anchor item.
+ *
+ * @param index the item index.
+ *
+ * @see #getAnchorSelectionIndex()
+ */
+ void setAnchorSelectionIndex(int index);
+
+ /**
+ * Returns the index of the lead item.
+ *
+ * @return The index of the lead item.
+ *
+ * @see #setLeadSelectionIndex(int)
+ */
+ int getLeadSelectionIndex();
+
+ /**
+ * Sets the index of the lead item.
+ *
+ * @param index the item index.
+ *
+ * @see #getLeadSelectionIndex()
+ */
+ void setLeadSelectionIndex(int index);
+
+ /**
+ * Sets the flag that is passed to listeners for each change notification.
+ * If a sequence of changes is made to the selection model, this flag should
+ * be set to <code>true</code> at the start of the sequence, and
+ * <code>false</code> for the last change - this gives listeners the option
+ * to ignore interim changes if that is more efficient.
+ *
+ * @param valueIsAdjusting the flag value.
+ *
+ * @see #getValueIsAdjusting()
+ */
+ void setValueIsAdjusting(boolean valueIsAdjusting);
+
+ /**
+ * Returns a flag that is passed to registered listeners when changes are
+ * made to the model. See the description for
+ * {@link #setValueIsAdjusting(boolean)} for more information.
+ *
+ * @return The flag.
+ */
+ boolean getValueIsAdjusting();
+
+ /**
+ * Registers a listener with the model so that it receives notification
+ * of changes to the model.
+ *
+ * @param listener the listener (<code>null</code> ignored).
+ *
+ * @see #removeListSelectionListener(ListSelectionListener)
+ */
+ void addListSelectionListener(ListSelectionListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives notification of
+ * changes to the model. If the specified listener is not registered with
+ * the model, or is <code>null</code>, this method does nothing.
+ *
+ * @param listener the listener (<code>null</code> ignored).
+ *
+ * @see #addListSelectionListener(ListSelectionListener)
+ */
+ void removeListSelectionListener(ListSelectionListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/LookAndFeel.java b/libjava/classpath/javax/swing/LookAndFeel.java
new file mode 100644
index 000000000..aec6ebb7f
--- /dev/null
+++ b/libjava/classpath/javax/swing/LookAndFeel.java
@@ -0,0 +1,433 @@
+/* LookAndFeel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.net.URL;
+
+import javax.swing.border.Border;
+import javax.swing.plaf.ComponentInputMapUIResource;
+import javax.swing.plaf.IconUIResource;
+import javax.swing.plaf.InputMapUIResource;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+import javax.swing.text.JTextComponent;
+
+/**
+ * A <i>look-and-feel</i> controls most aspects of the appearance and
+ * operation of user interface components in <code>javax.swing</code>. A
+ * cross-platform look-and-feel (the {@link MetalLookAndFeel}) is provided.
+ *
+ * @see UIManager#getInstalledLookAndFeels()
+ * @see UIManager#setLookAndFeel(LookAndFeel)
+ */
+public abstract class LookAndFeel
+{
+ /**
+ * Creates and returns a look-and-feel specific defaults table. This method
+ * is called once by {@link UIManager#setLookAndFeel(LookAndFeel)} and
+ * shouldn't be called again (as it creates a large table of defaults).
+ *
+ * @return The UI defaults.
+ */
+ public UIDefaults getDefaults()
+ {
+ return null;
+ }
+
+ /**
+ * Returns a description of the look and feel.
+ *
+ * @return A description of the look and feel.
+ */
+ public abstract String getDescription();
+
+ /**
+ * Returns the value of <code>Toolkit.getDefaultToolkit()
+ * .getDesktopProperty(systemPropertyName)</code>, or
+ * <code>fallbackValue</code> if no such property is defined.
+ *
+ * @param systemPropertyName the system property name.
+ * @param fallbackValue the fallback value.
+ *
+ * @return The property value or <code>fallbackValue</code>.
+ */
+ public static Object getDesktopPropertyValue(String systemPropertyName,
+ Object fallbackValue)
+ {
+ Object value = Toolkit.getDefaultToolkit().getDesktopProperty(
+ systemPropertyName);
+ return value != null ? value : fallbackValue;
+ }
+
+ /**
+ * Returns an identifier for the look and feel.
+ *
+ * @return An identifier for the look and feel.
+ */
+ public abstract String getID();
+
+ /**
+ * Returns the name for the look and feel.
+ *
+ * @return The name for the look and feel.
+ */
+ public abstract String getName();
+
+ /**
+ * Returns <code>true</code> when the look-and-feel supports window
+ * decorations, and <code>false</code> otherwise. This default implementation
+ * always returns <code>false</code> and needs to be overridden when the
+ * derived look-and-feel supports this.
+ *
+ * @return <code>false</code>.
+ *
+ * @since 1.4
+ */
+ public boolean getSupportsWindowDecorations()
+ {
+ return false;
+ }
+
+ /**
+ * Initializes the look-and-feel. The
+ * {@link UIManager#setLookAndFeel(LookAndFeel)} method calls this method
+ * before the first call (and typically the only call) to
+ * {@link #getDefaults()}. This default implementation does nothing, but
+ * subclasses can override this behaviour.
+ */
+ public void initialize()
+ {
+ // We do nothing here. This method is meant to be overridden by
+ // LookAndFeel implementations.
+ }
+
+ /**
+ * Convenience method for installing a component's default {@link Border}
+ * object on the specified component if either the border is currently
+ * <code>null</code> or already an instance of {@link UIResource}.
+ *
+ * @param c the component (<code>null</code> not permitted).
+ * @param defaultBorderName the border name (for lookup in the UIDefaults
+ * table).
+ */
+ public static void installBorder(JComponent c, String defaultBorderName)
+ {
+ Border b = c.getBorder();
+ if (b == null || b instanceof UIResource)
+ c.setBorder(UIManager.getBorder(defaultBorderName));
+ }
+
+ /**
+ * Convenience method for initializing a component's foreground and
+ * background color properties with values from the current defaults table.
+ *
+ * @param c the component (<code>null</code> not permitted).
+ * @param defaultBgName the key for the background color in the UIDefaults
+ * table.
+ * @param defaultFgName the key for the foreground color in the UIDefaults
+ * table.
+ */
+ public static void installColors(JComponent c, String defaultBgName,
+ String defaultFgName)
+ {
+ // Install background.
+ Color bg = c.getBackground();
+ if (bg == null || bg instanceof UIResource)
+ c.setBackground(UIManager.getColor(defaultBgName));
+
+ // Install foreground.
+ Color fg = c.getForeground();
+ if (fg == null || fg instanceof UIResource)
+ c.setForeground(UIManager.getColor(defaultFgName));
+ }
+
+ /**
+ * Convenience method for initializing a component's foreground, background
+ * and font properties with values from the current defaults table.
+ *
+ * @param component the component (<code>null</code> not permitted).
+ * @param defaultBgName the key for the background color in the UIDefaults
+ * table.
+ * @param defaultFgName the key for the foreground color in the UIDefaults
+ * table.
+ * @param defaultFontName the key for the font in the UIDefaults table.
+ */
+ public static void installColorsAndFont(JComponent component,
+ String defaultBgName,
+ String defaultFgName,
+ String defaultFontName)
+ {
+ // Install colors.
+ installColors(component, defaultBgName, defaultFgName);
+ // Install font.
+ Font f = component.getFont();
+ if (f == null || f instanceof UIResource)
+ component.setFont(UIManager.getFont(defaultFontName));
+ }
+
+ /**
+ * Returns <code>true</code> if the look-and-feel is the "native"
+ * look-and-feel for the current platform, and <code>false</code> otherwise.
+ * A native look-and-feel emulates the appearance and behaviour of the
+ * default windowing system on the host operating system.
+ *
+ * @return A flag indicating whether or not this is the native look and feel
+ * for the current platform.
+ */
+ public abstract boolean isNativeLookAndFeel();
+
+ /**
+ * Returns <code>true</code> if the look-and-feel is supported on the
+ * current operating system, and <code>false</code> otherwise. This
+ * mechanism is provided so that it is possible to prevent a look-and-feel
+ * from being used on some operating systems (usually for legal, not
+ * technical, reasons).
+ *
+ * @return A flag indicating whether or not the look-and-feel is supported
+ * on the current platform.
+ */
+ public abstract boolean isSupportedLookAndFeel();
+
+ /**
+ * Loads the bindings in keys into retMap. Does not remove existing entries
+ * from retMap. <code>keys</code> describes the InputMap, every even indexed
+ * item is either a KeyStroke or a String representing a KeyStroke and every
+ * odd indexed item is the Object associated with that KeyStroke in an
+ * ActionMap.
+ *
+ * @param retMap the InputMap into which we load bindings
+ * @param keys the Object array describing the InputMap as above
+ */
+ public static void loadKeyBindings(InputMap retMap, Object[] keys)
+ {
+ if (keys == null)
+ return;
+ for (int i = 0; i < keys.length - 1; i += 2)
+ {
+ Object key = keys[i];
+ KeyStroke keyStroke;
+ if (key instanceof KeyStroke)
+ keyStroke = (KeyStroke) key;
+ else
+ keyStroke = KeyStroke.getKeyStroke((String) key);
+ retMap.put(keyStroke, keys[i + 1]);
+ }
+ }
+
+ /**
+ * Creates a ComponentInputMap from keys.
+ * <code>keys</code> describes the InputMap, every even indexed
+ * item is either a KeyStroke or a String representing a KeyStroke and every
+ * odd indexed item is the Object associated with that KeyStroke in an
+ * ActionMap.
+ *
+ * @param c the JComponent associated with the ComponentInputMap
+ * @param keys the Object array describing the InputMap as above
+ *
+ * @return A new input map.
+ */
+ public static ComponentInputMap makeComponentInputMap(JComponent c,
+ Object[] keys)
+ {
+ ComponentInputMap retMap = new ComponentInputMapUIResource(c);
+ loadKeyBindings(retMap, keys);
+ return retMap;
+ }
+
+ /**
+ * Utility method that creates a UIDefaults.LazyValue that creates an
+ * ImageIcon UIResource for the specified gifFile filename.
+ *
+ * @param baseClass the base class for accessing the icon resource.
+ * @param gifFile the file name.
+ *
+ * @return A {@link UIDefaults.LazyValue} that serves up an
+ * {@link IconUIResource}.
+ */
+ public static Object makeIcon(Class<?> baseClass, String gifFile)
+ {
+ final URL file = baseClass.getResource(gifFile);
+ return new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ return new IconUIResource(new ImageIcon(file));
+ }
+ };
+ }
+
+ /**
+ * Creates a InputMap from keys.
+ * <code>keys</code> describes the InputMap, every even indexed
+ * item is either a KeyStroke or a String representing a KeyStroke and every
+ * odd indexed item is the Object associated with that KeyStroke in an
+ * ActionMap.
+ *
+ * @param keys the Object array describing the InputMap as above
+ *
+ * @return A new input map.
+ */
+ public static InputMap makeInputMap(Object[] keys)
+ {
+ InputMap retMap = new InputMapUIResource();
+ loadKeyBindings(retMap, keys);
+ return retMap;
+ }
+
+ /**
+ * Convenience method for building lists of KeyBindings.
+ * <code>keyBindingList</code> is an array of KeyStroke-Action pairs where
+ * even indexed elements are KeyStrokes or Strings representing KeyStrokes
+ * and odd indexed elements are the associated Actions.
+ *
+ * @param keyBindingList the array of KeyStroke-Action pairs
+ * @return a JTextComponent.KeyBinding array
+ */
+ public static JTextComponent.KeyBinding[] makeKeyBindings(
+ Object[] keyBindingList)
+ {
+ JTextComponent.KeyBinding[] retBindings =
+ new JTextComponent.KeyBinding[keyBindingList.length / 2];
+ for (int i = 0; i < keyBindingList.length - 1; i += 2)
+ {
+ KeyStroke stroke;
+ if (keyBindingList[i] instanceof KeyStroke)
+ stroke = (KeyStroke) keyBindingList[i];
+ else
+ stroke = KeyStroke.getKeyStroke((String) keyBindingList[i]);
+ retBindings[i / 2] = new JTextComponent.KeyBinding(stroke,
+ (String) keyBindingList[i + 1]);
+ }
+ return retBindings;
+ }
+
+ /**
+ * Invoked when the user attempts an invalid operation. The default
+ * implementation just beeps. Subclasses that wish to change this need to
+ * override this method.
+ *
+ * @param component the component the error occured in
+ */
+ public void provideErrorFeedback(Component component)
+ {
+ Toolkit.getDefaultToolkit().beep();
+ }
+
+ /**
+ * Returns a string that displays and identifies this object's properties.
+ *
+ * @return string containing the description and class name.
+ */
+ public String toString()
+ {
+ return getDescription() + " " + getClass().getName();
+ }
+
+ /**
+ * UIManager.setLookAndFeel calls this method just before we're replaced by
+ * a new default look and feel.
+ */
+ public void uninitialize()
+ {
+ // We do nothing here. This method is meant to be overridden by
+ // LookAndFeel implementations.
+ }
+
+ /**
+ * Convenience method for un-installing a component's default border on the
+ * specified component if the border is currently an instance of UIResource.
+ *
+ * @param c the component (<code>null</code> not permitted).
+ */
+ public static void uninstallBorder(JComponent c)
+ {
+ if (c.getBorder() instanceof UIResource)
+ c.setBorder(null);
+ }
+
+ /**
+ * This methods installs a UI property if it hasn't already been set by an
+ * application. This method is used by UI delegates that install a default
+ * value for a property with a primitive type but do not want to override
+ * a value that has been set by an application.
+ *
+ * The supported properties depend on the actual type of the component and
+ * are listed in the table below. The supported properties are of course
+ * inherited to subclasses.
+ *
+ * <table>
+ * <tr><th>Type</th><th>Supported properties</th></tr>
+ * <tr><td><code>JComponent</code></td>
+ * <td><code>opaque, autoscrolls</code></td></tr>
+ * <tr><td><code>AbstractButton</code></td>
+ * <td><code>borderPainted, rolloverEnabled, iconTextGap,
+ * contentAreaFilled</code></td></tr>
+ * <tr><td><code>JDesktopPane</code></td>
+ * <td><code>dragMode</code></td></tr>
+ * <tr><td><code>JSplitPane</code></td>
+ * <td><code>dividerSize, oneTouchExpandable</code></td></tr>
+ * <tr><td><code>JTable</code></td>
+ * <td><code>rowHeight</code></td></tr>
+ * <tr><td><code>JTree</code></td>
+ * <td><code>rowHeight, scrollsOnExpand, showsRootHandles</code></td></tr>
+ * </table>
+ *
+ * @param c the component to install the property to
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if <code>c</code> or
+ * <code>propertyValue</code> is <code>null</code>
+ *
+ * @since 1.5
+ */
+ public static void installProperty(JComponent c, String propertyName,
+ Object value)
+ {
+ c.setUIProperty(propertyName, value);
+ }
+}
diff --git a/libjava/classpath/javax/swing/MenuElement.java b/libjava/classpath/javax/swing/MenuElement.java
new file mode 100644
index 000000000..dab7b9cf1
--- /dev/null
+++ b/libjava/classpath/javax/swing/MenuElement.java
@@ -0,0 +1,89 @@
+/* MenuElement.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+/**
+ * Defines the methods that any menu element in a {@link JMenu} must
+ * implement.
+ *
+ * @author Andrew Selkirk
+ */
+public interface MenuElement
+{
+
+ /**
+ * processMouseEvent
+ * @param event TODO
+ * @param path TODO
+ * @param manager TODO
+ */
+ void processMouseEvent(MouseEvent event, MenuElement[] path,
+ MenuSelectionManager manager);
+
+ /**
+ * processKeyEvent
+ * @param event TODO
+ * @param path TODO
+ * @param manager TODO
+ */
+ void processKeyEvent(KeyEvent event, MenuElement[] path,
+ MenuSelectionManager manager);
+
+ /**
+ * menuSelectionChanged
+ * @param included TODO
+ */
+ void menuSelectionChanged(boolean included);
+
+ /**
+ * getSubElements
+ * @returns MenuElement[]
+ */
+ MenuElement[] getSubElements();
+
+ /**
+ * getComponent
+ * @returns Component
+ */
+ Component getComponent();
+
+}
diff --git a/libjava/classpath/javax/swing/MenuSelectionManager.java b/libjava/classpath/javax/swing/MenuSelectionManager.java
new file mode 100644
index 000000000..0b654499d
--- /dev/null
+++ b/libjava/classpath/javax/swing/MenuSelectionManager.java
@@ -0,0 +1,440 @@
+/* MenuSelectionManager.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Vector;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * This class manages current menu selectection. It provides
+ * methods to clear and set current selected menu path.
+ * It also fires StateChange event to its registered
+ * listeners whenever selected path of the current menu hierarchy
+ * changes.
+ *
+ */
+public class MenuSelectionManager
+{
+ /** ChangeEvent fired when selected path changes*/
+ protected ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /** List of listeners for this MenuSelectionManager */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /** Default manager for the current menu hierarchy*/
+ private static final MenuSelectionManager manager = new MenuSelectionManager();
+
+ /** Path to the currently selected menu */
+ private Vector selectedPath = new Vector();
+
+ /**
+ * Fires StateChange event to registered listeners
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Adds ChangeListener to this MenuSelectionManager
+ *
+ * @param listener ChangeListener to add
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Removes ChangeListener from the list of registered listeners
+ * for this MenuSelectionManager.
+ *
+ * @param listener ChangeListner to remove
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Returns list of registered listeners with MenuSelectionManager
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Unselects all the menu elements on the selection path
+ */
+ public void clearSelectedPath()
+ {
+ // Send events from the bottom most item in the menu - hierarchy to the
+ // top most
+ for (int i = selectedPath.size() - 1; i >= 0; i--)
+ ((MenuElement) selectedPath.get(i)).menuSelectionChanged(false);
+
+ // clear selected path
+ selectedPath.clear();
+
+ // notify all listeners that the selected path was changed
+ fireStateChanged();
+ }
+
+ /**
+ * This method returns menu element on the selected path that contains
+ * given source point. If no menu element on the selected path contains this
+ * point, then null is returned.
+ *
+ * @param source Component relative to which sourcePoint is given
+ * @param sourcePoint point for which we want to find menu element that contains it
+ *
+ * @return Returns menu element that contains given source point and belongs
+ * to the currently selected path. Null is return if no such menu element found.
+ */
+ public Component componentForPoint(Component source, Point sourcePoint)
+ {
+ // Convert sourcePoint to screen coordinates.
+ Point sourcePointOnScreen = sourcePoint;
+
+ if (source.isShowing())
+ SwingUtilities.convertPointToScreen(sourcePointOnScreen, source);
+
+ Point compPointOnScreen;
+ Component resultComp = null;
+
+ // For each menu element on the selected path, express its location
+ // in terms of screen coordinates and check if there is any
+ // menu element on the selected path that contains given source point.
+ for (int i = 0; i < selectedPath.size(); i++)
+ {
+ Component comp = ((Component) selectedPath.get(i));
+ Dimension size = comp.getSize();
+
+ // convert location of this menu item to screen coordinates
+ compPointOnScreen = comp.getLocationOnScreen();
+
+ if (compPointOnScreen.x <= sourcePointOnScreen.x
+ && sourcePointOnScreen.x < compPointOnScreen.x + size.width
+ && compPointOnScreen.y <= sourcePointOnScreen.y
+ && sourcePointOnScreen.y < compPointOnScreen.y + size.height)
+ {
+ Point p = sourcePointOnScreen;
+
+ if (comp.isShowing())
+ SwingUtilities.convertPointFromScreen(p, comp);
+
+ resultComp = SwingUtilities.getDeepestComponentAt(comp, p.x, p.y);
+ break;
+ }
+ }
+ return resultComp;
+ }
+
+ /**
+ * Returns shared instance of MenuSelection Manager
+ *
+ * @return default Manager
+ */
+ public static MenuSelectionManager defaultManager()
+ {
+ return manager;
+ }
+
+ /**
+ * Returns path representing current menu selection
+ *
+ * @return Current selection path
+ */
+ public MenuElement[] getSelectedPath()
+ {
+ MenuElement[] path = new MenuElement[selectedPath.size()];
+
+ for (int i = 0; i < path.length; i++)
+ path[i] = (MenuElement) selectedPath.get(i);
+
+ return path;
+ }
+
+ /**
+ * Returns true if specified component is part of current menu
+ * heirarchy and false otherwise
+ *
+ * @param c Component for which to check
+ * @return True if specified component is part of current menu
+ */
+ public boolean isComponentPartOfCurrentMenu(Component c)
+ {
+ MenuElement[] subElements;
+ boolean ret = false;
+ for (int i = 0; i < selectedPath.size(); i++)
+ {
+ // Check first element.
+ MenuElement first = (MenuElement) selectedPath.get(i);
+ if (SwingUtilities.isDescendingFrom(c, first.getComponent()))
+ {
+ ret = true;
+ break;
+ }
+ else
+ {
+ // Check sub elements.
+ subElements = first.getSubElements();
+ for (int j = 0; j < subElements.length; j++)
+ {
+ MenuElement me = subElements[j];
+ if (me != null
+ && (SwingUtilities.isDescendingFrom(c, me.getComponent())))
+ {
+ ret = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Processes key events on behalf of the MenuElements. MenuElement
+ * instances should always forward their key events to this method and
+ * get their {@link MenuElement#processKeyEvent(KeyEvent, MenuElement[],
+ * MenuSelectionManager)} eventually called back.
+ *
+ * @param e the key event
+ */
+ public void processKeyEvent(KeyEvent e)
+ {
+ MenuElement[] selection = (MenuElement[])
+ selectedPath.toArray(new MenuElement[selectedPath.size()]);
+ if (selection.length == 0)
+ return;
+
+ MenuElement[] path;
+ for (int index = selection.length - 1; index >= 0; index--)
+ {
+ MenuElement el = selection[index];
+ // This method's main purpose is to forward key events to the
+ // relevant menu items, so that they can act in response to their
+ // mnemonics beeing typed. So we also need to forward the key event
+ // to all the subelements of the currently selected menu elements
+ // in the path.
+ MenuElement[] subEls = el.getSubElements();
+ path = null;
+ for (int subIndex = 0; subIndex < subEls.length; subIndex++)
+ {
+ MenuElement sub = subEls[subIndex];
+ // Skip elements that are not showing or not enabled.
+ if (sub == null || ! sub.getComponent().isShowing()
+ || ! sub.getComponent().isEnabled())
+ {
+ continue;
+ }
+
+ if (path == null)
+ {
+ path = new MenuElement[index + 2];
+ System.arraycopy(selection, 0, path, 0, index + 1);
+ }
+ path[index + 1] = sub;
+ sub.processKeyEvent(e, path, this);
+ if (e.isConsumed())
+ break;
+ }
+ if (e.isConsumed())
+ break;
+ }
+
+ // Dispatch to first element in selection if it hasn't been consumed.
+ if (! e.isConsumed())
+ {
+ path = new MenuElement[1];
+ path[0] = selection[0];
+ path[0].processKeyEvent(e, path, this);
+ }
+ }
+
+ /**
+ * Forwards given mouse event to all of the source subcomponents.
+ *
+ * @param event Mouse event
+ */
+ public void processMouseEvent(MouseEvent event)
+ {
+ Component source = ((Component) event.getSource());
+
+ // In the case of drag event, event.getSource() returns component
+ // where drag event originated. However menu element processing this
+ // event should be the one over which mouse is currently located,
+ // which is not necessary the source of the drag event.
+ Component mouseOverMenuComp;
+
+ // find over which menu element the mouse is currently located
+ if (event.getID() == MouseEvent.MOUSE_DRAGGED
+ || event.getID() == MouseEvent.MOUSE_RELEASED)
+ mouseOverMenuComp = componentForPoint(source, event.getPoint());
+ else
+ mouseOverMenuComp = source;
+
+ // Process this event only if mouse is located over some menu element
+ if (mouseOverMenuComp != null && (mouseOverMenuComp instanceof MenuElement))
+ {
+ MenuElement[] path = getPath(mouseOverMenuComp);
+ ((MenuElement) mouseOverMenuComp).processMouseEvent(event, path,
+ manager);
+
+ // FIXME: Java specification says that mouse events should be
+ // forwarded to subcomponents. The code below does it, but
+ // menu's work fine without it. This code is commented for now.
+
+ /*
+ MenuElement[] subComponents = ((MenuElement) mouseOverMenuComp)
+ .getSubElements();
+
+ for (int i = 0; i < subComponents.length; i++)
+ {
+ subComponents[i].processMouseEvent(event, path, manager);
+ }
+ */
+ }
+ else
+ {
+ if (event.getID() == MouseEvent.MOUSE_RELEASED)
+ clearSelectedPath();
+ }
+ }
+
+ /**
+ * Sets menu selection to the specified path
+ *
+ * @param path new selection path
+ */
+ public void setSelectedPath(MenuElement[] path)
+ {
+ if (path == null)
+ {
+ clearSelectedPath();
+ return;
+ }
+
+ int minSize = path.length; // size of the smaller path.
+ int currentSize = selectedPath.size();
+ int firstDiff = 0;
+
+ // Search first item that is different in the current and new path.
+ for (int i = 0; i < minSize; i++)
+ {
+ if (i < currentSize && (MenuElement) selectedPath.get(i) == path[i])
+ firstDiff++;
+ else
+ break;
+ }
+
+ // Remove items from selection and send notification.
+ for (int i = currentSize - 1; i >= firstDiff; i--)
+ {
+ MenuElement el = (MenuElement) selectedPath.get(i);
+ selectedPath.remove(i);
+ el.menuSelectionChanged(false);
+ }
+
+ // Add new items to selection and send notification.
+ for (int i = firstDiff; i < minSize; i++)
+ {
+ if (path[i] != null)
+ {
+ selectedPath.add(path[i]);
+ path[i].menuSelectionChanged(true);
+ }
+ }
+
+ fireStateChanged();
+ }
+
+ /**
+ * Returns path to the specified component
+ *
+ * @param c component for which to find path for
+ *
+ * @return path to the specified component
+ */
+ private MenuElement[] getPath(Component c)
+ {
+ // FIXME: There is the same method in BasicMenuItemUI. However I
+ // cannot use it here instead of this method, since I cannot assume that
+ // all the menu elements on the selected path are JMenuItem or JMenu.
+ // For now I've just duplicated it here. Please
+ // fix me or delete me if another better approach will be found, and
+ // this method will not be necessary.
+ ArrayList path = new ArrayList();
+
+ // if given component is JMenu, we also need to include
+ // it's popup menu in the path
+ if (c instanceof JMenu)
+ path.add(((JMenu) c).getPopupMenu());
+ while (c instanceof MenuElement)
+ {
+ path.add(0, (MenuElement) c);
+
+ if (c instanceof JPopupMenu)
+ c = ((JPopupMenu) c).getInvoker();
+ else
+ c = c.getParent();
+ }
+
+ MenuElement[] pathArray = new MenuElement[path.size()];
+ path.toArray(pathArray);
+ return pathArray;
+ }
+}
diff --git a/libjava/classpath/javax/swing/MutableComboBoxModel.java b/libjava/classpath/javax/swing/MutableComboBoxModel.java
new file mode 100644
index 000000000..93091786e
--- /dev/null
+++ b/libjava/classpath/javax/swing/MutableComboBoxModel.java
@@ -0,0 +1,82 @@
+/* MutableComboBoxModel.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * A data model used in {@link JComboBox}es that keeps track of the
+ * components data and provides methods to insert and remove elements from
+ * it. The classes implementing this interface should
+ * fire appropriate events indicating the undergoing change in the data model.
+ *
+ * @author Andrew Selkirk
+ * @author Olga Rodimina
+ */
+public interface MutableComboBoxModel extends ComboBoxModel
+{
+ /**
+ * This method adds given object to its data model.
+ *
+ * @param object element to add to the data model.
+ */
+ void addElement(Object object);
+
+ /**
+ * This method removes elements located at the given index in the data
+ * model.
+ *
+ * @param index index specifying location of the element to remove.
+ */
+ void removeElementAt(int index);
+
+ /**
+ * This method inserts givent element to the data model, at the specified
+ * index.
+ *
+ * @param object element to insert
+ * @param index index specifying the position in the data model where the
+ * given element should be inserted.
+ */
+ void insertElementAt(Object object, int index);
+
+ /**
+ * This method removes given element from the data model
+ *
+ * @param object element to remove.
+ */
+ void removeElement(Object object);
+}
diff --git a/libjava/classpath/javax/swing/OverlayLayout.java b/libjava/classpath/javax/swing/OverlayLayout.java
new file mode 100644
index 000000000..b037a09a7
--- /dev/null
+++ b/libjava/classpath/javax/swing/OverlayLayout.java
@@ -0,0 +1,412 @@
+/* OverlayLayout.java -- A layout manager
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTError;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager2;
+import java.io.Serializable;
+
+/**
+ * A layout manager that lays out the components of a container one over
+ * another.
+ *
+ * The components take as much space as is available in the container, but not
+ * more than specified by their maximum size.
+ *
+ * The overall layout is mainly affected by the components
+ * <code>alignmentX</code> and <code>alignmentY</code> properties. All
+ * components are aligned, so that their alignment points (for either
+ * direction) are placed in one line (the baseline for this direction).
+ *
+ * For example: An X alignment of 0.0 means that the component's alignment
+ * point is at it's left edge, an X alignment of 0.5 means that the alignment
+ * point is in the middle, an X alignment of 1.0 means, the aligment point is
+ * at the right edge. So if you have three components, the first with 0.0, the
+ * second with 0.5 and the third with 1.0, then they are laid out like this:
+ *
+ * <pre>
+ * +-------+
+ * | 1 |
+ * +-------+
+ * +-------+
+ * | 2 |
+ * +-------+
+ * +---------+
+ * | 3 +
+ * +---------+
+ * </pre>
+ * The above picture shows the X alignment between the components. An Y
+ * alignment like shown above cannot be achieved with this layout manager. The
+ * components are place on top of each other, with the X alignment shown above.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Andrew Selkirk
+ */
+public class OverlayLayout implements LayoutManager2, Serializable
+{
+ private static final long serialVersionUID = 18082829169631543L;
+
+ /**
+ * The container to be laid out.
+ */
+ private Container target;
+
+ /**
+ * The size requirements of the containers children for the X direction.
+ */
+ private SizeRequirements[] xChildren;
+
+ /**
+ * The size requirements of the containers children for the Y direction.
+ */
+ private SizeRequirements[] yChildren;
+
+ /**
+ * The size requirements of the container to be laid out for the X direction.
+ */
+ private SizeRequirements xTotal;
+
+ /**
+ * The size requirements of the container to be laid out for the Y direction.
+ */
+ private SizeRequirements yTotal;
+
+ /**
+ * The offsets of the child components in the X direction.
+ */
+ private int[] offsetsX;
+
+ /**
+ * The offsets of the child components in the Y direction.
+ */
+ private int[] offsetsY;
+
+ /**
+ * The spans of the child components in the X direction.
+ */
+ private int[] spansX;
+
+ /**
+ * The spans of the child components in the Y direction.
+ */
+ private int[] spansY;
+
+ /**
+ * Creates a new OverlayLayout for the specified container.
+ *
+ * @param target the container to be laid out
+ */
+ public OverlayLayout(Container target)
+ {
+ this.target = target;
+ }
+
+ /**
+ * Notifies the layout manager that the layout has become invalid. It throws
+ * away cached layout information and recomputes it the next time it is
+ * requested.
+ *
+ * @param target not used here
+ */
+ public void invalidateLayout(Container target)
+ {
+ xChildren = null;
+ yChildren = null;
+ xTotal = null;
+ yTotal = null;
+ offsetsX = null;
+ offsetsY = null;
+ spansX = null;
+ spansY = null;
+ }
+
+ /**
+ * This method is not used in this layout manager.
+ *
+ * @param string not used here
+ * @param component not used here
+ */
+ public void addLayoutComponent(String string, Component component)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is not used in this layout manager.
+ *
+ * @param component not used here
+ * @param constraints not used here
+ */
+ public void addLayoutComponent(Component component, Object constraints)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is not used in this layout manager.
+ *
+ * @param component not used here
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the preferred size of the container that is laid out. This is
+ * computed by the children's preferred sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the preferred size of the container that is laid out
+ */
+ public Dimension preferredLayoutSize(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return new Dimension(xTotal.preferred, yTotal.preferred);
+ }
+
+ /**
+ * Returns the minimum size of the container that is laid out. This is
+ * computed by the children's minimum sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the minimum size of the container that is laid out
+ */
+ public Dimension minimumLayoutSize(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return new Dimension(xTotal.minimum, yTotal.minimum);
+ }
+
+ /**
+ * Returns the maximum size of the container that is laid out. This is
+ * computed by the children's maximum sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the maximum size of the container that is laid out
+ */
+ public Dimension maximumLayoutSize(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return new Dimension(xTotal.maximum, yTotal.maximum);
+ }
+
+ /**
+ * Returns the X alignment of the container that is laid out. This is
+ * computed by the children's preferred sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the X alignment of the container that is laid out
+ */
+ public float getLayoutAlignmentX(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return xTotal.alignment;
+ }
+
+ /**
+ * Returns the Y alignment of the container that is laid out. This is
+ * computed by the children's preferred sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the X alignment of the container that is laid out
+ */
+ public float getLayoutAlignmentY(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return yTotal.alignment;
+ }
+
+ /**
+ * Lays out the container and it's children.
+ *
+ * The children are laid out one over another.
+ *
+ * The components take as much space as is available in the container, but
+ * not more than specified by their maximum size.
+ *
+ * The overall layout is mainly affected by the components
+ * <code>alignmentX</code> and <code>alignmentY</code> properties. All
+ * components are aligned, so that their alignment points (for either
+ * direction) are placed in one line (the baseline for this direction).
+ *
+ * For example: An X alignment of 0.0 means that the component's alignment
+ * point is at it's left edge, an X alignment of 0.5 means that the alignment
+ * point is in the middle, an X alignment of 1.0 means, the aligment point is
+ * at the right edge. So if you have three components, the first with 0.0,
+ * the second with 0.5 and the third with 1.0, then they are laid out like
+ * this:
+ *
+ * <pre>
+ * +-------+
+ * | 1 |
+ * +-------+
+ * +-------+
+ * | 2 |
+ * +-------+
+ * +---------+
+ * | 3 +
+ * +---------+
+ * </pre>
+ * The above picture shows the X alignment between the components. An Y
+ * alignment like shown above cannot be achieved with this layout manager.
+ * The components are place on top of each other, with the X alignment shown
+ * above.
+ *
+ * @param target not used here
+ */
+ public void layoutContainer(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkLayout();
+ Component[] children = target.getComponents();
+ for (int i = 0; i < children.length; i++)
+ children[i].setBounds(offsetsX[i], offsetsY[i], spansX[i], spansY[i]);
+ }
+
+ /**
+ * Makes sure that the xChildren and yChildren fields are correctly set up.
+ * A call to {@link #invalidateLayout(Container)} sets these fields to null,
+ * so they have to be set up again.
+ */
+ private void checkRequirements()
+ {
+ if (xChildren == null || yChildren == null)
+ {
+ Component[] children = target.getComponents();
+ xChildren = new SizeRequirements[children.length];
+ yChildren = new SizeRequirements[children.length];
+ for (int i = 0; i < children.length; i++)
+ {
+ if (! children[i].isVisible())
+ {
+ xChildren[i] = new SizeRequirements();
+ yChildren[i] = new SizeRequirements();
+ }
+ else
+ {
+ xChildren[i] =
+ new SizeRequirements(children[i].getMinimumSize().width,
+ children[i].getPreferredSize().width,
+ children[i].getMaximumSize().width,
+ children[i].getAlignmentX());
+ yChildren[i] =
+ new SizeRequirements(children[i].getMinimumSize().height,
+ children[i].getPreferredSize().height,
+ children[i].getMaximumSize().height,
+ children[i].getAlignmentY());
+ }
+ }
+ }
+ }
+
+ /**
+ * Makes sure that the xTotal and yTotal fields are set up correctly. A call
+ * to {@link #invalidateLayout} sets these fields to null and they have to be
+ * recomputed.
+ */
+ private void checkTotalRequirements()
+ {
+ if (xTotal == null || yTotal == null)
+ {
+ checkRequirements();
+ xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren);
+ yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren);
+ }
+ }
+
+ /**
+ * Makes sure that the offsetsX, offsetsY, spansX and spansY fields are set
+ * up correctly. A call to {@link #invalidateLayout} sets these fields
+ * to null and they have to be recomputed.
+ */
+ private void checkLayout()
+ {
+ if (offsetsX == null || offsetsY == null || spansX == null
+ || spansY == null)
+ {
+ checkRequirements();
+ checkTotalRequirements();
+ int len = target.getComponents().length;
+ offsetsX = new int[len];
+ offsetsY = new int[len];
+ spansX = new int[len];
+ spansY = new int[len];
+
+ Insets in = target.getInsets();
+ int width = target.getWidth() - in.left - in.right;
+ int height = target.getHeight() - in.top - in.bottom;
+
+ SizeRequirements.calculateAlignedPositions(width, xTotal,
+ xChildren, offsetsX, spansX);
+ SizeRequirements.calculateAlignedPositions(height, yTotal,
+ yChildren, offsetsY, spansY);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/Popup.java b/libjava/classpath/javax/swing/Popup.java
new file mode 100644
index 000000000..65be7cfe1
--- /dev/null
+++ b/libjava/classpath/javax/swing/Popup.java
@@ -0,0 +1,301 @@
+/* Popup.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Point;
+import java.awt.Rectangle;
+
+
+/**
+ * Manages a popup window that displays a Component on top of
+ * everything else.
+ *
+ * <p>To obtain an instance of <code>Popup</code>, use the
+ * {@link javax.swing.PopupFactory}.
+ *
+ * @since 1.4
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class Popup
+{
+ /**
+ * Constructs a new <code>Popup</code> given its owner,
+ * contents and the screen position where the popup
+ * will appear.
+ *
+ * @param owner the Component to which <code>x</code> and
+ * <code>y</code> are relative, or <code>null</code> for
+ * placing the popup relative to the origin of the screen.
+ *
+ * @param contents the contents that will be displayed inside
+ * the <code>Popup</code>.
+ *
+ * @param x the horizontal position where the Popup will appear.
+ *
+ * @param y the vertical position where the Popup will appear.
+ *
+ * @throws IllegalArgumentException if <code>contents</code>
+ * is <code>null</code>.
+ */
+ protected Popup(Component owner, Component contents,
+ int x, int y)
+ {
+ if (contents == null)
+ throw new IllegalArgumentException();
+
+ // The real stuff happens in the implementation of subclasses,
+ // for instance JWindowPopup.
+ }
+
+
+ /**
+ * Constructs a new <code>Popup</code>.
+ */
+ protected Popup()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Displays the <code>Popup</code> on the screen. Nothing happens
+ * if it is currently shown.
+ */
+ public void show()
+ {
+ // Implemented by subclasses, for instance JWindowPopup.
+ }
+
+
+ /**
+ * Removes the <code>Popup</code> from the screen. Nothing happens
+ * if it is currently hidden.
+ */
+ public void hide()
+ {
+ // Implemented by subclasses, for instance JWindowPopup.
+ }
+
+
+ /**
+ * A <code>Popup</code> that uses a <code>JWindow</code> for
+ * displaying its contents.
+ *
+ * @see PopupFactory#getPopup
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ static class JWindowPopup
+ extends Popup
+ {
+ /**
+ * The <code>JWindow</code> used for displaying the contents
+ * of the popup.
+ */
+ JWindow window;
+
+ private Component contents;
+
+ /**
+ * Constructs a new <code>JWindowPopup</code> given its owner,
+ * contents and the screen position where the popup
+ * will appear.
+ *
+ * @param owner the Component to which <code>x</code> and
+ * <code>y</code> are relative, or <code>null</code> for
+ * placing the popup relative to the origin of the screen.
+ *
+ * @param contents the contents that will be displayed inside
+ * the <code>Popup</code>.
+ *
+ * @param x the horizontal position where the Popup will appear.
+ *
+ * @param y the vertical position where the Popup will appear.
+ *
+ * @throws IllegalArgumentException if <code>contents</code>
+ * is <code>null</code>.
+ */
+ public JWindowPopup(Component owner, Component contents,
+ int x, int y)
+ {
+ /* Checks whether contents is null. */
+ super(owner, contents, x, y);
+
+ this.contents = contents;
+ window = new JWindow(SwingUtilities.getWindowAncestor(owner));
+ window.getContentPane().add(contents);
+ window.setLocation(x, y);
+ window.setFocusableWindowState(false);
+ }
+
+
+ /**
+ * Displays the popup's <code>JWindow</code> on the screen.
+ * Nothing happens if it is already visible.
+ */
+ public void show()
+ {
+ window.setSize(contents.getSize());
+ window.show();
+ }
+
+
+ /**
+ * Removes the popup's <code>JWindow</code> from the
+ * screen. Nothing happens if it is currently not visible.
+ */
+ public void hide()
+ {
+ /* Calling dispose() instead of hide() will conserve native
+ * system resources, for example memory in an X11 server.
+ * They will automatically be re-allocated by a call to
+ * show().
+ */
+ window.dispose();
+ }
+ }
+
+ /**
+ * A popup that displays itself within the JLayeredPane of a JRootPane of
+ * the containment hierarchy of the owner component.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ static class LightweightPopup extends Popup
+ {
+ /**
+ * The owner component for this popup.
+ */
+ Component owner;
+
+ /**
+ * The contents that should be shown.
+ */
+ Component contents;
+
+ /**
+ * The X location in screen coordinates.
+ */
+ int x;
+
+ /**
+ * The Y location in screen coordinates.
+ */
+ int y;
+
+ /**
+ * The panel that holds the content.
+ */
+ private JPanel panel;
+
+ /**
+ * The layered pane of the owner.
+ */
+ private JLayeredPane layeredPane;
+
+ /**
+ * Constructs a new <code>LightweightPopup</code> given its owner,
+ * contents and the screen position where the popup
+ * will appear.
+ *
+ * @param owner the component that should own the popup window; this
+ * provides the JRootPane in which we place the popup window
+ *
+ * @param contents the contents that will be displayed inside
+ * the <code>Popup</code>.
+ *
+ * @param x the horizontal position where the Popup will appear in screen
+ * coordinates
+ *
+ * @param y the vertical position where the Popup will appear in screen
+ * coordinates
+ *
+ * @throws IllegalArgumentException if <code>contents</code>
+ * is <code>null</code>.
+ */
+ public LightweightPopup(Component owner, Component contents, int x, int y)
+ {
+ super(owner, contents, x, y);
+ this.owner = owner;
+ this.contents = contents;
+ this.x = x;
+ this.y = y;
+
+ JRootPane rootPane = SwingUtilities.getRootPane(owner);
+ JLayeredPane layeredPane = rootPane.getLayeredPane();
+ this.layeredPane = layeredPane;
+ }
+
+ /**
+ * Places the popup within the JLayeredPane of the owner component and
+ * makes it visible.
+ */
+ public void show()
+ {
+ // We insert a JPanel between the layered pane and the contents so we
+ // can fiddle with the setLocation() method without disturbing a
+ // JPopupMenu (which overrides setLocation in an unusual manner).
+ if (panel == null)
+ {
+ panel = new JPanel();
+ panel.setLayout(new FlowLayout(0, 0, 0));
+ }
+
+ panel.add(contents);
+ panel.setSize(contents.getSize());
+ Point layeredPaneLoc = layeredPane.getLocationOnScreen();
+ panel.setLocation(x - layeredPaneLoc.x, y - layeredPaneLoc.y);
+ layeredPane.add(panel, JLayeredPane.POPUP_LAYER, 0);
+ panel.repaint();
+ }
+
+ /**
+ * Removes the popup from the JLayeredPane thus making it invisible.
+ */
+ public void hide()
+ {
+ Rectangle bounds = panel.getBounds();
+ layeredPane.remove(panel);
+ layeredPane.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/PopupFactory.java b/libjava/classpath/javax/swing/PopupFactory.java
new file mode 100644
index 000000000..9468c8864
--- /dev/null
+++ b/libjava/classpath/javax/swing/PopupFactory.java
@@ -0,0 +1,171 @@
+/* PopupFactory.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Dimension;
+import java.awt.Point;
+
+
+/**
+ * A factory for <code>Popup</code> objects. These are used to
+ * managed little windows that float over everything else,
+ * typically containing a popup menu.
+ *
+ * @since 1.4
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class PopupFactory
+{
+ /**
+ * The shared factory object.
+ *
+ * @see #getSharedInstance
+ * @see #setSharedInstance
+ */
+ private static PopupFactory sharedFactory;
+
+
+ /**
+ * Constructs a new <code>PopupFactory</code>. Usually, a single
+ * <code>PopupFactory</code> is shared among multiple consumers
+ * of <code>Popup</code>. Use {@link #getSharedInstance} to retrieve
+ * the current factory.
+ */
+ public PopupFactory()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Sets the shared factory.
+ *
+ * @param factory the PopupFactory that future invocations of
+ * {@link #getSharedInstance} will return.
+ *
+ * @throws IllegalArgumentException if <code>factory</code>
+ * is <code>null</code>.
+ */
+ public static void setSharedInstance(PopupFactory factory)
+ {
+ if (factory == null)
+ throw new IllegalArgumentException();
+
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ sharedFactory = factory;
+ }
+
+
+ /**
+ * Retrieves the shared factory, creating a new factory if
+ * necessary.
+ *
+ * @return a <code>PopupFactory</code> that can be used
+ * to create <code>Popup</code> objects.
+ */
+ public static PopupFactory getSharedInstance()
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (sharedFactory == null)
+ sharedFactory = new PopupFactory();
+
+ return sharedFactory;
+ }
+
+
+ /**
+ * Creates a new <code>Popup</code> given its owner,
+ * contents and the screen position where the popup
+ * will appear.
+ *
+ * @param owner the Component to which <code>x</code> and
+ * <code>y</code> are relative, or <code>null</code> for
+ * placing the popup relative to the origin of the screen.
+ *
+ * @param contents the contents that will be displayed inside
+ * the <code>Popup</code>.
+ *
+ * @param x the horizontal position where the Popup will appear.
+ *
+ * @param y the vertical position where the Popup will appear.
+ *
+ * @throws IllegalArgumentException if <code>contents</code>
+ * is <code>null</code>.
+ */
+ public Popup getPopup(Component owner, Component contents,
+ int x, int y)
+ {
+ Popup popup = null;
+ // By default we enable lightweight popups since they are more efficient
+ // than heavyweight popups.
+ boolean lightweightEnabled = true;
+ // Special case JPopupMenu here, since it supports a lightweightEnabled
+ // flag that we must respect.
+ if (contents instanceof JPopupMenu)
+ {
+ JPopupMenu menu = (JPopupMenu) contents;
+ lightweightEnabled = menu.isLightWeightPopupEnabled();
+ }
+
+ // If we have a root pane and the contents fits within the root pane and
+ // lightweight popups are enabled, than we can use a lightweight popup.
+ JRootPane root = SwingUtilities.getRootPane(owner);
+ if (root != null)
+ {
+ Point rootLoc = root.getLocationOnScreen();
+ Dimension contentsSize = contents.getSize();
+ Dimension rootSize = root.getSize();
+ if (x >= rootLoc.x && y > rootLoc.y
+ && (x - rootLoc.x) + contentsSize.width < rootSize.width
+ && (y - rootLoc.y) + contentsSize.height < rootSize.height)
+ popup = new Popup.LightweightPopup(owner, contents, x, y);
+ else
+ popup = new Popup.JWindowPopup(owner, contents, x, y);
+ }
+ else
+ popup = new Popup.JWindowPopup(owner, contents, x, y);
+ return popup;
+ }
+}
diff --git a/libjava/classpath/javax/swing/ProgressMonitor.java b/libjava/classpath/javax/swing/ProgressMonitor.java
new file mode 100644
index 000000000..0d7ab3e27
--- /dev/null
+++ b/libjava/classpath/javax/swing/ProgressMonitor.java
@@ -0,0 +1,460 @@
+/* ProgressMonitor.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.accessibility.AccessibleContext;
+
+/**
+ * <p>Using this class you can easily monitor tasks where you cannot
+ * estimate the duration exactly.</p>
+ *
+ * <p>A ProgressMonitor instance waits until the first time setProgress
+ * is called. When <code>millisToDecideToPopup</code> time elapsed the
+ * instance estimates the duration until the whole operation is completed.
+ * If this duration exceeds <code>millisToPopup</code> a non-modal dialog
+ * with a message and a progress bar is shown.</p>
+ *
+ * <p>The value of <code>millisToDecideToPopup</code> defaults to
+ * <code>500</code> and <code>millisToPopup</code> to
+ * <code>2000</code>.</p>
+ *
+ * @author Andrew Selkirk
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.2
+ * @status updated to 1.2
+ */
+public class ProgressMonitor
+{
+
+ /**
+ * The accessible content for this component
+ */
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * parentComponent
+ */
+ Component component;
+
+ /**
+ * note
+ */
+ String note;
+
+ /**
+ * message
+ */
+ Object message;
+
+ /**
+ * millisToDecideToPopup
+ */
+ int millisToDecideToPopup = 500;
+
+ /**
+ * millisToPopup
+ */
+ int millisToPopup = 2000;
+
+ int min, max, progress;
+
+ JProgressBar progressBar;
+
+ JLabel noteLabel;
+
+ JDialog progressDialog;
+
+ Timer timer;
+
+ boolean canceled;
+
+ /**
+ * Creates a new <code>ProgressMonitor</code> instance. This is used to
+ * monitor a task and pops up a dialog if the task is taking a long time to
+ * run.
+ *
+ * @param component The parent component of the progress dialog or
+ * <code>null</code>.
+ * @param message A constant message object which works in the way it does
+ * in {@link JOptionPane}.
+ * @param note A string message which can be changed while the operation goes
+ * on.
+ * @param minimum The minimum value for the operation (start value).
+ * @param maximum The maximum value for the operation (end value).
+ */
+ public ProgressMonitor(Component component, Object message,
+ String note, int minimum, int maximum)
+ {
+
+ // Set data.
+ this.component = component;
+ this.message = message;
+ this.note = note;
+
+ min = minimum;
+ max = maximum;
+ }
+
+ /**
+ * <p>Hides the dialog and stops any measurements.</p>
+ *
+ * <p>Has no effect when <code>setProgress</code> is not at least
+ * called once.</p>
+ */
+ public void close()
+ {
+ if (progressDialog != null)
+ {
+ progressDialog.setVisible(false);
+ }
+
+ if (timer != null)
+ {
+ timer.stop();
+ timer = null;
+ }
+ }
+
+ /**
+ * <p>Updates the progress value.</p>
+ *
+ * <p>When called for the first time this initializes a timer
+ * which decides after <code>millisToDecideToPopup</code> time
+ * whether to show a progress dialog or not.</p>
+ *
+ * <p>If the progress value equals or exceeds the maximum
+ * value the progress dialog is closed automatically.</p>
+ *
+ * @param progress New progress value.
+ */
+ public void setProgress(int progress)
+ {
+ this.progress = progress;
+
+ // Initializes and starts a timer with a task
+ // which measures the duration and displays
+ // a progress dialog if neccessary.
+ if (timer == null && progressDialog == null)
+ {
+ timer = new Timer(25, null);
+ timer.addActionListener(new TimerListener());
+ timer.start();
+ }
+
+ // Cancels timer and hides progress dialog if the
+ // maximum value is reached.
+ if (progressBar != null && this.progress >= progressBar.getMaximum())
+ {
+ // The reason for using progressBar.getMaximum() instead of max is that
+ // we want to prevent that changes to the value have any effect after the
+ // progress dialog is visible (This is how the JDK behaves.).
+ close();
+ }
+
+ }
+
+ /**
+ * Returns the minimum or start value of the operation.
+ *
+ * @return Minimum or start value of the operation.
+ */
+ public int getMinimum()
+ {
+ return min;
+ }
+
+ /**
+ * <p>Use this method to set the minimum or start value of
+ * your operation.</p>
+ *
+ * <p>For typical application like copy operation this will be
+ * zero.</p>
+ *
+ * <p>Keep in mind that changing this value after the progress
+ * dialog is made visible has no effect upon the progress bar.</p>
+ *
+ * @param minimum The new minimum value.
+ */
+ public void setMinimum(int minimum)
+ {
+ min = minimum;
+ }
+
+ /**
+ * Return the maximum or end value of your operation.
+ *
+ * @return Maximum or end value.
+ */
+ public int getMaximum()
+ {
+ return max;
+ }
+
+ /**
+ * <p>Sets the maximum or end value of the operation to the
+ * given integer.</p>
+ *
+ * @param maximum
+ */
+ public void setMaximum(int maximum)
+ {
+ max = maximum;
+ }
+
+ /**
+ * Returns whether the user canceled the operation.
+ *
+ * @return Whether the operation was canceled.
+ */
+ public boolean isCanceled()
+ {
+ // The value is predefined to false
+ // and changes only when the user clicks
+ // the cancel button in the progress dialog.
+ return canceled;
+ }
+
+ /**
+ * Returns the amount of milliseconds to wait
+ * until the ProgressMonitor should decide whether
+ * a progress dialog is to be shown or not.
+ *
+ * @return The duration in milliseconds.
+ */
+ public int getMillisToDecideToPopup()
+ {
+ return millisToDecideToPopup;
+ }
+
+ /**
+ * Sets the amount of milliseconds to wait until the
+ * ProgressMonitor should decide whether a progress dialog
+ * is to be shown or not.
+ *
+ * <p>This method has no effect when the progress dialog
+ * is already visible.</p>
+ *
+ * @param time The duration in milliseconds.
+ */
+ public void setMillisToDecideToPopup(int time)
+ {
+ millisToDecideToPopup = time;
+ }
+
+ /**
+ * Returns the number of milliseconds to wait before displaying the progress
+ * dialog. The default value is 2000.
+ *
+ * @return The number of milliseconds.
+ *
+ * @see #setMillisToPopup(int)
+ */
+ public int getMillisToPopup()
+ {
+ return millisToPopup;
+ }
+
+ /**
+ * Sets the number of milliseconds to wait before displaying the progress
+ * dialog.
+ *
+ * @param time the number of milliseconds.
+ *
+ * @see #getMillisToPopup()
+ */
+ public void setMillisToPopup(int time)
+ {
+ millisToPopup = time;
+ }
+
+ /**
+ * Returns a message which is shown in the progress dialog.
+ *
+ * @return The changeable message visible in the progress dialog.
+ */
+ public String getNote()
+ {
+ return note;
+ }
+
+ /**
+ * <p>Set the message shown in the progess dialog.</p>
+ *
+ * <p>Changing the note while the progress dialog is visible
+ * is possible.</p>
+ *
+ * @param note A message shown in the progress dialog.
+ */
+ public void setNote(String note)
+ {
+ if (noteLabel != null)
+ {
+ noteLabel.setText(note);
+ }
+ else
+ {
+ this.note = note;
+ }
+ }
+
+ /**
+ * Internal method that creates the progress dialog.
+ */
+ void createDialog()
+ {
+ // If there is no note we suppress the generation of the
+ // label.
+ Object[] tmp = (note == null) ?
+ new Object[]
+ {
+ message,
+ progressBar = new JProgressBar(min, max)
+ }
+ :
+ new Object[]
+ {
+ message,
+ noteLabel = new JLabel(note),
+ progressBar = new JProgressBar(min, max)
+ };
+
+ JOptionPane pane = new JOptionPane(tmp, JOptionPane.INFORMATION_MESSAGE);
+
+ // FIXME: Internationalize the button
+ JButton cancelButton = new JButton("Cancel");
+ cancelButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent ae)
+ {
+ canceled = true;
+ }
+ });
+
+ pane.setOptions(new Object[] { cancelButton });
+
+ // FIXME: Internationalize the title
+ progressDialog = pane.createDialog(component, "Progress ...");
+ progressDialog.setModal(false);
+ progressDialog.setResizable(true);
+
+ progressDialog.pack();
+ progressDialog.setVisible(true);
+
+ }
+
+ /** An ActionListener implementation which does the measurements
+ * and estimations of the ProgressMonitor.
+ */
+ class TimerListener implements ActionListener
+ {
+ long timestamp;
+
+ int lastProgress;
+
+ boolean first = true;
+
+ TimerListener()
+ {
+ timestamp = System.currentTimeMillis();
+ }
+
+ public void actionPerformed(ActionEvent ae)
+ {
+ long now = System.currentTimeMillis();
+
+ if (first)
+ {
+ if ((now - timestamp) > millisToDecideToPopup)
+ {
+ first = false;
+
+
+ long expected = (progress - min == 0) ?
+ (now - timestamp) * (max - min) :
+ (now - timestamp) * (max - min) / (progress - min);
+
+ if (expected > millisToPopup)
+ {
+ createDialog();
+ }
+ }
+ else
+ {
+ // We have not waited long enough to make a decision,
+ // so return and try again when the timer is invoked.
+ return;
+ }
+ }
+ else if (progressDialog != null)
+ {
+ // The progress dialog is being displayed. We now calculate
+ // whether setting the progress bar to the current progress
+ // value would result in a visual difference.
+ int delta = progress - progressBar.getValue();
+
+ if ((delta * progressBar.getWidth() / (max - min)) > 0)
+ {
+ // At least one pixel would change.
+ progressBar.setValue(progress);
+ }
+ }
+ else
+ {
+ // No dialog necessary
+ timer.stop();
+ timer = null;
+ }
+
+ timestamp = now;
+ }
+ }
+
+ /**
+ * Gets the accessible context.
+ *
+ * @return the accessible context.
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java
new file mode 100644
index 000000000..ba98bf68e
--- /dev/null
+++ b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java
@@ -0,0 +1,249 @@
+/* ProgressMonitorInputStream.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+
+/**
+ * An input stream with a {@link ProgressMonitor}.
+ *
+ * @author Andrew Selkirk
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @status updated to 1.2
+ * @since 1.2
+ */
+public class ProgressMonitorInputStream extends FilterInputStream
+{
+
+ /**
+ * The monitor watching the progress of the input stream.
+ */
+ private ProgressMonitor monitor;
+
+ /**
+ * read
+ */
+ private int read;
+
+ /**
+ * Creates a new <code>ProgressMonitorInputStream</code>.
+ *
+ * @param component the parent component for the progress monitor dialog.
+ * @param message the task description.
+ * @param stream the underlying input stream.
+ */
+ public ProgressMonitorInputStream(Component component, Object message,
+ InputStream stream)
+ {
+ super(stream);
+
+ int max = 0;
+
+ try
+ {
+ max = stream.available();
+ }
+ catch ( IOException ioe )
+ {
+ // Behave like the JDK here.
+ }
+
+ monitor = new ProgressMonitor(component, message, null, 0, max);
+ }
+
+ /**
+ * Resets the input stream to the point where {@link #mark(int)} was called.
+ *
+ * @exception IOException TODO
+ */
+ public void reset() throws IOException
+ {
+ super.reset();
+
+ checkMonitorCanceled();
+
+ // TODO: The docs says the monitor should be resetted. But to which
+ // value? (mark is not overridden)
+ }
+
+ /**
+ * Reads an unsigned byte from the input stream and returns it as an
+ * <code>int</code> in the range of 0-255. Returns -1 if the end of the
+ * stream has been reached. The progress monitor is updated.
+ *
+ * @return int
+ *
+ * @exception IOException if there is a problem reading the stream.
+ */
+ public int read() throws IOException
+ {
+ int t = super.read();
+
+ monitor.setProgress(++read);
+
+ checkMonitorCanceled();
+
+ return t;
+ }
+
+ /**
+ * Reads bytes from the input stream and stores them in the supplied array,
+ * and updates the progress monitor (or closes it if the end of the stream
+ * is reached).
+ *
+ * @param data the data array for returning bytes read from the stream.
+ *
+ * @return The number of bytes read, or -1 if there are no more bytes in the
+ * stream.
+ *
+ * @throws IOException if there is a problem reading bytes from the stream.
+ */
+ public int read(byte[] data) throws IOException
+ {
+ int t = super.read(data);
+
+ if ( t > 0 )
+ {
+ read += t;
+ monitor.setProgress(read);
+
+ checkMonitorCanceled();
+ }
+ else
+ {
+ monitor.close();
+ }
+
+ return t;
+ }
+
+ /**
+ * Reads up to <code>length</code> bytes from the input stream and stores
+ * them in the supplied array at the given index, and updates the progress
+ * monitor (or closes it if the end of the stream is reached).
+ *
+ * @param data the data array for returning bytes read from the stream.
+ * @param offset the offset into the array where the bytes should be written.
+ * @param length the maximum number of bytes to read from the stream.
+ *
+ * @return The number of bytes read, or -1 if there are no more bytes in the
+ * stream.
+ *
+ * @throws IOException if there is a problem reading bytes from the stream.
+ */
+ public int read(byte[] data, int offset, int length) throws IOException
+ {
+ int t = super.read(data, offset, length);
+
+ if ( t > 0 )
+ {
+ read += t;
+ monitor.setProgress(read);
+
+ checkMonitorCanceled();
+ }
+ else
+ {
+ monitor.close();
+ }
+
+ return t;
+ }
+
+ /**
+ * Skips the specified number of bytes and updates the
+ * {@link ProgressMonitor}.
+ *
+ * @param length the number of bytes to skip.
+ *
+ * @return The actual number of bytes skipped.
+ *
+ * @throws IOException if there is a problem skipping bytes in the stream.
+ */
+ public long skip(long length) throws IOException
+ {
+ long t = super.skip(length);
+
+ // 'read' may overflow here in rare situations.
+ assert ( (long) read + t <= (long) Integer.MAX_VALUE );
+
+ read += (int) t;
+
+ monitor.setProgress(read);
+
+ checkMonitorCanceled();
+
+ return t;
+ }
+
+ /**
+ * Closes the input stream and the associated {@link ProgressMonitor}.
+ *
+ * @throws IOException if there is a problem closing the input stream.
+ */
+ public void close() throws IOException
+ {
+ super.close();
+ monitor.close();
+ }
+
+ /**
+ * Returns the {@link ProgressMonitor} used by this input stream.
+ *
+ * @return The progress monitor.
+ */
+ public ProgressMonitor getProgressMonitor()
+ {
+ return monitor;
+ }
+
+ private void checkMonitorCanceled() throws InterruptedIOException
+ {
+ if (monitor.isCanceled())
+ {
+ throw new InterruptedIOException("ProgressMonitor was canceled");
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/Renderer.java b/libjava/classpath/javax/swing/Renderer.java
new file mode 100644
index 000000000..4759c5b60
--- /dev/null
+++ b/libjava/classpath/javax/swing/Renderer.java
@@ -0,0 +1,68 @@
+/* Renderer.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+
+/**
+ * This interface is not used and exists only for compatibility.
+ * It probably has been replaced by {@link ListCellRenderer}, {@link
+ * javax.swing.table.TableCellRenderer} and {@link
+ * javax.swing.tree.TreeCellRenderer}.
+ *
+ * @specnote This interface is not used and exists only for compatibility.
+ *
+ * @author Andrew Selkirk
+ */
+public interface Renderer
+{
+ /**
+ * setValue
+ * @param value TODO
+ * @param selected TODO
+ */
+ void setValue(Object value, boolean selected);
+
+ /**
+ * getComponent
+ * @returns Component
+ */
+ Component getComponent();
+
+
+}
diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java
new file mode 100644
index 000000000..23c05a262
--- /dev/null
+++ b/libjava/classpath/javax/swing/RepaintManager.java
@@ -0,0 +1,860 @@
+/* RepaintManager.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+import gnu.java.awt.LowPriorityEvent;
+
+import java.applet.Applet;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.InvocationEvent;
+import java.awt.image.VolatileImage;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * <p>The repaint manager holds a set of dirty regions, invalid components,
+ * and a double buffer surface. The dirty regions and invalid components
+ * are used to coalesce multiple revalidate() and repaint() calls in the
+ * component tree into larger groups to be refreshed "all at once"; the
+ * double buffer surface is used by root components to paint
+ * themselves.</p>
+ *
+ * <p>See <a
+ * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">this
+ * document</a> for more details.</p>
+ * document</a> for more details.</p>
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+public class RepaintManager
+{
+ /**
+ * An InvocationEvent subclass that implements LowPriorityEvent. This is used
+ * to defer the execution of RepaintManager requests as long as possible on
+ * the event queue. This way we make sure that all available input is
+ * processed before getting active with the RepaintManager. This allows
+ * for better optimization (more validate and repaint requests can be
+ * coalesced) and thus has a positive effect on performance for GUI
+ * applications under heavy load.
+ */
+ private static class RepaintWorkerEvent
+ extends InvocationEvent
+ implements LowPriorityEvent
+ {
+
+ /**
+ * Creates a new RepaintManager event.
+ *
+ * @param source the source
+ * @param runnable the runnable to execute
+ */
+ public RepaintWorkerEvent(Object source, Runnable runnable,
+ Object notifier, boolean catchEx)
+ {
+ super(source, runnable, notifier, catchEx);
+ }
+
+ /**
+ * An application that I met implements its own event dispatching and
+ * calls dispatch() via reflection, and only checks declared methods,
+ * that is, it expects this method to be in the event's class, not
+ * in a superclass. So I put this in here... sigh.
+ */
+ public void dispatch()
+ {
+ super.dispatch();
+ }
+ }
+
+ /**
+ * The current repaint managers, indexed by their ThreadGroups.
+ */
+ static WeakHashMap currentRepaintManagers;
+
+ /**
+ * A rectangle object to be reused in damaged regions calculation.
+ */
+ private static Rectangle rectCache = new Rectangle();
+
+ /**
+ * <p>A helper class which is placed into the system event queue at
+ * various times in order to facilitate repainting and layout. There is
+ * typically only one of these objects active at any time. When the
+ * {@link RepaintManager} is told to queue a repaint, it checks to see if
+ * a {@link RepaintWorker} is "live" in the system event queue, and if
+ * not it inserts one using {@link SwingUtilities#invokeLater}.</p>
+ *
+ * <p>When the {@link RepaintWorker} comes to the head of the system
+ * event queue, its {@link RepaintWorker#run} method is executed by the
+ * swing paint thread, which revalidates all invalid components and
+ * repaints any damage in the swing scene.</p>
+ */
+ private class RepaintWorker
+ implements Runnable
+ {
+
+ boolean live;
+
+ public RepaintWorker()
+ {
+ live = false;
+ }
+
+ public synchronized void setLive(boolean b)
+ {
+ live = b;
+ }
+
+ public synchronized boolean isLive()
+ {
+ return live;
+ }
+
+ public void run()
+ {
+ try
+ {
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ RepaintManager rm =
+ (RepaintManager) currentRepaintManagers.get(threadGroup);
+ rm.validateInvalidComponents();
+ rm.paintDirtyRegions();
+ }
+ finally
+ {
+ setLive(false);
+ }
+ }
+
+ }
+
+ /**
+ * A table storing the dirty regions of components. The keys of this
+ * table are components, the values are rectangles. Each component maps
+ * to exactly one rectangle. When more regions are marked as dirty on a
+ * component, they are union'ed with the existing rectangle.
+ *
+ * This is package private to avoid a synthetic accessor method in inner
+ * class.
+ *
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ private HashMap dirtyComponents;
+
+ /**
+ * The dirtyComponents which is used in paintDiryRegions to avoid unnecessary
+ * locking.
+ */
+ private HashMap dirtyComponentsWork;
+
+ /**
+ * A single, shared instance of the helper class. Any methods which mark
+ * components as invalid or dirty eventually activate this instance. It
+ * is added to the event queue if it is not already active, otherwise
+ * reused.
+ *
+ * @see #addDirtyRegion
+ * @see #addInvalidComponent
+ */
+ private RepaintWorker repaintWorker;
+
+ /**
+ * The set of components which need revalidation, in the "layout" sense.
+ * There is no additional information about "what kind of layout" they
+ * need (as there is with dirty regions), so it is just a vector rather
+ * than a table.
+ *
+ * @see #addInvalidComponent
+ * @see #removeInvalidComponent
+ * @see #validateInvalidComponents
+ */
+ private ArrayList invalidComponents;
+
+ /**
+ * Whether or not double buffering is enabled on this repaint
+ * manager. This is merely a hint to clients; the RepaintManager will
+ * always return an offscreen buffer when one is requested.
+ *
+ * @see #isDoubleBufferingEnabled
+ * @see #setDoubleBufferingEnabled
+ */
+ private boolean doubleBufferingEnabled;
+
+ /**
+ * The offscreen buffers. This map holds one offscreen buffer per
+ * Window/Applet and releases them as soon as the Window/Applet gets garbage
+ * collected.
+ */
+ private WeakHashMap offscreenBuffers;
+
+ /**
+ * The maximum width and height to allocate as a double buffer. Requests
+ * beyond this size are ignored.
+ *
+ * @see #paintDirtyRegions
+ * @see #getDoubleBufferMaximumSize
+ * @see #setDoubleBufferMaximumSize
+ */
+ private Dimension doubleBufferMaximumSize;
+
+
+ /**
+ * Create a new RepaintManager object.
+ */
+ public RepaintManager()
+ {
+ dirtyComponents = new HashMap();
+ dirtyComponentsWork = new HashMap();
+ invalidComponents = new ArrayList();
+ repaintWorker = new RepaintWorker();
+ doubleBufferMaximumSize = new Dimension(2000,2000);
+ doubleBufferingEnabled =
+ SystemProperties.getProperty("gnu.swing.doublebuffering", "true")
+ .equals("true");
+ offscreenBuffers = new WeakHashMap();
+ }
+
+ /**
+ * Returns the <code>RepaintManager</code> for the current thread's
+ * thread group. The default implementation ignores the
+ * <code>component</code> parameter and returns the same repaint manager
+ * for all components.
+ *
+ * @param component a component to look up the manager of
+ *
+ * @return the current repaint manager for the calling thread's thread group
+ * and the specified component
+ *
+ * @see #setCurrentManager
+ */
+ public static RepaintManager currentManager(Component component)
+ {
+ if (currentRepaintManagers == null)
+ currentRepaintManagers = new WeakHashMap();
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ RepaintManager currentManager =
+ (RepaintManager) currentRepaintManagers.get(threadGroup);
+ if (currentManager == null)
+ {
+ currentManager = new RepaintManager();
+ currentRepaintManagers.put(threadGroup, currentManager);
+ }
+ return currentManager;
+ }
+
+ /**
+ * Returns the <code>RepaintManager</code> for the current thread's
+ * thread group. The default implementation ignores the
+ * <code>component</code> parameter and returns the same repaint manager
+ * for all components.
+ *
+ * This method is only here for backwards compatibility with older versions
+ * of Swing and simply forwards to {@link #currentManager(Component)}.
+ *
+ * @param component a component to look up the manager of
+ *
+ * @return the current repaint manager for the calling thread's thread group
+ * and the specified component
+ *
+ * @see #setCurrentManager
+ */
+ public static RepaintManager currentManager(JComponent component)
+ {
+ return currentManager((Component)component);
+ }
+
+ /**
+ * Sets the repaint manager for the calling thread's thread group.
+ *
+ * @param manager the repaint manager to set for the current thread's thread
+ * group
+ *
+ * @see #currentManager(Component)
+ */
+ public static void setCurrentManager(RepaintManager manager)
+ {
+ if (currentRepaintManagers == null)
+ currentRepaintManagers = new WeakHashMap();
+
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ currentRepaintManagers.put(threadGroup, manager);
+ }
+
+ /**
+ * Add a component to the {@link #invalidComponents} vector. If the
+ * {@link #repaintWorker} class is not active, insert it in the system
+ * event queue.
+ *
+ * @param component The component to add
+ *
+ * @see #removeInvalidComponent
+ */
+ public void addInvalidComponent(JComponent component)
+ {
+ Component validateRoot = null;
+ Component c = component;
+ while (c != null)
+ {
+ // Special cases we don't bother validating are when the invalidated
+ // component (or any of it's ancestors) is inside a CellRendererPane
+ // or if it doesn't have a peer yet (== not displayable).
+ if (c instanceof CellRendererPane || ! c.isDisplayable())
+ return;
+ if (c instanceof JComponent && ((JComponent) c).isValidateRoot())
+ {
+ validateRoot = c;
+ break;
+ }
+
+ c = c.getParent();
+ }
+
+ // If we didn't find a validate root, then we don't validate.
+ if (validateRoot == null)
+ return;
+
+ // Make sure the validate root and all of it's ancestors are visible.
+ c = validateRoot;
+ while (c != null)
+ {
+ if (! c.isVisible() || ! c.isDisplayable())
+ return;
+ c = c.getParent();
+ }
+
+ if (invalidComponents.contains(validateRoot))
+ return;
+
+ //synchronized (invalidComponents)
+ // {
+ invalidComponents.add(validateRoot);
+ // }
+
+ if (! repaintWorker.isLive())
+ {
+ repaintWorker.setLive(true);
+ invokeLater(repaintWorker);
+ }
+ }
+
+ /**
+ * Remove a component from the {@link #invalidComponents} vector.
+ *
+ * @param component The component to remove
+ *
+ * @see #addInvalidComponent
+ */
+ public void removeInvalidComponent(JComponent component)
+ {
+ synchronized (invalidComponents)
+ {
+ invalidComponents.remove(component);
+ }
+ }
+
+ /**
+ * Add a region to the set of dirty regions for a specified component.
+ * This involves union'ing the new region with any existing dirty region
+ * associated with the component. If the {@link #repaintWorker} class
+ * is not active, insert it in the system event queue.
+ *
+ * @param component The component to add a dirty region for
+ * @param x The left x coordinate of the new dirty region
+ * @param y The top y coordinate of the new dirty region
+ * @param w The width of the new dirty region
+ * @param h The height of the new dirty region
+ *
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ public void addDirtyRegion(JComponent component, int x, int y,
+ int w, int h)
+ {
+ if (w <= 0 || h <= 0 || !component.isShowing())
+ return;
+ component.computeVisibleRect(rectCache);
+ SwingUtilities.computeIntersection(x, y, w, h, rectCache);
+
+ if (! rectCache.isEmpty())
+ {
+ synchronized (dirtyComponents)
+ {
+ Rectangle dirtyRect = (Rectangle)dirtyComponents.get(component);
+ if (dirtyRect != null)
+ {
+ SwingUtilities.computeUnion(rectCache.x, rectCache.y,
+ rectCache.width, rectCache.height,
+ dirtyRect);
+ }
+ else
+ {
+ dirtyComponents.put(component, rectCache.getBounds());
+ }
+ }
+
+ if (! repaintWorker.isLive())
+ {
+ repaintWorker.setLive(true);
+ invokeLater(repaintWorker);
+ }
+ }
+ }
+
+ /**
+ * Get the dirty region associated with a component, or <code>null</code>
+ * if the component has no dirty region.
+ *
+ * @param component The component to get the dirty region of
+ *
+ * @return The dirty region of the component
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ public Rectangle getDirtyRegion(JComponent component)
+ {
+ Rectangle dirty = (Rectangle) dirtyComponents.get(component);
+ if (dirty == null)
+ dirty = new Rectangle();
+ return dirty;
+ }
+
+ /**
+ * Mark a component as dirty over its entire bounds.
+ *
+ * @param component The component to mark as dirty
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ */
+ public void markCompletelyDirty(JComponent component)
+ {
+ addDirtyRegion(component, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Remove all dirty regions for a specified component
+ *
+ * @param component The component to mark as clean
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyDirty
+ */
+ public void markCompletelyClean(JComponent component)
+ {
+ synchronized (dirtyComponents)
+ {
+ dirtyComponents.remove(component);
+ }
+ }
+
+ /**
+ * Return <code>true</code> if the specified component is completely
+ * contained within its dirty region, otherwise <code>false</code>
+ *
+ * @param component The component to check for complete dirtyness
+ *
+ * @return Whether the component is completely dirty
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ */
+ public boolean isCompletelyDirty(JComponent component)
+ {
+ boolean dirty = false;
+ Rectangle r = getDirtyRegion(component);
+ if(r.width == Integer.MAX_VALUE && r.height == Integer.MAX_VALUE)
+ dirty = true;
+ return dirty;
+ }
+
+ /**
+ * Validate all components which have been marked invalid in the {@link
+ * #invalidComponents} vector.
+ */
+ public void validateInvalidComponents()
+ {
+ // We don't use an iterator here because that would fail when there are
+ // components invalidated during the validation of others, which happens
+ // quite frequently. Instead we synchronize the access a little more.
+ while (invalidComponents.size() > 0)
+ {
+ Component comp;
+ synchronized (invalidComponents)
+ {
+ comp = (Component) invalidComponents.remove(0);
+ }
+ // Validate the validate component.
+ if (! (comp.isVisible() && comp.isShowing()))
+ continue;
+ comp.validate();
+ }
+ }
+
+ /**
+ * Repaint all regions of all components which have been marked dirty in the
+ * {@link #dirtyComponents} table.
+ */
+ public void paintDirtyRegions()
+ {
+ // Short circuit if there is nothing to paint.
+ if (dirtyComponents.size() == 0)
+ return;
+
+ // Swap dirtyRegions with dirtyRegionsWork to avoid locking.
+ synchronized (dirtyComponents)
+ {
+ HashMap swap = dirtyComponents;
+ dirtyComponents = dirtyComponentsWork;
+ dirtyComponentsWork = swap;
+ }
+
+ // Compile a set of repaint roots.
+ HashSet repaintRoots = new HashSet();
+ Set components = dirtyComponentsWork.keySet();
+ for (Iterator i = components.iterator(); i.hasNext();)
+ {
+ JComponent dirty = (JComponent) i.next();
+ compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots);
+ }
+
+ for (Iterator i = repaintRoots.iterator(); i.hasNext();)
+ {
+ JComponent comp = (JComponent) i.next();
+ Rectangle damaged = (Rectangle) dirtyComponentsWork.remove(comp);
+ if (damaged == null || damaged.isEmpty())
+ continue;
+ comp.paintImmediately(damaged);
+ }
+ dirtyComponentsWork.clear();
+ }
+
+ /**
+ * Compiles a list of components that really get repainted. This is called
+ * once for each component in the dirtyRegions HashMap, each time with
+ * another <code>dirty</code> parameter. This searches up the component
+ * hierarchy of <code>dirty</code> to find the highest parent that is also
+ * marked dirty and merges the dirty regions.
+ *
+ * @param dirtyRegions the dirty regions
+ * @param dirty the component for which to find the repaint root
+ * @param roots the list to which new repaint roots get appended
+ */
+ private void compileRepaintRoots(HashMap dirtyRegions, JComponent dirty,
+ HashSet roots)
+ {
+ Component current = dirty;
+ Component root = dirty;
+
+ // This will contain the dirty region in the root coordinate system,
+ // possibly clipped by ancestor's bounds.
+ Rectangle originalDirtyRect = (Rectangle) dirtyRegions.get(dirty);
+ rectCache.setBounds(originalDirtyRect);
+
+ // The bounds of the current component.
+ int x = dirty.getX();
+ int y = dirty.getY();
+ int w = dirty.getWidth();
+ int h = dirty.getHeight();
+
+ // Do nothing if dirty region is clipped away by the component's bounds.
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+ if (rectCache.isEmpty())
+ return;
+
+ // The cumulated offsets.
+ int dx = 0;
+ int dy = 0;
+ // The actual offset for the found root.
+ int rootDx = 0;
+ int rootDy = 0;
+
+ // Search the highest component that is also marked dirty.
+ Component parent;
+ while (true)
+ {
+ parent = current.getParent();
+ if (parent == null || !(parent instanceof JComponent))
+ break;
+
+ current = parent;
+ // Update the offset.
+ dx += x;
+ dy += y;
+ rectCache.x += x;
+ rectCache.y += y;
+
+ x = current.getX();
+ y = current.getY();
+ w = current.getWidth();
+ h = current.getHeight();
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+
+ // Don't paint if the dirty regions is clipped away by any of
+ // its ancestors.
+ if (rectCache.isEmpty())
+ return;
+
+ // We can skip to the next up when this parent is not dirty.
+ if (dirtyRegions.containsKey(parent))
+ {
+ root = current;
+ rootDx = dx;
+ rootDy = dy;
+ }
+ }
+
+ // Merge the rectangles of the root and the requested component if
+ // the are different.
+ if (root != dirty)
+ {
+ rectCache.x += rootDx - dx;
+ rectCache.y += rootDy - dy;
+ Rectangle dirtyRect = (Rectangle) dirtyRegions.get(root);
+ SwingUtilities.computeUnion(rectCache.x, rectCache.y, rectCache.width,
+ rectCache.height, dirtyRect);
+ }
+
+ // Adds the root to the roots set.
+ if (! roots.contains(root))
+ roots.add(root);
+ }
+
+ /**
+ * Get an offscreen buffer for painting a component's image. This image
+ * may be smaller than the proposed dimensions, depending on the value of
+ * the {@link #doubleBufferMaximumSize} property.
+ *
+ * @param component The component to return an offscreen buffer for
+ * @param proposedWidth The proposed width of the offscreen buffer
+ * @param proposedHeight The proposed height of the offscreen buffer
+ *
+ * @return A shared offscreen buffer for painting
+ */
+ public Image getOffscreenBuffer(Component component, int proposedWidth,
+ int proposedHeight)
+ {
+ Component root = SwingUtilities.getWindowAncestor(component);
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer == null
+ || buffer.getWidth(null) < proposedWidth
+ || buffer.getHeight(null) < proposedHeight)
+ {
+ int width = Math.max(proposedWidth, root.getWidth());
+ width = Math.min(doubleBufferMaximumSize.width, width);
+ int height = Math.max(proposedHeight, root.getHeight());
+ height = Math.min(doubleBufferMaximumSize.height, height);
+ buffer = component.createImage(width, height);
+ offscreenBuffers.put(root, buffer);
+ }
+ return buffer;
+ }
+
+ /**
+ * Blits the back buffer of the specified root component to the screen.
+ * This is package private because it must get called by JComponent.
+ *
+ * @param comp the component to be painted
+ * @param x the area to paint on screen, in comp coordinates
+ * @param y the area to paint on screen, in comp coordinates
+ * @param w the area to paint on screen, in comp coordinates
+ * @param h the area to paint on screen, in comp coordinates
+ */
+ void commitBuffer(Component comp, int x, int y, int w, int h)
+ {
+ Component root = comp;
+ while (root != null
+ && ! (root instanceof Window || root instanceof Applet))
+ {
+ x += root.getX();
+ y += root.getY();
+ root = root.getParent();
+ }
+
+ if (root != null)
+ {
+ Graphics g = root.getGraphics();
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer != null)
+ {
+ // Make sure we have a sane clip at this point.
+ g.clipRect(x, y, w, h);
+ g.drawImage(buffer, 0, 0, root);
+ g.dispose();
+ }
+ }
+ }
+
+ /**
+ * Creates and returns a volatile offscreen buffer for the specified
+ * component that can be used as a double buffer. The returned image
+ * is a {@link VolatileImage}. Its size will be <code>(proposedWidth,
+ * proposedHeight)</code> except when the maximum double buffer size
+ * has been set in this RepaintManager.
+ *
+ * @param comp the Component for which to create a volatile buffer
+ * @param proposedWidth the proposed width of the buffer
+ * @param proposedHeight the proposed height of the buffer
+ *
+ * @since 1.4
+ *
+ * @see VolatileImage
+ */
+ public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth,
+ int proposedHeight)
+ {
+ Component root = SwingUtilities.getWindowAncestor(comp);
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer == null
+ || buffer.getWidth(null) < proposedWidth
+ || buffer.getHeight(null) < proposedHeight
+ || !(buffer instanceof VolatileImage))
+ {
+ int width = Math.max(proposedWidth, root.getWidth());
+ width = Math.min(doubleBufferMaximumSize.width, width);
+ int height = Math.max(proposedHeight, root.getHeight());
+ height = Math.min(doubleBufferMaximumSize.height, height);
+ buffer = root.createVolatileImage(width, height);
+ if (buffer != null)
+ offscreenBuffers.put(root, buffer);
+ }
+ return buffer;
+ }
+
+
+ /**
+ * Get the value of the {@link #doubleBufferMaximumSize} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setDoubleBufferMaximumSize
+ */
+ public Dimension getDoubleBufferMaximumSize()
+ {
+ return doubleBufferMaximumSize;
+ }
+
+ /**
+ * Set the value of the {@link #doubleBufferMaximumSize} property.
+ *
+ * @param size The new value of the property
+ *
+ * @see #getDoubleBufferMaximumSize
+ */
+ public void setDoubleBufferMaximumSize(Dimension size)
+ {
+ doubleBufferMaximumSize = size;
+ }
+
+ /**
+ * Set the value of the {@link #doubleBufferingEnabled} property.
+ *
+ * @param buffer The new value of the property
+ *
+ * @see #isDoubleBufferingEnabled
+ */
+ public void setDoubleBufferingEnabled(boolean buffer)
+ {
+ doubleBufferingEnabled = buffer;
+ }
+
+ /**
+ * Get the value of the {@link #doubleBufferingEnabled} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setDoubleBufferingEnabled
+ */
+ public boolean isDoubleBufferingEnabled()
+ {
+ return doubleBufferingEnabled;
+ }
+
+ public String toString()
+ {
+ return "RepaintManager";
+ }
+
+ /**
+ * Sends an RepaintManagerEvent to the event queue with the specified
+ * runnable. This is similar to SwingUtilities.invokeLater(), only that the
+ * event is a low priority event in order to defer the execution a little
+ * more.
+ */
+ private void invokeLater(Runnable runnable)
+ {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ EventQueue evQueue = tk.getSystemEventQueue();
+ InvocationEvent ev = new RepaintWorkerEvent(evQueue, runnable, null, false);
+ evQueue.postEvent(ev);
+ }
+}
diff --git a/libjava/classpath/javax/swing/RootPaneContainer.java b/libjava/classpath/javax/swing/RootPaneContainer.java
new file mode 100644
index 000000000..4b1bece21
--- /dev/null
+++ b/libjava/classpath/javax/swing/RootPaneContainer.java
@@ -0,0 +1,96 @@
+/* RootPaneContainer.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+
+/**
+ * Components that contain a single {@link JRootPane} as only child
+ * implement this interface, typically this is implemented by the
+ * Swing top-level containers.
+ *
+ * @author Andrew Selkirk
+ */
+public interface RootPaneContainer
+{
+
+ /**
+ * getRootPane
+ * @returns JRootPane
+ */
+ JRootPane getRootPane();
+
+ /**
+ * setContentPane
+ * @param contentPane TODO
+ */
+ void setContentPane(Container contentPane);
+
+ /**
+ * getContentPane
+ * @returns Container
+ */
+ Container getContentPane();
+
+ /**
+ * setLayeredPane
+ * @param layeredPane TODO
+ */
+ void setLayeredPane(JLayeredPane layeredPane);
+
+ /**
+ * getLayeredPane
+ * @returns JLayeredPane
+ */
+ JLayeredPane getLayeredPane();
+
+ /**
+ * setGlassPane
+ * @param glassPane TODO
+ */
+ void setGlassPane(Component glassPane);
+
+ /**
+ * getGlassPane
+ * @returns Component
+ */
+ Component getGlassPane();
+
+}
diff --git a/libjava/classpath/javax/swing/ScrollPaneConstants.java b/libjava/classpath/javax/swing/ScrollPaneConstants.java
new file mode 100644
index 000000000..b5860609f
--- /dev/null
+++ b/libjava/classpath/javax/swing/ScrollPaneConstants.java
@@ -0,0 +1,152 @@
+/* ScrollPaneConstants.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Defines some constants that are used in {@link JScrollPane} and related
+ * components.
+ *
+ * @author Andrew Selkirk
+ */
+public interface ScrollPaneConstants
+{
+ /**
+ * VIEWPORT
+ */
+ String VIEWPORT = "VIEWPORT";
+
+ /**
+ * VERTICAL_SCROLLBAR
+ */
+ String VERTICAL_SCROLLBAR = "VERTICAL_SCROLLBAR";
+
+ /**
+ * HORIZONTAL_SCROLLBAR
+ */
+ String HORIZONTAL_SCROLLBAR = "HORIZONTAL_SCROLLBAR";
+
+ /**
+ * ROW_HEADER
+ */
+ String ROW_HEADER = "ROW_HEADER";
+
+ /**
+ * COLUMN_HEADER
+ */
+ String COLUMN_HEADER = "COLUMN_HEADER";
+
+ /**
+ * LOWER_LEFT_CORNER
+ */
+ String LOWER_LEFT_CORNER = "LOWER_LEFT_CORNER";
+
+ /**
+ * LOWER_RIGHT_CORNER
+ */
+ String LOWER_RIGHT_CORNER = "LOWER_RIGHT_CORNER";
+
+ /**
+ * UPPER_LEFT_CORNER
+ */
+ String UPPER_LEFT_CORNER = "UPPER_LEFT_CORNER";
+
+ /**
+ * UPPER_RIGHT_CORNER
+ */
+ String UPPER_RIGHT_CORNER = "UPPER_RIGHT_CORNER";
+
+ /**
+ * LOWER_LEADING_CORNER
+ */
+ String LOWER_LEADING_CORNER = "LOWER_LEADING_CORNER";
+
+ /**
+ * LOWER_TRAILING_CORNER
+ */
+ String LOWER_TRAILING_CORNER = "LOWER_TRAILING_CORNER";
+
+ /**
+ * UPPER_LEADING_CORNER
+ */
+ String UPPER_LEADING_CORNER = "UPPER_LEADING_CORNER";
+
+ /**
+ * UPPER_TRAILING_CORNER
+ */
+ String UPPER_TRAILING_CORNER = "UPPER_TRAILING_CORNER";
+
+ /**
+ * VERTICAL_SCROLLBAR_POLICY
+ */
+ String VERTICAL_SCROLLBAR_POLICY = "VERTICAL_SCROLLBAR_POLICY";
+
+ /**
+ * HORIZONTAL_SCROLLBAR_POLICY
+ */
+ String HORIZONTAL_SCROLLBAR_POLICY = "HORIZONTAL_SCROLLBAR_POLICY";
+
+ /**
+ * VERTICAL_SCROLLBAR_AS_NEEDED
+ */
+ int VERTICAL_SCROLLBAR_AS_NEEDED = 20;
+
+ /**
+ * VERTICAL_SCROLLBAR_NEVER
+ */
+ int VERTICAL_SCROLLBAR_NEVER = 21;
+
+ /**
+ * VERTICAL_SCROLLBAR_ALWAYS
+ */
+ int VERTICAL_SCROLLBAR_ALWAYS = 22;
+
+ /**
+ * HORIZONTAL_SCROLLBAR_AS_NEEDED
+ */
+ int HORIZONTAL_SCROLLBAR_AS_NEEDED = 30;
+
+ /**
+ * HORIZONTAL_SCROLLBAR_NEVER
+ */
+ int HORIZONTAL_SCROLLBAR_NEVER = 31;
+
+ /**
+ * HORIZONTAL_SCROLLBAR_ALWAYS
+ */
+ int HORIZONTAL_SCROLLBAR_ALWAYS = 32;
+}
diff --git a/libjava/classpath/javax/swing/ScrollPaneLayout.java b/libjava/classpath/javax/swing/ScrollPaneLayout.java
new file mode 100644
index 000000000..fa1743bed
--- /dev/null
+++ b/libjava/classpath/javax/swing/ScrollPaneLayout.java
@@ -0,0 +1,491 @@
+/* ScrollPaneLayout.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.swing.border.Border;
+
+/**
+ * ScrollPaneLayout
+ * @author Andrew Selkirk
+ * @version 1.0
+ */
+public class ScrollPaneLayout
+ implements LayoutManager, ScrollPaneConstants, Serializable
+{
+ private static final long serialVersionUID = -4480022884523193743L;
+
+ public static class UIResource extends ScrollPaneLayout
+ implements javax.swing.plaf.UIResource
+ {
+ public UIResource()
+ {
+ super();
+ }
+ }
+
+ protected JViewport viewport;
+ protected JScrollBar vsb;
+ protected JScrollBar hsb;
+ protected JViewport rowHead;
+ protected JViewport colHead;
+ protected Component lowerLeft;
+ protected Component lowerRight;
+ protected Component upperLeft;
+ protected Component upperRight;
+ protected int vsbPolicy;
+ protected int hsbPolicy;
+
+ public ScrollPaneLayout()
+ {
+ // Nothing to do here.
+ }
+
+ public void syncWithScrollPane(JScrollPane scrollPane)
+ {
+ viewport = scrollPane.getViewport();
+ rowHead = scrollPane.getRowHeader();
+ colHead = scrollPane.getColumnHeader();
+ vsb = scrollPane.getVerticalScrollBar();
+ hsb = scrollPane.getHorizontalScrollBar();
+ vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
+ hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
+ lowerLeft = scrollPane.getCorner(LOWER_LEFT_CORNER);
+ lowerRight = scrollPane.getCorner(LOWER_RIGHT_CORNER);
+ upperLeft = scrollPane.getCorner(UPPER_LEFT_CORNER);
+ upperRight = scrollPane.getCorner(UPPER_RIGHT_CORNER);
+ }
+
+ /**
+ * Removes an existing component. If oldComponent is not null
+ * and is not equal to newComponent, oldComponent must be removed
+ * from its parent.
+ * @param oldComponent the old Component that may need to be removed.
+ * @param newComponent the Component to add.
+ * @return the newComponent
+ */
+ protected Component addSingletonComponent(Component oldComponent,
+ Component newComponent)
+ {
+ if (oldComponent != null && oldComponent != newComponent)
+ oldComponent.getParent().remove(oldComponent);
+ return newComponent;
+ }
+
+ /**
+ * Add the specified component to the layout.
+ * @param key must be one of VIEWPORT, VERTICAL_SCROLLBAR,
+ * HORIZONTAL_SCROLLBAR, ROW_HEADER, COLUMN_HEADER,
+ * LOWER_RIGHT_CORNER, LOWER_LEFT_CORNER, UPPER_RIGHT_CORNER,
+ * UPPER_LEFT_CORNER.
+ * @param component the Component to add
+ * @throws IllegalArgumentException if key is not as above
+ */
+ public void addLayoutComponent(String key, Component component)
+ {
+ if (key == VIEWPORT)
+ viewport = (JViewport) component;
+ else if (key == VERTICAL_SCROLLBAR)
+ vsb = (JScrollBar) component;
+ else if (key == HORIZONTAL_SCROLLBAR)
+ hsb = (JScrollBar) component;
+ else if (key == ROW_HEADER)
+ rowHead = (JViewport) component;
+ else if (key == COLUMN_HEADER)
+ colHead = (JViewport) component;
+ else if (key == LOWER_RIGHT_CORNER)
+ lowerRight = component;
+ else if (key == UPPER_RIGHT_CORNER)
+ upperRight = component;
+ else if (key == LOWER_LEFT_CORNER)
+ lowerLeft = component;
+ else if (key == UPPER_LEFT_CORNER)
+ upperLeft = component;
+ else
+ throw new IllegalArgumentException();
+ }
+
+ public void removeLayoutComponent(Component component)
+ {
+ if (component == viewport)
+ viewport = null;
+ else if (component == vsb)
+ vsb = null;
+ else if (component == hsb)
+ hsb = null;
+ else if (component == rowHead)
+ rowHead = null;
+ else if (component == colHead)
+ colHead = null;
+ else if (component == lowerRight)
+ lowerRight = null;
+ else if (component == upperRight)
+ upperRight = null;
+ else if (component == lowerLeft)
+ lowerLeft = null;
+ else if (component == upperLeft)
+ upperLeft = null;
+ }
+
+ public int getVerticalScrollBarPolicy()
+ {
+ return vsbPolicy;
+ }
+
+ /**
+ * Sets the vertical scrollbar policy.
+ * @param policy must be one of VERTICAL_SCROLLBAR_AS_NEEDED,
+ * VERTICAL_SCROLLBAR_NEVER, VERTICAL_SCROLLBAR_ALWAYS.
+ * @throws IllegalArgumentException if policy is not one of the valid
+ * JScrollBar policies.
+ */
+ public void setVerticalScrollBarPolicy(int policy)
+ {
+ if (policy != VERTICAL_SCROLLBAR_AS_NEEDED &&
+ policy != VERTICAL_SCROLLBAR_NEVER &&
+ policy != VERTICAL_SCROLLBAR_ALWAYS)
+ throw new IllegalArgumentException("Illegal Scrollbar Policy");
+ vsbPolicy = policy;
+ }
+
+ public int getHorizontalScrollBarPolicy()
+ {
+ return hsbPolicy;
+ }
+
+ /**
+ * Sets the horizontal scrollbar policy.
+ * @param policy must be one of HORIZONTAL_SCROLLBAR_AS_NEEDED,
+ * HORIZONTAL_SCROLLBAR_NEVER, HORIZONTAL_SCROLLBAR_ALWAYS.
+ * @throws IllegalArgumentException if policy is not one of the valid
+ * JScrollbar policies.
+ */
+ public void setHorizontalScrollBarPolicy(int policy)
+ {
+ if (policy != HORIZONTAL_SCROLLBAR_AS_NEEDED &&
+ policy != HORIZONTAL_SCROLLBAR_NEVER &&
+ policy != HORIZONTAL_SCROLLBAR_ALWAYS)
+ throw new IllegalArgumentException("Illegal Scrollbar Policy");
+ hsbPolicy = policy;
+ }
+
+ public JViewport getViewport()
+ {
+ return viewport;
+ }
+
+ public JScrollBar getHorizontalScrollBar()
+ {
+ return hsb;
+ }
+
+ public JScrollBar getVerticalScrollBar()
+ {
+ return vsb;
+ }
+
+ public JViewport getRowHeader()
+ {
+ return rowHead;
+ }
+
+ public JViewport getColumnHeader()
+ {
+ return colHead;
+ }
+
+ /**
+ * Returns the Component at the specified corner.
+ * @param key the corner.
+ * @return the Component at the specified corner, or null if
+ * key is not one of the four valid corners.
+ */
+ public Component getCorner(String key)
+ {
+ if (key == LOWER_RIGHT_CORNER)
+ return lowerRight;
+ else if (key == UPPER_RIGHT_CORNER)
+ return upperRight;
+ else if (key == LOWER_LEFT_CORNER)
+ return lowerLeft;
+ else if (key == UPPER_LEFT_CORNER)
+ return upperLeft;
+ return null;
+ }
+
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ // Sun's implementation simply throws a ClassCastException if
+ // parent is no JScrollPane, so do we.
+ JScrollPane sc = (JScrollPane) parent;
+ Dimension viewportSize = viewport.getPreferredSize();
+ Dimension viewSize = viewport.getViewSize();
+ int width = viewportSize.width;
+ int height = viewportSize.height;
+
+ // horizontal scrollbar needed if the view's preferred width
+ // is larger than the viewport's preferred width
+ if (hsb != null && viewSize.width > viewportSize.width)
+ height += hsb.getPreferredSize().height;
+
+ // vertical scrollbar needed if the view's preferred height
+ // is larger than the viewport's preferred height
+ if (vsb != null && viewSize.height > viewportSize.height)
+ width += vsb.getPreferredSize().width;
+ if (rowHead != null && rowHead.isVisible())
+ width += rowHead.getPreferredSize().width;
+ if (colHead != null && colHead.isVisible())
+ height += colHead.getPreferredSize().height;
+
+ // Add insets of viewportBorder if present.
+ Border vpBorder = sc.getViewportBorder();
+ if (vpBorder != null)
+ {
+ Insets i = vpBorder.getBorderInsets(sc);
+ width += i.left + i.right;
+ height += i.top + i.bottom;
+ }
+
+ Insets i = sc.getInsets();
+ return new Dimension(width + i.left + i.right,
+ height + i.left + i.right);
+ }
+
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ // Sun's implementation simply throws a ClassCastException if
+ // parent is no JScrollPane, so do we.
+ JScrollPane sc = (JScrollPane) parent;
+ Insets i = sc.getInsets();
+ Dimension viewportMinSize = sc.getViewport().getMinimumSize();
+
+ int width = i.left + i.right + viewportMinSize.width;
+ if (sc.getVerticalScrollBarPolicy()
+ != JScrollPane.VERTICAL_SCROLLBAR_NEVER)
+ width += sc.getVerticalScrollBar().getMinimumSize().width;
+
+ int height = i.top + i.bottom + viewportMinSize.height;
+ if (sc.getHorizontalScrollBarPolicy()
+ != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
+ height += sc.getHorizontalScrollBar().getMinimumSize().height;
+
+ // Add insets of viewportBorder if present.
+ Border vpBorder = sc.getViewportBorder();
+ if (vpBorder != null)
+ {
+ i = vpBorder.getBorderInsets(sc);
+ width += i.left + i.right;
+ height += i.top + i.bottom;
+ }
+
+ return new Dimension(width, height);
+ }
+
+ /**
+ *
+ * +----+--------------------+----+ y1
+ * | c1 | column header | c2 |
+ * +----+--------------------+----+ y2
+ * | r | | v |
+ * | o | | |
+ * | w | | s |
+ * | | | r |
+ * | h | | o |
+ * | e | viewport | l |
+ * | a | | l |
+ * | d | | b |
+ * | e | | a |
+ * | r | | r |
+ * +----+--------------------+----+ y3
+ * | c3 | h scrollbar | c4 |
+ * +----+--------------------+----+ y4
+ * x1 x2 x3 x4
+ *
+ */
+ public void layoutContainer(Container parent)
+ {
+ // Sun's implementation simply throws a ClassCastException if
+ // parent is no JScrollPane, so do we.
+ JScrollPane sc = (JScrollPane) parent;
+ JViewport viewport = sc.getViewport();
+ Component view = viewport.getView();
+
+ // If there is no view in the viewport, there is no work to be done.
+ if (view == null)
+ return;
+
+ Dimension viewSize = viewport.getView().getPreferredSize();
+
+ int x1 = 0, x2 = 0, x3 = 0, x4 = 0;
+ int y1 = 0, y2 = 0, y3 = 0, y4 = 0;
+ Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null);
+
+ // If there is a viewportBorder, remove its insets from the available
+ // space.
+ Border vpBorder = sc.getViewportBorder();
+ Insets vpi;
+ if (vpBorder != null)
+ vpi = vpBorder.getBorderInsets(sc);
+ else
+ vpi = new Insets(0, 0, 0, 0);
+
+ x1 = scrollPaneBounds.x;
+ y1 = scrollPaneBounds.y;
+ x4 = scrollPaneBounds.x + scrollPaneBounds.width;
+ y4 = scrollPaneBounds.y + scrollPaneBounds.height;
+ if (colHead != null)
+ y2 = y1 + colHead.getPreferredSize().height;
+ else
+ y2 = y1;
+
+ if (rowHead != null)
+ x2 = x1 + rowHead.getPreferredSize().width;
+ else
+ x2 = x1;
+
+ int vsbPolicy = sc.getVerticalScrollBarPolicy();
+ int hsbPolicy = sc.getHorizontalScrollBarPolicy();
+
+ int vsWidth = 0;
+ int hsHeight = 0;
+
+ boolean showVsb =
+ (vsb != null)
+ && ((vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS)
+ || (vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
+ && viewSize.height > (y4 - y2)));
+
+ if (showVsb)
+ vsWidth = vsb.getPreferredSize().width;
+
+ // The horizontal scroll bar may become necessary if the vertical scroll
+ // bar appears, reducing the space, left for the component.
+
+ boolean showHsb =
+ (hsb != null)
+ && ((hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS)
+ || (hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED
+ && viewSize.width > (x4 - x2 - vsWidth)));
+
+ if (showHsb)
+ hsHeight = hsb.getPreferredSize().height;
+
+ // If the horizontal scroll bar appears, and the vertical scroll bar
+ // was not necessary assuming that there is no horizontal scroll bar,
+ // the vertical scroll bar may become necessary because the horizontal
+ // scroll bar reduces the vertical space for the component.
+ if (!showVsb)
+ {
+ showVsb =
+ (vsb != null)
+ && ((vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS)
+ || (vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
+ && viewSize.height > (y4 - y2)));
+
+ if (showVsb)
+ vsWidth = vsb.getPreferredSize().width;
+ }
+
+ x3 = x4 - vsWidth;
+ y3 = y4 - hsHeight;
+
+ // now set the layout
+ if (viewport != null)
+ viewport.setBounds(new Rectangle(x2 + vpi.left, y2 + vpi.top,
+ x3 - x2 - vpi.left - vpi.right,
+ y3 - y2 - vpi.top - vpi.bottom));
+
+ if (colHead != null)
+ colHead.setBounds(new Rectangle(x2, y1, x3 - x2, y2 - y1));
+
+ if (rowHead != null)
+ rowHead.setBounds(new Rectangle(x1, y2, x2 - x1, y3 - y2));
+
+ if (showVsb)
+ {
+ vsb.setVisible(true);
+ vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2 ));
+ }
+ else if (vsb != null)
+ vsb.setVisible(false);
+
+ if (showHsb)
+ {
+ hsb.setVisible(true);
+ hsb.setBounds(new Rectangle(x2 , y3, x3 - x2, y4 - y3));
+ }
+ else if (hsb != null)
+ hsb.setVisible(false);
+
+ if (upperLeft != null)
+ upperLeft.setBounds(new Rectangle(x1, y1, x2 - x1, y2 - y1));
+
+ if (upperRight != null)
+ upperRight.setBounds(new Rectangle(x3, y1, x4 - x3, y2 - y1));
+
+ if (lowerLeft != null)
+ lowerLeft.setBounds(new Rectangle(x1, y3, x2 - x1, y4 - y3));
+
+ if (lowerRight != null)
+ lowerRight.setBounds(new Rectangle(x3, y3, x4 - x3, y4 - y3));
+ }
+
+ /**
+ * Returns the bounds of the border around a ScrollPane's viewport.
+ *
+ * @param scrollPane the ScrollPane for which's viewport the border
+ * is requested
+ *
+ * @deprecated As of Swing 1.1 replaced by
+ * {@link javax.swing.JScrollPane#getViewportBorderBounds}.
+ */
+ public Rectangle getViewportBorderBounds(JScrollPane scrollPane)
+ {
+ return null;
+ }
+
+
+}
diff --git a/libjava/classpath/javax/swing/Scrollable.java b/libjava/classpath/javax/swing/Scrollable.java
new file mode 100644
index 000000000..26fae2429
--- /dev/null
+++ b/libjava/classpath/javax/swing/Scrollable.java
@@ -0,0 +1,106 @@
+/* Scrollable.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Dimension;
+import java.awt.Rectangle;
+
+/**
+ * Defines the method that a component should implement to behave nicely
+ * in {@link JScrollPane}s. Note that this is not required for a component
+ * to be used in a <code>JScrollPane</code>, but can highly improve the
+ * user experience when scrolling the component.
+ */
+public interface Scrollable
+{
+ Dimension getPreferredScrollableViewportSize();
+
+ /**
+ * Return the preferred scrolling amount (in pixels) for the given
+ * scrolling direction and orientation when scrolling in small amounts
+ * like table lines.
+ *
+ * @param visibleRect the currently visible part of the component.
+ * @param orientation the scrolling orientation
+ * @param direction the scrolling direction (negative - up, positive -down).
+ * The values greater than one means that more mouse wheel or similar
+ * events were generated, and hence it is better to scroll the longer
+ * distance.
+ *
+ * @return the preferred scrolling distance, negative if up or left.
+ */
+ int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
+ int direction);
+
+ /**
+ * Return the preferred scrolling amount (in pixels) for the given
+ * scrolling direction and orientation when scrolling in large amounts
+ * (pages).
+ *
+ * @param visibleRect the currently visible part of the component.
+ * @param orientation the scrolling orientation
+ * @param direction the scrolling direction (negative - up, positive -down).
+ * The values greater than one means that more mouse wheel or similar
+ * events were generated, and hence it is better to scroll the longer
+ * distance.
+ *
+ * @return the preferred scrolling distance, negative if up or left.
+ */
+ int getScrollableBlockIncrement(Rectangle visibleRect, int orientation,
+ int direction);
+
+ /**
+ * Return true if the width of the scrollable is always equal to the
+ * view, where it is displayed, width (for instance, the text area with
+ * the word wrap). In such case, the horizontal scrolling should not be
+ * performed.
+ *
+ * @return true is no horizontal scrolling is assumed, faster otherwise.
+ */
+ boolean getScrollableTracksViewportWidth();
+
+ /**
+ * Return true if the height of the scrollable is always equal to the view,
+ * where it is displayed, height.In such case, the vertical scrolling should
+ * not be performed.
+ *
+ * @return true is no horizontal scrolling is assumed, faster otherwise.
+ */
+ boolean getScrollableTracksViewportHeight();
+
+}
diff --git a/libjava/classpath/javax/swing/SingleSelectionModel.java b/libjava/classpath/javax/swing/SingleSelectionModel.java
new file mode 100644
index 000000000..2a76ff389
--- /dev/null
+++ b/libjava/classpath/javax/swing/SingleSelectionModel.java
@@ -0,0 +1,103 @@
+/* SingleSelectionModel.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * A data model that is used in components that support at most one
+ * selected element, like {@link JTabbedPane}, {@link JMenu} and
+ * {@link JPopupMenu}.
+ *
+ * @author Andrew Selkirk
+ */
+public interface SingleSelectionModel
+{
+ /**
+ * Returns the selected index or <code>-1</code> if there is no selection.
+ *
+ * @return The selected index.
+ *
+ * @see #setSelectedIndex(int)
+ */
+ int getSelectedIndex();
+
+ /**
+ * Sets the selected index and, if this is different to the previous
+ * selection, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param index the index (use <code>-1</code> to represent no selection).
+ *
+ * @see #getSelectedIndex()
+ * @see #clearSelection
+ */
+ void setSelectedIndex(int index);
+
+ /**
+ * Clears the selection by setting the selected index to <code>-1</code> and
+ * sends a {@link ChangeEvent} to all registered listeners. If the selected
+ * index is already <code>-1</code>, this method does nothing.
+ */
+ void clearSelection();
+
+ /**
+ * Returns <code>true</code> if there is a selection, and <code>false</code>
+ * otherwise.
+ *
+ * @return A boolean.
+ */
+ boolean isSelected();
+
+ /**
+ * Registers a listener to receive {@link ChangeEvent} notifications from
+ * this model whenever the selected index changes.
+ *
+ * @param listener the listener to add.
+ */
+ void addChangeListener(ChangeListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives {@link ChangeEvent}
+ * notifications from this model.
+ *
+ * @param listener the listener to remove.
+ */
+ void removeChangeListener(ChangeListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/SizeRequirements.java b/libjava/classpath/javax/swing/SizeRequirements.java
new file mode 100644
index 000000000..1ee0e285c
--- /dev/null
+++ b/libjava/classpath/javax/swing/SizeRequirements.java
@@ -0,0 +1,523 @@
+/* SizeRequirements.java --
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * This class calculates information about the size and position requirements
+ * of components.
+ *
+ * Two types of layout are supported:
+ * <ul>
+ * <li>Tiled: the components are placed at position top-left or bottom-right
+ * position within their allocated space</li>
+ * <li>Aligned: the components are placed aligned in their allocated space
+ * according to their alignment value</li>
+ * </ul>
+ *
+ * @author Andrew Selkirk
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class SizeRequirements implements Serializable
+{
+ /**
+ * The serialVersionUID.
+ */
+ private static final long serialVersionUID = 9217749429906736553L;
+
+ /**
+ * The minimum reasonable width or height of a component.
+ */
+ public int minimum;
+
+ /**
+ * The preferred width or height of a component.
+ */
+ public int preferred;
+
+ /**
+ * The maximum reasonable width or height of a component.
+ */
+ public int maximum;
+
+ /**
+ * The horizontal or vertical alignment of a component.
+ */
+ public float alignment;
+
+ /**
+ * Creates a SizeRequirements object with minimum, preferred and
+ * maximum size set to zero, and an alignment value of 0.5.
+ */
+ public SizeRequirements()
+ {
+ this (0, 0, 0, 0.5F);
+ }
+
+ /**
+ * Creates a SizeRequirements object with the specified minimum,
+ * preferred, maximum and alignment values.
+ *
+ * @param min the minimum reasonable size of the component
+ * @param pref the preferred size of the component
+ * @param max the maximum size of the component
+ * @param align the alignment of the component
+ */
+ public SizeRequirements(int min, int pref, int max, float align)
+ {
+ minimum = min;
+ preferred = pref;
+ maximum = max;
+ alignment = align;
+ }
+
+ /**
+ * Returns a String representation of this SizeRequirements object,
+ * containing information about the minimum, preferred, maximum and
+ * alignment value.
+ *
+ * @return a String representation of this SizeRequirements object
+ */
+ public String toString()
+ {
+ StringBuilder b = new StringBuilder();
+ b.append("<[");
+ b.append(minimum);
+ b.append(',');
+ b.append(preferred);
+ b.append(',');
+ b.append(maximum);
+ b.append("]@");
+ b.append(alignment);
+ b.append('>');
+ return b.toString();
+ }
+
+ /**
+ * Calculates how much space is nessecary to place a set of components
+ * end-to-end. The size requirements of the components is specified
+ * in <code>children</code>.
+ *
+ * @param children the SizeRequirements of each of the components
+ *
+ * @return the SizeRequirements that describe how much space is needed
+ * to place the components end-to-end
+ */
+ public static SizeRequirements
+ getTiledSizeRequirements(SizeRequirements[] children)
+ {
+ long minimum = 0;
+ long preferred = 0;
+ long maximum = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ minimum += children[i].minimum;
+ preferred += children[i].preferred;
+ maximum += children[i].maximum;
+ }
+ // Overflow check.
+ if (minimum > Integer.MAX_VALUE)
+ minimum = Integer.MAX_VALUE;
+ if (preferred > Integer.MAX_VALUE)
+ preferred = Integer.MAX_VALUE;
+ if (maximum > Integer.MAX_VALUE)
+ maximum = Integer.MAX_VALUE;
+ SizeRequirements result = new SizeRequirements((int) minimum,
+ (int) preferred,
+ (int) maximum,
+ 0.5F);
+ return result;
+ }
+
+ /**
+ * Calculates how much space is nessecary to place a set of components
+ * aligned according to their alignment value.
+ * The size requirements of the components is specified in
+ * <code>children</code>.
+ *
+ * @param children the SizeRequirements of each of the components
+ *
+ * @return the SizeRequirements that describe how much space is needed
+ * to place the components aligned
+ */
+ public static SizeRequirements
+ getAlignedSizeRequirements(SizeRequirements[] children)
+ {
+ float minLeft = 0;
+ float minRight = 0;
+ float prefLeft = 0;
+ float prefRight = 0;
+ float maxLeft = 0;
+ float maxRight = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ float myMinLeft = children[i].minimum * children[i].alignment;
+ float myMinRight = children[i].minimum - myMinLeft;
+ minLeft = Math.max(myMinLeft, minLeft);
+ minRight = Math.max(myMinRight, minRight);
+ float myPrefLeft = children[i].preferred * children[i].alignment;
+ float myPrefRight = children[i].preferred - myPrefLeft;
+ prefLeft = Math.max(myPrefLeft, prefLeft);
+ prefRight = Math.max(myPrefRight, prefRight);
+ float myMaxLeft = children[i].maximum * children[i].alignment;
+ float myMaxRight = children[i].maximum - myMaxLeft;
+ maxLeft = Math.max(myMaxLeft, maxLeft);
+ maxRight = Math.max(myMaxRight, maxRight);
+ }
+ int minSize = (int) (minLeft + minRight);
+ int prefSize = (int) (prefLeft + prefRight);
+ int maxSize = (int) (maxLeft + maxRight);
+ float align = prefLeft / (prefRight + prefLeft);
+ if (Float.isNaN(align))
+ align = 0;
+ return new SizeRequirements(minSize, prefSize, maxSize, align);
+ }
+
+ /**
+ * Calculate the offsets and spans of the components, when they should
+ * be placed end-to-end.
+ *
+ * You must specify the amount of allocated space in
+ * <code>allocated</code>, the total size requirements of the set of
+ * components in <code>total</code> (this can be calculated using
+ * {@link #getTiledSizeRequirements} and the size requirements of the
+ * components in <code>children</code>.
+ *
+ * The calculated offset and span values for each component are then
+ * stored in the arrays <code>offsets</code> and <code>spans</code>.
+ *
+ * The components are placed in the forward direction, beginning with
+ * an offset of 0.
+ *
+ * @param allocated the amount of allocated space
+ * @param total the total size requirements of the components
+ * @param children the size requirement of each component
+ * @param offsets will hold the offset values for each component
+ * @param spans will hold the span values for each component
+ */
+ public static void calculateTiledPositions(int allocated,
+ SizeRequirements total,
+ SizeRequirements[] children,
+ int[] offsets, int[] spans)
+ {
+ calculateTiledPositions(allocated, total, children, offsets, spans, true);
+ }
+
+ /**
+ * Calculate the offsets and spans of the components, when they should
+ * be placed end-to-end.
+ *
+ * You must specify the amount of allocated space in
+ * <code>allocated</code>, the total size requirements of the set of
+ * components in <code>total</code> (this can be calculated using
+ * {@link #getTiledSizeRequirements} and the size requirements of the
+ * components in <code>children</code>.
+ *
+ * The calculated offset and span values for each component are then
+ * stored in the arrays <code>offsets</code> and <code>spans</code>.
+ *
+ * Depending on the value of <code>forward</code> the components are
+ * placed in the forward direction (left-right or top-bottom), where
+ * the offsets begin with 0, or in the reverse direction
+ * (right-left or bottom-top).
+ *
+ * @param allocated the amount of allocated space
+ * @param total the total size requirements of the components
+ * @param children the size requirement of each component
+ * @param offsets will hold the offset values for each component
+ * @param spans will hold the span values for each component
+ * @param forward whether the components should be placed in the forward
+ * direction (left-right or top-bottom) or reverse direction
+ * (right-left or bottom-top)
+ */
+ public static void calculateTiledPositions(int allocated,
+ SizeRequirements total,
+ SizeRequirements[] children,
+ int[] offsets, int[] spans,
+ boolean forward)
+ {
+ int span = 0;
+ if (forward)
+ {
+ int offset = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ offsets[i] = offset;
+ spans[i] = children[i].preferred;
+ span += spans[i];
+ offset += children[i].preferred;
+ }
+ }
+ else
+ {
+ int offset = allocated;
+ for (int i = 0; i < children.length; i++)
+ {
+ offset -= children[i].preferred;
+ offsets[i] = offset;
+ span += spans[i];
+ spans[i] = children[i].preferred;
+ }
+ }
+ // Adjust spans so that we exactly fill the allocated region. If
+ if (span > allocated)
+ adjustSmaller(allocated, children, spans, span);
+ else if (span < allocated)
+ adjustGreater(allocated, children, spans, span);
+
+ // Adjust offsets.
+ if (forward)
+ {
+ int offset = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ offsets[i] = offset;
+ offset += spans[i];
+ }
+ }
+ else
+ {
+ int offset = allocated;
+ for (int i = 0; i < children.length; i++)
+ {
+ offset -= spans[i];
+ offsets[i] = offset;
+ }
+ }
+ }
+
+ private static void adjustSmaller(int allocated, SizeRequirements[] children,
+ int[] spans, int span)
+ {
+ // Sum up (prefSize - minSize) over all children
+ int sumDelta = 0;
+ for (int i = 0; i < children.length; i++)
+ sumDelta += children[i].preferred - children[i].minimum;
+
+ // If we have sumDelta == 0, then all components have prefSize == maxSize
+ // and we can't do anything about it.
+ if (sumDelta == 0)
+ return;
+
+ // Adjust all sizes according to their preferred and minimum sizes.
+ for (int i = 0; i < children.length; i++)
+ {
+ double factor = ((double) (children[i].preferred - children[i].minimum))
+ / ((double) sumDelta);
+ // In case we have a sumDelta of 0, the factor should also be 0.
+ if (Double.isNaN(factor))
+ factor = 0;
+ spans[i] -= factor * (span - allocated);
+ }
+ }
+
+ private static void adjustGreater(int allocated, SizeRequirements[] children,
+ int[] spans, int span)
+ {
+ // Sum up (maxSize - prefSize) over all children
+ long sumDelta = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ sumDelta += children[i].maximum - children[i].preferred;
+ }
+
+ // If we have sumDelta == 0, then all components have prefSize == maxSize
+ // and we can't do anything about it.
+ if (sumDelta == 0)
+ return;
+
+ // Adjust all sizes according to their preferred and minimum sizes.
+ for (int i = 0; i < children.length; i++)
+ {
+ double factor = ((double) (children[i].maximum - children[i].preferred))
+ / ((double) sumDelta);
+ spans[i] += factor * (allocated - span);
+ }
+ }
+
+ /**
+ * Calculate the offsets and spans of the components, when they should
+ * be placed end-to-end.
+ *
+ * You must specify the amount of allocated space in
+ * <code>allocated</code>, the total size requirements of the set of
+ * components in <code>total</code> (this can be calculated using
+ * {@link #getTiledSizeRequirements} and the size requirements of the
+ * components in <code>children</code>.
+ *
+ * The calculated offset and span values for each component are then
+ * stored in the arrays <code>offsets</code> and <code>spans</code>.
+ *
+ * The components are tiled in the forward direction, beginning with
+ * an offset of 0.
+ *
+ * @param allocated the amount of allocated space
+ * @param total the total size requirements of the components
+ * @param children the size requirement of each component
+ * @param offsets will hold the offset values for each component
+ * @param spans will hold the span values for each component
+ */
+ public static void calculateAlignedPositions(int allocated,
+ SizeRequirements total,
+ SizeRequirements[] children,
+ int[] offsets, int[] spans)
+ {
+ calculateAlignedPositions(allocated, total, children, offsets, spans,
+ true);
+ }
+
+ /**
+ * Calculate the offsets and spans of the components, when they should
+ * be placed end-to-end.
+ *
+ * You must specify the amount of allocated space in
+ * <code>allocated</code>, the total size requirements of the set of
+ * components in <code>total</code> (this can be calculated using
+ * {@link #getTiledSizeRequirements} and the size requirements of the
+ * components in <code>children</code>.
+ *
+ * The calculated offset and span values for each component are then
+ * stored in the arrays <code>offsets</code> and <code>spans</code>.
+ *
+ * Depending on the value of <code>forward</code> the components are
+ * placed in the forward direction (left-right or top-bottom), where
+ * the offsets begin with 0, or in the reverse direction
+ * (right-left or bottom-top).
+ *
+ * @param allocated the amount of allocated space
+ * @param total the total size requirements of the components
+ * @param children the size requirement of each component
+ * @param spans will hold the span values for each component
+ * @param forward whether the components should be placed in the forward
+ * direction (left-right or top-bottom) or reverse direction
+ * (right-left or bottom-top)
+ */
+ public static void calculateAlignedPositions(int allocated,
+ SizeRequirements total,
+ SizeRequirements[] children,
+ int[] offset, int[] spans,
+ boolean forward)
+ {
+ // First we compute the position of the baseline.
+ float baseline = allocated * total.alignment;
+
+ // Now we can layout the components along the baseline.
+ for (int i = 0; i < children.length; i++)
+ {
+ float align = children[i].alignment;
+ // Try to fit the component into the available space.
+ int[] spanAndOffset = new int[2];
+ if (align < .5F || baseline == 0)
+ adjustFromRight(children[i], baseline, allocated, spanAndOffset);
+ else
+ adjustFromLeft(children[i], baseline, allocated, spanAndOffset);
+ spans[i] = spanAndOffset[0];
+ offset[i] = spanAndOffset[1];
+ }
+ }
+
+ /**
+ * Adjusts the span and offset of a component for the aligned layout.
+ *
+ * @param reqs
+ * @param baseline
+ * @param allocated
+ * @param spanAndOffset
+ */
+ private static void adjustFromRight(SizeRequirements reqs, float baseline,
+ int allocated, int[] spanAndOffset)
+ {
+ float right = allocated - baseline;
+ // If the resulting span exceeds the maximum of the component, then adjust
+ // accordingly.
+ float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment);
+ if (right / (1.F - reqs.alignment) > reqs.maximum)
+ right = maxRight;
+ // If we have not enough space on the left side, then adjust accordingly.
+ if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline)
+ right = ((float) (allocated - baseline))
+ / reqs.alignment * (1.F - reqs.alignment);
+
+ spanAndOffset[0] = (int) (right / (1.F - reqs.alignment));
+ spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
+ }
+
+ /**
+ * Adjusts the span and offset of a component for the aligned layout.
+ *
+ * @param reqs
+ * @param baseline
+ * @param allocated
+ * @param spanAndOffset
+ */
+ private static void adjustFromLeft(SizeRequirements reqs, float baseline,
+ int allocated, int[] spanAndOffset)
+ {
+ float left = baseline;
+ // If the resulting span exceeds the maximum of the component, then adjust
+ // accordingly.
+ float maxLeft = ((float) reqs.maximum) * reqs.alignment;
+ if (left / reqs.alignment > reqs.maximum)
+ left = maxLeft;
+ // If we have not enough space on the right side, then adjust accordingly.
+ if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline)
+ left = ((float) (allocated - baseline))
+ / (1.F - reqs.alignment) * reqs.alignment;
+
+ spanAndOffset[0] = (int) (left / reqs.alignment);
+ spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
+ }
+
+ /**
+ * Returns an array of new preferred sizes for the children based on
+ * <code>delta</code>. <code>delta</code> specifies a change in the
+ * allocated space. The sizes of the children will be shortened or
+ * lengthened to accomodate the new allocation.
+ *
+ * @param delta the change of the size of the total allocation for
+ * the components
+ * @param children the size requirements of each component
+ *
+ * @return the new preferred sizes for each component
+ */
+ public static int[] adjustSizes(int delta, SizeRequirements[] children)
+ {
+ return null; // TODO
+ }
+}
diff --git a/libjava/classpath/javax/swing/SizeSequence.java b/libjava/classpath/javax/swing/SizeSequence.java
new file mode 100644
index 000000000..69c7366be
--- /dev/null
+++ b/libjava/classpath/javax/swing/SizeSequence.java
@@ -0,0 +1,225 @@
+/* SizeSequence.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.Arrays;
+
+/**
+ * A sequence of values that represent the dimensions (widths or heights) of
+ * some collection of items (for example, the widths of the columns in a table).
+ *
+ * @author Andrew Selkirk
+ */
+public class SizeSequence
+{
+ // TODO: Sun's API specification for this class contains an implementation
+ // note regarding the encoding for the element sizes. We currently use the
+ // simple size encoding but we should look at improving this.
+
+ /** Storage for the element sizes. */
+ private int[] sizes;
+
+ /**
+ * Creates a new empty <code>SizeSequence</code> instance.
+ */
+ public SizeSequence()
+ {
+ sizes = new int[0];
+ }
+
+ /**
+ * Creates a new <code>SizeSequence</code> instance with the specified number
+ * of elements, each having a size of 0.
+ *
+ * @param numEntries the number of elements.
+ */
+ public SizeSequence(int numEntries)
+ {
+ this(numEntries, 0);
+ }
+
+ /**
+ * Creates a new <code>SizeSequence</code> instance with the specified number
+ * of elements all having the same size (<code>value</code>).
+ *
+ * @param numEntries the number of elements.
+ * @param value the value for each element.
+ */
+ public SizeSequence(int numEntries, int value)
+ {
+ sizes = new int[numEntries];
+ Arrays.fill(sizes, value);
+ }
+
+ /**
+ * Creates a new <code>SizeSequence</code> instance using the specified
+ * element sizes.
+ *
+ * @param sizes the element sizes (<code>null</code> not permitted).
+ */
+ public SizeSequence(int[] sizes)
+ {
+ this.sizes = (int[]) sizes.clone();
+ }
+
+ /**
+ * Sets the size of the element at the specified index.
+ *
+ * @param index the index.
+ * @param size the size.
+ */
+ public void setSize(int index, int size)
+ {
+ if (index >= 0 && index < sizes.length)
+ sizes[index] = size;
+ }
+
+ /**
+ * Returns the index of the element that contains the specified position.
+ *
+ * @param position the position.
+ *
+ * @return The index of the element that contains the specified position.
+ */
+ public int getIndex(int position)
+ {
+ int i = 0;
+ int runningTotal = 0;
+ while (i < sizes.length && position >= runningTotal + sizes[i])
+ {
+ runningTotal += sizes[i];
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * Returns the size of the specified element, or 0 if the element index is
+ * outside the defined range.
+ *
+ * @param index the element index.
+ *
+ * @return The size of the specified element, or 0 if the element index is
+ * outside the defined range.
+ */
+ public int getSize(int index)
+ {
+ if (index < 0 || index >= sizes.length)
+ return 0;
+ return sizes[index];
+ }
+
+ /**
+ * Sets the sizes for the elements in the sequence.
+ *
+ * @param sizes the element sizes (<code>null</code> not permitted).
+ */
+ public void setSizes(int[] sizes)
+ {
+ this.sizes = (int[]) sizes.clone();
+ }
+
+ /**
+ * Returns an array containing the sizes for all the elements in the sequence.
+ *
+ * @return The element sizes.
+ */
+ public int[] getSizes()
+ {
+ return (int[]) sizes.clone();
+ }
+
+ /**
+ * Returns the position of the specified element.
+ *
+ * @param index the element index.
+ *
+ * @return The position.
+ */
+ public int getPosition(int index)
+ {
+ int position;
+ int loop;
+ position = 0;
+ for (loop = 0; loop < index; loop++)
+ position += sizes[loop];
+ return position;
+
+ }
+
+ /**
+ * Inserts new entries into the sequence at the <code>start</code> position.
+ * There are <code>length</code> new entries each having the specified
+ * <code>value</code>.
+ *
+ * @param start the start element.
+ * @param length the number of elements to insert.
+ * @param value the size for each of the new elements.
+ */
+ public void insertEntries(int start, int length, int value)
+ {
+ int[] newSizes = new int[sizes.length + length];
+ System.arraycopy(sizes, 0, newSizes, 0, start);
+ for (int i = start; i < start + length; i++)
+ newSizes[i] = value;
+ System.arraycopy(sizes, start, newSizes, start + length,
+ sizes.length - start);
+ sizes = newSizes;
+ }
+
+ /**
+ * Removes the element(s) at index <code>start</code> (the number of elements
+ * removed is <code>length</code>).
+ *
+ * @param start the index of the first element to remove.
+ * @param length the number of elements to remove.
+ */
+ public void removeEntries(int start, int length)
+ {
+ // Sanity check.
+ if ((start + length) > sizes.length)
+ throw new IllegalArgumentException("Specified start/length that "
+ + "is greater than available sizes");
+
+ int[] newSizes = new int[sizes.length - length];
+ System.arraycopy(sizes, 0, newSizes, 0, start);
+ System.arraycopy(sizes, start + length, newSizes, start,
+ sizes.length - start - length);
+ sizes = newSizes;
+ }
+}
diff --git a/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java
new file mode 100644
index 000000000..3c07dbd93
--- /dev/null
+++ b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java
@@ -0,0 +1,333 @@
+/* SortingFocusTraversalPolicy.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+/**
+ * @author Graydon Hoare
+ * @author Michael Koch
+ *
+ * @since 1.4
+ */
+public class SortingFocusTraversalPolicy
+ extends InternalFrameFocusTraversalPolicy
+{
+ /**
+ * The comparator used to sort elements in the focus traversal cycle
+ * managed by this class.
+ */
+ Comparator comparator;
+
+ /**
+ * <p>Whether or not to perform an "implicit DownCycle" when selecting
+ * successor components within a focus cycle.</p>
+ *
+ * <p>When this is true, requesting the "next" component following a
+ * component which is a focus cycle root (and, necessarily, a container)
+ * will enter the focus cycle root of that container, and return its
+ * default focus.</p>
+ *
+ * <p>When this property is false, requesting the "next" component will
+ * simply advance within the containing focus cycle, subject to the
+ * {@link #comparator} order and the {@link #accept} judgment.</p>
+ *
+ * @see #getImplicitDownCycleTraversal()
+ */
+ boolean implicitDownCycleTraversal = true;
+
+ /**
+ * Creates a new <code>SortingFocusTraversalPolicy</code> with no
+ * comparator set.
+ */
+ protected SortingFocusTraversalPolicy()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Creates a new <code>SortingFocusTraversalPolicy</code> with the given
+ * comparator set.
+ *
+ * @param comparator the comparator to set
+ */
+ public SortingFocusTraversalPolicy(Comparator<? super Component> comparator)
+ {
+ this.comparator = comparator;
+ }
+
+ /**
+ * Decide whether a component is an acceptable focus owner.
+ *
+ * @param comp The component which is a candidate for focus ownership.
+ *
+ * @return true if the component is focusable, displayable, visible, and
+ * enabled; otherwise false
+ */
+ protected boolean accept(Component comp)
+ {
+ return (comp.isVisible()
+ && comp.isDisplayable()
+ && comp.isEnabled()
+ && comp.isFocusable());
+ }
+
+ /**
+ * Get the current value of the {@link #comparator} property.
+ *
+ * @return the current value of the property
+ *
+ * @see #setComparator
+ */
+ protected Comparator<? super Component> getComparator()
+ {
+ return comparator;
+ }
+
+ /**
+ * Set the current value of the {@link #comparator} property.
+ *
+ * @param comparator the new value of the property
+ *
+ * @see #getComparator
+ */
+ protected void setComparator(Comparator<? super Component> comparator)
+ {
+ this.comparator = comparator;
+ }
+
+ private TreeSet getSortedCycle(Container root, TreeSet set)
+ {
+ if (set == null)
+ set = (getComparator() == null
+ ? new TreeSet()
+ : new TreeSet(getComparator()));
+
+ if (root != null)
+ {
+ Component[] comps = root.getComponents();
+ for (int i = 0; i < comps.length; ++i)
+ {
+ Component c = comps[i];
+ if (accept(c))
+ set.add(c);
+ if (c instanceof Container)
+ getSortedCycle((Container) c, set);
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Return the component which follows the specified component in this
+ * focus cycle, relative to the order imposed by {@link
+ * #comparator}. Candidate components are only considered if they are
+ * accepted by the {@link #accept} method.
+ *
+ * If {@link #getImplicitDownCycleTraversal} is <code>true</code> and the
+ * <code>comp</code> is a focus cycle root, an "implicit DownCycle"
+ * occurs and the method returns the
+ * <code>getDefaultComponent(comp)</code>.
+ *
+ * @param root the focus cycle root to search for a successor within
+ * @param comp the component to search for the successor of
+ *
+ * @return the component following the specified component under
+ * the specified root, or null if no such component is found
+ *
+ * @throws IllegalArgumentException if either argument is null, or
+ * if the root is not a focus cycle root of the component
+ */
+ public Component getComponentAfter(Container root,
+ Component comp)
+ {
+ if (comp == null || root == null || !comp.isFocusCycleRoot(root))
+ throw new IllegalArgumentException();
+
+ if (getImplicitDownCycleTraversal()
+ && comp instanceof Container
+ && ((Container)comp).isFocusCycleRoot())
+ {
+ return getDefaultComponent((Container) comp);
+ }
+
+ TreeSet set = getSortedCycle(root, null);
+ Iterator i = set.iterator();
+ while (i.hasNext())
+ {
+ Component c = (Component) i.next();
+ if (c != null && c.equals(comp))
+ {
+ if (i.hasNext())
+ return (Component) i.next();
+ break;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Return the component which precedes the specified component in this
+ * focus cycle, relative to the order imposed by {@link
+ * #comparator}. Candidate components are only considered if they are
+ * accepted by the {@link #accept} method.
+ *
+ * @param root the focus cycle root to search for a predecessor within
+ * @param comp the component to search for the predecessor of
+ *
+ * @return the component preceding the specified component under the
+ * specified root, or null if no such component is found
+ *
+ * @throws IllegalArgumentException if either argument is null, or
+ * if the root is not a focus cycle root of the component
+ */
+ public Component getComponentBefore(Container root,
+ Component comp)
+ {
+ if (comp == null || root == null || !comp.isFocusCycleRoot(root))
+ throw new IllegalArgumentException();
+ TreeSet set = getSortedCycle(root, null);
+ Iterator i = set.iterator();
+ Component prev = null;
+ while (i.hasNext())
+ {
+ Component c = (Component) i.next();
+ if (c != null && c.equals(comp))
+ break;
+ prev = c;
+ }
+ return prev;
+ }
+
+ /**
+ * Return the default component of <code>root</code>, which is by default
+ * the same as the first component, returned by {@link
+ * #getFirstComponent}.
+ *
+ * @param root the focus cycle root to return the default component of
+ *
+ * @return the default focus component for <code>root</code>
+ *
+ * @throws IllegalArgumentException if root is null
+ */
+ public Component getDefaultComponent(Container root)
+ {
+ return getFirstComponent(root);
+ }
+
+ /**
+ * Return the first focusable component of the focus cycle root
+ * <code>comp</code> under the ordering imposed by the {@link
+ * #comparator} property. Candidate components are only considered if
+ * they are accepted by the {@link #accept} method.
+ *
+ * @param root the focus cycle root to search for the first component of
+ *
+ * @return the first component under <code>root</code>, or null if
+ * no components are found.
+ *
+ * @throws IllegalArgumentException if root is null
+ */
+ public Component getFirstComponent(Container root)
+ {
+ if (root == null)
+ throw new IllegalArgumentException();
+ TreeSet set = getSortedCycle(root, null);
+ Iterator i = set.iterator();
+ if (i.hasNext())
+ return (Component) i.next();
+ return null;
+ }
+
+ /**
+ * Return the last focusable component of the focus cycle root
+ * <code>comp</code> under the ordering imposed by the {@link
+ * #comparator} property. Candidate components are only considered if
+ * they are accepted by the {@link #accept} method.
+ *
+ * @param root the focus cycle root to search for the last component of
+ *
+ * @return the last component under <code>root</code>, or null if
+ * no components are found.
+ *
+ * @throws IllegalArgumentException if root is null
+ */
+ public Component getLastComponent(Container root)
+ {
+ if (root == null)
+ throw new IllegalArgumentException();
+ TreeSet set = getSortedCycle(root, null);
+ Iterator i = set.iterator();
+ Component last = null;
+ while (i.hasNext())
+ last = (Component) i.next();
+ return last;
+ }
+
+ /**
+ * Return the current value of the {@link #implicitDownCycleTraversal}
+ * property.
+ *
+ * @return the current value of the property
+ *
+ * @see #setImplicitDownCycleTraversal
+ */
+ public boolean getImplicitDownCycleTraversal()
+ {
+ return implicitDownCycleTraversal;
+ }
+
+ /**
+ * Set the current value of the {@link #implicitDownCycleTraversal}
+ * property.
+ *
+ * @param down the new value of the property
+ *
+ * @see #getImplicitDownCycleTraversal
+ */
+ public void setImplicitDownCycleTraversal(boolean down)
+ {
+ implicitDownCycleTraversal = down;
+ }
+}
diff --git a/libjava/classpath/javax/swing/SpinnerDateModel.java b/libjava/classpath/javax/swing/SpinnerDateModel.java
new file mode 100644
index 000000000..f0e9d1ec4
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpinnerDateModel.java
@@ -0,0 +1,317 @@
+/* SpinnerDateModel.java --
+ Copyright (C) 2002, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.swing.event.ChangeEvent;
+
+/**
+ * A date model used by the {@link JSpinner} component. This implements a
+ * spinner model for dates, rotating a calendar field such as month, year,
+ * day, week, hour, minute.
+ *
+ * @author Sven de Marothy
+ * @since 1.4
+ */
+public class SpinnerDateModel extends AbstractSpinnerModel
+ implements Serializable
+{
+ /** The current date. */
+ private Calendar date;
+
+ /**
+ * A constraint on the start or earliest permitted date (<code>null</code>
+ * for no minimum).
+ */
+ private Comparable start;
+
+ /**
+ * A constraint on the end or latest permitted date (<code>null</code> for no
+ * maximum).
+ */
+ private Comparable end;
+
+ /**
+ * The calendar field used to calculate the previous or next date.
+ */
+ private int calendarField;
+
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = -4802518107105940612L;
+
+ /**
+ * Constructs a <code>SpinnerDateModel</code> using the current date,
+ * no start or end limit, and {@link Calendar#DAY_OF_MONTH} as the calendar
+ * field.
+ */
+ public SpinnerDateModel()
+ {
+ this(new Date(), null, null, Calendar.DAY_OF_MONTH);
+ }
+
+ /**
+ * Constructs a <code>SpinnerDateModel</code> with the specified value, lower
+ * and upper bounds, and which spins the specified calendar field.
+ * <p>
+ * The <code>start</code> and <code>end</code> limits must have a
+ * <code>compareTo</code> method that supports instances of {@link Date}, but
+ * do not themselves need to be instances of {@link Date} (although typically
+ * they are).
+ *
+ * @param value the initial value/date (<code>null</code> not permitted).
+ * @param start a constraint that specifies the earliest permitted date
+ * value, or <code>null</code> for no lower limit.
+ * @param end a constraint that specifies the latest permitted date value,
+ * or <code>null</code> for no upper limit.
+ * @param calendarField the <code>Calendar</code> field to spin,
+ * (Calendar.ZONE_OFFSET and Calendar.DST_OFFSET are invalid)
+ */
+ public SpinnerDateModel(Date value, Comparable start, Comparable end,
+ int calendarField)
+ {
+ if (value == null)
+ throw new IllegalArgumentException("Null 'value' argument.");
+ if (start != null && start.compareTo(value) > 0)
+ throw new IllegalArgumentException("Require value on or after start.");
+ if (end != null && end.compareTo(value) < 0)
+ throw new IllegalArgumentException("Require value on or before end.");
+ date = Calendar.getInstance();
+ date.setTime(value);
+ this.start = start;
+ this.end = end;
+ setCalendarField(calendarField);
+ }
+
+ /**
+ * Returns the {@link Calendar} field used to calculate the previous and
+ * next dates in the sequence.
+ *
+ * @return The date field code.
+ */
+ public int getCalendarField()
+ {
+ return calendarField;
+ }
+
+ /**
+ * Returns the current date/time.
+ *
+ * @return The current date/time (never <code>null</code>).
+ *
+ * @see #getValue()
+ */
+ public Date getDate()
+ {
+ return date.getTime();
+ }
+
+ /**
+ * Returns the lower limit on the date/time value, or <code>null</code> if
+ * there is no minimum date/time.
+ *
+ * @return The lower limit.
+ *
+ * @see #setStart(Comparable)
+ */
+ public Comparable getStart()
+ {
+ return start;
+ }
+
+ /**
+ * Returns the upper limit on the date/time value, or <code>null</code> if
+ * there is no maximum date/time.
+ *
+ * @return The upper limit.
+ *
+ * @see #setEnd(Comparable)
+ */
+ public Comparable getEnd()
+ {
+ return end;
+ }
+
+ /**
+ * Returns the current date in the sequence (this method returns the same as
+ * {@link #getDate()}).
+ *
+ * @return The current date (never <code>null</code>).
+ */
+ public Object getValue()
+ {
+ return date.getTime();
+ }
+
+ /**
+ * Returns the next date in the sequence, or <code>null</code> if the
+ * next date is past the upper limit (if one is specified). The current date
+ * is not changed.
+ *
+ * @return The next date, or <code>null</code> if the current value is
+ * the latest date represented by the model.
+ *
+ * @see #getEnd()
+ */
+ public Object getNextValue()
+ {
+ Calendar nextCal = Calendar.getInstance();
+ nextCal.setTime(date.getTime());
+ nextCal.roll(calendarField, true);
+ Date nextDate = nextCal.getTime();
+ if (end != null)
+ if (end.compareTo(nextDate) < 0)
+ return null;
+ return nextDate;
+ }
+
+ /**
+ * Returns the previous date in the sequence, or <code>null</code> if the
+ * previous date is prior to the lower limit (if one is specified). The
+ * current date is not changed.
+ *
+ * @return The previous date, or <code>null</code> if the current value is
+ * the earliest date represented by the model.
+ *
+ * @see #getStart()
+ */
+ public Object getPreviousValue()
+ {
+ Calendar prevCal = Calendar.getInstance();
+ prevCal.setTime(date.getTime());
+ prevCal.roll(calendarField, false);
+ Date prevDate = prevCal.getTime();
+ if (start != null)
+ if (start.compareTo(prevDate) > 0)
+ return null;
+ return prevDate;
+ }
+
+ /**
+ * Sets the date field to change when calculating the next and previous
+ * values. It must be a valid {@link Calendar} field, excluding
+ * {@link Calendar#ZONE_OFFSET} and {@link Calendar#DST_OFFSET}.
+ *
+ * @param calendarField the calendar field to set.
+ *
+ * @throws IllegalArgumentException if <code>calendarField</code> is not
+ * a valid code.
+ */
+ public void setCalendarField(int calendarField)
+ {
+ if (calendarField < 0 || calendarField >= Calendar.FIELD_COUNT
+ || calendarField == Calendar.ZONE_OFFSET
+ || calendarField == Calendar.DST_OFFSET)
+ throw new IllegalArgumentException("Illegal calendarField");
+
+ if (this.calendarField != calendarField)
+ {
+ this.calendarField = calendarField;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Sets the lower limit for the date/time value and, if the new limit is
+ * different to the old limit, sends a {@link ChangeEvent} to all registered
+ * listeners. A <code>null</code> value is interpreted as "no lower limit".
+ * No check is made to ensure that the current date/time is on or after the
+ * new lower limit - the caller is responsible for ensuring that this
+ * relationship holds. In addition, the caller should ensure that
+ * <code>start</code> is {@link Serializable}.
+ *
+ * @param start the new lower limit for the date/time value
+ * (<code>null</code> permitted).
+ */
+ public void setStart(Comparable start)
+ {
+ if (this.start != start)
+ {
+ this.start = start;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Sets the upper limit for the date/time value and, if the new limit is
+ * different to the old limit, sends a {@link ChangeEvent} to all registered
+ * listeners. A <code>null</code> value is interpreted as "no upper limit".
+ * No check is made to ensure that the current date/time is on or before the
+ * new upper limit - the caller is responsible for ensuring that this
+ * relationship holds. In addition, the caller should ensure that
+ * <code>end</code> is {@link Serializable}.
+ *
+ * @param end the new upper limit for the date/time value (<code>null</code>
+ * permitted).
+ */
+ public void setEnd(Comparable end)
+ {
+ if (this.end != end)
+ {
+ this.end = end;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Sets the current date and, if the new value is different to the old
+ * value, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param value the new date (<code>null</code> not permitted, must be an
+ * instance of <code>Date</code>).
+ *
+ * @throws IllegalArgumentException if <code>value</code> is not an instance
+ * of <code>Date</code>.
+ */
+ public void setValue(Object value)
+ {
+ if (! (value instanceof Date) || value == null)
+ throw new IllegalArgumentException("Value not a date.");
+
+ if (!date.getTime().equals(value))
+ {
+ date.setTime((Date) value);
+ fireStateChanged();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/SpinnerListModel.java b/libjava/classpath/javax/swing/SpinnerListModel.java
new file mode 100644
index 000000000..de4926afd
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpinnerListModel.java
@@ -0,0 +1,295 @@
+/* SpinnerListModel.java -- A spinner model backed by a list or an array.
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.event.ChangeEvent;
+
+/**
+ * An implementation of <code>SpinnerModel</code> which uses the values
+ * contained within a list or an array. The backing list or array is
+ * only stored as a reference within the class. As a result, changes
+ * made elsewhere to the members of the list or array are reflected by
+ * this model.
+ * <p>
+ *
+ * The model itself inherits a list of <code>ChangeListener</code>s from
+ * <code>AbstractSpinnerModel</code>. As this code is unaware of changes
+ * made to the backing list or array, it is the responsibility of the
+ * application using the model to invoke <code>fireStateChanged()</code>,
+ * in order to notify any <code>ChangeListener</code>s, when the list or array
+ * changes. The model handles notification when the reference itself
+ * is changed via <code>setList()</code> or when the current value is
+ * set directly using <code>setValue()</code>.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see SpinnerModel
+ * @see AbstractSpinnerModel
+ * @see JSpinner
+ * @since 1.4
+ */
+
+public class SpinnerListModel extends AbstractSpinnerModel
+ implements Serializable
+{
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 3358804052191994516L;
+
+ /**
+ * The backing list for this model.
+ */
+ private List list;
+
+ /**
+ * The current index in the list.
+ */
+ private transient int index;
+
+ /**
+ * Constructs a default <code>SpinnerListModel</code>. This
+ * is a model backed by a list containing only the single
+ * <code>String</code> element, "empty".
+ */
+ public SpinnerListModel()
+ {
+ List defaultList;
+
+ // Create an empty list.
+ defaultList = new ArrayList();
+ // Add the string "empty".
+ defaultList.add("empty");
+ // Set the list.
+ setList(defaultList);
+ }
+
+ /**
+ * Constructs a <code>SpinnerListModel</code> using the supplied list.
+ * The model maintains a reference to this list, and returns
+ * consecutive elements in response to calls to <code>getNextValue()</code>.
+ * The initial value is that at position 0, so an initial call
+ * to <code>getValue()</code> returns the same as <code>list.get(0)</code>.
+ *
+ * @param list The list to use for this model.
+ *
+ * @throws IllegalArgumentException if the list is null or contains no
+ * elements.
+ *
+ * @see SpinnerListModel#getNextValue()
+ * @see SpinnerListModel#getValue()
+ */
+ public SpinnerListModel(List<?> list)
+ {
+ // Retain a reference to the valid list.
+ setList(list);
+ }
+
+ /**
+ * Constructs a <code>SpinnerListModel</code> using the supplied array.
+ * The model stores a reference to the wrapper list returned by
+ * <code>Arrays.asList()</code>. The wrapper list reflects modifications
+ * in the underlying array, so these changes will also be reflected
+ * by the model. The model produces consecutive elements from the array
+ * in response to calls to <code>getNextValue()</code>. The initial
+ * value returned by <code>getValue()</code> is the same as
+ * <code>array[0]</code>.
+ *
+ * @param array The array to use for this model.
+ *
+ * @throws IllegalArgumentException if the array is null or contains
+ * no elements.
+ *
+ * @see Arrays#asList(Object[])
+ * @see SpinnerListModel#getNextValue()
+ * @see SpinnerListModel#getValue()
+ */
+ public SpinnerListModel(Object[] array)
+ {
+ // Check for a null or zero-sized array.
+ if (array == null || array.length == 0)
+ {
+ throw new IllegalArgumentException("The supplied array was invalid.");
+ }
+
+ // Retain a reference to a wrapper around the valid array.
+ // The array, in list form, will be tested again here, but we can't really
+ // avoid this -- a null value to Arrays.asList will throw a
+ // NullPointerException.
+ setList(Arrays.asList(array));
+ }
+
+ /**
+ * Returns the backing list for this model.
+ *
+ * @return The backing list.
+ */
+ public List<?> getList()
+ {
+ return list;
+ }
+
+ /**
+ * Returns the next value from the list, which is the same as the element
+ * stored at the current index + 1. Null is returned if there are no more
+ * values to be returned (the end of the list has been reached). An
+ * ambiguity can occur here, as null may also be returned as a valid list
+ * element. This operation does not change the current value.
+ *
+ * @return The next value from the list or null.
+ */
+ public Object getNextValue()
+ {
+ // Check for a next value.
+ if (index < (list.size() - 1))
+ // Return the element at the next index.
+ return list.get(index + 1);
+ else
+ // Return null as this is the end of the list.
+ return null;
+ }
+
+ /**
+ * Returns the previous value from the list, which is the same as the element
+ * stored at the current index - 1. Null is returned if there are no more
+ * values to be returned (the start of the list has been reached). An
+ * ambiguity can occur here, as null may also be returned as a valid list
+ * element. This operation does not change the current value.
+ *
+ * @return The previous value from the list or null.
+ */
+ public Object getPreviousValue()
+ {
+ // Check for a previous value.
+ if (index > 0)
+ // Return the element at the previous position.
+ return list.get(index - 1);
+ else
+ // Return null as this is the start of the list.
+ return null;
+ }
+
+ /**
+ * Returns the current value of the model. Initially, this will
+ * be the element at position 0. On later invocations, this will
+ * be the last element returned by <code>getNextValue()</code>
+ * or <code>getPreviousValue()</code>.
+ *
+ * @return The current value.
+ *
+ * @see SpinnerListModel#getPreviousValue()
+ * @see SpinnerListModel#getNextValue()
+ */
+ public Object getValue()
+ {
+ return list.get(index);
+ }
+
+ /**
+ * Changes the backing list for this model. The model only stores
+ * a reference to the list, so any changes made to the list elsewhere
+ * will be reflected in the values returned by the model. A
+ * <code>ChangeEvent</code> is fired if the list being used actually
+ * changes (i.e. the new list is not referentially equal (!=) to the
+ * old one).
+ *
+ * @param list The new list to use.
+ *
+ * @throws IllegalArgumentException if the list is null or contains
+ * no elements.
+ *
+ * @see ChangeEvent
+ */
+ public void setList(List<?> list)
+ {
+ // Check for null or zero size list.
+ if (list == null || list.size() == 0)
+ throw new IllegalArgumentException("The supplied list was invalid.");
+
+ // Check for a change of referenced list.
+ if (this.list != list)
+ {
+ // Store the new list.
+ this.list = list;
+ // Notify listeners of a change.
+ fireStateChanged();
+ }
+ // We reset the other values in either case.
+ // Set the index to 0.
+ index = 0;
+ }
+
+ /**
+ * Sets the current value of the model to be the one supplied.
+ * The value must exist within the backing list in order for
+ * the change to take place. Otherwise, an exception is thrown.
+ * The value used is the first occurrence of the value within
+ * the backing list. Listeners are notified of this change.
+ * Following the change, <code>getNextValue()</code> and
+ * <code>getPreviousValue()</code> return the objects following
+ * and prior to the supplied value, respectively.
+ *
+ * @param value The requested new value of the list.
+ *
+ * @throws IllegalArgumentException if the supplied value does
+ * not exist in the backing list.
+ *
+ * @see SpinnerListModel#getPreviousValue()
+ * @see SpinnerListModel#getNextValue()
+ */
+ public void setValue(Object value)
+ {
+ int valueIndex;
+
+ // Search for the value in the list.
+ valueIndex = list.indexOf(value);
+ // Check for the value being found.
+ if (valueIndex == -1)
+ throw new IllegalArgumentException("The supplied value does not "
+ + "exist in this list");
+ // Make the indices match.
+ index = valueIndex;
+ // Notify the listeners.
+ fireStateChanged();
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/SpinnerModel.java b/libjava/classpath/javax/swing/SpinnerModel.java
new file mode 100644
index 000000000..6cab34da2
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpinnerModel.java
@@ -0,0 +1,111 @@
+/* SpinnerModel.java --
+ Copyright (C) 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.event.ChangeListener;
+
+/**
+ * The data model that is used in {@link JSpinner}s.
+ *
+ * @since 1.4
+ */
+public interface SpinnerModel
+{
+ /**
+ * Sets the current value of the model to that specified.
+ * Implementations can choose to refuse to accept the value
+ * and throw an exception instead. For example, a date model
+ * may throw invalid dates, or a list model may throw out
+ * values which don't exist in the underlying list. Models
+ * may also throw out unusual values, such as null. The decision
+ * is left to the discretion of the implementator. If the
+ * operation succeeds, the implementation should also notify
+ * any registered <code>ChangeListener</code>s.
+ *
+ * @param value The new value of the model.
+ * @throws IllegalArgumentException if the model does not accept
+ * the given value.
+ */
+ void setValue(Object value);
+
+ /**
+ * Returns the current value of the model.
+ *
+ * @return The current value.
+ */
+ Object getValue();
+
+ /**
+ * Returns the next value from the model. If the model is bounded,
+ * this method may return null when the upper bound is met.
+ * The current value is not changed.
+ *
+ * @return The next value, or null if there are no more values
+ * to retrieve.
+ */
+ Object getNextValue();
+
+ /**
+ * Returns the previous value from the model. If the model is
+ * bounded, this method may return null when the lower bound is
+ * met. The current value is not changed.
+ *
+ * @return The previous value, or null if there are no more
+ * values to retrieve.
+ */
+ Object getPreviousValue();
+
+ /**
+ * Adds a <code>ChangeListener</code> to the list of registered
+ * listeners. Each listener is notified when the current value
+ * is changed.
+ *
+ * @param listener The new listener to register.
+ */
+ void addChangeListener(ChangeListener listener);
+
+ /**
+ * Removes a given <code>ChangeListener</code> from the list
+ * of registered listeners.
+ *
+ * @param listener The listener to remove.
+ */
+ void removeChangeListener(ChangeListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/SpinnerNumberModel.java b/libjava/classpath/javax/swing/SpinnerNumberModel.java
new file mode 100644
index 000000000..af3fea65f
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpinnerNumberModel.java
@@ -0,0 +1,361 @@
+/* SpinnerNumberModel.java --
+ Copyright (C) 2002, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import javax.swing.event.ChangeEvent;
+
+/**
+ * A model used by the {@link JSpinner} component.
+ *
+ * @author Ka-Hing Cheung
+ * @since 1.4
+ */
+public class SpinnerNumberModel extends AbstractSpinnerModel
+ implements Serializable
+{
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 7279176385485777821L;
+
+ /** The current value. */
+ private Number value;
+
+ /** The minimum value (or <code>null</code>). */
+ private Comparable minimum;
+
+ /** The maximum value (or <code>null</code>). */
+ private Comparable maximum;
+
+ /** The step size. */
+ private Number stepSize;
+
+ /**
+ * Creates a <code>SpinnerNumberModel</code> with initial value 0, step 1,
+ * and no maximum nor minimum.
+ */
+ public SpinnerNumberModel()
+ {
+ this(new Integer(0), null, null, new Integer(1));
+ }
+
+ /**
+ * Creates a <code>SpinnerNumberModel</code> with double precision.
+ *
+ * @param value the initial value
+ * @param minimum the minimum value
+ * @param maximum the maximum value
+ * @param stepSize the step size
+ * @throws IllegalArgumentException if minimum &lt;= value &lt;= maximum does
+ * not hold.
+ */
+ public SpinnerNumberModel(double value, double minimum, double maximum,
+ double stepSize)
+ {
+ this(new Double(value), new Double(minimum), new Double(maximum),
+ new Double(stepSize));
+ }
+
+ /**
+ * Creates a <code>SpinnerNumberModel</code> with integer precision.
+ *
+ * @param value the initial value
+ * @param minimum the minimum value
+ * @param maximum the maximum value
+ * @param stepSize the step size
+ * @throws IllegalArgumentException if minimum &lt;= value &lt;= maximum does
+ * not hold.
+ */
+ public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)
+ {
+ this(new Integer(value), new Integer(minimum), new Integer(maximum),
+ new Integer(stepSize));
+ }
+
+ /**
+ * Creates a <code>SpinnerNumberModel</code> with the given attributes. The
+ * caller should ensure that both <code>minimum</code> and
+ * <code>maximum</code> are serializable.
+ *
+ * @param value the initial value (<code>null</code> not permitted).
+ * @param minimum the minimum value (<code>null</code> permitted).
+ * @param maximum the maximum value (<code>null</code> permitted).
+ * @param stepSize the step size (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if minimum &lt;= value &lt;= maximum
+ * does not hold
+ * @throws IllegalArgumentException if <code>value</code> is
+ * <code>null</code>.
+ * @throws IllegalArgumentException if <code>stepSize</code> is
+ * <code>null</code>.
+ */
+ public SpinnerNumberModel(Number value, Comparable minimum,
+ Comparable maximum, Number stepSize)
+ {
+ if (stepSize == null)
+ throw new IllegalArgumentException("stepSize may not be null");
+ if (value == null)
+ throw new IllegalArgumentException("value may not be null");
+ if (minimum != null)
+ {
+ if (minimum.compareTo(value) > 0)
+ throw new IllegalArgumentException("minimum is not <= value");
+ }
+ if (maximum != null)
+ {
+ if (maximum.compareTo(value) < 0)
+ throw new IllegalArgumentException("maximum is not >= value");
+ }
+
+ this.value = value;
+ this.stepSize = stepSize;
+ this.minimum = minimum;
+ this.maximum = maximum;
+ }
+
+ /**
+ * Sets the current value and, if the new value is different to the old
+ * value, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param value the new value (<code>null</code> not permitted, must be an
+ * instance of <code>Number</code>).
+ *
+ * @throws IllegalArgumentException if <code>value</code> is not an instance
+ * of <code>Number</code>.
+ */
+ public void setValue(Object value)
+ {
+ if (! (value instanceof Number))
+ throw new IllegalArgumentException("value must be a Number");
+
+ if (!this.value.equals(value))
+ {
+ this.value = (Number) value;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the current value, which for this class is always an instance of
+ * {@link Number}.
+ *
+ * @return The current value.
+ *
+ * @see #getNumber()
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Returns the next value, or <code>null</code> if adding the step size to
+ * the current value results in a value greater than the maximum value.
+ * The current value is not changed.
+ *
+ * @return The next value, or <code>null</code> if the current value is the
+ * maximum value represented by this model.
+ */
+ public Object getNextValue()
+ {
+ Number num;
+
+ if (value instanceof Double)
+ num = new Double(value.doubleValue() + stepSize.doubleValue());
+ else if (value instanceof Float)
+ num = new Double(value.floatValue() + stepSize.floatValue());
+ else if (value instanceof Long)
+ num = new Long(value.longValue() + stepSize.longValue());
+ else if (value instanceof Integer)
+ num = new Integer(value.intValue() + stepSize.intValue());
+ else if (value instanceof Short)
+ num = new Short((short) (value.shortValue() + stepSize.shortValue()));
+ else
+ num = new Byte((byte) (value.byteValue() + stepSize.byteValue()));
+
+ // check upper bound if set
+ if ((maximum != null) && maximum.compareTo(num) < 0)
+ num = null;
+
+ return num;
+ }
+
+ /**
+ * Returns the previous value, or <code>null</code> if subtracting the
+ * step size from the current value results in a value less than the minimum
+ * value. The current value is not changed.
+ *
+ * @return The previous value, or <code>null</code> if the current value
+ * is the minimum value represented by this model.
+ */
+ public Object getPreviousValue()
+ {
+ Number num;
+
+ if (value instanceof Double)
+ num = new Double(value.doubleValue() - stepSize.doubleValue());
+ else if (value instanceof Float)
+ num = new Double(value.floatValue() - stepSize.floatValue());
+ else if (value instanceof Long)
+ num = new Long(value.longValue() - stepSize.longValue());
+ else if (value instanceof Integer)
+ num = new Integer(value.intValue() - stepSize.intValue());
+ else if (value instanceof Short)
+ num = new Short((short) (value.shortValue() - stepSize.shortValue()));
+ else
+ num = new Byte((byte) (value.byteValue() - stepSize.byteValue()));
+
+ // check lower bound if set
+ if ((minimum != null) && minimum.compareTo(num) > 0)
+ num = null;
+
+ return num;
+ }
+
+ /**
+ * Returns the current value.
+ *
+ * @return The current value.
+ */
+ public Number getNumber()
+ {
+ return value;
+ }
+
+ /**
+ * Returns the minimum value, or <code>null</code> if there is no minimum.
+ *
+ * @return The minimum value.
+ *
+ * @see #setMinimum(Comparable)
+ */
+ public Comparable getMinimum()
+ {
+ return minimum;
+ }
+
+ /**
+ * Sets the minimum value and, if the new value is different to the old
+ * value, sends a {@link ChangeEvent} to all registered listeners. A
+ * <code>null</code> value is interpreted as "no minimum value". No check
+ * is made to ensure that the new minimum is less than or equal to the
+ * current value, the caller is responsible for ensuring that this
+ * relationship holds. In addition, the caller should ensure that
+ * <code>newMinimum</code> is {@link Serializable}.
+ *
+ * @param newMinimum the new minimum value (<code>null</code> permitted).
+ *
+ * @see #getMinimum()
+ */
+ public void setMinimum(Comparable newMinimum)
+ {
+ if (minimum != null ? !minimum.equals(newMinimum) : newMinimum != null)
+ {
+ minimum = newMinimum;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the maximum value, or <code>null</code> if there is no maximum.
+ *
+ * @return The maximum value.
+ *
+ * @see #getMinimum()
+ * @see #setMaximum(Comparable)
+ */
+ public Comparable getMaximum()
+ {
+ return maximum;
+ }
+
+ /**
+ * Sets the maximum value and, if the new value is different to the old
+ * value, sends a {@link ChangeEvent} to all registered listeners. A
+ * <code>null</code> value is interpreted as "no maximum value". No check
+ * is made to ensure that the new maximum is greater than or equal to the
+ * current value, the caller is responsible for ensuring that this
+ * relationship holds. In addition, the caller should ensure that
+ * <code>newMaximum</code> is {@link Serializable}.
+ *
+ * @param newMaximum the new maximum (<code>null</code> permitted).
+ *
+ * @see #getMaximum()
+ */
+ public void setMaximum(Comparable newMaximum)
+ {
+ if (maximum != null ? !maximum.equals(newMaximum) : newMaximum != null)
+ {
+ maximum = newMaximum;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the step size.
+ *
+ * @return The step size (never <code>null</code>).
+ */
+ public Number getStepSize()
+ {
+ return stepSize;
+ }
+
+ /**
+ * Sets the step size and, if the new step size is different to the old
+ * step size, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param newStepSize the new step size (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>newStepSize</code> is
+ * <code>null</code>.
+ */
+ public void setStepSize(Number newStepSize)
+ {
+ if (newStepSize == null)
+ throw new IllegalArgumentException();
+
+ if (!stepSize.equals(newStepSize))
+ {
+ stepSize = newStepSize;
+ fireStateChanged();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/Spring.java b/libjava/classpath/javax/swing/Spring.java
new file mode 100644
index 000000000..4cc06e539
--- /dev/null
+++ b/libjava/classpath/javax/swing/Spring.java
@@ -0,0 +1,745 @@
+/* Spring.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Dimension;
+
+/**
+ * Calculates the space between component edges, that are layed out by
+ * {@link SpringLayout}.
+ * <p>
+ * A Spring defines a minimum, preferred and maximum distance for each edge
+ * (north, east, south, west) of a component.
+ * </p>
+ * However, springs are not static, their actual values are computed at
+ * runtime. That means, if a Spring C is defined as the sum of Spring A and
+ * Spring B, then the values (min, pref and max) are not calculated at
+ * creation of Spring C, but instead always when {@link #getValue} is
+ * called. So, when Spring A or Spring B changes, this is reflected in
+ * Spring C.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+public abstract class Spring
+{
+
+ /** Indicates a not-set value. **/
+ public static final int UNSET = Integer.MIN_VALUE;
+
+ /**
+ * Creates a new Spring object. This constructor is used by the static
+ * methods which create Springs.
+ */
+ protected Spring()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a Spring which min, pref and max values are all the same.
+ * These kind of Springs are 'struts'.
+ *
+ * @param val the constant for min, pref and max values.
+ * @return a Spring object with constant values for min, pref and max.
+ */
+ public static Spring constant(int val)
+ {
+ return new SimpleSpring(val, val, val);
+ }
+
+ /** Creates a Spring which min, pref and max values are constants.
+ * @param min the constant for the minimum value.
+ * @param pref the constant for the preferred value.
+ * @param max the constant for the maximum value.
+ * @return a Spring object with constant values for min, pref and max.
+ */
+ public static Spring constant(int min, int pref, int max)
+ {
+ return new SimpleSpring(min, pref, max);
+ }
+
+ /**
+ * Returns the maximum value of the Spring.
+ *
+ * @return the maximum value.
+ */
+ public abstract int getMaximumValue();
+
+ /**
+ * Returns the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public abstract int getMinimumValue();
+
+ /**
+ * Return the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public abstract int getPreferredValue();
+
+ /**
+ * Return the actual value of this Spring.
+ *
+ * @return the actual value of this Spring.
+ */
+ public abstract int getValue();
+
+ /**
+ * Creates and returns a Spring, which always has the maximum values
+ * min = max(min_s1, min_s2), pref = max(pref_s1, pref_s2), max =
+ * max(max_s1, max_s2).
+ *
+ * @param s1 the first summand of the max Spring.
+ * @param s2 the second summand of the max Spring.
+ * @return a Spring which is max(s1, s2).
+ */
+ public static Spring max(Spring s1, Spring s2)
+ {
+ return new MaxSpring(s1, s2);
+ }
+
+ /**
+ * Creates and returns a Spring, which is always the negation of s.
+ * min = -min_s, pref = -pref_s, max = -max_pref.
+ *
+ * @param s the Spring to be negated.
+ * @return the negative of <code>s</code>.
+ */
+ public static Spring minus(Spring s)
+ {
+ return new MinusSpring(s);
+ }
+
+ /**
+ * Sets the actual value. If <code>value</code> is out of the (min, max)
+ * bounds, then the value is adjusted, so that is inside these bounds.
+ *
+ * @param value the value to be set.
+ */
+ public abstract void setValue(int value);
+
+ private int getShrinkRange()
+ {
+ return (getPreferredValue() - getMinimumValue());
+ }
+
+ private int getExpandRange()
+ {
+ return (getMaximumValue() - getPreferredValue());
+ }
+
+ double getStrain()
+ {
+ int v = getValue();
+ int p = getPreferredValue();
+ int r = (v < p) ? getShrinkRange() : getExpandRange();
+ if (r == 0)
+ r = 1;
+ return (double)(v - p) / r;
+ }
+
+ void setStrain(double strain)
+ {
+ int r = (strain < 0) ? getShrinkRange() : getExpandRange();
+ int v = (getPreferredValue() + (int)(strain * r));
+ setValue(v);
+ }
+
+ /**
+ * Creates and returns a Spring, which is always the sum of s1 and s2.
+ * min_sum = min_s1 + min_s2, pref_sum = pref_s1 + pref_s2, max_sum =
+ * max_s1 + max_s2.
+ *
+ * @param s1 the 1st summand of the sum Spring.
+ * @param s2 the 2nd summand of the sum Spring.
+ * @return a sum which is <code>s1 + s2</code>.
+ */
+ public static Spring sum(Spring s1, Spring s2)
+ {
+ return new AddSpring(s1, s2);
+ }
+
+ /**
+ * Return a new Spring which computes its values by scaling
+ * the values of another spring by a constant factor. If the
+ * factor is negative, the minimum and maximum values of
+ * the argument spring will be interchanged.
+ * @param spring the spring to track
+ * @param factor the factor by which to scale
+ * @return a new multiplicative Spring
+ * @since 1.5
+ */
+ public static Spring scale(final Spring spring, final float factor)
+ {
+ if (spring == null)
+ throw new NullPointerException("spring argument is null");
+ return new Spring()
+ {
+ public int getMaximumValue()
+ {
+ return (int) ((factor < 0 ? spring.getMinimumValue()
+ : spring.getMaximumValue())
+ * factor);
+ }
+
+ public int getMinimumValue()
+ {
+ return (int) ((factor < 0 ? spring.getMaximumValue()
+ : spring.getMinimumValue())
+ * factor);
+ }
+
+ public int getPreferredValue()
+ {
+ return (int) (spring.getPreferredValue() * factor);
+ }
+
+ public int getValue()
+ {
+ return (int) (spring.getValue() * factor);
+ }
+
+ public void setValue(int value)
+ {
+ spring.setValue((int) (value / factor));
+ }
+ };
+ }
+
+ /**
+ * Return a new Spring which takes its values from the specified
+ * Component. In particular, the maximum value is taken from
+ * the maximumSize, the minimum value is taken from the minimumSize,
+ * the preferred value is taken from the preferredSize, and the
+ * value is taken from the component's current size. These values
+ * change as the component changes size.
+ * @param component the component
+ * @return a new Spring which tracks the component's width
+ * @since 1.5
+ */
+ public static Spring width(final Component component)
+ {
+ return new Spring()
+ {
+ public int getMaximumValue()
+ {
+ return component.getMaximumSize().width;
+ }
+
+ public int getMinimumValue()
+ {
+ return component.getMinimumSize().width;
+ }
+
+ public int getPreferredValue()
+ {
+ return component.getPreferredSize().width;
+ }
+
+ public int getValue()
+ {
+ return component.getSize().width;
+ }
+
+ public void setValue(int value)
+ {
+ Dimension d = component.getSize();
+ component.setSize(value, d.height);
+ }
+ };
+ }
+
+ /**
+ * Return a new Spring which takes its values from the specified
+ * Component. In particular, the maximum value is taken from
+ * the maximumSize, the minimum value is taken from the minimumSize,
+ * the preferred value is taken from the preferredSize, and the
+ * value is taken from the component's current size. These values
+ * change as the component changes size.
+ * @param component the component
+ * @return a new Spring which tracks the component's height
+ * @since 1.5
+ */
+ public static Spring height(final Component component)
+ {
+ return new Spring()
+ {
+ public int getMaximumValue()
+ {
+ return component.getMaximumSize().height;
+ }
+
+ public int getMinimumValue()
+ {
+ return component.getMinimumSize().height;
+ }
+
+ public int getPreferredValue()
+ {
+ return component.getPreferredSize().height;
+ }
+
+ public int getValue()
+ {
+ return component.getSize().height;
+ }
+
+ public void setValue(int value)
+ {
+ Dimension d = component.getSize();
+ component.setSize(d.width, value);
+ }
+ };
+ }
+
+ /**
+ * A simple Spring, that holds constant values for min, pref and max.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ private static final class SimpleSpring extends Spring
+ {
+
+ /** The constant value for min. */
+ private final int min;
+
+ /** The constant value for pref. */
+ private final int pref;
+
+ /** The constant value for max. */
+ private final int max;
+
+ /** The actual value of the spring. */
+ private int value;
+
+ public String toString()
+ {
+ return "SimpleSpring of " + value;
+ }
+
+ /**
+ * Creates a new SimpleSpring object.
+ *
+ * @param newMin the constant minimum value.
+ * @param newPref the constant preferred value.
+ * @param newMax the constant maximum value.
+ */
+ public SimpleSpring(int newMin, int newPref, int newMax)
+ {
+ min = newMin;
+ pref = newPref;
+ max = newMax;
+ value = newPref;
+ }
+
+ /**
+ * Returns the maximum value of this Spring.
+ *
+ * @return the maximum value.
+ */
+ public int getMaximumValue()
+ {
+ return max;
+ }
+
+ /**
+ * Returns the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public int getMinimumValue()
+ {
+ return min;
+ }
+
+ /**
+ * Returns the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public int getPreferredValue()
+ {
+ return pref;
+ }
+
+ /**
+ * Return the actual current value of this Spring.
+ *
+ * @return the current value.
+ */
+ public int getValue()
+ {
+ if (value == Spring.UNSET)
+ return pref;
+ return value;
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @param val the value to be set.
+ */
+ public void setValue(int val)
+ {
+ value = val;
+ }
+ }
+
+
+ /**
+ * A Spring, that is the sum of two other Springs.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ private static final class AddSpring extends Spring
+ {
+
+ /** The springs, that are the 'operands' of this Spring. */
+ private final Spring s1;
+ private final Spring s2;
+
+ /** The current value for this Spring. */
+ private int value;
+
+ public String toString()
+ {
+ return "AddSpring of " + s1 + " and " + s2;
+ }
+
+ /**
+ * Creates a new AddSpring object.
+ *
+ * @param s1 the first operand.
+ * @param s2 the second operand.
+ */
+ protected AddSpring(Spring s1, Spring s2)
+ {
+ super();
+ this.s1 = s1;
+ this.s2 = s2;
+ value = Spring.UNSET;
+ }
+
+ /**
+ * Returns the maximum value of this Spring.
+ *
+ * @return the maximum value.
+ */
+ public int getMaximumValue()
+ {
+ int max1 = s1.getMaximumValue();
+ int max2 = s2.getMaximumValue();
+ return max1 + max2;
+ }
+
+ /**
+ * Return the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public int getMinimumValue()
+ {
+ int min1 = s1.getMinimumValue();
+ int min2 = s2.getMinimumValue();
+ return min1 + min2;
+ }
+
+ /**
+ * Returns the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public int getPreferredValue()
+ {
+ int pref1 = s1.getPreferredValue();
+ int pref2 = s2.getPreferredValue();
+ return pref1 + pref2;
+ }
+
+ /**
+ * Returns the actual current value of this Spring.
+ *
+ * @return the current value of this Spring.
+ */
+ public int getValue()
+ {
+ if (value == Spring.UNSET)
+ {
+ int val1 = s1.getValue();
+ int val2 = s2.getValue();
+ value = val1 + val2;
+ }
+ return value;
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @param val the value to be set.
+ */
+ public void setValue(int val)
+ {
+ if (val == Spring.UNSET)
+ {
+ if (value != Spring.UNSET)
+ {
+ s1.setValue(Spring.UNSET);
+ s2.setValue(Spring.UNSET);
+ }
+ value = Spring.UNSET;
+ return;
+ }
+
+ value = val;
+
+ //Spead the value over the two components
+ double fStrain = getStrain();
+ s1.setStrain(fStrain);
+ int remainder = val - s1.getValue();
+ s2.setValue(remainder);
+ }
+
+ }
+
+
+ /**
+ * A Spring that is calculated as the negation of another Spring.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ private static final class MinusSpring extends Spring
+ {
+
+ /** The Spring from which to calculate the negation. */
+ private final Spring s;
+
+ public String toString()
+ {
+ return "MinusSpring of " + s;
+ }
+
+ /**
+ * Creates a new MinusSpring object.
+ * @param s the Spring from which to calculate the negation.
+ */
+ protected MinusSpring(Spring s)
+ {
+ super();
+ this.s = s;
+ }
+
+ /** Returns the maximum value of this Spring.
+ *
+ * @return the maximum value.
+ */
+ public int getMaximumValue()
+ {
+ return -s.getMinimumValue();
+ }
+
+ /**
+ * Returns the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public int getMinimumValue()
+ {
+ return -s.getMaximumValue();
+ }
+
+ /**
+ * Returns the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public int getPreferredValue()
+ {
+ return -s.getPreferredValue();
+ }
+
+ /**
+ * Returns the current value of this Spring.
+ *
+ * @return the current value.
+ */
+ public int getValue()
+ {
+ return -s.getValue();
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @param val the value to be set.
+ */
+ public void setValue(int val)
+ {
+ if (val == Spring.UNSET)
+ s.setValue(Spring.UNSET);
+ else
+ s.setValue(-val);
+ }
+ }
+
+
+ /**
+ * A Spring, that is calculated as the maximum of two Springs.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ private static final class MaxSpring extends Spring
+ {
+
+ /** The two other Springs from which to calculate the maximum. */
+ private final Spring s1;
+ private final Spring s2;
+
+ public String toString()
+ {
+ return "MaxSpring of " + s1 + " and " + s2;
+ }
+
+ /** The current value of this Spring. */
+ private int value;
+
+ /**
+ * Creates a new MaxSpring object.
+ *
+ * @param s1 the 1st operand.
+ * @param s2 the 2nd operand.
+ */
+ protected MaxSpring(Spring s1, Spring s2)
+ {
+ super();
+ this.s1 = s1;
+ this.s2 = s2;
+ value = Spring.UNSET;
+ }
+
+
+ /**
+ * Returns the maximum value of this Spring.
+ *
+ * @return the maximum value.
+ */
+ public int getMaximumValue()
+ {
+ int max1 = s1.getMaximumValue();
+ int max2 = s2.getMaximumValue();
+ return Math.max(max1, max2);
+ }
+
+ /**
+ * Returns the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public int getMinimumValue()
+ {
+ int min1 = s1.getMinimumValue();
+ int min2 = s2.getMinimumValue();
+ return Math.max(min1, min2);
+ }
+
+ /**
+ * Returns the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public int getPreferredValue()
+ {
+ int pref1 = s1.getPreferredValue();
+ int pref2 = s2.getPreferredValue();
+ return Math.max(pref1, pref2);
+ }
+
+ /**
+ * Returns the actual value of this Spring.
+ *
+ * @return the current value.
+ */
+ public int getValue()
+ {
+ if (value == Spring.UNSET)
+ {
+ int val1 = s1.getValue();
+ int val2 = s2.getValue();
+ value = Math.max(val1, val2);
+ }
+ return value;
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @param val the value to be set.
+ */
+ public void setValue(int val)
+ {
+ if (val == Spring.UNSET)
+ {
+ if (value != Spring.UNSET)
+ {
+ s1.setValue(Spring.UNSET);
+ s2.setValue(Spring.UNSET);
+ }
+ value = Spring.UNSET;
+ return;
+ }
+
+ value = val;
+
+ int p1 = s1.getPreferredValue();
+ int p2 = s2.getPreferredValue();
+
+ if (p1 < p2)
+ {
+ s1.setValue(Math.min(val, p1));
+ s2.setValue(val);
+ }
+ else
+ {
+ s1.setValue(val);
+ s2.setValue(Math.min(val, p2));
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/SpringLayout.java b/libjava/classpath/javax/swing/SpringLayout.java
new file mode 100644
index 000000000..2be5189b5
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpringLayout.java
@@ -0,0 +1,832 @@
+/* SpringLayout.java --
+ Copyright (C) 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.LayoutManager2;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A very flexible layout manager. Components are laid out by defining the
+ * relationships between them. The relationships are expressed as
+ * {@link Spring}s. You can attach a Spring for each edge of a component and
+ * link it to an edge of a different component. For example, you can say,
+ * the northern edge of component A should be attached to the southern edge
+ * of component B, and the space between them should be something between
+ * x and y pixels, and preferably z pixels.
+ * <p>While quite simple, this layout manager can be used to emulate most other
+ * layout managers, and can also be used to solve some layout problems, which
+ * would be hard to solve with other layout managers.</p>
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+public class SpringLayout implements LayoutManager2
+{
+
+ /** The right edge of a component. */
+ public static final String EAST = "East";
+
+ /** The top edge of a component. */
+ public static final String NORTH = "North";
+
+ /** The bottom edge of a component. */
+ public static final String SOUTH = "South";
+
+ /** The left edge of a component. */
+ public static final String WEST = "West";
+
+ /** maps components to their constraints. */
+ private Map constraintsMap;
+
+ /**
+ * The constraints that define the relationships between components.
+ * Each Constraints object can hold 4 Springs: one for each edge of the
+ * component. Additionally it can hold Springs for the components width
+ * and the components height. Since the height and width constraints are
+ * dependend on the other constraints, a component can be over-constraint.
+ * In this case (like when all of NORTH, SOUTH and HEIGHT are constraint),
+ * the values are adjusted, so that the mathematics still hold true.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ public static class Constraints
+ {
+
+ // The constraints for each edge, and width and height.
+ /** The Spring for the left edge. */
+ private Spring x;
+
+ /** The Spring for the upper edge. */
+ private Spring y;
+
+ /** The Spring for the height. */
+ private Spring height;
+
+ /** The Spring for the width. */
+ private Spring width;
+
+ /** The Spring for the right edge. */
+ private Spring east;
+
+ /** The Spring for the bottom edge. */
+ private Spring south;
+
+ /**
+ In each axis the user can set three values, i.e. x, width, east, if all
+ three are set, then there's no room for manoeuvre so in those cases the
+ third will be described by the below spring which is calculated in terms
+ of the other two
+ */
+ private Spring v;
+ private Spring h;
+
+ /**
+ * Creates a new Constraints object.
+ * There is no constraint set.
+ */
+ public Constraints()
+ {
+ x = y = height = width = east = south = v = h = null;
+ }
+
+ /**
+ * Creates a new Constraints object.
+ *
+ * @param x the constraint for the left edge of the component.
+ * @param y the constraint for the upper edge of the component.
+ */
+ public Constraints(Spring x, Spring y)
+ {
+ this.x = x;
+ this.y = y;
+ width = height = east = south = v = h = null;
+ }
+
+ /**
+ * Creates a new Constraints object.
+ *
+ * @param x the constraint for the left edge of the component.
+ * @param y the constraint for the upper edge of the component.
+ * @param width the constraint for the width of the component.
+ * @param height the constraint for the height of the component.
+ */
+ public Constraints(Spring x, Spring y, Spring width, Spring height)
+ {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ east = south = v = h = null;
+ }
+
+ /**
+ * Create a new Constraints object which tracks the indicated
+ * component. The x and y positions for this Constraints object
+ * are constant Springs created with the component's location at
+ * the time this constructor is called. The width and height
+ * of this Constraints are Springs created using
+ * {@link Spring#width(Component)} and {@link Spring#height(Component)},
+ * respectively.
+ * @param component the component to track
+ * @since 1.5
+ */
+ public Constraints(Component component)
+ {
+ this(Spring.constant(component.getX()),
+ Spring.constant(component.getY()),
+ Spring.width(component),
+ Spring.height(component));
+ }
+
+ /**
+ * Returns the constraint for the edge with the <code>edgeName</code>.
+ * This is expected to be one of
+ * {@link #EAST}, {@link #WEST}, {@link #NORTH} or {@link #SOUTH}.
+ *
+ * @param edgeName the name of the edge.
+ * @return the constraint for the specified edge.
+ */
+ public Spring getConstraint(String edgeName)
+ {
+ Spring retVal = null;
+ if (edgeName.equals(SpringLayout.NORTH))
+ retVal = getY();
+ else if (edgeName.equals(SpringLayout.WEST))
+ retVal = getX();
+ else if (edgeName.equals(SpringLayout.SOUTH))
+ retVal = getSouth();
+ else if (edgeName.equals(SpringLayout.EAST))
+ retVal = getEast();
+ return retVal;
+ }
+
+ /**
+ * Returns the constraint for the height of the component.
+ *
+ * @return the height constraint.
+ */
+ public Spring getHeight()
+ {
+ if (height != null)
+ return height;
+ else if ((v == null) && (y != null) && (south != null))
+ v = Spring.sum(south, Spring.minus(y));
+ return v;
+ }
+
+ /**
+ * Returns the constraint for the width of the component.
+ *
+ * @return the width constraint.
+ */
+ public Spring getWidth()
+ {
+ if (width != null)
+ return width;
+ else if ((h == null) && (x != null) && (east != null))
+ h = Spring.sum(east, Spring.minus(x));
+ return h;
+ }
+
+ /**
+ * Returns the constraint for the left edge of the component.
+ *
+ * @return the left-edge constraint (== WEST).
+ */
+ public Spring getX()
+ {
+ if (x != null)
+ return x;
+ else if ((h == null) && (width != null) && (east != null))
+ h = Spring.sum(east, Spring.minus(width));
+ return h;
+ }
+
+ /**
+ * Returns the constraint for the upper edge of the component.
+ *
+ * @return the upper-edge constraint (== NORTH).
+ */
+ public Spring getY()
+ {
+ if (y != null)
+ return y;
+ else if ((v == null) && (height != null) && (south != null))
+ v = Spring.sum(south, Spring.minus(height));
+ return v;
+ }
+
+ /**
+ * Returns the constraint for the lower edge of the component.
+ *
+ * @return the lower-edge constraint (== SOUTH).
+ */
+ public Spring getSouth()
+ {
+ if (south != null)
+ return south;
+ else if ((v == null) && (height != null) && (y != null))
+ v = Spring.sum(y, height);
+ return v;
+ }
+
+ /**
+ * Returns the constraint for the right edge of the component.
+ *
+ * @return the right-edge constraint (== EAST).
+ */
+ public Spring getEast()
+ {
+ if (east != null)
+ return east;
+ else if ((h == null) && (width != null) && (x != null))
+ h = Spring.sum(x, width);
+ return h;
+ }
+
+ /**
+ * Sets a constraint for the specified edge. If this leads to an
+ * over-constrained situation, the constraints get adjusted, so that
+ * the mathematics still hold true.
+ *
+ * @param edgeName the name of the edge, one of {@link #EAST},
+ * {@link #WEST}, {@link #NORTH} or {@link #SOUTH}.
+ * @param s the constraint to be set.
+ */
+ public void setConstraint(String edgeName, Spring s)
+ {
+
+ if (edgeName.equals(SpringLayout.WEST))
+ setX(s);
+ else if (edgeName.equals(SpringLayout.NORTH))
+ setY(s);
+ else if (edgeName.equals(SpringLayout.EAST))
+ setEast(s);
+ else if (edgeName.equals(SpringLayout.SOUTH))
+ setSouth(s);
+
+ }
+
+ /**
+ * Sets the height-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setHeight(Spring s)
+ {
+ height = s;
+ v = null;
+ if ((south != null) && (y != null) && (height != null))
+ south = null;
+ }
+
+ /**
+ * Sets the width-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setWidth(Spring s)
+ {
+ width = s;
+ h = null;
+ if ((east != null) && (x != null) && (width != null))
+ east = null;
+ }
+
+ /**
+ * Sets the WEST-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setX(Spring s)
+ {
+ x = s;
+ h = null;
+ if ((width != null) && (east != null) && (x != null))
+ width = null;
+ }
+
+ /**
+ * Sets the NORTH-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setY(Spring s)
+ {
+ y = s;
+ v = null;
+ if ((height != null) && (south != null) && (y != null))
+ height = null;
+ }
+
+ /**
+ * Sets the SOUTH-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setSouth(Spring s)
+ {
+ south = s;
+ v = null;
+ if ((height != null) && (south != null) && (y != null))
+ y = null;
+ }
+
+ /**
+ * Sets the EAST-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setEast(Spring s)
+ {
+ east = s;
+ h = null;
+ if ((width != null) && (east != null) && (x != null))
+ x = null;
+ }
+
+ public void dropCalcResult()
+ {
+ if (x != null)
+ x.setValue(Spring.UNSET);
+ if (y != null)
+ y.setValue(Spring.UNSET);
+ if (width != null)
+ width.setValue(Spring.UNSET);
+ if (height != null)
+ height.setValue(Spring.UNSET);
+ if (east != null)
+ east.setValue(Spring.UNSET);
+ if (south != null)
+ south.setValue(Spring.UNSET);
+ if (h != null)
+ h.setValue(Spring.UNSET);
+ if (v != null)
+ v.setValue(Spring.UNSET);
+ }
+ }
+
+ /**
+ * Creates a new SpringLayout.
+ */
+ public SpringLayout()
+ {
+ constraintsMap = new HashMap();
+ }
+
+ /**
+ * Adds a layout component and a constraint object to this layout.
+ * This method is usually only called by a {@link java.awt.Container}s add
+ * method.
+ *
+ * @param component the component to be added.
+ * @param constraint the constraint to be set.
+ */
+ public void addLayoutComponent(Component component, Object constraint)
+ {
+ constraintsMap.put(component, constraint);
+ }
+
+ /**
+ * Adds a layout component and a constraint object to this layout.
+ * This method is usually only called by a {@link java.awt.Container}s add
+ * method. This method does nothing, since SpringLayout does not manage
+ * String-indexed components.
+ *
+ * @param name the name.
+ * @param c the component to be added.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // do nothing here.
+ }
+
+ /**
+ * The trick to SpringLayout is that the network of Springs needs to
+ * completely created before the positioning results are generated.
+ *
+ * Using the springs directly during network creation will set their values
+ * before the network is completed, Using Deferred Springs during creation of
+ * the network allows all the edges to be connected together and the network
+ * to be created without resolving the Springs until their results need to be
+ * known, at which point the network is complete and the spring addition and
+ * and substitution calculations will work on a complete and valid network.
+ *
+ * @author Caolan McNamara (caolanm@redhat.com)
+ */
+ private static class DeferredSpring extends Spring
+ {
+ private SpringLayout sl;
+ private String edgeName;
+ private Component c;
+
+ public String toString()
+ {
+ return "DeferredSpring of edge" + edgeName + " of " + "something";
+ }
+
+ public DeferredSpring(SpringLayout s, String edge, Component component)
+ {
+ sl = s;
+ edgeName = edge;
+ c = component;
+ }
+
+ private Spring resolveSpring()
+ {
+ return sl.getConstraints(c).getConstraint(edgeName);
+ }
+
+ public int getMaximumValue()
+ {
+ return resolveSpring().getMaximumValue();
+ }
+
+ public int getMinimumValue()
+ {
+ return resolveSpring().getMinimumValue();
+ }
+
+ public int getPreferredValue()
+ {
+ return resolveSpring().getPreferredValue();
+ }
+
+ public int getValue()
+ {
+ int nRet = resolveSpring().getValue();
+ if (nRet == Spring.UNSET)
+ nRet = getPreferredValue();
+ return nRet;
+ }
+
+ public void setValue(int size)
+ {
+ resolveSpring().setValue(size);
+ }
+ }
+
+ private abstract static class DeferredDimension extends Spring
+ {
+ private int value;
+
+ public DeferredDimension()
+ {
+ value = Spring.UNSET;
+ }
+
+ public void setValue(int val)
+ {
+ value = val;
+ }
+
+ public int getValue()
+ {
+ if (value == Spring.UNSET)
+ return getPreferredValue();
+ return value;
+ }
+ }
+
+ private static class DeferredWidth extends DeferredDimension
+ {
+ private Component c;
+
+
+ public DeferredWidth(Component component)
+ {
+ c = component;
+ }
+
+ public String toString()
+ {
+ return "DeferredWidth of " + "something";
+ }
+
+ //clip max to a value we can do meaningful calculation with
+ public int getMaximumValue()
+ {
+ int widget_width = c.getMaximumSize().width;
+ return Math.min(Short.MAX_VALUE, widget_width);
+ }
+
+ public int getMinimumValue()
+ {
+ return c.getMinimumSize().width;
+ }
+
+ public int getPreferredValue()
+ {
+ return c.getPreferredSize().width;
+ }
+ }
+
+ private static class DeferredHeight extends DeferredDimension
+ {
+ private Component c;
+
+ public String toString()
+ {
+ return "DeferredHeight of " + "something";
+ }
+
+ public DeferredHeight(Component component)
+ {
+ c = component;
+ }
+
+ //clip max to a value we can do meaningful calculations with it
+ public int getMaximumValue()
+ {
+ int widget_height = c.getMaximumSize().height;
+ return Math.min(Short.MAX_VALUE, widget_height);
+ }
+
+ public int getMinimumValue()
+ {
+ return c.getMinimumSize().height;
+ }
+
+ public int getPreferredValue()
+ {
+ return c.getPreferredSize().height;
+ }
+ }
+
+ /**
+ * Returns the constraint of the edge named by <code>edgeName</code>.
+ *
+ * @param c the component from which to get the constraint.
+ * @param edgeName the name of the edge, one of {@link #EAST},
+ * {@link #WEST}, {@link #NORTH} or {@link #SOUTH}.
+ * @return the constraint of the edge <code>edgeName</code> of the
+ * component c.
+ */
+ public Spring getConstraint(String edgeName, Component c)
+ {
+ return new DeferredSpring(this, edgeName, c);
+ }
+
+ /**
+ * Returns the {@link Constraints} object associated with the specified
+ * component.
+ *
+ * @param c the component for which to determine the constraint.
+ * @return the {@link Constraints} object associated with the specified
+ * component.
+ */
+ public SpringLayout.Constraints getConstraints(Component c)
+ {
+ Constraints constraints = (Constraints) constraintsMap.get(c);
+
+ if (constraints == null)
+ {
+ constraints = new Constraints();
+
+ constraints.setWidth(new DeferredWidth(c));
+ constraints.setHeight(new DeferredHeight(c));
+ constraints.setX(Spring.constant(0));
+ constraints.setY(Spring.constant(0));
+
+ constraintsMap.put(c, constraints);
+ }
+
+ return constraints;
+ }
+
+ /**
+ * Returns the X alignment of the Container <code>p</code>.
+ *
+ * @param p
+ * the {@link java.awt.Container} for which to determine the X
+ * alignment.
+ * @return always 0.0
+ */
+ public float getLayoutAlignmentX(Container p)
+ {
+ return 0.0F;
+ }
+
+ /**
+ * Returns the Y alignment of the Container <code>p</code>.
+ *
+ * @param p the {@link java.awt.Container} for which to determine the Y
+ * alignment.
+ * @return always 0.0
+ */
+ public float getLayoutAlignmentY(Container p)
+ {
+ return 0.0F;
+ }
+
+ /**
+ * Recalculate a possibly cached layout.
+ */
+ public void invalidateLayout(Container p)
+ {
+ // nothing to do here yet
+ }
+
+ private Constraints initContainer(Container p)
+ {
+ Constraints c = getConstraints(p);
+
+ c.setX(Spring.constant(0));
+ c.setY(Spring.constant(0));
+ c.setWidth(null);
+ c.setHeight(null);
+ if (c.getEast() == null)
+ c.setEast(Spring.constant(0, 0, Integer.MAX_VALUE));
+ if (c.getSouth() == null)
+ c.setSouth(Spring.constant(0, 0, Integer.MAX_VALUE));
+
+ return c;
+ }
+
+ /**
+ * Lays out the container <code>p</code>.
+ *
+ * @param p the container to be laid out.
+ */
+ public void layoutContainer(Container p)
+ {
+ java.awt.Insets insets = p.getInsets();
+
+ Component[] components = p.getComponents();
+
+ Constraints cs = initContainer(p);
+ cs.dropCalcResult();
+
+ for (int index = 0 ; index < components.length; index++)
+ {
+ Component c = components[index];
+ getConstraints(c).dropCalcResult();
+ }
+
+ int offsetX = p.getInsets().left;
+ int offsetY = p.getInsets().right;
+
+ cs.getX().setValue(0);
+ cs.getY().setValue(0);
+ cs.getWidth().setValue(p.getWidth() - offsetX - insets.right);
+ cs.getHeight().setValue(p.getHeight() - offsetY - insets.bottom);
+
+ for (int index = 0; index < components.length; index++)
+ {
+ Component c = components[index];
+
+ Constraints constraints = getConstraints(c);
+
+ int x = constraints.getX().getValue();
+ int y = constraints.getY().getValue();
+ int width = constraints.getWidth().getValue();
+ int height = constraints.getHeight().getValue();
+
+ c.setBounds(x + offsetX, y + offsetY, width, height);
+ }
+ }
+
+ /**
+ * Calculates the maximum size of the layed out container. This
+ * respects the maximum sizes of all contained components.
+ *
+ * @param p the container to be laid out.
+ * @return the maximum size of the container.
+ */
+ public Dimension maximumLayoutSize(Container p)
+ {
+ java.awt.Insets insets = p.getInsets();
+
+ Constraints cs = initContainer(p);
+
+ int maxX = cs.getWidth().getMaximumValue() + insets.left + insets.right;
+ int maxY = cs.getHeight().getMaximumValue() + insets.top + insets.bottom;
+
+ return new Dimension(maxX, maxY);
+ }
+
+
+ /**
+ * Calculates the minimum size of the layed out container. This
+ * respects the minimum sizes of all contained components.
+ *
+ * @param p the container to be laid out.
+ * @return the minimum size of the container.
+ */
+ public Dimension minimumLayoutSize(Container p)
+ {
+ java.awt.Insets insets = p.getInsets();
+
+ Constraints cs = initContainer(p);
+
+ int maxX = cs.getWidth().getMinimumValue() + insets.left + insets.right;
+ int maxY = cs.getHeight().getMinimumValue() + insets.top + insets.bottom;
+
+ return new Dimension(maxX, maxY);
+ }
+
+ /**
+ * Calculates the preferred size of the layed out container. This
+ * respects the preferred sizes of all contained components.
+ *
+ * @param p the container to be laid out.
+ * @return the preferred size of the container.
+ */
+ public Dimension preferredLayoutSize(Container p)
+ {
+ java.awt.Insets insets = p.getInsets();
+
+ Constraints cs = initContainer(p);
+
+ int maxX = cs.getWidth().getPreferredValue() + insets.left + insets.right;
+ int maxY = cs.getHeight().getPreferredValue() + insets.top + insets.bottom;
+
+ return new Dimension(maxX, maxY);
+ }
+
+ /**
+ * Attaches the edge <code>e1</code> of component <code>c1</code> to
+ * the edge <code>e2</code> of component <code>c2</code> width the
+ * fixed strut <code>pad</code>.
+ *
+ * @param e1 the edge of component 1.
+ * @param c1 the component 1.
+ * @param pad the space between the components in pixels.
+ * @param e2 the edge of component 2.
+ * @param c2 the component 2.
+ */
+ public void putConstraint(String e1, Component c1, int pad, String e2,
+ Component c2)
+ {
+ putConstraint(e1, c1, Spring.constant(pad), e2, c2);
+ }
+
+ /**
+ * Attaches the edge <code>e1</code> of component <code>c1</code> to
+ * the edge <code>e2</code> of component <code>c2</code> width the
+ * {@link Spring} <code>s</code>.
+ *
+ * @param e1 the edge of component 1.
+ * @param c1 the component 1.
+ * @param s the space between the components as a {@link Spring} object.
+ * @param e2 the edge of component 2.
+ * @param c2 the component 2.
+ */
+ public void putConstraint(String e1, Component c1, Spring s, String e2,
+ Component c2)
+ {
+ Constraints constraints1 = getConstraints(c1);
+
+ Spring otherEdge = getConstraint(e2, c2);
+ constraints1.setConstraint(e1, Spring.sum(s, otherEdge));
+
+ }
+
+ /**
+ * Removes a layout component.
+ * @param c the layout component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // do nothing here
+ }
+}
diff --git a/libjava/classpath/javax/swing/SwingConstants.java b/libjava/classpath/javax/swing/SwingConstants.java
new file mode 100644
index 000000000..fccf2ea95
--- /dev/null
+++ b/libjava/classpath/javax/swing/SwingConstants.java
@@ -0,0 +1,76 @@
+/* SwingConstants.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Defines constant values that are used throughout the Swing packages.
+ */
+public interface SwingConstants
+{
+ int CENTER = 0;
+ int TOP = 1;
+ int LEFT = 2;
+ int BOTTOM = 3;
+ int RIGHT = 4;
+
+ int NORTH = 1;
+ int NORTH_EAST = 2;
+ int EAST = 3;
+ int SOUTH_EAST = 4;
+ int SOUTH = 5;
+ int SOUTH_WEST = 6;
+ int WEST = 7;
+ int NORTH_WEST = 8;
+
+ int HORIZONTAL = 0;
+ int VERTICAL = 1;
+
+ int LEADING = 10;
+ int TRAILING = 11;
+
+ /**
+ * @since 1.4
+ */
+ int NEXT = 12;
+
+ /**
+ * @since 1.4
+ */
+ int PREVIOUS = 13;
+}
diff --git a/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java
new file mode 100644
index 000000000..61047323f
--- /dev/null
+++ b/libjava/classpath/javax/swing/SwingUtilities.java
@@ -0,0 +1,1754 @@
+/* SwingUtilities.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.applet.Applet;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.FontMetrics;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.KeyboardFocusManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+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.accessibility.Accessible;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.InputMapUIResource;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.View;
+
+/**
+ * A number of static utility functions which are
+ * useful when drawing swing components, dispatching events, or calculating
+ * regions which need painting.
+ *
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+public class SwingUtilities
+ implements SwingConstants
+{
+ /**
+ * This frame should be used as parent for JWindow or JDialog
+ * that doesn't an owner
+ */
+ private static OwnerFrame ownerFrame;
+
+ private SwingUtilities()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Calculates the portion of the component's bounds which is inside the
+ * component's border insets. This area is usually the area a component
+ * should confine its painting to. The coordinates are returned in terms
+ * of the <em>component's</em> coordinate system, where (0,0) is the
+ * upper left corner of the component's bounds.
+ *
+ * @param c the component to measure the bounds of (if <code>null</code>,
+ * this method returns <code>null</code>).
+ * @param r a carrier to store the return value in (if <code>null</code>, a
+ * new <code>Rectangle</code> instance is created).
+ *
+ * @return The calculated area inside the component and its border insets.
+ */
+ public static Rectangle calculateInnerArea(JComponent c, Rectangle r)
+ {
+ if (c == null)
+ return null;
+ r = c.getBounds(r);
+ Insets i = c.getInsets();
+ r.x = i.left;
+ r.width = r.width - i.left - i.right;
+ r.y = i.top;
+ r.height = r.height - i.top - i.bottom;
+ return r;
+ }
+
+ /**
+ * Returns the focus owner or <code>null</code> if <code>comp</code> is not
+ * the focus owner or a parent of it.
+ *
+ * @param comp the focus owner or a parent of it
+ *
+ * @return the focus owner, or <code>null</code>
+ *
+ * @deprecated 1.4 Replaced by
+ * <code>KeyboardFocusManager.getFocusOwner()</code>.
+ */
+ public static Component findFocusOwner(Component comp)
+ {
+ // Get real focus owner.
+ Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager()
+ .getFocusOwner();
+
+ // Check if comp is the focus owner or a parent of it.
+ Component tmp = focusOwner;
+
+ while (tmp != null)
+ {
+ if (tmp == comp)
+ return focusOwner;
+
+ tmp = tmp.getParent();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the <code>Accessible</code> child of the specified component
+ * which appears at the supplied <code>Point</code>. If there is no
+ * child located at that particular pair of co-ordinates, null is returned
+ * instead.
+ *
+ * @param c the component whose children may be found at the specified
+ * point.
+ * @param p the point at which to look for the existence of children
+ * of the specified component.
+ * @return the <code>Accessible</code> child at the point, <code>p</code>,
+ * or null if there is no child at this point.
+ * @see javax.accessibility.AccessibleComponent#getAccessibleAt
+ */
+ public static Accessible getAccessibleAt(Component c, Point p)
+ {
+ return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p);
+ }
+
+ /**
+ * <p>
+ * Returns the <code>Accessible</code> child of the specified component
+ * that has the supplied index within the parent component. The indexing
+ * of the children is zero-based, making the first child have an index of
+ * 0.
+ * </p>
+ * <p>
+ * Caution is advised when using this method, as its operation relies
+ * on the behaviour of varying implementations of an abstract method.
+ * For greater surety, direct use of the AWT component implementation
+ * of this method is advised.
+ * </p>
+ *
+ * @param c the component whose child should be returned.
+ * @param i the index of the child within the parent component.
+ * @return the <code>Accessible</code> child at index <code>i</code>
+ * in the component, <code>c</code>.
+ * @see javax.accessibility.AccessibleContext#getAccessibleChild
+ * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChild
+ */
+ public static Accessible getAccessibleChild(Component c, int i)
+ {
+ return c.getAccessibleContext().getAccessibleChild(i);
+ }
+
+ /**
+ * <p>
+ * Returns the number of <code>Accessible</code> children within
+ * the supplied component.
+ * </p>
+ * <p>
+ * Caution is advised when using this method, as its operation relies
+ * on the behaviour of varying implementations of an abstract method.
+ * For greater surety, direct use of the AWT component implementation
+ * of this method is advised.
+ * </p>
+ *
+ * @param c the component whose children should be counted.
+ * @return the number of children belonging to the component,
+ * <code>c</code>.
+ * @see javax.accessibility.AccessibleContext#getAccessibleChildrenCount
+ * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChildrenCount
+ */
+ public static int getAccessibleChildrenCount(Component c)
+ {
+ return c.getAccessibleContext().getAccessibleChildrenCount();
+ }
+
+ /**
+ * <p>
+ * Returns the zero-based index of the specified component
+ * within its parent. If the component doesn't have a parent,
+ * -1 is returned.
+ * </p>
+ * <p>
+ * Caution is advised when using this method, as its operation relies
+ * on the behaviour of varying implementations of an abstract method.
+ * For greater surety, direct use of the AWT component implementation
+ * of this method is advised.
+ * </p>
+ *
+ * @param c the component whose parental index should be found.
+ * @return the index of the component within its parent, or -1
+ * if the component doesn't have a parent.
+ * @see javax.accessibility.AccessibleContext#getAccessibleIndexInParent
+ * @see java.awt.Component.AccessibleAWTComponent#getAccessibleIndexInParent
+ */
+ public static int getAccessibleIndexInParent(Component c)
+ {
+ return c.getAccessibleContext().getAccessibleIndexInParent();
+ }
+
+ /**
+ * <p>
+ * Returns a set of <code>AccessibleState</code>s, which represent
+ * the state of the supplied component.
+ * </p>
+ * <p>
+ * Caution is advised when using this method, as its operation relies
+ * on the behaviour of varying implementations of an abstract method.
+ * For greater surety, direct use of the AWT component implementation
+ * of this method is advised.
+ * </p>
+ *
+ * @param c the component whose accessible state should be retrieved.
+ * @return a set of <code>AccessibleState</code> objects, which represent
+ * the state of the supplied component.
+ * @see javax.accessibility.AccessibleContext#getAccessibleStateSet
+ * @see java.awt.Component.AccessibleAWTComponent#getAccessibleStateSet
+ */
+ public static AccessibleStateSet getAccessibleStateSet(Component c)
+ {
+ return c.getAccessibleContext().getAccessibleStateSet();
+ }
+
+ /**
+ * Calculates the bounds of a component in the component's own coordinate
+ * space. The result has the same height and width as the component's
+ * bounds, but its location is set to (0,0).
+ *
+ * @param aComponent The component to measure
+ *
+ * @return The component's bounds in its local coordinate space
+ */
+ public static Rectangle getLocalBounds(Component aComponent)
+ {
+ Rectangle bounds = aComponent.getBounds();
+ return new Rectangle(0, 0, bounds.width, bounds.height);
+ }
+
+ /**
+ * If <code>comp</code> is a RootPaneContainer, return its JRootPane.
+ * Otherwise call <code>getAncestorOfClass(JRootPane.class, a)</code>.
+ *
+ * @param comp The component to get the JRootPane of
+ *
+ * @return a suitable JRootPane for <code>comp</code>, or <code>null</code>
+ *
+ * @see javax.swing.RootPaneContainer#getRootPane
+ * @see #getAncestorOfClass
+ */
+ public static JRootPane getRootPane(Component comp)
+ {
+ if (comp instanceof RootPaneContainer)
+ return ((RootPaneContainer)comp).getRootPane();
+ else
+ return (JRootPane) getAncestorOfClass(JRootPane.class, comp);
+ }
+
+ /**
+ * Returns the least ancestor of <code>comp</code> which has the
+ * specified name.
+ *
+ * @param name The name to search for
+ * @param comp The component to search the ancestors of
+ *
+ * @return The nearest ancestor of <code>comp</code> with the given
+ * name, or <code>null</code> if no such ancestor exists
+ *
+ * @see java.awt.Component#getName
+ * @see #getAncestorOfClass
+ */
+ public static Container getAncestorNamed(String name, Component comp)
+ {
+ while (comp != null && (comp.getName() != name))
+ comp = comp.getParent();
+ return (Container) comp;
+ }
+
+ /**
+ * Returns the least ancestor of <code>comp</code> which is an instance
+ * of the specified class.
+ *
+ * @param c The class to search for
+ * @param comp The component to search the ancestors of
+ *
+ * @return The nearest ancestor of <code>comp</code> which is an instance
+ * of the given class, or <code>null</code> if no such ancestor exists
+ *
+ * @see #getAncestorOfClass
+ * @see #windowForComponent
+ */
+ public static Container getAncestorOfClass(Class<?> c, Component comp)
+ {
+ while (comp != null && (! c.isInstance(comp)))
+ comp = comp.getParent();
+ return (Container) comp;
+ }
+
+ /**
+ * Returns the first ancestor of <code>comp</code> that is a {@link Window}
+ * or <code>null</code> if <code>comp</code> is not contained in a
+ * {@link Window}.
+ *
+ * This is equivalent to calling
+ * <code>getAncestorOfClass(Window, comp)</code> or
+ * <code>windowForComponent(comp)</code>.
+ *
+ * @param comp the component for which we are searching the ancestor Window
+ *
+ * @return the first ancestor Window of <code>comp</code> or
+ * <code>null</code> if <code>comp</code> is not contained in a Window
+ */
+ public static Window getWindowAncestor(Component comp)
+ {
+ return (Window) getAncestorOfClass(Window.class, comp);
+ }
+
+ /**
+ * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>.
+ *
+ * @param comp The component to search for an ancestor window
+ *
+ * @return An ancestral window, or <code>null</code> if none exists
+ */
+ public static Window windowForComponent(Component comp)
+ {
+ return (Window) getAncestorOfClass(Window.class, comp);
+ }
+
+ /**
+ * Returns the "root" of the component tree containint <code>comp</code>
+ * The root is defined as either the <em>least</em> ancestor of
+ * <code>comp</code> which is a {@link Window}, or the <em>greatest</em>
+ * ancestor of <code>comp</code> which is a {@link Applet} if no {@link
+ * Window} ancestors are found.
+ *
+ * @param comp The component to search for a root
+ *
+ * @return The root of the component's tree, or <code>null</code>
+ */
+ public static Component getRoot(Component comp)
+ {
+ Applet app = null;
+ Window win = null;
+
+ while (comp != null)
+ {
+ if (win == null && comp instanceof Window)
+ win = (Window) comp;
+ else if (comp instanceof Applet)
+ app = (Applet) comp;
+ comp = comp.getParent();
+ }
+
+ if (win != null)
+ return win;
+ return app;
+ }
+
+ /**
+ * Return true if a descends from b, in other words if b is an ancestor of a.
+ *
+ * @param a The child to search the ancestry of
+ * @param b The potential ancestor to search for
+ * @return true if a is a descendent of b, false otherwise
+ */
+ public static boolean isDescendingFrom(Component a, Component b)
+ {
+ while (true)
+ {
+ if (a == null || b == null)
+ return false;
+ if (a == b)
+ return true;
+ a = a.getParent();
+ }
+ }
+
+ /**
+ * Returns the deepest descendent of parent which is both visible and
+ * contains the point <code>(x,y)</code>. Returns parent when either
+ * parent is not a container, or has no children which contain
+ * <code>(x,y)</code>. Returns <code>null</code> when either
+ * <code>(x,y)</code> is outside the bounds of parent, or parent is
+ * <code>null</code>.
+ *
+ * @param parent The component to search the descendents of
+ * @param x Horizontal coordinate to search for
+ * @param y Vertical coordinate to search for
+ *
+ * @return A component containing <code>(x,y)</code>, or
+ * <code>null</code>
+ *
+ * @see java.awt.Container#findComponentAt(int, int)
+ */
+ public static Component getDeepestComponentAt(Component parent, int x, int y)
+ {
+ if (parent == null || (! parent.contains(x, y)))
+ return null;
+
+ if (! (parent instanceof Container))
+ return parent;
+
+ Container c = (Container) parent;
+ return c.findComponentAt(x, y);
+ }
+
+ /**
+ * Converts a point from a component's local coordinate space to "screen"
+ * coordinates (such as the coordinate space mouse events are delivered
+ * in). This operation is equivalent to translating the point by the
+ * location of the component (which is the origin of its coordinate
+ * space).
+ *
+ * @param p The point to convert
+ * @param c The component which the point is expressed in terms of
+ *
+ * @see #convertPointFromScreen
+ */
+ public static void convertPointToScreen(Point p, Component c)
+ {
+ Point c0 = c.getLocationOnScreen();
+ p.translate(c0.x, c0.y);
+ }
+
+ /**
+ * Converts a point from "screen" coordinates (such as the coordinate
+ * space mouse events are delivered in) to a component's local coordinate
+ * space. This operation is equivalent to translating the point by the
+ * negation of the component's location (which is the origin of its
+ * coordinate space).
+ *
+ * @param p The point to convert
+ * @param c The component which the point should be expressed in terms of
+ */
+ public static void convertPointFromScreen(Point p, Component c)
+ {
+ Point c0 = c.getLocationOnScreen();
+ p.translate(-c0.x, -c0.y);
+ }
+
+ /**
+ * Converts a point <code>(x,y)</code> from the coordinate space of one
+ * component to another. This is equivalent to converting the point from
+ * <code>source</code> space to screen space, then back from screen space
+ * to <code>destination</code> space. If exactly one of the two
+ * Components is <code>null</code>, it is taken to refer to the root
+ * ancestor of the other component. If both are <code>null</code>, no
+ * transformation is done.
+ *
+ * @param source The component which the point is expressed in terms of
+ * @param x Horizontal coordinate of point to transform
+ * @param y Vertical coordinate of point to transform
+ * @param destination The component which the return value will be
+ * expressed in terms of
+ *
+ * @return The point <code>(x,y)</code> converted from the coordinate space of the
+ * source component to the coordinate space of the destination component
+ *
+ * @see #convertPointToScreen
+ * @see #convertPointFromScreen
+ * @see #convertRectangle
+ * @see #getRoot
+ */
+ public static Point convertPoint(Component source, int x, int y,
+ Component destination)
+ {
+ Point pt = new Point(x, y);
+
+ if (source == null && destination == null)
+ return pt;
+
+ if (source == null)
+ source = getRoot(destination);
+
+ if (destination == null)
+ destination = getRoot(source);
+
+ if (source.isShowing() && destination.isShowing())
+ {
+ convertPointToScreen(pt, source);
+ convertPointFromScreen(pt, destination);
+ }
+
+ return pt;
+ }
+
+ public static Point convertPoint(Component source, Point aPoint, Component destination)
+ {
+ return convertPoint(source, aPoint.x, aPoint.y, destination);
+ }
+
+ /**
+ * Converts a rectangle from the coordinate space of one component to
+ * another. This is equivalent to converting the rectangle from
+ * <code>source</code> space to screen space, then back from screen space
+ * to <code>destination</code> space. If exactly one of the two
+ * Components is <code>null</code>, it is taken to refer to the root
+ * ancestor of the other component. If both are <code>null</code>, no
+ * transformation is done.
+ *
+ * @param source The component which the rectangle is expressed in terms of
+ * @param rect The rectangle to convert
+ * @param destination The component which the return value will be
+ * expressed in terms of
+ *
+ * @return A new rectangle, equal in size to the input rectangle, but
+ * with its position converted from the coordinate space of the source
+ * component to the coordinate space of the destination component
+ *
+ * @see #convertPointToScreen
+ * @see #convertPointFromScreen
+ * @see #convertPoint(Component, int, int, Component)
+ * @see #getRoot
+ */
+ public static Rectangle convertRectangle(Component source,
+ Rectangle rect,
+ Component destination)
+ {
+ Point pt = convertPoint(source, rect.x, rect.y, destination);
+ return new Rectangle(pt.x, pt.y, rect.width, rect.height);
+ }
+
+ /**
+ * Convert a mouse event which refrers to one component to another. This
+ * includes changing the mouse event's coordinate space, as well as the
+ * source property of the event. If <code>source</code> is
+ * <code>null</code>, it is taken to refer to <code>destination</code>'s
+ * root component. If <code>destination</code> is <code>null</code>, the
+ * new event will remain expressed in <code>source</code>'s coordinate
+ * system.
+ *
+ * @param source The component the mouse event currently refers to
+ * @param sourceEvent The mouse event to convert
+ * @param destination The component the new mouse event should refer to
+ *
+ * @return A new mouse event expressed in terms of the destination
+ * component's coordinate space, and with the destination component as
+ * its source
+ *
+ * @see #convertPoint(Component, int, int, Component)
+ */
+ public static MouseEvent convertMouseEvent(Component source,
+ MouseEvent sourceEvent,
+ Component destination)
+ {
+ Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
+ destination);
+
+ return new MouseEvent(destination, sourceEvent.getID(),
+ sourceEvent.getWhen(), sourceEvent.getModifiersEx(),
+ newpt.x, newpt.y, sourceEvent.getClickCount(),
+ sourceEvent.isPopupTrigger(), sourceEvent.getButton());
+ }
+
+ /**
+ * Recursively walk the component tree under <code>comp</code> calling
+ * <code>updateUI</code> on each {@link JComponent} found. This causes
+ * the entire tree to re-initialize its UI delegates.
+ *
+ * @param comp The component to walk the children of, calling <code>updateUI</code>
+ */
+ public static void updateComponentTreeUI(Component comp)
+ {
+ updateComponentTreeUIImpl(comp);
+ if (comp instanceof JComponent)
+ {
+ JComponent jc = (JComponent) comp;
+ jc.revalidate();
+ }
+ else
+ {
+ comp.invalidate();
+ comp.validate();
+ }
+ comp.repaint();
+ }
+
+ /**
+ * Performs the actual work for {@link #updateComponentTreeUI(Component)}.
+ * This calls updateUI() on c if it is a JComponent, and then walks down
+ * the component tree and calls this method on each child component.
+ *
+ * @param c the component to update the UI
+ */
+ private static void updateComponentTreeUIImpl(Component c)
+ {
+ if (c instanceof JComponent)
+ {
+ JComponent jc = (JComponent) c;
+ jc.updateUI();
+ }
+
+ Component[] components = null;
+ if (c instanceof JMenu)
+ components = ((JMenu) c).getMenuComponents();
+ else if (c instanceof Container)
+ components = ((Container) c).getComponents();
+ if (components != null)
+ {
+ for (int i = 0; i < components.length; ++i)
+ updateComponentTreeUIImpl(components[i]);
+ }
+ }
+
+ /**
+ * <p>Layout a "compound label" consisting of a text string and an icon
+ * which is to be placed near the rendered text. Once the text and icon
+ * are laid out, the text rectangle and icon rectangle parameters are
+ * altered to store the calculated positions.</p>
+ *
+ * <p>The size of the text is calculated from the provided font metrics
+ * object. This object should be the metrics of the font you intend to
+ * paint the label with.</p>
+ *
+ * <p>The position values control where the text is placed relative to
+ * the icon. The horizontal position value should be one of the constants
+ * <code>LEADING</code>, <code>TRAILING</code>, <code>LEFT</code>,
+ * <code>RIGHT</code> or <code>CENTER</code>. The vertical position value
+ * should be one fo the constants <code>TOP</code>, <code>BOTTOM</code>
+ * or <code>CENTER</code>.</p>
+ *
+ * <p>The text-icon gap value controls the number of pixels between the
+ * icon and the text.</p>
+ *
+ * <p>The alignment values control where the text and icon are placed, as
+ * a combined unit, within the view rectangle. The horizontal alignment
+ * value should be one of the constants <code>LEADING</code>,
+ * <code>TRAILING</code>, <code>LEFT</code>, <code>RIGHT</code> or
+ * <code>CENTER</code>. The vertical alignment valus should be one of the
+ * constants <code>TOP</code>, <code>BOTTOM</code> or
+ * <code>CENTER</code>.</p>
+ *
+ * <p>If the <code>LEADING</code> or <code>TRAILING</code> constants are
+ * given for horizontal alignment or horizontal text position, they are
+ * interpreted relative to the provided component's orientation property,
+ * a constant in the {@link java.awt.ComponentOrientation} class. For
+ * example, if the component's orientation is <code>LEFT_TO_RIGHT</code>,
+ * then the <code>LEADING</code> value is a synonym for <code>LEFT</code>
+ * and the <code>TRAILING</code> value is a synonym for
+ * <code>RIGHT</code></p>
+ *
+ * <p>If the text and icon are equal to or larger than the view
+ * rectangle, the horizontal and vertical alignment values have no
+ * affect.</p>
+ *
+ * @param c A component used for its orientation value
+ * @param fm The font metrics used to measure the text
+ * @param text The text to place in the compound label
+ * @param icon The icon to place next to the text
+ * @param verticalAlignment The vertical alignment of the label relative
+ * to its component
+ * @param horizontalAlignment The horizontal alignment of the label
+ * relative to its component
+ * @param verticalTextPosition The vertical position of the label's text
+ * relative to its icon
+ * @param horizontalTextPosition The horizontal position of the label's
+ * text relative to its icon
+ * @param viewR The view rectangle, specifying the area which layout is
+ * constrained to
+ * @param iconR A rectangle which is modified to hold the laid-out
+ * position of the icon
+ * @param textR A rectangle which is modified to hold the laid-out
+ * position of the text
+ * @param textIconGap The distance between text and icon
+ *
+ * @return The string of characters, possibly truncated with an elipsis,
+ * which is laid out in this label
+ */
+
+ public static String layoutCompoundLabel(JComponent c,
+ FontMetrics fm,
+ String text,
+ Icon icon,
+ int verticalAlignment,
+ int horizontalAlignment,
+ int verticalTextPosition,
+ int horizontalTextPosition,
+ Rectangle viewR,
+ Rectangle iconR,
+ Rectangle textR,
+ int textIconGap)
+ {
+
+ // Fix up the orientation-based horizontal positions.
+
+ boolean ltr = true;
+ if (c != null && ! c.getComponentOrientation().isLeftToRight())
+ ltr = false;
+
+ switch (horizontalTextPosition)
+ {
+ case LEADING:
+ horizontalTextPosition = ltr ? LEFT : RIGHT;
+ break;
+ case TRAILING:
+ horizontalTextPosition = ltr ? RIGHT : LEFT;
+ break;
+ default:
+ // Nothing to do in the other cases.
+ }
+
+ // Fix up the orientation-based alignments.
+ switch (horizontalAlignment)
+ {
+ case LEADING:
+ horizontalAlignment = ltr ? LEFT : RIGHT;
+ break;
+ case TRAILING:
+ horizontalAlignment = ltr ? RIGHT : LEFT;
+ break;
+ default:
+ // Nothing to do in the other cases.
+ }
+
+ return layoutCompoundLabelImpl(c, fm, text, icon,
+ verticalAlignment,
+ horizontalAlignment,
+ verticalTextPosition,
+ horizontalTextPosition,
+ viewR, iconR, textR, textIconGap);
+ }
+
+ /**
+ * <p>Layout a "compound label" consisting of a text string and an icon
+ * which is to be placed near the rendered text. Once the text and icon
+ * are laid out, the text rectangle and icon rectangle parameters are
+ * altered to store the calculated positions.</p>
+ *
+ * <p>The size of the text is calculated from the provided font metrics
+ * object. This object should be the metrics of the font you intend to
+ * paint the label with.</p>
+ *
+ * <p>The position values control where the text is placed relative to
+ * the icon. The horizontal position value should be one of the constants
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
+ * vertical position value should be one fo the constants
+ * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p>
+ *
+ * <p>The text-icon gap value controls the number of pixels between the
+ * icon and the text.</p>
+ *
+ * <p>The alignment values control where the text and icon are placed, as
+ * a combined unit, within the view rectangle. The horizontal alignment
+ * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or
+ * <code>CENTER</code>. The vertical alignment valus should be one of the
+ * constants <code>TOP</code>, <code>BOTTOM</code> or
+ * <code>CENTER</code>.</p>
+ *
+ * <p>If the text and icon are equal to or larger than the view
+ * rectangle, the horizontal and vertical alignment values have no
+ * affect.</p>
+ *
+ * <p>Note that this method does <em>not</em> know how to deal with
+ * horizontal alignments or positions given as <code>LEADING</code> or
+ * <code>TRAILING</code> values. Use the other overloaded variant of this
+ * method if you wish to use such values.
+ *
+ * @param fm The font metrics used to measure the text
+ * @param text The text to place in the compound label
+ * @param icon The icon to place next to the text
+ * @param verticalAlignment The vertical alignment of the label relative
+ * to its component
+ * @param horizontalAlignment The horizontal alignment of the label
+ * relative to its component
+ * @param verticalTextPosition The vertical position of the label's text
+ * relative to its icon
+ * @param horizontalTextPosition The horizontal position of the label's
+ * text relative to its icon
+ * @param viewR The view rectangle, specifying the area which layout is
+ * constrained to
+ * @param iconR A rectangle which is modified to hold the laid-out
+ * position of the icon
+ * @param textR A rectangle which is modified to hold the laid-out
+ * position of the text
+ * @param textIconGap The distance between text and icon
+ *
+ * @return The string of characters, possibly truncated with an elipsis,
+ * which is laid out in this label
+ */
+
+ public static String layoutCompoundLabel(FontMetrics fm,
+ String text,
+ Icon icon,
+ int verticalAlignment,
+ int horizontalAlignment,
+ int verticalTextPosition,
+ int horizontalTextPosition,
+ Rectangle viewR,
+ Rectangle iconR,
+ Rectangle textR,
+ int textIconGap)
+ {
+ return layoutCompoundLabelImpl(null, fm, text, icon, verticalAlignment,
+ horizontalAlignment, verticalTextPosition,
+ horizontalTextPosition, viewR, iconR, textR,
+ textIconGap);
+ }
+
+ /**
+ * <p>Layout a "compound label" consisting of a text string and an icon
+ * which is to be placed near the rendered text. Once the text and icon
+ * are laid out, the text rectangle and icon rectangle parameters are
+ * altered to store the calculated positions.</p>
+ *
+ * <p>The size of the text is calculated from the provided font metrics
+ * object. This object should be the metrics of the font you intend to
+ * paint the label with.</p>
+ *
+ * <p>The position values control where the text is placed relative to
+ * the icon. The horizontal position value should be one of the constants
+ * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>. The
+ * vertical position value should be one fo the constants
+ * <code>TOP</code>, <code>BOTTOM</code> or <code>CENTER</code>.</p>
+ *
+ * <p>The text-icon gap value controls the number of pixels between the
+ * icon and the text.</p>
+ *
+ * <p>The alignment values control where the text and icon are placed, as
+ * a combined unit, within the view rectangle. The horizontal alignment
+ * value should be one of the constants <code>LEFT</code>, <code>RIGHT</code> or
+ * <code>CENTER</code>. The vertical alignment valus should be one of the
+ * constants <code>TOP</code>, <code>BOTTOM</code> or
+ * <code>CENTER</code>.</p>
+ *
+ * <p>If the text and icon are equal to or larger than the view
+ * rectangle, the horizontal and vertical alignment values have no
+ * affect.</p>
+ *
+ * <p>Note that this method does <em>not</em> know how to deal with
+ * horizontal alignments or positions given as <code>LEADING</code> or
+ * <code>TRAILING</code> values. Use the other overloaded variant of this
+ * method if you wish to use such values.
+ *
+ * @param fm The font metrics used to measure the text
+ * @param text The text to place in the compound label
+ * @param icon The icon to place next to the text
+ * @param verticalAlignment The vertical alignment of the label relative
+ * to its component
+ * @param horizontalAlignment The horizontal alignment of the label
+ * relative to its component
+ * @param verticalTextPosition The vertical position of the label's text
+ * relative to its icon
+ * @param horizontalTextPosition The horizontal position of the label's
+ * text relative to its icon
+ * @param viewR The view rectangle, specifying the area which layout is
+ * constrained to
+ * @param iconR A rectangle which is modified to hold the laid-out
+ * position of the icon
+ * @param textR A rectangle which is modified to hold the laid-out
+ * position of the text
+ * @param textIconGap The distance between text and icon
+ *
+ * @return The string of characters, possibly truncated with an elipsis,
+ * which is laid out in this label
+ */
+ private static String layoutCompoundLabelImpl(JComponent c,
+ FontMetrics fm,
+ String text,
+ Icon icon,
+ int verticalAlignment,
+ int horizontalAlignment,
+ int verticalTextPosition,
+ int horizontalTextPosition,
+ Rectangle viewR,
+ Rectangle iconR,
+ Rectangle textR,
+ int textIconGap)
+ {
+
+ // Work out basic height and width.
+
+ if (icon == null)
+ {
+ textIconGap = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+ }
+ else
+ {
+ iconR.width = icon.getIconWidth();
+ iconR.height = icon.getIconHeight();
+ }
+
+ if (text == null || text.equals(""))
+ {
+ textIconGap = 0;
+ textR.width = 0;
+ textR.height = 0;
+ text = "";
+ }
+ else
+ {
+ int availableWidth = viewR.width;
+ if (horizontalTextPosition != CENTER)
+ availableWidth -= iconR.width + textIconGap;
+ View html = c == null ? null
+ : (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ textR.width = (int) html.getPreferredSpan(View.X_AXIS);
+ textR.width = Math.min(availableWidth, textR.width);
+ textR.height = (int) html.getPreferredSpan(View.Y_AXIS);
+ }
+ else
+ {
+ int fromIndex = 0;
+ textR.width = fm.stringWidth(text);
+ textR.height = fm.getHeight();
+ if (textR.width > availableWidth)
+ {
+ text = clipString(c, fm, text, availableWidth);
+ textR.width = fm.stringWidth(text);
+ }
+ }
+ }
+
+ // Work out the position of text, assuming the top-left coord
+ // starts at (0,0). We will fix that up momentarily, after these
+ // "position" decisions are made and we look at alignment.
+
+ switch (verticalTextPosition)
+ {
+ case TOP:
+ textR.y = horizontalTextPosition == CENTER ?
+ - textR.height - textIconGap : 0;
+ break;
+ case BOTTOM:
+ textR.y = horizontalTextPosition == CENTER ?
+ iconR.height + textIconGap : iconR.height - textR.height;
+ break;
+ case CENTER:
+ textR.y = iconR.height / 2 - textR.height / 2;
+ break;
+ }
+
+ switch (horizontalTextPosition)
+ {
+ case LEFT:
+ textR.x = -(textR.width + textIconGap);
+ break;
+ case RIGHT:
+ textR.x = iconR.width + textIconGap;
+ break;
+ case CENTER:
+ textR.x = iconR.width / 2 - textR.width / 2;
+ break;
+ }
+
+ // The two rectangles are laid out correctly now, but only assuming
+ // that their upper left corner is at (0,0). If we have any alignment other
+ // than TOP and LEFT, we need to adjust them.
+
+ // These coordinates specify the rectangle that contains both the
+ // icon and text. Move it so that it fullfills the alignment properties.
+ int lx = Math.min(iconR.x, textR.x);
+ int lw = Math.max(iconR.x + iconR.width, textR.x + textR.width) - lx;
+ int ly = Math.min(iconR.y, textR.y);
+ int lh = Math.max(iconR.y + iconR.height, textR.y + textR.height) - ly;
+ int horizontalAdjustment = 0;
+ int verticalAdjustment = 0;
+ switch (verticalAlignment)
+ {
+ case TOP:
+ verticalAdjustment = viewR.y - ly;
+ break;
+ case BOTTOM:
+ verticalAdjustment = viewR.y + viewR.height - ly - lh;
+ break;
+ case CENTER:
+ verticalAdjustment = viewR.y + viewR.height / 2 - ly - lh / 2;
+ break;
+ }
+ switch (horizontalAlignment)
+ {
+ case LEFT:
+ horizontalAdjustment = viewR.x - lx;
+ break;
+ case RIGHT:
+ horizontalAdjustment = viewR.x + viewR.width - lx - lw;
+ break;
+ case CENTER:
+ horizontalAdjustment = (viewR.x + (viewR.width / 2)) - (lx + (lw / 2));
+ break;
+ }
+ iconR.x += horizontalAdjustment;
+ iconR.y += verticalAdjustment;
+
+ textR.x += horizontalAdjustment;
+ textR.y += verticalAdjustment;
+
+ return text;
+ }
+
+ /**
+ * The method clips the specified string so that it fits into the
+ * available width. It is only called when the text really doesn't fit,
+ * so we don't need to check that again.
+ *
+ * @param c the component
+ * @param fm the font metrics
+ * @param text the text
+ * @param availableWidth the available width
+ *
+ * @return the clipped string
+ */
+ private static String clipString(JComponent c, FontMetrics fm, String text,
+ int availableWidth)
+ {
+ String dots = "...";
+ int dotsWidth = fm.stringWidth(dots);
+ char[] string = text.toCharArray();
+ int endIndex = string.length;
+ while (fm.charsWidth(string, 0, endIndex) + dotsWidth > availableWidth
+ && endIndex > 0)
+ endIndex--;
+ String clipped;
+ if (string.length >= endIndex + 3)
+ {
+ string[endIndex] = '.';
+ string[endIndex + 1] = '.';
+ string[endIndex + 2] = '.';
+ clipped = new String(string, 0, endIndex + 3);
+ }
+ else
+ {
+ char[] clippedChars = new char[string.length + 3];
+ System.arraycopy(string, 0, clippedChars, 0, string.length);
+ clippedChars[endIndex] = '.';
+ clippedChars[endIndex + 1] = '.';
+ clippedChars[endIndex + 2] = '.';
+ clipped = new String(clippedChars, 0, endIndex + 3);
+ }
+ return clipped;
+ }
+
+ /**
+ * Calls {@link java.awt.EventQueue#invokeLater} with the
+ * specified {@link Runnable}.
+ */
+ public static void invokeLater(Runnable doRun)
+ {
+ java.awt.EventQueue.invokeLater(doRun);
+ }
+
+ /**
+ * Calls {@link java.awt.EventQueue#invokeAndWait} with the
+ * specified {@link Runnable}.
+ */
+ public static void invokeAndWait(Runnable doRun)
+ throws InterruptedException,
+ InvocationTargetException
+ {
+ java.awt.EventQueue.invokeAndWait(doRun);
+ }
+
+ /**
+ * Calls {@link java.awt.EventQueue#isDispatchThread()}.
+ *
+ * @return <code>true</code> if the current thread is the current AWT event
+ * dispatch thread.
+ */
+ public static boolean isEventDispatchThread()
+ {
+ return java.awt.EventQueue.isDispatchThread();
+ }
+
+ /**
+ * This method paints the given component at the given position and size.
+ * The component will be reparented to the container given.
+ *
+ * @param g The Graphics object to draw with.
+ * @param c The Component to draw
+ * @param p The Container to reparent to.
+ * @param x The x coordinate to draw at.
+ * @param y The y coordinate to draw at.
+ * @param w The width of the drawing area.
+ * @param h The height of the drawing area.
+ */
+ public static void paintComponent(Graphics g, Component c, Container p,
+ int x, int y, int w, int h)
+ {
+ Container parent = c.getParent();
+ if (parent != null)
+ parent.remove(c);
+ if (p != null)
+ p.add(c);
+
+ Shape savedClip = g.getClip();
+
+ g.setClip(x, y, w, h);
+ g.translate(x, y);
+
+ c.paint(g);
+
+ g.translate(-x, -y);
+ g.setClip(savedClip);
+ }
+
+ /**
+ * This method paints the given component in the given rectangle.
+ * The component will be reparented to the container given.
+ *
+ * @param g The Graphics object to draw with.
+ * @param c The Component to draw
+ * @param p The Container to reparent to.
+ * @param r The rectangle that describes the drawing area.
+ */
+ public static void paintComponent(Graphics g, Component c,
+ Container p, Rectangle r)
+ {
+ paintComponent(g, c, p, r.x, r.y, r.width, r.height);
+ }
+
+ /**
+ * This method returns the common Frame owner used in JDialogs or
+ * JWindow when no owner is provided.
+ *
+ * @return The common Frame
+ */
+ static Window getOwnerFrame(Window owner)
+ {
+ Window result = owner;
+ if (result == null)
+ {
+ if (ownerFrame == null)
+ ownerFrame = new OwnerFrame();
+ result = ownerFrame;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if left mouse button was clicked.
+ *
+ * @param event the event to check
+ *
+ * @return true if left mouse was clicked, false otherwise.
+ */
+ public static boolean isLeftMouseButton(MouseEvent event)
+ {
+ return ((event.getModifiers() & InputEvent.BUTTON1_MASK) != 0);
+ }
+
+ /**
+ * Checks if middle mouse button was clicked.
+ *
+ * @param event the event to check
+ *
+ * @return true if middle mouse was clicked, false otherwise.
+ */
+ public static boolean isMiddleMouseButton(MouseEvent event)
+ {
+ return ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK)
+ == InputEvent.BUTTON2_DOWN_MASK);
+ }
+
+ /**
+ * Checks if right mouse button was clicked.
+ *
+ * @param event the event to check
+ *
+ * @return true if right mouse was clicked, false otherwise.
+ */
+ public static boolean isRightMouseButton(MouseEvent event)
+ {
+ return ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK)
+ == InputEvent.BUTTON3_DOWN_MASK);
+ }
+
+ /**
+ * This frame should be used when constructing a Window/JDialog without
+ * a parent. In this case, we are forced to use this frame as a window's
+ * parent, because we simply cannot pass null instead of parent to Window
+ * constructor, since doing it will result in NullPointerException.
+ */
+ private static class OwnerFrame extends Frame
+ {
+ public void setVisible(boolean b)
+ {
+ // Do nothing here.
+ }
+
+ public boolean isShowing()
+ {
+ 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()}]
+ * --&gt; [{@link javax.swing.ActionMap}]
+ * parent --&gt; [{@link javax.swing.text.JTextComponent.KeymapActionMap}]
+ * parent --&gt; [{@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
+ {
+ ActionMap parent = child.getParent();
+ while (parent != null && !(parent instanceof ActionMapUIResource))
+ {
+ child = parent;
+ parent = child.getParent();
+ }
+ // Sanity check to avoid loops.
+ if (child != uiActionMap)
+ 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()}]
+ * --&gt; [{@link javax.swing.InputMap}]
+ * parent --&gt; [{@link javax.swing.text.JTextComponent.KeymapWrapper}]
+ * parent --&gt; [{@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
+ {
+ InputMap parent = child.getParent();
+ while (parent != null && !(parent instanceof InputMapUIResource))
+ {
+ child = parent;
+ parent = parent.getParent();
+ }
+ // Sanity check to avoid loops.
+ if (child != uiInputMap)
+ child.setParent(uiInputMap);
+ }
+ }
+
+ /**
+ * Subtracts a rectangle from another and return the area as an array
+ * of rectangles.
+ * Returns the areas of rectA which are not covered by rectB.
+ * If the rectangles do not overlap, or if either parameter is
+ * <code>null</code>, a zero-size array is returned.
+ * @param rectA The first rectangle
+ * @param rectB The rectangle to subtract from the first
+ * @return An array of rectangles representing the area in rectA
+ * not overlapped by rectB
+ */
+ public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB)
+ {
+ if (rectA == null || rectB == null)
+ return new Rectangle[0];
+
+ Rectangle[] r = new Rectangle[4];
+ int x1 = rectA.x;
+ int y1 = rectA.y;
+ int w1 = rectA.width;
+ int h1 = rectA.height;
+ int x2 = rectB.x;
+ int y2 = rectB.y;
+ int w2 = rectB.width;
+ int h2 = rectB.height;
+
+ // (outer box = rectA)
+ // -------------
+ // |_____0_____|
+ // | |rectB| |
+ // |_1|_____|_2|
+ // | 3 |
+ // -------------
+ int H0 = (y2 > y1) ? y2 - y1 : 0; // height of box 0
+ int H3 = (y2 + h2 < y1 + h1) ? y1 + h1 - y2 - h2 : 0; // height box 3
+ int W1 = (x2 > x1) ? x2 - x1 : 0; // width box 1
+ int W2 = (x1 + w1 > x2 + w2) ? x1 + w1 - x2 - w2 : 0; // w. box 2
+ int H12 = (H0 + H3 < h1) ? h1 - H0 - H3 : 0; // height box 1 & 2
+
+ if (H0 > 0)
+ r[0] = new Rectangle(x1, y1, w1, H0);
+ else
+ r[0] = null;
+
+ if (W1 > 0 && H12 > 0)
+ r[1] = new Rectangle(x1, y1 + H0, W1, H12);
+ else
+ r[1] = null;
+
+ if (W2 > 0 && H12 > 0)
+ r[2] = new Rectangle(x2 + w2, y1 + H0, W2, H12);
+ else
+ r[2] = null;
+
+ if (H3 > 0)
+ r[3] = new Rectangle(x1, y1 + H0 + H12, w1, H3);
+ else
+ r[3] = null;
+
+ // sort out null objects
+ int n = 0;
+ for (int i = 0; i < 4; i++)
+ if (r[i] != null)
+ n++;
+ Rectangle[] out = new Rectangle[n];
+ for (int i = 3; i >= 0; i--)
+ if (r[i] != null)
+ out[--n] = r[i];
+
+ return out;
+ }
+
+ /**
+ * Calculates the intersection of two rectangles. The result is stored
+ * in <code>rect</code>. This is basically the same
+ * like {@link Rectangle#intersection(Rectangle)}, only that it does not
+ * create new Rectangle instances. The tradeoff is that you loose any data in
+ * <code>rect</code>.
+ *
+ * @param x upper-left x coodinate of first rectangle
+ * @param y upper-left y coodinate of first rectangle
+ * @param w width of first rectangle
+ * @param h height of first rectangle
+ * @param rect a Rectangle object of the second rectangle
+ *
+ * @throws NullPointerException if rect is null
+ *
+ * @return a rectangle corresponding to the intersection of the
+ * two rectangles. An empty rectangle is returned if the rectangles
+ * do not overlap
+ */
+ public static Rectangle computeIntersection(int x, int y, int w, int h,
+ Rectangle rect)
+ {
+ int x2 = (int) rect.x;
+ int y2 = (int) rect.y;
+ int w2 = (int) rect.width;
+ int h2 = (int) rect.height;
+
+ int dx = (x > x2) ? x : x2;
+ int dy = (y > y2) ? y : y2;
+ int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
+ int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
+
+ if (dw >= 0 && dh >= 0)
+ rect.setBounds(dx, dy, dw, dh);
+ else
+ rect.setBounds(0, 0, 0, 0);
+
+ return rect;
+ }
+
+ /**
+ * Calculates the width of a given string.
+ *
+ * @param fm the <code>FontMetrics</code> object to use
+ * @param str the string
+ *
+ * @return the width of the the string.
+ */
+ public static int computeStringWidth(FontMetrics fm, String str)
+ {
+ return fm.stringWidth(str);
+ }
+
+ /**
+ * Calculates the union of two rectangles. The result is stored in
+ * <code>rect</code>. This is basically the same as
+ * {@link Rectangle#union(Rectangle)} except that it avoids creation of new
+ * Rectangle objects. The tradeoff is that you loose any data in
+ * <code>rect</code>.
+ *
+ * @param x upper-left x coodinate of first rectangle
+ * @param y upper-left y coodinate of first rectangle
+ * @param w width of first rectangle
+ * @param h height of first rectangle
+ * @param rect a Rectangle object of the second rectangle
+ *
+ * @throws NullPointerException if rect is null
+ *
+ * @return a rectangle corresponding to the union of the
+ * two rectangles; a rectangle encompassing both is returned if the
+ * rectangles do not overlap
+ */
+ public static Rectangle computeUnion(int x, int y, int w, int h,
+ Rectangle rect)
+ {
+ int x2 = (int) rect.x;
+ int y2 = (int) rect.y;
+ int w2 = (int) rect.width;
+ int h2 = (int) rect.height;
+
+ int dx = (x < x2) ? x : x2;
+ int dy = (y < y2) ? y : y2;
+ int dw = (x + w > x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
+ int dh = (y + h > y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
+
+ if (dw >= 0 && dh >= 0)
+ rect.setBounds(dx, dy, dw, dh);
+ else
+ rect.setBounds(0, 0, 0, 0);
+ return rect;
+ }
+
+ /**
+ * Tests if a rectangle contains another.
+ * @param a first rectangle
+ * @param b second rectangle
+ * @return true if a contains b, false otherwise
+ * @throws NullPointerException
+ */
+ public static boolean isRectangleContainingRectangle(Rectangle a, Rectangle b)
+ {
+ // Note: zero-size rects inclusive, differs from Rectangle.contains()
+ return b.width >= 0 && b.height >= 0 && b.width >= 0 && b.height >= 0
+ && b.x >= a.x && b.x + b.width <= a.x + a.width && b.y >= a.y
+ && b.y + b.height <= a.y + a.height;
+ }
+
+ /**
+ * Returns the InputMap that is provided by the ComponentUI of
+ * <code>component</code> for the specified condition.
+ *
+ * @param component the component for which the InputMap is returned
+ * @param cond the condition that specifies which of the three input
+ * maps should be returned, may be
+ * {@link JComponent#WHEN_IN_FOCUSED_WINDOW},
+ * {@link JComponent#WHEN_FOCUSED} or
+ * {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}
+ *
+ * @return The input map.
+ */
+ public static InputMap getUIInputMap(JComponent component, int cond)
+ {
+ if (UIManager.getUI(component) != null)
+ // we assume here that the UI class sets the parent of the component's
+ // InputMap, which is the correct behaviour. If it's not, then
+ // this can be considered a bug
+ return component.getInputMap(cond).getParent();
+ else
+ return null;
+ }
+
+ /**
+ * Returns the ActionMap that is provided by the ComponentUI of
+ * <code>component</code>.
+ *
+ * @param component the component for which the ActionMap is returned
+ */
+ public static ActionMap getUIActionMap(JComponent component)
+ {
+ if (UIManager.getUI(component) != null)
+ // we assume here that the UI class sets the parent of the component's
+ // ActionMap, which is the correct behaviour. If it's not, then
+ // this can be considered a bug
+ return component.getActionMap().getParent();
+ else
+ return null;
+ }
+
+ /**
+ * Processes key bindings for the component that is associated with the
+ * key event. Note that this method does not make sense for
+ * JComponent-derived components, except when
+ * {@link JComponent#processKeyEvent(KeyEvent)} is overridden and super is
+ * not called.
+ *
+ * This method searches through the component hierarchy of the component's
+ * top-level container to find a <code>JComponent</code> that has a binding
+ * for the key event in the WHEN_IN_FOCUSED_WINDOW scope.
+ *
+ * @param ev the key event
+ *
+ * @return <code>true</code> if a binding has been found and processed,
+ * <code>false</code> otherwise
+ *
+ * @since 1.4
+ */
+ public static boolean processKeyBindings(KeyEvent ev)
+ {
+ Component c = ev.getComponent();
+ KeyStroke s = KeyStroke.getKeyStrokeForEvent(ev);
+ KeyboardManager km = KeyboardManager.getManager();
+ return km.processKeyStroke(c, s, ev);
+ }
+
+ /**
+ * Returns a string representing one of the horizontal alignment codes
+ * defined in the {@link SwingConstants} interface. The following table
+ * lists the constants and return values:
+ * <p>
+ * <table border="0">
+ * <tr>
+ * <th>Code:</th><th>Returned String:</th>
+ * </tr>
+ * <tr>
+ * <td>{@link SwingConstants#CENTER}</td>
+ * <td><code>"CENTER"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link SwingConstants#LEFT}</td>
+ * <td><code>"LEFT"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link SwingConstants#RIGHT}</td>
+ * <td><code>"RIGHT"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link SwingConstants#LEADING}</td>
+ * <td><code>"LEADING"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link SwingConstants#TRAILING}</td>
+ * <td><code>"TRAILING"</code></td>
+ * </tr>
+ * </table>
+ * </p>
+ * If the supplied code is not one of those listed, this methods will throw
+ * an {@link IllegalArgumentException}.
+ *
+ * @param code the code.
+ *
+ * @return A string representing the given code.
+ */
+ static String convertHorizontalAlignmentCodeToString(int code)
+ {
+ switch (code)
+ {
+ case SwingConstants.CENTER:
+ return "CENTER";
+ case SwingConstants.LEFT:
+ return "LEFT";
+ case SwingConstants.RIGHT:
+ return "RIGHT";
+ case SwingConstants.LEADING:
+ return "LEADING";
+ case SwingConstants.TRAILING:
+ return "TRAILING";
+ default:
+ throw new IllegalArgumentException("Unrecognised code: " + code);
+ }
+ }
+
+ /**
+ * Returns a string representing one of the vertical alignment codes
+ * defined in the {@link SwingConstants} interface. The following table
+ * lists the constants and return values:
+ * <p>
+ * <table border="0">
+ * <tr>
+ * <th>Code:</th><th>Returned String:</th>
+ * </tr>
+ * <tr>
+ * <td>{@link SwingConstants#CENTER}</td>
+ * <td><code>"CENTER"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link SwingConstants#TOP}</td>
+ * <td><code>"TOP"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link SwingConstants#BOTTOM}</td>
+ * <td><code>"BOTTOM"</code></td>
+ * </tr>
+ * </table>
+ * </p>
+ * If the supplied code is not one of those listed, this methods will throw
+ * an {@link IllegalArgumentException}.
+ *
+ * @param code the code.
+ *
+ * @return A string representing the given code.
+ */
+ static String convertVerticalAlignmentCodeToString(int code)
+ {
+ switch (code)
+ {
+ case SwingConstants.CENTER:
+ return "CENTER";
+ case SwingConstants.TOP:
+ return "TOP";
+ case SwingConstants.BOTTOM:
+ return "BOTTOM";
+ default:
+ throw new IllegalArgumentException("Unrecognised code: " + code);
+ }
+ }
+
+ /**
+ * Returns a string representing one of the default operation codes
+ * defined in the {@link WindowConstants} interface. The following table
+ * lists the constants and return values:
+ * <p>
+ * <table border="0">
+ * <tr>
+ * <th>Code:</th><th>Returned String:</th>
+ * </tr>
+ * <tr>
+ * <td>{@link WindowConstants#DO_NOTHING_ON_CLOSE}</td>
+ * <td><code>"DO_NOTHING_ON_CLOSE"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link WindowConstants#HIDE_ON_CLOSE}</td>
+ * <td><code>"HIDE_ON_CLOSE"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link WindowConstants#DISPOSE_ON_CLOSE}</td>
+ * <td><code>"DISPOSE_ON_CLOSE"</code></td>
+ * </tr>
+ * <tr>
+ * <td>{@link WindowConstants#EXIT_ON_CLOSE}</td>
+ * <td><code>"EXIT_ON_CLOSE"</code></td>
+ * </tr>
+ * </table>
+ * </p>
+ * If the supplied code is not one of those listed, this method will throw
+ * an {@link IllegalArgumentException}.
+ *
+ * @param code the code.
+ *
+ * @return A string representing the given code.
+ */
+ static String convertWindowConstantToString(int code)
+ {
+ switch (code)
+ {
+ case WindowConstants.DO_NOTHING_ON_CLOSE:
+ return "DO_NOTHING_ON_CLOSE";
+ case WindowConstants.HIDE_ON_CLOSE:
+ return "HIDE_ON_CLOSE";
+ case WindowConstants.DISPOSE_ON_CLOSE:
+ return "DISPOSE_ON_CLOSE";
+ case WindowConstants.EXIT_ON_CLOSE:
+ return "EXIT_ON_CLOSE";
+ default:
+ throw new IllegalArgumentException("Unrecognised code: " + code);
+ }
+ }
+
+ /**
+ * Converts a rectangle in the coordinate system of a child component into
+ * a rectangle of one of it's Ancestors. The result is stored in the input
+ * rectangle.
+ *
+ * @param comp the child component
+ * @param r the rectangle to convert
+ * @param ancestor the ancestor component
+ */
+ static void convertRectangleToAncestor(Component comp, Rectangle r,
+ Component ancestor)
+ {
+ if (comp == ancestor)
+ return;
+
+ r.x += comp.getX();
+ r.y += comp.getY();
+
+ Component parent = comp.getParent();
+ if (parent != null && parent != ancestor)
+ convertRectangleToAncestor(parent, r, ancestor);
+ }
+}
diff --git a/libjava/classpath/javax/swing/Timer.java b/libjava/classpath/javax/swing/Timer.java
new file mode 100644
index 000000000..e9954b4b1
--- /dev/null
+++ b/libjava/classpath/javax/swing/Timer.java
@@ -0,0 +1,476 @@
+/* Timer.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.EventListenerList;
+
+/**
+ * Fires one or more action events after the specified delay. This is
+ * a specialised version of <code>java.util.Timer</code> just for
+ * firing <code>ActionEvent</code>s. All Timers share one (daemon)
+ * Thread (or java.util.Timer). All events are fired from the event
+ * queue.
+ *
+ * @author Ronald Veldema
+ * @author Audrius Meskauskas (audriusa@Bionformatics.org) - bug fixes
+ * and documentation comments
+ */
+public class Timer
+ implements Serializable
+{
+ /**
+ * Given to the shared java.util.Timer to (possibly repeatedly) call
+ * queueEvent().
+ */
+ private class Task extends java.util.TimerTask
+ {
+ public void run()
+ {
+ if (logTimers)
+ System.out.println("javax.swing.Timer -> queueEvent()");
+ queueEvent();
+
+ if (!repeats)
+ task = null;
+ }
+ }
+
+ /**
+ * Use serialVersionUID for interoperability.
+ */
+ private static final long serialVersionUID = -1116180831621385484L;
+
+ /**
+ * The encloding class, used with {@link SwingUtilities#invokeLater}
+ * to invoke the {@link #drainEvents()}.
+ */
+ private Runnable drainer = new Runnable()
+ {
+ public void run()
+ {
+ drainEvents();
+ }
+ };
+
+ /**
+ * The static java.util.Timer daemon which will be used to schedule
+ * all javax.swing.Timer.Task objects. The daemon will always be
+ * running, even if there's no task scheduled in it.
+ */
+ private static java.util.Timer timer = new java.util.Timer("swing.Timer",
+ true);
+
+ /**
+ * If <code>true</code>, the timer prints a message to
+ * {@link System#out} when firing each event.
+ */
+ static boolean logTimers;
+
+ /**
+ * A field to store all listeners who are listening to this timer.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * <code>true</code> if the timer coalesces events.
+ */
+ boolean coalesce = true;
+
+ /**
+ * <code>true</code> if the timer is firing repetetive events.
+ */
+ boolean repeats = true;
+
+ /**
+ * The delay between subsequent repetetive events.
+ */
+ int delay;
+
+ /**
+ * The initial delay before the first event.
+ */
+ int initialDelay;
+
+ /**
+ * The number of events that have been already fired by this timer.
+ * This is used as a numeric identifier for the next event that would
+ * be fired.
+ */
+ int ticks;
+
+ /**
+ * The task that calls queueEvent(). When null this Timer is stopped.
+ * This is package private to avoid synthetic accessor method.
+ */
+ Task task;
+
+ /**
+ * This object manages a "queue" of virtual actionEvents, maintained as a
+ * simple long counter. When the timer expires, a new event is queued,
+ * and a dispatcher object is pushed into the system event queue. When
+ * the system thread runs the dispatcher, it will fire as many
+ * ActionEvents as have been queued, unless the timer is set to
+ * coalescing mode, in which case it will fire only one ActionEvent.
+ */
+ private long queue;
+
+ /**
+ * <code>synchronized(queueLock)</code> replaces
+ * <code>synchronized(queue)</code> that is not supported by this language.
+ */
+ private Object queueLock = new Object();
+
+ /**
+ * Creates a new Timer object.
+ *
+ * @param d the default value for both initial and between event delay, in
+ * milliseconds.
+ * @param listener the first action listener, can be <code>null</code>.
+ */
+ public Timer(int d, ActionListener listener)
+ {
+ delay = d;
+ initialDelay = d;
+
+ if (listener != null)
+ addActionListener(listener);
+ }
+
+ /**
+ * Get the array of action listeners.
+ *
+ * @return the array of action listeners that are listening for the events,
+ * fired by this timer
+ *
+ * @since 1.4
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) listenerList.getListeners(ActionListener.class);
+ }
+
+ /**
+ * Sets whether the Timer coalesces multiple pending event firings.
+ * If the coalescing is enabled, the multiple events that have not been
+ * fired on time are replaced by the single event. The events may not
+ * be fired on time if the application is busy.
+ *
+ * @param c <code>true</code> (default) to enable the event coalescing,
+ * <code>false</code> otherwise
+ */
+ public void setCoalesce(boolean c)
+ {
+ coalesce = c;
+ }
+
+ /**
+ * Checks if the Timer coalesces multiple pending event firings.
+ * If the coalescing is enabled, the multiple events that have not been
+ * fired on time are replaced by the single event. The events may not
+ * be fired on time if the application is busy.
+ *
+ * @return <code>true</code> if the coalescing is enabled,
+ * <code>false</code> otherwise
+ */
+ public boolean isCoalesce()
+ {
+ return coalesce;
+ }
+
+ /**
+ * Get the event listeners of the given type that are listening for the
+ * events, fired by this timer.
+ *
+ * @param listenerType the listener type (for example, ActionListener.class)
+ *
+ * @return the array of event listeners that are listening for the events,
+ * fired by this timer
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Set the timer logging state. If it is set to <code>true</code>, the
+ * timer prints a message to {@link System#out} when firing each
+ * action event.
+ *
+ * @param lt <code>true</code> if logging is enabled, <code>false</code>
+ * (default value) otherwise
+ */
+ public static void setLogTimers(boolean lt)
+ {
+ logTimers = lt;
+ }
+
+ /**
+ * Return the logging state.
+ *
+ * @return <code>true</code> if the timer is printing a message to
+ * {@link System#out}
+ * when firing each action event
+ */
+ public static boolean getLogTimers()
+ {
+ return logTimers;
+ }
+
+ /**
+ * Set the delay between firing the subsequent events.
+ * This parameter does not change the value of the initial delay before
+ * firing the first event.
+ *
+ * @param d The time gap between the subsequent events, in milliseconds
+ *
+ * @throws IllegalArgumentException if <code>d</code> is less than zero.
+ */
+ public void setDelay(int d)
+ {
+ if (d < 0)
+ throw new IllegalArgumentException("Invalid delay: " + d);
+ delay = d;
+ }
+
+ /**
+ * Get the delay between firing the subsequent events.
+ *
+ * @return The delay between subsequent events, in milliseconds
+ */
+ public int getDelay()
+ {
+ return delay;
+ }
+
+ /**
+ * Set the intial delay before firing the first event since calling
+ * the {@link #start()} method. If the initial delay has not been
+ * set, it is assumed having the same value as the delay between the
+ * subsequent events.
+ *
+ * @param i the initial delay, in milliseconds
+ *
+ * @throws IllegalArgumentException if <code>i</code> is less than zero.
+ */
+ public void setInitialDelay(int i)
+ {
+ if (i < 0)
+ throw new IllegalArgumentException("Invalid initial delay: " + i);
+ initialDelay = i;
+ }
+
+ /**
+ * Get the intial delay before firing the first event since calling
+ * the {@link #start()} method. If the initial delay has not been
+ * set, returns the same value as {@link #getDelay()}.
+ *
+ * @return the initial delay before firing the first action event.
+ */
+ public int getInitialDelay()
+ {
+ return initialDelay;
+ }
+
+ /**
+ * Enable firing the repetetive events.
+ *
+ * @param r <code>true</code> (default value) to fire repetetive events.
+ * <code>false</code> to fire
+ * only one event after the initial delay
+ */
+ public void setRepeats(boolean r)
+ {
+ repeats = r;
+ }
+
+ /**
+ * Check is this timer fires repetetive events.
+ *
+ * @return <code>true</code> if the timer fires repetetive events,
+ * <code>false</code> if it fires
+ * only one event after the initial delay
+ */
+ public boolean isRepeats()
+ {
+ return repeats;
+ }
+
+ /**
+ * Get the timer state.
+ *
+ * @return <code>true</code> if the timer has been started and is firing
+ * the action events as scheduled. <code>false</code>
+ * if the timer is inactive.
+ */
+ public boolean isRunning()
+ {
+ return task != null;
+ }
+
+ /**
+ * Add the action listener
+ *
+ * @param listener the action listener to add
+ */
+ public void addActionListener(ActionListener listener)
+ {
+ listenerList.add(ActionListener.class, listener);
+ }
+
+ /**
+ * Remove the action listener.
+ *
+ * @param listener the action listener to remove
+ */
+ public void removeActionListener(ActionListener listener)
+ {
+ listenerList.remove(ActionListener.class, listener);
+ }
+
+ /**
+ * Cancel all pending tasks and fire the first event after the initial
+ * delay.
+ */
+ public void restart()
+ {
+ stop();
+ start();
+ }
+
+ /**
+ * Start firing the action events.
+ */
+ public void start()
+ {
+ Task t = task;
+ if (t == null)
+ {
+ t = new Task();
+ if (isRepeats())
+ timer.schedule(t, getInitialDelay(), getDelay());
+ else
+ timer.schedule(t, getInitialDelay());
+ task = t;
+ }
+ }
+
+ /**
+ * Stop firing the action events.
+ */
+ public void stop()
+ {
+ Task t = task;
+ if (t != null)
+ {
+ t.cancel();
+ task = null;
+ }
+ }
+
+ /**
+ * Fire the given action event to the action listeners.
+ *
+ * @param event the event to fire
+ */
+ protected void fireActionPerformed(ActionEvent event)
+ {
+ ActionListener[] listeners = getActionListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners [ i ].actionPerformed(event);
+ }
+
+ /**
+ * Fire the action event, named "Timer" and having the numeric
+ * identifier, equal to the numer of events that have been
+ * already fired before.
+ */
+ void fireActionPerformed()
+ {
+ fireActionPerformed(new ActionEvent(this, ticks++, "Timer"));
+ }
+
+ /**
+ * Fire the queued action events.
+ * In the coalescing mode, a single event is fired as a replacement
+ * for all queued events. In non coalescing mode, a series of
+ * all queued events is fired.
+ * This is package-private to avoid an accessor method.
+ */
+ void drainEvents()
+ {
+ synchronized (queueLock)
+ {
+ if (isCoalesce())
+ {
+ if (queue > 0)
+ fireActionPerformed();
+ }
+ else
+ {
+ while (queue > 0)
+ {
+ fireActionPerformed();
+ queue--;
+ }
+ }
+ queue = 0;
+ }
+ }
+
+ /**
+ * Post a scheduled event to the event queue.
+ * Package-private to avoid an accessor method.
+ */
+ void queueEvent()
+ {
+ synchronized(queueLock)
+ {
+ queue++;
+ if (queue == 1)
+ SwingUtilities.invokeLater(drainer);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/ToolTipManager.java b/libjava/classpath/javax/swing/ToolTipManager.java
new file mode 100644
index 000000000..8a31f79f6
--- /dev/null
+++ b/libjava/classpath/javax/swing/ToolTipManager.java
@@ -0,0 +1,593 @@
+/* ToolTipManager.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+
+/**
+ * This class is responsible for the registration of JToolTips to Components
+ * and for displaying them when appropriate.
+ */
+public class ToolTipManager extends MouseAdapter implements MouseMotionListener
+{
+ /**
+ * This ActionListener is associated with the Timer that listens to whether
+ * the JToolTip can be hidden after four seconds.
+ */
+ protected class stillInsideTimerAction implements ActionListener
+ {
+ /**
+ * This method creates a new stillInsideTimerAction object.
+ */
+ protected stillInsideTimerAction()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method hides the JToolTip when the Timer has finished.
+ *
+ * @param event The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ hideTip();
+ }
+ }
+
+ /**
+ * This Actionlistener is associated with the Timer that listens to whether
+ * the mouse cursor has re-entered the JComponent in time for an immediate
+ * redisplay of the JToolTip.
+ */
+ protected class outsideTimerAction implements ActionListener
+ {
+ /**
+ * This method creates a new outsideTimerAction object.
+ */
+ protected outsideTimerAction()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the Timer that listens to whether the mouse
+ * cursor has re-entered the JComponent has run out.
+ *
+ * @param event The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * This ActionListener is associated with the Timer that listens to whether
+ * it is time for the JToolTip to be displayed after the mouse has entered
+ * the JComponent.
+ */
+ protected class insideTimerAction implements ActionListener
+ {
+ /**
+ * This method creates a new insideTimerAction object.
+ */
+ protected insideTimerAction()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method displays the JToolTip when the Mouse has been still for the
+ * delay.
+ *
+ * @param event The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ showTip();
+ }
+ }
+
+ /**
+ * The Timer that determines whether the Mouse has been still long enough
+ * for the JToolTip to be displayed.
+ */
+ Timer enterTimer;
+
+ /**
+ * The Timer that determines whether the Mouse has re-entered the JComponent
+ * quickly enough for the JToolTip to be displayed immediately.
+ */
+ Timer exitTimer;
+
+ /**
+ * The Timer that determines whether the JToolTip has been displayed long
+ * enough for it to be hidden.
+ */
+ Timer insideTimer;
+
+ /** A global enabled setting for the ToolTipManager. */
+ private transient boolean enabled = true;
+
+ /** lightWeightPopupEnabled */
+ protected boolean lightWeightPopupEnabled = true;
+
+ /** heavyWeightPopupEnabled */
+ protected boolean heavyWeightPopupEnabled = false;
+
+ /** The shared instance of the ToolTipManager. */
+ private static ToolTipManager shared;
+
+ /** The current component the tooltip is being displayed for. */
+ private JComponent currentComponent;
+
+ /** The current tooltip. */
+ private JToolTip currentTip;
+
+ /**
+ * The tooltip text.
+ */
+ private String toolTipText;
+
+ /** The last known position of the mouse cursor. */
+ private Point currentPoint;
+
+ /** */
+ private Popup popup;
+
+ /**
+ * Creates a new ToolTipManager and sets up the timers.
+ */
+ ToolTipManager()
+ {
+ enterTimer = new Timer(750, new insideTimerAction());
+ enterTimer.setRepeats(false);
+
+ insideTimer = new Timer(4000, new stillInsideTimerAction());
+ insideTimer.setRepeats(false);
+
+ exitTimer = new Timer(500, new outsideTimerAction());
+ exitTimer.setRepeats(false);
+ }
+
+ /**
+ * This method returns the shared instance of ToolTipManager used by all
+ * JComponents.
+ *
+ * @return The shared instance of ToolTipManager.
+ */
+ public static ToolTipManager sharedInstance()
+ {
+ if (shared == null)
+ shared = new ToolTipManager();
+
+ return shared;
+ }
+
+ /**
+ * This method sets whether ToolTips are enabled or disabled for all
+ * JComponents.
+ *
+ * @param enabled Whether ToolTips are enabled or disabled for all
+ * JComponents.
+ */
+ public void setEnabled(boolean enabled)
+ {
+ if (! enabled)
+ {
+ enterTimer.stop();
+ exitTimer.stop();
+ insideTimer.stop();
+ }
+
+ this.enabled = enabled;
+ }
+
+ /**
+ * This method returns whether ToolTips are enabled.
+ *
+ * @return Whether ToolTips are enabled.
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * This method returns whether LightweightToolTips are enabled.
+ *
+ * @return Whether LighweightToolTips are enabled.
+ */
+ public boolean isLightWeightPopupEnabled()
+ {
+ return lightWeightPopupEnabled;
+ }
+
+ /**
+ * This method sets whether LightweightToolTips are enabled. If you mix
+ * Lightweight and Heavyweight components, you must set this to false to
+ * ensure that the ToolTips popup above all other components.
+ *
+ * @param enabled Whether LightweightToolTips will be enabled.
+ */
+ public void setLightWeightPopupEnabled(boolean enabled)
+ {
+ lightWeightPopupEnabled = enabled;
+ heavyWeightPopupEnabled = ! enabled;
+ }
+
+ /**
+ * This method returns the initial delay before the ToolTip is shown when
+ * the mouse enters a Component.
+ *
+ * @return The initial delay before the ToolTip is shown.
+ */
+ public int getInitialDelay()
+ {
+ return enterTimer.getDelay();
+ }
+
+ /**
+ * Sets the initial delay before the ToolTip is shown when the
+ * mouse enters a Component.
+ *
+ * @param delay The initial delay before the ToolTip is shown.
+ *
+ * @throws IllegalArgumentException if <code>delay</code> is less than zero.
+ */
+ public void setInitialDelay(int delay)
+ {
+ enterTimer.setDelay(delay);
+ }
+
+ /**
+ * This method returns the time the ToolTip will be shown before being
+ * hidden.
+ *
+ * @return The time the ToolTip will be shown before being hidden.
+ */
+ public int getDismissDelay()
+ {
+ return insideTimer.getDelay();
+ }
+
+ /**
+ * Sets the time the ToolTip will be shown before being hidden.
+ *
+ * @param delay the delay (in milliseconds) before tool tips are hidden.
+ *
+ * @throws IllegalArgumentException if <code>delay</code> is less than zero.
+ */
+ public void setDismissDelay(int delay)
+ {
+ insideTimer.setDelay(delay);
+ }
+
+ /**
+ * This method returns the amount of delay where if the mouse re-enters a
+ * Component, the tooltip will be shown immediately.
+ *
+ * @return The reshow delay.
+ */
+ public int getReshowDelay()
+ {
+ return exitTimer.getDelay();
+ }
+
+ /**
+ * Sets the amount of delay where if the mouse re-enters a
+ * Component, the tooltip will be shown immediately.
+ *
+ * @param delay The reshow delay (in milliseconds).
+ *
+ * @throws IllegalArgumentException if <code>delay</code> is less than zero.
+ */
+ public void setReshowDelay(int delay)
+ {
+ exitTimer.setDelay(delay);
+ }
+
+ /**
+ * This method registers a JComponent with the ToolTipManager.
+ *
+ * @param component The JComponent to register with the ToolTipManager.
+ */
+ public void registerComponent(JComponent component)
+ {
+ component.addMouseListener(this);
+ component.addMouseMotionListener(this);
+ }
+
+ /**
+ * This method unregisters a JComponent with the ToolTipManager.
+ *
+ * @param component The JComponent to unregister with the ToolTipManager.
+ */
+ public void unregisterComponent(JComponent component)
+ {
+ component.removeMouseMotionListener(this);
+ component.removeMouseListener(this);
+ }
+
+ /**
+ * This method is called whenever the mouse enters a JComponent registered
+ * with the ToolTipManager. When the mouse enters within the period of time
+ * specified by the reshow delay, the tooltip will be displayed
+ * immediately. Otherwise, it must wait for the initial delay before
+ * displaying the tooltip.
+ *
+ * @param event The MouseEvent.
+ */
+ public void mouseEntered(MouseEvent event)
+ {
+ if (currentComponent != null
+ && getContentPaneDeepestComponent(event) == currentComponent)
+ return;
+ currentPoint = event.getPoint();
+
+ currentComponent = (JComponent) event.getSource();
+ toolTipText = currentComponent.getToolTipText(event);
+ if (exitTimer.isRunning())
+ {
+ exitTimer.stop();
+ showTip();
+ return;
+ }
+ // This should always be stopped unless we have just fake-exited.
+ if (!enterTimer.isRunning())
+ enterTimer.start();
+ }
+
+ /**
+ * This method is called when the mouse exits a JComponent registered with the
+ * ToolTipManager. When the mouse exits, the tooltip should be hidden
+ * immediately.
+ *
+ * @param event
+ * The MouseEvent.
+ */
+ public void mouseExited(MouseEvent event)
+ {
+ if (getContentPaneDeepestComponent(event) == currentComponent)
+ return;
+
+ currentPoint = event.getPoint();
+ currentComponent = null;
+ hideTip();
+
+ if (! enterTimer.isRunning())
+ exitTimer.start();
+ if (enterTimer.isRunning())
+ enterTimer.stop();
+ if (insideTimer.isRunning())
+ insideTimer.stop();
+ }
+
+ /**
+ * This method is called when the mouse is pressed on a JComponent
+ * registered with the ToolTipManager. When the mouse is pressed, the
+ * tooltip (if it is shown) must be hidden immediately.
+ *
+ * @param event The MouseEvent.
+ */
+ public void mousePressed(MouseEvent event)
+ {
+ currentPoint = event.getPoint();
+ if (enterTimer.isRunning())
+ enterTimer.restart();
+ else if (insideTimer.isRunning())
+ {
+ insideTimer.stop();
+ hideTip();
+ }
+ }
+
+ /**
+ * This method is called when the mouse is dragged in a JComponent
+ * registered with the ToolTipManager.
+ *
+ * @param event The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent event)
+ {
+ currentPoint = event.getPoint();
+ if (enterTimer.isRunning())
+ enterTimer.restart();
+ }
+
+ /**
+ * This method is called when the mouse is moved in a JComponent registered
+ * with the ToolTipManager.
+ *
+ * @param event The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent event)
+ {
+ currentPoint = event.getPoint();
+ if (currentTip != null && currentTip.isShowing())
+ checkTipUpdate(event);
+ else
+ {
+ if (enterTimer.isRunning())
+ enterTimer.restart();
+ }
+ }
+
+ /**
+ * Checks if the tooltip's text or location changes when the mouse is moved
+ * over the component.
+ */
+ private void checkTipUpdate(MouseEvent ev)
+ {
+ JComponent comp = (JComponent) ev.getSource();
+ String newText = comp.getToolTipText(ev);
+ String oldText = toolTipText;
+ if (newText != null)
+ {
+ if (((newText != null && newText.equals(oldText)) || newText == null))
+ {
+ // No change at all. Restart timers.
+ if (popup == null)
+ enterTimer.restart();
+ else
+ insideTimer.restart();
+ }
+ else
+ {
+ // Update the tooltip.
+ toolTipText = newText;
+ hideTip();
+ showTip();
+ exitTimer.stop();
+ }
+ }
+ else
+ {
+ // Hide tooltip.
+ currentTip = null;
+ currentPoint = null;
+ hideTip();
+ enterTimer.stop();
+ exitTimer.stop();
+ }
+ }
+
+ /**
+ * This method displays the ToolTip. It can figure out the method needed to
+ * show it as well (whether to display it in heavyweight/lightweight panel
+ * or a window.) This is package-private to avoid an accessor method.
+ */
+ void showTip()
+ {
+ if (!enabled || currentComponent == null || !currentComponent.isEnabled()
+ || !currentComponent.isShowing())
+ {
+ popup = null;
+ return;
+ }
+
+ if (currentTip == null || currentTip.getComponent() != currentComponent)
+ currentTip = currentComponent.createToolTip();
+ currentTip.setTipText(toolTipText);
+
+ Point p = currentPoint;
+ Point cP = currentComponent.getLocationOnScreen();
+ Dimension dims = currentTip.getPreferredSize();
+
+ JLayeredPane pane = null;
+ JRootPane r = ((JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class,
+ currentComponent));
+ if (r != null)
+ pane = r.getLayeredPane();
+ if (pane == null)
+ return;
+
+ p.translate(cP.x, cP.y);
+ adjustLocation(p, pane, dims);
+
+ currentTip.setBounds(0, 0, dims.width, dims.height);
+
+ PopupFactory factory = PopupFactory.getSharedInstance();
+ popup = factory.getPopup(currentComponent, currentTip, p.x, p.y);
+ popup.show();
+ }
+
+ /**
+ * Adjusts the point to a new location on the component,
+ * using the currentTip's dimensions.
+ *
+ * @param p - the point to convert.
+ * @param c - the component the point is on.
+ * @param d - the dimensions of the currentTip.
+ */
+ private Point adjustLocation(Point p, Component c, Dimension d)
+ {
+ if (p.x + d.width > c.getWidth())
+ p.x -= d.width;
+ if (p.x < 0)
+ p.x = 0;
+ if (p.y + d.height < c.getHeight())
+ p.y += d.height;
+ if (p.y + d.height > c.getHeight())
+ p.y -= d.height;
+
+ return p;
+ }
+
+ /**
+ * This method hides the ToolTip.
+ * This is package-private to avoid an accessor method.
+ */
+ void hideTip()
+ {
+ if (popup != null)
+ popup.hide();
+ }
+
+ /**
+ * This method returns the deepest component in the content pane for the
+ * first RootPaneContainer up from the currentComponent. This method is
+ * used in conjunction with one of the mouseXXX methods.
+ *
+ * @param e The MouseEvent.
+ *
+ * @return The deepest component in the content pane.
+ */
+ private Component getContentPaneDeepestComponent(MouseEvent e)
+ {
+ Component source = (Component) e.getSource();
+ Container parent = SwingUtilities.getAncestorOfClass(JRootPane.class,
+ currentComponent);
+ if (parent == null)
+ return null;
+ parent = ((JRootPane) parent).getContentPane();
+ Point p = e.getPoint();
+ p = SwingUtilities.convertPoint(source, p, parent);
+ Component target = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y);
+ return target;
+ }
+}
diff --git a/libjava/classpath/javax/swing/TransferHandler.java b/libjava/classpath/javax/swing/TransferHandler.java
new file mode 100644
index 000000000..abf6e8c2e
--- /dev/null
+++ b/libjava/classpath/javax/swing/TransferHandler.java
@@ -0,0 +1,654 @@
+/* TransferHandler.java --
+ Copyright (C) 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DragGestureEvent;
+import java.awt.dnd.DragGestureListener;
+import java.awt.dnd.DragGestureRecognizer;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceContext;
+import java.awt.dnd.DragSourceDragEvent;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceEvent;
+import java.awt.dnd.DragSourceListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+public class TransferHandler implements Serializable
+{
+
+ /**
+ * An implementation of {@link Transferable} that can be used to export
+ * data from a component's property.
+ */
+ private static class PropertyTransferable
+ implements Transferable
+ {
+ /**
+ * The component from which we export.
+ */
+ private JComponent component;
+
+ /**
+ * The property descriptor of the property that we handle.
+ */
+ private PropertyDescriptor property;
+
+ /**
+ * Creates a new PropertyTransferable.
+ *
+ * @param c the component from which we export
+ * @param prop the property from which we export
+ */
+ PropertyTransferable(JComponent c, PropertyDescriptor prop)
+ {
+ component = c;
+ property = prop;
+ }
+
+ /**
+ * Returns the data flavors supported by the Transferable.
+ *
+ * @return the data flavors supported by the Transferable
+ */
+ public DataFlavor[] getTransferDataFlavors()
+ {
+ DataFlavor[] flavors;
+ Class propClass = property.getPropertyType();
+ String mime = DataFlavor.javaJVMLocalObjectMimeType + "; class="
+ + propClass.getName();
+ try
+ {
+ DataFlavor flavor = new DataFlavor(mime);
+ flavors = new DataFlavor[]{ flavor };
+ }
+ catch (ClassNotFoundException ex)
+ {
+ flavors = new DataFlavor[0];
+ }
+ return flavors;
+ }
+
+ /**
+ * Returns <code>true</code> when the specified data flavor is supported,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> when the specified data flavor is supported,
+ * <code>false</code> otherwise
+ */
+ public boolean isDataFlavorSupported(DataFlavor flavor)
+ {
+ Class propClass = property.getPropertyType();
+ return flavor.getPrimaryType().equals("application")
+ && flavor.getSubType().equals("x-java-jvm-local-objectref")
+ && propClass.isAssignableFrom(flavor.getRepresentationClass());
+ }
+
+ /**
+ * Returns the actual transfer data.
+ *
+ * @param flavor the data flavor
+ *
+ * @return the actual transfer data
+ */
+ public Object getTransferData(DataFlavor flavor)
+ throws UnsupportedFlavorException, IOException
+ {
+ if (isDataFlavorSupported(flavor))
+ {
+ Method getter = property.getReadMethod();
+ Object o;
+ try
+ {
+ o = getter.invoke(component);
+ return o;
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Property read failed: "
+ + property.getName());
+ }
+ }
+ else
+ throw new UnsupportedFlavorException(flavor);
+ }
+ }
+
+ static class TransferAction extends AbstractAction
+ {
+ private String command;
+
+ public TransferAction(String command)
+ {
+ super(command);
+ this.command = command;
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JComponent component = (JComponent) event.getSource();
+ TransferHandler transferHandler = component.getTransferHandler();
+ Clipboard clipboard = getClipboard(component);
+
+ if (clipboard == null)
+ {
+ // Access denied!
+ Toolkit.getDefaultToolkit().beep();
+ return;
+ }
+
+ if (command.equals(COMMAND_COPY))
+ transferHandler.exportToClipboard(component, clipboard, COPY);
+ else if (command.equals(COMMAND_CUT))
+ transferHandler.exportToClipboard(component, clipboard, MOVE);
+ else if (command.equals(COMMAND_PASTE))
+ {
+ Transferable transferable = clipboard.getContents(null);
+
+ if (transferable != null)
+ transferHandler.importData(component, transferable);
+ }
+ }
+
+ /**
+ * Get the system cliboard or null if the caller isn't allowed to
+ * access the system clipboard.
+ *
+ * @param component a component, used to get the toolkit.
+ * @return the clipboard
+ */
+ private static Clipboard getClipboard(JComponent component)
+ {
+ try
+ {
+ return component.getToolkit().getSystemClipboard();
+ }
+ catch (SecurityException se)
+ {
+ return null;
+ }
+ }
+ }
+
+ private static class SwingDragGestureRecognizer extends DragGestureRecognizer
+ {
+
+ protected SwingDragGestureRecognizer(DragGestureListener dgl)
+ {
+ super(DragSource.getDefaultDragSource(), null, NONE, dgl);
+ }
+
+ void gesture(JComponent c, MouseEvent e, int src, int drag)
+ {
+ setComponent(c);
+ setSourceActions(src);
+ appendEvent(e);
+ fireDragGestureRecognized(drag, e.getPoint());
+ }
+
+ protected void registerListeners()
+ {
+ // Nothing to do here.
+ }
+
+ protected void unregisterListeners()
+ {
+ // Nothing to do here.
+ }
+
+ }
+
+ private static class SwingDragHandler
+ implements DragGestureListener, DragSourceListener
+ {
+
+ private boolean autoscrolls;
+
+ public void dragGestureRecognized(DragGestureEvent e)
+ {
+ JComponent c = (JComponent) e.getComponent();
+ TransferHandler th = c.getTransferHandler();
+ Transferable t = th.createTransferable(c);
+ if (t != null)
+ {
+ autoscrolls = c.getAutoscrolls();
+ c.setAutoscrolls(false);
+ try
+ {
+ e.startDrag(null, t, this);
+ return;
+ }
+ finally
+ {
+ c.setAutoscrolls(autoscrolls);
+ }
+ }
+ th.exportDone(c, t, NONE);
+ }
+
+ public void dragDropEnd(DragSourceDropEvent e)
+ {
+ DragSourceContext ctx = e.getDragSourceContext();
+ JComponent c = (JComponent) ctx.getComponent();
+ TransferHandler th = c.getTransferHandler();
+ if (e.getDropSuccess())
+ {
+ th.exportDone(c, ctx.getTransferable(), e.getDropAction());
+ }
+ else
+ {
+ th.exportDone(c, ctx.getTransferable(), e.getDropAction());
+ }
+ c.setAutoscrolls(autoscrolls);
+ }
+
+ public void dragEnter(DragSourceDragEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void dragExit(DragSourceEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void dragOver(DragSourceDragEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void dropActionChanged(DragSourceDragEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ }
+
+ private static final long serialVersionUID = -967749805571669910L;
+
+ private static final String COMMAND_COPY = "copy";
+ private static final String COMMAND_CUT = "cut";
+ private static final String COMMAND_PASTE = "paste";
+
+ 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;
+
+ private static Action copyAction = new TransferAction(COMMAND_COPY);
+ private static Action cutAction = new TransferAction(COMMAND_CUT);
+ private static Action pasteAction = new TransferAction(COMMAND_PASTE);
+
+ private int sourceActions;
+ private Icon visualRepresentation;
+
+ /**
+ * The name of the property into/from which this TransferHandler
+ * imports/exports.
+ */
+ private String propertyName;
+
+ /**
+ * The DragGestureRecognizer for Swing.
+ */
+ private SwingDragGestureRecognizer recognizer;
+
+ public static Action getCopyAction()
+ {
+ return copyAction;
+ }
+
+ public static Action getCutAction()
+ {
+ return cutAction;
+ }
+
+ public static Action getPasteAction()
+ {
+ return pasteAction;
+ }
+
+ protected TransferHandler()
+ {
+ this.sourceActions = NONE;
+ }
+
+ public TransferHandler(String property)
+ {
+ propertyName = property;
+ this.sourceActions = property != null ? COPY : NONE;
+ }
+
+ /**
+ * Returns <code>true</code> if the data in this TransferHandler can be
+ * imported into the specified component. This will be the case when:
+ * <ul>
+ * <li>The component has a readable and writable property with the property
+ * name specified in the TransferHandler constructor.</li>
+ * <li>There is a dataflavor with a mime type of
+ * <code>application/x-java-jvm-local-object-ref</code>.</li>
+ * <li>The dataflavor's representation class matches the class of the
+ * property in the component.</li>
+ * </li>
+ *
+ * @param c the component to check
+ * @param flavors the possible data flavors
+ *
+ * @return <code>true</code> if the data in this TransferHandler can be
+ * imported into the specified component, <code>false</code>
+ * otherwise
+ */
+ public boolean canImport(JComponent c, DataFlavor[] flavors)
+ {
+ PropertyDescriptor propDesc = getPropertyDescriptor(c);
+ boolean canImport = false;
+ if (propDesc != null)
+ {
+ // Check if the property is writable. The readable check is already
+ // done in getPropertyDescriptor().
+ Method writer = propDesc.getWriteMethod();
+ if (writer != null)
+ {
+ Class[] params = writer.getParameterTypes();
+ if (params.length == 1)
+ {
+ // Number of parameters ok, now check mime type and
+ // representation class.
+ DataFlavor flavor = getPropertyDataFlavor(params[0], flavors);
+ if (flavor != null)
+ canImport = true;
+ }
+ }
+ }
+ return canImport;
+ }
+
+ /**
+ * Creates a {@link Transferable} that can be used to export data
+ * from the specified component.
+ *
+ * This method returns <code>null</code> when the specified component
+ * doesn't have a readable property that matches the property name
+ * specified in the <code>TransferHandler</code> constructor.
+ *
+ * @param c the component to create a transferable for
+ *
+ * @return a {@link Transferable} that can be used to export data
+ * from the specified component, or null if the component doesn't
+ * have a readable property like the transfer handler
+ */
+ protected Transferable createTransferable(JComponent c)
+ {
+ Transferable transferable = null;
+ if (propertyName != null)
+ {
+ PropertyDescriptor prop = getPropertyDescriptor(c);
+ if (prop != null)
+ transferable = new PropertyTransferable(c, prop);
+ }
+ return transferable;
+ }
+
+ public void exportAsDrag(JComponent c, InputEvent e, int action)
+ {
+ int src = getSourceActions(c);
+ int drag = src & action;
+ if (! (e instanceof MouseEvent))
+ {
+ drag = NONE;
+ }
+ if (drag != NONE)
+ {
+ if (recognizer == null)
+ {
+ SwingDragHandler ds = new SwingDragHandler();
+ recognizer = new SwingDragGestureRecognizer(ds);
+ }
+ recognizer.gesture(c, (MouseEvent) e, src, drag);
+ }
+ else
+ {
+ exportDone(c, null, NONE);
+ }
+ }
+
+ /**
+ * This method is invoked after data has been exported.
+ * Subclasses should implement this method to remove the data that has been
+ * transferred when the action was <code>MOVE</code>.
+ *
+ * The default implementation does nothing because MOVE is not supported.
+ *
+ * @param c the source component
+ * @param data the data that has been transferred or <code>null</code>
+ * when the action is NONE
+ * @param action the action that has been performed
+ */
+ protected void exportDone(JComponent c, Transferable data, int action)
+ {
+ // Nothing to do in the default implementation.
+ }
+
+ /**
+ * Exports the property of the component <code>c</code> that was
+ * specified for this TransferHandler to the clipboard, performing
+ * the specified action.
+ *
+ * This will check if the action is allowed by calling
+ * {@link #getSourceActions(JComponent)}. If the action is not allowed,
+ * then no export is performed.
+ *
+ * In either case the method {@link #exportDone} will be called with
+ * the action that has been performed, or {@link #NONE} if the action
+ * was not allowed or could otherwise not be completed.
+ * Any IllegalStateException that is thrown by the Clipboard due to
+ * beeing unavailable will be propagated through this method.
+ *
+ * @param c the component from which to export
+ * @param clip the clipboard to which the data will be exported
+ * @param action the action to perform
+ *
+ * @throws IllegalStateException when the clipboard is not available
+ */
+ public void exportToClipboard(JComponent c, Clipboard clip, int action)
+ throws IllegalStateException
+ {
+ action &= getSourceActions(c);
+ Transferable transferable = createTransferable(c);
+ if (transferable != null && action != NONE)
+ {
+ try
+ {
+ clip.setContents(transferable, null);
+ exportDone(c, transferable, action);
+ }
+ catch (IllegalStateException ex)
+ {
+ exportDone(c, transferable, NONE);
+ throw ex;
+ }
+ }
+ else
+ exportDone(c, null, NONE);
+ }
+
+ public int getSourceActions(JComponent c)
+ {
+ return sourceActions;
+ }
+
+ public Icon getVisualRepresentation(Transferable t)
+ {
+ return visualRepresentation;
+ }
+
+ /**
+ * Imports the transfer data represented by <code>t</code> into the specified
+ * component <code>c</code> by setting the property of this TransferHandler
+ * on that component. If this succeeds, this method returns
+ * <code>true</code>, otherwise <code>false</code>.
+ *
+ *
+ * @param c the component to import into
+ * @param t the transfer data to import
+ *
+ * @return <code>true</code> if the transfer succeeds, <code>false</code>
+ * otherwise
+ */
+ public boolean importData(JComponent c, Transferable t)
+ {
+ boolean ok = false;
+ PropertyDescriptor prop = getPropertyDescriptor(c);
+ if (prop != null)
+ {
+ Method writer = prop.getWriteMethod();
+ if (writer != null)
+ {
+ Class[] params = writer.getParameterTypes();
+ if (params.length == 1)
+ {
+ DataFlavor flavor = getPropertyDataFlavor(params[0],
+ t.getTransferDataFlavors());
+ if (flavor != null)
+ {
+ try
+ {
+ Object value = t.getTransferData(flavor);
+ writer.invoke(c, new Object[]{ value });
+ ok = true;
+ }
+ catch (Exception ex)
+ {
+ // If anything goes wrong here, do nothing and return
+ // false;
+ }
+ }
+ }
+ }
+ }
+ return ok;
+ }
+
+ /**
+ * Returns the property descriptor for the property of this TransferHandler
+ * in the specified component, or <code>null</code> if no such property
+ * exists in the component. This method only returns properties that are
+ * at least readable (that is, it has a public no-arg getter method).
+ *
+ * @param c the component to check
+ *
+ * @return the property descriptor for the property of this TransferHandler
+ * in the specified component, or <code>null</code> if no such
+ * property exists in the component
+ */
+ private PropertyDescriptor getPropertyDescriptor(JComponent c)
+ {
+ PropertyDescriptor prop = null;
+ if (propertyName != null)
+ {
+ Class clazz = c.getClass();
+ BeanInfo beanInfo;
+ try
+ {
+ beanInfo = Introspector.getBeanInfo(clazz);
+ }
+ catch (IntrospectionException ex)
+ {
+ beanInfo = null;
+ }
+ if (beanInfo != null)
+ {
+ PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
+ for (int i = 0; i < props.length && prop == null; i++)
+ {
+ PropertyDescriptor desc = props[i];
+ if (desc.getName().equals(propertyName))
+ {
+ Method reader = desc.getReadMethod();
+ if (reader != null)
+ {
+ Class[] params = reader.getParameterTypes();
+ if (params == null || params.length == 0)
+ prop = desc;
+ }
+ }
+ }
+ }
+ }
+ return prop;
+ }
+
+ /**
+ * Searches <code>flavors</code> to find a suitable data flavor that
+ * has the mime type application/x-java-jvm-local-objectref and a
+ * representation class that is the same as the specified <code>clazz</code>.
+ * When no such data flavor is found, this returns <code>null</code>.
+ *
+ * @param clazz the representation class required for the data flavor
+ * @param flavors the possible data flavors
+ *
+ * @return the suitable data flavor or null if none is found
+ */
+ private DataFlavor getPropertyDataFlavor(Class clazz, DataFlavor[] flavors)
+ {
+ DataFlavor found = null;
+ for (int i = 0; i < flavors.length && found == null; i++)
+ {
+ DataFlavor flavor = flavors[i];
+ if (flavor.getPrimaryType().equals("application")
+ && flavor.getSubType().equals("x-java-jvm-local-objectref")
+ && clazz.isAssignableFrom(flavor.getRepresentationClass()))
+ found = flavor;
+ }
+ return found;
+ }
+}
diff --git a/libjava/classpath/javax/swing/UIDefaults.java b/libjava/classpath/javax/swing/UIDefaults.java
new file mode 100644
index 000000000..904b4c2b8
--- /dev/null
+++ b/libjava/classpath/javax/swing/UIDefaults.java
@@ -0,0 +1,847 @@
+/* UIDefaults.java -- database for all settings and interface bindings.
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Insets;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.border.Border;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.InputMapUIResource;
+
+/**
+ * UIDefaults is a database where all settings and interface bindings are
+ * stored into. A PLAF implementation fills one of these (see for example
+ * plaf/basic/BasicLookAndFeel.java) with "ButtonUI" -&gt; new BasicButtonUI().
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class UIDefaults extends Hashtable<Object, Object>
+{
+
+ /** Our ResourceBundles. */
+ private LinkedList bundles;
+
+ /** The default locale. */
+ private Locale defaultLocale;
+
+ /** We use this for firing PropertyChangeEvents. */
+ private PropertyChangeSupport propertyChangeSupport;
+
+ /**
+ * Used for lazy instantiation of UIDefaults values so that they are not
+ * all loaded when a Swing application starts up, but only the values that
+ * are really needed. An <code>ActiveValue</code> is newly instantiated
+ * every time when the value is requested, as opposed to the normal
+ * {@link LazyValue} that is only instantiated once.
+ */
+ public static interface ActiveValue
+ {
+ Object createValue(UIDefaults table);
+ }
+
+ public static class LazyInputMap implements LazyValue
+ {
+ Object[] bind;
+ public LazyInputMap(Object[] bindings)
+ {
+ bind = bindings;
+ }
+ public Object createValue(UIDefaults table)
+ {
+ InputMapUIResource im = new InputMapUIResource();
+ for (int i = 0; 2 * i + 1 < bind.length; ++i)
+ {
+ Object curr = bind[2 * i];
+ if (curr instanceof KeyStroke)
+ im.put((KeyStroke) curr, bind[2 * i + 1]);
+ else
+ im.put(KeyStroke.getKeyStroke((String) curr),
+ bind[2 * i + 1]);
+ }
+ return im;
+ }
+ }
+
+ /**
+ * Used for lazy instantiation of UIDefaults values so that they are not
+ * all loaded when a Swing application starts up, but only the values that
+ * are really needed. A <code>LazyValue</code> is only instantiated once,
+ * as opposed to the {@link ActiveValue} that is newly created every time
+ * it is requested.
+ */
+ public static interface LazyValue
+ {
+ Object createValue(UIDefaults table);
+ }
+
+ public static class ProxyLazyValue implements LazyValue
+ {
+ LazyValue inner;
+ public ProxyLazyValue(String s)
+ {
+ final String className = s;
+ inner = new LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ try
+ {
+ return Class
+ .forName(className)
+ .getConstructor(new Class[] {})
+ .newInstance(new Object[] {});
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ };
+ }
+
+ public ProxyLazyValue(String c, String m)
+ {
+ final String className = c;
+ final String methodName = m;
+ inner = new LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ try
+ {
+ return Class
+ .forName(className)
+ .getMethod(methodName, new Class[] {})
+ .invoke(null, new Object[] {});
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ };
+ }
+
+ public ProxyLazyValue(String c, Object[] os)
+ {
+ final String className = c;
+ final Object[] objs = os;
+ final Class[] clss = new Class[objs.length];
+ for (int i = 0; i < objs.length; ++i)
+ {
+ clss[i] = objs[i].getClass();
+ }
+ inner = new LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ try
+ {
+ return Class
+ .forName(className)
+ .getConstructor(clss)
+ .newInstance(objs);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ };
+ }
+
+ public ProxyLazyValue(String c, String m, Object[] os)
+ {
+ final String className = c;
+ final String methodName = m;
+ final Object[] objs = os;
+ final Class[] clss = new Class[objs.length];
+ for (int i = 0; i < objs.length; ++i)
+ {
+ clss[i] = objs[i].getClass();
+ }
+ inner = new LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ try
+ {
+ return Class
+ .forName(className)
+ .getMethod(methodName, clss)
+ .invoke(null, objs);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ };
+ }
+
+ public Object createValue(UIDefaults table)
+ {
+ return inner.createValue(table);
+ }
+ }
+
+ /** Our serialVersionUID for serialization. */
+ private static final long serialVersionUID = 7341222528856548117L;
+
+ /**
+ * Constructs a new empty UIDefaults instance.
+ */
+ public UIDefaults()
+ {
+ bundles = new LinkedList();
+ defaultLocale = Locale.getDefault();
+ propertyChangeSupport = new PropertyChangeSupport(this);
+ }
+
+ /**
+ * Constructs a new UIDefaults instance and loads the specified entries.
+ * The entries are expected to come in pairs, that means
+ * <code>entries[0]</code> is a key, <code>entries[1]</code> is a value,
+ * <code>entries[2]</code> a key and so forth.
+ *
+ * @param entries the entries to initialize the UIDefaults instance with
+ */
+ public UIDefaults(Object[] entries)
+ {
+ this();
+
+ for (int i = 0; (2 * i + 1) < entries.length; ++i)
+ put(entries[2 * i], entries[2 * i + 1]);
+ }
+
+ /**
+ * Returns the entry for the specified <code>key</code> in the default
+ * locale.
+ *
+ * @return the entry for the specified <code>key</code>
+ */
+ public Object get(Object key)
+ {
+ return this.get(key, getDefaultLocale());
+ }
+
+ /**
+ * Returns the entry for the specified <code>key</code> in the Locale
+ * <code>loc</code>.
+ *
+ * @param key the key for which we return the value
+ * @param loc the locale
+ */
+ public Object get(Object key, Locale loc)
+ {
+ Object obj = null;
+
+ if (super.containsKey(key))
+ {
+ obj = super.get(key);
+ }
+ else if (key instanceof String)
+ {
+ String keyString = (String) key;
+ ListIterator i = bundles.listIterator(0);
+ while (i.hasNext())
+ {
+ String bundle_name = (String) i.next();
+ ResourceBundle res =
+ ResourceBundle.getBundle(bundle_name, loc);
+ if (res != null)
+ {
+ try
+ {
+ obj = res.getObject(keyString);
+ break;
+ }
+ catch (MissingResourceException me)
+ {
+ // continue, this bundle has no such key
+ }
+ }
+ }
+ }
+
+ // now we've found the object, resolve it.
+ // nb: LazyValues aren't supported in resource bundles, so it's correct
+ // to insert their results in the locale-less hashtable.
+
+ if (obj == null)
+ return null;
+
+ if (obj instanceof LazyValue)
+ {
+ Object resolved = ((LazyValue) obj).createValue(this);
+ super.remove(key);
+ super.put(key, resolved);
+ return resolved;
+ }
+ else if (obj instanceof ActiveValue)
+ {
+ return ((ActiveValue) obj).createValue(this);
+ }
+
+ return obj;
+ }
+
+ /**
+ * Puts a key and value into this UIDefaults object.<br>
+ * In contrast to
+ * {@link java.util.Hashtable}s <code>null</code>-values are accepted
+ * here and treated like #remove(key).
+ * <br>
+ * This fires a PropertyChangeEvent with key as name and the old and new
+ * values.
+ *
+ * @param key the key to put into the map
+ * @param value the value to put into the map
+ *
+ * @return the old value for key or <code>null</code> if <code>key</code>
+ * had no value assigned
+ */
+ public Object put(Object key, Object value)
+ {
+ Object old = checkAndPut(key, value);
+
+ if (key instanceof String && old != value)
+ firePropertyChange((String) key, old, value);
+ return old;
+ }
+
+ /**
+ * Puts a set of key-value pairs into the map.
+ * The entries are expected to come in pairs, that means
+ * <code>entries[0]</code> is a key, <code>entries[1]</code> is a value,
+ * <code>entries[2]</code> a key and so forth.
+ * <br>
+ * If a value is <code>null</code> it is treated like #remove(key).
+ * <br>
+ * This unconditionally fires a PropertyChangeEvent with
+ * <code>&apos;UIDefaults&apos;</code> as name and <code>null</code> for
+ * old and new value.
+ *
+ * @param entries the entries to be put into the map
+ */
+ public void putDefaults(Object[] entries)
+ {
+ for (int i = 0; (2 * i + 1) < entries.length; ++i)
+ {
+ checkAndPut(entries[2 * i], entries[2 * i + 1]);
+ }
+ firePropertyChange("UIDefaults", null, null);
+ }
+
+ /**
+ * Checks the value for <code>null</code> and put it into the Hashtable, if
+ * it is not <code>null</code>. If the value is <code>null</code> then
+ * remove the corresponding key.
+ *
+ * @param key the key to put into this UIDefauls table
+ * @param value the value to put into this UIDefaults table
+ *
+ * @return the old value for <code>key</code>
+ */
+ private Object checkAndPut(Object key, Object value)
+ {
+ Object old;
+
+ if (value != null)
+ old = super.put(key, value);
+ else
+ old = super.remove(key);
+
+ return old;
+ }
+
+ /**
+ * Returns a font entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return the font entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Font getFont(Object key)
+ {
+ Object o = get(key);
+ return o instanceof Font ? (Font) o : null;
+ }
+
+ /**
+ * Returns a font entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the font entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Font getFont(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ return o instanceof Font ? (Font) o : null;
+ }
+
+ /**
+ * Returns a color entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return the color entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Color getColor(Object key)
+ {
+ Object o = get(key);
+ return o instanceof Color ? (Color) o : null;
+ }
+
+ /**
+ * Returns a color entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the color entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Color getColor(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ return o instanceof Color ? (Color) o : null;
+ }
+
+ /**
+ * Returns an icon entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return the icon entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Icon getIcon(Object key)
+ {
+ Object o = get(key);
+ return o instanceof Icon ? (Icon) o : null;
+ }
+
+ /**
+ * Returns an icon entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the icon entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Icon getIcon(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ return o instanceof Icon ? (Icon) o : null;
+ }
+
+ /**
+ * Returns a border entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return the border entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Border getBorder(Object key)
+ {
+ Object o = get(key);
+ return o instanceof Border ? (Border) o : null;
+ }
+
+ /**
+ * Returns a border entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the border entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Border getBorder(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ return o instanceof Border ? (Border) o : null;
+ }
+
+ /**
+ * Returns a string entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return the string entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public String getString(Object key)
+ {
+ Object o = get(key);
+ return o instanceof String ? (String) o : null;
+ }
+
+ /**
+ * Returns a string entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the string entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public String getString(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ return o instanceof String ? (String) o : null;
+ }
+
+ /**
+ * Returns an integer entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return the integer entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public int getInt(Object key)
+ {
+ Object o = get(key);
+ return o instanceof Integer ? ((Integer) o).intValue() : 0;
+ }
+
+ /**
+ * Returns an integer entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the integer entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public int getInt(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ return o instanceof Integer ? ((Integer) o).intValue() : 0;
+ }
+
+ /**
+ * Returns a boolean entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return The boolean entry for <code>key</code> or <code>false</code> if no
+ * such entry exists.
+ */
+ public boolean getBoolean(Object key)
+ {
+ return Boolean.TRUE.equals(get(key));
+ }
+
+ /**
+ * Returns a boolean entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the boolean entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public boolean getBoolean(Object key, Locale locale)
+ {
+ return Boolean.TRUE.equals(get(key, locale));
+ }
+
+ /**
+ * Returns an insets entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return the insets entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Insets getInsets(Object key)
+ {
+ Object o = get(key);
+ return o instanceof Insets ? (Insets) o : null;
+ }
+
+ /**
+ * Returns an insets entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the boolean entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Insets getInsets(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ return o instanceof Insets ? (Insets) o : null;
+ }
+
+ /**
+ * Returns a dimension entry for the default locale.
+ *
+ * @param key the key to the requested entry
+ *
+ * @return the dimension entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Dimension getDimension(Object key)
+ {
+ Object o = get(key);
+ return o instanceof Dimension ? (Dimension) o : null;
+ }
+
+ /**
+ * Returns a dimension entry for a specic locale.
+ *
+ * @param key the key to the requested entry
+ * @param locale the locale to the requested entry
+ *
+ * @return the boolean entry for <code>key</code> or null if no such entry
+ * exists
+ */
+ public Dimension getDimension(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ return o instanceof Dimension ? (Dimension) o : null;
+ }
+
+ /**
+ * Returns the ComponentUI class that renders a component. <code>id</code>
+ * is the ID for which the String value of the classname is stored in
+ * this UIDefaults map.
+ *
+ * @param id the ID of the UI class
+ * @param loader the ClassLoader to use
+ *
+ * @return the UI class for <code>id</code>
+ */
+ public Class<? extends ComponentUI> getUIClass(String id, ClassLoader loader)
+ {
+ String className = (String) get(id);
+ if (className == null)
+ return null;
+ try
+ {
+ if (loader == null)
+ loader = ClassLoader.getSystemClassLoader();
+ return (Class<? extends ComponentUI>) loader.loadClass (className);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the ComponentUI class that renders a component. <code>id</code>
+ * is the ID for which the String value of the classname is stored in
+ * this UIDefaults map.
+ *
+ * @param id the ID of the UI class
+ *
+ * @return the UI class for <code>id</code>
+ */
+ public Class<? extends ComponentUI> getUIClass(String id)
+ {
+ return getUIClass (id, null);
+ }
+
+ /**
+ * If a key is requested in #get(key) that has no value, this method
+ * is called before returning <code>null</code>.
+ *
+ * @param msg the error message
+ */
+ protected void getUIError(String msg)
+ {
+ System.err.println ("UIDefaults.getUIError: " + msg);
+ }
+
+ /**
+ * Returns the {@link ComponentUI} for the specified {@link JComponent}.
+ *
+ * @param target the component for which the ComponentUI is requested
+ *
+ * @return the {@link ComponentUI} for the specified {@link JComponent}
+ */
+ public ComponentUI getUI(JComponent target)
+ {
+ String classId = target.getUIClassID ();
+ Class cls = getUIClass (classId);
+ if (cls == null)
+ {
+ getUIError ("failed to locate UI class:" + classId);
+ return null;
+ }
+
+ Method factory;
+
+ try
+ {
+ factory = cls.getMethod ("createUI", new Class[] { JComponent.class } );
+ }
+ catch (NoSuchMethodException nme)
+ {
+ getUIError ("failed to locate createUI method on " + cls.toString ());
+ return null;
+ }
+
+ try
+ {
+ return (ComponentUI) factory.invoke (null, new Object[] { target });
+ }
+ catch (java.lang.reflect.InvocationTargetException ite)
+ {
+ getUIError ("InvocationTargetException ("+ ite.getTargetException()
+ +") calling createUI(...) on " + cls.toString ());
+ return null;
+ }
+ catch (Exception e)
+ {
+ getUIError ("exception calling createUI(...) on " + cls.toString ());
+ return null;
+ }
+ }
+
+ /**
+ * Adds a {@link PropertyChangeListener} to this UIDefaults map.
+ * Registered PropertyChangeListener are notified when values
+ * are beeing put into this UIDefaults map.
+ *
+ * @param listener the PropertyChangeListener to add
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ propertyChangeSupport.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Removes a PropertyChangeListener from this UIDefaults map.
+ *
+ * @param listener the PropertyChangeListener to remove
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ propertyChangeSupport.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Returns an array of all registered PropertyChangeListeners.
+ *
+ * @return all registered PropertyChangeListeners
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners()
+ {
+ return propertyChangeSupport.getPropertyChangeListeners();
+ }
+
+ /**
+ * Fires a PropertyChangeEvent.
+ *
+ * @param property the property name
+ * @param oldValue the old value
+ * @param newValue the new value
+ */
+ protected void firePropertyChange(String property,
+ Object oldValue, Object newValue)
+ {
+ propertyChangeSupport.firePropertyChange(property, oldValue, newValue);
+ }
+
+ /**
+ * Adds a ResourceBundle for localized values.
+ *
+ * @param name the name of the ResourceBundle to add
+ */
+ public void addResourceBundle(String name)
+ {
+ bundles.addFirst(name);
+ }
+
+ /**
+ * Removes a ResourceBundle.
+ *
+ * @param name the name of the ResourceBundle to remove
+ */
+ public void removeResourceBundle(String name)
+ {
+ bundles.remove(name);
+ }
+
+ /**
+ * Sets the current locale to <code>loc</code>.
+ *
+ * @param loc the Locale to be set
+ */
+ public void setDefaultLocale(Locale loc)
+ {
+ defaultLocale = loc;
+ }
+
+ /**
+ * Returns the current default locale.
+ *
+ * @return the current default locale
+ */
+ public Locale getDefaultLocale()
+ {
+ return defaultLocale;
+ }
+}
diff --git a/libjava/classpath/javax/swing/UIManager.java b/libjava/classpath/javax/swing/UIManager.java
new file mode 100644
index 000000000..05a3fceda
--- /dev/null
+++ b/libjava/classpath/javax/swing/UIManager.java
@@ -0,0 +1,948 @@
+/* UIManager.java --
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Insets;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Locale;
+
+import javax.swing.border.Border;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+
+/**
+ * Manages the current {@link LookAndFeel} and any auxiliary {@link LookAndFeel}
+ * instances.
+ */
+public class UIManager implements Serializable
+{
+ /**
+ * Represents the basic information about a {@link LookAndFeel} (LAF), so
+ * that a list of installed LAFs can be presented without actually loading
+ * the LAF class(es).
+ */
+ public static class LookAndFeelInfo
+ {
+ String name, clazz;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param name the look and feel name.
+ * @param clazz the look and feel class name.
+ */
+ public LookAndFeelInfo(String name,
+ String clazz)
+ {
+ this.name = name;
+ this.clazz = clazz;
+ }
+
+ /**
+ * Returns the name of the look and feel.
+ *
+ * @return The name of the look and feel.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Returns the fully qualified class name for the {@link LookAndFeel}.
+ *
+ * @return The fully qualified class name for the {@link LookAndFeel}.
+ */
+ public String getClassName()
+ {
+ return clazz;
+ }
+
+ /**
+ * Returns a String representation of the LookAndFeelInfo object.
+ *
+ * @return a String representation of the LookAndFeelInfo object
+ */
+ public String toString()
+ {
+ CPStringBuilder s = new CPStringBuilder();
+ s.append(getClass().getName());
+ s.append('[');
+ s.append(getName());
+ s.append(' ');
+ s.append(getClassName());
+ s.append(']');
+ return s.toString();
+ }
+ }
+
+ /**
+ * A UIDefaults subclass that multiplexes between itself and a 'fallback'
+ * UIDefaults instance. This is used to protect the L&F UIDefaults from beeing
+ * overwritten by applications.
+ */
+ private static class MultiplexUIDefaults
+ extends UIDefaults
+ {
+ private class MultiplexEnumeration
+ implements Enumeration
+ {
+ Enumeration[] enums;
+ int i;
+ MultiplexEnumeration(Enumeration e1, Enumeration e2)
+ {
+ enums = new Enumeration[]{ e1, e2 };
+ i = 0;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return enums[i].hasMoreElements() || i < enums.length - 1;
+ }
+
+ public Object nextElement()
+ {
+ Object val = enums[i].nextElement();
+ if (! enums[i].hasMoreElements() && i < enums.length - 1)
+ i++;
+ return val;
+ }
+
+ }
+
+ UIDefaults fallback;
+
+ /**
+ * Creates a new <code>MultiplexUIDefaults</code> instance with
+ * <code>d</code> as the fallback defaults.
+ *
+ * @param d the fallback defaults (<code>null</code> not permitted).
+ */
+ MultiplexUIDefaults(UIDefaults d)
+ {
+ if (d == null)
+ throw new NullPointerException();
+ fallback = d;
+ }
+
+ public Object get(Object key)
+ {
+ Object val = super.get(key);
+ if (val == null)
+ val = fallback.get(key);
+ return val;
+ }
+
+ public Object get(Object key, Locale l)
+ {
+ Object val = super.get(key, l);
+ if (val == null)
+ val = fallback.get(key, l);
+ return val;
+ }
+
+ public Object remove(Object key)
+ {
+ Object val = super.remove(key);
+ if (val == null)
+ val = fallback.remove(key);
+ return val;
+ }
+
+ public int size()
+ {
+ return super.size() + fallback.size();
+ }
+
+ public Enumeration keys()
+ {
+ return new MultiplexEnumeration(super.keys(), fallback.keys());
+ }
+
+ public Enumeration elements()
+ {
+ return new MultiplexEnumeration(super.elements(), fallback.elements());
+ }
+ }
+
+ private static final long serialVersionUID = -5547433830339189365L;
+
+ /** The installed look and feel(s). */
+ static LookAndFeelInfo [] installed = {
+ new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel"),
+ new LookAndFeelInfo("GNU", "gnu.javax.swing.plaf.gnu.GNULookAndFeel")
+ };
+
+ /** The installed auxiliary look and feels. */
+ static LookAndFeel[] auxLookAndFeels;
+
+ /** The current look and feel. */
+ static LookAndFeel currentLookAndFeel;
+
+ static MultiplexUIDefaults currentUIDefaults;
+
+ static UIDefaults lookAndFeelDefaults;
+
+ /** Property change listener mechanism. */
+ static PropertyChangeSupport listeners
+ = new PropertyChangeSupport(UIManager.class);
+
+ static
+ {
+ String defaultlaf = System.getProperty("swing.defaultlaf");
+ try
+ {
+ if (defaultlaf != null)
+ {
+ setLookAndFeel(defaultlaf);
+ }
+ else
+ {
+ setLookAndFeel(new MetalLookAndFeel());
+ }
+ }
+ catch (Exception ex)
+ {
+ System.err.println("cannot initialize Look and Feel: " + defaultlaf);
+ System.err.println("error: " + ex.toString());
+ ex.printStackTrace();
+ System.err.println("falling back to Metal Look and Feel");
+ try
+ {
+ setLookAndFeel(new MetalLookAndFeel());
+ }
+ catch (Exception ex2)
+ {
+ throw (Error) new AssertionError("There must be no problem installing"
+ + " the MetalLookAndFeel.")
+ .initCause(ex2);
+ }
+ }
+ }
+
+ /**
+ * Creates a new instance of the <code>UIManager</code>. There is no need
+ * to construct an instance of this class, since all methods are static.
+ */
+ public UIManager()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Add a <code>PropertyChangeListener</code> to the listener list.
+ *
+ * @param listener the listener to add
+ */
+ public static void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ listeners.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Remove a <code>PropertyChangeListener</code> from the listener list.
+ *
+ * @param listener the listener to remove
+ */
+ public static void removePropertyChangeListener(PropertyChangeListener
+ listener)
+ {
+ listeners.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Returns an array of all added <code>PropertyChangeListener</code> objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public static PropertyChangeListener[] getPropertyChangeListeners()
+ {
+ return listeners.getPropertyChangeListeners();
+ }
+
+ /**
+ * Add a {@link LookAndFeel} to the list of auxiliary look and feels.
+ *
+ * @param laf the auxiliary look and feel (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>laf</code> is <code>null</code>.
+ *
+ * @see #getAuxiliaryLookAndFeels()
+ */
+ public static void addAuxiliaryLookAndFeel(LookAndFeel laf)
+ {
+ if (laf == null)
+ throw new NullPointerException("Null 'laf' argument.");
+ if (auxLookAndFeels == null)
+ {
+ auxLookAndFeels = new LookAndFeel[1];
+ auxLookAndFeels[0] = laf;
+ return;
+ }
+
+ LookAndFeel[] temp = new LookAndFeel[auxLookAndFeels.length + 1];
+ System.arraycopy(auxLookAndFeels, 0, temp, 0, auxLookAndFeels.length);
+ auxLookAndFeels = temp;
+ auxLookAndFeels[auxLookAndFeels.length - 1] = laf;
+ }
+
+ /**
+ * Removes a {@link LookAndFeel} (LAF) from the list of auxiliary LAFs.
+ *
+ * @param laf the LAF to remove.
+ *
+ * @return <code>true</code> if the LAF was removed, and <code>false</code>
+ * otherwise.
+ */
+ public static boolean removeAuxiliaryLookAndFeel(LookAndFeel laf)
+ {
+ if (auxLookAndFeels == null)
+ return false;
+ int count = auxLookAndFeels.length;
+ if (count == 1 && auxLookAndFeels[0] == laf)
+ {
+ auxLookAndFeels = null;
+ return true;
+ }
+ for (int i = 0; i < count; i++)
+ {
+ if (auxLookAndFeels[i] == laf)
+ {
+ LookAndFeel[] temp = new LookAndFeel[auxLookAndFeels.length - 1];
+ if (i == 0)
+ {
+ System.arraycopy(auxLookAndFeels, 1, temp, 0, count - 1);
+ }
+ else if (i == count - 1)
+ {
+ System.arraycopy(auxLookAndFeels, 0, temp, 0, count - 1);
+ }
+ else
+ {
+ System.arraycopy(auxLookAndFeels, 0, temp, 0, i);
+ System.arraycopy(auxLookAndFeels, i + 1, temp, i,
+ count - i - 1);
+ }
+ auxLookAndFeels = temp;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns an array (possibly <code>null</code>) containing the auxiliary
+ * {@link LookAndFeel}s that are in use. These are used by the
+ * {@link javax.swing.plaf.multi.MultiLookAndFeel} class.
+ *
+ * @return The auxiliary look and feels (possibly <code>null</code>).
+ *
+ * @see #addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+ public static LookAndFeel[] getAuxiliaryLookAndFeels()
+ {
+ return auxLookAndFeels;
+ }
+
+ /**
+ * Returns an object from the {@link UIDefaults} table for the current
+ * {@link LookAndFeel}.
+ *
+ * @param key the key.
+ *
+ * @return The object.
+ */
+ public static Object get(Object key)
+ {
+ return getDefaults().get(key);
+ }
+
+ /**
+ * Returns an object from the {@link UIDefaults} table for the current
+ * {@link LookAndFeel}.
+ *
+ * @param key the key.
+ *
+ * @return The object.
+ *
+ * @since 1.4
+ */
+ public static Object get(Object key, Locale locale)
+ {
+ return getDefaults().get(key, locale);
+ }
+
+ /**
+ * Returns a boolean value from the defaults table. If there is no value
+ * for the specified key, or the value is not an instance of {@link Boolean},
+ * this method returns <code>false</code>.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The boolean value associated with the specified key.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
+ * @since 1.4
+ */
+ public static boolean getBoolean(Object key)
+ {
+ Object value = get(key);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ return false;
+ }
+
+ /**
+ * Returns a boolean value from the defaults table. If there is no value
+ * for the specified key, or the value is not an instance of {@link Boolean},
+ * this method returns <code>false</code>.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The boolean value associated with the specified key.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
+ * @since 1.4
+ */
+ public static boolean getBoolean(Object key, Locale locale)
+ {
+ Object value = get(key, locale);
+ if (value instanceof Boolean)
+ return ((Boolean) value).booleanValue();
+ return false;
+ }
+
+ /**
+ * Returns a border from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The border associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ */
+ public static Border getBorder(Object key)
+ {
+ Object value = get(key);
+ if (value instanceof Border)
+ return (Border) value;
+ return null;
+ }
+
+ /**
+ * Returns a border from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The border associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
+ * @since 1.4
+ */
+ public static Border getBorder(Object key, Locale locale)
+ {
+ Object value = get(key, locale);
+ if (value instanceof Border)
+ return (Border) value;
+ return null;
+ }
+
+ /**
+ * Returns a drawing color from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The color associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ */
+ public static Color getColor(Object key)
+ {
+ Object value = get(key);
+ if (value instanceof Color)
+ return (Color) value;
+ return null;
+ }
+
+ /**
+ * Returns a drawing color from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The color associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
+ * @since 1.4
+ */
+ public static Color getColor(Object key, Locale locale)
+ {
+ Object value = get(key, locale);
+ if (value instanceof Color)
+ return (Color) value;
+ return null;
+ }
+
+ /**
+ * The fully qualified class name of the cross platform (Metal) look and feel.
+ * This string can be passed to Class.forName()
+ *
+ * @return <code>"javax.swing.plaf.metal.MetalLookAndFeel"</code>
+ */
+ public static String getCrossPlatformLookAndFeelClassName()
+ {
+ return "javax.swing.plaf.metal.MetalLookAndFeel";
+ }
+
+ /**
+ * Returns the default values for this look and feel.
+ *
+ * @return The {@link UIDefaults} for the current {@link LookAndFeel}.
+ */
+ public static UIDefaults getDefaults()
+ {
+ if (currentUIDefaults == null)
+ currentUIDefaults = new MultiplexUIDefaults(new UIDefaults());
+ return currentUIDefaults;
+ }
+
+ /**
+ * Returns a dimension from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The color associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ */
+ public static Dimension getDimension(Object key)
+ {
+ Object value = get(key);
+ if (value instanceof Dimension)
+ return (Dimension) value;
+ return null;
+ }
+
+ /**
+ * Returns a dimension from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The color associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ * @since 1.4
+ */
+ public static Dimension getDimension(Object key, Locale locale)
+ {
+ Object value = get(key, locale);
+ if (value instanceof Dimension)
+ return (Dimension) value;
+ return null;
+ }
+
+ /**
+ * Retrieves a font from the defaults table of the current
+ * LookAndFeel.
+ *
+ * @param key an Object that specifies the font. Typically,
+ * this is a String such as
+ * <code>TitledBorder.font</code>.
+ *
+ * @return The font associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ */
+ public static Font getFont(Object key)
+ {
+ Object value = get(key);
+ if (value instanceof Font)
+ return (Font) value;
+ return null;
+ }
+
+ /**
+ * Retrieves a font from the defaults table of the current
+ * LookAndFeel.
+ *
+ * @param key an Object that specifies the font. Typically,
+ * this is a String such as
+ * <code>TitledBorder.font</code>.
+ * @param locale the locale.
+ *
+ * @return The font associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ *
+ * @since 1.4
+ */
+ public static Font getFont(Object key, Locale locale)
+ {
+ Object value = get(key, locale);
+ if (value instanceof Font)
+ return (Font) value;
+ return null;
+ }
+
+ /**
+ * Returns an icon from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The icon associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ */
+ public static Icon getIcon(Object key)
+ {
+ Object value = get(key);
+ if (value instanceof Icon)
+ return (Icon) value;
+ return null;
+ }
+
+ /**
+ * Returns an icon from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The icon associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ * @since 1.4
+ */
+ public static Icon getIcon(Object key, Locale locale)
+ {
+ Object value = get(key, locale);
+ if (value instanceof Icon)
+ return (Icon) value;
+ return null;
+ }
+
+ /**
+ * Returns an Insets object from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The insets associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ */
+ public static Insets getInsets(Object key)
+ {
+ Object o = get(key);
+ if (o instanceof Insets)
+ return (Insets) o;
+ else
+ return null;
+ }
+
+ /**
+ * Returns an Insets object from the defaults table.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The insets associated with the given key, or <code>null</code>.
+ *
+ * @throws NullPointerException if <code>key</code> is <code>null</code>.
+ * @since 1.4
+ */
+ public static Insets getInsets(Object key, Locale locale)
+ {
+ Object o = get(key, locale);
+ if (o instanceof Insets)
+ return (Insets) o;
+ else
+ return null;
+ }
+
+ /**
+ * Returns an array containing information about the {@link LookAndFeel}s
+ * that are installed.
+ *
+ * @return A list of the look and feels that are available (installed).
+ */
+ public static LookAndFeelInfo[] getInstalledLookAndFeels()
+ {
+ return installed;
+ }
+
+ /**
+ * Returns the integer value of the {@link Integer} associated with the
+ * given key. If there is no value, or the value is not an instance of
+ * {@link Integer}, this method returns 0.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The integer value associated with the given key, or 0.
+ */
+ public static int getInt(Object key)
+ {
+ Object x = get(key);
+ if (x instanceof Integer)
+ return ((Integer) x).intValue();
+ return 0;
+ }
+
+ /**
+ * Returns the integer value of the {@link Integer} associated with the
+ * given key. If there is no value, or the value is not an instance of
+ * {@link Integer}, this method returns 0.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The integer value associated with the given key, or 0.
+ *
+ * @since 1.4
+ */
+ public static int getInt(Object key, Locale locale)
+ {
+ Object x = get(key, locale);
+ if (x instanceof Integer)
+ return ((Integer) x).intValue();
+ return 0;
+ }
+
+ /**
+ * Returns the current look and feel (which may be <code>null</code>).
+ *
+ * @return The current look and feel.
+ *
+ * @see #setLookAndFeel(LookAndFeel)
+ */
+ public static LookAndFeel getLookAndFeel()
+ {
+ return currentLookAndFeel;
+ }
+
+ /**
+ * Returns the <code>UIDefaults</code> table of the currently active
+ * look and feel.
+ *
+ * @return The {@link UIDefaults} for the current {@link LookAndFeel}.
+ */
+ public static UIDefaults getLookAndFeelDefaults()
+ {
+ return lookAndFeelDefaults;
+ }
+
+ /**
+ * Returns the {@link String} associated with the given key. If the value
+ * is not a {@link String}, this method returns <code>null</code>.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ *
+ * @return The string associated with the given key, or <code>null</code>.
+ */
+ public static String getString(Object key)
+ {
+ Object s = get(key);
+ if (s instanceof String)
+ return (String) s;
+ return null;
+ }
+
+ /**
+ * Returns the {@link String} associated with the given key. If the value
+ * is not a {@link String}, this method returns <code>null</code>.
+ *
+ * @param key the key (<code>null</code> not permitted).
+ * @param locale the locale.
+ *
+ * @return The string associated with the given key, or <code>null</code>.
+ *
+ * @since 1.4
+ */
+ public static String getString(Object key, Locale locale)
+ {
+ Object s = get(key, locale);
+ if (s instanceof String)
+ return (String) s;
+ return null;
+ }
+
+ /**
+ * Returns the name of the {@link LookAndFeel} class that implements the
+ * native systems look and feel if there is one, otherwise the name
+ * of the default cross platform LookAndFeel class.
+ *
+ * @return The fully qualified class name for the system look and feel.
+ *
+ * @see #getCrossPlatformLookAndFeelClassName()
+ */
+ public static String getSystemLookAndFeelClassName()
+ {
+ return getCrossPlatformLookAndFeelClassName();
+ }
+
+ /**
+ * Returns UI delegate from the current {@link LookAndFeel} that renders the
+ * target component.
+ *
+ * @param target the target component.
+ */
+ public static ComponentUI getUI(JComponent target)
+ {
+ return getDefaults().getUI(target);
+ }
+
+ /**
+ * Creates a new look and feel and adds it to the current array.
+ *
+ * @param name the look and feel name.
+ * @param className the fully qualified name of the class that implements the
+ * look and feel.
+ */
+ public static void installLookAndFeel(String name, String className)
+ {
+ installLookAndFeel(new LookAndFeelInfo(name, className));
+ }
+
+ /**
+ * Adds the specified look and feel to the current array and then calls
+ * setInstalledLookAndFeels(javax.swing.UIManager.LookAndFeelInfo[]).
+ */
+ public static void installLookAndFeel(LookAndFeelInfo info)
+ {
+ LookAndFeelInfo[] newInstalled = new LookAndFeelInfo[installed.length + 1];
+ System.arraycopy(installed, 0, newInstalled, 0, installed.length);
+ newInstalled[newInstalled.length - 1] = info;
+ setInstalledLookAndFeels(newInstalled);
+ }
+
+ /**
+ * Stores an object in the defaults table.
+ *
+ * @param key the key.
+ * @param value the value.
+ */
+ public static Object put(Object key, Object value)
+ {
+ return getDefaults().put(key, value);
+ }
+
+ /**
+ * Replaces the current array of installed LookAndFeelInfos.
+ */
+ public static void setInstalledLookAndFeels(UIManager.LookAndFeelInfo[] infos)
+ {
+ installed = infos;
+ }
+
+ /**
+ * Sets the current {@link LookAndFeel}.
+ *
+ * @param newLookAndFeel the new look and feel (<code>null</code> permitted).
+ *
+ * @throws UnsupportedLookAndFeelException if the look and feel is not
+ * supported on the current platform.
+ *
+ * @see LookAndFeel#isSupportedLookAndFeel()
+ */
+ public static void setLookAndFeel(LookAndFeel newLookAndFeel)
+ throws UnsupportedLookAndFeelException
+ {
+ if (newLookAndFeel != null && ! newLookAndFeel.isSupportedLookAndFeel())
+ throw new UnsupportedLookAndFeelException(newLookAndFeel.getName()
+ + " not supported on this platform");
+ LookAndFeel oldLookAndFeel = currentLookAndFeel;
+ if (oldLookAndFeel != null)
+ oldLookAndFeel.uninitialize();
+
+ // Set the current default look and feel using a LookAndFeel object.
+ currentLookAndFeel = newLookAndFeel;
+ if (newLookAndFeel != null)
+ {
+ newLookAndFeel.initialize();
+ lookAndFeelDefaults = newLookAndFeel.getDefaults();
+ if (currentUIDefaults == null)
+ currentUIDefaults =
+ new MultiplexUIDefaults(lookAndFeelDefaults);
+ else
+ currentUIDefaults.fallback = lookAndFeelDefaults;
+ }
+ else
+ {
+ currentUIDefaults = null;
+ }
+ listeners.firePropertyChange("lookAndFeel", oldLookAndFeel, newLookAndFeel);
+ //revalidate();
+ //repaint();
+ }
+
+ /**
+ * Set the current default look and feel using a class name.
+ *
+ * @param className the look and feel class name.
+ *
+ * @throws UnsupportedLookAndFeelException if the look and feel is not
+ * supported on the current platform.
+ *
+ * @see LookAndFeel#isSupportedLookAndFeel()
+ */
+ public static void setLookAndFeel(String className)
+ throws ClassNotFoundException, InstantiationException, IllegalAccessException,
+ UnsupportedLookAndFeelException
+ {
+ Class c = Class.forName(className, true,
+ Thread.currentThread().getContextClassLoader());
+ LookAndFeel a = (LookAndFeel) c.newInstance(); // throws class-cast-exception
+ setLookAndFeel(a);
+ }
+}
diff --git a/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java b/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java
new file mode 100644
index 000000000..74c6ac8bd
--- /dev/null
+++ b/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java
@@ -0,0 +1,57 @@
+/* UnsupportedLookAndFeelException.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown by the {@link UIManager#setLookAndFeel(LookAndFeel)} method when the
+ * specified look and feel is not supported on the current platform.
+ *
+ * @see LookAndFeel#isSupportedLookAndFeel()
+ */
+public class UnsupportedLookAndFeelException extends Exception
+{
+ /**
+ * Creates a new exception instance with the specified message.
+ *
+ * @param s the exception message.
+ */
+ public UnsupportedLookAndFeelException(String s)
+ {
+ super(s);
+ }
+}
diff --git a/libjava/classpath/javax/swing/ViewportLayout.java b/libjava/classpath/javax/swing/ViewportLayout.java
new file mode 100644
index 000000000..02f9626ad
--- /dev/null
+++ b/libjava/classpath/javax/swing/ViewportLayout.java
@@ -0,0 +1,202 @@
+/* ViewportLayout.java --
+ Copyright (C) 2002, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+/**
+ * The default layout for {@link JViewport}. The viewport makes its view the
+ * same size as itself, but not smaller than its minimum size.
+ *
+ * If the port extends extends into space <em>past</em> the edge of the view,
+ * this layout manager moves the port up or to the left, in view space, by the
+ * amount of empty space (keep the lower and right edges lined up).
+ *
+ * @author Andrew Selkirk
+ * @author Graydon Hoare
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+public class ViewportLayout implements LayoutManager, Serializable
+{
+ private static final long serialVersionUID = -788225906076097229L;
+
+ public ViewportLayout()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * The method is not used with this manager.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * The method is not used with this manager.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Get the preferred layout size. If the view implements
+ * {@link Scrollable}, this method returns
+ * {@link Scrollable#getPreferredScrollableViewportSize}.
+ * Otherwise, it returns {@link Component#getPreferredSize()}.
+ *
+ * @return the preferred layout size, as described about.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ JViewport vp = (JViewport)parent;
+ Component view = vp.getView();
+ if (view != null)
+ {
+ if (view instanceof Scrollable)
+ return ((Scrollable)view).getPreferredScrollableViewportSize();
+ return view.getPreferredSize();
+ }
+ else
+ return new Dimension();
+ }
+
+ /**
+ * Get the minimum layout size. Normally this method returns the value,
+ * returned by the view method {@link Component#getMinimumSize()}.
+ *
+ * If the view is not set, the zero size is returned.
+ *
+ * @param parent the viewport
+ * @return the minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ // These values have been determined by the Mauve test for this method.
+ return new Dimension(4, 4);
+ }
+
+ /**
+ * Layout the view and viewport to respect the following rules. These are not
+ * precisely the rules described in sun's javadocs, but they are the rules
+ * which sun's swing implementation follows, if you watch its behavior:
+ * <ol>
+ * <li>If the port is smaller than the view, leave the view at its current
+ * size.</li>
+ * <li>If the view is smaller than the port, the view is top aligned.</li>
+ * <li>If the view tracks the port size, the view position is always zero and
+ * the size equal to the viewport size</li>
+ * <li>In {@link JViewport#setViewSize(Dimension)}, the view size is never
+ * set smaller that its minimum size.</li>
+ * </ol>
+ *
+ * @see JViewport#getViewSize
+ * @see JViewport#setViewSize
+ * @see JViewport#getViewPosition
+ * @see JViewport#setViewPosition
+ */
+ public void layoutContainer(Container parent)
+ {
+ // The way to interpret this function is basically to ignore the names
+ // of methods it calls, and focus on the variable names here. getViewRect
+ // doesn't, for example, return the view; it returns the port bounds in
+ // view space. Likwise setViewPosition doesn't reposition the view; it
+ // positions the port, in view coordinates.
+
+ JViewport port = (JViewport) parent;
+ Component view = port.getView();
+
+ if (view == null)
+ return;
+
+ // These dimensions and positions are in *view space*. Do not mix
+ // variables in here from port space (eg. parent.getBounds()). This
+ // function should be entirely in view space, because the methods on
+ // the viewport require inputs in view space.
+
+ Rectangle portBounds = port.getViewRect();
+ Dimension viewPref = new Dimension(view.getPreferredSize());
+
+ Point portLowerRight = new Point(portBounds.x + portBounds.width,
+ portBounds.y + portBounds.height);
+
+ // vertical implementation of the above rules
+ if (view instanceof Scrollable)
+ {
+ Scrollable sView = (Scrollable) view;
+
+ // If the view size matches viewport size, the port offset can
+ // only be zero.
+ if (sView.getScrollableTracksViewportWidth())
+ {
+ viewPref.width = portBounds.width;
+ portBounds.x = 0;
+ }
+ if (sView.getScrollableTracksViewportHeight())
+ {
+ viewPref.height = portBounds.height;
+ portBounds.y = 0;
+ }
+ }
+
+ if (viewPref.width < portBounds.width)
+ viewPref.width = portBounds.width;
+ if (viewPref.height < portBounds.height)
+ viewPref.height = portBounds.height;
+
+ // If the view is larger than the port, the port is top and right
+ // aligned.
+ if (portLowerRight.x > viewPref.width)
+ portBounds.x = 0;
+
+ if (portLowerRight.y > viewPref.height)
+ portBounds.y = 0;
+
+ port.setViewSize(viewPref);
+ port.setViewPosition(portBounds.getLocation());
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/WindowConstants.java b/libjava/classpath/javax/swing/WindowConstants.java
new file mode 100644
index 000000000..63ec2cbb5
--- /dev/null
+++ b/libjava/classpath/javax/swing/WindowConstants.java
@@ -0,0 +1,79 @@
+/* WindowConstants.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Defines some constants that are used in Swing's top-level containers. See
+ * the following methods:
+ * <ul>
+ * <li>{@link JFrame#setDefaultCloseOperation(int)};</li>
+ * <li>{@link JInternalFrame#setDefaultCloseOperation(int)};</li>
+ * <li>{@link JDialog#setDefaultCloseOperation(int)};</li>
+ * </ul>
+ *
+ * @author Andrew Selkirk
+ */
+public interface WindowConstants
+{
+ /**
+ * Do nothing when the container is closed.
+ */
+ int DO_NOTHING_ON_CLOSE = 0;
+
+ /**
+ * Hide the container when it is closed.
+ */
+ int HIDE_ON_CLOSE = 1;
+
+ /**
+ * Dispose the container when it is closed.
+ *
+ * @see Window#dispose()
+ */
+ int DISPOSE_ON_CLOSE = 2;
+
+ /**
+ * Exit the application when the container is closed.
+ *
+ * @see System#exit(int)
+ *
+ * @since 1.4
+ */
+ int EXIT_ON_CLOSE = 3;
+
+}
diff --git a/libjava/classpath/javax/swing/border/AbstractBorder.java b/libjava/classpath/javax/swing/border/AbstractBorder.java
new file mode 100644
index 000000000..f75bcbbc2
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/AbstractBorder.java
@@ -0,0 +1,199 @@
+/* AbstractBorder.java --
+ Copyright (C) 2003, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+
+/**
+ * An invisible zero-width border, serving as a base class for
+ * implementing more interesting borders.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public abstract class AbstractBorder implements Border, Serializable
+{
+ static final long serialVersionUID = -545885975315191844L;
+
+ /**
+ * Constructs a new AbstractBorder.
+ */
+ public AbstractBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Performs nothing, because the default implementation provided by
+ * this class is an invisible, zero-width border. Subclasses will
+ * likely want to override this method, but they are not required
+ * to do so.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ // A previous version of Classpath had emitted a warning when
+ // this method was called. The warning was removed because it is
+ // perfectly legal for a subclass to not override the paintBorder
+ // method. An example would be EmptyBorder.
+ }
+
+ /**
+ * Returns the insets required for drawing this border around the specified
+ * component.
+ *
+ * @param c the component that the border applies to (ignored here,
+ * subclasses may use it).
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge, which is zero
+ * for the default implementation provided by AbstractButton.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(0, 0, 0, 0);
+ }
+
+ /**
+ * Returns the insets required for drawing this border around the specified
+ * component. The default implementation provided here sets the
+ * <code>left</code>, <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields of the passed <code>insets</code> parameter to
+ * zero.
+ *
+ * @param c the component that the border applies to (ignored here,
+ * subclasses may use it).
+ * @param insets an instance that will be overwritten and returned as the
+ * result (<code>null</code> not permitted).
+ *
+ * @return The border insets (the same object that was passed as the
+ * <code>insets</code> argument).
+ *
+ * @see #getBorderInsets(Component)
+ *
+ * @throws NullPointerException if <code>insets</code> is <code>null</code>.
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ insets.left = insets.right = insets.top = insets.bottom = 0;
+ return insets;
+ }
+
+ /**
+ * Determines whether or not this border is opaque. An opaque border
+ * fills every pixel in its area when painting. Partially
+ * translucent borders must return <code>false</code>, or ugly
+ * artifacts can appear on screen. The default implementation
+ * provided here always returns <code>false</code>.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isBorderOpaque()
+ {
+ return false;
+ }
+
+ /**
+ * Returns a rectangle that covers the specified area minus the insets
+ * required to draw this border. Components that wish to determine an area
+ * into which they can safely draw without intersecting with a border might
+ * want to use this helper method.
+ *
+ * @param c the component in the center of this border.
+ * @param x the horizontal position of the border.
+ * @param y the vertical position of the border.
+ * @param width the width of the available area for the border.
+ * @param height the height of the available area for the border.
+ *
+ * @return The interior rectangle.
+ */
+ public Rectangle getInteriorRectangle(Component c, int x, int y, int width,
+ int height)
+ {
+ return getInteriorRectangle(c, this, x, y, width, height);
+ }
+
+ /**
+ * Returns a rectangle that covers the specified area minus the insets
+ * required to draw the specified border (if the border is <code>null</code>,
+ * zero insets are assumed). Components that wish to determine an area into
+ * which they can safely draw without intersecting with a border might want
+ * to use this helper method.
+ *
+ * @param c the component in the center of this border.
+ * @param b the border (<code>null</code> permitted).
+ * @param x the horizontal position of the border.
+ * @param y the vertical position of the border.
+ * @param width the width of the available area for the border.
+ * @param height the height of the available area for the border.
+ *
+ * @return The interior rectangle.
+ */
+ public static Rectangle getInteriorRectangle(Component c, Border b, int x,
+ int y, int width, int height)
+ {
+ Insets borderInsets;
+
+ if (b != null)
+ {
+ borderInsets = b.getBorderInsets(c);
+ x += borderInsets.left;
+ y += borderInsets.top;
+ width -= borderInsets.left + borderInsets.right;
+ height -= borderInsets.top + borderInsets.bottom;
+ }
+
+ return new Rectangle(x, y, width, height);
+ }
+}
diff --git a/libjava/classpath/javax/swing/border/BevelBorder.java b/libjava/classpath/javax/swing/border/BevelBorder.java
new file mode 100644
index 000000000..b91961d66
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/BevelBorder.java
@@ -0,0 +1,584 @@
+/* BevelBorder.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+
+/**
+ * A rectangular, two pixel thick border that causes the enclosed area
+ * to appear as if it was raising out of or lowered into the screen. Some
+ * LookAndFeels use this kind of border for rectangular buttons.
+ *
+ * <p>A BevelBorder has a highlight and a shadow color. In the raised
+ * variant, the highlight color is used for the top and left edges,
+ * and the shadow color is used for the bottom and right edge. For an
+ * image, see the documentation of the individual constructors.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class BevelBorder extends AbstractBorder
+{
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -1034942243356299676L;
+
+
+ /**
+ * Indicates that the BevelBorder looks like if the enclosed area was
+ * raising out of the screen.
+ */
+ public static final int RAISED = 0;
+
+
+ /**
+ * Indicates that the BevelBorder looks like if the enclosed area was
+ * pressed into the screen.
+ */
+ public static final int LOWERED = 1;
+
+
+ /**
+ * The type of this BevelBorder, which is either {@link #RAISED}
+ * or {@link #LOWERED}.
+ */
+ protected int bevelType;
+
+
+ /**
+ * The outer highlight color, or <code>null</code> to indicate that
+ * the color shall be derived from the background of the component
+ * whose border is being painted.
+ */
+ protected Color highlightOuter;
+
+
+ /**
+ * The inner highlight color, or <code>null</code> to indicate that
+ * the color shall be derived from the background of the component
+ * whose border is being painted.
+ */
+ protected Color highlightInner;
+
+
+ /**
+ * The outer shadow color, or <code>null</code> to indicate that the
+ * color shall be derived from the background of the component whose
+ * border is being painted.
+ */
+ protected Color shadowOuter;
+
+
+ /**
+ * The inner shadow color, or <code>null</code> to indicate that the
+ * color shall be derived from the background of the component whose
+ * border is being painted.
+ */
+ protected Color shadowInner;
+
+
+ /**
+ * Constructs a BevelBorder whose colors will be derived from the
+ * background of the enclosed component. The background color is
+ * retrieved each time the border is painted, so a BevelBorder
+ * constructed by this method will automatically reflect a change
+ * to the component&#x2019;s background color.
+ *
+ * <p><img src="doc-files/BevelBorder-1.png" width="500" height="150"
+ * alt="[An illustration showing raised and lowered BevelBorders]" />
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link #RAISED} or {@link #LOWERED}.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has
+ * an unsupported value.
+ */
+ public BevelBorder(int bevelType)
+ {
+ if ((bevelType != RAISED) && (bevelType != LOWERED))
+ throw new IllegalArgumentException();
+
+ this.bevelType = bevelType;
+ }
+
+
+ /**
+ * Constructs a BevelBorder given its appearance type and two colors
+ * for its highlight and shadow.
+ *
+ * <p><img src="doc-files/BevelBorder-2.png" width="500" height="150"
+ * alt="[An illustration showing BevelBorders that were constructed
+ * with this method]" />
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link #RAISED} or {@link #LOWERED}.
+ *
+ * @param highlight the color that will be used for the inner
+ * side of the highlighted edges (top and left if
+ * if <code>bevelType</code> is {@link #RAISED}; bottom
+ * and right otherwise). The color for the outer side
+ * is a brightened version of this color.
+ *
+ * @param shadow the color that will be used for the outer
+ * side of the shadowed edges (bottom and right
+ * if <code>bevelType</code> is {@link #RAISED}; top
+ * and left otherwise). The color for the inner side
+ * is a brightened version of this color.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has
+ * an unsupported value.
+ *
+ * @throws NullPointerException if <code>highlight</code> or
+ * <code>shadow</code> is <code>null</code>.
+ *
+ * @see java.awt.Color#brighter()
+ */
+ public BevelBorder(int bevelType, Color highlight, Color shadow)
+ {
+ this(bevelType,
+ /* highlightOuter */ highlight.brighter(),
+ /* highlightInner */ highlight,
+ /* shadowOuter */ shadow,
+ /* shadowInner */ shadow.brighter());
+ }
+
+
+ /**
+ * Constructs a BevelBorder given its appearance type and all
+ * colors.
+ *
+ * <p><img src="doc-files/BevelBorder-3.png" width="500" height="150"
+ * alt="[An illustration showing BevelBorders that were constructed
+ * with this method]" />
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link #RAISED} or {@link #LOWERED}.
+ *
+ * @param highlightOuter the color that will be used for the outer
+ * side of the highlighted edges (top and left if
+ * <code>bevelType</code> is {@link #RAISED}; bottom and
+ * right otherwise).
+ *
+ * @param highlightInner the color that will be used for the inner
+ * side of the highlighted edges.
+ *
+ * @param shadowOuter the color that will be used for the outer
+ * side of the shadowed edges (bottom and right
+ * if <code>bevelType</code> is {@link #RAISED}; top
+ * and left otherwise).
+ *
+ * @param shadowInner the color that will be used for the inner
+ * side of the shadowed edges.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has
+ * an unsupported value.
+ *
+ * @throws NullPointerException if one of the passed colors
+ * is <code>null</code>.
+ */
+ public BevelBorder(int bevelType,
+ Color highlightOuter, Color highlightInner,
+ Color shadowOuter, Color shadowInner)
+ {
+ this(bevelType); // checks the validity of bevelType
+
+ if ((highlightOuter == null) || (highlightInner == null)
+ || (shadowOuter == null) || (shadowInner == null))
+ throw new NullPointerException();
+
+ this.highlightOuter = highlightOuter;
+ this.highlightInner = highlightInner;
+ this.shadowOuter = shadowOuter;
+ this.shadowInner = shadowInner;
+ }
+
+
+ /**
+ * Paints the border for a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ switch (bevelType)
+ {
+ case RAISED:
+ paintRaisedBevel(c, g, x, y, width, height);
+ break;
+
+ case LOWERED:
+ paintLoweredBevel(c, g, x, y, width, height);
+ break;
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(2, 2, 2, 2);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ insets.left = insets.right = insets.top = insets.bottom = 2;
+ return insets;
+ }
+
+
+ /**
+ * Determines the color that will be used for the outer side of
+ * highlighted edges when painting the border. If a highlight color
+ * has been specified upon constructing the border, that color is
+ * returned. Otherwise, the inner highlight color is brightened.
+ *
+ * @param c the component enclosed by this border.
+ *
+ * @return The color.
+ *
+ * @see #getHighlightInnerColor(java.awt.Component)
+ * @see java.awt.Color#brighter()
+ */
+ public Color getHighlightOuterColor(Component c)
+ {
+ if (highlightOuter != null)
+ return highlightOuter;
+ else
+ return getHighlightInnerColor(c).brighter();
+ }
+
+
+ /**
+ * Determines the color that will be used for the inner side of
+ * highlighted edges when painting the border. If a highlight color
+ * has been specified upon constructing the border, that color is
+ * returned. Otherwise, the background color of the enclosed
+ * component is brightened.
+ *
+ * @param c the component enclosed by this border.
+ *
+ * @return The color.
+ *
+ * @see java.awt.Component#getBackground()
+ * @see java.awt.Color#brighter()
+ */
+ public Color getHighlightInnerColor(Component c)
+ {
+ if (highlightInner != null)
+ return highlightInner;
+ else
+ return c.getBackground().brighter();
+ }
+
+
+ /**
+ * Determines the color that will be used for the inner side of
+ * shadowed edges when painting the border. If a shadow color has
+ * been specified upon constructing the border, that color is
+ * returned. Otherwise, the background color of the enclosed
+ * component is darkened.
+ *
+ * @param c the component enclosed by this border.
+ *
+ * @return The color.
+ *
+ * @see java.awt.Component#getBackground()
+ * @see java.awt.Color#darker()
+ */
+ public Color getShadowInnerColor(Component c)
+ {
+ if (shadowInner != null)
+ return shadowInner;
+ else
+ return c.getBackground().darker();
+ }
+
+
+ /**
+ * Determines the color that will be used for the outer side of
+ * shadowed edges when painting the border. If a shadow color
+ * has been specified upon constructing the border, that color is
+ * returned. Otherwise, the inner shadow color is darkened.
+ *
+ * @param c the component enclosed by this border.
+ *
+ * @return The color.
+ *
+ * @see #getShadowInnerColor(java.awt.Component)
+ * @see java.awt.Color#darker()
+ */
+ public Color getShadowOuterColor(Component c)
+ {
+ if (shadowOuter != null)
+ return shadowOuter;
+ else
+ return getShadowInnerColor(c).darker();
+ }
+
+
+ /**
+ * Returns the color that will be used for the outer side of
+ * highlighted edges when painting the border, or <code>null</code>
+ * if that color will be derived from the background of the enclosed
+ * Component.
+ *
+ * @return The color (possibly <code>null</code>).
+ */
+ public Color getHighlightOuterColor()
+ {
+ return highlightOuter;
+ }
+
+
+ /**
+ * Returns the color that will be used for the inner side of
+ * highlighted edges when painting the border, or <code>null</code>
+ * if that color will be derived from the background of the enclosed
+ * Component.
+ *
+ * @return The color (possibly <code>null</code>).
+ */
+ public Color getHighlightInnerColor()
+ {
+ return highlightInner;
+ }
+
+
+ /**
+ * Returns the color that will be used for the inner side of
+ * shadowed edges when painting the border, or <code>null</code> if
+ * that color will be derived from the background of the enclosed
+ * Component.
+ *
+ * @return The color (possibly <code>null</code>).
+ */
+ public Color getShadowInnerColor()
+ {
+ return shadowInner;
+ }
+
+
+ /**
+ * Returns the color that will be used for the outer side of
+ * shadowed edges when painting the border, or <code>null</code> if
+ * that color will be derived from the background of the enclosed
+ * Component.
+ *
+ * @return The color (possibly <code>null</code>).
+ */
+ public Color getShadowOuterColor()
+ {
+ return shadowOuter;
+ }
+
+
+ /**
+ * Returns the appearance of this border, which is either {@link
+ * #RAISED} or {@link #LOWERED}.
+ *
+ * @return The bevel type ({@link #RAISED} or {@link #LOWERED}).
+ */
+ public int getBevelType()
+ {
+ return bevelType;
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * <p>If the border colors are derived from the background color of
+ * the enclosed component, the result is <code>true</code> because
+ * the derivation method always returns opaque colors. Otherwise,
+ * the result depends on the opacity of the individual colors.
+ *
+ * @return <code>true</code> if the border is fully opaque, or
+ * <code>false</code> if some pixels of the background
+ * can shine through the border.
+ */
+ public boolean isBorderOpaque()
+ {
+ /* If the colors are to be drived from the enclosed Component's
+ * background color, the border is guaranteed to be fully opaque
+ * because Color.brighten() and Color.darken() always return an
+ * opaque color.
+ */
+ return
+ ((highlightOuter == null) || (highlightOuter.getAlpha() == 255))
+ && ((highlightInner == null) || (highlightInner.getAlpha() == 255))
+ && ((shadowInner == null) || (shadowInner.getAlpha() == 255))
+ && ((shadowOuter == null) || (shadowOuter.getAlpha() == 255));
+ }
+
+
+ /**
+ * Paints a raised bevel border around a component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ protected void paintRaisedBevel(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ paintBevel(g, x, y, width, height,
+ getHighlightOuterColor(c), getHighlightInnerColor(c),
+ getShadowInnerColor(c), getShadowOuterColor(c));
+ }
+
+
+ /**
+ * Paints a lowered bevel border around a component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ protected void paintLoweredBevel(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ paintBevel(g, x, y, width, height,
+ getShadowInnerColor(c), getShadowOuterColor(c),
+ getHighlightInnerColor(c), getHighlightOuterColor(c));
+ }
+
+
+ /**
+ * Paints a two-pixel bevel in four colors.
+ *
+ * <pre>
+ * ++++++++++++
+ * +..........# + = color a
+ * +. X# . = color b
+ * +. X# X = color c
+ * +.XXXXXXXXX# # = color d
+ * ############</pre>
+ *
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ * @param a the color for the outer side of the top and left edges.
+ * @param b the color for the inner side of the top and left edges.
+ * @param c the color for the inner side of the bottom and right edges.
+ * @param d the color for the outer side of the bottom and right edges.
+ */
+ private static void paintBevel(Graphics g,
+ int x, int y, int width, int height,
+ Color a, Color b, Color c, Color d)
+ {
+ Color oldColor;
+
+ oldColor = g.getColor();
+ g.translate(x, y);
+ width = width - 1;
+ height = height - 1;
+
+ try
+ {
+ /* To understand this code, it might be helpful to look at the
+ * images that are included with the JavaDoc. They are located
+ * in the "doc-files" subdirectory.
+ */
+ g.setColor(a);
+ g.drawLine(0, 0, width, 0); // a, horizontal
+ g.drawLine(0, 1, 0, height); // a, vertical
+
+ g.setColor(b);
+ g.drawLine(1, 1, width - 1, 1); // b, horizontal
+ g.drawLine(1, 2, 1, height - 1); // b, vertical
+
+ g.setColor(c);
+ g.drawLine(2, height - 1, width - 1, height - 1); // c, horizontal
+ g.drawLine(width - 1, 2, width - 1, height - 2); // c, vertical
+
+ g.setColor(d);
+ g.drawLine(1, height, width, height); // d, horizontal
+ g.drawLine(width, 1, width, height - 1); // d, vertical
+ }
+ finally
+ {
+ g.translate(-x, -y);
+ g.setColor(oldColor);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/border/Border.java b/libjava/classpath/javax/swing/border/Border.java
new file mode 100644
index 000000000..da25832ad
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/Border.java
@@ -0,0 +1,103 @@
+/* Border.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+
+/**
+ * An public interface for decorative or spacing borders around a Component.
+ *
+ * <p>To reduce memory consumption, several Components may share a
+ * single Border instance. {@link javax.swing.BorderFactory} is a
+ * factory class for producing commonly used shared borders.
+ *
+ * @see javax.swing.BorderFactory
+ * @see EmptyBorder
+ * @see CompoundBorder
+ * @see BevelBorder
+ * @see EtchedBorder
+ * @see LineBorder
+ * @see MatteBorder
+ * @see SoftBevelBorder
+ * @see TitledBorder
+ * @see AbstractBorder
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public interface Border
+{
+ /**
+ * Paints the border for a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height);
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ */
+ Insets getBorderInsets(Component c);
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * @return <code>true</code> if the border is fully opaque, or
+ * <code>false</code> if some pixels of the background
+ * can shine through the border.
+ */
+ boolean isBorderOpaque();
+}
diff --git a/libjava/classpath/javax/swing/border/CompoundBorder.java b/libjava/classpath/javax/swing/border/CompoundBorder.java
new file mode 100644
index 000000000..d811150ba
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/CompoundBorder.java
@@ -0,0 +1,255 @@
+/* CompoundBorder.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+/**
+ * A Border that is composed of an interior and an exterior border,
+ * where the interior border is tightly nested into the exterior.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class CompoundBorder extends AbstractBorder
+{
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 9054540377030555103L;
+
+ /**
+ * The inside border, which is painted between the bordered
+ * Component and the outside border. It is valid for
+ * <code>insideBorder</code> to be <code>null</code>.
+ */
+ protected Border insideBorder;
+
+ /**
+ * The outside border, which is painted outside both the
+ * bordered Component and the inside border. It is valid for
+ * <code>outsideBorder</code> to be <code>null</code>.
+ */
+ protected Border outsideBorder;
+
+ /**
+ * Constructs a CompoundBorder whose inside and outside borders
+ * are both <code>null</code>. While this does not really make
+ * any sense (there exists a class EmptyBorder as well, and not
+ * every Component needs to have a border at all), the API
+ * specification requires the existence of this constructor.
+ *
+ * @see EmptyBorder
+ */
+ public CompoundBorder()
+ {
+ this (null, null);
+ }
+
+ /**
+ * Constructs a CompoundBorder with the specified inside and
+ * outside borders.
+ *
+ * @param outsideBorder the outside border, which is painted to the
+ * outside of both <code>insideBorder</code> and the enclosed
+ * component. It is acceptable to pass <code>null</code>, in
+ * which case no outside border is painted.
+ *
+ * @param insideBorder the inside border, which is painted to
+ * between <code>outsideBorder</code> and the enclosed
+ * component. It is acceptable to pass <code>null</code>, in
+ * which case no inside border is painted.
+ */
+ public CompoundBorder(Border outsideBorder, Border insideBorder)
+ {
+ this.outsideBorder = outsideBorder;
+ this.insideBorder = insideBorder;
+ }
+
+ /**
+ * Determines whether or not this border is opaque. An opaque
+ * border fills every pixel in its area when painting. Partially
+ * translucent borders must return <code>false</code>, or ugly
+ * artifacts can appear on screen.
+ *
+ * @return <code>true</code> if both the inside and outside borders
+ * are opaque, or <code>false</code> otherwise.
+ */
+ public boolean isBorderOpaque()
+ {
+ // Although the API specification states that this method
+ // returns true if both the inside and outside borders are non-null
+ // and opaque, and false otherwise, a mauve test shows that if both
+ // the inside or outside borders are null, then true is returned.
+ if ((insideBorder == null) && (outsideBorder == null))
+ return true;
+
+ // A mauve test shows that if the inside border has a null value,
+ // then true is returned if the outside border is opaque; if the
+ // outside border has a null value, then true is returned if the
+ // inside border is opaque; else, true is returned if both the
+ // inside and outside borders are opaque.
+ if (insideBorder == null)
+ return outsideBorder.isBorderOpaque();
+ else if (outsideBorder == null)
+ return insideBorder.isBorderOpaque();
+ else
+ return insideBorder.isBorderOpaque() && outsideBorder.isBorderOpaque();
+ }
+
+ /**
+ * Paints the compound border by first painting the outside border,
+ * then painting the inside border tightly nested into the outside.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ // If there is an outside border, paint it and reduce the
+ // bounding box by its insets.
+ //
+ if (outsideBorder != null)
+ {
+ Insets outsideInsets;
+
+ outsideBorder.paintBorder(c, g, x, y, width, height);
+ outsideInsets = outsideBorder.getBorderInsets(c);
+
+ x += outsideInsets.left;
+ y += outsideInsets.top;
+
+ // Reduce width and height by the respective extent of the
+ // outside border.
+ width -= outsideInsets.left + outsideInsets.right;
+ height -= outsideInsets.top + outsideInsets.bottom;
+ }
+
+ if (insideBorder != null)
+ insideBorder.paintBorder(c, g, x, y, width, height);
+ }
+
+ /**
+ * Changes the specified insets to the insets of this border,
+ * which is the sum of the insets of the inside and the outside
+ * border.
+ *
+ * @param c the component in the center of this border.
+ * @param insets an Insets object for holding the added insets.
+ *
+ * @return the <code>insets</code> object.
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ Insets borderInsets;
+
+ if (insets == null)
+ insets = new Insets(0, 0, 0, 0);
+ else
+ insets.left = insets.right = insets.top = insets.bottom = 0;
+
+ // If there is an outside border, add it to insets.
+ if (outsideBorder != null)
+ {
+ borderInsets = outsideBorder.getBorderInsets(c);
+ insets.left += borderInsets.left;
+ insets.right += borderInsets.right;
+ insets.top += borderInsets.top;
+ insets.bottom += borderInsets.bottom;
+ }
+
+ // If there is an inside border, add it to insets.
+ if (insideBorder != null)
+ {
+ borderInsets = insideBorder.getBorderInsets(c);
+ insets.left += borderInsets.left;
+ insets.right += borderInsets.right;
+ insets.top += borderInsets.top;
+ insets.bottom += borderInsets.bottom;
+ }
+
+ return insets;
+ }
+
+ /**
+ * Determines the insets of this border, which is the sum of the
+ * insets of the inside and the outside border.
+ *
+ * @param c the component in the center of this border.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ // It is not clear why CompoundBorder does not simply inherit
+ // the implementation from AbstractBorder. However, we want
+ // to be compatible with the API specification, which overrides
+ // the getBorderInsets(Component) method.
+ return getBorderInsets(c, null);
+ }
+
+ /**
+ * Returns the outside border, which is painted outside both the
+ * bordered Component and the inside border. It is valid for the
+ * result to be <code>null</code>.
+ *
+ * @return The outside border (possibly <code>null</code>).
+ */
+ public Border getOutsideBorder()
+ {
+ return outsideBorder;
+ }
+
+ /**
+ * Returns the inside border, which is painted between the bordered
+ * Component and the outside border. It is valid for the result to
+ * be <code>null</code>.
+ *
+ * @return The inside border (possibly <code>null</code>).
+ */
+ public Border getInsideBorder()
+ {
+ return insideBorder;
+ }
+}
diff --git a/libjava/classpath/javax/swing/border/EmptyBorder.java b/libjava/classpath/javax/swing/border/EmptyBorder.java
new file mode 100644
index 000000000..da6651163
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/EmptyBorder.java
@@ -0,0 +1,223 @@
+/* EmptyBorder.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+
+/**
+ * A border for leaving a specifiable number of pixels empty around
+ * the enclosed component. An EmptyBorder requires some space on each
+ * edge, but does not perform any drawing.
+ *
+ * <p><img src="EmptyBorder-1.png" width="290" height="200"
+ * alt="[An illustration of EmptyBorder]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class EmptyBorder extends AbstractBorder
+{
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -8116076291731988694L;
+
+
+ /**
+ * The number of pixels required at the left edge.
+ */
+ protected int left;
+
+
+ /**
+ * The number of pixels required at the right edge.
+ */
+ protected int right;
+
+
+ /**
+ * The number of pixels required at the top edge.
+ */
+ protected int top;
+
+
+ /**
+ * The number of pixels required at the bottom edge.
+ */
+ protected int bottom;
+
+
+ /**
+ * Constructs an empty border given the number of pixels required
+ * on each side.
+ *
+ * @param top the number of pixels that the border will need
+ * for its top edge.
+ *
+ * @param left the number of pixels that the border will need
+ * for its left edge.
+ *
+ * @param bottom the number of pixels that the border will need
+ * for its bottom edge.
+ *
+ * @param right the number of pixels that the border will need
+ * for its right edge.
+ */
+ public EmptyBorder(int top, int left, int bottom, int right)
+ {
+ this.top = top;
+ this.left = left;
+ this.bottom = bottom;
+ this.right = right;
+ }
+
+
+ /**
+ * Constructs an empty border given the number of pixels required
+ * on each side, passed in an Insets object.
+ *
+ * @param borderInsets the Insets for the new border.
+ */
+ public EmptyBorder(Insets borderInsets)
+ {
+ this(borderInsets.top, borderInsets.left,
+ borderInsets.bottom, borderInsets.right);
+ }
+
+
+ /**
+ * Performs nothing because an EmptyBorder does not paint any
+ * pixels. While the inherited implementation provided by
+ * {@link AbstractBorder#paintBorder} is a no-op as well,
+ * it is overwritten in order to match the API of the Sun
+ * reference implementation.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets()
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ if (insets == null)
+ insets = new Insets(0, 0, 0, 0);
+
+ insets.left = left;
+ insets.right = right;
+ insets.top = top;
+ insets.bottom = bottom;
+ return insets;
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets()
+ {
+ return getBorderInsets(null, null);
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting. Since an empty border does not paint any pixels
+ * whatsoever, the result is <code>false</code>.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isBorderOpaque()
+ {
+ /* The inherited implementation of AbstractBorder.isBorderOpaque()
+ * would also return false. It is not clear why this is overriden
+ * in the Sun implementation, at least not from just reading the
+ * JavaDoc.
+ */
+ return false;
+ }
+}
diff --git a/libjava/classpath/javax/swing/border/EtchedBorder.java b/libjava/classpath/javax/swing/border/EtchedBorder.java
new file mode 100644
index 000000000..e9ee9e218
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/EtchedBorder.java
@@ -0,0 +1,414 @@
+/* EtchedBorder.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+
+/**
+ * A border that looks like an engraving etched into the background
+ * surface, or (in its raised variant) coming out of the surface
+ * plane. Using different constructors, it is possible to either
+ * explicitly specify the border colors, or to let the colors derive
+ * from the background color of the enclosed Component.
+ *
+ * <p><img src="doc-files/EtchedBorder-1.png" width="500" height="200"
+ * alt="[An illustration of the two EtchedBorder variants]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class EtchedBorder extends AbstractBorder
+{
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 4001244046866360638L;
+
+
+ /**
+ * Indicates that the border appears as coming out of the
+ * background.
+ */
+ public static final int RAISED = 0;
+
+
+ /**
+ * Indicates that the border appears as engraved into the
+ * background.
+ */
+ public static final int LOWERED = 1;
+
+
+ /**
+ * The type of this EtchedBorder, which is either {@link #RAISED}
+ * or {@link #LOWERED}.
+ */
+ protected int etchType;
+
+
+ /**
+ * The highlight color, or <code>null</code> to indicate that the
+ * color shall be derived from the background of the enclosed
+ * component.
+ */
+ protected Color highlight;
+
+
+ /**
+ * The shadow color, or <code>null</code> to indicate that the
+ * color shall be derived from the background of the enclosed
+ * component.
+ */
+ protected Color shadow;
+
+
+ /**
+ * Constructs a lowered EtchedBorder. The colors will be derived
+ * from the background color of the enclosed Component when the
+ * border gets painted.
+ */
+ public EtchedBorder()
+ {
+ this(LOWERED);
+ }
+
+
+ /**
+ * Constructs an EtchedBorder with the specified appearance. The
+ * colors will be derived from the background color of the enclosed
+ * Component when the border gets painted.
+ *
+ * <p><img src="doc-files/EtchedBorder-1.png" width="500" height="200"
+ * alt="[An illustration of the two EtchedBorder variants]" />
+ *
+ * @param etchType the desired appearance of the border. The value
+ * must be either {@link #RAISED} or {@link #LOWERED}.
+ *
+ * @throws IllegalArgumentException if <code>etchType</code> has
+ * an unsupported value.
+ */
+ public EtchedBorder(int etchType)
+ {
+ if ((etchType != RAISED) && (etchType != LOWERED))
+ throw new IllegalArgumentException();
+
+ this.etchType = etchType;
+
+ /* The highlight and shadow fields already have a null value
+ * when the constructor gets called, so there is no need to
+ * assign a value here.
+ */
+ }
+
+
+ /**
+ * Constructs a lowered EtchedBorder, explicitly selecting the
+ * colors that will be used for highlight and shadow.
+ *
+ * @param highlight the color that will be used for painting
+ * the highlight part of the border.
+ *
+ * @param shadow the color that will be used for painting
+ * the shadow part of the border.
+ *
+ * @see #EtchedBorder(int, Color, Color)
+ */
+ public EtchedBorder(Color highlight, Color shadow)
+ {
+ this(LOWERED, highlight, shadow);
+ }
+
+
+ /**
+ * Constructs an EtchedBorder with the specified appearance,
+ * explicitly selecting the colors that will be used for
+ * highlight and shadow.
+ *
+ * <p><img src="doc-files/EtchedBorder-2.png" width="500" height="200"
+ * alt="[An illustration that shows which pixels get painted
+ * in what color]" />
+ *
+ * @param etchType the desired appearance of the border. The value
+ * must be either {@link #RAISED} or {@link #LOWERED}.
+ *
+ * @param highlight the color that will be used for painting
+ * the highlight part of the border.
+ *
+ * @param shadow the color that will be used for painting
+ * the shadow part of the border.
+ *
+ * @throws IllegalArgumentException if <code>etchType</code> has
+ * an unsupported value.
+ */
+ public EtchedBorder(int etchType, Color highlight, Color shadow)
+ {
+ this(etchType); // Checks the validity of the value.
+ this.highlight = highlight;
+ this.shadow = shadow;
+ }
+
+
+ /**
+ * Paints the border for a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ switch (etchType)
+ {
+ case RAISED:
+ paintEtchedBorder(g, x, y, width, height,
+ getHighlightColor(c), getShadowColor(c));
+ break;
+
+ case LOWERED:
+ paintEtchedBorder(g, x, y, width, height,
+ getShadowColor(c), getHighlightColor(c));
+ break;
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(2, 2, 2, 2);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ insets.left = insets.right = insets.top = insets.bottom = 2;
+ return insets;
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * <p>If the border colors are derived from the background color of
+ * the enclosed component, the result is <code>true</code> because
+ * the derivation method always returns opaque colors. Otherwise,
+ * the result depends on the opacity of the individual colors.
+ *
+ * @return <code>true</code> if the border is fully opaque, or
+ * <code>false</code> if some pixels of the background
+ * can shine through the border.
+ */
+ public boolean isBorderOpaque()
+ {
+ // If the colors are to be derived from the enclosed Component's
+ // background color, the border is guaranteed to be fully opaque
+ // because Color.brighten() and Color.darken() always return an
+ // opaque color.
+ return
+ ((highlight == null) || (highlight.getAlpha() == 255))
+ && ((shadow == null) || (shadow.getAlpha() == 255));
+ }
+
+ /**
+ * Returns the appearance of this EtchedBorder, which is either
+ * {@link #RAISED} or {@link #LOWERED}.
+ *
+ * @return The type ({@link #RAISED} or {@link #LOWERED}).
+ */
+ public int getEtchType()
+ {
+ return etchType;
+ }
+
+
+ /**
+ * Determines the color that will be used for highlighted parts when
+ * painting the border around a given component. If a highlight
+ * color has been specified upon constructing the border, that color
+ * is returned. Otherwise, the background color of the enclosed
+ * component is brightened.
+ *
+ * @param c the component enclosed by this border.
+ *
+ * @return The color.
+ *
+ * @see java.awt.Component#getBackground()
+ * @see java.awt.Color#brighter()
+ */
+ public Color getHighlightColor(Component c)
+ {
+ if (highlight != null)
+ return highlight;
+ else
+ return c.getBackground().brighter();
+ }
+
+ /**
+ * Returns the color that will be used for highlighted parts when
+ * painting the border, or <code>null</code> if that color will be
+ * derived from the background of the enclosed Component.
+ *
+ * @return The highlight color (possibly <code>null</code>).
+ */
+ public Color getHighlightColor()
+ {
+ return highlight;
+ }
+
+
+ /**
+ * Determines the color that will be used for shadowed parts when
+ * painting the border around a given component. If a shadow color
+ * has been specified upon constructing the border, that color is
+ * returned. Otherwise, the background color of the enclosed
+ * component is darkened.
+ *
+ * @param c the component enclosed by this border.
+ *
+ * @return The shadow color.
+ *
+ * @see java.awt.Component#getBackground()
+ * @see java.awt.Color#darker()
+ */
+ public Color getShadowColor(Component c)
+ {
+ if (shadow != null)
+ return shadow;
+ else
+ return c.getBackground().darker();
+ }
+
+
+ /**
+ * Returns the color that will be used for shadowed parts when
+ * painting the border, or <code>null</code> if that color will be
+ * derived from the background of the enclosed Component.
+ *
+ * @return The shadow color (possibly <code>null</code>).
+ */
+ public Color getShadowColor()
+ {
+ return shadow;
+ }
+
+
+ /**
+ * Paints a two-pixel etching in two colors.
+ *
+ * <pre>
+ * +++++++++++.
+ * +.........+. + = color a
+ * +. +. . = color b
+ * +. +.
+ * +++++++++++.
+ * ............</pre>
+ *
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ * @param a one of the two colors.
+ * @param b the second of the two colors.
+ */
+ private static void paintEtchedBorder(Graphics g, int x, int y, int width,
+ int height, Color a, Color b)
+ {
+ Color oldColor;
+
+ oldColor = g.getColor();
+ g.translate(x, y);
+ width = width - 1;
+ height = height - 1;
+
+ try
+ {
+ // To understand this code, it might be helpful to look at the
+ // images that are included with the JavaDoc. They are located
+ // in the "doc-files" subdirectory. EtchedBorder-2.png might
+ // be especially informative.
+ g.setColor(a);
+ g.drawRect(0, 0, width - 1, height - 1);
+
+ g.setColor(b);
+ g.drawLine(1, 1, width - 2, 1); // top edge
+ g.drawLine(1, 2, 1, height - 2); // left edge
+ g.drawLine(0, height, width, height); // bottom edge
+ g.drawLine(width, 0, width, height - 1); // right edge
+ }
+ finally
+ {
+ g.translate(-x, -y);
+ g.setColor(oldColor);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/border/LineBorder.java b/libjava/classpath/javax/swing/border/LineBorder.java
new file mode 100644
index 000000000..e78bbee3b
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/LineBorder.java
@@ -0,0 +1,347 @@
+/* LineBorder.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+
+/**
+ * A border that consists of a line whose thickness and color can be
+ * specified. There also is a variant with rounded corners.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class LineBorder extends AbstractBorder
+{
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -787563427772288970L;
+
+
+ /**
+ * A shared instance of a black, one pixel thick, plain LineBorder.
+ * The singleton object is lazily created by {@link
+ * #createBlackLineBorder()} upon its first invocation.
+ */
+ private static LineBorder blackLineBorder;
+
+
+ /**
+ * A shared instance of a gray, one pixel thick, plain LineBorder.
+ * The singleton object is lazily created by {@link
+ * #createGrayLineBorder()} upon its first invocation.
+ */
+ private static LineBorder grayLineBorder;
+
+
+ /**
+ * The width of the line in pixels.
+ */
+ protected int thickness;
+
+
+ /**
+ * The color of the line.
+ */
+ protected Color lineColor;
+
+
+ /**
+ * Indicates whether the line is drawn with rounded corners
+ * (<code>true</code>) or not ((<code>false</code>).
+ */
+ protected boolean roundedCorners;
+
+
+ /**
+ * Constructs a LineBorder given its color. The border will be one
+ * pixel thick and have plain corners.
+ *
+ * @param color the color for drawing the border.
+ *
+ * @see #LineBorder(java.awt.Color, int, boolean)
+ */
+ public LineBorder(Color color)
+ {
+ this(color, /* thickness */ 1, /* roundedCorners */ false);
+ }
+
+
+ /**
+ * Constructs a LineBorder given its color and thickness. The
+ * border will have plain corners.
+ *
+ * @param color the color for drawing the border.
+ * @param thickness the width of the line in pixels.
+ *
+ * @see #LineBorder(java.awt.Color, int, boolean)
+ */
+ public LineBorder(Color color, int thickness)
+ {
+ this (color, thickness, /* roundedCorners */ false);
+ }
+
+
+ /**
+ * Constructs a LineBorder given its color, thickness, and whether
+ * it has rounded corners.
+ *
+ * <p><img src="doc-files/LineBorder-1.png" width="500" height="200"
+ * alt="[An illustration of two LineBorders]" />
+ *
+ * <p>Note that the enlarged view in the right-hand picture shows
+ * that the implementation draws one more pixel than specified,
+ * provided that <code>roundedCorders</code> is <code>true</code>
+ * and anti-aliasing is turned on while painting. While this might
+ * be considered a bug, the Sun reference implementation (at least
+ * JDK 1.3.1 on Apple MacOS X 10.1.5) can be observed to fill
+ * exactly the same pixels as shown above. The GNU Classpath
+ * LineBorder replicates the observed behavior of the Sun
+ * implementation.
+ *
+ * @param color the color for drawing the border.
+ * @param thickness the width of the line in pixels.
+ * @param roundedCorners <code>true</code> for rounded corners,
+ * <code>false</code> for plain corners.
+ *
+ * @since 1.3
+ */
+ // For the bug mentioned in the JavaDoc, please see also the comment
+ // in the paintBorder method below.
+ //
+ public LineBorder(Color color, int thickness, boolean roundedCorners)
+ {
+ if ((color == null) || (thickness < 0))
+ throw new IllegalArgumentException();
+
+ this.lineColor = color;
+ this.thickness = thickness;
+ this.roundedCorners = roundedCorners;
+ }
+
+
+ /**
+ * Returns a black, one pixel thick, plain {@link LineBorder}. The method
+ * may always return the same (singleton) {@link LineBorder} instance.
+ *
+ * @return The border.
+ */
+ public static Border createBlackLineBorder()
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (blackLineBorder == null)
+ blackLineBorder = new LineBorder(Color.black);
+
+ return blackLineBorder;
+ }
+
+
+ /**
+ * Returns a gray, one pixel thick, plain {@link LineBorder}. The method
+ * may always return the same (singleton) {@link LineBorder} instance.
+ *
+ * @return The border.
+ */
+ public static Border createGrayLineBorder()
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (grayLineBorder == null)
+ grayLineBorder = new LineBorder(Color.gray);
+
+ return grayLineBorder;
+ }
+
+
+ /**
+ * Paints the line border around a given Component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ Color oldColor = g.getColor();
+
+ try
+ {
+ g.setColor(lineColor);
+
+ // If width and height were not adjusted, the border would
+ // appear one pixel too large in both directions.
+ width -= 1;
+ height -= 1;
+
+ // Blurred, too large appearance
+ // -----------------------------
+ // While Java 2D has introduced line strokes of arbitrary width,
+ // it seems desirable to keep this code independent of Java 2D.
+ // Therefore, multiple nested rectangles (or rounded rectangles)
+ // are drawn in order to simulate a line whose thickness is
+ // greater than one pixel.
+ //
+ // This hack causes a blurred appearance when anti-aliasing is
+ // on. Interestingly enough, though, the Sun JDK 1.3.1 (at least
+ // on MacOS X 10.1.5) shows exactly the same appearance under
+ // this condition. It thus seems likely that Sun does the same
+ // hack for simulating thick lines. For this reason, the
+ // blurred appearance seems acceptable -- especially since GNU
+ // Classpath tries to be compatible with the Sun reference
+ // implementation.
+ for (int i = 0; i < thickness; i++)
+ {
+ if (roundedCorners)
+ g.drawRoundRect(x, y, width, height, thickness, thickness);
+ else
+ g.drawRect(x, y, width, height);
+
+ x += 1;
+ y += 1;
+ width -= 2;
+ height -= 2;
+ }
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge, which is the
+ * thickness of the line.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(thickness, thickness, thickness, thickness);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge, which is the thickness
+ * of the line.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ insets.left = insets.right = insets.top = insets.bottom = thickness;
+ return insets;
+ }
+
+
+ /**
+ * Returns the color of the line.
+ *
+ * @return The line color (never <code>null</code>).
+ */
+ public Color getLineColor()
+ {
+ return lineColor;
+ }
+
+
+ /**
+ * Returns the thickness of the line in pixels.
+ *
+ * @return The line thickness (in pixels).
+ */
+ public int getThickness()
+ {
+ return thickness;
+ }
+
+
+ /**
+ * Returns whether this LineBorder os drawm with rounded
+ * or with plain corners.
+ *
+ * @return <code>true</code> if the corners are rounded,
+ * <code>false</code> if the corners are plain.
+ */
+ public boolean getRoundedCorners()
+ {
+ return roundedCorners;
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * @return <code>true</code> if the corners are plain and the line
+ * color is fully opaque; <code>false</code> if the corners
+ * are rounded or the line color is partially transparent.
+ */
+ public boolean isBorderOpaque()
+ {
+ return (!roundedCorners) && (lineColor.getAlpha() == 255);
+ }
+}
diff --git a/libjava/classpath/javax/swing/border/MatteBorder.java b/libjava/classpath/javax/swing/border/MatteBorder.java
new file mode 100644
index 000000000..c0c2d7760
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/MatteBorder.java
@@ -0,0 +1,407 @@
+/* MatteBorder.java --
+ Copyright (C) 2003, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.Icon;
+
+/**
+ * A border that is filled with either a solid color or with repeated
+ * icon tiles.
+ *
+ * <p><img src="doc-files/MatteBorder-1.png" width="500" height="150"
+ * alt="[Two MatteBorders]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class MatteBorder extends EmptyBorder
+{
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 4422248989617298224L;
+
+
+ /**
+ * The color that is used for filling the border, or
+ * <code>null</code> if the border is filled with repetitions of a
+ * tile icon.
+ *
+ * @see #tileIcon
+ */
+ protected Color color;
+
+
+ /**
+ * The icon is used for filling the border with a tile, or
+ * <code>null</code> if the border is filled with a solid
+ * color.
+ *
+ * @see #color
+ */
+ protected Icon tileIcon;
+
+
+ /**
+ * Constructs a MatteBorder given the width on each side
+ * and a fill color.
+ *
+ * <p><img src="doc-files/MatteBorder-2.png" width="500" height="150"
+ * alt="[A picture of a MatteBorder made by this constructor]" />
+ *
+ * @param top the width of the border at its top edge.
+ * @param left the width of the border at its left edge.
+ * @param bottom the width of the border at its bottom edge.
+ * @param right the width of the border at its right edge.
+ * @param matteColor the color for filling the border.
+ */
+ public MatteBorder(int top, int left, int bottom, int right,
+ Color matteColor)
+ {
+ super(top, left, bottom, right);
+
+ if (matteColor == null)
+ throw new IllegalArgumentException();
+
+ this.color = matteColor;
+ }
+
+
+ /**
+ * Constructs a MatteBorder given its insets and fill color.
+ *
+ * <p><img src="doc-files/MatteBorder-3.png" width="500" height="150"
+ * alt="[A picture of a MatteBorder made by this constructor]" />
+ *
+ * @param borderInsets an Insets object whose <code>top</code>,
+ * <code>left</code>, <code>bottom</code> and <code>right</code>
+ * fields indicate the with of the border at the respective
+ * edge.
+ *
+ * @param matteColor the color for filling the border.
+ */
+ public MatteBorder(Insets borderInsets, Color matteColor)
+ {
+ this(borderInsets.top, borderInsets.left,
+ borderInsets.bottom, borderInsets.right,
+ matteColor);
+ }
+
+
+ /**
+ * Constructs a MatteBorder given the width on each side
+ * and an icon for tiling the border area.
+ *
+ * <p><img src="doc-files/MatteBorder-4.png" width="500" height="150"
+ * alt="[A picture of a MatteBorder made by this constructor]" />
+ *
+ * @param top the width of the border at its top edge.
+ * @param left the width of the border at its left edge.
+ * @param bottom the width of the border at its bottom edge.
+ * @param right the width of the border at its right edge.
+ * @param tileIcon an icon for tiling the border area.
+ */
+ public MatteBorder(int top, int left, int bottom, int right,
+ Icon tileIcon)
+ {
+ super(top, left, bottom, right);
+
+ this.tileIcon = tileIcon;
+ }
+
+
+ /**
+ * Constructs a MatteBorder given its insets and an icon
+ * for tiling the border area.
+ *
+ * <p><img src="doc-files/MatteBorder-5.png" width="500" height="150"
+ * alt="[A picture of a MatteBorder made by this constructor]" />
+ *
+ * @param borderInsets an Insets object whose <code>top</code>,
+ * <code>left</code>, <code>bottom</code> and <code>right</code>
+ * fields indicate the with of the border at the respective
+ * edge.
+ *
+ * @param tileIcon an icon for tiling the border area.
+ */
+ public MatteBorder(Insets borderInsets, Icon tileIcon)
+ {
+ this(borderInsets.top, borderInsets.left,
+ borderInsets.bottom, borderInsets.right,
+ tileIcon);
+ }
+
+
+ /**
+ * Constructs a MatteBorder given an icon for tiling the
+ * border area. The icon width is used for the border insets
+ * at the left and right edge, the icon height for the top and
+ * bottom edge.
+ *
+ * <p><img src="doc-files/MatteBorder-6.png" width="379" height="150"
+ * alt="[A picture of a MatteBorder made by this constructor]" />
+ *
+ * @param tileIcon an icon for tiling the border area.
+ */
+ public MatteBorder(Icon tileIcon)
+ {
+ this(-1, -1, -1, -1, tileIcon);
+ }
+
+
+ /**
+ * Paints the border for a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ Insets i = getBorderInsets();
+ paintEdge(c, g, x, y, width, i.top, 0, 0); // top edge
+ paintEdge(c, g, x, y + height - i.bottom, // bottom edge
+ width, i.bottom,
+ 0, height - i.bottom);
+ paintEdge(c, g, x, y + i.top, // left edge
+ i.left, height - i.top,
+ 0, i.top);
+ paintEdge(c, g, x + width - i.right, y + i.top, // right edge
+ i.right, height - i.bottom,
+ width - i.right, i.top);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return this.getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets()
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ if (insets == null)
+ insets = new Insets(0, 0, 0, 0);
+
+ if ((tileIcon != null)
+ && (top < 0) && (left < 0)
+ && (right < 0) && (bottom < 0))
+ {
+ insets.left = insets.right = tileIcon.getIconWidth();
+ insets.top = insets.bottom = tileIcon.getIconHeight();
+ return insets;
+ }
+
+ /* Copy top, left, bottom and right into the respective
+ * field of insets.
+ */
+ return super.getBorderInsets(c, insets);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets()
+ {
+ /* The inherited implementation of EmptyBorder.isBorderOpaque()
+ * would do the same. It is not clear why this is overriden in the
+ * Sun implementation, at least not from just reading the JavaDoc.
+ */
+ return this.getBorderInsets(null, null);
+ }
+
+
+ /**
+ * Returns the color that is used for filling the border, or
+ * <code>null</code> if the border is filled with repetitions of a
+ * tile icon.
+ *
+ * @return The color (possibly <code>null</code>).
+ */
+ public Color getMatteColor()
+ {
+ return color;
+ }
+
+
+ /**
+ * Returns the icon is used for tiling the border, or
+ * <code>null</code> if the border is filled with a color instead of
+ * an icon.
+ *
+ * @return The icon (possibly <code>null</code>).
+ */
+ public Icon getTileIcon()
+ {
+ return tileIcon;
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * @return <code>true</code> if the border is filled with an
+ * opaque color; <code>false</code> if it is filled with
+ * a semi-transparent color or with an icon.
+ */
+ public boolean isBorderOpaque()
+ {
+ return (color != null) && (color.getAlpha() == 255);
+ }
+
+
+ /**
+ * Paints a rectangular area of the border. This private helper
+ * method is called once for each of the border edges
+ * by {@link #paintBorder}.
+ *
+ * @param c the component whose border is being painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position of the rectangular area.
+ * @param y the vertical position of the rectangular area.
+ * @param width the width of the rectangular area.
+ * @param height the height of the rectangular area.
+ * @param dx the x displacement for repeating the tile.
+ * @param dy the y displacement for repeating the tile.
+ */
+ private void paintEdge(Component c, Graphics g,
+ int x, int y, int width, int height,
+ int dx, int dy)
+ {
+ Color oldColor;
+ int iconWidth, iconHeight;
+ Graphics clipped;
+
+ if ((width <= 0) || (height <= 0))
+ return;
+
+ /* Paint a colored rectangle if desired. */
+ if (color != null)
+ {
+ oldColor = g.getColor();
+ try
+ {
+ g.setColor(color);
+ g.fillRect(x, y, width, height);
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ return;
+ }
+
+ // If this border has no icon end painting here.
+ if (tileIcon == null)
+ return;
+
+ /* Determine the width and height of the icon. Some icons return
+ * -1 if it is an image whose dimensions have not yet been
+ * retrieved. There is not much we can do about this, but we
+ * should at least avoid entering the paint loop below
+ * with negative increments.
+ */
+ iconWidth = tileIcon.getIconWidth();
+ iconHeight = tileIcon.getIconHeight();
+ if ((iconWidth <= 0) || (iconHeight <= 0))
+ return;
+
+ dx = dx % iconWidth;
+ dy = dy % iconHeight;
+
+ clipped = g.create();
+ try
+ {
+ clipped.setClip(x, y, width, height);
+ for (int ty = y - dy; ty < y + height; ty += iconHeight)
+ for (int tx = x - dx; tx < x + width; tx += iconWidth)
+ tileIcon.paintIcon(c, clipped, tx, ty);
+ }
+ finally
+ {
+ clipped.dispose();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/border/SoftBevelBorder.java b/libjava/classpath/javax/swing/border/SoftBevelBorder.java
new file mode 100644
index 000000000..c0c9ae811
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/SoftBevelBorder.java
@@ -0,0 +1,327 @@
+/* SoftBevelBorder.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+
+/**
+ * A rectangular, three pixel thick border that looks like a BevelBorder
+ * with slightly softened corners.
+ *
+ * <p>Like BevelBorder, SoftBevelBorder has a highlight and a shadow
+ * color. In the raised variant, the highlight color is used for the
+ * top and left edges, and the shadow color is used for the bottom and
+ * right edge. In the lowered variant, color usage is reversed. For
+ * an image, see the documentation of the individual constructors.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class SoftBevelBorder extends BevelBorder
+{
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Sun JDK 1.4.1_01 on GNU/Linux 2.4.20. Interestingly,
+ * the Apple/Sun JDK 1.3.1 on MacOS X 10.1.5 gives a different
+ * value, namely -6658357140774549493L.
+ */
+ static final long serialVersionUID = 5248789787305979975L;
+
+
+ /**
+ * Constructs a SoftBevelBorder whose colors will be derived from the
+ * background of the enclosed component. The background color is
+ * retrieved each time the border is painted, so a SoftBevelBorder
+ * constructed by this method will automatically reflect a change
+ * to the component&#x2019;s background color.
+ *
+ * <p><img src="doc-files/SoftBevelBorder-1.png" width="500" height="200"
+ * alt="[An illustration showing raised and lowered SoftBevelBorders]" />
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link BevelBorder#RAISED}
+ * or {@link BevelBorder#LOWERED}.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has
+ * an unsupported value.
+ */
+ public SoftBevelBorder(int bevelType)
+ {
+ super(bevelType);
+ }
+
+
+ /**
+ * Constructs a SoftBevelBorder given its appearance type and two
+ * colors for its highlight and shadow.
+ *
+ * <p><img src="doc-files/SoftBevelBorder-2.png" width="500" height="150"
+ * alt="[An illustration showing SoftBevelBorders that were
+ * constructed with this method]" />
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link BevelBorder#RAISED} or {@link
+ * BevelBorder#LOWERED}.
+ *
+ * @param highlight the color that will be used for the inner side
+ * of the highlighted edges (top and left if if
+ * <code>bevelType</code> is {@link BevelBorder#RAISED};
+ * bottom and right otherwise). The color for the outer side
+ * is a brightened version of this color.
+ *
+ * @param shadow the color that will be used for the outer side of
+ * the shadowed edges (bottom and right if
+ * <code>bevelType</code> is {@link BevelBorder#RAISED}; top
+ * and left otherwise). The color for the inner side is a
+ * brightened version of this color.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has an
+ * unsupported value.
+ *
+ * @throws NullPointerException if <code>highlight</code> or
+ * <code>shadow</code> is <code>null</code>.
+ *
+ * @see java.awt.Color#brighter()
+ */
+ public SoftBevelBorder(int bevelType, Color highlight, Color shadow)
+ {
+ this(bevelType,
+ /* highlightOuter */ highlight.brighter(),
+ /* highlightInner */ highlight,
+ /* shadowOuter */ shadow,
+ /* shadowInner */ shadow.brighter());
+ }
+
+
+ /**
+ * Constructs a SoftBevelBorder given its appearance type and all
+ * colors.
+ *
+ * <p><img src="doc-files/SoftBevelBorder-3.png" width="500" height="150"
+ * alt="[An illustration showing SoftBevelBorders that were
+ * constructed with this method]" />
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link BevelBorder#RAISED} or {@link
+ * BevelBorder#LOWERED}.
+ *
+ * @param highlightOuter the color that will be used for the outer
+ * side of the highlighted edges (top and left if
+ * <code>bevelType</code> is {@link BevelBorder#RAISED};
+ * bottom and right otherwise).
+ *
+ * @param highlightInner the color that will be used for the inner
+ * side of the highlighted edges.
+ *
+ * @param shadowOuter the color that will be used for the outer side
+ * of the shadowed edges (bottom and right if
+ * <code>bevelType</code> is {@link BevelBorder#RAISED}; top
+ * and left otherwise).
+ *
+ * @param shadowInner the color that will be used for the inner
+ * side of the shadowed edges.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has
+ * an unsupported value.
+ *
+ * @throws NullPointerException if one of the passed colors
+ * is <code>null</code>.
+ */
+ public SoftBevelBorder(int bevelType,
+ Color highlightOuter, Color highlightInner,
+ Color shadowOuter, Color shadowInner)
+ {
+ super(bevelType,
+ highlightOuter, highlightInner,
+ shadowOuter, shadowInner);
+ }
+
+
+ /**
+ * Paints the border for a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ switch (bevelType)
+ {
+ case RAISED:
+ paintSoftBevel(g, x, y, width, height,
+ getHighlightOuterColor(c), getHighlightInnerColor(c),
+ getShadowInnerColor(c), getShadowOuterColor(c));
+ break;
+
+ case LOWERED:
+ paintSoftBevel(g, x, y, width, height,
+ getShadowOuterColor(c), getShadowInnerColor(c),
+ getHighlightInnerColor(c), getHighlightOuterColor(c));
+ break;
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(3, 3, 3, 3);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ insets.left = insets.right = insets.top = insets.bottom = 3;
+ return insets;
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * <p>The enlarged view (see documentation for constructors) shows
+ * that a SoftBevelBorder does not paint all pixels. Therefore,
+ * this method always returns <code>false</code>.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isBorderOpaque()
+ {
+ return false;
+ }
+
+
+ /**
+ * Paints a soft bevel in four colors.
+ *
+ * <pre>
+ * +++++++++++.
+ * ++.........# + = color a
+ * +.. # . = color b
+ * +. # X = color c
+ * .. X# # = color d
+ * . ##########</pre>
+ *
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ * @param a the color for the outer side of the top and left edges.
+ * @param b the color for the inner side of the top and left edges.
+ * @param c the color for the inner side of the bottom and right edges.
+ * @param d the color for the outer side of the bottom and right edges.
+ */
+ private static void paintSoftBevel(Graphics g,
+ int x, int y, int width, int height,
+ Color a, Color b, Color c, Color d)
+ {
+ Color oldColor;
+
+ oldColor = g.getColor();
+ g.translate(x, y);
+ width = width - 1;
+ height = height - 1;
+
+ try
+ {
+ /* To understand this code, it might be helpful to look at the
+ * images that are included with the JavaDoc, especially
+ * SoftBevelBorder-3.png. They are located in the "doc-files"
+ * subdirectory.
+ */
+ g.setColor(a);
+ g.drawLine(0, 0, width - 1, 0); // a, horizontal
+ g.drawLine(0, 1, 2, 1); // a, horizontal
+ g.drawLine(0, 2, 0, height - 1); // a, vertical
+
+ g.setColor(b);
+ g.drawLine(width, 0, width, 0); // b, horizontal
+ g.drawLine(2, 1, width - 1, 1); // b, horizontal
+ g.drawLine(1, 2, 2, 2); // b, horizontal
+ g.drawLine(1, 3, 1, height - 1); // b, vertical
+ g.drawLine(0, height - 1, 0, height); // b, vertical
+
+ g.setColor(c);
+ g.drawLine(width - 1, height - 1, // c, one pixel
+ width - 1, height - 1);
+
+ g.setColor(d);
+ g.drawLine(2, height, width, height); // d, horizontal
+ g.drawLine(width, 2, width, height - 1); // d, vertical
+ }
+ finally
+ {
+ g.translate(-x, -y);
+ g.setColor(oldColor);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/border/TitledBorder.java b/libjava/classpath/javax/swing/border/TitledBorder.java
new file mode 100644
index 000000000..4d4f3af7d
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/TitledBorder.java
@@ -0,0 +1,1078 @@
+/* TitledBorder.java --
+ Copyright (C) 2003, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.border;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+
+/**
+ * A border that paints a title on top of another border.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class TitledBorder extends AbstractBorder
+{
+ /**
+ * A value for the <code>titlePosition</code> property that vertically
+ * positions the title text at the default vertical position, which
+ * is in the middle of the top line of the border.
+ *
+ * @see #getTitlePosition()
+ * @see #setTitlePosition(int)
+ */
+ public static final int DEFAULT_POSITION = 0;
+
+
+ /**
+ * A value for the <code>titlePosition</code> property that vertically
+ * positions the title text above the top line of the border.
+ *
+ * @see #getTitlePosition()
+ * @see #setTitlePosition(int)
+ */
+ public static final int ABOVE_TOP = 1;
+
+
+ /**
+ * A value for the <code>titlePosition</code> property that vertically
+ * positions the title text at the middle of the top line
+ * of the border.
+ *
+ * @see #getTitlePosition()
+ * @see #setTitlePosition(int)
+ */
+ public static final int TOP = 2;
+
+
+ /**
+ * A value for the <code>titlePosition</code> property that vertically
+ * positions the title text below the top line of the border.
+ *
+ * @see #getTitlePosition()
+ * @see #setTitlePosition(int)
+ */
+ public static final int BELOW_TOP = 3;
+
+
+ /**
+ * A value for the <code>titlePosition</code> property that vertically
+ * positions the title text above the bottom line of the border.
+ *
+ * @see #getTitlePosition()
+ * @see #setTitlePosition(int)
+ */
+ public static final int ABOVE_BOTTOM = 4;
+
+
+ /**
+ * A value for the <code>titlePosition</code> property that vertically
+ * positions the title text at the center of the bottom line
+ * of the border.
+ *
+ * @see #getTitlePosition()
+ * @see #setTitlePosition(int)
+ */
+ public static final int BOTTOM = 5;
+
+
+ /**
+ * A value for the <code>titlePosition</code> property that vertically
+ * positions the title text below the bottom line of the border.
+ *
+ * @see #getTitlePosition()
+ * @see #setTitlePosition(int)
+ */
+ public static final int BELOW_BOTTOM = 6;
+
+
+ /**
+ * A value for the <code>titleJustification</code> property that
+ * horizontally aligns the title text with either the left or the
+ * right edge of the border, depending on the orientation of the
+ * component nested into the border. If the component orientation
+ * is left-to-right, the title text is aligned with the left edge;
+ * otherwise, it is aligned with the right edge. This is the same
+ * behavior as with {@link #LEADING}.
+ *
+ * @see #getTitleJustification()
+ * @see #setTitleJustification(int)
+ * @see java.awt.ComponentOrientation#isLeftToRight()
+ */
+ public static final int DEFAULT_JUSTIFICATION = 0;
+
+
+ /**
+ * A value for the <code>titleJustification</code> property that
+ * horizontally aligns the title text with the left-hand edge of
+ * the border.
+ *
+ * @see #getTitleJustification()
+ * @see #setTitleJustification(int)
+ */
+ public static final int LEFT = 1;
+
+
+ /**
+ * A value for the <code>titleJustification</code> property that
+ * horizontally aligns the title text with the center of the border.
+ *
+ * @see #getTitleJustification()
+ * @see #setTitleJustification(int)
+ */
+ public static final int CENTER = 2;
+
+
+ /**
+ * A value for the <code>titleJustification</code> property that
+ * horizontally aligns the title text with the right-hand edge of
+ * the border.
+ *
+ * @see #getTitleJustification()
+ * @see #setTitleJustification(int)
+ */
+ public static final int RIGHT = 3;
+
+
+ /**
+ * A value for the <code>titleJustification</code> property that
+ * horizontally aligns the title text with either the left or the
+ * right edge of the border, depending on the orientation of the
+ * component nested into the border. If the component orientation
+ * is left-to-right, the title text is aligned with the left edge;
+ * otherwise, it is aligned with the right edge. This is the same
+ * behavior as with {@link #DEFAULT_JUSTIFICATION}.
+ *
+ * @see #getTitleJustification()
+ * @see #setTitleJustification(int)
+ * @see java.awt.ComponentOrientation#isLeftToRight()
+ */
+ public static final int LEADING = 4;
+
+
+ /**
+ * A value for the <code>titleJustification</code> property that
+ * horizontally aligns the title text with either the right or the
+ * left edge of the border, depending on the orientation of the
+ * component nested into the border. If the component orientation
+ * is left-to-right, the title text is aligned with the right edge;
+ * otherwise, it is aligned with the left edge.
+ *
+ * @see #getTitleJustification()
+ * @see #setTitleJustification(int)
+ * @see java.awt.ComponentOrientation#isLeftToRight()
+ */
+ public static final int TRAILING = 5;
+
+
+ /**
+ * The number of pixels between the inside of {@link #border}
+ * and the bordered component.
+ */
+ protected static final int EDGE_SPACING = 2;
+
+
+ /**
+ * The number of pixels between the outside of this TitledBorder
+ * and the beginning (if left-aligned) or end (if right-aligned)
+ * of the title text.
+ */
+ protected static final int TEXT_INSET_H = 5;
+
+
+ /**
+ * The number of pixels between the title text and {@link #border}.
+ * This value is only relevant if the title text does not intersect
+ * {@link #border}. No intersection occurs if {@link #titlePosition}
+ * is one of {@link #ABOVE_TOP}, {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM},
+ * or {@link #BELOW_BOTTOM}.
+ */
+ protected static final int TEXT_SPACING = 2;
+
+
+ /**
+ * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
+ * on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 8012999415147721601L;
+
+
+ /**
+ * The title, or <code>null</code> to display no title.
+ */
+ protected String title;
+
+
+ /**
+ * The border underneath the title. If this value is
+ * <code>null</code>, the border will be retrieved from the {@link
+ * javax.swing.UIManager}&#x2019;s defaults table using the key
+ * <code>TitledBorder.border</code>.
+ */
+ protected Border border;
+
+
+ /**
+ * The vertical position of the title text relative to the border,
+ * which is one of {@link #ABOVE_TOP}, {@link #TOP}, {@link
+ * #BELOW_TOP}, {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link
+ * #BELOW_BOTTOM}, or {@link #DEFAULT_POSITION}.
+ */
+ protected int titlePosition;
+
+
+ /**
+ * The horizontal alignment of the title text in relation to the
+ * border, which is one of {@link #LEFT}, {@link #CENTER}, {@link
+ * #RIGHT}, {@link #LEADING}, {@link #TRAILING}, or {@link
+ * #DEFAULT_JUSTIFICATION}.
+ */
+ protected int titleJustification;
+
+
+ /**
+ * The font for displaying the title text. If this value is
+ * <code>null</code>, the font will be retrieved from the {@link
+ * javax.swing.UIManager}&#x2019;s defaults table using the key
+ * <code>TitledBorder.font</code>.
+ */
+ protected Font titleFont;
+
+
+ /**
+ * The color for displaying the title text. If this value is
+ * <code>null</code>, the color will be retrieved from the {@link
+ * javax.swing.UIManager}&#x2019;s defaults table using the key
+ * <code>TitledBorder.titleColor</code>.
+ */
+ protected Color titleColor;
+
+
+ /**
+ * Constructs a TitledBorder given the text of its title.
+ *
+ * @param title the title text, or <code>null</code> to use no title text.
+ */
+ public TitledBorder(String title)
+ {
+ this(/* border */ null,
+ title, LEADING, TOP,
+ /* titleFont */ null, /* titleColor */ null);
+ }
+
+
+ /**
+ * Constructs an initially untitled TitledBorder given another border.
+ *
+ * @param border the border underneath the title, or <code>null</code>
+ * to use a default from the current look and feel.
+ */
+ public TitledBorder(Border border)
+ {
+ this(border, /* title */ "", LEADING, TOP,
+ /* titleFont */ null, /* titleColor */ null);
+ }
+
+
+ /**
+ * Constructs a TitledBorder given its border and title text.
+ *
+ * @param border the border underneath the title, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @param title the title text, or <code>null</code> to use no title
+ * text.
+ */
+ public TitledBorder(Border border, String title)
+ {
+ this(border, title, LEADING, TOP,
+ /* titleFont */ null, /* titleColor */ null);
+ }
+
+
+ /**
+ * Constructs a TitledBorder given its border, title text, horizontal
+ * alignment, and vertical position.
+ *
+ * @param border the border underneath the title, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @param title the title text, or <code>null</code> to use no title
+ * text.
+ *
+ * @param titleJustification the horizontal alignment of the title
+ * text in relation to the border. The value must be one of
+ * {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
+ * {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
+
+ * @param titlePosition the vertical position of the title text
+ * in relation to the border. The value must be one of
+ * {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
+ * {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
+ * or {@link #DEFAULT_POSITION}.
+ *
+ * @throws IllegalArgumentException if <code>titleJustification</code>
+ * or <code>titlePosition</code> have an unsupported value.
+ */
+ public TitledBorder(Border border, String title, int titleJustification,
+ int titlePosition)
+ {
+ this(border, title, titleJustification, titlePosition,
+ /* titleFont */ null, /* titleColor */ null);
+ }
+
+
+ /**
+ * Constructs a TitledBorder given its border, title text, horizontal
+ * alignment, vertical position, and font.
+ *
+ * @param border the border underneath the title, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @param title the title text, or <code>null</code> to use no title
+ * text.
+ *
+ * @param titleJustification the horizontal alignment of the title
+ * text in relation to the border. The value must be one of
+ * {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
+ * {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
+ *
+ * @param titlePosition the vertical position of the title text
+ * in relation to the border. The value must be one of
+ * {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
+ * {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
+ * or {@link #DEFAULT_POSITION}.
+ *
+ * @param titleFont the font for the title text, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @throws IllegalArgumentException if <code>titleJustification</code>
+ * or <code>titlePosition</code> have an unsupported value.
+ */
+ public TitledBorder(Border border, String title, int titleJustification,
+ int titlePosition, Font titleFont)
+ {
+ this(border, title, titleJustification, titlePosition, titleFont,
+ /* titleColor */ null);
+ }
+
+
+ /**
+ * Constructs a TitledBorder given its border, title text, horizontal
+ * alignment, vertical position, font, and color.
+ *
+ * @param border the border underneath the title, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @param title the title text, or <code>null</code> to use no title
+ * text.
+ *
+ * @param titleJustification the horizontal alignment of the title
+ * text in relation to the border. The value must be one of
+ * {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
+ * {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
+ *
+ * @param titlePosition the vertical position of the title text
+ * in relation to the border. The value must be one of
+ * {@link #ABOVE_TOP}, {@link #TOP}, {@link #BELOW_TOP},
+ * {@link #ABOVE_BOTTOM}, {@link #BOTTOM}, {@link #BELOW_BOTTOM},
+ * or {@link #DEFAULT_POSITION}.
+ *
+ * @param titleFont the font for the title text, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @param titleColor the color for the title text, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @throws IllegalArgumentException if <code>titleJustification</code>
+ * or <code>titlePosition</code> have an unsupported value.
+ */
+ public TitledBorder(Border border, String title, int titleJustification,
+ int titlePosition, Font titleFont, Color titleColor)
+ {
+ this.border = border;
+ this.title = title;
+
+ /* Invoking the setter methods ensures that the newly constructed
+ * TitledBorder has valid property values.
+ */
+ setTitleJustification(titleJustification);
+ setTitlePosition(titlePosition);
+
+ this.titleFont = titleFont;
+ this.titleColor = titleColor;
+ }
+
+
+ /**
+ * Paints the border and the title text.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ Rectangle borderRect = new Rectangle(x + EDGE_SPACING, y + EDGE_SPACING,
+ width - (EDGE_SPACING * 2),
+ height - (EDGE_SPACING * 2));
+ Point textLoc = new Point();
+
+ // Save color and font.
+ Color savedColor = g.getColor();
+ Font savedFont = g.getFont();
+
+ // The font metrics.
+ Font font = getFont(c);
+ g.setFont(font);
+ FontMetrics fm = c.getFontMetrics(font);
+
+ layoutBorderWithTitle(c, fm, borderRect, textLoc);
+ paintBorderWithTitle(c, g, x, y, width, height, borderRect, textLoc, fm);
+
+ g.setColor(getTitleColor());
+ g.drawString(getTitle(), textLoc.x, textLoc.y);
+ g.setFont(savedFont);
+ g.setColor(savedColor);
+ }
+
+ /**
+ * Calculates the bounding box of the inner border and the location of the
+ * title string.
+ *
+ * @param c the component on which to paint the border
+ * @param fm the font metrics
+ * @param borderRect output parameter, holds the bounding box of the inner
+ * border on method exit
+ * @param textLoc output parameter, holds the location of the title text
+ * on method exit
+ */
+ private void layoutBorderWithTitle(Component c, FontMetrics fm,
+ Rectangle borderRect,
+ Point textLoc)
+ {
+ Border b = getBorder();
+
+ // The font metrics.
+ int fontHeight = fm.getHeight();
+ int fontDescent = fm.getDescent();
+ int fontAscent = fm.getAscent();
+ int titleWidth = fm.stringWidth(getTitle());
+
+ // The base insets.
+ Insets insets;
+ if (b == null)
+ insets = new Insets(0, 0, 0, 0);
+ else
+ insets = b.getBorderInsets(c);
+
+ // The offset of the border rectangle, dependend on the title placement.
+ int offset;
+
+ // Layout border and text vertically.
+ int titlePosition = getTitlePosition();
+ switch (titlePosition)
+ {
+ case ABOVE_BOTTOM:
+ textLoc.y = borderRect.y + borderRect.height - insets.bottom
+ - fontDescent - TEXT_SPACING;
+ break;
+ case BOTTOM:
+ borderRect.height -= fontHeight / 2;
+ textLoc.y = borderRect.y + borderRect.height - fontDescent
+ + (fontAscent + fontDescent - insets.bottom) / 2;
+ break;
+ case BELOW_BOTTOM:
+ borderRect.height -= fontHeight;
+ textLoc.y = borderRect.y + borderRect.height + fontAscent
+ + TEXT_SPACING;
+ break;
+ case ABOVE_TOP:
+ offset = fontAscent + fontDescent
+ + Math.max(EDGE_SPACING, TEXT_SPACING * 2) - EDGE_SPACING;
+ borderRect.y += offset;
+ borderRect.height -= offset;
+ textLoc.y = borderRect.y - (fontDescent + TEXT_SPACING);
+ break;
+ case BELOW_TOP:
+ textLoc.y = borderRect.y + insets.top + fontAscent + TEXT_SPACING;
+ break;
+ case TOP:
+ case DEFAULT_POSITION:
+ default:
+ offset = Math.max(0, ((fontAscent / 2) + TEXT_SPACING) - EDGE_SPACING);
+ borderRect.y += offset;
+ borderRect.height -= offset;
+ textLoc.y = borderRect.y - fontDescent
+ + (insets.top + fontAscent + fontDescent) / 2;
+ break;
+ }
+
+ // Layout border and text horizontally.
+ int justification = getTitleJustification();
+ // Adjust justification for LEADING and TRAILING depending on the direction
+ // of the component.
+ if (c.getComponentOrientation().isLeftToRight())
+ {
+ if (justification == LEADING || justification == DEFAULT_JUSTIFICATION)
+ justification = LEFT;
+ else if (justification == TRAILING)
+ justification = RIGHT;
+ }
+ else
+ {
+ if (justification == LEADING || justification == DEFAULT_JUSTIFICATION)
+ justification = RIGHT;
+ else if (justification == TRAILING)
+ justification = LEFT;
+ }
+
+ switch (justification)
+ {
+ case CENTER:
+ textLoc.x = borderRect.x + (borderRect.width - titleWidth) / 2;
+ break;
+ case RIGHT:
+ textLoc.x = borderRect.x + borderRect.width - titleWidth
+ - TEXT_INSET_H - insets.right;
+ break;
+ case LEFT:
+ default:
+ textLoc.x = borderRect.x + TEXT_INSET_H + insets.left;
+ }
+ }
+
+ /**
+ * Paints the border with the title.
+ *
+ * @param c the component to paint on
+ * @param g the graphics context used for paintin
+ * @param x the upper left corner of the whole border
+ * @param y the upper left corner of the whole border
+ * @param width the width of the whole border
+ * @param height the width of the whole border
+ * @param borderRect the bounding box of the inner border
+ * @param textLoc the location of the border title
+ * @param fm the font metrics of the title
+ */
+ private void paintBorderWithTitle(Component c, Graphics g, int x, int y,
+ int width, int height,
+ Rectangle borderRect, Point textLoc,
+ FontMetrics fm)
+ {
+ Border b = getBorder();
+ int fontDescent = fm.getDescent();
+ int fontAscent = fm.getAscent();
+ int titleWidth = fm.stringWidth(getTitle());
+
+ if (b != null)
+ {
+ // Paint border in segments, when the title is painted above the
+ // border.
+ if (((titlePosition == TOP || titlePosition == DEFAULT_POSITION)
+ && (borderRect.y > textLoc.y - fontAscent))
+ || (titlePosition == BOTTOM
+ && borderRect.y + borderRect.height < textLoc.y + fontDescent))
+ {
+ Rectangle clip = new Rectangle();
+ Rectangle saved = g.getClipBounds();
+
+ // Paint border left from the text.
+ clip.setBounds(saved);
+ SwingUtilities.computeIntersection(x, y, textLoc.x - x - 1,
+ height, clip);
+ if (! clip.isEmpty())
+ {
+ g.setClip(clip);
+ b.paintBorder(c, g, borderRect.x, borderRect.y,
+ borderRect.width,
+ borderRect.height);
+ }
+ // Paint border right from the text.
+ clip.setBounds(saved);
+ SwingUtilities.computeIntersection(textLoc.x + titleWidth + 1, y,
+ x + width - (textLoc.x + titleWidth + 1), height, clip);
+ if (! clip.isEmpty())
+ {
+ g.setClip(clip);
+ b.paintBorder(c, g, borderRect.x, borderRect.y,
+ borderRect.width,
+ borderRect.height);
+ }
+
+ if (titlePosition == TOP || titlePosition == DEFAULT_POSITION)
+ {
+ // Paint border below the text.
+ clip.setBounds(saved);
+ SwingUtilities.computeIntersection(textLoc.x - 1,
+ textLoc.y + fontDescent,
+ titleWidth + 2,
+ y + height - textLoc.y - fontDescent,
+ clip);
+ if (! clip.isEmpty())
+ {
+ g.setClip(clip);
+ b.paintBorder(c, g, borderRect.x, borderRect.y,
+ borderRect.width,
+ borderRect.height);
+ }
+
+ }
+ else
+ {
+ // Paint border above the text.
+ clip.setBounds(saved);
+ SwingUtilities.computeIntersection(textLoc.x - 1, y,
+ titleWidth + 2,
+ textLoc.y - fontDescent - y,
+ clip);
+ if (! clip.isEmpty())
+ {
+ g.setClip(clip);
+ b.paintBorder(c, g, borderRect.x, borderRect.y,
+ borderRect.width,
+ borderRect.height);
+ }
+
+ }
+ g.setClip(saved);
+ }
+ else
+ {
+ b.paintBorder(c, g, borderRect.x, borderRect.y, borderRect.width,
+ borderRect.height);
+ }
+ }
+ }
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, new Insets(0, 0, 0, 0));
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ // Initialize insets with the insets from our border.
+ Border border = getBorder();
+ if (border != null)
+ {
+ if (border instanceof AbstractBorder)
+ {
+ AbstractBorder aBorder = (AbstractBorder) border;
+ aBorder.getBorderInsets(c, insets);
+ }
+ else
+ {
+ Insets i = border.getBorderInsets(c);
+ insets.top = i.top;
+ insets.bottom = i.bottom;
+ insets.left = i.left;
+ insets.right = i.right;
+ }
+ }
+ else
+ {
+ insets.top = 0;
+ insets.bottom = 0;
+ insets.left = 0;
+ insets.right = 0;
+ }
+
+ // Add spacing.
+ insets.top += EDGE_SPACING + TEXT_SPACING;
+ insets.bottom += EDGE_SPACING + TEXT_SPACING;
+ insets.left += EDGE_SPACING + TEXT_SPACING;
+ insets.right += EDGE_SPACING + TEXT_SPACING;
+
+ String title = getTitle();
+ if (c != null && title != null && !title.equals(""))
+ {
+ Font font = getFont(c);
+ FontMetrics fm = c.getFontMetrics(font);
+ int ascent = fm.getAscent();
+ int descent = fm.getDescent();
+ int height = fm.getHeight();
+ switch (getTitlePosition())
+ {
+ case ABOVE_BOTTOM:
+ insets.bottom += ascent + descent + TEXT_SPACING;
+ break;
+ case BOTTOM:
+ insets.bottom += ascent + descent;
+ break;
+ case BELOW_BOTTOM:
+ insets.bottom += height;
+ break;
+ case ABOVE_TOP:
+ insets.top += ascent + descent +
+ Math.max(EDGE_SPACING, TEXT_SPACING * 2)
+ - EDGE_SPACING;
+ break;
+ case BELOW_TOP:
+ insets.top += ascent + descent + TEXT_SPACING;
+ break;
+ case TOP:
+ case DEFAULT_POSITION:
+ default:
+ insets.top += ascent + descent;
+ }
+ }
+ return insets;
+ }
+
+
+ /**
+ * Returns <code>false</code>, indicating that there are pixels inside
+ * the area of this border where the background shines through.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isBorderOpaque()
+ {
+ /* Note that the AbstractBorder.isBorderOpaque would also return
+ * false, so there is actually no need to override the inherited
+ * implementation. However, GNU Classpath strives for exact
+ * compatibility with the Sun reference implementation, which
+ * overrides isBorderOpaque for unknown reasons.
+ */
+ return false;
+ }
+
+
+ /**
+ * Returns the text of the title.
+ *
+ * @return the title text, or <code>null</code> if no title is
+ * displayed.
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+
+ /**
+ * Retrieves the border underneath the title. If no border has been
+ * set, or if it has been set to<code>null</code>, the current
+ * {@link javax.swing.LookAndFeel} will be asked for a border
+ * using the key <code>TitledBorder.border</code>.
+ *
+ * @return a border, or <code>null</code> if the current LookAndFeel
+ * does not provide a border for the key
+ * <code>TitledBorder.border</code>.
+ *
+ * @see javax.swing.UIManager#getBorder(Object)
+ */
+ public Border getBorder()
+ {
+ if (border != null)
+ return border;
+
+ return UIManager.getBorder("TitledBorder.border");
+ }
+
+
+ /**
+ * Returns the vertical position of the title text in relation
+ * to the border.
+ *
+ * @return one of the values {@link #ABOVE_TOP}, {@link #TOP},
+ * {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM}, {@link #BOTTOM},
+ * {@link #BELOW_BOTTOM}, or {@link #DEFAULT_POSITION}.
+ */
+ public int getTitlePosition()
+ {
+ return titlePosition;
+ }
+
+
+ /**
+ * Returns the horizontal alignment of the title text in relation to
+ * the border.
+ *
+ * @return one of the values {@link #LEFT}, {@link #CENTER}, {@link
+ * #RIGHT}, {@link #LEADING}, {@link #TRAILING}, or {@link
+ * #DEFAULT_JUSTIFICATION}.
+ */
+ public int getTitleJustification()
+ {
+ return titleJustification;
+ }
+
+
+ /**
+ * Retrieves the font for displaying the title text. If no font has
+ * been set, or if it has been set to<code>null</code>, the current
+ * {@link javax.swing.LookAndFeel} will be asked for a font
+ * using the key <code>TitledBorder.font</code>.
+ *
+ * @return a font, or <code>null</code> if the current LookAndFeel
+ * does not provide a font for the key
+ * <code>TitledBorder.font</code>.
+ *
+ * @see javax.swing.UIManager#getFont(Object)
+ */
+ public Font getTitleFont()
+ {
+ if (titleFont != null)
+ return titleFont;
+
+ return UIManager.getFont("TitledBorder.font");
+ }
+
+
+ /**
+ * Retrieves the color for displaying the title text. If no color has
+ * been set, or if it has been set to<code>null</code>, the current
+ * {@link javax.swing.LookAndFeel} will be asked for a color
+ * using the key <code>TitledBorder.titleColor</code>.
+ *
+ * @return a color, or <code>null</code> if the current LookAndFeel
+ * does not provide a color for the key
+ * <code>TitledBorder.titleColor</code>.
+ *
+ * @see javax.swing.UIManager#getColor(Object)
+ */
+ public Color getTitleColor()
+ {
+ if (titleColor != null)
+ return titleColor;
+
+ return UIManager.getColor("TitledBorder.titleColor");
+ }
+
+
+ /**
+ * Sets the text of the title.
+ *
+ * @param title the new title text, or <code>null</code> for displaying
+ * no text at all.
+ */
+ public void setTitle(String title)
+ {
+ // Swing borders are not JavaBeans, thus no need to fire an event.
+ this.title = title;
+ }
+
+
+ /**
+ * Sets the border underneath the title.
+ *
+ * @param border a border, or <code>null</code> to use the
+ * border that is supplied by the current LookAndFeel.
+ *
+ * @see #getBorder()
+ */
+ public void setBorder(Border border)
+ {
+ // Swing borders are not JavaBeans, thus no need to fire an event.
+ this.border = border;
+ }
+
+
+ /**
+ * Sets the vertical position of the title text in relation
+ * to the border.
+ *
+ * @param titlePosition one of the values {@link #ABOVE_TOP},
+ * {@link #TOP}, {@link #BELOW_TOP}, {@link #ABOVE_BOTTOM},
+ * {@link #BOTTOM}, {@link #BELOW_BOTTOM},
+ * or {@link #DEFAULT_POSITION}.
+ *
+ * @throws IllegalArgumentException if an unsupported value is passed
+ * for <code>titlePosition</code>.
+ */
+ public void setTitlePosition(int titlePosition)
+ {
+ if ((titlePosition < DEFAULT_POSITION) || (titlePosition > BELOW_BOTTOM))
+ throw new IllegalArgumentException(titlePosition
+ + " is not a valid title position.");
+
+ // Swing borders are not JavaBeans, thus no need to fire an event.
+ this.titlePosition = titlePosition;
+ }
+
+
+ /**
+ * Sets the horizontal alignment of the title text in relation to the border.
+ *
+ * @param titleJustification the new alignment, which must be one of
+ * {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, {@link #LEADING},
+ * {@link #TRAILING}, or {@link #DEFAULT_JUSTIFICATION}.
+ *
+ * @throws IllegalArgumentException if an unsupported value is passed
+ * for <code>titleJustification</code>.
+ */
+ public void setTitleJustification(int titleJustification)
+ {
+ if ((titleJustification < DEFAULT_JUSTIFICATION)
+ || (titleJustification > TRAILING))
+ throw new IllegalArgumentException(titleJustification
+ + " is not a valid title justification.");
+
+ // Swing borders are not JavaBeans, thus no need to fire an event.
+ this.titleJustification = titleJustification;
+ }
+
+
+ /**
+ * Sets the font for displaying the title text.
+ *
+ * @param titleFont the font, or <code>null</code> to use the font
+ * provided by the current {@link javax.swing.LookAndFeel}.
+ *
+ * @see #getTitleFont()
+ */
+ public void setTitleFont(Font titleFont)
+ {
+ // Swing borders are not JavaBeans, thus no need to fire an event.
+ this.titleFont = titleFont;
+ }
+
+
+ /**
+ * Sets the color for displaying the title text.
+ *
+ * @param titleColor the color, or <code>null</code> to use the color
+ * provided by the current {@link javax.swing.LookAndFeel}.
+ *
+ * @see #getTitleColor()
+ */
+ public void setTitleColor(Color titleColor)
+ {
+ // Swing borders are not JavaBeans, thus no need to fire an event.
+ this.titleColor = titleColor;
+ }
+
+
+ /**
+ * Calculates the minimum size needed for displaying the border
+ * and its title.
+ *
+ * @param c the Component for which this TitledBorder constitutes
+ * a border.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(Component c)
+ {
+ Insets i = getBorderInsets(c);
+ Dimension minSize = new Dimension(i.left + i.right, i.top + i.bottom);
+ Font font = getFont(c);
+ FontMetrics fm = c.getFontMetrics(font);
+ int titleWidth = fm.stringWidth(getTitle());
+ switch (getTitlePosition())
+ {
+ case ABOVE_TOP:
+ case BELOW_BOTTOM:
+ minSize.width = Math.max(minSize.width, titleWidth);
+ break;
+ case BELOW_TOP:
+ case ABOVE_BOTTOM:
+ case TOP:
+ case BOTTOM:
+ case DEFAULT_POSITION:
+ default:
+ minSize.width += titleWidth;
+ }
+ return minSize;
+ }
+
+
+ /**
+ * Returns the font that is used for displaying the title text for
+ * a given Component.
+ *
+ * @param c the Component for which this TitledBorder is the border.
+ *
+ * @return The font returned by {@link #getTitleFont()}, or a fallback
+ * if {@link #getTitleFont()} returned <code>null</code>.
+ */
+ protected Font getFont(Component c)
+ {
+ Font f;
+
+ f = getTitleFont();
+ if (f != null)
+ return f;
+
+ return new Font("Dialog", Font.PLAIN, 12);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/border/doc-files/BevelBorder-1.png b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-1.png
new file mode 100644
index 000000000..8c3e4b277
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/BevelBorder-2.png b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-2.png
new file mode 100644
index 000000000..ac52d47a7
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/BevelBorder-3.png b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-3.png
new file mode 100644
index 000000000..dd531ff68
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-3.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/EmptyBorder-1.png b/libjava/classpath/javax/swing/border/doc-files/EmptyBorder-1.png
new file mode 100644
index 000000000..2f21140b1
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/EmptyBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/EtchedBorder-1.png b/libjava/classpath/javax/swing/border/doc-files/EtchedBorder-1.png
new file mode 100644
index 000000000..6b1085c1b
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/EtchedBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/EtchedBorder-2.png b/libjava/classpath/javax/swing/border/doc-files/EtchedBorder-2.png
new file mode 100644
index 000000000..36b07056c
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/EtchedBorder-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/LineBorder-1.png b/libjava/classpath/javax/swing/border/doc-files/LineBorder-1.png
new file mode 100644
index 000000000..45b8afc61
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/LineBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/MatteBorder-1.png b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-1.png
new file mode 100644
index 000000000..fc49f4b43
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/MatteBorder-2.png b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-2.png
new file mode 100644
index 000000000..9c2c8d955
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/MatteBorder-3.png b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-3.png
new file mode 100644
index 000000000..62089eab5
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-3.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/MatteBorder-4.png b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-4.png
new file mode 100644
index 000000000..bffbcc84e
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-4.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/MatteBorder-5.png b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-5.png
new file mode 100644
index 000000000..807eee8df
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-5.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/MatteBorder-6.png b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-6.png
new file mode 100644
index 000000000..2c4ce1e3d
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-6.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-1.png b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-1.png
new file mode 100644
index 000000000..4404bf98c
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-2.png b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-2.png
new file mode 100644
index 000000000..ebd9849e6
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-3.png b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-3.png
new file mode 100644
index 000000000..9f939b7a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-3.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/border/package.html b/libjava/classpath/javax/swing/border/package.html
new file mode 100644
index 000000000..de8479a6a
--- /dev/null
+++ b/libjava/classpath/javax/swing/border/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.border package.
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.border</title></head>
+
+<body>
+<p>Provides borders for use by components in the <code>javax.swing</code>
+package.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java b/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java
new file mode 100644
index 000000000..a554208ad
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java
@@ -0,0 +1,193 @@
+/* AbstractColorChooserPanel.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.colorchooser;
+
+import java.awt.Color;
+import java.awt.Graphics;
+
+import javax.swing.Icon;
+import javax.swing.JColorChooser;
+import javax.swing.JPanel;
+
+/**
+ * AbstractColorChooserPanel
+ *
+ * @author Andrew Selkirk
+ * @version 1.0
+ */
+public abstract class AbstractColorChooserPanel extends JPanel
+{
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -977469671210173863L;
+
+ /** The chooser associated with this panel. */
+ private JColorChooser chooser;
+
+ /**
+ * This is the constructor for the AbstractColorChooserPanel.
+ */
+ public AbstractColorChooserPanel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method returns the name displayed in the tab for this chooser panel.
+ *
+ * @return The name displayed in the JTabbedPane's tabs.
+ */
+ public abstract String getDisplayName();
+
+ /**
+ * Returns the key code for the mnemonic for this panel. This method returns
+ * zero to indicate no mnemonic, subclasses can override this.
+ *
+ * @return <code>0</code>, to indicate no mnemonic key code.
+ *
+ * @see #getDisplayedMnemonicIndex()
+ * @since 1.4
+ */
+ public int getMnemonic()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the index of the character in the display name that is the
+ * mnemonic. This method returns <code>-1</code> to indicate no mnemonic,
+ * subclasses can override.
+ *
+ * @return <code>-1</code>, to indicate no mnemonic.
+ *
+ * @see #getDisplayName()
+ * @see #getMnemonic()
+ * @since 1.4
+ */
+ public int getDisplayedMnemonicIndex()
+ {
+ return -1;
+ }
+
+ /**
+ * This method updates the chooser panel when the JColorChooser's color has
+ * changed.
+ */
+ public abstract void updateChooser();
+
+ /**
+ * This method constructs and does any initialization necessary for the
+ * chooser panel.
+ */
+ protected abstract void buildChooser();
+
+ /**
+ * This method sets the small icon used in the JTabbedPane for this chooser
+ * panel.
+ *
+ * @return The small icon used in the JTabbedPane.
+ */
+ public abstract Icon getSmallDisplayIcon();
+
+ /**
+ * This method sets the large icon useed in the jTabbedPane for this chooser
+ * panel.
+ *
+ * @return The large icon.
+ */
+ public abstract Icon getLargeDisplayIcon();
+
+ /**
+ * This method installs the chooser panel for the given JColorChooser.
+ *
+ * @param chooser The JColorChooser that will have this panel installed.
+ */
+ public void installChooserPanel(JColorChooser chooser)
+ {
+ this.chooser = chooser;
+ buildChooser();
+ } // installChooserPanel()
+
+ /**
+ * This method removes the chooser panel from the given JColorChooser and
+ * does any necessary clean up for the chooser panel.
+ *
+ * @param chooser The JColorChooser that is having this panel removed.
+ */
+ public void uninstallChooserPanel(JColorChooser chooser)
+ {
+ this.chooser = null;
+ } // uninstallChooserPanel()
+
+ /**
+ * This method returns the ColorSelectionModel for the JColorChooser
+ * associated with this chooser panel.
+ *
+ * @return The ColorSelectionModel for the JColorChooser associated with
+ * this chooser panel.
+ */
+ public ColorSelectionModel getColorSelectionModel()
+ {
+ if (chooser != null)
+ return chooser.getSelectionModel();
+ return null;
+ } // getColorSelectionModel()
+
+ /**
+ * This method returns the current color stored in the model for this
+ * chooser panel.
+ *
+ * @return The current color.
+ */
+ protected Color getColorFromModel()
+ {
+ if (chooser != null)
+ return chooser.getColor();
+ return null;
+ } // getColorFromModel()
+
+ /**
+ * This method paints the chooser panel.
+ *
+ * @param graphics The Graphics object to paint with.
+ */
+ public void paint(Graphics graphics)
+ {
+ super.paint(graphics);
+ } // paint()
+} // AbstractColorChooserPanel
diff --git a/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java b/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java
new file mode 100644
index 000000000..ef8ea21f1
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java
@@ -0,0 +1,86 @@
+/* ColorChooserComponentFactory.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.colorchooser;
+
+import javax.swing.JComponent;
+
+
+/**
+ * ColorChooserComponentFactory
+ *
+ * @author Andrew Selkirk
+ * @version 1.0
+ */
+public class ColorChooserComponentFactory
+{
+ /**
+ * Constructor ColorChooserComponentFactory
+ */
+ private ColorChooserComponentFactory()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method returns the three default chooser panels to be used in
+ * JColorChooser.
+ *
+ * @return The default chooser panels.
+ */
+ public static AbstractColorChooserPanel[] getDefaultChooserPanels()
+ {
+ AbstractColorChooserPanel[] values =
+ {
+ new DefaultSwatchChooserPanel(),
+ new DefaultHSBChooserPanel(),
+ new DefaultRGBChooserPanel()
+ };
+ return values;
+ }
+
+ /**
+ * This method returns the default preview panel to be used with
+ * JColorChoosers.
+ *
+ * @return The default preview panel.
+ */
+ public static JComponent getPreviewPanel()
+ {
+ return new DefaultPreviewPanel();
+ } // getPreviewPanel()
+} // ColorChooserComponentFactory
diff --git a/libjava/classpath/javax/swing/colorchooser/ColorSelectionModel.java b/libjava/classpath/javax/swing/colorchooser/ColorSelectionModel.java
new file mode 100644
index 000000000..7831e4790
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/ColorSelectionModel.java
@@ -0,0 +1,86 @@
+/* ColorSelectionModel.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.colorchooser;
+
+import java.awt.Color;
+
+import javax.swing.JColorChooser;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * A model that is used by the {@link JColorChooser} component to represent the
+ * selected color.
+ *
+ * @author Andrew Selkirk
+ */
+public interface ColorSelectionModel
+{
+
+ /**
+ * Returns the selected color.
+ *
+ * @return The selected color.
+ */
+ Color getSelectedColor();
+
+ /**
+ * Sets the selected color.
+ *
+ * @param color the selected color.
+ */
+ void setSelectedColor(Color color);
+
+ /**
+ * Registers a listener to receive {@link ChangeEvent} notifications
+ * from this model.
+ *
+ * @param listener the listener.
+ */
+ void addChangeListener(ChangeListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives {@link ChangeEvent}
+ * notifications from this action.
+ *
+ * @param listener the listener.
+ */
+ void removeChangeListener(ChangeListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/colorchooser/DefaultColorSelectionModel.java b/libjava/classpath/javax/swing/colorchooser/DefaultColorSelectionModel.java
new file mode 100644
index 000000000..07493f6d6
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/DefaultColorSelectionModel.java
@@ -0,0 +1,163 @@
+/* DefaultColorSelectionModel.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.colorchooser;
+
+import java.awt.Color;
+import java.io.Serializable;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * This is the default implementation of the ColorSelectionModel interface
+ * that JColorChoosers use.
+ *
+ * @author Andrew Selkirk
+ * @version 1.0
+ */
+public class DefaultColorSelectionModel implements ColorSelectionModel,
+ Serializable
+{
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -8117143602864778804L;
+
+ /** The currently selected color. */
+ private Color selectedColor;
+
+ /** The ChangeEvent fired to all ChangeListeners. */
+ protected transient ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /** The list of listeners. */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Creates a new color selection model with the default white color.
+ */
+ public DefaultColorSelectionModel()
+ {
+ this(Color.white);
+ }
+
+ /**
+ * Creates a new color selection model with a given selected color.
+ *
+ * @param color The initial color.
+ *
+ * @throws Error If the color is null.
+ */
+ public DefaultColorSelectionModel(Color color)
+ {
+ super();
+ if (color == null)
+ throw new Error("ColorSelectionModel cannot be set to have null color.");
+ this.selectedColor = color;
+ }
+
+ /**
+ * Returns the selected color.
+ *
+ * @return The selected color.
+ */
+ public Color getSelectedColor()
+ {
+ return selectedColor;
+ }
+
+ /**
+ * This method sets the color.
+ *
+ * @param color The color to set.
+ *
+ * @throws Error If the color is set.
+ */
+ public void setSelectedColor(Color color)
+ {
+ if (color == null)
+ throw new Error("ColorSelectionModel cannot be set to have null color.");
+ if (color != selectedColor)
+ {
+ this.selectedColor = color;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Adds a listener to this model.
+ *
+ * @param listener The listener to add.
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Removes a listener from this model.
+ *
+ * @param listener The listener to remove.
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Returns all currently added <code>ChangeListener</code> objects.
+ *
+ * @return Array of <code>ChangeListener</code> objects.
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Calls all the <code>stateChanged()</code> method of all added
+ * <code>ChangeListener</code> objects with <code>changeEvent</code> as
+ * argument.
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].stateChanged(changeEvent);
+ }
+}
diff --git a/libjava/classpath/javax/swing/colorchooser/DefaultHSBChooserPanel.java b/libjava/classpath/javax/swing/colorchooser/DefaultHSBChooserPanel.java
new file mode 100644
index 000000000..d035fe194
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/DefaultHSBChooserPanel.java
@@ -0,0 +1,890 @@
+/* DefaultHSBChooserPanel.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.colorchooser;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.GridLayout;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.awt.image.MemoryImageSource;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonGroup;
+import javax.swing.Icon;
+import javax.swing.JColorChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JSlider;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SwingConstants;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * This is the Default HSB Panel displayed in the JColorChooser.
+ */
+class DefaultHSBChooserPanel extends AbstractColorChooserPanel
+{
+ /** The gradient image displayed.
+ * This is package-private to avoid an accessor method. */
+ transient Image gradientImage;
+
+ /** The Panel that holds the gradient image. */
+ private transient JPanel gradientPanel;
+
+ /** The track gradient image.
+ * This is package-private to avoid an accessor method. */
+ transient Image trackImage;
+
+ /** The panel that holds the track. */
+ private transient JPanel trackPanel;
+
+ /** The slider for the locked HSB value.
+ * This is package-private to avoid an accessor method. */
+ transient JSlider slider;
+
+ /** The RadioButton that controls the Hue.
+ * This is package-private to avoid an accessor method. */
+ transient JRadioButton hRadio;
+
+ /** The RadioButton that controls the Saturation.
+ * This is package-private to avoid an accessor method. */
+ transient JRadioButton sRadio;
+
+ /** The RadioButton that controls the Brightness.
+ * This is package-private to avoid an accessor method. */
+ transient JRadioButton bRadio;
+
+ /** The JSpinner that controls the Hue.
+ * This is package-private to avoid an accessor method. */
+ transient JSpinner hSpinner;
+
+ /** The JSpinner that controls the Saturation.
+ * This is package-private to avoid an accessor method. */
+ transient JSpinner sSpinner;
+
+ /** The JSpinner that controls the Brightness.
+ * This is package-private to avoid an accessor method. */
+ transient JSpinner bSpinner;
+
+ /** The default width of the gradient image. */
+ private static final int imgWidth = 200;
+
+ /** The default height of the gradient image. */
+ private static final int imgHeight = 200;
+
+ /** The default width of the track gradient. */
+ private static final int trackWidth = 30;
+
+ /** The JLabel for Red. */
+ private static final JLabel R = new JLabel("R");
+
+ /** The JLabel for Green. */
+ private static final JLabel G = new JLabel("G");
+
+ /** The JLabel for Blue. */
+ private static final JLabel B = new JLabel("B");
+
+ // FIXME: Should be textfields.
+
+ /** The JLabel that displays the value of Red. */
+ private transient JLabel rFull;
+
+ /** The JLabel that displays the value of Green. */
+ private transient JLabel gFull;
+
+ /** The JLabel that displays the value of Blue. */
+ private transient JLabel bFull;
+
+ /** The point that is displayed in the gradient image.
+ * Package-private to avoid an accessor method.
+ */
+ transient Point gradientPoint = new Point();
+
+ /**
+ * This indicates that the change to the slider or point is triggered
+ * internally.
+ * This is package-private to avoid an accessor method.
+ */
+ transient boolean internalChange = false;
+
+ /** This indicates that the change to the spinner is triggered
+ * internally.
+ * This is package-private to avoid an accessor method. */
+ transient boolean spinnerTrigger = false;
+
+ /** This int identifies which spinner is currently locked.
+ * This is package-private to avoid an accessor method. */
+ transient int locked = -1;
+
+ /** This value indicates that the Hue spinner is locked. */
+ static final int HLOCKED = 0;
+
+ /** This value indicates that the Saturation spinner is locked. */
+ static final int SLOCKED = 1;
+
+ /** This value indicates that the Brightness spinner is locked. */
+ static final int BLOCKED = 2;
+
+ /**
+ * This method indicates that the mouse event is in the process of being
+ * handled.
+ * This is package-private to avoid an accessor method.
+ */
+ transient boolean handlingMouse;
+
+ /**
+ * This helper class handles mouse events on the gradient image.
+ */
+ class MainGradientMouseListener extends MouseAdapter
+ implements MouseMotionListener
+ {
+ /**
+ * This method is called when the mouse is pressed over the gradient
+ * image. The JColorChooser is then updated with new HSB values.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ gradientPoint = e.getPoint();
+ update(e.getPoint());
+ }
+
+ /**
+ * This method is called when the mouse is dragged over the gradient
+ * image. The JColorChooser is then updated with the new HSB values.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ Point p = e.getPoint();
+ if (p.x < 0 || p.y < 0 || p.y > imgHeight || p.x > imgWidth)
+ return;
+
+ gradientPoint = p;
+ update(p);
+ }
+
+ /**
+ * This method is called when the mouse is moved over the gradient image.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method updates the JColorChooser with the new values.
+ *
+ * @param p The Point where the MouseEvent occurred.
+ */
+ private void update(Point p)
+ {
+ handlingMouse = true;
+ if (hSpinner.isEnabled())
+ updateH(p);
+ else if (sSpinner.isEnabled())
+ updateS(p);
+ else
+ updateB(p);
+ handlingMouse = false;
+ }
+
+ /**
+ * This method updates the SB values if Hue is locked.
+ *
+ * @param p The point where the MouseEvent occurred.
+ */
+ private void updateH(Point p)
+ {
+ float s = (imgWidth - p.x * 1f) / imgWidth;
+ float b = (imgHeight - p.y * 1f) / imgHeight;
+
+ // Avoid two changes to the model by changing internalChange to true.
+ internalChange = true;
+ sSpinner.setValue(new Integer((int) (s * 100)));
+ internalChange = false;
+ bSpinner.setValue(new Integer((int) (b * 100)));
+
+ revalidate();
+ }
+
+ /**
+ * This method updates the HB values if Saturation is locked.
+ *
+ * @param p The point where the MouseEvent occurred.
+ */
+ private void updateS(Point p)
+ {
+ float h = p.x * 1f / imgWidth;
+ float b = (imgHeight - p.y * 1f) / imgHeight;
+
+ internalChange = true;
+ hSpinner.setValue(new Integer((int) (h * 365)));
+ internalChange = false;
+ bSpinner.setValue(new Integer((int) (b * 100)));
+
+ revalidate();
+ }
+
+ /**
+ * This method updates the HS values if Brightness is locked.
+ *
+ * @param p The point where the MouseEvent occurred.
+ */
+ private void updateB(Point p)
+ {
+ float h = p.x * 1f / imgWidth;
+ float s = (imgHeight - p.y * 1f) / imgHeight;
+
+ internalChange = true;
+ hSpinner.setValue(new Integer((int) (h * 365)));
+ internalChange = false;
+ sSpinner.setValue(new Integer((int) (s * 100)));
+
+ revalidate();
+ }
+ }
+
+ /**
+ * This method listens for slider value changes.
+ */
+ class SliderChangeListener implements ChangeListener
+ {
+ /**
+ * This method is called when the slider value changes. It should change
+ * the color of the JColorChooser.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ if (internalChange)
+ return;
+
+ Integer value = new Integer(slider.getValue());
+
+ switch (locked)
+ {
+ case HLOCKED:
+ hSpinner.setValue(value);
+ break;
+ case SLOCKED:
+ sSpinner.setValue(value);
+ break;
+ case BLOCKED:
+ bSpinner.setValue(value);
+ break;
+ }
+ }
+ }
+
+ /**
+ * This helper class determines the active JSpinner.
+ */
+ class RadioStateListener implements ChangeListener
+ {
+ /**
+ * This method is called when there is a new JRadioButton that was
+ * selected. As a result, it should activate the associated JSpinner.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ JSpinner change;
+ if (e.getSource() == hRadio)
+ {
+ locked = HLOCKED;
+ change = hSpinner;
+ }
+ else if (e.getSource() == sRadio)
+ {
+ locked = SLOCKED;
+ change = sSpinner;
+ }
+ else
+ {
+ locked = BLOCKED;
+ change = bSpinner;
+ }
+
+ change.setEnabled(((AbstractButton) e.getSource()).isSelected());
+ updateSlider();
+ updateTrack();
+ updateImage();
+ repaint();
+ }
+ }
+
+ /**
+ * This class listens to the JSpinners for changes.
+ */
+ class ImageScrollListener implements ChangeListener
+ {
+ /**
+ * This method is called whenever one of the JSpinner values change. The
+ * JColorChooser should be updated with the new HSB values.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ if (internalChange)
+ return;
+
+ float h = ((Number) hSpinner.getValue()).intValue() / 360f;
+ float s = ((Number) sSpinner.getValue()).intValue() / 100f;
+ float b = ((Number) bSpinner.getValue()).intValue() / 100f;
+
+ spinnerTrigger = true;
+ getColorSelectionModel().setSelectedColor(new Color(Color.HSBtoRGB(h, s,
+ b)));
+ spinnerTrigger = false;
+
+ if (! handlingMouse && slider != null && ! slider.getValueIsAdjusting())
+ {
+ updateImage();
+ updateTrack();
+ }
+ repaint();
+ }
+ }
+
+ /**
+ * Creates a new DefaultHSBChooserPanel object.
+ */
+ DefaultHSBChooserPanel()
+ {
+ super();
+ }
+
+ /**
+ * This method returns the name displayed by the JColorChooser tab that
+ * holds this panel.
+ *
+ * @return The name displayed in the JColorChooser tab.
+ */
+ public String getDisplayName()
+ {
+ return "HSB";
+ }
+
+ /**
+ * This method updates the various components inside the HSBPanel (the
+ * JSpinners, the JSlider, and the gradient image point) with updated
+ * values when the JColorChooser color value changes.
+ */
+ public void updateChooser()
+ {
+ Color c = getColorSelectionModel().getSelectedColor();
+
+ float[] hsbVals = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(),
+ null);
+
+ internalChange = true;
+
+ if (! spinnerTrigger)
+ {
+ hSpinner.setValue(new Integer((int) (hsbVals[0] * 360)));
+ sSpinner.setValue(new Integer((int) (hsbVals[1] * 100)));
+ bSpinner.setValue(new Integer((int) (hsbVals[2] * 100)));
+ }
+
+ switch (locked)
+ {
+ case HLOCKED:
+ if (slider != null)
+ slider.setValue(((Number) hSpinner.getValue()).intValue());
+ if (! handlingMouse)
+ {
+ gradientPoint.x = (int) ((1
+ - ((Number) sSpinner.getValue()).intValue() / 100f) * imgWidth);
+ gradientPoint.y = (int) ((1
+ - ((Number) bSpinner.getValue()).intValue() / 100f) * imgHeight);
+ }
+ break;
+ case SLOCKED:
+ if (slider != null)
+ slider.setValue(((Number) sSpinner.getValue()).intValue());
+ if (! handlingMouse)
+ {
+ gradientPoint.x = (int) (((Number) hSpinner.getValue()).intValue() / 360f * imgWidth);
+ gradientPoint.y = (int) ((1
+ - ((Number) bSpinner.getValue()).intValue() / 100f) * imgHeight);
+ }
+ break;
+ case BLOCKED:
+ if (slider != null)
+ slider.setValue(((Number) bSpinner.getValue()).intValue());
+ if (! handlingMouse)
+ {
+ gradientPoint.x = (int) (((Number) hSpinner.getValue()).intValue() / 360f * imgWidth);
+ gradientPoint.y = (int) ((1
+ - ((Number) sSpinner.getValue()).intValue() / 100f) * imgHeight);
+ }
+ break;
+ }
+ internalChange = false;
+
+ if (! handlingMouse && slider != null && ! slider.getValueIsAdjusting())
+ updateImage();
+
+ if (! handlingMouse || locked != HLOCKED)
+ updateTrack();
+ updateTextFields();
+ }
+
+ /**
+ * This method builds the DefaultHSBChooserPanel.
+ */
+ protected void buildChooser()
+ {
+ setLayout(new BorderLayout());
+
+ add(buildRightPanel(), BorderLayout.EAST);
+
+ JPanel container = new JPanel();
+ container.setLayout(new BorderLayout());
+
+ gradientPanel = new JPanel()
+ {
+ public Dimension getPreferredSize()
+ {
+ return new Dimension(imgWidth, imgHeight);
+ }
+
+ public void paint(Graphics g)
+ {
+ if (gradientImage != null)
+ g.drawImage(gradientImage, 0, 0, this);
+
+ Color saved = g.getColor();
+ g.setColor(Color.WHITE);
+ g.drawOval(gradientPoint.x - 3, gradientPoint.y - 3, 6, 6);
+ g.setColor(saved);
+ }
+ };
+
+ MouseAdapter ml = new MainGradientMouseListener();
+ gradientPanel.addMouseListener(ml);
+ gradientPanel.addMouseMotionListener((MouseMotionListener) ml);
+
+ trackPanel = new JPanel()
+ {
+ public Dimension getPreferredSize()
+ {
+ return new Dimension(trackWidth, imgHeight);
+ }
+
+ public void paint(Graphics g)
+ {
+ if (trackImage != null)
+ g.drawImage(trackImage, 0, 0, this);
+ }
+ };
+
+ slider = new JSlider();
+ slider.setPaintTrack(false);
+ slider.setPaintTicks(false);
+
+ slider.setOrientation(SwingConstants.VERTICAL);
+
+ updateSlider();
+
+ container.add(gradientPanel, BorderLayout.WEST);
+ container.add(slider, BorderLayout.CENTER);
+ container.add(trackPanel, BorderLayout.EAST);
+
+ add(container, BorderLayout.WEST);
+ slider.addChangeListener(new SliderChangeListener());
+ repaint();
+ }
+
+ /**
+ * This method uninstalls the DefaultHSBPanel.
+ *
+ * @param chooser The JColorChooser to remove this panel from.
+ */
+ public void uninstallChooserPanel(JColorChooser chooser)
+ {
+ trackImage = null;
+ gradientImage = null;
+ gradientPanel = null;
+ slider = null;
+
+ hSpinner = null;
+ sSpinner = null;
+ bSpinner = null;
+
+ hRadio = null;
+ sRadio = null;
+ bRadio = null;
+
+ removeAll();
+ super.uninstallChooserPanel(chooser);
+ }
+
+ /**
+ * This helper method creates the right side panel (the panel with the
+ * Spinners and TextFields).
+ *
+ * @return The right side panel.
+ */
+ private Container buildRightPanel()
+ {
+ JPanel container = new JPanel();
+ container.setLayout(new GridLayout(6, 2));
+
+ hRadio = new JRadioButton("H");
+ sRadio = new JRadioButton("S");
+ bRadio = new JRadioButton("B");
+
+ ButtonGroup group = new ButtonGroup();
+ group.add(hRadio);
+ group.add(sRadio);
+ group.add(bRadio);
+
+ hSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 359, 1));
+ sSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1));
+ bSpinner = new JSpinner(new SpinnerNumberModel(100, 0, 100, 1));
+
+ hSpinner.setEnabled(false);
+ sSpinner.setEnabled(false);
+ bSpinner.setEnabled(false);
+
+ ChangeListener cl = new RadioStateListener();
+ ChangeListener scroll = new ImageScrollListener();
+
+ hRadio.addChangeListener(cl);
+ sRadio.addChangeListener(cl);
+ bRadio.addChangeListener(cl);
+
+ hSpinner.addChangeListener(scroll);
+ sSpinner.addChangeListener(scroll);
+ bSpinner.addChangeListener(scroll);
+
+ hRadio.setSelected(true);
+
+ container.add(hRadio);
+ container.add(hSpinner);
+
+ container.add(sRadio);
+ container.add(sSpinner);
+
+ container.add(bRadio);
+ container.add(bSpinner);
+
+ rFull = new JLabel("red full");
+ gFull = new JLabel("green full");
+ bFull = new JLabel("blue full");
+
+ container.add(R);
+ container.add(rFull);
+
+ container.add(G);
+ container.add(gFull);
+
+ container.add(B);
+ container.add(bFull);
+
+ return container;
+ }
+
+ /**
+ * This method returns the small display icon.
+ *
+ * @return The small display icon.
+ */
+ public Icon getSmallDisplayIcon()
+ {
+ return null;
+ }
+
+ /**
+ * This method returns the large display icon.
+ *
+ * @return The large display icon.
+ */
+ public Icon getLargeDisplayIcon()
+ {
+ return null;
+ }
+
+ /**
+ * This method paints the chooser panel.
+ *
+ * @param g The graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ super.paint(g);
+ }
+
+ /**
+ * This method updates the gradient image with a new one taking the Hue
+ * value as the constant.
+ */
+ private void updateHLockImage()
+ {
+ int index = 0;
+ int[] pix = new int[imgWidth * imgHeight];
+ float hValue = ((Number) hSpinner.getValue()).intValue() / 360f;
+
+ for (int j = 0; j < imgHeight; j++)
+ for (int i = 0; i < imgWidth; i++)
+ pix[index++] = Color.HSBtoRGB(hValue, (imgWidth - i * 1f) / imgWidth,
+ (imgHeight - j * 1f) / imgHeight)
+ | (255 << 24);
+
+ gradientImage = createImage(new MemoryImageSource(imgWidth, imgHeight,
+ pix, 0, imgWidth));
+ }
+
+ /**
+ * This method updates the gradient image with a new one taking the
+ * Brightness value as the constant.
+ */
+ private void updateBLockImage()
+ {
+ int[] pix = new int[imgWidth * imgHeight];
+ float bValue = ((Number) bSpinner.getValue()).intValue() / 100f;
+
+ int index = 0;
+ for (int j = 0; j < imgHeight; j++)
+ for (int i = 0; i < imgWidth; i++)
+ pix[index++] = Color.HSBtoRGB(i * 1f / imgWidth,
+ (imgHeight - j * 1f) / imgHeight, bValue)
+ | (255 << 24);
+
+ gradientImage = createImage(new MemoryImageSource(imgWidth, imgHeight,
+ pix, 0, imgWidth));
+ }
+
+ /**
+ * This method updates the gradient image with a new one taking the
+ * Saturation value as the constant.
+ */
+ private void updateSLockImage()
+ {
+ int[] pix = new int[imgWidth * imgHeight];
+ float sValue = ((Number) sSpinner.getValue()).intValue() / 100f;
+
+ int index = 0;
+ for (int j = 0; j < imgHeight; j++)
+ for (int i = 0; i < imgWidth; i++)
+ pix[index++] = Color.HSBtoRGB(i * 1f / imgWidth, sValue,
+ (imgHeight - j * 1f) / imgHeight)
+ | (255 << 24);
+ gradientImage = createImage(new MemoryImageSource(imgWidth, imgHeight,
+ pix, 0, imgWidth));
+ }
+
+ /**
+ * This method calls the appropriate method to update the gradient image
+ * depending on which HSB value is constant.
+ * This is package-private to avoid an accessor method.
+ */
+ void updateImage()
+ {
+ switch (locked)
+ {
+ case HLOCKED:
+ updateHLockImage();
+ break;
+ case SLOCKED:
+ updateSLockImage();
+ break;
+ case BLOCKED:
+ updateBLockImage();
+ break;
+ }
+ }
+
+ /**
+ * This method updates the TextFields with the correct RGB values.
+ */
+ private void updateTextFields()
+ {
+ int c = getColorSelectionModel().getSelectedColor().getRGB();
+
+ rFull.setText("" + (c >> 16 & 0xff));
+ gFull.setText("" + (c >> 8 & 0xff));
+ bFull.setText("" + (c & 0xff));
+
+ repaint();
+ }
+
+ /**
+ * This method updates the slider in response to making a different HSB
+ * property the constant.
+ * This is package-private to avoid an accessor method.
+ */
+ void updateSlider()
+ {
+ if (slider == null)
+ return;
+
+ slider.setMinimum(0);
+ if (locked == HLOCKED)
+ {
+ slider.setMaximum(359);
+ slider.setValue(((Number) hSpinner.getValue()).intValue());
+ slider.setInverted(true);
+ }
+ else
+ {
+ slider.setMaximum(100);
+ slider.setInverted(false);
+ if (sRadio.isSelected())
+ slider.setValue(((Number) sSpinner.getValue()).intValue());
+ else
+ slider.setValue(((Number) bSpinner.getValue()).intValue());
+ }
+ repaint();
+ }
+
+ /**
+ * This method updates the track gradient image depending on which HSB
+ * property is constant.
+ * This is package-private to avoid an accessor method.
+ */
+ void updateTrack()
+ {
+ switch (locked)
+ {
+ case HLOCKED:
+ updateHTrack();
+ break;
+ case SLOCKED:
+ updateSTrack();
+ break;
+ case BLOCKED:
+ updateBTrack();
+ break;
+ }
+ }
+
+ /**
+ * This method updates the track gradient image if the Hue value is allowed
+ * to change (according to the JRadioButtons).
+ */
+ private void updateHTrack()
+ {
+ int trackIndex = 0;
+ int[] trackPix = new int[trackWidth * imgHeight];
+
+ for (int j = 0; j < imgHeight; j++)
+ for (int i = 0; i < trackWidth; i++)
+ trackPix[trackIndex++] = Color.HSBtoRGB(j * 1f / imgHeight, 1f, 1f)
+ | (255 << 24);
+
+ trackImage = createImage(new MemoryImageSource(trackWidth, imgHeight,
+ trackPix, 0, trackWidth));
+ }
+
+ /**
+ * This method updates the track gradient image if the Saturation value is
+ * allowed to change (according to the JRadioButtons).
+ */
+ private void updateSTrack()
+ {
+ int[] trackPix = new int[trackWidth * imgHeight];
+
+ float hValue = ((Number) hSpinner.getValue()).intValue() / 360f;
+ float bValue = ((Number) bSpinner.getValue()).intValue() / 100f;
+
+ int trackIndex = 0;
+ for (int j = 0; j < imgHeight; j++)
+ for (int i = 0; i < trackWidth; i++)
+ trackPix[trackIndex++] = Color.HSBtoRGB(hValue,
+ (imgHeight - j * 1f) / imgHeight,
+ bValue) | (255 << 24);
+
+ trackImage = createImage(new MemoryImageSource(trackWidth, imgHeight,
+ trackPix, 0, trackWidth));
+ }
+
+ /**
+ * This method updates the track gradient image if the Brightness value is
+ * allowed to change (according to the JRadioButtons).
+ */
+ private void updateBTrack()
+ {
+ int[] trackPix = new int[trackWidth * imgHeight];
+
+ float hValue = ((Number) hSpinner.getValue()).intValue() / 360f;
+ float sValue = ((Number) sSpinner.getValue()).intValue() / 100f;
+
+ int trackIndex = 0;
+ for (int j = 0; j < imgHeight; j++)
+ for (int i = 0; i < trackWidth; i++)
+ trackPix[trackIndex++] = Color.HSBtoRGB(hValue, sValue,
+ (imgHeight - j * 1f) / imgHeight)
+ | (255 << 24);
+
+ trackImage = createImage(new MemoryImageSource(trackWidth, imgHeight,
+ trackPix, 0, trackWidth));
+ }
+
+ /**
+ * This method returns the HSB values for the currently selected color.
+ *
+ * @return The HSB values for the currently selected color.
+ */
+ private float[] getHSBValues()
+ {
+ Color c = getColorFromModel();
+ float[] f = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
+ return f;
+ }
+}
diff --git a/libjava/classpath/javax/swing/colorchooser/DefaultPreviewPanel.java b/libjava/classpath/javax/swing/colorchooser/DefaultPreviewPanel.java
new file mode 100644
index 000000000..2462add3d
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/DefaultPreviewPanel.java
@@ -0,0 +1,318 @@
+/* DefaultPreviewPanel.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.colorchooser;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.JColorChooser;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.border.Border;
+
+/**
+ * This is the default preview panel for the JColorChooser. The default
+ * preview panel is responsible for displaying the currently selected color
+ * of the JColorChooser.
+ */
+class DefaultPreviewPanel extends JPanel
+{
+ /**
+ * This is the border around the preview panel.
+ */
+ class PreviewBorder implements Border
+ {
+ /** This is the value of the top, bottom, top, and right inset. */
+ private static final int edge = 20;
+
+ /**
+ * This is the distance from the top left corner of the border to the
+ * text.
+ */
+ private static final int lead = 5;
+
+ /** This is the horizontal gap between the text and the border. */
+ private static final int gap = 3;
+
+ /**
+ * This method returns the border insets for the given Component.
+ *
+ * @param c The Component to retrieve insets for.
+ *
+ * @return The insets for the given Component.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(edge, edge, edge, edge);
+ }
+
+ /**
+ * This method returns whether the border is responsible for painting its
+ * own background.
+ *
+ * @return Whether the border is responsible for painting its own
+ * background.
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * This method paints the border for the given component with the graphics
+ * object using the given properties.
+ *
+ * @param c The Component to paint the border for.
+ * @param g The Graphics object to paint with.
+ * @param x The x location to paint at.
+ * @param y The y location to paint at.
+ * @param width The width of the component.
+ * @param height The height of the component.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ Color saved = g.getColor();
+ FontMetrics fm = g.getFontMetrics();
+
+ g.setColor(Color.BLACK);
+ g.drawLine(x + edge / 2, y + edge / 2, x + edge / 2,
+ y + height - edge / 2);
+ g.drawLine(x + edge / 2, y + height - edge / 2, x + width - edge / 2,
+ y + height - edge / 2);
+ g.drawLine(x + width - edge / 2, y + edge / 2, x + width - edge / 2,
+ y + height - edge / 2);
+ g.drawLine(x + edge / 2, y + edge / 2, x + edge / 2 + lead, y + edge / 2);
+
+ int strwidth = fm.stringWidth("Preview");
+
+ g.drawString("Preview", x + edge / 2 + lead + gap,
+ y + edge / 2 + fm.getAscent() / 2);
+
+ g.drawLine(x + lead + edge / 2 + strwidth + gap * 2, y + edge / 2,
+ x + width - edge / 2, y + edge / 2);
+
+ g.setColor(saved);
+ }
+ }
+
+ /** A standard large gap size. */
+ private static int largeGap = 6;
+
+ /** A standard small gap size. */
+ private static int smallGap = 2;
+
+ /** The size of each side of the square. */
+ private static int squareSize = 36;
+
+ /** This padding between the text and the edge of its box. */
+ private static int textPadding = 4;
+
+ /** The width of the right most rectangles. */
+ private static int rightSideRectWidth = 60;
+
+ /** The sample text. */
+ private static String sample = "Sample Text Sample Text";
+
+ /**
+ * Creates a new DefaultPreviewPanel object.
+ */
+ DefaultPreviewPanel()
+ {
+ super();
+ setBorder(new PreviewBorder());
+ }
+
+ /**
+ * This method paints the default preview panel with the given Graphics
+ * object.
+ *
+ * @param g The Graphics object.
+ */
+ public void paint(Graphics g)
+ {
+ super.paint(g);
+ Color currentColor = null;
+ JColorChooser chooser = (JColorChooser) SwingUtilities.getAncestorOfClass(JColorChooser.class,
+ this);
+ if (chooser != null)
+ currentColor = chooser.getColor();
+
+ Color saved = g.getColor();
+ Insets insets = getInsets();
+
+ int down = insets.top + squareSize + largeGap;
+ int currX = insets.left;
+
+ paintSquare(g, currX, insets.top, Color.WHITE, currentColor, Color.WHITE,
+ -1, -1, -1);
+ paintSquare(g, currX, down, currentColor, null, null, -1, -1, -1);
+
+ currX += squareSize + largeGap;
+
+ paintSquare(g, currX, insets.top, Color.BLACK, currentColor, Color.WHITE,
+ -1, -1, -1);
+ paintSquare(g, currX, down, Color.WHITE, currentColor, null, -1, -1, -1);
+
+ currX += squareSize + largeGap;
+
+ paintSquare(g, currX, insets.top, Color.WHITE, currentColor, Color.BLACK,
+ -1, -1, -1);
+ paintSquare(g, currX, down, Color.BLACK, currentColor, null, -1, -1, -1);
+
+ FontMetrics fm = g.getFontMetrics();
+ int strWidth = fm.stringWidth(sample);
+ int strHeight = fm.getHeight();
+
+ currX += squareSize + largeGap;
+
+ int boxWidth = 2 * textPadding + strWidth;
+ int boxHeight = 2 * textPadding + strHeight;
+
+ int first = insets.top + textPadding;
+ int second = insets.top + boxHeight + smallGap;
+ int third = insets.top + 2 * (boxHeight + smallGap);
+
+ g.setColor(Color.WHITE);
+ g.fillRect(currX, third, boxWidth, boxHeight);
+
+ g.setColor(currentColor);
+ g.drawString(sample, currX + textPadding,
+ first + textPadding + fm.getAscent());
+
+ g.fillRect(currX, second, boxWidth, boxHeight);
+
+ g.drawString(sample, currX + textPadding,
+ third + textPadding + fm.getAscent());
+
+ g.setColor(Color.BLACK);
+ g.drawString(sample, currX + textPadding,
+ second + textPadding + fm.getAscent());
+
+ currX += boxWidth + largeGap;
+
+ g.setColor(Color.WHITE);
+ g.fillRect(currX, insets.top, rightSideRectWidth, squareSize
+ + largeGap / 2);
+
+ g.setColor(currentColor);
+ g.fillRect(currX, insets.top + squareSize + largeGap / 2,
+ rightSideRectWidth, squareSize + largeGap / 2);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method creates and paints a square. The square has two smaller
+ * squares inside of it. Each of the three squares has their sizes
+ * determined by the size arguments. If the size is not given (by passing
+ * in -1), then the size is determined automatically.
+ *
+ * @param g The Graphics object to paint with.
+ * @param x The x location to paint at.
+ * @param y The y location to paint at.
+ * @param first The color of the first square.
+ * @param second The color of the second square.
+ * @param third The color of the third square.
+ * @param firstSize The size of the first square.
+ * @param secondSize The size of the second square.
+ * @param thirdSize The size of the third square.
+ */
+ private void paintSquare(Graphics g, int x, int y, Color first,
+ Color second, Color third, int firstSize,
+ int secondSize, int thirdSize)
+ {
+ Color saved = g.getColor();
+ if (firstSize == -1)
+ firstSize = squareSize;
+ if (secondSize == -1)
+ secondSize = squareSize * 2 / 3;
+ if (thirdSize == -1)
+ thirdSize = squareSize / 3;
+ int secondOffset = (firstSize - secondSize) / 2;
+ int thirdOffset = (firstSize - thirdSize) / 2;
+
+ if (first == null)
+ return;
+ g.setColor(first);
+ g.fillRect(x, y, firstSize, firstSize);
+ if (second == null)
+ return;
+ g.setColor(second);
+ g.fillRect(x + secondOffset, y + secondOffset, secondSize, secondSize);
+ if (third == null)
+ return;
+ g.setColor(third);
+ g.fillRect(x + thirdOffset, y + thirdOffset, thirdSize, thirdSize);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method returns the preferred size of the default preview panel.
+ *
+ * @return The preferred size of the default preview panel.
+ */
+ public Dimension getPreferredSize()
+ {
+ Graphics g = getGraphics();
+ FontMetrics fm = g.getFontMetrics();
+ g.dispose();
+
+ int strWidth = fm.stringWidth(sample);
+ int strHeight = fm.getHeight();
+
+ int h1 = (strHeight + 2 * textPadding) * 3 + 2 * smallGap;
+ int h2 = 2 * squareSize + largeGap;
+
+ int height = Math.max(h1, h2);
+
+ int width = 3 * (squareSize + largeGap) + strWidth + 2 * textPadding
+ + largeGap + rightSideRectWidth;
+
+ Insets insets = getInsets();
+
+ return new Dimension(width + insets.right + insets.left,
+ height + insets.top + insets.bottom);
+ }
+}
diff --git a/libjava/classpath/javax/swing/colorchooser/DefaultRGBChooserPanel.java b/libjava/classpath/javax/swing/colorchooser/DefaultRGBChooserPanel.java
new file mode 100644
index 000000000..24f0a9a71
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/DefaultRGBChooserPanel.java
@@ -0,0 +1,402 @@
+/* DefaultRGHChooserPanel.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.colorchooser;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+
+import javax.swing.Icon;
+import javax.swing.JColorChooser;
+import javax.swing.JLabel;
+import javax.swing.JSlider;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SwingConstants;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * This is the default RGB panel for the JColorChooser. The color is selected
+ * using three sliders that represent the RGB values.
+ */
+class DefaultRGBChooserPanel extends AbstractColorChooserPanel
+{
+ /**
+ * This class handles the slider value changes for all three sliders.
+ */
+ class SliderHandler implements ChangeListener
+ {
+ /**
+ * This method is called whenever any of the slider values change.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ if (updateChange)
+ return;
+
+ int color = R.getValue() << 16 | G.getValue() << 8 | B.getValue();
+
+ sliderChange = true;
+ getColorSelectionModel().setSelectedColor(new Color(color));
+ sliderChange = false;
+ }
+ }
+
+ /**
+ * This class handles the Spinner values changing.
+ */
+ class SpinnerHandler implements ChangeListener
+ {
+ /**
+ * This method is called whenever any of the JSpinners change values.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ if (updateChange)
+ return;
+
+ int red = ((Number) RSpinner.getValue()).intValue();
+ int green = ((Number) GSpinner.getValue()).intValue();
+ int blue = ((Number) BSpinner.getValue()).intValue();
+
+ int color = red << 16 | green << 8 | blue;
+
+ spinnerChange = true;
+ getColorSelectionModel().setSelectedColor(new Color(color));
+ spinnerChange = false;
+ }
+ }
+
+ /** Whether the color change was initiated by the spinners.
+ * This is package-private to avoid an accessor method. */
+ transient boolean spinnerChange = false;
+
+ /** Whether the color change was initiated by the sliders.
+ * This is package-private to avoid an accessor method. */
+ transient boolean sliderChange = false;
+
+ /**
+ * Whether the change was forced by the chooser (meaning the color has
+ * already been changed).
+ * This is package-private to avoid an accessor method.
+ */
+ transient boolean updateChange = false;
+
+ /** The ChangeListener for the sliders. */
+ private transient ChangeListener colorChanger;
+
+ /** The ChangeListener for the spinners. */
+ private transient ChangeListener spinnerHandler;
+
+ /** The slider that handles the red values.
+ * This is package-private to avoid an accessor method. */
+ transient JSlider R;
+
+ /** The slider that handles the green values.
+ * This is package-private to avoid an accessor method. */
+ transient JSlider G;
+
+ /** The slider that handles the blue values.
+ * This is package-private to avoid an accessor method. */
+ transient JSlider B;
+
+ /** The label for the red slider. */
+ private transient JLabel RLabel;
+
+ /** The label for the green slider. */
+ private transient JLabel GLabel;
+
+ /** The label for the blue slider. */
+ private transient JLabel BLabel;
+
+ /** The spinner that handles the red values.
+ * This is package-private to avoid an accessor method. */
+ transient JSpinner RSpinner;
+
+ /** The spinner that handles the green values.
+ * This is package-private to avoid an accessor method. */
+ transient JSpinner GSpinner;
+
+ /** The spinner that handles the blue values.
+ * This is package-private to avoid an accessor method. */
+ transient JSpinner BSpinner;
+
+ /**
+ * Creates a new DefaultRGBChooserPanel object.
+ */
+ public DefaultRGBChooserPanel()
+ {
+ super();
+ }
+
+ /**
+ * This method returns the name displayed in the JTabbedPane.
+ *
+ * @return The name displayed in the JTabbedPane.
+ */
+ public String getDisplayName()
+ {
+ return "RGB";
+ }
+
+ /**
+ * This method updates the chooser panel with the new color chosen in the
+ * JColorChooser.
+ */
+ public void updateChooser()
+ {
+ Color c = getColorFromModel();
+ int rgb = c.getRGB();
+
+ int red = rgb >> 16 & 0xff;
+ int green = rgb >> 8 & 0xff;
+ int blue = rgb & 0xff;
+
+ updateChange = true;
+
+ if (! sliderChange)
+ {
+ if (R != null)
+ R.setValue(red);
+ if (G != null)
+ G.setValue(green);
+ if (B != null)
+ B.setValue(blue);
+ }
+ if (! spinnerChange)
+ {
+ if (GSpinner != null)
+ GSpinner.setValue(new Integer(green));
+ if (RSpinner != null)
+ RSpinner.setValue(new Integer(red));
+ if (BSpinner != null)
+ BSpinner.setValue(new Integer(blue));
+ }
+
+ updateChange = false;
+
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * This method builds the chooser panel.
+ */
+ protected void buildChooser()
+ {
+ setLayout(new GridBagLayout());
+
+ RLabel = new JLabel("Red");
+ RLabel.setDisplayedMnemonic('d');
+ GLabel = new JLabel("Green");
+ GLabel.setDisplayedMnemonic('n');
+ BLabel = new JLabel("Blue");
+ BLabel.setDisplayedMnemonic('B');
+
+ R = new JSlider(SwingConstants.HORIZONTAL, 0, 255, 255);
+ G = new JSlider(SwingConstants.HORIZONTAL, 0, 255, 255);
+ B = new JSlider(SwingConstants.HORIZONTAL, 0, 255, 255);
+
+ R.setPaintTicks(true);
+ R.setSnapToTicks(false);
+ G.setPaintTicks(true);
+ G.setSnapToTicks(false);
+ B.setPaintTicks(true);
+ B.setSnapToTicks(false);
+
+ R.setLabelTable(R.createStandardLabels(85));
+ R.setPaintLabels(true);
+ G.setLabelTable(G.createStandardLabels(85));
+ G.setPaintLabels(true);
+ B.setLabelTable(B.createStandardLabels(85));
+ B.setPaintLabels(true);
+
+ R.setMajorTickSpacing(85);
+ G.setMajorTickSpacing(85);
+ B.setMajorTickSpacing(85);
+
+ R.setMinorTickSpacing(17);
+ G.setMinorTickSpacing(17);
+ B.setMinorTickSpacing(17);
+
+ RSpinner = new JSpinner(new SpinnerNumberModel(R.getValue(),
+ R.getMinimum(),
+ R.getMaximum(), 1));
+ GSpinner = new JSpinner(new SpinnerNumberModel(G.getValue(),
+ G.getMinimum(),
+ G.getMaximum(), 1));
+ BSpinner = new JSpinner(new SpinnerNumberModel(B.getValue(),
+ B.getMinimum(),
+ B.getMaximum(), 1));
+
+ RLabel.setLabelFor(R);
+ GLabel.setLabelFor(G);
+ BLabel.setLabelFor(B);
+
+ GridBagConstraints bag = new GridBagConstraints();
+ bag.fill = GridBagConstraints.VERTICAL;
+
+ bag.gridx = 0;
+ bag.gridy = 0;
+ add(RLabel, bag);
+
+ bag.gridx = 1;
+ add(R, bag);
+
+ bag.gridx = 2;
+ add(RSpinner, bag);
+
+ bag.gridx = 0;
+ bag.gridy = 1;
+ add(GLabel, bag);
+
+ bag.gridx = 1;
+ add(G, bag);
+
+ bag.gridx = 2;
+ add(GSpinner, bag);
+
+ bag.gridx = 0;
+ bag.gridy = 2;
+ add(BLabel, bag);
+
+ bag.gridx = 1;
+ add(B, bag);
+
+ bag.gridx = 2;
+ add(BSpinner, bag);
+
+ installListeners();
+ }
+
+ /**
+ * This method uninstalls the chooser panel from the JColorChooser.
+ *
+ * @param chooser The JColorChooser to remove this chooser panel from.
+ */
+ public void uninstallChooserPanel(JColorChooser chooser)
+ {
+ uninstallListeners();
+ removeAll();
+
+ R = null;
+ G = null;
+ B = null;
+
+ RSpinner = null;
+ GSpinner = null;
+ BSpinner = null;
+
+ super.uninstallChooserPanel(chooser);
+ }
+
+ /**
+ * This method uninstalls any listeners that were added by the chooser
+ * panel.
+ */
+ private void uninstallListeners()
+ {
+ R.removeChangeListener(colorChanger);
+ G.removeChangeListener(colorChanger);
+ B.removeChangeListener(colorChanger);
+
+ colorChanger = null;
+
+ RSpinner.removeChangeListener(spinnerHandler);
+ GSpinner.removeChangeListener(spinnerHandler);
+ BSpinner.removeChangeListener(spinnerHandler);
+
+ spinnerHandler = null;
+ }
+
+ /**
+ * This method installs any listeners that the chooser panel needs to
+ * operate.
+ */
+ private void installListeners()
+ {
+ colorChanger = new SliderHandler();
+
+ R.addChangeListener(colorChanger);
+ G.addChangeListener(colorChanger);
+ B.addChangeListener(colorChanger);
+
+ spinnerHandler = new SpinnerHandler();
+
+ RSpinner.addChangeListener(spinnerHandler);
+ GSpinner.addChangeListener(spinnerHandler);
+ BSpinner.addChangeListener(spinnerHandler);
+ }
+
+ /**
+ * This method returns the small display icon.
+ *
+ * @return The small display icon.
+ */
+ public Icon getSmallDisplayIcon()
+ {
+ return null;
+ }
+
+ /**
+ * This method returns the large display icon.
+ *
+ * @return The large display icon.
+ */
+ public Icon getLargeDisplayIcon()
+ {
+ return null;
+ }
+
+ /**
+ * This method paints the default RGB chooser panel.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ super.paint(g);
+ }
+}
diff --git a/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java b/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java
new file mode 100644
index 000000000..99b715789
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java
@@ -0,0 +1,896 @@
+/* DefaultSwatchChooserPanel.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.colorchooser;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.Icon;
+import javax.swing.JColorChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+/**
+ * This class is the DefaultSwatchChooserPanel. This chooser panel displays a
+ * set of colors that can be picked. Recently picked items will go into a
+ * side panel so the user can see the history of the chosen colors.
+ */
+class DefaultSwatchChooserPanel extends AbstractColorChooserPanel
+{
+ /** The main panel that holds the set of choosable colors. */
+ MainSwatchPanel mainPalette;
+
+ /** A panel that holds the recent colors. */
+ RecentSwatchPanel recentPalette;
+
+ /** The mouse handlers for the panels. */
+ MouseListener mouseHandler;
+
+ /**
+ * This the base class for all swatch panels. Swatch panels are panels that
+ * hold a set of blocks where colors are displayed.
+ */
+ abstract static class SwatchPanel extends JPanel
+ {
+ /** The width of each block. */
+ protected int cellWidth = 10;
+
+ /** The height of each block. */
+ protected int cellHeight = 10;
+
+ /** The gap between blocks. */
+ protected int gap = 1;
+
+ /** The number of rows in the swatch panel. */
+ protected int numRows;
+
+ /** The number of columns in the swatch panel. */
+ protected int numCols;
+
+ /**
+ * Creates a new SwatchPanel object.
+ */
+ SwatchPanel()
+ {
+ super();
+ setBackground(Color.WHITE);
+ }
+
+ /**
+ * This method returns the preferred size of the swatch panel based on the
+ * number of rows and columns and the size of each cell.
+ *
+ * @return The preferred size of the swatch panel.
+ */
+ public Dimension getPreferredSize()
+ {
+ int height = numRows * cellHeight + (numRows - 1) * gap;
+ int width = numCols * cellWidth + (numCols - 1) * gap;
+ Insets insets = getInsets();
+
+ return new Dimension(width + insets.left + insets.right,
+ height + insets.top + insets.bottom);
+ }
+
+ /**
+ * This method returns the color for the given position.
+ *
+ * @param x The x coordinate of the position.
+ * @param y The y coordinate of the position.
+ *
+ * @return The color at the given position.
+ */
+ public abstract Color getColorForPosition(int x, int y);
+
+ /**
+ * This method initializes the colors for the swatch panel.
+ */
+ protected abstract void initializeColors();
+ }
+
+ /**
+ * This is the main swatch panel. This panel sits in the middle and allows a
+ * set of colors to be picked which will move to the recent swatch panel.
+ */
+ static class MainSwatchPanel extends SwatchPanel
+ {
+ /** The color describing (204, 255, 255) */
+ public static final Color C204255255 = new Color(204, 204, 255);
+
+ /** The color describing (255, 204, 204) */
+ public static final Color C255204204 = new Color(255, 204, 204);
+
+ /** The color describing (204, 255, 204) */
+ public static final Color C204255204 = new Color(204, 255, 204);
+
+ /** The color describing (204, 204, 204) */
+ public static final Color C204204204 = new Color(204, 204, 204);
+
+ /** The color (153, 153, 255). */
+ public static final Color C153153255 = new Color(153, 153, 255);
+
+ /** The color (51, 51, 255). */
+ public static final Color C051051255 = new Color(51, 51, 255);
+
+ /** The color (153, 0, 153). */
+ public static final Color C153000153 = new Color(153, 0, 153);
+
+ /** The color (0, 51, 51). */
+ public static final Color C000051051 = new Color(0, 51, 51);
+
+ /** The color (51, 0, 51). */
+ public static final Color C051000051 = new Color(51, 0, 51);
+
+ /** The color (51, 51, 0). */
+ public static final Color C051051000 = new Color(51, 51, 0);
+
+ /** The color (102, 102, 0). */
+ public static final Color C102102000 = new Color(102, 102, 0);
+
+ /** The color (153, 255, 153). */
+ public static final Color C153255153 = new Color(153, 255, 153);
+
+ /** The color (102, 255, 102). */
+ public static final Color C102255102 = new Color(102, 255, 102);
+
+ /** The color (0, 102, 102). */
+ public static final Color C000102102 = new Color(0, 102, 102);
+
+ /** The color (102, 0, 102). */
+ public static final Color C102000102 = new Color(102, 0, 102);
+
+ /** The color (0, 153, 153). */
+ public static final Color C000153153 = new Color(0, 153, 153);
+
+ /** The color (153, 153, 0). */
+ public static final Color C153153000 = new Color(153, 153, 0);
+
+ /** The color (204, 204, 0). */
+ public static final Color C204204000 = new Color(204, 204, 0);
+
+ /** The color (204, 0, 204). */
+ public static final Color C204000204 = new Color(204, 0, 204);
+
+ /** The color (0, 204, 204). */
+ public static final Color C000204204 = new Color(0, 204, 204);
+
+ /** The color (51, 255, 51). */
+ public static final Color C051255051 = new Color(51, 255, 51);
+
+ /** The color (255, 51, 51). */
+ public static final Color C255051051 = new Color(255, 51, 51);
+
+ /** The color (255, 102, 102). */
+ public static final Color C255102102 = new Color(255, 102, 102);
+
+ /** The color (102, 102, 255). */
+ public static final Color C102102255 = new Color(102, 102, 255);
+
+ /** The color (255, 153, 153). */
+ public static final Color C255153153 = new Color(255, 153, 153);
+ static Color[] colors =
+ {
+ // Row 1
+ Color.WHITE, new Color(204, 255, 255), C204255255, C204255255, C204255255,
+ C204255255, C204255255, C204255255, C204255255,
+ C204255255, C204255255, new Color(255, 204, 255),
+ C255204204, C255204204, C255204204, C255204204,
+ C255204204, C255204204, C255204204, C255204204,
+ C255204204, new Color(255, 255, 204), C204255204,
+ C204255204, C204255204, C204255204, C204255204,
+ C204255204, C204255204, C204255204, C204255204,
+
+ // Row 2
+ C204204204, new Color(153, 255, 255), new Color(153, 204, 255), C153153255,
+ C153153255, C153153255, C153153255, C153153255,
+ C153153255, C153153255, new Color(204, 153, 255),
+ new Color(255, 153, 255),
+ new Color(255, 153, 204), C255153153, C255153153,
+ C255153153, C255153153, C255153153, C255153153,
+ C255153153, new Color(255, 204, 153),
+ new Color(255, 255, 153),
+ new Color(204, 255, 153), C153255153, C153255153,
+ C153255153, C153255153, C153255153, C153255153,
+ C153255153, new Color(153, 255, 204),
+
+ // Row 3
+ C204204204, new Color(102, 255, 255), new Color(102, 204, 255),
+ new Color(102, 153, 255), C102102255, C102102255,
+ C102102255, C102102255, C102102255,
+ new Color(153, 102, 255),
+ new Color(204, 102, 255),
+ new Color(255, 102, 255),
+ new Color(255, 102, 204),
+ new Color(255, 102, 153), C255102102, C255102102,
+ C255102102, C255102102, C255102102,
+ new Color(255, 153, 102),
+ new Color(255, 204, 102),
+ new Color(255, 255, 102),
+ new Color(204, 255, 102),
+ new Color(153, 255, 102), C102255102, C102255102,
+ C102255102, C102255102, C102255102,
+ new Color(102, 255, 153),
+ new Color(102, 255, 204),
+
+ // Row 4
+ new Color(153, 153, 153), new Color(51, 255, 255), new Color(51, 204, 255),
+ new Color(51, 153, 255), new Color(51, 102, 255),
+ C051051255, C051051255, C051051255,
+ new Color(102, 51, 255), new Color(153, 51, 255),
+ new Color(204, 51, 255), new Color(255, 51, 255),
+ new Color(255, 51, 204), new Color(255, 51, 153),
+ new Color(255, 51, 102), C255051051, C255051051,
+ C255051051, new Color(255, 102, 51),
+ new Color(255, 153, 51), new Color(255, 204, 51),
+ new Color(255, 255, 51), new Color(204, 255, 51),
+ new Color(153, 255, 51), new Color(102, 255, 51),
+ C051255051, C051255051, C051255051,
+ new Color(51, 255, 102), new Color(51, 255, 153),
+ new Color(51, 255, 204),
+
+ // Row 5
+ new Color(153, 153, 153), new Color(0, 255, 255), new Color(0, 204, 255),
+ new Color(0, 153, 255), new Color(0, 102, 255),
+ new Color(0, 51, 255), new Color(0, 0, 255),
+ new Color(51, 0, 255), new Color(102, 0, 255),
+ new Color(153, 0, 255), new Color(204, 0, 255),
+ new Color(255, 0, 255), new Color(255, 0, 204),
+ new Color(255, 0, 153), new Color(255, 0, 102),
+ new Color(255, 0, 51), new Color(255, 0, 0),
+ new Color(255, 51, 0), new Color(255, 102, 0),
+ new Color(255, 153, 0), new Color(255, 204, 0),
+ new Color(255, 255, 0), new Color(204, 255, 0),
+ new Color(153, 255, 0), new Color(102, 255, 0),
+ new Color(51, 255, 0), new Color(0, 255, 0),
+ new Color(0, 255, 51), new Color(0, 255, 102),
+ new Color(0, 255, 153), new Color(0, 255, 204),
+
+ // Row 6
+ new Color(102, 102, 102), C000204204, C000204204, new Color(0, 153, 204),
+ new Color(0, 102, 204), new Color(0, 51, 204),
+ new Color(0, 0, 204), new Color(51, 0, 204),
+ new Color(102, 0, 204), new Color(153, 0, 204),
+ C204000204, C204000204, C204000204,
+ new Color(204, 0, 153), new Color(204, 0, 102),
+ new Color(204, 0, 51), new Color(204, 0, 0),
+ new Color(204, 51, 0), new Color(204, 102, 0),
+ new Color(204, 153, 0), C204204000, C204204000,
+ C204204000, new Color(153, 204, 0),
+ new Color(102, 204, 0), new Color(51, 204, 0),
+ new Color(0, 204, 0), new Color(0, 204, 51),
+ new Color(0, 204, 102), new Color(0, 204, 153),
+ new Color(0, 204, 204),
+
+ // Row 7
+ new Color(102, 102, 102), C000153153, C000153153, C000153153,
+ new Color(0, 102, 153), new Color(0, 51, 153),
+ new Color(0, 0, 153), new Color(51, 0, 153),
+ new Color(102, 0, 153), C153000153, C153000153,
+ C153000153, C153000153, C153000153,
+ new Color(153, 0, 102), new Color(153, 0, 51),
+ new Color(153, 0, 0), new Color(153, 51, 0),
+ new Color(153, 102, 0), C153153000, C153153000,
+ C153153000, C153153000, C153153000,
+ new Color(102, 153, 0), new Color(51, 153, 0),
+ new Color(0, 153, 0), new Color(0, 153, 51),
+ new Color(0, 153, 102), C000153153, C000153153,
+
+ // Row 8
+ new Color(51, 51, 51), C000102102, C000102102, C000102102, C000102102,
+ new Color(0, 51, 102), new Color(0, 0, 102),
+ new Color(51, 0, 102), C102000102, C102000102,
+ C102000102, C102000102, C102000102, C102000102,
+ C102000102, new Color(102, 0, 51),
+ new Color(102, 0, 0), new Color(102, 51, 0),
+ C102102000, C102102000, C102102000, C102102000,
+ C102102000, C102102000, C102102000,
+ new Color(51, 102, 0), new Color(0, 102, 0),
+ new Color(0, 102, 51), C000102102, C000102102,
+ C000102102,
+
+ // Row 9.
+ Color.BLACK, C000051051, C000051051, C000051051, C000051051, C000051051,
+ new Color(0, 0, 51), C051000051, C051000051,
+ C051000051, C051000051, C051000051, C051000051,
+ C051000051, C051000051, C051000051,
+ new Color(51, 0, 0), C051051000, C051051000,
+ C051051000, C051051000, C051051000, C051051000,
+ C051051000, C051051000, new Color(0, 51, 0),
+ C000051051, C000051051, C000051051, C000051051,
+ new Color(51, 51, 51)
+ };
+
+ /**
+ * Creates a new MainSwatchPanel object.
+ */
+ MainSwatchPanel()
+ {
+ super();
+ numCols = 31;
+ numRows = 9;
+ initializeColors();
+ revalidate();
+ }
+
+ /**
+ * This method returns the color for the given position.
+ *
+ * @param x The x location for the position.
+ * @param y The y location for the position.
+ *
+ * @return The color for the given position.
+ */
+ public Color getColorForPosition(int x, int y)
+ {
+ if (x % (cellWidth + gap) > cellWidth
+ || y % (cellHeight + gap) > cellHeight)
+ // position is located in gap.
+ return null;
+
+ int row = y / (cellHeight + gap);
+ int col = x / (cellWidth + gap);
+ return colors[row * numCols + col];
+ }
+
+ /**
+ * This method initializes the colors for the main swatch panel.
+ */
+ protected void initializeColors()
+ {
+ // Unnecessary
+ }
+
+ /**
+ * This method paints the main graphics panel with the given Graphics
+ * object.
+ *
+ * @param graphics The Graphics object to paint with.
+ */
+ public void paint(Graphics graphics)
+ {
+ int index = 0;
+ Insets insets = getInsets();
+ int currX = insets.left;
+ int currY = insets.top;
+ Color saved = graphics.getColor();
+
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < numCols; j++)
+ {
+ graphics.setColor(colors[index++]);
+ graphics.fill3DRect(currX, currY, cellWidth, cellHeight, true);
+ currX += gap + cellWidth;
+ }
+ currX = insets.left;
+ currY += gap + cellHeight;
+ }
+ graphics.setColor(saved);
+ }
+
+ /**
+ * This method returns the tooltip text for the given MouseEvent.
+ *
+ * @param e The MouseEvent to find tooltip text for.
+ *
+ * @return The tooltip text.
+ */
+ public String getToolTipText(MouseEvent e)
+ {
+ Color c = getColorForPosition(e.getX(), e.getY());
+ if (c == null)
+ return null;
+ return (c.getRed() + "," + c.getGreen() + "," + c.getBlue());
+ }
+ }
+
+ /**
+ * This class is the recent swatch panel. It holds recently selected colors.
+ */
+ static class RecentSwatchPanel extends SwatchPanel
+ {
+ /** The array for storing recently stored colors. */
+ Color[] colors;
+
+ /** The default color. */
+ public static final Color defaultColor = Color.GRAY;
+
+ /** The index of the array that is the start. */
+ int start = 0;
+
+ /**
+ * Creates a new RecentSwatchPanel object.
+ */
+ RecentSwatchPanel()
+ {
+ super();
+ numCols = 5;
+ numRows = 7;
+ initializeColors();
+ revalidate();
+ }
+
+ /**
+ * This method returns the color for the given position.
+ *
+ * @param x The x coordinate of the position.
+ * @param y The y coordinate of the position.
+ *
+ * @return The color for the given position.
+ */
+ public Color getColorForPosition(int x, int y)
+ {
+ if (x % (cellWidth + gap) > cellWidth
+ || y % (cellHeight + gap) > cellHeight)
+ // position is located in gap.
+ return null;
+
+ int row = y / (cellHeight + gap);
+ int col = x / (cellWidth + gap);
+
+ return colors[getIndexForCell(row, col)];
+ }
+
+ /**
+ * This method initializes the colors for the recent swatch panel.
+ */
+ protected void initializeColors()
+ {
+ colors = new Color[numRows * numCols];
+ for (int i = 0; i < colors.length; i++)
+ colors[i] = defaultColor;
+ }
+
+ /**
+ * This method returns the array index for the given row and column.
+ *
+ * @param row The row.
+ * @param col The column.
+ *
+ * @return The array index for the given row and column.
+ */
+ private int getIndexForCell(int row, int col)
+ {
+ return ((row * numCols) + col + start) % (numRows * numCols);
+ }
+
+ /**
+ * This method adds the given color to the beginning of the swatch panel.
+ * Package-private to avoid an accessor method.
+ *
+ * @param c The color to add.
+ */
+ void addColorToQueue(Color c)
+ {
+ if (--start == -1)
+ start = numRows * numCols - 1;
+
+ colors[start] = c;
+ }
+
+ /**
+ * This method paints the panel with the given Graphics object.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ Color saved = g.getColor();
+ Insets insets = getInsets();
+ int currX = insets.left;
+ int currY = insets.top;
+
+ for (int i = 0; i < numRows; i++)
+ {
+ for (int j = 0; j < numCols; j++)
+ {
+ g.setColor(colors[getIndexForCell(i, j)]);
+ g.fill3DRect(currX, currY, cellWidth, cellHeight, true);
+ currX += cellWidth + gap;
+ }
+ currX = insets.left;
+ currY += cellWidth + gap;
+ }
+ }
+
+ /**
+ * This method returns the tooltip text for the given MouseEvent.
+ *
+ * @param e The MouseEvent.
+ *
+ * @return The tooltip text.
+ */
+ public String getToolTipText(MouseEvent e)
+ {
+ Color c = getColorForPosition(e.getX(), e.getY());
+ if (c == null)
+ return null;
+ return c.getRed() + "," + c.getGreen() + "," + c.getBlue();
+ }
+ }
+
+ /**
+ * This class handles mouse events for the two swatch panels.
+ */
+ class MouseHandler extends MouseAdapter
+ {
+ /**
+ * This method is called whenever the mouse is pressed.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ SwatchPanel panel = (SwatchPanel) e.getSource();
+ Color c = panel.getColorForPosition(e.getX(), e.getY());
+ recentPalette.addColorToQueue(c);
+ DefaultSwatchChooserPanel.this.getColorSelectionModel().setSelectedColor(c);
+ DefaultSwatchChooserPanel.this.repaint();
+ }
+ }
+
+ /**
+ * This is the layout manager for the main panel.
+ */
+ static class MainPanelLayout implements LayoutManager
+ {
+ /**
+ * This method is called when a new component is added to the container.
+ *
+ * @param name The name of the component.
+ * @param comp The added component.
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called to set the size and position of the child
+ * components for the given container.
+ *
+ * @param parent The container to lay out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ Component[] comps = parent.getComponents();
+ Insets insets = parent.getInsets();
+ Dimension[] pref = new Dimension[comps.length];
+
+ int xpos = 0;
+ int ypos = 0;
+ int maxHeight = 0;
+ int totalWidth = 0;
+
+ for (int i = 0; i < comps.length; i++)
+ {
+ pref[i] = comps[i].getPreferredSize();
+ if (pref[i] == null)
+ return;
+ maxHeight = Math.max(maxHeight, pref[i].height);
+ totalWidth += pref[i].width;
+ }
+
+ ypos = (parent.getSize().height - maxHeight) / 2 + insets.top;
+ xpos = insets.left + (parent.getSize().width - totalWidth) / 2;
+
+ for (int i = 0; i < comps.length; i++)
+ {
+ if (pref[i] == null)
+ continue;
+ comps[i].setBounds(xpos, ypos, pref[i].width, pref[i].height);
+ xpos += pref[i].width;
+ }
+ }
+
+ /**
+ * This method is called when a component is removed from the container.
+ *
+ * @param comp The component that was removed.
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This methods calculates the minimum layout size for the container.
+ *
+ * @param parent The container.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return preferredLayoutSize(parent);
+ }
+
+ /**
+ * This method returns the preferred layout size for the given container.
+ *
+ * @param parent The container.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ int xmax = 0;
+ int ymax = 0;
+
+ Component[] comps = parent.getComponents();
+ Dimension pref;
+
+ for (int i = 0; i < comps.length; i++)
+ {
+ pref = comps[i].getPreferredSize();
+ if (pref == null)
+ continue;
+ xmax += pref.width;
+ ymax = Math.max(ymax, pref.height);
+ }
+
+ Insets insets = parent.getInsets();
+
+ return new Dimension(insets.left + insets.right + xmax,
+ insets.top + insets.bottom + ymax);
+ }
+ }
+
+ /**
+ * This is the layout manager for the recent swatch panel.
+ */
+ static class RecentPanelLayout implements LayoutManager
+ {
+ /**
+ * This method is called when a component is added to the container.
+ *
+ * @param name The name of the component.
+ * @param comp The added component.
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Nothing needs to be done.
+ }
+
+ /**
+ * This method sets the size and position of the child components of the
+ * given container.
+ *
+ * @param parent The container to lay out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ Component[] comps = parent.getComponents();
+ Dimension parentSize = parent.getSize();
+ Insets insets = parent.getInsets();
+ int currY = insets.top;
+ Dimension pref;
+
+ for (int i = 0; i < comps.length; i++)
+ {
+ pref = comps[i].getPreferredSize();
+ if (pref == null)
+ continue;
+ comps[i].setBounds(insets.left, currY, pref.width, pref.height);
+ currY += pref.height;
+ }
+ }
+
+ /**
+ * This method calculates the minimum layout size for the given container.
+ *
+ * @param parent The container.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return preferredLayoutSize(parent);
+ }
+
+ /**
+ * This method calculates the preferred layout size for the given
+ * container.
+ *
+ * @param parent The container.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ int width = 0;
+ int height = 0;
+ Insets insets = parent.getInsets();
+ Component[] comps = parent.getComponents();
+ Dimension pref;
+ for (int i = 0; i < comps.length; i++)
+ {
+ pref = comps[i].getPreferredSize();
+ if (pref != null)
+ {
+ width = Math.max(width, pref.width);
+ height += pref.height;
+ }
+ }
+
+ return new Dimension(width + insets.left + insets.right,
+ height + insets.top + insets.bottom);
+ }
+
+ /**
+ * This method is called whenever a component is removed from the
+ * container.
+ *
+ * @param comp The removed component.
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Nothing needs to be done.
+ }
+ }
+
+ /**
+ * Creates a new DefaultSwatchChooserPanel object.
+ */
+ DefaultSwatchChooserPanel()
+ {
+ super();
+ }
+
+ /**
+ * This method updates the chooser panel with the new value from the
+ * JColorChooser.
+ */
+ public void updateChooser()
+ {
+ // Nothing to do here yet.
+ }
+
+ /**
+ * This method builds the chooser panel.
+ */
+ protected void buildChooser()
+ {
+ // The structure of the swatch panel is:
+ // One large panel (minus the insets).
+ // Inside that panel, there are two panels, one holds the palette.
+ // The other holds the label and the recent colors palette.
+ // The two palettes are two custom swatch panels.
+ setLayout(new MainPanelLayout());
+
+ JPanel mainPaletteHolder = new JPanel();
+ JPanel recentPaletteHolder = new JPanel();
+
+ mainPalette = new MainSwatchPanel();
+ recentPalette = new RecentSwatchPanel();
+ JLabel label = new JLabel("Recent:");
+
+ mouseHandler = new MouseHandler();
+ mainPalette.addMouseListener(mouseHandler);
+ recentPalette.addMouseListener(mouseHandler);
+
+ mainPaletteHolder.setLayout(new BorderLayout());
+ mainPaletteHolder.add(mainPalette, BorderLayout.CENTER);
+
+ recentPaletteHolder.setLayout(new RecentPanelLayout());
+ recentPaletteHolder.add(label);
+ recentPaletteHolder.add(recentPalette);
+
+ JPanel main = new JPanel();
+ main.add(mainPaletteHolder);
+ main.add(recentPaletteHolder);
+
+ this.add(main);
+ }
+
+ /**
+ * This method removes the chooser panel from the JColorChooser.
+ *
+ * @param chooser The JColorChooser this panel is being removed from.
+ */
+ public void uninstallChooserPanel(JColorChooser chooser)
+ {
+ recentPalette = null;
+ mainPalette = null;
+
+ removeAll();
+ super.uninstallChooserPanel(chooser);
+ }
+
+ /**
+ * This method returns the JTabbedPane displayed name.
+ *
+ * @return The name displayed in the JTabbedPane.
+ */
+ public String getDisplayName()
+ {
+ return "Swatches";
+ }
+
+ /**
+ * This method returns the small display icon.
+ *
+ * @return The small display icon.
+ */
+ public Icon getSmallDisplayIcon()
+ {
+ return null;
+ }
+
+ /**
+ * This method returns the large display icon.
+ *
+ * @return The large display icon.
+ */
+ public Icon getLargeDisplayIcon()
+ {
+ return null;
+ }
+
+ /**
+ * This method paints the chooser panel with the given Graphics object.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ super.paint(g);
+ }
+
+ /**
+ * This method returns the tooltip text for the given MouseEvent.
+ *
+ * @param e The MouseEvent.
+ *
+ * @return The tooltip text.
+ */
+ public String getToolTipText(MouseEvent e)
+ {
+ return null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/colorchooser/package.html b/libjava/classpath/javax/swing/colorchooser/package.html
new file mode 100644
index 000000000..c04e96ebb
--- /dev/null
+++ b/libjava/classpath/javax/swing/colorchooser/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.event package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.colorchooser</title></head>
+
+<body>
+<p>Provides support classes for the {@link javax.swing.JColorChooser}
+component.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/event/AncestorEvent.java b/libjava/classpath/javax/swing/event/AncestorEvent.java
new file mode 100644
index 000000000..27b469a8f
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/AncestorEvent.java
@@ -0,0 +1,100 @@
+/* AncestorEvent.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.awt.AWTEvent;
+import java.awt.Container;
+
+import javax.swing.JComponent;
+
+/**
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public class AncestorEvent extends AWTEvent
+{
+ private static final long serialVersionUID = -8079801679695605002L;
+
+ public static final int ANCESTOR_ADDED = 1;
+ public static final int ANCESTOR_REMOVED = 2;
+ public static final int ANCESTOR_MOVED = 3;
+
+ private JComponent sourceComponent;
+ private Container ancestor;
+ private Container ancestorParent;
+
+ /**
+ * @param source Source component
+ * @param id ID
+ * @param ancestor ancestor
+ * @param ancestorParent parent ancestor
+ */
+ public AncestorEvent(JComponent source, int id, Container ancestor,
+ Container ancestorParent)
+ {
+ super(source, id);
+ this.sourceComponent = source;
+ this.ancestor = ancestor;
+ this.ancestorParent = ancestorParent;
+ }
+
+ /**
+ * Returns the ancestor of this event.
+ */
+ public Container getAncestor()
+ {
+ return ancestor;
+ }
+
+ /**
+ * Returns the ancester parent of this event.
+ */
+ public Container getAncestorParent()
+ {
+ return ancestorParent;
+ }
+
+ /**
+ * Returns the source of this event.
+ */
+ public JComponent getComponent()
+ {
+ return sourceComponent;
+ }
+}
diff --git a/libjava/classpath/javax/swing/event/AncestorListener.java b/libjava/classpath/javax/swing/event/AncestorListener.java
new file mode 100644
index 000000000..623956f6e
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/AncestorListener.java
@@ -0,0 +1,69 @@
+/* AncestorListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * AncestorListener Interface
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public interface AncestorListener extends EventListener {
+
+ /**
+ * Ancestor Added
+ * @param event Ancestor Event
+ */
+ void ancestorAdded(AncestorEvent event);
+
+ /**
+ * Ancestor Removed
+ * @param event Ancestor Event
+ */
+ void ancestorRemoved(AncestorEvent event);
+
+ /**
+ * Ancestor Moved
+ * @param event Ancestor Event
+ */
+ void ancestorMoved(AncestorEvent event);
+
+
+} // AncestorListener
diff --git a/libjava/classpath/javax/swing/event/CaretEvent.java b/libjava/classpath/javax/swing/event/CaretEvent.java
new file mode 100644
index 000000000..ef0436d1c
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/CaretEvent.java
@@ -0,0 +1,70 @@
+/* CaretEvent.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+/**
+ * CaretEvent
+ * @author Andrew Selkirk
+ */
+public abstract class CaretEvent extends EventObject
+{
+
+ /**
+ * CaretEvent constructor
+ * @param source Source object
+ */
+ public CaretEvent(Object source)
+ {
+ super(source);
+ }
+
+ /**
+ * Get caret location
+ * @return the dot
+ */
+ public abstract int getDot();
+
+ /**
+ * Get mark
+ * @return the mark
+ */
+ public abstract int getMark();
+
+}
diff --git a/libjava/classpath/javax/swing/event/CaretListener.java b/libjava/classpath/javax/swing/event/CaretListener.java
new file mode 100644
index 000000000..ab7305d5e
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/CaretListener.java
@@ -0,0 +1,56 @@
+/* CaretListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * CaretListener public interface
+ * @author Andrew Selkirk
+ */
+public interface CaretListener extends EventListener {
+
+ /**
+ * Caret position has been updated
+ * @param event Caret Event
+ */
+ void caretUpdate(CaretEvent event);
+
+
+} // CaretListener
diff --git a/libjava/classpath/javax/swing/event/CellEditorListener.java b/libjava/classpath/javax/swing/event/CellEditorListener.java
new file mode 100644
index 000000000..4252c2712
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/CellEditorListener.java
@@ -0,0 +1,62 @@
+/* CellEditorListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * CellEditorListener public interface
+ * @author Andrew Selkirk
+ */
+public interface CellEditorListener extends EventListener {
+
+ /**
+ * Editing has been canceled
+ * @param event Change Event
+ */
+ void editingCanceled(ChangeEvent event);
+
+ /**
+ * Editing has been stopped
+ * @param event Change Event
+ */
+ void editingStopped(ChangeEvent event);
+
+
+} // CellEditorListener
diff --git a/libjava/classpath/javax/swing/event/ChangeEvent.java b/libjava/classpath/javax/swing/event/ChangeEvent.java
new file mode 100644
index 000000000..86834128d
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/ChangeEvent.java
@@ -0,0 +1,66 @@
+/* ChangeEvent.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+/**
+ * An event used to signal a state change for an object.
+ *
+ * @see ChangeListener
+ * @see CellEditorListener
+ * @see TableColumnModelListener
+ *
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public class ChangeEvent
+ extends EventObject
+{
+
+ /**
+ * Creates a new <code>ChangeEvent</code> instance for the specified source.
+ *
+ * @param source the source for the event (<code>null</code> not permitted).
+ */
+ public ChangeEvent(Object source)
+ {
+ super(source);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/ChangeListener.java b/libjava/classpath/javax/swing/event/ChangeListener.java
new file mode 100644
index 000000000..0f9d08774
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/ChangeListener.java
@@ -0,0 +1,63 @@
+/* ChangeListener.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+/**
+ * A <code>ChangeListener</code> can register with an object to receive
+ * notification of state changes (for objects that support this mechanism).
+ *
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public interface ChangeListener
+ extends EventListener
+{
+
+ /**
+ * Called by an object to notify the listener that the object's state has
+ * changed. The incoming <code>event</code> identifies the
+ * <code>source</code> of the event, allowing the listener to differentiate
+ * when it is listening for changes in multiple sources.
+ *
+ * @param event the change event.
+ */
+ void stateChanged(ChangeEvent event);
+
+}
diff --git a/libjava/classpath/javax/swing/event/DocumentEvent.java b/libjava/classpath/javax/swing/event/DocumentEvent.java
new file mode 100644
index 000000000..4e1235542
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/DocumentEvent.java
@@ -0,0 +1,156 @@
+/* DocumentEvent.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+
+/**
+ * DocumentEvent public interface
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public interface DocumentEvent
+{
+ /**
+ * ElementChange public interface
+ */
+ public static interface ElementChange
+ {
+ /**
+ * getIndex
+ * @return int
+ */
+ int getIndex();
+
+ /**
+ * getElement
+ * @return Element
+ */
+ Element getElement();
+
+ /**
+ * getChildrenRemoved
+ * @return Element[]
+ */
+ Element[] getChildrenRemoved();
+
+ /**
+ * getChildrenAdded
+ * @return Element[]
+ */
+ Element[] getChildrenAdded();
+
+ }
+
+ /**
+ * EventType
+ */
+ final class EventType
+ {
+ /**
+ * INSERT
+ */
+ public static final EventType INSERT = new EventType("INSERT"); // TODO
+
+ /**
+ * REMOVE
+ */
+ public static final EventType REMOVE = new EventType("REMOVE"); // TODO
+
+ /**
+ * CHANGE
+ */
+ public static final EventType CHANGE = new EventType("CHANGE"); // TODO
+
+ /**
+ * typeString
+ */
+ private String type;
+
+ /**
+ * Constructor EventType
+ * @param type TODO
+ */
+ private EventType(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * toString
+ * @return String
+ */
+ public String toString()
+ {
+ return type;
+ }
+ }
+
+ /**
+ * getType
+ * @return EventType
+ */
+ EventType getType();
+
+ /**
+ * getOffset
+ * @return int
+ */
+ int getOffset();
+
+ /**
+ * getLength
+ * @return int
+ */
+ int getLength();
+
+ /**
+ * getDocument
+ * @return Document
+ */
+ Document getDocument();
+
+ /**
+ * getChange
+ * @param element TODO
+ * @return ElementChange
+ */
+ ElementChange getChange(Element element);
+
+}
diff --git a/libjava/classpath/javax/swing/event/DocumentListener.java b/libjava/classpath/javax/swing/event/DocumentListener.java
new file mode 100644
index 000000000..28a7d9d0a
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/DocumentListener.java
@@ -0,0 +1,68 @@
+/* DocumentListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+/**
+ * DocumentListener public interface
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public interface DocumentListener extends EventListener {
+
+ /**
+ * Changed update
+ * @param event Document Event
+ */
+ void changedUpdate(DocumentEvent event);
+
+ /**
+ * Insert update
+ * @param event Document Event
+ */
+ void insertUpdate(DocumentEvent event);
+
+ /**
+ * Remove update
+ * @param event Document Event
+ */
+ void removeUpdate(DocumentEvent event);
+
+
+} // DocumentListener
diff --git a/libjava/classpath/javax/swing/event/EventListenerList.java b/libjava/classpath/javax/swing/event/EventListenerList.java
new file mode 100644
index 000000000..940d1574c
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/EventListenerList.java
@@ -0,0 +1,359 @@
+/* EventListenerList.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.EventListener;
+
+
+/**
+ * A utility class for keeping track of {@link EventListener}s.
+ *
+ * <p><b>Example for using this class:</b>
+ *
+ * <blockquote><pre> import java.util.EventListener;
+ * import javax.swing.event.EventListenerList;
+ *
+ * class Foo
+ * {
+ * protected final EventListenerList listeners = new EventListenerList();
+ * protected BarClosedEvent barClosedEvent = null;
+ *
+ * public void addBarListener(BarListener l)
+ * {
+ * listeners.<a href="#add(java.lang.Class, java.util.EventListener)"
+ * >add</a>(BarListener.class, l);
+ * }
+ *
+ * public void removeBarListener(BarListener l)
+ * {
+ * listeners.<a href="#remove(java.lang.Class, java.util.EventListener)"
+ * >remove</a>(BarListener.class, l);
+ * }
+ *
+ * protected void fireBarClosedEvent()
+ * {
+ * Object[] l = listeners.<a href="#getListenerList()"
+ * >getListenerList()</a>;
+ *
+ * for (int i = l.length - 2; i >= 0; i -= 2)
+ * if (l[i] == BarListener.class)
+ * {
+ * // Create the event on demand, when it is needed the first time.
+ * if (barClosedEvent == null)
+ * barClosedEvent = new BarClosedEvent(this);
+ *
+ * ((BarClosedListener) l[i + 1]).barClosed(barClosedEvent);
+ * }
+ * }
+ * }</pre></blockquote>
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class EventListenerList
+ implements Serializable
+{
+ /**
+ * An ID for serializing instances of this class; verified with the
+ * serialver tool of Sun J2SE 1.4.1_01.
+ */
+ static final long serialVersionUID = -5677132037850737084L;
+
+
+ /**
+ * An empty array that is shared by all instances of this class that
+ * have no listeners.
+ */
+ private static final Object[] NO_LISTENERS = new Object[0];
+
+
+ /**
+ * An array with all currently registered listeners. The array has
+ * twice as many elements as there are listeners. For an even
+ * integer <code>i</code>, <code>listenerList[i]</code> indicates
+ * the registered class, and <code>listenerList[i + 1]</code> is the
+ * listener.
+ */
+ protected transient Object[] listenerList = NO_LISTENERS;
+
+
+ /**
+ * EventListenerList constructor
+ */
+ public EventListenerList()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Registers a listener of a specific type.
+ *
+ * @param t the type of the listener.
+ *
+ * @param listener the listener to add, which must be an instance of
+ * <code>t</code>, or of a subclass of <code>t</code>.
+ *
+ * @throws IllegalArgumentException if <code>listener</code> is not
+ * an instance of <code>t</code> (or a subclass thereof).
+ *
+ * @throws NullPointerException if <code>t</code> is <code>null</code>.
+ */
+ public <T extends EventListener> void add(Class<T> t, T listener)
+ {
+ int oldLength;
+ Object[] newList;
+
+ if (listener == null)
+ return;
+
+ if (!t.isInstance(listener))
+ throw new IllegalArgumentException();
+
+ oldLength = listenerList.length;
+ newList = new Object[oldLength + 2];
+ if (oldLength > 0)
+ System.arraycopy(listenerList, 0, newList, 0, oldLength);
+
+ newList[oldLength] = t;
+ newList[oldLength + 1] = listener;
+ listenerList = newList;
+ }
+
+
+ /**
+ * Determines the number of listeners.
+ */
+ public int getListenerCount()
+ {
+ return listenerList.length / 2;
+ }
+
+
+ /**
+ * Determines the number of listeners of a particular class.
+ *
+ * @param t the type of listeners to be counted. In order to get
+ * counted, a subscribed listener must be exactly of class
+ * <code>t</code>. Thus, subclasses of <code>t</code> will not be
+ * counted.
+ */
+ public int getListenerCount(Class<?> t)
+ {
+ int result = 0;
+ for (int i = 0; i < listenerList.length; i += 2)
+ if (t == listenerList[i])
+ ++result;
+
+ return result;
+ }
+
+
+ /**
+ * Returns an array containing a sequence of listenerType/listener pairs, one
+ * for each listener.
+ *
+ * @return An array containing the listener types and references.
+ */
+ public Object[] getListenerList()
+ {
+ // returning the internal storage is a bad idea, but tests show that the
+ // reference implementation does this...
+ return listenerList;
+ }
+
+
+ /**
+ * Retrieves the currently subscribed listeners of a particular
+ * type. For a listener to be returned, it must have been
+ * registered with exactly the type <code>c</code>; subclasses are
+ * not considered equal.
+ *
+ * <p>The returned array can always be cast to <code>c[]</code>.
+ * Since it is a newly allocated copy, the caller may arbitrarily
+ * modify the array.
+ *
+ * @param c the class which was passed to {@link #add}.
+ *
+ * @throws ClassCastException if <code>c</code> does not implement
+ * the {@link EventListener} interface.
+ *
+ * @throws NullPointerException if <code>c</code> is
+ * <code>null</code>.
+ *
+ * @return an array of <code>c</code> whose elements are the
+ * currently subscribed listeners of the specified type. If there
+ * are no such listeners, an empty array is returned.
+ *
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> c)
+ {
+ int count, f;
+ EventListener[] result;
+
+ count = getListenerCount(c);
+ result = (EventListener[]) Array.newInstance(c, count);
+ f = 0;
+ for (int i = listenerList.length - 2; i >= 0; i -= 2)
+ if (listenerList[i] == c)
+ result[f++] = (EventListener) listenerList[i + 1];
+
+ return (T[]) result;
+ }
+
+
+ /**
+ * Removes a listener of a specific type.
+ *
+ * @param t the type of the listener.
+ *
+ * @param listener the listener to remove, which must be an instance
+ * of <code>t</code>, or of a subclass of <code>t</code>.
+ *
+ * @throws IllegalArgumentException if <code>listener</code> is not
+ * an instance of <code>t</code> (or a subclass thereof).
+ *
+ * @throws NullPointerException if <code>t</code> is <code>null</code>.
+ */
+ public <T extends EventListener> void remove(Class<T> t, T listener)
+ {
+ Object[] oldList, newList;
+ int oldLength;
+
+ if (listener == null)
+ return;
+
+ if (!t.isInstance(listener))
+ throw new IllegalArgumentException();
+
+ oldList = listenerList;
+ oldLength = oldList.length;
+ for (int i = 0; i < oldLength; i += 2)
+ if (oldList[i] == t && oldList[i + 1] == listener)
+ {
+ if (oldLength == 2)
+ newList = NO_LISTENERS;
+ else
+ {
+ newList = new Object[oldLength - 2];
+ if (i > 0)
+ System.arraycopy(oldList, 0, newList, 0, i);
+ if (i < oldLength - 2)
+ System.arraycopy(oldList, i + 2, newList, i,
+ oldLength - 2 - i);
+ }
+ listenerList = newList;
+ return;
+ }
+ }
+
+
+ /**
+ * Returns a string representation of this object that may be useful
+ * for debugging purposes.
+ */
+ public String toString()
+ {
+ CPStringBuilder buf = new CPStringBuilder("EventListenerList: ");
+ buf.append(listenerList.length / 2);
+ buf.append(" listeners: ");
+ for (int i = 0; i < listenerList.length; i += 2)
+ {
+ buf.append(" type ");
+ buf.append(((Class) listenerList[i]).getName());
+ buf.append(" listener ");
+ buf.append(listenerList[i + 1]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Serializes an instance to an ObjectOutputStream.
+ *
+ * @param out the stream to serialize to
+ *
+ * @throws IOException if something goes wrong
+ */
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+ for (int i = 0; i < listenerList.length; i += 2)
+ {
+ Class cl = (Class) listenerList[i];
+ EventListener l = (EventListener) listenerList[i + 1];
+ if (l != null && l instanceof Serializable)
+ {
+ out.writeObject(cl.getName());
+ out.writeObject(l);
+ }
+ }
+ // Write end marker.
+ out.writeObject(null);
+ }
+
+ /**
+ * Deserializes an instance from an ObjectInputStream.
+ *
+ * @param in the input stream
+ *
+ * @throws ClassNotFoundException if a serialized class can't be found
+ * @throws IOException if something goes wrong
+ */
+ private <T extends EventListener> void readObject(ObjectInputStream in)
+ throws ClassNotFoundException, IOException
+ {
+ listenerList = NO_LISTENERS;
+ in.defaultReadObject();
+ Object type;
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ while ((type = in.readObject()) != null)
+ {
+ EventListener l = (EventListener) in.readObject();
+ add(((Class<T>) Class.forName((String) type, true, cl)), (T) l);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/event/HyperlinkEvent.java b/libjava/classpath/javax/swing/event/HyperlinkEvent.java
new file mode 100644
index 000000000..75092b51a
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/HyperlinkEvent.java
@@ -0,0 +1,162 @@
+/* HyperlinkEvent.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.net.URL;
+import java.util.EventObject;
+
+import javax.swing.text.Element;
+
+/**
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public class HyperlinkEvent extends EventObject
+{
+ public static final class EventType
+ {
+ public static final EventType ENTERED = new EventType("ENTERED"); // TODO
+ public static final EventType EXITED = new EventType("EXITED"); // TODO
+ public static final EventType ACTIVATED = new EventType("ACTIVATED"); // TODO
+
+ private String type;
+
+ /**
+ * Creates a new Event type.
+ *
+ * @param type String representing the event type.
+ */
+ private EventType(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns a <code>String</code> of this object.
+ */
+ public String toString()
+ {
+ return type;
+ }
+ }
+
+ private static final long serialVersionUID = -2054640811732867012L;
+
+ private EventType type;
+ private URL url;
+ private String description;
+ private Element element;
+
+ /**
+ * Creates a new <code>HyperlinkEvent</code> with the given arguments.
+ *
+ * @param source The object this link is associated to.
+ * @param type The type of event.
+ * @param url The URL this link pointing too.
+ */
+ public HyperlinkEvent(Object source, EventType type, URL url)
+ {
+ this (source, type, url, null, null);
+ }
+
+ /**
+ * Creates a new <code>HyperlinkEvent</code> with the given arguments.
+ *
+ * @param source The object this link is associated to.
+ * @param type The type of event.
+ * @param url The URL this link pointing too.
+ * @param description The description for this link.
+ */
+ public HyperlinkEvent(Object source, EventType type, URL url,
+ String description)
+ {
+ this (source, type, url, description, null);
+ }
+
+ /**
+ * Creates a new <code>HyperlinkEvent</code> with the given arguments.
+ *
+ * @param source The object this link is associated to.
+ * @param type The type of event.
+ * @param url The URL this link pointing too.
+ * @param description The description for this link.
+ * @param element The element in the document representing the anchor.
+ */
+ public HyperlinkEvent(Object source, EventType type, URL url,
+ String description, Element element)
+ {
+ super(source);
+ this.type = type;
+ this.url = url;
+ this.description = description;
+ this.element = element;
+ }
+
+ /**
+ * Returns the element of the document repesenting this anchor.
+ */
+ public Element getSourceElement()
+ {
+ return element;
+ }
+
+ /**
+ * Returns the URL of this event.
+ */
+ public URL getURL()
+ {
+ return url;
+ }
+
+ /**
+ * Returns the type of this event.
+ */
+ public EventType getEventType()
+ {
+ return type;
+ }
+
+ /**
+ * Returns the description of this event.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+}
diff --git a/libjava/classpath/javax/swing/event/HyperlinkListener.java b/libjava/classpath/javax/swing/event/HyperlinkListener.java
new file mode 100644
index 000000000..0e01ba7ed
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/HyperlinkListener.java
@@ -0,0 +1,57 @@
+/* HyperlinkListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * HyperlinkListener
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public interface HyperlinkListener extends EventListener {
+
+ /**
+ * Hyperlink updated
+ * @param event Hyperlink Event
+ */
+ void hyperlinkUpdate(HyperlinkEvent event);
+
+
+} // HyperlinkListener
diff --git a/libjava/classpath/javax/swing/event/InternalFrameAdapter.java b/libjava/classpath/javax/swing/event/InternalFrameAdapter.java
new file mode 100644
index 000000000..da893c76d
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/InternalFrameAdapter.java
@@ -0,0 +1,126 @@
+/* InternalFrameAdapter.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+/**
+ * InternalFrameAdapter.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class InternalFrameAdapter implements InternalFrameListener
+{
+ /**
+ * InternalFrameAdapter constructor.
+ */
+ public InternalFrameAdapter()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Internal frame activated.
+ *
+ * @param event internal frame event
+ */
+ public void internalFrameActivated(InternalFrameEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Internal frame closed.
+ *
+ * @param event internal frame event
+ */
+ public void internalFrameClosed(InternalFrameEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Internal frame closing.
+ *
+ * @param event internal frame event
+ */
+ public void internalFrameClosing(InternalFrameEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Internal frame deactivated.
+ *
+ * @param event internal frame event
+ */
+ public void internalFrameDeactivated(InternalFrameEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Internal frame deiconified.
+ *
+ * @param event internal frame event
+ */
+ public void internalFrameDeiconified(InternalFrameEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Internal frame iconified.
+ *
+ * @param event internal frame event
+ */
+ public void internalFrameIconified(InternalFrameEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Internal frame opened.
+ *
+ * @param event internal frame event
+ */
+ public void internalFrameOpened(InternalFrameEvent event)
+ {
+ // Nothing to do here.
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/InternalFrameEvent.java b/libjava/classpath/javax/swing/event/InternalFrameEvent.java
new file mode 100644
index 000000000..1d7145d31
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/InternalFrameEvent.java
@@ -0,0 +1,154 @@
+/* InternalFrameEvent.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.awt.AWTEvent;
+
+import javax.swing.JInternalFrame;
+
+/**
+ * An event that indicates a change to a {@link JInternalFrame} component.
+ *
+ * @author Andrew Selkirk
+ */
+public class InternalFrameEvent extends AWTEvent
+{
+ private static final long serialVersionUID = -5204823611874873183L;
+
+ /**
+ * Internal frame activated event.
+ */
+ public static final int INTERNAL_FRAME_ACTIVATED = 25554;
+
+ /**
+ * Internal frame closed event.
+ */
+ public static final int INTERNAL_FRAME_CLOSED = 25551;
+
+ /**
+ * Internal frame closing event.
+ */
+ public static final int INTERNAL_FRAME_CLOSING = 25550;
+
+ /**
+ * Internal frame deactivated event.
+ */
+ public static final int INTERNAL_FRAME_DEACTIVATED = 25555;
+
+ /**
+ * Internal frame deiconifed event.
+ */
+ public static final int INTERNAL_FRAME_DEICONIFIED = 25553;
+
+ /**
+ * Internal frame frame first event.
+ */
+ public static final int INTERNAL_FRAME_FIRST = 25549;
+
+ /**
+ * Internal frame iconified event.
+ */
+ public static final int INTERNAL_FRAME_ICONIFIED = 25552;
+
+ /**
+ * Internal frame last event.
+ */
+ public static final int INTERNAL_FRAME_LAST = 25555;
+
+ /**
+ * Internal frame opened event.
+ */
+ public static final int INTERNAL_FRAME_OPENED = 25549;
+
+ /**
+ * Creates a new <code>JInternalFrameEvent</code> instance.
+ *
+ * @param source the source of this event (<code>null</code> not permitted).
+ * @param id the event ID of this event (see the constants defined by this
+ * class).
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public InternalFrameEvent(JInternalFrame source, int id)
+ {
+ super(source, id);
+ }
+
+ /**
+ * Returns the <code>JInternalFrame</code> component that is the source for
+ * this event.
+ *
+ * @return The source.
+ *
+ * @since 1.3
+ */
+ public JInternalFrame getInternalFrame()
+ {
+ return (JInternalFrame) source;
+ }
+
+ /**
+ * Returns a string that indicates the event id. This is used by the
+ * {@link #toString()} method.
+ *
+ * @return A string that indicates the event id.
+ */
+ public String paramString()
+ {
+ switch (id) {
+ case INTERNAL_FRAME_ACTIVATED:
+ return "INTERNAL_FRAME_ACTIVATED";
+ case INTERNAL_FRAME_CLOSED:
+ return "INTERNAL_FRAME_CLOSED";
+ case INTERNAL_FRAME_CLOSING:
+ return "INTERNAL_FRAME_CLOSING";
+ case INTERNAL_FRAME_DEACTIVATED:
+ return "INTERNAL_FRAME_DEACTIVATED";
+ case INTERNAL_FRAME_DEICONIFIED:
+ return "INTERNAL_FRAME_DEICONIFIED";
+ case INTERNAL_FRAME_ICONIFIED:
+ return "INTERNAL_FRAME_ICONIFIED";
+ case INTERNAL_FRAME_OPENED:
+ return "INTERNAL_FRAME_OPENED";
+ default:
+ return "unknown type";
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/event/InternalFrameListener.java b/libjava/classpath/javax/swing/event/InternalFrameListener.java
new file mode 100644
index 000000000..36874d216
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/InternalFrameListener.java
@@ -0,0 +1,92 @@
+/* InternalFrameListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * InternalFrameListener public interface
+ * @author Andrew Selkirk
+ */
+public interface InternalFrameListener extends EventListener {
+
+ /**
+ * Internal frame activated
+ * @param event Internal Frame Event
+ */
+ void internalFrameActivated(InternalFrameEvent event);
+
+ /**
+ * Internal frame closed
+ * @param event Internal Frame Event
+ */
+ void internalFrameClosed(InternalFrameEvent event);
+
+ /**
+ * Internal frame closing
+ * @param event Internal Frame Event
+ */
+ void internalFrameClosing(InternalFrameEvent event);
+
+ /**
+ * Internal frame deactivated
+ * @param event Internal Frame Event
+ */
+ void internalFrameDeactivated(InternalFrameEvent event);
+
+ /**
+ * Internal frame deiconified
+ * @param event Internal Frame Event
+ */
+ void internalFrameDeiconified(InternalFrameEvent event);
+
+ /**
+ * Internal frame iconified
+ * @param event Internal Frame Event
+ */
+ void internalFrameIconified(InternalFrameEvent event);
+
+ /**
+ * Internal frame opened
+ * @param event Internal Frame Event
+ */
+ void internalFrameOpened(InternalFrameEvent event);
+
+
+} // InternalFrameListener
diff --git a/libjava/classpath/javax/swing/event/ListDataEvent.java b/libjava/classpath/javax/swing/event/ListDataEvent.java
new file mode 100644
index 000000000..51fa887f8
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/ListDataEvent.java
@@ -0,0 +1,132 @@
+/* ListDataEvent.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+/**
+ * An event that contains information about a modification to the content of
+ * a list.
+ *
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public class ListDataEvent extends EventObject
+{
+ private static final long serialVersionUID = 2510353260071004774L;
+
+ /** An event type indicating that the list content has been modified. */
+ public static final int CONTENTS_CHANGED = 0;
+
+ /** An event type indicating that an interval has been added to the list. */
+ public static final int INTERVAL_ADDED = 1;
+
+ /**
+ * An event type indicating that an interval has been removed from the
+ * list.
+ */
+ public static final int INTERVAL_REMOVED = 2;
+
+ private int type;
+ private int index0;
+ private int index1;
+
+ /**
+ * Creates a <code>ListDataEvent</code> object.
+ *
+ * @param source the source of the event (<code>null</code> not permitted).
+ * @param type the type of the event (should be one of
+ * {@link #CONTENTS_CHANGED}, {@link #INTERVAL_ADDED} or
+ * {@link #INTERVAL_REMOVED}, although this is not enforced).
+ * @param index0 the index for one end of the modified range of list
+ * elements.
+ * @param index1 the index for the other end of the modified range of list
+ * elements.
+ */
+ public ListDataEvent(Object source, int type, int index0, int index1)
+ {
+ super(source);
+ this.type = type;
+ this.index0 = Math.min(index0, index1);
+ this.index1 = Math.max(index0, index1);
+ }
+
+ /**
+ * Returns the index of the first item in the range of modified list items.
+ *
+ * @return The index of the first item in the range of modified list items.
+ */
+ public int getIndex0()
+ {
+ return index0;
+ }
+
+ /**
+ * Returns the index of the last item in the range of modified list items.
+ *
+ * @return The index of the last item in the range of modified list items.
+ */
+ public int getIndex1()
+ {
+ return index1;
+ }
+
+ /**
+ * Returns a code representing the type of this event, which is usually one
+ * of {@link #CONTENTS_CHANGED}, {@link #INTERVAL_ADDED} or
+ * {@link #INTERVAL_REMOVED}.
+ *
+ * @return The event type.
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+ /**
+ * Returns a string representing the state of this event.
+ *
+ * @return A string.
+ */
+ public String toString()
+ {
+ return getClass().getName() + "[type=" + type + ",index0=" + index0
+ + ",index1=" + index1 + "]";
+ }
+}
diff --git a/libjava/classpath/javax/swing/event/ListDataListener.java b/libjava/classpath/javax/swing/event/ListDataListener.java
new file mode 100644
index 000000000..4bbe1e5e4
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/ListDataListener.java
@@ -0,0 +1,82 @@
+/* ListDataListener.java --
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+import javax.swing.ListModel;
+
+/**
+ * A <code>ListDataListener</code> can register with a {@link ListModel} and
+ * receive notification of updates to the model.
+ *
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public interface ListDataListener extends EventListener
+{
+
+ /**
+ * Notifies the listener that the contents of the list have changed
+ * in some way. This method will be called if the change cannot be
+ * notified via the {@link #intervalAdded(ListDataEvent)} or the
+ * {@link #intervalRemoved(ListDataEvent)} methods.
+ *
+ * @param event the event.
+ */
+ void contentsChanged(ListDataEvent event);
+
+ /**
+ * Notifies the listener that one or more items have been added to the
+ * list. The <code>event</code> argument can supply the indices for the
+ * range of items added.
+ *
+ * @param event the event.
+ */
+ void intervalAdded(ListDataEvent event);
+
+ /**
+ * Notifies the listener that one or more items have been removed from
+ * the list. The <code>event</code> argument can supply the indices for
+ * the range of items removed.
+ *
+ * @param event the event.
+ */
+ void intervalRemoved(ListDataEvent event);
+
+}
diff --git a/libjava/classpath/javax/swing/event/ListSelectionEvent.java b/libjava/classpath/javax/swing/event/ListSelectionEvent.java
new file mode 100644
index 000000000..97555d17a
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/ListSelectionEvent.java
@@ -0,0 +1,135 @@
+/* ListSelectionEvent.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+import javax.swing.ListSelectionModel;
+
+/**
+ * An event that indicates a change to a list selection, including the source
+ * of the change (a {@link ListSelectionModel}) and the range of items in the
+ * list that have potentially changed their selection status.
+ *
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public class ListSelectionEvent extends EventObject
+{
+
+ /**
+ * The index of the first list item in the range of items that has
+ * potentially had its selection status modified.
+ */
+ private int firstIndex = 0;
+
+ /**
+ * The index of the last list item in the range of items that has
+ * potentially had its selection status modified.
+ */
+ private int lastIndex = 0;
+
+ /** A flag that indicates that this event is one in a series of events. */
+ private boolean isAdjusting = false;
+
+ /**
+ * Creates a new <code>ListSelectionEvent</code>.
+ *
+ * @param source the event source (<code>null</code> not permitted).
+ * @param firstIndex the first index.
+ * @param lastIndex the last index.
+ * @param isAdjusting a flag indicating that this event is one in a series
+ * of events updating a selection.
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public ListSelectionEvent(Object source, int firstIndex,
+ int lastIndex, boolean isAdjusting)
+ {
+ super(source);
+ this.firstIndex = firstIndex;
+ this.lastIndex = lastIndex;
+ this.isAdjusting = isAdjusting;
+ }
+
+ /**
+ * Returns the first index.
+ *
+ * @return The first index.
+ */
+ public int getFirstIndex()
+ {
+ return firstIndex;
+ }
+
+ /**
+ * Returns the last index.
+ *
+ * @return The last index.
+ */
+ public int getLastIndex()
+ {
+ return lastIndex;
+ }
+
+ /**
+ * Returns the flag that indicates that this event is one in a series of
+ * events updating a selection.
+ *
+ * @return A boolean.
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return isAdjusting;
+ }
+
+ /**
+ * Returns a string representation of the event, typically used for debugging
+ * purposes.
+ *
+ * @return A string representation of the event.
+ */
+ public String toString()
+ {
+ return this.getClass().toString() + "[ source=" + source.toString()
+ + " firstIndex= " + firstIndex + " lastIndex= " + lastIndex
+ + " isAdjusting= " + isAdjusting + " ]";
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/ListSelectionListener.java b/libjava/classpath/javax/swing/event/ListSelectionListener.java
new file mode 100644
index 000000000..20e40da58
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/ListSelectionListener.java
@@ -0,0 +1,61 @@
+/* ListSelectionListener.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+import javax.swing.ListSelectionModel;
+
+/**
+ * A listener that receives {@link ListSelectionEvent} notifications,
+ * typically from a {@link ListSelectionModel} when it is modified.
+ *
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public interface ListSelectionListener extends EventListener
+{
+
+ /**
+ * Receives notification of a {@link ListSelectionEvent}.
+ *
+ * @param event the event.
+ */
+ void valueChanged(ListSelectionEvent event);
+
+}
diff --git a/libjava/classpath/javax/swing/event/MenuDragMouseEvent.java b/libjava/classpath/javax/swing/event/MenuDragMouseEvent.java
new file mode 100644
index 000000000..952d99e39
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/MenuDragMouseEvent.java
@@ -0,0 +1,104 @@
+/* MenuDragMouseEvent.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.awt.Component;
+import java.awt.event.MouseEvent;
+
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+
+/**
+ * MenuDragMouseEvent
+ * @author Andrew Selkirk
+ */
+public class MenuDragMouseEvent extends MouseEvent
+{
+
+ /**
+ * path
+ */
+ private MenuElement[] path = null;
+
+ /**
+ * manager
+ */
+ private MenuSelectionManager manager = null;
+
+ /**
+ * Constructor MenuDragMouseEvent
+ * @param source Source
+ * @param id MouseEvent type
+ * @param when Time
+ * @param modifiers Key modifiers
+ * @param x Horizontal position
+ * @param y Vertical position
+ * @param clickCount Click count
+ * @param popupTrigger Popup trigger?
+ * @param path Path
+ * @param manager MenuSelectionManager
+ */
+ public MenuDragMouseEvent(Component source, int id, long when, int modifiers,
+ int x, int y, int clickCount, boolean popupTrigger,
+ MenuElement[] path, MenuSelectionManager manager)
+ {
+ super(source, id, when, modifiers, x, y, clickCount, popupTrigger);
+ this.path = path;
+ this.manager = manager;
+ }
+
+ /**
+ * Get path
+ * @return path
+ */
+ public MenuElement[] getPath()
+ {
+ return path;
+ }
+
+ /**
+ * Get menu selection manager
+ * @return manager
+ */
+ public MenuSelectionManager getMenuSelectionManager()
+ {
+ return manager;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/MenuDragMouseListener.java b/libjava/classpath/javax/swing/event/MenuDragMouseListener.java
new file mode 100644
index 000000000..d0cd0530f
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/MenuDragMouseListener.java
@@ -0,0 +1,74 @@
+/* MenuDragMouseListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * MenuDragMouseListener public interface
+ * @author Andrew Selkirk
+ */
+public interface MenuDragMouseListener extends EventListener {
+
+ /**
+ * Menu drag mouse dragged
+ * @param event Menu Drag Mouse Event
+ */
+ void menuDragMouseDragged(MenuDragMouseEvent event);
+
+ /**
+ * Menu drag mouse entered
+ * @param event Menu Drag Mouse Event
+ */
+ void menuDragMouseEntered(MenuDragMouseEvent event);
+
+ /**
+ * Menu drag mouse exited
+ * @param event Menu Drag Mouse Event
+ */
+ void menuDragMouseExited(MenuDragMouseEvent event);
+
+ /**
+ * Menu drag mouse released
+ * @param event Menu Drag Mouse Event
+ */
+ void menuDragMouseReleased(MenuDragMouseEvent event);
+
+
+} // MenuDragMouseListener
diff --git a/libjava/classpath/javax/swing/event/MenuEvent.java b/libjava/classpath/javax/swing/event/MenuEvent.java
new file mode 100644
index 000000000..6a7e0215f
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/MenuEvent.java
@@ -0,0 +1,59 @@
+/* MenuEvent.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+/**
+ * MenuEvent
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public class MenuEvent extends EventObject
+{
+
+ /**
+ * Constructor MenuEvent
+ * @param source Source object
+ */
+ public MenuEvent(Object source)
+ {
+ super(source);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/MenuKeyEvent.java b/libjava/classpath/javax/swing/event/MenuKeyEvent.java
new file mode 100644
index 000000000..937089ec2
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/MenuKeyEvent.java
@@ -0,0 +1,102 @@
+/* MenuKeyEvent.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.awt.Component;
+import java.awt.event.KeyEvent;
+
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+
+/**
+ * MenuKeyEvent
+ * @author Andrew Selkirk
+ */
+public class MenuKeyEvent extends KeyEvent
+{
+
+ /**
+ * path
+ */
+ private MenuElement[] path = null;
+
+ /**
+ * manager
+ */
+ private MenuSelectionManager manager = null;
+
+ /**
+ * Constructor MenuKeyEvent
+ * @param source Source
+ * @param id KeyEvent ID
+ * @param when Time
+ * @param modifiers Modifier keys
+ * @param keyCode Key code
+ * @param keyChar Key char
+ * @param path Path
+ * @param manager MenuSelectionManager
+ */
+ public MenuKeyEvent(Component source, int id, long when, int modifiers,
+ int keyCode, char keyChar, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ super(source, id, when, modifiers, keyCode, keyChar);
+ this.path = path;
+ this.manager = manager;
+ }
+
+ /**
+ * getPath
+ * @return path
+ */
+ public MenuElement[] getPath()
+ {
+ return path;
+ }
+
+ /**
+ * getMenuSelectionManager
+ * @return MenuSelectionManager
+ */
+ public MenuSelectionManager getMenuSelectionManager()
+ {
+ return manager;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/MenuKeyListener.java b/libjava/classpath/javax/swing/event/MenuKeyListener.java
new file mode 100644
index 000000000..36d6ecdd4
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/MenuKeyListener.java
@@ -0,0 +1,68 @@
+/* MenuKeyListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * MenuKeyListener public interface
+ * @author Andrew Selkirk
+ */
+public interface MenuKeyListener extends EventListener {
+
+ /**
+ * Menu key pressed
+ * @param event Menu Key Event
+ */
+ void menuKeyPressed(MenuKeyEvent event);
+
+ /**
+ * Menu key released
+ * @param event Menu Key Event
+ */
+ void menuKeyReleased(MenuKeyEvent event);
+
+ /**
+ * Menu key typed
+ * @param event Menu Key Event
+ */
+ void menuKeyTyped(MenuKeyEvent event);
+
+
+} // MenuKeyListener
diff --git a/libjava/classpath/javax/swing/event/MenuListener.java b/libjava/classpath/javax/swing/event/MenuListener.java
new file mode 100644
index 000000000..10a18f215
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/MenuListener.java
@@ -0,0 +1,68 @@
+/* MenuListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * MenuListener public interface
+ * @author Andrew Selkirk
+ */
+public interface MenuListener extends EventListener {
+
+ /**
+ * Menu canceled
+ * @param event Menu Event
+ */
+ void menuCanceled(MenuEvent event);
+
+ /**
+ * Menu deselected
+ * @param event Menu Event
+ */
+ void menuDeselected(MenuEvent event);
+
+ /**
+ * Menu selected
+ * @param event Menu Event
+ */
+ void menuSelected(MenuEvent event);
+
+
+} // MenuListener
diff --git a/libjava/classpath/javax/swing/event/MouseInputAdapter.java b/libjava/classpath/javax/swing/event/MouseInputAdapter.java
new file mode 100644
index 000000000..2da5543eb
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/MouseInputAdapter.java
@@ -0,0 +1,119 @@
+/* MouseInputAdapter.java --
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.awt.event.MouseEvent;
+
+/**
+ * MouseInputAdapter
+ * @author Andrew Selkirk
+ */
+public abstract class MouseInputAdapter implements MouseInputListener
+{
+ /**
+ * Constructor MouseInputAdapter
+ */
+ public MouseInputAdapter()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Mouse clicked
+ * @param event Mouse event
+ */
+ public void mouseClicked(MouseEvent event)
+ {
+ // Do nothing by default.
+ }
+
+ /**
+ * Mouse dragged
+ * @param event Mouse event
+ */
+ public void mouseDragged(MouseEvent event)
+ {
+ // Do nothing by default.
+ }
+
+ /**
+ * Mouse entered
+ * @param event Mouse event
+ */
+ public void mouseEntered(MouseEvent event)
+ {
+ // Do nothing by default.
+ }
+
+ /**
+ * Mouse exited
+ * @param event Mouse event
+ */
+ public void mouseExited(MouseEvent event)
+ {
+ // Do nothing by default.
+ }
+
+ /**
+ * Mouse moved
+ * @param event Mouse event
+ */
+ public void mouseMoved(MouseEvent event)
+ {
+ // Do nothing by default.
+ }
+
+ /**
+ * Mouse pressed
+ * @param event Mouse event
+ */
+ public void mousePressed(MouseEvent event)
+ {
+ // Do nothing by default.
+ }
+
+ /**
+ * Mouse released
+ * @param event Mouse event
+ */
+ public void mouseReleased(MouseEvent event)
+ {
+ // Do nothing by default.
+ }
+}
diff --git a/libjava/classpath/javax/swing/event/MouseInputListener.java b/libjava/classpath/javax/swing/event/MouseInputListener.java
new file mode 100644
index 000000000..fc9261533
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/MouseInputListener.java
@@ -0,0 +1,53 @@
+/* MouseInputListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+/**
+ * MouseInputListener public interface.
+ *
+ * @author Andrew Selkirk
+ */
+public interface MouseInputListener extends MouseListener,
+ MouseMotionListener
+{
+ // This interface only pulls together MouseListener and MouseMotionListener
+ // without adding any methods on its own.
+}
diff --git a/libjava/classpath/javax/swing/event/PopupMenuEvent.java b/libjava/classpath/javax/swing/event/PopupMenuEvent.java
new file mode 100644
index 000000000..640b900ad
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/PopupMenuEvent.java
@@ -0,0 +1,58 @@
+/* PopupMenuEvent.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventObject;
+
+/**
+ * PopupMenuEvent
+ * @author Andrew Selkirk
+ */
+public class PopupMenuEvent extends EventObject {
+
+ /**
+ * Constructor PopupMenuEvent
+ * @param source Source
+ */
+ public PopupMenuEvent(Object source) {
+ super(source);
+ } // PopupMenuEvent()
+
+
+} // PopupMenuEvent
diff --git a/libjava/classpath/javax/swing/event/PopupMenuListener.java b/libjava/classpath/javax/swing/event/PopupMenuListener.java
new file mode 100644
index 000000000..18d68f406
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/PopupMenuListener.java
@@ -0,0 +1,68 @@
+/* PopupMenuListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * PopupMenuListener public interface
+ * @author Andrew Selkirk
+ */
+public interface PopupMenuListener extends EventListener {
+
+ /**
+ * Popup Menu Canceled
+ * @param event Popup Menu Event
+ */
+ void popupMenuCanceled(PopupMenuEvent event);
+
+ /**
+ * Popup Menu will become invisible
+ * @param event Popup Menu Event
+ */
+ void popupMenuWillBecomeInvisible(PopupMenuEvent event);
+
+ /**
+ * Popup Menu will become visible
+ * @param event Popup Menu Event
+ */
+ void popupMenuWillBecomeVisible(PopupMenuEvent event);
+
+
+} // PopupMenuListener
diff --git a/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java b/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java
new file mode 100644
index 000000000..5228b3d57
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java
@@ -0,0 +1,70 @@
+/* SwingPropertyChangeSupport.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+/**
+ * Provides a mechanism for registering {@link PropertyChangeListener}s and
+ * forwarding {@link PropertyChangeEvent}s to those listeners.
+ *
+ * As of JDK1.5 this class is no longer in use. Use
+ * {@link PropertyChangeSupport} instead.
+ *
+ * @author Andrew Selkirk
+ */
+public final class SwingPropertyChangeSupport
+ extends PropertyChangeSupport
+{
+
+ private static final long serialVersionUID = 7162625831330845068L;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param source the source (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>source</code> is <code>null</code>.
+ */
+ public SwingPropertyChangeSupport(Object source)
+ {
+ super(source);
+ }
+}
diff --git a/libjava/classpath/javax/swing/event/TableColumnModelEvent.java b/libjava/classpath/javax/swing/event/TableColumnModelEvent.java
new file mode 100644
index 000000000..70bd7d050
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TableColumnModelEvent.java
@@ -0,0 +1,93 @@
+/* TableColumnModelEvent.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+import javax.swing.table.TableColumnModel;
+
+/**
+ * TableColumnModelEvent
+ * @author Andrew Selkirk
+ */
+public class TableColumnModelEvent extends EventObject
+{
+
+ /**
+ * fromIndex
+ */
+ protected int fromIndex = 0;
+
+ /**
+ * toIndex
+ */
+ protected int toIndex = 0;
+
+ /**
+ * Constructor TableColumnModelEvent
+ * @param source Source TableColumnModel
+ * @param from From index
+ * @param to To index
+ */
+ public TableColumnModelEvent(TableColumnModel source, int from, int to)
+ {
+ super(source);
+ fromIndex = from;
+ toIndex = to;
+ }
+
+ /**
+ * getFromIndex.
+ * @return From index
+ */
+ public int getFromIndex()
+ {
+ return fromIndex;
+ }
+
+ /**
+ * getToIndex.
+ * @return To index
+ */
+ public int getToIndex()
+ {
+ return toIndex;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/TableColumnModelListener.java b/libjava/classpath/javax/swing/event/TableColumnModelListener.java
new file mode 100644
index 000000000..362a4d9cd
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TableColumnModelListener.java
@@ -0,0 +1,94 @@
+/* TableColumnModelListener.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+import javax.swing.table.TableColumnModel;
+
+/**
+ * A <code>TableColumnModelListener</code> can register with a
+ * {@link TableColumnModel} to receive notification of changes to the model.
+ *
+ * @author Andrew Selkirk
+ */
+public interface TableColumnModelListener
+ extends EventListener
+{
+
+ /**
+ * Called by the {@link TableColumnModel} to indicate that a column has been
+ * added to the model.
+ *
+ * @param event information about the column addition.
+ */
+ void columnAdded(TableColumnModelEvent event);
+
+ /**
+ * Called by the {@link TableColumnModel} to indicate that the model's
+ * column margin has changed.
+ *
+ * @param event the event (identifies the source).
+ */
+ void columnMarginChanged(ChangeEvent event);
+
+ /**
+ * Called by the {@link TableColumnModel} to indicate that a column has been
+ * moved.
+ *
+ * @param event information about the column move.
+ */
+ void columnMoved(TableColumnModelEvent event);
+
+ /**
+ * Called by the {@link TableColumnModel} to indicate that a column has been
+ * removed from the model.
+ *
+ * @param event information about the column removal.
+ */
+ void columnRemoved(TableColumnModelEvent event);
+
+ /**
+ * Called by the {@link TableColumnModel} to indicate that the column
+ * selection state has changed.
+ *
+ * @param event information about the column selection state.
+ */
+ void columnSelectionChanged(ListSelectionEvent event);
+
+}
diff --git a/libjava/classpath/javax/swing/event/TableModelEvent.java b/libjava/classpath/javax/swing/event/TableModelEvent.java
new file mode 100644
index 000000000..b75a78a1c
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TableModelEvent.java
@@ -0,0 +1,220 @@
+/* TableModelEvent.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+import javax.swing.table.TableModel;
+
+/**
+ * An event that describes changes to a {@link TableModel}.
+ *
+ * @see javax.swing.event.TableModelListener
+ *
+ * @author Andrew Selkirk
+ */
+public class TableModelEvent extends EventObject
+{
+ private static final long serialVersionUID = -7849342674552212824L;
+
+ /** A column index representing all columns. */
+ public static final int ALL_COLUMNS = -1;
+
+ /**
+ * An event type indicating that one or more rows have been deleted from the
+ * model.
+ */
+ public static final int DELETE = -1;
+
+ /** A row index representing the header row. */
+ public static final int HEADER_ROW = -1;
+
+ /**
+ * An event type indicating that one or more rows have been inserted into the
+ * model.
+ */
+ public static final int INSERT = 1;
+
+ /** An event type indicating that data has been updated in the model. */
+ public static final int UPDATE = 0;
+
+ /** The column in the table model that the event relates to. */
+ protected int column = 0;
+
+ /** The first row in the table model that the event relates to. */
+ protected int firstRow = 0;
+
+ /** The last row in the table model that the event relates to. */
+ protected int lastRow = 0;
+
+ /**
+ * The event type (one of {@link #UPDATE}, {@link #INSERT}, {@link #DELETE}).
+ */
+ protected int type = 0;
+
+ /**
+ * Creates a new <code>TableModelEvent</code> indicating an {@link #UPDATE}
+ * to the data in all columns and rows.
+ *
+ * @param source the source object (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public TableModelEvent(TableModel source)
+ {
+ this(source, 0, Integer.MAX_VALUE, ALL_COLUMNS, UPDATE);
+ }
+
+ /**
+ * Creates a new <code>TableModelEvent</code> indicating an {@link #UPDATE}
+ * to the data in a single row across all columns.
+ *
+ * @param source the source object (<code>null</code> not permitted).
+ * @param row the updated row.
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public TableModelEvent(TableModel source, int row)
+ {
+ this(source, row, row, ALL_COLUMNS, UPDATE);
+ }
+
+ /**
+ * Creates a new <code>TableModelEvent</code> indicating an {@link #UPDATE}
+ * to the data in the specified rows across all columns.
+ *
+ * @param source the source object (<code>null</code> not permitted).
+ * @param firstRow the first row of update.
+ * @param lastRow the last row of update.
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public TableModelEvent(TableModel source, int firstRow, int lastRow)
+ {
+ this(source, firstRow, lastRow, ALL_COLUMNS, UPDATE);
+ }
+
+ /**
+ * Creates a new <code>TableModelEvent</code> indicating an {@link #UPDATE}
+ * to the data in the specified rows and column. Use {@link #ALL_COLUMNS}
+ * for the <code>column</code> argument to indicate all columns.
+ *
+ * @param source the source object (<code>null</code> not permitted).
+ * @param firstRow the first row of update.
+ * @param lastRow the last row of update.
+ * @param column the affected column.
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public TableModelEvent(TableModel source, int firstRow, int lastRow,
+ int column)
+ {
+ this(source, firstRow, lastRow, column, UPDATE);
+ }
+
+ /**
+ * Creates a new <code>TableModelEvent</code> indicating an operation of
+ * the specified <code>type</code> on the data in the specified rows and
+ * column. The event type is usually one of {@link #UPDATE}, {@link #INSERT},
+ * and {@link #DELETE}.
+ *
+ * @param source the source object (<code>null</code> not permitted).
+ * @param firstRow the first row of update.
+ * @param lastRow the last row of update.
+ * @param column the affected column.
+ * @param type the type of change.
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public TableModelEvent(TableModel source, int firstRow, int lastRow,
+ int column, int type)
+ {
+ super(source);
+ this.firstRow = firstRow;
+ this.lastRow = lastRow;
+ this.column = column;
+ this.type = type;
+ }
+
+ /**
+ * Returns the affected column of this event.
+ *
+ * @return The column index.
+ */
+ public int getColumn()
+ {
+ return column;
+ }
+
+ /**
+ * Returns the first affected row of this event.
+ *
+ * @return The row index.
+ */
+ public int getFirstRow()
+ {
+ return firstRow;
+ }
+
+ /**
+ * Returns the last affected row of this event.
+ *
+ * @return The row index.
+ */
+ public int getLastRow()
+ {
+ return lastRow;
+ }
+
+ /**
+ * Returns the type of change indicated by this event (usually one of
+ * {@link #UPDATE}, {@link #INSERT}, {@link #DELETE}).
+ *
+ * @return The type.
+ */
+ public int getType()
+ {
+ return type;
+ }
+}
diff --git a/libjava/classpath/javax/swing/event/TableModelListener.java b/libjava/classpath/javax/swing/event/TableModelListener.java
new file mode 100644
index 000000000..612712f85
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TableModelListener.java
@@ -0,0 +1,60 @@
+/* TableModelListener.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+/**
+ * A <code>TableModelListener</code> can register with a
+ * {@link javax.swing.table.TableModel} and receive notification of updates to
+ * the model.
+ *
+ * @author Andrew Selkirk
+ */
+public interface TableModelListener extends EventListener
+{
+
+ /**
+ * Called to notify the listener that the
+ * {@link javax.swing.table.TableModel} has been updated.
+ *
+ * @param event contains details of the update.
+ */
+ void tableChanged(TableModelEvent event);
+
+}
diff --git a/libjava/classpath/javax/swing/event/TreeExpansionEvent.java b/libjava/classpath/javax/swing/event/TreeExpansionEvent.java
new file mode 100644
index 000000000..99e166677
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TreeExpansionEvent.java
@@ -0,0 +1,77 @@
+/* TreeExpansionEvent.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+import javax.swing.tree.TreePath;
+
+/**
+ * TreeExpansionEvent
+ * @author Andrew Selkirk
+ */
+public class TreeExpansionEvent extends EventObject
+{
+
+ /**
+ * path
+ */
+ protected TreePath path = null;
+
+ /**
+ * Constructor TreeExpansionEvent
+ * @param source Source object
+ * @param path Path
+ */
+ public TreeExpansionEvent(Object source, TreePath path)
+ {
+ super(source);
+ this.path = path;
+ }
+
+ /**
+ * getPath
+ * @return Tree path
+ */
+ public TreePath getPath()
+ {
+ return path;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/TreeExpansionListener.java b/libjava/classpath/javax/swing/event/TreeExpansionListener.java
new file mode 100644
index 000000000..3461f2ce4
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TreeExpansionListener.java
@@ -0,0 +1,62 @@
+/* TreeExpansionListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+/**
+ * TreeExpansionListener public interface
+ * @author Andrew Selkirk
+ */
+public interface TreeExpansionListener extends EventListener
+{
+
+ /**
+ * Tree collapsed
+ * @param event Tree Expansion Event
+ */
+ void treeCollapsed(TreeExpansionEvent event);
+
+ /**
+ * Tree expanded
+ * @param event Tree Expansion Event
+ */
+ void treeExpanded(TreeExpansionEvent event);
+
+
+}
diff --git a/libjava/classpath/javax/swing/event/TreeModelEvent.java b/libjava/classpath/javax/swing/event/TreeModelEvent.java
new file mode 100644
index 000000000..490126f4d
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TreeModelEvent.java
@@ -0,0 +1,168 @@
+/* TreeModelEvent.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+import javax.swing.tree.TreePath;
+
+/**
+ * TreeModelEvent
+ * @author Andrew Selkirk
+ */
+public class TreeModelEvent extends EventObject
+{
+
+ /**
+ * childIndices
+ */
+ protected int[] childIndices = null;
+
+ /**
+ * children
+ */
+ protected Object[] children = null;
+
+ /**
+ * path
+ */
+ protected TreePath path = null;
+
+ /**
+ * Constructor TreeModelEvent
+ * @param source Source object
+ * @param path
+ */
+ public TreeModelEvent(Object source, Object[] path)
+ {
+ super(source);
+ this.path = new TreePath(path);
+ }
+
+ /**
+ * Constructor TreeModelEvent
+ * @param source Source object
+ * @param path path
+ * @param childIndices Child indices
+ * @param children Children
+ */
+ public TreeModelEvent(Object source, Object[] path,
+ int[] childIndices, Object[] children)
+ {
+ super(source);
+ this.path = new TreePath(path);
+ this.childIndices = childIndices;
+ this.children = children;
+ }
+
+ /**
+ * Constructor TreeModelEvent
+ * @param source Source object
+ * @param path Path
+ */
+ public TreeModelEvent(Object source, TreePath path)
+ {
+ super(source);
+ this.path = path;
+ }
+
+ /**
+ * Constructor TreeModelEvent
+ * @param source Source object
+ * @param path Path
+ * @param childIndices Child indices
+ * @param children Children
+ */
+ public TreeModelEvent(Object source, TreePath path,
+ int[] childIndices, Object[] children)
+ {
+ super(source);
+ this.path = path;
+ this.childIndices = childIndices;
+ this.children = children;
+ }
+
+ /**
+ * getChildIndices
+ * @return child indices
+ */
+ public int[] getChildIndices()
+ {
+ return childIndices;
+ }
+
+ /**
+ * getChildren
+ * @return children
+ */
+ public Object[] getChildren()
+ {
+ return children;
+ }
+
+ /**
+ * getPath
+ * @return path
+ */
+ public Object[] getPath()
+ {
+ return path.getPath();
+ }
+
+ /**
+ * getTreePath
+ * @return TreePath
+ */
+ public TreePath getTreePath()
+ {
+ return path;
+ }
+
+ /**
+ * String representation
+ * @return String representation
+ */
+ public String toString()
+ {
+ return getClass() + " [Source: " + getSource() + ", TreePath: "
+ + getTreePath() + ", Child Indicies: " + getChildIndices()
+ + ", Children: " + getChildren() + ", Path: " + getPath() +"]";
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/TreeModelListener.java b/libjava/classpath/javax/swing/event/TreeModelListener.java
new file mode 100644
index 000000000..780410588
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TreeModelListener.java
@@ -0,0 +1,74 @@
+/* TreeModelListener.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+// Imports
+import java.util.EventListener;
+
+/**
+ * TreeModelListener public interface
+ * @author Andrew Selkirk
+ */
+public interface TreeModelListener extends EventListener {
+
+ /**
+ * Tree nodes changed
+ * @param event Tree Model Event
+ */
+ void treeNodesChanged(TreeModelEvent event);
+
+ /**
+ * Tree nodes inserted
+ * @param event Tree Model Event
+ */
+ void treeNodesInserted(TreeModelEvent event);
+
+ /**
+ * Tree nodes removed
+ * @param event Tree Model Event
+ */
+ void treeNodesRemoved(TreeModelEvent event);
+
+ /**
+ * Tree structured changed
+ * @param event Tree Model Event
+ */
+ void treeStructureChanged(TreeModelEvent event);
+
+
+} // TreeModelListener
diff --git a/libjava/classpath/javax/swing/event/TreeSelectionEvent.java b/libjava/classpath/javax/swing/event/TreeSelectionEvent.java
new file mode 100644
index 000000000..830170d9f
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TreeSelectionEvent.java
@@ -0,0 +1,257 @@
+/* TreeSelectionEvent.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+/**
+ * An event that carries information about a change to a
+ * {@link TreeSelectionModel}.
+ *
+ * @see TreeSelectionListener
+ *
+ * @author Andrew Selkirk
+ */
+public class TreeSelectionEvent extends EventObject
+{
+
+ /**
+ * The paths that have been added or removed from the selection.
+ */
+ protected TreePath[] paths;
+
+ /**
+ * Flags indicating if the paths were added (<code>true</code>) or removed
+ * (<code>false</code>) from the selection.
+ */
+ protected boolean[] areNew;
+
+ /**
+ * The old lead selection path (may be <code>null</code>).
+ */
+ protected TreePath oldLeadSelectionPath;
+
+ /**
+ * The new lead selection path (may be <code>null</code>).
+ */
+ protected TreePath newLeadSelectionPath;
+
+ /**
+ * Creates a new <code>TreeSelectionEvent</code>.
+ *
+ * @param source the source (usually a {@link TreeSelectionModel},
+ * <code>null</code> not permitted).
+ * @param paths an array of the paths that have been added to or removed
+ * from the selection.
+ * @param areNew a flag for each path where <code>true</code> indicates the
+ * corresponding path has been added to the selection and
+ * <code>false</code> indicates the path has been removed.
+ * @param oldLeadSelectionPath the old lead selection path (<code>null</code>
+ * permitted).
+ * @param newLeadSelectionPath the new lead selection path (<code>null</code>
+ * permitted).
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public TreeSelectionEvent(Object source, TreePath[] paths,
+ boolean[] areNew, TreePath oldLeadSelectionPath,
+ TreePath newLeadSelectionPath)
+ {
+ super(source);
+ this.paths = paths;
+ this.areNew = areNew;
+ this.oldLeadSelectionPath = oldLeadSelectionPath;
+ this.newLeadSelectionPath = newLeadSelectionPath;
+ }
+
+ /**
+ * Creates a new <code>TreeSelectionEvent</code>.
+ *
+ * @param source the event source (usually a {@link TreeSelectionModel},
+ * <code>null</code> not permitted).
+ * @param path the path.
+ * @param isNew <code>true</code> indicates that <code>path</code> has been
+ * added to the selection, and <code>false</code> indicates that it has
+ * been removed.
+ * @param oldLeadSelectionPath the old lead selection path (<code>null</code>
+ * permitted).
+ * @param newLeadSelectionPath the new lead selection path (<code>null</code>
+ * permitted).
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public TreeSelectionEvent(Object source, TreePath path,
+ boolean isNew, TreePath oldLeadSelectionPath,
+ TreePath newLeadSelectionPath)
+ {
+ super(source);
+ this.paths = new TreePath[]{path};
+ this.areNew = new boolean[]{isNew};
+ this.oldLeadSelectionPath = oldLeadSelectionPath;
+ this.newLeadSelectionPath = newLeadSelectionPath;
+ }
+
+ /**
+ * Returns the first path element.
+ *
+ * @return The first path element.
+ *
+ * @see #getPaths()
+ */
+ public TreePath getPath()
+ {
+ return paths[0];
+ }
+
+ /**
+ * Returns an array of the paths that changed in the selection.
+ *
+ * @return The paths that changed in the selection.
+ *
+ * @see #isAddedPath(TreePath)
+ */
+ public TreePath[] getPaths()
+ {
+ return (TreePath[]) paths.clone();
+ }
+
+ /**
+ * Returns <code>true</code> if the path returned by {@link #getPath()} has
+ * been added to the selection, and <code>false</code> if it has been
+ * removed.
+ *
+ * @return A boolean.
+ *
+ * @see #isAddedPath(int)
+ */
+ public boolean isAddedPath()
+ {
+ return areNew[0];
+ }
+
+ /**
+ * Returns <code>true</code> if <code>path</code> has been added to the
+ * selection, and <code>false</code> if the path has been removed from the
+ * selection.
+ *
+ * @param path the path to check.
+ *
+ * @return A flag indicating whether the path has been added to, or removed
+ * from, the selection.
+ *
+ * @throw IllegalArgumentException if <code>path</code> is not one of the
+ * paths in {@link #getPaths()}.
+ *
+ * @see #isAddedPath(int)
+ */
+ public boolean isAddedPath(TreePath path)
+ {
+ for (int i = paths.length - 1; i >= 0; i--)
+ if (paths[i].equals(path))
+ return areNew[i];
+
+ throw new IllegalArgumentException("Unknown 'path' argument.");
+ }
+
+ /**
+ * Returns <code>true</code> if the path at the specified index has been
+ * added to the selection, and <code>false</code> if the path has been
+ * removed from the selection.
+ *
+ * @param index the path index.
+ *
+ * @return A flag indicating whether the path has been added to, or removed
+ * from, the selection.
+ *
+ * @since 1.3
+ *
+ * @see #isAddedPath(TreePath)
+ */
+ public boolean isAddedPath(int index)
+ {
+ return areNew[index];
+ }
+
+ /**
+ * Returns the old lead selection path.
+ *
+ * @return The old lead selection path (possibly <code>null</code>).
+ *
+ * @see #getNewLeadSelectionPath()
+ */
+ public TreePath getOldLeadSelectionPath()
+ {
+ return oldLeadSelectionPath;
+ }
+
+ /**
+ * Returns the new lead selection path.
+ *
+ * @return The new lead selection path (possibly <code>null</code>).
+ *
+ * @see #getOldLeadSelectionPath()
+ */
+ public TreePath getNewLeadSelectionPath()
+ {
+ return newLeadSelectionPath;
+ }
+
+ /**
+ * Creates a shallow copy of this <code>TreeSelectionEvent</code>, replacing
+ * the source with <code>source</code>.
+ *
+ * @param source the new event source (<code>null</code> not permitted).
+ *
+ * @return A cloned event with another event source.
+ *
+ * @throws IllegalArgumentException if <code>source</code> is
+ * <code>null</code>.
+ */
+ public Object cloneWithSource(Object source)
+ {
+ return new TreeSelectionEvent (source, paths, areNew, oldLeadSelectionPath,
+ newLeadSelectionPath);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/TreeSelectionListener.java b/libjava/classpath/javax/swing/event/TreeSelectionListener.java
new file mode 100644
index 000000000..8f8711d3a
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TreeSelectionListener.java
@@ -0,0 +1,60 @@
+/* TreeSelectionListener.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+import javax.swing.tree.TreeSelectionModel;
+
+/**
+ * A listener that receives {@link TreeSelectionEvent} notifications from a
+ * source (such as a {@link TreeSelectionModel}).
+ *
+ * @author Andrew Selkirk
+ */
+public interface TreeSelectionListener extends EventListener
+{
+
+ /**
+ * Receives notification of a change to a tree selection model.
+ *
+ * @param event information about the event.
+ */
+ void valueChanged(TreeSelectionEvent event);
+
+}
diff --git a/libjava/classpath/javax/swing/event/TreeWillExpandListener.java b/libjava/classpath/javax/swing/event/TreeWillExpandListener.java
new file mode 100644
index 000000000..7bdcbe48d
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/TreeWillExpandListener.java
@@ -0,0 +1,65 @@
+/* TreeWillExpandListener.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+import javax.swing.tree.ExpandVetoException;
+
+/**
+ * @author Andrew Selkirk
+ */
+public interface TreeWillExpandListener extends EventListener
+{
+ /**
+ * Invoked whenever a node in the tree is about to be collapsed.
+ *
+ * @param event The tree expansion Event
+ */
+ void treeWillCollapse(TreeExpansionEvent event)
+ throws ExpandVetoException;
+
+ /**
+ * Invoked whenever a node in the tree is about to be expanded.
+ *
+ * @param event The tree expansion Event
+ */
+ void treeWillExpand(TreeExpansionEvent event)
+ throws ExpandVetoException;
+}
diff --git a/libjava/classpath/javax/swing/event/UndoableEditEvent.java b/libjava/classpath/javax/swing/event/UndoableEditEvent.java
new file mode 100644
index 000000000..b889bb656
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/UndoableEditEvent.java
@@ -0,0 +1,80 @@
+/* UndoableEditEvent.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventObject;
+
+import javax.swing.undo.UndoableEdit;
+
+/**
+ * UndoableEditEvent
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public class UndoableEditEvent extends EventObject
+{
+
+ private static final long serialVersionUID = 4418044561759134484L;
+
+ /**
+ * edit
+ */
+ private UndoableEdit edit;
+
+ /**
+ * Constructor UndoableEditEvent
+ * @param source TODO
+ * @param edit TODO
+ */
+ public UndoableEditEvent(Object source, UndoableEdit edit)
+ {
+ super(source);
+ this.edit = edit;
+ }
+
+ /**
+ * getEdit
+ * @return UndoableEdit
+ */
+ public UndoableEdit getEdit()
+ {
+ return edit;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/event/UndoableEditListener.java b/libjava/classpath/javax/swing/event/UndoableEditListener.java
new file mode 100644
index 000000000..13eecf51a
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/UndoableEditListener.java
@@ -0,0 +1,56 @@
+/* UndoableEditListener.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event;
+
+import java.util.EventListener;
+
+
+/**
+ * UndoableEditListener public interface
+ * @author Andrew Selkirk
+ * @author Ronald Veldema
+ */
+public interface UndoableEditListener extends EventListener
+{
+ /**
+ * Undoable edit has happened
+ *
+ * @param event Undoable Edit Event
+ */
+ void undoableEditHappened(UndoableEditEvent event);
+}
diff --git a/libjava/classpath/javax/swing/event/package.html b/libjava/classpath/javax/swing/event/package.html
new file mode 100644
index 000000000..faef7e15a
--- /dev/null
+++ b/libjava/classpath/javax/swing/event/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.event package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.event</title></head>
+
+<body>
+<p>Provides events and listeners used by components in the
+<code>javax.swing</code> package.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/filechooser/FileFilter.java b/libjava/classpath/javax/swing/filechooser/FileFilter.java
new file mode 100644
index 000000000..68bcb6531
--- /dev/null
+++ b/libjava/classpath/javax/swing/filechooser/FileFilter.java
@@ -0,0 +1,85 @@
+/* FileFilter.java --
+ Copyright (C) 2002, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.filechooser;
+
+import java.io.File;
+
+import javax.swing.JFileChooser;
+
+/**
+ * The base class for filters that control the visibility of files in the
+ * {@link JFileChooser} component.
+ *
+ * @see JFileChooser#addChoosableFileFilter(FileFilter)
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class FileFilter
+{
+
+ /**
+ * Default constructor.
+ */
+ public FileFilter()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns <code>true</code> if the specified file matches the filter, and
+ * <code>false</code> otherwise.
+ *
+ * @param file the file.
+ *
+ * @return A boolean.
+ */
+ public abstract boolean accept(File file);
+
+ /**
+ * Returns a description of the files that will be selected by the filter
+ * (for example, "Java source files"). This description will usually be
+ * displayed on the {@link JFileChooser} component, often in a combo box that
+ * is used to select the appropriate filter (in cases where more than one
+ * filter is available).
+ *
+ * @return A description of the filter.
+ */
+ public abstract String getDescription();
+
+}
diff --git a/libjava/classpath/javax/swing/filechooser/FileSystemView.java b/libjava/classpath/javax/swing/filechooser/FileSystemView.java
new file mode 100644
index 000000000..5c3c0b8ee
--- /dev/null
+++ b/libjava/classpath/javax/swing/filechooser/FileSystemView.java
@@ -0,0 +1,410 @@
+/* FileSystemView.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.filechooser;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.swing.Icon;
+import javax.swing.JFileChooser;
+
+
+/**
+ * The base class providing a view of the file system for use by the
+ * {@link JFileChooser} component.
+ */
+public abstract class FileSystemView
+{
+ /** The instance returned by {@link #getFileSystemView()}. */
+ private static FileSystemView defaultFileSystemView;
+
+ /**
+ * Creates a new file object with the given name in the specified directory.
+ *
+ * @param dir the directory (<code>null</code> permitted).
+ * @param filename the file name.
+ *
+ * @return A new file object.
+ */
+ public File createFileObject(File dir, String filename)
+ {
+ return new File(dir, filename);
+ }
+
+ /**
+ * Creates a new file object from the specified path.
+ *
+ * @param path the path.
+ *
+ * @return A new file object.
+ */
+ public File createFileObject(String path)
+ {
+ File f = new File(path);
+ if (isFileSystemRoot(f))
+ f = this.createFileSystemRoot(f);
+ return f;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param f DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected File createFileSystemRoot(File f)
+ {
+ File[] roots = File.listRoots();
+ if (roots == null)
+ return null;
+ return roots[0];
+ }
+
+ /**
+ * Creates a new folder with a unique name in the specified directory and
+ * returns a {@link File} object representing the new directory.
+ *
+ * @param containingDir the directory to contain the new folder
+ * (<code>null</code> not permitted).
+ *
+ * @return A {@link File} object representing the new directory.
+ *
+ * @throws IOException if an exception occurs while creating the new
+ * directory.
+ */
+ public abstract File createNewFolder(File containingDir)
+ throws IOException;
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param parent DOCUMENT ME!
+ * @param fileName DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public File getChild(File parent, String fileName)
+ {
+ // FIXME: Handle the case when parent and child are special folders.
+ return new File(parent, fileName);
+ }
+
+ /**
+ * Returns the default directory.
+ *
+ * @return The default directory.
+ */
+ public File getDefaultDirectory()
+ {
+ return getHomeDirectory();
+ }
+
+ /**
+ * Returns an array containing the files in the given directory. The
+ * <code>useFileHiding</code> controls whether or not hidden files are
+ * included in the result.
+ *
+ * @param dir the directory (if <code>null</code>
+ * @param useFileHiding a flag that controls whether or not hidden files are
+ * included in the result (pass in <code>true</code> to
+ * exclude hidden files).
+ *
+ * @return The files in the given directory (possibly <code>null</code>).
+ */
+ public File[] getFiles(File dir, boolean useFileHiding)
+ {
+ if (dir == null || dir.listFiles() == null)
+ return null;
+ File[] files = dir.listFiles();
+ if (! useFileHiding)
+ return files;
+ ArrayList trim = new ArrayList();
+ for (int i = 0; i < files.length; i++)
+ if (! files[i].isHidden())
+ trim.add(files[i]);
+ File[] value = (File[]) trim.toArray(new File[trim.size()]);
+ return value;
+ }
+
+ /**
+ * Returns a default {@link FileSystemView} appropriate for the platform.
+ *
+ * @return A default {@link FileSystemView} appropriate for the platform.
+ */
+ public static FileSystemView getFileSystemView()
+ {
+ if (defaultFileSystemView == null)
+ {
+ // FIXME: We need to support other file systems too.
+ defaultFileSystemView = new UnixFileSystemView();
+ }
+ return defaultFileSystemView;
+ }
+
+ /**
+ * Returns the home directory for the current user.
+ *
+ * @return The home directory for the current user.
+ */
+ public File getHomeDirectory()
+ {
+ return createFileObject(System.getProperty("user.home"));
+ }
+
+ /**
+ * Returns the parent directory for the given file/directory.
+ *
+ * @param f the file/directory.
+ *
+ * @return The parent directory (or <code>null</code> if there is no parent
+ * directory).
+ */
+ public File getParentDirectory(File f)
+ {
+ if (f == null)
+ return null;
+ return f.getParentFile();
+ }
+
+ /**
+ * Returns an array containing the file system roots. On Unix-like platforms,
+ * this array will contain just a single item ("/"), while other platforms
+ * may return multiple roots.
+ * <p>
+ * This method is implemented to return <code>null</code>, subclasses must
+ * override this method.
+ *
+ * @return An array containing the file system roots.
+ */
+ public File[] getRoots()
+ {
+ // subclass
+ return null;
+ }
+
+ /**
+ * Returns the name of a file as it would be displayed by the underlying
+ * system.
+ *
+ * @param f the file.
+ *
+ * @return the name of a file as it would be displayed by the underlying
+ * system
+ *
+ * @specnote The specification suggests that the information here is
+ * fetched from a ShellFolder class. This seems to be a non public
+ * private file handling class. We simply return File.getName()
+ * here and leave special handling to subclasses.
+ */
+ public String getSystemDisplayName(File f)
+ {
+ String name = null;
+ if (f != null)
+ name = f.getName();
+ return name;
+ }
+
+ /**
+ * Returns the icon that would be displayed for the given file by the
+ * underlying system. This implementation returns <code>null</code>,
+ * subclasses must override.
+ *
+ * @param f the file.
+ *
+ * @return <code>null</code>.
+ */
+ public Icon getSystemIcon(File f)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the type description of a file that would be displayed by the
+ * underlying system. This implementation returns <code>null</code>,
+ * subclasses must override.
+ *
+ * @param f the file.
+ *
+ * @return <code>null</code>.
+ */
+ public String getSystemTypeDescription(File f)
+ {
+ return null;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param dir DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public boolean isComputerNode(File dir)
+ {
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> if the given directory represents a disk
+ * drive, and <code>false</code> otherwise. This default implementation
+ * always returns <code>false</code>.
+ *
+ * @param dir the directory.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isDrive(File dir)
+ {
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> if <code>f</code> is a file or directory, and
+ * <code>false</code> otherwise.
+ *
+ * @param f the file/directory.
+ *
+ * @return <code>true</code> if <code>f</code> is a file or directory, and
+ * <code>false</code> otherwise.
+ */
+ public boolean isFileSystem(File f)
+ {
+ return (f.isFile() || f.isDirectory());
+ }
+
+ /**
+ * Returns <code>true</code> if the given directory is a file system root,
+ * and <code>false</code> otherwise.
+ *
+ * @param dir the directory.
+ *
+ * @return <code>true</code> if the given directory is a file system root,
+ * and <code>false</code> otherwise.
+ */
+ public boolean isFileSystemRoot(File dir)
+ {
+ File[] roots = File.listRoots();
+ if (roots == null || dir == null)
+ return false;
+ String filename = dir.getAbsolutePath();
+ for (int i = 0; i < roots.length; i++)
+ if (roots[i].getAbsolutePath().equals(filename))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> if the given directory represents a floppy
+ * drive, and <code>false</code> otherwise. This default implementation
+ * always returns <code>false</code>.
+ *
+ * @param dir the directory.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isFloppyDrive(File dir)
+ {
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> if the given file is hidden, and
+ * <code>false</code> otherwise.
+ *
+ * @param f the file.
+ *
+ * @return <code>true</code> if the given file is hidden, and
+ * <code>false</code> otherwise.
+ */
+ public boolean isHiddenFile(File f)
+ {
+ return f.isHidden();
+ }
+
+ /**
+ * Returns <code>true</code> if <code>folder</code> is the parent of
+ * <code>file</code>, and <code>false</code> otherwise.
+ *
+ * @param folder the folder (<code>null</code> not permitted).
+ * @param file the file (<code>null</code> not permitted).
+ *
+ * @return <code>true</code> if <code>folder</code> is the parent of
+ * <code>file</code>, and <code>false</code> otherwise.
+ */
+ public boolean isParent(File folder, File file)
+ {
+ File parent = file.getParentFile();
+ if (parent == null)
+ return false;
+ return folder.equals(parent);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param f DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public boolean isRoot(File f)
+ {
+ // These are not file system roots.
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> if the file is traversable, and
+ * <code>false</code> otherwise. Here, all directories are considered
+ * traversable, and files are considered non-traversable.
+ *
+ * @param f the file or directory (<code>null</code> not permitted).
+ *
+ * @return <code>true</code> if the file is traversable, and
+ * <code>false</code> otherwise.
+ */
+ public Boolean isTraversable(File f)
+ {
+ // Tested. A directory where the user has no permission to rwx is still
+ // traversable. (No files are listed when you traverse the directory)
+ // My best guess is that as long as it's a directory, the file is
+ // traversable.
+ return Boolean.valueOf(f.isDirectory());
+ }
+}
diff --git a/libjava/classpath/javax/swing/filechooser/FileView.java b/libjava/classpath/javax/swing/filechooser/FileView.java
new file mode 100644
index 000000000..cfa39996b
--- /dev/null
+++ b/libjava/classpath/javax/swing/filechooser/FileView.java
@@ -0,0 +1,128 @@
+/* FileView.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.filechooser;
+
+import java.io.File;
+
+import javax.swing.Icon;
+
+/**
+ * An abstract class that provides presentation information about files and
+ * directories. .
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class FileView
+{
+
+ /**
+ * Creates a new <code>FileView</code> instance.
+ */
+ public FileView()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the name for the specified file. This method always returns
+ * <code>null</code> and should be overridden by subclasses.
+ *
+ * @param file the file.
+ *
+ * @return Always <code>null</code>.
+ */
+ public String getName(File file)
+ {
+ return null;
+ }
+
+ /**
+ * Returns a description for the specified file. This method always returns
+ * <code>null</code> and should be overridden by subclasses.
+ *
+ * @param file the file.
+ *
+ * @return Always <code>null</code>.
+ */
+ public String getDescription(File file)
+ {
+ return null;
+ }
+
+ /**
+ * Returns a description for the type of the specified file. This method
+ * always returns <code>null</code> and should be overridden by subclasses.
+ *
+ * @param file the file.
+ *
+ * @return Always <code>null</code>.
+ */
+ public String getTypeDescription(File file)
+ {
+ return null;
+ }
+
+ /**
+ * Returns an {@link Icon} to represent the specified file. This method
+ * always returns <code>null</code> and should be overridden by subclasses.
+ *
+ * @param file the file.
+ *
+ * @return Always <code>null</code>.
+ */
+ public Icon getIcon(File file)
+ {
+ return null;
+ }
+
+ /**
+ * Returns {@link Boolean#TRUE} if the given directory is traversable, and
+ * {@link Boolean#FALSE} if it is not. This method always returns
+ * <code>null</code> and should be overridden by subclasses.
+ *
+ * @param directory the directory.
+ *
+ * @return Always <code>null</code>.
+ */
+ public Boolean isTraversable(File directory)
+ {
+ return null;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java
new file mode 100644
index 000000000..13417683a
--- /dev/null
+++ b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java
@@ -0,0 +1,180 @@
+/* UnixFileSystemView.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.filechooser;
+
+import gnu.classpath.NotImplementedException;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.swing.Icon;
+
+
+/**
+ * A concrete implementation of {@link FileSystemView} that is appropriate for
+ * Unix-like systems.
+ *
+ * @see FileSystemView#getFileSystemView()
+ */
+class UnixFileSystemView extends FileSystemView
+{
+ /** The default name for new folders. */
+ private static final String NEW_FOLDER_NAME = "NewFolder";
+
+ /**
+ * Creates a new folder with a unique name in the specified directory and
+ * returns a {@link File} object representing the new directory. The name
+ * of the new folder is <code>NewFolder</code> or, if a directory or file
+ * with that name already exists, <code>NewFolder.n</code> where
+ * <code>n</code> is the lowest integer greater than zero that results in
+ * a unique directory name.
+ *
+ * @param containingDir the directory to contain the new folder
+ * (<code>null</code> not permitted).
+ *
+ * @return A {@link File} object representing the new directory.
+ *
+ * @throws IOException if an exception occurs while creating the new
+ * directory.
+ */
+ public File createNewFolder(File containingDir) throws IOException
+ {
+ int count = 0;
+ File f = null;
+ String filename = containingDir.getAbsolutePath() + File.separator
+ + NEW_FOLDER_NAME;
+ while (f == null)
+ {
+ String full = filename;
+ if (count > 0)
+ full += "." + (count++);
+ f = new File(full);
+ if (f.isDirectory() || f.isFile())
+ {
+ count++;
+ f = null;
+ }
+ }
+ f.mkdir();
+ return f;
+ }
+
+ /**
+ * Returns an array containing the file system root.
+ *
+ * @return An array containing the file system root.
+ */
+ public File[] getRoots()
+ {
+ return File.listRoots();
+ }
+
+ /**
+ * Returns the name of a file as it would be displayed by the underlying
+ * system.
+ *
+ * @param f the file.
+ *
+ * @return the name of a file as it would be displayed by the underlying
+ * system
+ */
+ public String getSystemDisplayName(File f)
+ {
+ String name = null;
+ if (f != null)
+ {
+ if (isRoot(f))
+ name = f.getAbsolutePath();
+ else
+ {
+ try
+ {
+ String path = f.getCanonicalPath();
+ name = path.substring(path.lastIndexOf(File.separator) + 1);
+ }
+ catch (IOException e)
+ {
+ name = f.getName();
+ }
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Returns the icon that would be displayed for the given file by the
+ * underlying system. This method is NOT YET IMPLEMENTED.
+ *
+ * @param f the file.
+ *
+ * @return <code>null</code>.
+ */
+ public Icon getSystemIcon(File f)
+ throws NotImplementedException
+ {
+ // FIXME: Implement;
+ return null;
+ }
+
+ /**
+ * Returns the description of a file that would be displayed by the
+ * underlying system. This method is NOT YET IMPLEMENTED.
+ *
+ * @param f the file.
+ *
+ * @return <code>null</code>.
+ */
+ public String getSystemTypeDescription(File f)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ return null;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param f DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public boolean isRoot(File f)
+ {
+ return isFileSystemRoot(f);
+ }
+}
diff --git a/libjava/classpath/javax/swing/filechooser/package.html b/libjava/classpath/javax/swing/filechooser/package.html
new file mode 100644
index 000000000..bf897abb5
--- /dev/null
+++ b/libjava/classpath/javax/swing/filechooser/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.event package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.filechooser</title></head>
+
+<body>
+<p>Provides support classes for the {@link javax.swing.JFileChooser}
+component.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/package.html b/libjava/classpath/javax/swing/package.html
new file mode 100644
index 000000000..30ceb88bf
--- /dev/null
+++ b/libjava/classpath/javax/swing/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing</title></head>
+
+<body>
+<p>Provides a collection of cross-platform user interface
+components.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java b/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java
new file mode 100644
index 000000000..07292fe24
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java
@@ -0,0 +1,62 @@
+/* ActionMapUIResource.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import javax.swing.ActionMap;
+
+
+/**
+ * An <code>ActionMap</code> that implements the {@link UIResource}
+ * interface to indicate that it belongs to a pluggable
+ * LookAndFeel.
+ *
+ * @see javax.swing.ActionMap
+ *
+ * @author Andrew Selkirk
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class ActionMapUIResource extends ActionMap implements UIResource
+{
+ /**
+ * Constructs a new ActionMapUIResource.
+ */
+ public ActionMapUIResource()
+ {
+ /* The constructor does nothing. */
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/BorderUIResource.java b/libjava/classpath/javax/swing/plaf/BorderUIResource.java
new file mode 100644
index 000000000..17424944c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/BorderUIResource.java
@@ -0,0 +1,930 @@
+/* BorderUIResource.java
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.io.Serializable;
+
+import javax.swing.Icon;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+import javax.swing.border.LineBorder;
+import javax.swing.border.MatteBorder;
+import javax.swing.border.TitledBorder;
+
+/**
+ * A wrapper for {@link javax.swing.border.Border} that also
+ * implements the {@link UIResource} marker interface. This is useful
+ * for implementing pluggable look-and-feels: When switching the
+ * current LookAndFeel, only those borders are replaced that are
+ * marked as {@link UIResource}. For this reason, a look-and-feel
+ * should always install borders that implement
+ * <code>UIResource</code>, such as the borders provided by this
+ * class.
+ *
+ * @serial
+ * @serialField delegate Border the <code>Border</code> wrapped
+ *
+ * @author Brian Jones (cbj@gnu.org)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class BorderUIResource implements Border, UIResource, Serializable
+{
+ /**
+ * Verified using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -3440553684010079691L;
+
+
+ /**
+ * A shared instance of an {@link EtchedBorderUIResource}, or
+ * <code>null</code> if the {@link #getEtchedBorderUIResource()}
+ * method has not yet been called.
+ */
+ private static Border etchedBorderUIResource;
+
+
+ /**
+ * A shared instance of a {@link BevelBorderUIResource} whose
+ * <code>bevelType</code> is {@link
+ * javax.swing.border.BevelBorder#LOWERED}, or <code>null</code> if
+ * the {@link #getLoweredBevelBorderUIResource()} has not yet been
+ * called.
+ */
+ private static Border loweredBevelBorderUIResource;
+
+
+ /**
+ * A shared instance of a {@link BevelBorderUIResource} whose
+ * <code>bevelType</code> is {@link
+ * javax.swing.border.BevelBorder#RAISED}, or <code>null</code> if
+ * the {@link #getRaisedBevelBorderUIResource()} has not yet been
+ * called.
+ */
+ private static Border raisedBevelBorderUIResource;
+
+
+ /**
+ * A shared instance of a {@link LineBorderUIResource} for
+ * a one-pixel thick black line, or <code>null</code> if
+ * the {@link #getBlackLineBorderUIResource()} has not yet been
+ * called.
+ */
+ private static Border blackLineBorderUIResource;
+
+
+ /**
+ * Returns a shared instance of an etched border which also
+ * is marked as an {@link UIResource}.
+ *
+ * @see javax.swing.border.EtchedBorder
+ */
+ public static Border getEtchedBorderUIResource()
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (etchedBorderUIResource == null)
+ etchedBorderUIResource = new EtchedBorderUIResource();
+ return etchedBorderUIResource;
+ }
+
+
+ /**
+ * Returns a shared instance of {@link BevelBorderUIResource} whose
+ * <code>bevelType</code> is {@link
+ * javax.swing.border.BevelBorder#LOWERED}.
+ *
+ * @see javax.swing.border.BevelBorder
+ */
+ public static Border getLoweredBevelBorderUIResource()
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (loweredBevelBorderUIResource == null)
+ loweredBevelBorderUIResource = new BevelBorderUIResource(
+ BevelBorder.LOWERED);
+ return loweredBevelBorderUIResource;
+ }
+
+
+ /**
+ * Returns a shared instance of {@link BevelBorderUIResource} whose
+ * <code>bevelType</code> is {@link
+ * javax.swing.border.BevelBorder#RAISED}.
+ *
+ * @see javax.swing.border.BevelBorder
+ */
+ public static Border getRaisedBevelBorderUIResource()
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (raisedBevelBorderUIResource == null)
+ raisedBevelBorderUIResource = new BevelBorderUIResource(
+ BevelBorder.RAISED);
+ return raisedBevelBorderUIResource;
+ }
+
+
+ /**
+ * Returns a shared instance of {@link LineBorderUIResource} for
+ * a black, one-pixel width border.
+ *
+ * @see javax.swing.border.LineBorder
+ */
+ public static Border getBlackLineBorderUIResource()
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (blackLineBorderUIResource == null)
+ blackLineBorderUIResource = new LineBorderUIResource(Color.black);
+ return blackLineBorderUIResource;
+ }
+
+
+ /**
+ * The wrapped border.
+ */
+ private Border delegate;
+
+
+ /**
+ * Constructs a <code>BorderUIResource</code> for wrapping
+ * a <code>Border</code> object.
+ *
+ * @param delegate the border to be wrapped.
+ */
+ public BorderUIResource(Border delegate)
+ {
+ if (delegate == null)
+ throw new IllegalArgumentException();
+
+ this.delegate = delegate;
+ }
+
+
+ /**
+ * Paints the border around an enclosed component by calling
+ * the <code>paintBorder</code> method of the wrapped delegate.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ delegate.paintBorder(c, g, x, y, width, height);
+ }
+
+
+ /**
+ * Measures the width of this border by calling the
+ * <code>getBorderInsets</code> method of the wrapped
+ * delegate.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return delegate.getBorderInsets(c);
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting by calling the <code>isBorderOpaque</code>
+ * method of the wrapped delegate.
+ *
+ * @return <code>true</code> if the border is fully opaque, or
+ * <code>false</code> if some pixels of the background
+ * can shine through the border.
+ */
+ public boolean isBorderOpaque()
+ {
+ return delegate.isBorderOpaque();
+ }
+
+
+ /**
+ * A {@link javax.swing.border.BevelBorder} that also implements the
+ * {@link UIResource} marker interface. This is useful for
+ * implementing pluggable look-and-feels: When switching the current
+ * LookAndFeel, only those borders are replaced that are marked as
+ * {@link UIResource}. For this reason, a look-and-feel should
+ * always install borders that implement <code>UIResource</code>,
+ * such as the borders provided by this class.
+ *
+ * @author Brian Jones (cbj@gnu.org)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class BevelBorderUIResource
+ extends BevelBorder
+ implements UIResource, Serializable
+ {
+ private static final long serialVersionUID = -1275542891108351642L;
+
+ /**
+ * Constructs a BevelBorderUIResource whose colors will be derived
+ * from the background of the enclosed component. The background
+ * color is retrieved each time the border is painted, so a border
+ * constructed by this method will automatically reflect a change
+ * to the component&#x2019;s background color.
+ *
+ * <p><img src="../border/doc-files/BevelBorder-1.png"
+ * width="500" height="150"
+ * alt="[An illustration showing raised and lowered BevelBorders]" /></p>
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link javax.swing.border.BevelBorder#RAISED}
+ * or {@link javax.swing.border.BevelBorder#LOWERED}.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has
+ * an unsupported value.
+ */
+ public BevelBorderUIResource(int bevelType)
+ {
+ super(bevelType);
+ }
+
+
+ /**
+ * Constructs a BevelBorderUIResource given its appearance type
+ * and two colors for its highlight and shadow.
+ *
+ * <p><img src="../border/doc-files/BevelBorder-2.png" width="500"
+ * height="150" alt="[An illustration showing BevelBorders that were
+ * constructed with this method]" /></p>
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link javax.swing.border.BevelBorder#RAISED}
+ * or {@link javax.swing.border.BevelBorder#LOWERED}.
+ *
+ * @param highlight the color that will be used for the inner side
+ * of the highlighted edges (top and left if if
+ * <code>bevelType</code> is {@link
+ * javax.swing.border.BevelBorder#RAISED}; bottom and right
+ * otherwise). The color for the outer side is a brightened
+ * version of this color.
+ *
+ * @param shadow the color that will be used for the outer side of
+ * the shadowed edges (bottom and right if
+ * <code>bevelType</code> is {@link
+ * javax.swing.border.BevelBorder#RAISED}; top and left
+ * otherwise). The color for the inner side is a brightened
+ * version of this color.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has
+ * an unsupported value.
+ *
+ * @throws NullPointerException if <code>highlight</code> or
+ * <code>shadow</code> is <code>null</code>.
+ */
+ public BevelBorderUIResource(int bevelType,
+ Color highlight,
+ Color shadow)
+ {
+ super(bevelType, highlight, shadow);
+ }
+
+
+ /**
+ * Constructs a BevelBorderUIResource given its appearance type
+ * and all its colors.
+ *
+ * <p><img src="../border/doc-files/BevelBorder-3.png" width="500"
+ * height="150" alt="[An illustration showing BevelBorders that
+ * were constructed with this method]" /></p>
+ *
+ * @param bevelType the desired appearance of the border. The value
+ * must be either {@link javax.swing.border.BevelBorder#RAISED}
+ * or {@link javax.swing.border.BevelBorder#LOWERED}.
+ *
+ * @param highlightOuter the color that will be used for the outer
+ * side of the highlighted edges (top and left if
+ * <code>bevelType</code> is {@link
+ * javax.swing.border.BevelBorder#RAISED}; bottom and right
+ * otherwise).
+ *
+ * @param highlightInner the color that will be used for the inner
+ * side of the highlighted edges.
+ *
+ * @param shadowOuter the color that will be used for the outer
+ * side of the shadowed edges (bottom and right if
+ * <code>bevelType</code> is {@link
+ * javax.swing.border.BevelBorder#RAISED}; top and left
+ * otherwise).
+ *
+ * @param shadowInner the color that will be used for the inner
+ * side of the shadowed edges.
+ *
+ * @throws IllegalArgumentException if <code>bevelType</code> has
+ * an unsupported value.
+ *
+ * @throws NullPointerException if one of the passed colors
+ * is <code>null</code>.
+ */
+ public BevelBorderUIResource(int bevelType,
+ Color highlightOuter,
+ Color highlightInner,
+ Color shadowOuter,
+ Color shadowInner)
+ {
+ super(bevelType,
+ highlightOuter, highlightInner,
+ shadowOuter, shadowInner);
+ }
+ }
+
+
+ /**
+ * A {@link javax.swing.border.CompoundBorder} that also implements the
+ * {@link UIResource} marker interface. This is useful for
+ * implementing pluggable look-and-feels: When switching the current
+ * LookAndFeel, only those borders are replaced that are marked as
+ * {@link UIResource}. For this reason, a look-and-feel should
+ * always install borders that implement <code>UIResource</code>,
+ * such as the borders provided by this class.
+ *
+ * @author Brian Jones (cbj@gnu.org)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class CompoundBorderUIResource
+ extends CompoundBorder
+ implements UIResource, Serializable
+ {
+ private static final long serialVersionUID = 7550017084975167341L;
+
+ /**
+ * Constructs a CompoundBorderUIResource with the specified inside
+ * and outside borders.
+ *
+ * @param outsideBorder the outside border, which is painted to the
+ * outside of both <code>insideBorder</code> and the enclosed
+ * component. It is acceptable to pass <code>null</code>, in
+ * which case no outside border is painted.
+ *
+ * @param insideBorder the inside border, which is painted to
+ * between <code>outsideBorder</code> and the enclosed
+ * component. It is acceptable to pass <code>null</code>, in
+ * which case no inside border is painted.
+ */
+ public CompoundBorderUIResource(Border outsideBorder,
+ Border insideBorder)
+ {
+ super(outsideBorder, insideBorder);
+ }
+ }
+
+
+ /**
+ * An {@link javax.swing.border.EmptyBorder} that also implements the
+ * {@link UIResource} marker interface. This is useful for
+ * implementing pluggable look-and-feels: When switching the current
+ * LookAndFeel, only those borders are replaced that are marked as
+ * {@link UIResource}. For this reason, a look-and-feel should
+ * always install borders that implement <code>UIResource</code>,
+ * such as the borders provided by this class.
+ *
+ * <p><img src="../border/doc-files/EmptyBorder-1.png"
+ * width="290" height="200"
+ * alt="[An illustration of EmptyBorder]" /></p>
+ *
+ * @author Brian Jones (cbj@gnu.org)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class EmptyBorderUIResource
+ extends EmptyBorder
+ implements UIResource, Serializable
+ {
+ private static final long serialVersionUID = -4914187529340071708L;
+
+ /**
+ * Constructs an empty border given the number of pixels required
+ * on each side.
+ *
+ * @param top the number of pixels that the border will need
+ * for its top edge.
+ *
+ * @param left the number of pixels that the border will need
+ * for its left edge.
+ *
+ * @param bottom the number of pixels that the border will need
+ * for its bottom edge.
+ *
+ * @param right the number of pixels that the border will need
+ * for its right edge.
+ */
+ public EmptyBorderUIResource(int top, int left, int bottom, int right)
+ {
+ super(top, left, bottom, right);
+ }
+
+
+ /**
+ * Constructs an empty border given the number of pixels required
+ * on each side, passed in an Insets object.
+ *
+ * @param insets the Insets for the new border.
+ */
+ public EmptyBorderUIResource(Insets insets)
+ {
+ super(insets);
+ }
+ }
+
+
+ /**
+ * An {@link javax.swing.border.EtchedBorder} that also implements the
+ * {@link UIResource} marker interface. This is useful for
+ * implementing pluggable look-and-feels: When switching the current
+ * LookAndFeel, only those borders are replaced that are marked as
+ * {@link UIResource}. For this reason, a look-and-feel should
+ * always install borders that implement <code>UIResource</code>,
+ * such as the borders provided by this class.
+ *
+ * <p><img src="../border/doc-files/EtchedBorder-1.png" width="500"
+ * height="200" alt="[An illustration of the two EtchedBorder
+ * variants]" /></p>
+ *
+ * @author Brian Jones (cbj@gnu.org)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class EtchedBorderUIResource
+ extends EtchedBorder
+ implements UIResource, Serializable
+ {
+ private static final long serialVersionUID = -8186391754165296656L;
+
+ /**
+ * Constructs an EtchedBorderUIResource that appears lowered into
+ * the surface. The colors will be derived from the background
+ * color of the enclosed Component when the border gets painted.
+ */
+ public EtchedBorderUIResource()
+ {
+ super();
+ }
+
+
+ /**
+ * Constructs an EtchedBorderUIResource with the specified
+ * appearance. The colors will be derived from the background
+ * color of the enclosed Component when the border gets painted.
+ *
+ * <p><img src="../border/doc-files/EtchedBorder-1.png"
+ * width="500" height="200" alt="[An illustration of the two
+ * EtchedBorder variants]" /></p>
+ *
+ * @param etchType the desired appearance of the border. The value
+ * must be either {@link javax.swing.border.EtchedBorder#RAISED}
+ * or {@link javax.swing.border.EtchedBorder#LOWERED}.
+ *
+ * @throws IllegalArgumentException if <code>etchType</code> has
+ * an unsupported value.
+ */
+ public EtchedBorderUIResource(int etchType)
+ {
+ super(etchType);
+ }
+
+
+ /**
+ * Constructs a lowered EtchedBorderUIResource, explicitly
+ * selecting the colors that will be used for highlight and
+ * shadow.
+ *
+ * @param highlight the color that will be used for painting
+ * the highlight part of the border.
+ *
+ * @param shadow the color that will be used for painting
+ * the shadow part of the border.
+ *
+ * @see EtchedBorderUIResource#EtchedBorderUIResource(int, Color, Color)
+ */
+ public EtchedBorderUIResource(Color highlight, Color shadow)
+ {
+ super(highlight, shadow);
+ }
+
+
+ /**
+ * Constructs an EtchedBorderUIResource with the specified
+ * appearance, explicitly selecting the colors that will be used
+ * for highlight and shadow.
+ *
+ * <p><img src="../border/doc-files/EtchedBorder-2.png" width="500"
+ * height="200" alt="[An illustration that shows which pixels get
+ * painted in what color]" /></p>
+ *
+ * @param etchType the desired appearance of the border. The value
+ * must be either {@link javax.swing.border.EtchedBorder#RAISED}
+ * or {@link javax.swing.border.EtchedBorder#LOWERED}.
+ *
+ * @param highlight the color that will be used for painting
+ * the highlight part of the border.
+ *
+ * @param shadow the color that will be used for painting
+ * the shadow part of the border.
+ *
+ * @throws IllegalArgumentException if <code>etchType</code> has
+ * an unsupported value.
+ */
+ public EtchedBorderUIResource(int etchType,
+ Color highlight, Color shadow)
+ {
+ super(etchType, highlight, shadow);
+ }
+ }
+
+
+ /**
+ * A {@link javax.swing.border.LineBorder} that also implements the
+ * {@link UIResource} marker interface. This is useful for
+ * implementing pluggable look-and-feels: When switching the current
+ * LookAndFeel, only those borders are replaced that are marked as
+ * {@link UIResource}. For this reason, a look-and-feel should
+ * always install borders that implement <code>UIResource</code>,
+ * such as the borders provided by this class.
+ *
+ * <p><img src="../border/doc-files/LineBorder-1.png" width="500"
+ * height="200" alt="[An illustration of two LineBorders]" /></p>
+ *
+ * @author Brian Jones (cbj@gnu.org)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class LineBorderUIResource
+ extends LineBorder
+ implements UIResource, Serializable
+ {
+ private static final long serialVersionUID = -6171232338180172310L;
+
+ /**
+ * Constructs a LineBorderUIResource given its color. The border
+ * will be one pixel thick and have plain corners.
+ *
+ * @param color the color for drawing the border.
+ */
+ public LineBorderUIResource(Color color)
+ {
+ super(color);
+ }
+
+
+ /**
+ * Constructs a LineBorder given its color and thickness. The
+ * border will have plain corners.
+ *
+ * @param color the color for drawing the border.
+ * @param thickness the width of the line in pixels.
+ */
+ public LineBorderUIResource(Color color, int thickness)
+ {
+ super(color, thickness);
+ }
+
+
+ /* Note: Since JDK1.3, javax.swing.border.LineBorder also has a
+ * constructor which accepts a value for the roundedCorners
+ * property. However, as of JDK1.4.1, the LineBorderUIResource
+ * subclass does not have a corresponding constructor.
+ *
+ * A request for enhancing the Swing API has been filed with Sun:
+ * http://developer.java.sun.com/developer/bugParade/bugs/4879999.html
+ */
+ }
+
+
+ /**
+ * A {@link javax.swing.border.MatteBorder} that also implements the
+ * {@link UIResource} marker interface. This is useful for
+ * implementing pluggable look-and-feels: When switching the current
+ * LookAndFeel, only those borders are replaced that are marked as
+ * {@link UIResource}. For this reason, a look-and-feel should
+ * always install borders that implement <code>UIResource</code>,
+ * such as the borders provided by this class.
+ *
+ * <p><img src="../border/doc-files/MatteBorder-1.png" width="500"
+ * height="150" alt="[An illustration of two MatteBorders]" /></p>
+ *
+ * @author Brian Jones (cbj@gnu.org)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class MatteBorderUIResource
+ extends MatteBorder
+ implements UIResource, Serializable
+ {
+ private static final long serialVersionUID = -8107923147541851122L;
+
+ /**
+ * Constructs a MatteBorderUIResource given the width on each side
+ * and a fill color.
+ *
+ * <p><img src="../border/doc-files/MatteBorder-2.png" width="500"
+ * height="150" alt="[A picture of a MatteBorder made by this
+ * constructor]" /></p>
+ *
+ * @param top the width of the border at its top edge.
+ * @param left the width of the border at its left edge.
+ * @param bottom the width of the border at its bottom edge.
+ * @param right the width of the border at its right edge.
+ * @param color the color for filling the border.
+ */
+ public MatteBorderUIResource(int top, int left,
+ int bottom, int right,
+ Color color)
+ {
+ super(top, left, bottom, right, color);
+ }
+
+
+ /**
+ * Constructs a MatteBorderUIResource given the width on each side
+ * and an icon for tiling the border area.
+ *
+ * <p><img src="../border/doc-files/MatteBorder-4.png" width="500"
+ * height="150" alt="[A picture of a MatteBorder made by this
+ * constructor]" /></p>
+ *
+ * @param top the width of the border at its top edge.
+ * @param left the width of the border at its left edge.
+ * @param bottom the width of the border at its bottom edge.
+ * @param right the width of the border at its right edge.
+ * @param tileIcon an icon for tiling the border area.
+ */
+ public MatteBorderUIResource(int top, int left,
+ int bottom, int right,
+ Icon tileIcon)
+ {
+ super(top, left, bottom, right, tileIcon);
+ }
+
+
+ /**
+ * Constructs a MatteBorderUIResource given an icon for tiling the
+ * border area. The icon width is used for the border insets at
+ * the left and right edge, the icon height for the top and bottom
+ * edge.
+ *
+ * <p><img src="../border/doc-files/MatteBorder-6.png" width="500"
+ * height="150" alt="[A picture of a MatteBorder made by this
+ * constructor]" /></p>
+ *
+ * @param tileIcon an icon for tiling the border area.
+ */
+ public MatteBorderUIResource(Icon tileIcon)
+ {
+ super(tileIcon);
+ }
+ }
+
+
+ /**
+ * A {@link javax.swing.border.TitledBorder} that also implements the
+ * {@link UIResource} marker interface. This is useful for
+ * implementing pluggable look-and-feels: When switching the current
+ * LookAndFeel, only those borders are replaced that are marked as
+ * {@link UIResource}. For this reason, a look-and-feel should
+ * always install borders that implement <code>UIResource</code>,
+ * such as the borders provided by this class.
+ *
+ * @author Brian Jones (cbj@gnu.org)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class TitledBorderUIResource
+ extends TitledBorder
+ implements UIResource, Serializable
+ {
+ private static final long serialVersionUID = 7667113547406407427L;
+
+ /**
+ * Constructs a TitledBorderUIResource given the text of its title.
+ *
+ * @param title the title text, or <code>null</code> to use no
+ * title text.
+ */
+ public TitledBorderUIResource(String title)
+ {
+ super(title);
+ }
+
+
+ /**
+ * Constructs an initially untitled TitledBorderUIResource
+ * given another border.
+ *
+ * @param border the border underneath the title, or
+ * <code>null</code> to use a default from
+ * the current look and feel.
+ */
+ public TitledBorderUIResource(Border border)
+ {
+ super(border);
+ }
+
+
+ /**
+ * Constructs a TitledBorder given its border and title text.
+ *
+ * @param border the border underneath the title, or
+ * <code>null</code> to use a default from
+ * the current look and feel.
+ *
+ * @param title the title text, or <code>null</code>
+ * to use no title text.
+ */
+ public TitledBorderUIResource(Border border, String title)
+ {
+ super(border, title);
+ }
+
+
+ /**
+ * Constructs a TitledBorderUIResource given its border, title
+ * text, horizontal alignment, and vertical position.
+ *
+ * @param border the border underneath the title, or
+ * <code>null</code> to use a default
+ * from the current look and feel.
+ *
+ * @param title the title text, or <code>null</code>
+ * to use no title text.
+ *
+ * @param titleJustification the horizontal alignment of the title
+ * text in relation to the border. The value must be one of
+ * {@link javax.swing.border.TitledBorder#LEFT},
+ * {@link javax.swing.border.TitledBorder#CENTER},
+ * {@link javax.swing.border.TitledBorder#RIGHT},
+ * {@link javax.swing.border.TitledBorder#LEADING},
+ * {@link javax.swing.border.TitledBorder#TRAILING}, or
+ * {@link javax.swing.border.TitledBorder#DEFAULT_JUSTIFICATION}.
+ *
+ * @param titlePosition the vertical position of the title text
+ * in relation to the border. The value must be one of
+ * {@link javax.swing.border.TitledBorder#ABOVE_TOP},
+ * {@link javax.swing.border.TitledBorder#TOP},
+ * {@link javax.swing.border.TitledBorder#BELOW_TOP},
+ * {@link javax.swing.border.TitledBorder#ABOVE_BOTTOM},
+ * {@link javax.swing.border.TitledBorder#BOTTOM},
+ * {@link javax.swing.border.TitledBorder#BELOW_BOTTOM},
+ * or {@link javax.swing.border.TitledBorder#DEFAULT_POSITION}.
+ *
+ * @throws IllegalArgumentException if <code>titleJustification</code>
+ * or <code>titlePosition</code> have an unsupported value.
+ */
+ public TitledBorderUIResource(Border border, String title,
+ int titleJustification,
+ int titlePosition)
+ {
+ super(border, title, titleJustification, titlePosition);
+ }
+
+
+ /**
+ * Constructs a TitledBorder given its border, title text,
+ * horizontal alignment, vertical position, and font.
+ *
+ * @param border the border underneath the title, or
+ * <code>null</code> to use a default
+ * from the current look and feel.
+ *
+ * @param title the title text, or <code>null</code>
+ * to use no title text.
+ *
+ * @param titleJustification the horizontal alignment of the title
+ * text in relation to the border. The value must be one of
+ * {@link javax.swing.border.TitledBorder#LEFT},
+ * {@link javax.swing.border.TitledBorder#CENTER},
+ * {@link javax.swing.border.TitledBorder#RIGHT},
+ * {@link javax.swing.border.TitledBorder#LEADING},
+ * {@link javax.swing.border.TitledBorder#TRAILING}, or
+ * {@link javax.swing.border.TitledBorder#DEFAULT_JUSTIFICATION}.
+ *
+ * @param titlePosition the vertical position of the title text
+ * in relation to the border. The value must be one of
+ * {@link javax.swing.border.TitledBorder#ABOVE_TOP},
+ * {@link javax.swing.border.TitledBorder#TOP},
+ * {@link javax.swing.border.TitledBorder#BELOW_TOP},
+ * {@link javax.swing.border.TitledBorder#ABOVE_BOTTOM},
+ * {@link javax.swing.border.TitledBorder#BOTTOM},
+ * {@link javax.swing.border.TitledBorder#BELOW_BOTTOM},
+ * or {@link javax.swing.border.TitledBorder#DEFAULT_POSITION}.
+ *
+ * @param titleFont the font for the title text, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @throws IllegalArgumentException if <code>titleJustification</code>
+ * or <code>titlePosition</code> have an unsupported value.
+ */
+ public TitledBorderUIResource(Border border, String title,
+ int titleJustification,
+ int titlePosition,
+ Font titleFont)
+ {
+ super(border, title, titleJustification, titlePosition,
+ titleFont);
+ }
+
+
+ /**
+ * Constructs a TitledBorder given its border, title text,
+ * horizontal alignment, vertical position, font, and color.
+ *
+ * @param border the border underneath the title, or
+ * <code>null</code> to use a default
+ * from the current look and feel.
+ *
+ * @param title the title text, or <code>null</code>
+ * to use no title text.
+ *
+ * @param titleJustification the horizontal alignment of the title
+ * text in relation to the border. The value must be one of
+ * {@link javax.swing.border.TitledBorder#LEFT},
+ * {@link javax.swing.border.TitledBorder#CENTER},
+ * {@link javax.swing.border.TitledBorder#RIGHT},
+ * {@link javax.swing.border.TitledBorder#LEADING},
+ * {@link javax.swing.border.TitledBorder#TRAILING}, or
+ * {@link javax.swing.border.TitledBorder#DEFAULT_JUSTIFICATION}.
+ *
+ * @param titlePosition the vertical position of the title text
+ * in relation to the border. The value must be one of
+ * {@link javax.swing.border.TitledBorder#ABOVE_TOP},
+ * {@link javax.swing.border.TitledBorder#TOP},
+ * {@link javax.swing.border.TitledBorder#BELOW_TOP},
+ * {@link javax.swing.border.TitledBorder#ABOVE_BOTTOM},
+ * {@link javax.swing.border.TitledBorder#BOTTOM},
+ * {@link javax.swing.border.TitledBorder#BELOW_BOTTOM},
+ * or {@link javax.swing.border.TitledBorder#DEFAULT_POSITION}.
+ *
+ * @param titleFont the font for the title text, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @param titleColor the color for the title text, or <code>null</code>
+ * to use a default from the current look and feel.
+ *
+ * @throws IllegalArgumentException if <code>titleJustification</code>
+ * or <code>titlePosition</code> have an unsupported value.
+ */
+ public TitledBorderUIResource(Border border, String title,
+ int titleJustification, int titlePosition,
+ Font titleFont, Color titleColor)
+ {
+ super(border, title, titleJustification, titlePosition,
+ titleFont, titleColor);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ButtonUI.java b/libjava/classpath/javax/swing/plaf/ButtonUI.java
new file mode 100644
index 000000000..6910e4298
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ButtonUI.java
@@ -0,0 +1,52 @@
+/* ButtonUI.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JButton</code>.
+ *
+ * @see javax.swing.JButton
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ButtonUI extends ComponentUI
+{
+ // This abstract class does not define any methods of its own.
+}
diff --git a/libjava/classpath/javax/swing/plaf/ColorChooserUI.java b/libjava/classpath/javax/swing/plaf/ColorChooserUI.java
new file mode 100644
index 000000000..e9cb32caa
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ColorChooserUI.java
@@ -0,0 +1,58 @@
+/* ColorChooserUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JColorChooser</code>.
+ *
+ * @see javax.swing.JColorChooser
+ *
+ * @author Andrew Selkirk
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ColorChooserUI extends ComponentUI
+{
+ /**
+ * Constructs a ColorChooserUI.
+ */
+ public ColorChooserUI()
+ {
+ /* The constructor does not do anything. */
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ColorUIResource.java b/libjava/classpath/javax/swing/plaf/ColorUIResource.java
new file mode 100644
index 000000000..3c9db2ec8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ColorUIResource.java
@@ -0,0 +1,121 @@
+/* ColorUIResource.java
+ Copyright (C) 2002, 2003, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Color;
+
+
+/**
+ * A Color that is marked as <code>UIResource</code>, which indicates that
+ * the color has been installed by a pluggable LookAndFeel. Such colors
+ * are replaced when the LookAndFeel changes.
+ *
+ * @see java.awt.Color
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class ColorUIResource extends Color implements UIResource
+{
+ /**
+ * Constructs a <code>ColorUIResource</code> using the specified
+ * red, green, and blue values, which must be given as integers in
+ * the range of 0-255. The alpha channel value will default to 255,
+ * meaning that the color is fully opaque.
+ *
+ * @param r the red intensity, which must be in the range [0 .. 255].
+ * @param g the green intensity, which must be in the range [0 .. 255].
+ * @param b the blue intensity, which must be in the range [0 .. 255].
+ *
+ * @throws IllegalArgumentException if any of the values is outside the
+ * specified range.
+ */
+ public ColorUIResource(int r, int g, int b)
+ {
+ super(r, g, b);
+ }
+
+
+ /**
+ * Constructs a <code>ColorUIResource</code> using the specified
+ * RGB value. The blue value is in bits 0-7, green in bits 8-15, and
+ * red in bits 16-23. The other bits are ignored. The alpha value is set
+ * to 255, meaning that the color is fully opaque.
+ *
+ * @param rgb the rgb value, as discussed above.
+ */
+ public ColorUIResource(int rgb)
+ {
+ super(rgb);
+ }
+
+
+ /**
+ * Constructs a <code>ColorUIResource</code> using the specified
+ * red, green, and blue intensities, which must be given as floats in
+ * the range of 0-1. The alpha channel value will default to 1.0f,
+ * meaning that the color is fully opaque.
+ *
+ * @param r the red intensity, which must be in the range [0.0 .. 1.0].
+ * @param g the green intensity, which must be in the range [0.0 .. 1.0].
+ * @param b the blue intensity, which must be in the range [0.0 .. 1.0].
+ *
+ * @throws IllegalArgumentException if any of the values is outside the
+ * specified range.
+ */
+ public ColorUIResource(float r, float g, float b)
+ {
+ super(r, g, b);
+ }
+
+
+ /**
+ * Constructs a <code>ColorUIResource</code>, using the intensities
+ * of another color.
+ *
+ * @param c the color whose intensities will be considered when
+ * constructing this <code>ColorUIResource</code> (<code>null</code>
+ * not permitted).
+ *
+ * @throws NullPointerException if <code>c</code> is <code>null</code>.
+ */
+ public ColorUIResource(Color c)
+ {
+ super(c.getRGB());
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ComboBoxUI.java b/libjava/classpath/javax/swing/plaf/ComboBoxUI.java
new file mode 100644
index 000000000..0a06d263e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ComboBoxUI.java
@@ -0,0 +1,92 @@
+/* ComboBoxUI.java --
+ Copyright (C) 2002, 2003, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import javax.swing.JComboBox;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a {@link JComboBox}.
+ *
+ * @author Andrew Selkirk
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ComboBoxUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>ComboBoxUI</code>.
+ */
+ public ComboBoxUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the visibility of the popup button.
+ *
+ * @param c the <code>JComboBox</code> whose popup
+ * is shown or hidden.
+ *
+ * @param visible <code>true</code> to show the popup, <code>false</code>
+ * to hide it.
+ */
+ public abstract void setPopupVisible(JComboBox c, boolean visible);
+
+ /**
+ * Determines whether the popup button is currently visible.
+ *
+ * @param c the <code>JComboBox</code> whose popup visibility
+ * is retrieved.
+ *
+ * @return <code>true</code> if the popup button is currently
+ * visible, <code>false</code> otherwise.
+ */
+ public abstract boolean isPopupVisible(JComboBox c);
+
+ /**
+ * Determines whether the combo box can receive input focus.
+ *
+ * @param c <code>JComboBox</code> whose focus traversability
+ * is to be retrieved.
+ *
+ * @return <code>true</code> if <code>c</code> can receive
+ * input focus, <code>false</code> otherwise.
+ */
+ public abstract boolean isFocusTraversable(JComboBox c);
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java b/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java
new file mode 100644
index 000000000..4553562e9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java
@@ -0,0 +1,68 @@
+/* ComponentInputMapUIResource.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import javax.swing.ComponentInputMap;
+import javax.swing.JComponent;
+
+
+/**
+ * A <code>ComponentInputMap</code> that implements the {@link UIResource}
+ * interface to indicate that it belongs to a pluggable
+ * LookAndFeel.
+ *
+ * @see javax.swing.ComponentInputMap
+ * @see javax.swing.InputMap
+ *
+ * @author Andrew Selkirk
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class ComponentInputMapUIResource extends ComponentInputMap
+ implements UIResource
+{
+ /**
+ * Constructs a new <code>ComponentInputMapUIResource</code>.
+ *
+ * @param component the <code>JComponent</code> associated with
+ * this <code>InputMap</code>.
+ */
+ public ComponentInputMapUIResource(JComponent component)
+ {
+ super(component);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ComponentUI.java b/libjava/classpath/javax/swing/plaf/ComponentUI.java
new file mode 100644
index 000000000..35f2de3ed
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ComponentUI.java
@@ -0,0 +1,331 @@
+/* ComponentUI.java --
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+
+/**
+ * The abstract base class for all delegates that provide the
+ * pluggable look and feel for Swing components. User applications
+ * should not need to access this class; it is internal to Swing
+ * and the look-and-feel implementations.
+ *
+ * <p><img src="doc-files/ComponentUI-1.png" width="700" height="550"
+ * alt="[UML diagram illustrating the architecture for pluggable
+ * look and feels]" /></p>
+ *
+ * <p>Components such as {@link javax.swing.JSlider} do not directly
+ * implement operations related to the look and feel of the user
+ * interface, such as painting or layout. Instead, they use a delegate
+ * object for all such tasks. In the case of <code>JSlider</code>, the
+ * user interface would be provided by some concrete subclass of
+ * {@link javax.swing.plaf.SliderUI}.
+ *
+ * <p>Soon after its creation, a <code>ComponentUI</code> will be sent
+ * an {@link #installUI} message. The <code>ComponentUI</code> will
+ * react by setting properties such as the border or the background
+ * color of the <code>JComponent</code> for which it provides its
+ * services. Soon before the end of its lifecycle, the
+ * <code>ComponentUI</code> will receive an {@link #uninstallUI}
+ * message, at which time the <code>ComponentUI</code> is expected to
+ * undo any changes.</p>
+ *
+ * <p>Note that the <code>ui</code> of a <code>JComponent</code>
+ * changes whenever the user switches between look and feels. For
+ * example, the <code>ui</code> property of a <code>JSlider</code>
+ * could change from an instance of <code>MetalSliderUI</code> to an
+ * instance of <code>FooSliderUI</code>. This switch can happen at any
+ * time, but it will always be performed from inside the Swing thread.</p>
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ComponentUI
+{
+ /**
+ * Constructs a new UI delegate.
+ */
+ public ComponentUI()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Sets up the specified component so it conforms the the design
+ * guidelines of the implemented look and feel. When the look and
+ * feel changes, a <code>ComponentUI</code> delegate is created.
+ * The delegate object then receives an <code>installUI</code>
+ * message.
+ *
+ * <p>This method should perform the following tasks:</p>
+ *
+ * <ul>
+ * <li>Set visual properties such as borders, fonts, colors, or
+ * icons. However, no change should be performed for those
+ * properties whose values have been directly set by the client
+ * application. To allow the distinction, LookAndFeels are expected
+ * to use values that implement the {@link UIResource} marker
+ * interface, such as {@link BorderUIResource} or {@link
+ * ColorUIResource}.</li>
+ * <li>If necessary, install a {@link java.awt.LayoutManager}.</li>
+ * <li>Embed custom sub-components. For instance, the UI delegate
+ * for a {@link javax.swing.JSplitPane} might install a special
+ * component for the divider.</li>
+ * <li>Register event listeners.</li>
+ * <li>Set up properties related to keyborad navigation, such as
+ * mnemonics or focus traversal policies.</li>
+ * </ul>
+ *
+ * @param c the component for which this delegate will provide
+ * services.
+ *
+ * @see #uninstallUI
+ * @see javax.swing.JComponent#setUI
+ * @see javax.swing.JComponent#updateUI
+ */
+ public void installUI(JComponent c)
+ {
+ // The default implementation does not change any properties.
+ }
+
+
+ /**
+ * Puts the specified component into the state it had before
+ * {@link #installUI} was called.
+ *
+ * @param c the component for which this delegate has provided
+ * services.
+ *
+ * @see #installUI
+ * @see javax.swing.JComponent#setUI
+ * @see javax.swing.JComponent#updateUI
+ */
+ public void uninstallUI(JComponent c)
+ {
+ // The default implementation does not change any properties.
+ }
+
+
+ /**
+ * Paints the component according to the design guidelines
+ * of the look and feel. Most subclasses will want to override
+ * this method.
+ *
+ * @param g the graphics for painting.
+ *
+ * @param c the component for which this delegate performs
+ * services.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ // Nothing is done here. This method is meant to be overridden by
+ // subclasses.
+ }
+
+
+ /**
+ * Fills the specified component with its background color
+ * (unless the <code>opaque</code> property is <code>false</code>)
+ * before calling {@link #paint}.
+ *
+ * <p>It is unlikely that a subclass needs to override this method.
+ * The actual rendering should be performed by the {@link #paint}
+ * method.
+ *
+ * @param g the graphics for painting.
+ *
+ * @param c the component for which this delegate performs
+ * services.
+ *
+ * @see #paint
+ * @see javax.swing.JComponent#paintComponent
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ if (c.isOpaque())
+ {
+ Color oldColor = g.getColor();
+ g.setColor(c.getBackground());
+ g.fillRect(0, 0, c.getWidth(), c.getHeight());
+ g.setColor(oldColor);
+ }
+ paint(g, c);
+ }
+
+ /**
+ * Determines the preferred size of a component. The default
+ * implementation returns <code>null</code>, which means that
+ * <code>c</code>&#x2019;s layout manager should be asked to
+ * calculate the preferred size.
+ *
+ * @param c the component for which this delegate performs services.
+ *
+ * @return the preferred size, or <code>null</code> to indicate that
+ * <code>c</code>&#x2019;s layout manager should be asked
+ * for the preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return null;
+ }
+
+
+ /**
+ * Determines the minimum size of a component. The default
+ * implementation calls {@link #getPreferredSize}, but subclasses
+ * might want to override this.
+ *
+ * @param c the component for which this delegate performs services.
+ *
+ * @return the minimum size, or <code>null</code> to indicate that
+ * <code>c</code>&#x2019;s layout manager should be asked
+ * to calculate the minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+
+ /**
+ * Determines the maximum size of a component. The default
+ * implementation calls {@link #getPreferredSize}, but subclasses
+ * might want to override this.
+ *
+ * @param c the component for which this delegate performs services.
+ *
+ * @return the maximum size, or <code>null</code> to indicate that
+ * <code>c</code>&#x2019;s layout manager should be asked
+ * to calculate the maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+
+ /**
+ * Determines whether a click into the component at a specified
+ * location is considered as having hit the component. The default
+ * implementation checks whether the point falls into the
+ * component&#x2019;s bounding rectangle. Some subclasses might want
+ * to override this, for example in the case of a rounded button.
+ *
+ * @param c the component for which this delegate performs services.
+ *
+ * @param x the x coordinate of the point, relative to the local
+ * coordinate system of the component. Zero would be be
+ * component&#x2019;s left edge, irrespective of the location
+ * inside its parent.
+ *
+ * @param y the y coordinate of the point, relative to the local
+ * coordinate system of the component. Zero would be be
+ * component&#x2019;s top edge, irrespective of the location
+ * inside its parent.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ /* JComponent.contains calls the ui delegate for hit
+ * testing. Therefore, endless mutual recursion would result if we
+ * called c.contains(x, y) here.
+ *
+ * The previous Classpath implementation called the deprecated
+ * method java.awt.Component.inside. In the Sun implementation, it
+ * can be observed that inside, other than contains, does not call
+ * the ui delegate. But that inside() behaves different to
+ * contains() clearly is in violation of the method contract, and
+ * it is not something that a good implementation should rely upon
+ * -- even if Classpath ends up being forced to replicate this
+ * apparent bug of the Sun implementation.
+ */
+ return (x >= 0) && (x < c.getWidth())
+ && (y >= 0) && (y < c.getHeight());
+ }
+
+
+ /**
+ * Creates a delegate object for the specified component. Users
+ * should use the <code>createUI</code> method of a suitable
+ * subclass. The implementation of <code>ComponentUI</code>
+ * always throws an error.
+ *
+ * @param c the component for which a UI delegate is requested.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ throw new Error(
+ "javax.swing.plaf.ComponentUI does not implement createUI; call "
+ + "createUI on a subclass.");
+ }
+
+
+ /**
+ * Counts the number of accessible children in the component. The
+ * default implementation delegates the inquiry to the {@link
+ * javax.accessibility.AccessibleContext} of <code>c</code>.
+ *
+ * @param c the component whose accessible children
+ * are to be counted.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ return c.getAccessibleContext().getAccessibleChildrenCount();
+ }
+
+
+ /**
+ * Returns the specified accessible child of the component. The
+ * default implementation delegates the inquiry to the {@link
+ * javax.accessibility.AccessibleContext} of <code>c</code>.
+ *
+ * @param i the index of the accessible child, starting at zero.
+ *
+ * @param c the component whose <code>i</code>-th accessible child
+ * is requested.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ return c.getAccessibleContext().getAccessibleChild(i);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/DesktopIconUI.java b/libjava/classpath/javax/swing/plaf/DesktopIconUI.java
new file mode 100644
index 000000000..676233ec2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/DesktopIconUI.java
@@ -0,0 +1,56 @@
+/* DesktopIconUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a desktop icon.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class DesktopIconUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>DesktopIconUI</code>.
+ */
+ public DesktopIconUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java b/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java
new file mode 100644
index 000000000..3d4cfc830
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java
@@ -0,0 +1,58 @@
+/* DesktopPaneUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JDesktopPane</code>.
+ *
+ * @see javax.swing.JDesktopPane
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class DesktopPaneUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>DesktopPaneUI</code>.
+ */
+ public DesktopPaneUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/DimensionUIResource.java b/libjava/classpath/javax/swing/plaf/DimensionUIResource.java
new file mode 100644
index 000000000..618c22036
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/DimensionUIResource.java
@@ -0,0 +1,66 @@
+/* DimensionUIResource.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Dimension;
+
+/**
+ * A Dimension that is marked as <code>UIResource</code>, which
+ * indicates that it has been installed by a pluggable
+ * LookAndFeel. Such dimensions are replaced when the LookAndFeel
+ * changes.
+ *
+ * @see java.awt.Dimension
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class DimensionUIResource extends Dimension implements UIResource
+{
+ /**
+ * Constructs a new DimensionUIResource, given its width and height.
+ *
+ * @param width the width in pixels.
+ * @param height the height in pixels.
+ */
+ public DimensionUIResource(int width, int height)
+ {
+ super(width, height);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/FileChooserUI.java b/libjava/classpath/javax/swing/plaf/FileChooserUI.java
new file mode 100644
index 000000000..fafd21d2a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/FileChooserUI.java
@@ -0,0 +1,138 @@
+/* FileChooserUI.java --
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.io.File;
+
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileView;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JFileChooser</code>.
+ *
+ * @see javax.swing.JFileChooser
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class FileChooserUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>FileChooserUI</code>.
+ */
+ public FileChooserUI()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Returns a <code>FileFilter</code> that accepts every file. While
+ * the filtering itself is not specific to any look and feel, the
+ * text returned by <code>FileFilter.getDescription()</code> need
+ * not be the same across all look and feels.
+ *
+ * @param chooser the <code>JFileChooser</code> for which
+ * a <code>FileFilter</code> is requested.
+ *
+ * @see javax.swing.JFileChooser#getAcceptAllFileFilter
+ * @see javax.swing.filechooser.FileFilter#getDescription
+ */
+ public abstract FileFilter getAcceptAllFileFilter(JFileChooser chooser);
+
+
+ /**
+ * Returns a view to a file, which is able to retrieve its name,
+ * icon, and other properties that are relevant for presenting
+ * the file to the user.
+ *
+ * @param chooser the <code>JFileChooser</code> for which
+ * a <code>FileFilter</code> is requested.
+ */
+ public abstract FileView getFileView(JFileChooser chooser);
+
+
+ /**
+ * Determines which text is appropriate for the approve button
+ * according to the design guidelines of the implemented
+ * look and feel.
+ *
+ * @param chooser the <code>JFileChooser</code> whose
+ * button text is requested.
+ *
+ * @see javax.swing.JFileChooser#getApproveButtonText
+ */
+ public abstract String getApproveButtonText(JFileChooser chooser);
+
+
+ /**
+ * Determines which text is appropriate for the title bar of a
+ * <code>JFileChooser</code> according to the design guidelines of
+ * the implemented look and feel.
+ *
+ * @param chooser the <code>JFileChooser</code> whose
+ * dialog title is requested.
+ *
+ * @see javax.swing.JFileChooser#getDialogTitle
+ */
+ public abstract String getDialogTitle(JFileChooser chooser);
+
+
+ /**
+ * Refreshes the currently displayed directory.
+ *
+ * @param chooser the <code>JFileChooser</code> whose
+ * dialog title needs re-scanning.
+ */
+ public abstract void rescanCurrentDirectory(JFileChooser chooser);
+
+
+ /**
+ * Ensures that a specified file is visible in the
+ * <code>JFileChooser</code>
+ *
+ * @param chooser the <code>JFileChooser</code> that
+ * should display the file <code>file</code>.
+ *
+ * @param file the file that needs to be made visible.
+ */
+ public abstract void ensureFileIsVisible(JFileChooser chooser, File file);
+}
diff --git a/libjava/classpath/javax/swing/plaf/FontUIResource.java b/libjava/classpath/javax/swing/plaf/FontUIResource.java
new file mode 100644
index 000000000..c54f987fd
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/FontUIResource.java
@@ -0,0 +1,99 @@
+/* FontUIResource.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Font;
+
+
+/**
+ * A font that is marked as <code>UIResource</code>, which
+ * indicates that it has been installed by a pluggable
+ * LookAndFeel. Such dimensions are replaced when the LookAndFeel
+ * changes.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class FontUIResource extends Font implements UIResource
+{
+ /**
+ * Constructs a new <code>FontUIResource</code> given
+ * the name, style and size of the font.
+ *
+ * @param name the name of the font. A number of
+ * &#x201c;logical&#x201d; names are supported by any Java
+ * implementation. These are
+ * <code>&#x201c;Dialog&#x201d;</code>,
+ * <code>&#x201c;DialogInput&#x201d;</code>,
+ * <code>&#x201c;Monospaced&#x201d;</code>,
+ * <code>&#x201c;Serif&#x201d;</code>, and
+ * <code>&#x201c;SansSerif&#x201d;</code>.
+ *
+ * @param style the style of the font, for instance {@link
+ * java.awt.Font#BOLD} or {@link java.awt.Font#PLAIN}.
+ *
+ * @param size the size of the font in typographic points, for
+ * instance 10, 12 or 13. Designers of LookAndFeels should be
+ * aware that some languages (like Japanese and Chinese) have
+ * glyphs that are too complex to be legible at small point
+ * sizes.
+ */
+ public FontUIResource(String name, int style, int size)
+ {
+ super(name, style, size);
+ }
+
+
+ /**
+ * Constructs a new <code>FontUIResource</code> given
+ * an existing font.
+ *
+ * @param f the font that serves as a template.
+ */
+ public FontUIResource(Font f)
+ {
+ /* This implementation will get rid of many font properties,
+ * such as skewing, values of multiple master design axes,
+ * etc., unless they get encoded into the name. It probably
+ * is not a problem for LookAndFeels because user interfaces
+ * are usually not very advanced with respect to typography.
+ */
+ super(f.getName(), f.getStyle(), f.getSize());
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/IconUIResource.java b/libjava/classpath/javax/swing/plaf/IconUIResource.java
new file mode 100644
index 000000000..48503f8a0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/IconUIResource.java
@@ -0,0 +1,124 @@
+/* IconUIResource.java --
+ Copyright (C) 2002, 2003, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.io.Serializable;
+
+import javax.swing.Icon;
+
+/**
+ * An icon that is marked as <code>UIResource</code>, which
+ * indicates that it has been installed by a pluggable
+ * LookAndFeel. Such icons are replaced when the LookAndFeel
+ * changes.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class IconUIResource implements Icon, UIResource, Serializable
+{
+ /**
+ * Verified using the <code>serialver</code> tool of Sun JDK 1.4.1_01
+ * on GNU/Linux 2.4.18.
+ */
+ static final long serialVersionUID = 3327049506004830542L;
+
+
+ /**
+ * The icon that is wrapped by this <code>IconUIResource</code> (never
+ * <code>null</code>).
+ */
+ private Icon delegate;
+
+
+ /**
+ * Constructs a <code>IconUIResource</code> that wraps another
+ * icon. All messages are forwarded to the delegate icon.
+ *
+ * @param delegate the icon that is wrapped by this
+ * <code>IconUIResource</code> (<code>null</code> not permitted).
+ */
+ public IconUIResource(Icon delegate)
+ {
+ if (delegate == null)
+ throw new IllegalArgumentException("Null 'delegate' argument.");
+ this.delegate = delegate;
+ }
+
+
+ /**
+ * Paints the icon by asking the delegate icon to paint itself.
+ *
+ * @param c the Component whose icon is being painted. Some icons
+ * use this argument to retrieve properties like the
+ * background color.
+ *
+ * @param g the graphics into which the icon will be painted.
+ *
+ * @param x the horizontal position of the icon.
+ *
+ * @param y the vertical position of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ delegate.paintIcon(c, g, x, y);
+ }
+
+
+ /**
+ * Returns the width of the icon in pixels. The implementation
+ * determines and returns the width of the delegate icon.
+ */
+ public int getIconWidth()
+ {
+ return delegate.getIconWidth();
+ }
+
+
+ /**
+ * Returns the height of the icon in pixels. The implementation
+ * determines and returns the height of the delegate icon.
+ */
+ public int getIconHeight()
+ {
+ return delegate.getIconHeight();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/InputMapUIResource.java b/libjava/classpath/javax/swing/plaf/InputMapUIResource.java
new file mode 100644
index 000000000..0c5f6f9e9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/InputMapUIResource.java
@@ -0,0 +1,61 @@
+/* InputMapUIResource.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import javax.swing.InputMap;
+
+
+/**
+ * An <code>InputMap</code> that is marked as <code>UIResource</code>,
+ * which indicates that it has been installed by a pluggable
+ * LookAndFeel. Such dimensions are replaced when the LookAndFeel
+ * changes.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class InputMapUIResource extends InputMap implements UIResource
+{
+ /**
+ * Constructs a new <code>InputMapUIResource</code>.
+ */
+ public InputMapUIResource()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/InsetsUIResource.java b/libjava/classpath/javax/swing/plaf/InsetsUIResource.java
new file mode 100644
index 000000000..d64feb44c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/InsetsUIResource.java
@@ -0,0 +1,76 @@
+/* InsetsUIResource.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Insets;
+import java.io.Serializable;
+
+
+/**
+ * An <code>Insets</code> that is marked as <code>UIResource</code>,
+ * which indicates that it has been installed by a pluggable
+ * LookAndFeel. Such insets are replaced when the LookAndFeel changes.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class InsetsUIResource extends Insets
+ implements Cloneable, UIResource, Serializable
+{
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 5622110143266315421L;
+
+
+ /**
+ * Constructs a new <code>InsetsUIResource</code> given the
+ * inset at each edge.
+ *
+ * @param top the inset at the top, in pixels.
+ * @param left the inset at the left, in pixels.
+ * @param bottom the inset at the bottom, in pixels.
+ * @param right the inset at the right, in pixels.
+ */
+ public InsetsUIResource(int top, int left, int bottom, int right)
+ {
+ super(top, left, bottom, right);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/InternalFrameUI.java b/libjava/classpath/javax/swing/plaf/InternalFrameUI.java
new file mode 100644
index 000000000..0b2f77caa
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/InternalFrameUI.java
@@ -0,0 +1,59 @@
+/* InternalFrameUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JInternalFrame</code>.
+ *
+ * @see javax.swing.JInternalFrame
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class InternalFrameUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>InternalFrameUI</code>.
+ */
+ public InternalFrameUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/LabelUI.java b/libjava/classpath/javax/swing/plaf/LabelUI.java
new file mode 100644
index 000000000..f4b74d59e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/LabelUI.java
@@ -0,0 +1,59 @@
+/* LabelUI.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JLabel</code>.
+ *
+ * @see javax.swing.JLabel
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class LabelUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>LabelUI</code>.
+ */
+ public LabelUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ListUI.java b/libjava/classpath/javax/swing/plaf/ListUI.java
new file mode 100644
index 000000000..bdfe4b307
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ListUI.java
@@ -0,0 +1,114 @@
+/* ListUI.java --
+ Copyright (C) 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+
+import javax.swing.JList;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JList</code>.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ListUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>ListUI</code>.
+ */
+ public ListUI()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Determines the cell index which is the closest to the specified
+ * location. The find out whether the returned cell actually
+ * contains the location, the caller should also use {@link
+ * #getCellBounds}.
+ *
+ * @param list the <code>JList</code> for which this delegate object
+ * provides the pluggable user interface.
+ *
+ * @param location a point in the <code>JList</code> coordinate
+ * system.
+ *
+ * @return the index of the closest cell, or -1 if the list model
+ * is empty.
+ */
+ public abstract int locationToIndex(JList list, Point location);
+
+
+ /**
+ * Determines the location of the specified cell.
+ *
+ * @param list the <code>JList</code> for which this delegate object
+ * provides the pluggable user interface.
+ *
+ * @param index the zero-based index of the cell whose location shall be
+ * determined.
+ *
+ * @return the position of the top left corner of the cell in the
+ * <code>JList</code> coordinate system, or <code>null</code>
+ * if <code>cell</code> does not designate a valid cell.
+ */
+ public abstract Point indexToLocation(JList list, int index);
+
+
+ /**
+ * Determines the bounding box of the rectangle spanned by
+ * two list indices.
+ *
+ * @param list the <code>JList</code> for which this delegate object
+ * provides the pluggable user interface.
+ *
+ * @param index1 the zero-based index of the first cell.
+ *
+ * @param index2 the zero-based index of the second cell.
+ *
+ * @return the spanned rectangle, or <code>null</code> if either
+ * <code>index1</code> or <code>index2</code> does not
+ * designate a valid cell.
+ */
+ public abstract Rectangle getCellBounds(JList list,
+ int index1, int index2);
+}
diff --git a/libjava/classpath/javax/swing/plaf/MenuBarUI.java b/libjava/classpath/javax/swing/plaf/MenuBarUI.java
new file mode 100644
index 000000000..2c82adfa0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/MenuBarUI.java
@@ -0,0 +1,59 @@
+/* MenuBarUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JMenuBar</code>.
+ *
+ * @see javax.swing.JMenuBar
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class MenuBarUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>MenuBarUI</code>.
+ */
+ public MenuBarUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/MenuItemUI.java b/libjava/classpath/javax/swing/plaf/MenuItemUI.java
new file mode 100644
index 000000000..83ad52fb1
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/MenuItemUI.java
@@ -0,0 +1,59 @@
+/* MenuItemUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JMenuItem</code>.
+ *
+ * @see javax.swing.JMenuItem
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class MenuItemUI extends ButtonUI
+{
+ /**
+ * Constructs a new <code>MenuItemUI</code>.
+ */
+ public MenuItemUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/OptionPaneUI.java b/libjava/classpath/javax/swing/plaf/OptionPaneUI.java
new file mode 100644
index 000000000..13d1caa6a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/OptionPaneUI.java
@@ -0,0 +1,75 @@
+/* OptionPaneUI.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import javax.swing.JOptionPane;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JOptionPane</code>.
+ *
+ * @see javax.swing.JOptionPane
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class OptionPaneUI
+ extends ComponentUI
+{
+ /**
+ * Gives keyboard input focus to the component that represents
+ * the default value.
+ *
+ * @param pane the <code>JOptionPane</code> for which this delegate
+ * object provides the pluggable user interface.
+ */
+ public abstract void selectInitialValue(JOptionPane pane);
+
+
+ /**
+ * Determines whether the user has provided custom components
+ * for the options or the message.
+ *
+ * @param pane the <code>JOptionPane</code> for which this delegate
+ * object provides the pluggable user interface.
+ *
+ * @return <code>true</code> if the user has supplied any custom
+ * components; <code>false</code> if all components are
+ * provided by Swing or a LookAndFeel.
+ */
+ public abstract boolean containsCustomComponents(JOptionPane pane);
+}
diff --git a/libjava/classpath/javax/swing/plaf/PanelUI.java b/libjava/classpath/javax/swing/plaf/PanelUI.java
new file mode 100644
index 000000000..12a6f52cf
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/PanelUI.java
@@ -0,0 +1,58 @@
+/* PanelUI.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JPanel</code>.
+ *
+ * @see javax.swing.JPanel
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class PanelUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>PanelUI</code>.
+ */
+ public PanelUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/PopupMenuUI.java b/libjava/classpath/javax/swing/plaf/PopupMenuUI.java
new file mode 100644
index 000000000..de351f2ef
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/PopupMenuUI.java
@@ -0,0 +1,117 @@
+/* PopupMenuUI.java --
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.event.MouseEvent;
+
+import javax.swing.JPopupMenu;
+import javax.swing.Popup;
+import javax.swing.PopupFactory;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JPopupMenu</code>.
+ *
+ * @see javax.swing.JPopupMenu
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class PopupMenuUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>PopupMenuUI</code>.
+ */
+ public PopupMenuUI()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Tests whether or not a mouse event triggers a popup menu.
+ *
+ * <p>The default implementation calls
+ * <code>event.isPopupTrigger()</code>, which checks for the gesture
+ * that is common for the platform on which the application runs. If
+ * a look and feel wants to employ non-standard conventions for
+ * triggering a popup menu, it can override this method.
+ *
+ * @param event the event to check.
+ *
+ * @return <code>true</code> if the event triggers a popup menu;
+ * <code>false</code> otherwise.
+ *
+ * @since 1.3
+ */
+ public boolean isPopupTrigger(MouseEvent event)
+ {
+ return event.isPopupTrigger();
+ }
+
+
+ /**
+ * Creates a <code>Popup</code> for displaying the popup menu. The
+ * default implementation uses the {@link javax.swing.PopupFactory}
+ * for retrieving a suitable <code>Popup</code>, but subclasses
+ * might want to override this method if a LookAndFeel needs special
+ * Popups.
+ *
+ * @param popup the <code>JPopupMenu</code> for whose display
+ * a <code>Popup</code> is needed.
+ *
+ * @param x the horizontal position where the popup will be
+ * displayed.
+ *
+ * @param y the vertical position where the popup will be
+ * displayed.
+ *
+ * @return a <code>Popup</code> for showing and hiding
+ * the menu.
+ *
+ * @since 1.4
+ */
+ public Popup getPopup(JPopupMenu popup, int x, int y)
+ {
+ return PopupFactory.getSharedInstance().getPopup(
+ /* origin/owner of the popup */ popup.getInvoker(),
+ /* contents */ popup,
+ x, y);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ProgressBarUI.java b/libjava/classpath/javax/swing/plaf/ProgressBarUI.java
new file mode 100644
index 000000000..013b8c5c2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ProgressBarUI.java
@@ -0,0 +1,59 @@
+/* ProgressBarUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JProgressBar</code>.
+ *
+ * @see javax.swing.JProgressBar
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ProgressBarUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>ProgressBarUI</code>.
+ */
+ public ProgressBarUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/RootPaneUI.java b/libjava/classpath/javax/swing/plaf/RootPaneUI.java
new file mode 100644
index 000000000..9637c9cf2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/RootPaneUI.java
@@ -0,0 +1,58 @@
+/* RootPaneUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JRootPane</code>.
+ *
+ * @see javax.swing.JRootPane
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class RootPaneUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>RootPaneUI</code>.
+ */
+ public RootPaneUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ScrollBarUI.java b/libjava/classpath/javax/swing/plaf/ScrollBarUI.java
new file mode 100644
index 000000000..51b4bf2d8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ScrollBarUI.java
@@ -0,0 +1,58 @@
+/* ScrollBarUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JScrollBar</code>.
+ *
+ * @see javax.swing.JScrollBar
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ScrollBarUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>ScrollBarUI</code>.
+ */
+ public ScrollBarUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java
new file mode 100644
index 000000000..8b37fed22
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java
@@ -0,0 +1,59 @@
+/* ScrollPaneUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JScrollPane</code>.
+ *
+ * @see javax.swing.JScrollPane
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ScrollPaneUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>ScrollPaneUI</code>.
+ */
+ public ScrollPaneUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/SeparatorUI.java b/libjava/classpath/javax/swing/plaf/SeparatorUI.java
new file mode 100644
index 000000000..8a9f8cf4d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/SeparatorUI.java
@@ -0,0 +1,59 @@
+/* SeparatorUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JSeparator</code>.
+ *
+ * @see javax.swing.JSeparator
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class SeparatorUI
+ extends ComponentUI
+{
+ /**
+ * Constructs a new <code>SeparatorUI</code>.
+ */
+ public SeparatorUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/SliderUI.java b/libjava/classpath/javax/swing/plaf/SliderUI.java
new file mode 100644
index 000000000..570e962aa
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/SliderUI.java
@@ -0,0 +1,59 @@
+/* SliderUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JSlider</code>.
+ *
+ * @see javax.swing.JSlider
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class SliderUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>SliderUI</code>.
+ */
+ public SliderUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/SpinnerUI.java b/libjava/classpath/javax/swing/plaf/SpinnerUI.java
new file mode 100644
index 000000000..ca29ddb8e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/SpinnerUI.java
@@ -0,0 +1,59 @@
+/* SpinnerUI.java --
+ Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JSpinner</code>.
+ *
+ * @since 1.4
+ * @see javax.swing.JSpinner
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class SpinnerUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>SpinnerUI</code>.
+ */
+ public SpinnerUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/SplitPaneUI.java b/libjava/classpath/javax/swing/plaf/SplitPaneUI.java
new file mode 100644
index 000000000..faf17cb08
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/SplitPaneUI.java
@@ -0,0 +1,133 @@
+/* SplitPaneUI.java --
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Graphics;
+
+import javax.swing.JSplitPane;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JSplitPane</code>.
+ *
+ * @see javax.swing.JSplitPane
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class SplitPaneUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>SplitPaneUI</code>.
+ */
+ public SplitPaneUI()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Moves the divider to the location which best respects
+ * the preferred sizes of the children.
+ *
+ * @param pane the <code>JSplitPane</code> for thich this
+ * delegate provides the look and feel.
+ */
+ public abstract void resetToPreferredSizes(JSplitPane pane);
+
+
+ /**
+ * Moves the divider to the specified location.
+ *
+ * @param pane the <code>JSplitPane</code> for thich this
+ * delegate provides the look and feel.
+ *
+ * @param location the new location of the divider.
+ */
+ public abstract void setDividerLocation(JSplitPane pane,
+ int location);
+
+
+ /**
+ * Determines the current location of the divider.
+ *
+ * @param pane the <code>JSplitPane</code> for thich this
+ * delegate provides the look and feel.
+ *
+ * @return the current location of the divider.
+ */
+ public abstract int getDividerLocation(JSplitPane pane);
+
+
+ /**
+ * Determines the minimum location of the divider.
+ *
+ * @param pane the <code>JSplitPane</code> for thich this
+ * delegate provides the look and feel.
+ *
+ * @return the leftmost (or topmost) possible location
+ * of the divider.
+ */
+ public abstract int getMinimumDividerLocation(JSplitPane pane);
+
+
+ /**
+ * Determines the maximum location of the divider.
+ *
+ * @param pane the <code>JSplitPane</code> for thich this
+ * delegate provides the look and feel.
+ *
+ * @return the bottommost (or rightmost) possible location
+ * of the divider.
+ */
+ public abstract int getMaximumDividerLocation(JSplitPane pane);
+
+
+ /**
+ * Called by the <code>JSplitPane</code> after it has finished
+ * painting its children.
+ *
+ * @param pane the <code>JSplitPane</code> for thich this
+ * delegate provides the look and feel.
+ *
+ * @param g the Graphics used for painting.
+ */
+ public abstract void finishedPaintingChildren(JSplitPane pane,
+ Graphics g);
+}
diff --git a/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java
new file mode 100644
index 000000000..59136e04c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java
@@ -0,0 +1,110 @@
+/* TabbedPaneUI.java --
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Rectangle;
+
+import javax.swing.JTabbedPane;
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JTabbedPane</code>.
+ *
+ * @see javax.swing.JTabbedPane
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class TabbedPaneUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>TabbedPaneUI</code>.
+ */
+ public TabbedPaneUI()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Determines which tab lies at a given position.
+ *
+ * @param pane the <code>JTabbedPane</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param x the horizontal position, where zero is the left
+ * edge of <code>pane</code>.
+ *
+ * @param y the vertical position, where zero is the top
+ * edge of <code>pane</code>.
+ *
+ * @return the zero-based index of the tab, or -1 if no
+ * tab is at the specified position.
+ */
+ public abstract int tabForCoordinate(JTabbedPane pane,
+ int x, int y);
+
+
+ /**
+ * Calculates the bounding box of a tab.
+ *
+ * @param pane the <code>JTabbedPane</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param index the index of the tab, which must be an integer
+ * in the range <code>[0 .. pane.getTabCount() - 1]</code>.
+ *
+ * @return the bounding box of the <code>index</code>-th tab,
+ * in the coordinate system of <code>pane</code>.
+ */
+ public abstract Rectangle getTabBounds(JTabbedPane pane, int index);
+
+
+ /**
+ * Determines how many runs are used to display tabs.
+ *
+ * @param pane the <code>JTabbedPane</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @return the number of tab runs.
+ *
+ * @see javax.swing.JTabbedPane#getTabRunCount()
+ */
+ public abstract int getTabRunCount(JTabbedPane pane);
+}
diff --git a/libjava/classpath/javax/swing/plaf/TableHeaderUI.java b/libjava/classpath/javax/swing/plaf/TableHeaderUI.java
new file mode 100644
index 000000000..34ac0e0fc
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/TableHeaderUI.java
@@ -0,0 +1,59 @@
+/* TableHeaderUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JTableHeader</code>.
+ *
+ * @see javax.swing.table.JTableHeader
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class TableHeaderUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>TableHeaderUI</code>.
+ */
+ public TableHeaderUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/TableUI.java b/libjava/classpath/javax/swing/plaf/TableUI.java
new file mode 100644
index 000000000..a8c6bf909
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/TableUI.java
@@ -0,0 +1,59 @@
+/* TableUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JTable</code>.
+ *
+ * @see javax.swing.JTable
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class TableUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>TableUI</code>.
+ */
+ public TableUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/TextUI.java b/libjava/classpath/javax/swing/plaf/TextUI.java
new file mode 100644
index 000000000..424cee92d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/TextUI.java
@@ -0,0 +1,281 @@
+/* TextUI.java --
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.EditorKit;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.Position;
+import javax.swing.text.View;
+
+/**
+ * An abstract base class for delegates that provide the user
+ * interface for text editors.
+ *
+ * @see javax.swing.text.JTextComponent
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class TextUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>TextUI</code>.
+ */
+ public TextUI()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Calculates the geometric extent of the character at the
+ * given offset.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param pos the zero-based index of the character into the
+ * document model.
+ *
+ * @return the bounding box of the character at index
+ * <code>pos</code>, in view coordinates.
+ *
+ * @throws BadLocationException if <code>pos</code> does not
+ * designate a valid position in the document model.
+ *
+ * @see javax.swing.text.ComponentView#modelToView(int, Shape, Position.Bias)
+ */
+ public abstract Rectangle modelToView(JTextComponent tc, int pos)
+ throws BadLocationException;
+
+
+ /**
+ * Calculates the geometric extent of the character at the
+ * given offset.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param pos the zero-based index of the character into the
+ * document model.
+ *
+ * @param bias whether to take the character before or after the
+ * caret position indicated by <code>pos</code>. The value
+ * must be either {@link
+ * javax.swing.text.Position.Bias#Backward} or {@link
+ * javax.swing.text.Position.Bias#Forward}.
+ *
+ * @return the bounding box of the character at index
+ * <code>pos</code>, in view coordinates.
+ *
+ * @throws BadLocationException if <code>pos</code> does not
+ * designate a valid position in the document model.
+ *
+ * @see javax.swing.text.ComponentView#modelToView(int, Shape, Position.Bias)
+ */
+ public abstract Rectangle modelToView(JTextComponent tc, int pos,
+ Position.Bias bias)
+ throws BadLocationException;
+
+
+ /**
+ * Finds the caret position which is closest to the specified visual
+ * location.
+ *
+ * @param t the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param pt the position in view coordinates.
+ *
+ * @return the caret position which is closest to <code>loc</code>.
+ *
+ * @see #viewToModel(JTextComponent, Point, Position.Bias[])
+ */
+ public abstract int viewToModel(JTextComponent t, Point pt);
+
+
+ /**
+ * Finds the caret position which is closest to the specified visual
+ * location.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param loc the position in view coordinates.
+ *
+ * @param outBias an array whose size must be at least one.
+ * After the call, <code>outBias[0]</code> will indicate
+ * whether <code>loc</code> is in the glyph before
+ * (<code>Position.Bias.Backward</code>) or after
+ * (<code>Position.Bias.Forward</code>) the returned
+ * caret position.
+ *
+ * @return the caret position which is closest to <code>loc</code>.
+ */
+ public abstract int viewToModel(JTextComponent tc, Point loc,
+ Position.Bias[] outBias);
+
+
+
+ /**
+ * Calculates the caret position that is visually next to the given
+ * position. This is useful to determine where to move the caret
+ * after the user has pressed an arrow key.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param pos the current caret position, a zero-based index
+ * into the document model.
+ *
+ * @param bias whether to take the character before or after the
+ * caret position indicated by <code>pos</code>. The value
+ * must be either {@link
+ * javax.swing.text.Position.Bias#Backward} or {@link
+ * javax.swing.text.Position.Bias#Forward}.
+ *
+ * @param direction the visual direction. Pass
+ * {@link javax.swing.SwingConstants#WEST} for the left
+ * arrow key, {@link javax.swing.SwingConstants#EAST}
+ * for the right arrow key, {@link
+ * javax.swing.SwingConstants#NORTH} for the up arrow
+ * key, or {@link javax.swing.SwingConstants#SOUTH}
+ * for the down arrow key.
+ *
+ * @throws BadLocationException if <code>pos</code> does not
+ * designate a valid position in the document model.
+ *
+ * @throws IllegalArgumentException if <code>direction</code>
+ * is not one of <code>Position.Bias.Forward</code>
+ * or <code>Position.Bias.Backward</code>.
+ */
+ public abstract int getNextVisualPositionFrom(JTextComponent tc,
+ int pos,
+ Position.Bias bias,
+ int direction,
+ Position.Bias[] outBias)
+ throws BadLocationException;
+
+
+ /**
+ * Repaints a range of characters.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param start the first character in the range that needs
+ * painting, indicated as an index into the document model.
+ *
+ * @param end the last character in the range that needs
+ * painting, indicated as an index into the document model.
+ * <code>end</code> must be greater than or equal to
+ * <code>start</code>.
+ */
+ public abstract void damageRange(JTextComponent tc, int start, int end);
+
+
+ /**
+ * Repaints a range of characters, also specifying the bias for the
+ * start and end of the range.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param start the first character in the range that needs
+ * painting, indicated as an index into the document model.
+ *
+ * @param end the last character in the range that needs
+ * painting, indicated as an index into the document model.
+ * <code>end</code> must be greater than or equal to
+ * <code>start</code>.
+ */
+ public abstract void damageRange(JTextComponent tc,
+ int start, int end,
+ Position.Bias startBias,
+ Position.Bias endBias);
+
+
+ /**
+ * Retrieves the <code>EditorKit</code> managing policies and
+ * persistent state.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @return the <code>EditorKit</code> used by <code>tc</code>.
+ */
+ public abstract EditorKit getEditorKit(JTextComponent tc);
+
+
+ /**
+ * Retrieves the root of the view tree that visually presents
+ * the text.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @return the root <code>View</code> used by <code>tc</code>.
+ */
+ public abstract View getRootView(JTextComponent tc);
+
+
+ /**
+ * Returns a String for presenting a tool tip at the specified
+ * location.
+ *
+ * @param tc the <code>JTextComponent</code> for which this
+ * delegate object provides the user interface.
+ *
+ * @param loc the location for which the tool tip is requested.
+ *
+ * @return the text for the tool tip, or <code>null</code> to
+ * display no tool tip.
+ *
+ * @since 1.4
+ */
+ public String getToolTipText(JTextComponent tc, Point loc)
+ {
+ return null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ToolBarUI.java b/libjava/classpath/javax/swing/plaf/ToolBarUI.java
new file mode 100644
index 000000000..9a26e7b59
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ToolBarUI.java
@@ -0,0 +1,59 @@
+/* ToolBarUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JToolBar</code>.
+ *
+ * @see javax.swing.JToolBar
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ToolBarUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>ToolBarUI</code>.
+ */
+ public ToolBarUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/ToolTipUI.java b/libjava/classpath/javax/swing/plaf/ToolTipUI.java
new file mode 100644
index 000000000..ae2d465e5
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ToolTipUI.java
@@ -0,0 +1,59 @@
+/* ToolTipUI.java --
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JToolTip</code>.
+ *
+ * @see javax.swing.JToolTip
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ToolTipUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>ToolTipUI</code>.
+ */
+ public ToolTipUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/TreeUI.java b/libjava/classpath/javax/swing/plaf/TreeUI.java
new file mode 100644
index 000000000..308ec63e2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/TreeUI.java
@@ -0,0 +1,211 @@
+/* TreeUI.java --
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+import java.awt.Rectangle;
+
+import javax.swing.JTree;
+import javax.swing.tree.TreePath;
+
+/**
+ * An abstract base class for delegates that provide the user
+ * interface for <code>JTree</code>.
+ *
+ * @see javax.swing.JTree
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class TreeUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>TreeUI</code>.
+ */
+ public TreeUI()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Determines the geometric extent of the label that is
+ * drawn for a path.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ *
+ * @param path the path whose label extent is requested.
+ *
+ * @return a rectangle enclosing the label, or <code>null</code>
+ * if <code>path</code> contains invalid nodes.
+ */
+ public abstract Rectangle getPathBounds(JTree tree, TreePath path);
+
+
+ /**
+ * Creates a <code>TreePath</code> for the specified row.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ *
+ * @param row the index of the row, which should be a number
+ * in the range <code>[0, getRowCount(tree) - 1]</code>.
+ *
+ * @return a <code>TreePath</code> for the specified row, or
+ * <code>null</code> if <code>row</code> is outside
+ * the valid range.
+ */
+ public abstract TreePath getPathForRow(JTree tree, int row);
+
+
+ /**
+ * Determines in which row a <code>TreePath</code> is currently
+ * being displayed.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ *
+ * @param path the path for which the caller wants to know
+ * in which row it is being displayed.
+ *
+ * @return a number in the range <code>[0, getRowCount(tree)
+ * - 1]</code> if the path is currently on display;
+ * <code>-1</code> if the path is not shown to the
+ * user.
+ */
+ public abstract int getRowForPath(JTree tree, TreePath path);
+
+
+ /**
+ * Counts how many rows are currently displayed.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ *
+ * @return the number of visible rows.
+ */
+ public abstract int getRowCount(JTree tree);
+
+
+ /**
+ * Finds the path that is closest to the specified position.
+ *
+ * <p><img src="doc-files/TreeUI-1.png" width="300" height="250"
+ * alt="[A screen shot of a JTree]" />
+ *
+ * <p>As shown by the above illustration, the bounds of the
+ * closest path do not necessarily need to contain the passed
+ * location.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ *
+ * @param x the horizontal location, relative to the origin
+ * of <code>tree</code>.
+ *
+ * @param y the vertical location, relative to the origin
+ * of <code>tree</code>.
+ *
+ * @return the closest path, or <code>null</code> if the
+ * tree is currenlty not displaying any paths at all.
+ */
+ public abstract TreePath getClosestPathForLocation(JTree tree,
+ int x, int y);
+
+
+ /**
+ * Determines whether the user is currently editing a tree cell.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ *
+ * @see #getEditingPath
+ */
+ public abstract boolean isEditing(JTree tree);
+
+
+ /**
+ * Stops editing a tree cell, committing the entered value into the
+ * tree&#x2019;s model. If no editing session is active, or if the
+ * active editor does not agree to stopping, nothing happens. In
+ * some look and feels, this action happens when the user has
+ * pressed the enter key.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ *
+ * @return <code>false</code> if the editing still goes on because
+ * the cell editor has objected to stopping the session;
+ * <code>true</code> if editing has been stopped.
+ */
+ public abstract boolean stopEditing(JTree tree);
+
+
+ /**
+ * Cancels editing a tree cell, discarding any entered value.
+ * If no editing session is active, nothing happens. The cell
+ * editor is not given an opportunity to veto the canceling.
+ * In some look and feels, this action happens when the user has
+ * pressed the escape key.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ */
+ public abstract void cancelEditing(JTree tree);
+
+
+ /**
+ * Starts a session to edit a tree cell. If the cell editor
+ * rejects editing the cell, it will just be selected.
+ *
+ * @param tree the <code>JTree</code> for which this delegate
+ * object provides the user interface.
+ *
+ * @param path the cell to edit.
+ */
+ public abstract void startEditingAtPath(JTree tree, TreePath path);
+
+
+ /**
+ * Retrieves the tree cell that is currently being edited.
+ *
+ * @return the currently edited path, or <code>null</code>
+ * if no editing session is currently active.
+ */
+ public abstract TreePath getEditingPath(JTree tree);
+}
diff --git a/libjava/classpath/javax/swing/plaf/UIResource.java b/libjava/classpath/javax/swing/plaf/UIResource.java
new file mode 100644
index 000000000..511aaa70f
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/UIResource.java
@@ -0,0 +1,59 @@
+/* UIResource.java
+ Copyright (C) 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * This public interface is used to designate which objects were created by
+ * {@link ComponentUI} delegates. When uninstalling the user public interface
+ * renderer with <code>ComponentUI.uninstallUI()</code> the renderer
+ * property is set to <code>null</code>.
+ * <br>
+ * A comparison against null can be used with all properties except for
+ * the <code>java.awt.Component</code> properties font, foreground, and
+ * background. The container can provide the value of the properties if
+ * they are initialized or set to <code>null</code>.
+ *
+ * @author Brian Jones
+ *
+ * @see ComponentUI
+ */
+public interface UIResource
+{
+ // This is a marker interface and declares no methods.
+}
diff --git a/libjava/classpath/javax/swing/plaf/ViewportUI.java b/libjava/classpath/javax/swing/plaf/ViewportUI.java
new file mode 100644
index 000000000..db514de1a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/ViewportUI.java
@@ -0,0 +1,60 @@
+/* ViewportUI.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * An abstract base class for delegates that implement the pluggable
+ * look and feel for a <code>JViewport</code>.
+ *
+ * @see javax.swing.JViewport
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public abstract class ViewportUI extends ComponentUI
+{
+ /**
+ * Constructs a new <code>ViewportUI</code>.
+ */
+ public ViewportUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java
new file mode 100644
index 000000000..1d6e8874d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java
@@ -0,0 +1,422 @@
+/* BasicArrowButton.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Polygon;
+
+import javax.swing.ButtonModel;
+import javax.swing.JButton;
+import javax.swing.SwingConstants;
+
+/**
+ * A button that displays an arrow (triangle) that points {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} or {@link #WEST}. This button is used by
+ * the {@link BasicComboBoxUI} class.
+ *
+ * @see BasicComboBoxUI#createArrowButton
+ */
+public class BasicArrowButton extends JButton implements SwingConstants
+{
+
+ /**
+ * The direction that the arrow points.
+ *
+ * @see #getDirection()
+ */
+ protected int direction;
+
+ /**
+ * The color the arrow is painted in if disabled and the bottom and right
+ * edges of the button.
+ * This is package-private to avoid an accessor method.
+ */
+ transient Color shadow = Color.GRAY;
+
+ /**
+ * The color the arrow is painted in if enabled and the bottom and right
+ * edges of the button.
+ * This is package-private to avoid an accessor method.
+ */
+ transient Color darkShadow = new Color(102, 102, 102);
+
+ /**
+ * The top and left edges of the button.
+ * This is package-private to avoid an accessor method.
+ */
+ transient Color highlight = Color.WHITE;
+
+ /**
+ * Creates a new <code>BasicArrowButton</code> object with an arrow pointing
+ * in the specified direction. If the <code>direction</code> is not one of
+ * the specified constants, no arrow is drawn.
+ *
+ * @param direction The direction the arrow points in (one of:
+ * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ */
+ public BasicArrowButton(int direction)
+ {
+ super();
+ setDirection(direction);
+ setFocusable(false);
+ }
+
+ /**
+ * Creates a new BasicArrowButton object with the given colors and
+ * direction.
+ *
+ * @param direction The direction to point in (one of:
+ * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ * @param background The background color.
+ * @param shadow The shadow color.
+ * @param darkShadow The dark shadow color.
+ * @param highlight The highlight color.
+ */
+ public BasicArrowButton(int direction, Color background, Color shadow,
+ Color darkShadow, Color highlight)
+ {
+ this(direction);
+ setBackground(background);
+ this.shadow = shadow;
+ this.darkShadow = darkShadow;
+ this.highlight = highlight;
+ setFocusable(false);
+ }
+
+ /**
+ * Returns whether the focus can traverse to this component. This method
+ * always returns <code>false</code>.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Returns the direction of the arrow (one of: {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ *
+ * @return The direction of the arrow.
+ */
+ public int getDirection()
+ {
+ return direction;
+ }
+
+ /**
+ * Sets the direction of the arrow.
+ *
+ * @param dir The new direction of the arrow (one of: {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ */
+ public void setDirection(int dir)
+ {
+ this.direction = dir;
+ }
+
+ /**
+ * Paints the arrow button. The painting is delegated to the
+ * paintTriangle method.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ super.paint(g);
+
+ int height = getHeight();
+ int size = height / 4;
+
+ int x = (getWidth() - size) / 2;
+ int y = (height - size) / 2;
+
+ ButtonModel m = getModel();
+ if (m.isArmed())
+ {
+ x++;
+ y++;
+ }
+
+ paintTriangle(g, x, y, size, direction, isEnabled());
+ }
+
+ /**
+ * Returns the preferred size of the arrow button.
+ *
+ * @return The preferred size (always 16 x 16).
+ */
+ public Dimension getPreferredSize()
+ {
+ // since Dimension is NOT immutable, we must return a new instance
+ // every time (if we return a cached value, the caller might modify it)
+ // - tests show that the reference implementation does the same.
+ return new Dimension(16, 16);
+ }
+
+ /**
+ * Returns the minimum size of the arrow button.
+ *
+ * @return The minimum size (always 5 x 5).
+ */
+ public Dimension getMinimumSize()
+ {
+ // since Dimension is NOT immutable, we must return a new instance
+ // every time (if we return a cached value, the caller might modify it)
+ // - tests show that the reference implementation does the same.
+ return new Dimension(5, 5);
+ }
+
+ /**
+ * Returns the maximum size of the arrow button.
+ *
+ * @return The maximum size (always Integer.MAX_VALUE x Integer.MAX_VALUE).
+ */
+ public Dimension getMaximumSize()
+ {
+ // since Dimension is NOT immutable, we must return a new instance
+ // every time (if we return a cached value, the caller might modify it)
+ // - tests show that the reference implementation does the same.
+ return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Paints a triangle with the given size, location and direction. It is
+ * difficult to explain the rationale behind the positioning of the triangle
+ * relative to the given (x, y) position - by trial and error we seem to
+ * match the behaviour of the reference implementation (which is missing a
+ * specification for this method).
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the triangle's location.
+ * @param y the y-coordinate for the triangle's location.
+ * @param size the arrow size (depth).
+ * @param direction the direction of the arrow (one of: {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ public void paintTriangle(Graphics g, int x, int y, int size, int direction,
+ boolean isEnabled)
+ {
+ Color savedColor = g.getColor();
+ switch (direction)
+ {
+ case NORTH:
+ paintTriangleNorth(g, x, y, size, isEnabled);
+ break;
+ case SOUTH:
+ paintTriangleSouth(g, x, y, size, isEnabled);
+ break;
+ case LEFT:
+ case WEST:
+ paintTriangleWest(g, x, y, size, isEnabled);
+ break;
+ case RIGHT:
+ case EAST:
+ paintTriangleEast(g, x, y, size, isEnabled);
+ break;
+ }
+ g.setColor(savedColor);
+ }
+
+ /**
+ * Paints an upward-pointing triangle. This method is called by the
+ * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method.
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the anchor point.
+ * @param y the y-coordinate for the anchor point.
+ * @param size the arrow size (depth).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ private void paintTriangleNorth(Graphics g, int x, int y, int size,
+ boolean isEnabled)
+ {
+ int tipX = x + (size - 2) / 2;
+ int tipY = y;
+ int baseX1 = tipX - (size - 1);
+ int baseX2 = tipX + (size - 1);
+ int baseY = y + (size - 1);
+ Polygon triangle = new Polygon();
+ triangle.addPoint(tipX, tipY);
+ triangle.addPoint(baseX1, baseY);
+ triangle.addPoint(baseX2, baseY);
+ if (isEnabled)
+ {
+ g.setColor(Color.DARK_GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ }
+ else
+ {
+ g.setColor(Color.GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ g.setColor(Color.WHITE);
+ g.drawLine(baseX1 + 1, baseY + 1, baseX2 + 1, baseY + 1);
+ }
+ }
+
+ /**
+ * Paints an downward-pointing triangle. This method is called by the
+ * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method.
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the anchor point.
+ * @param y the y-coordinate for the anchor point.
+ * @param size the arrow size (depth).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ private void paintTriangleSouth(Graphics g, int x, int y, int size,
+ boolean isEnabled)
+ {
+ int tipX = x + (size - 2) / 2;
+ int tipY = y + (size - 1);
+ int baseX1 = tipX - (size - 1);
+ int baseX2 = tipX + (size - 1);
+ int baseY = y;
+ Polygon triangle = new Polygon();
+ triangle.addPoint(tipX, tipY);
+ triangle.addPoint(baseX1, baseY);
+ triangle.addPoint(baseX2, baseY);
+ if (isEnabled)
+ {
+ g.setColor(Color.DARK_GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ }
+ else
+ {
+ g.setColor(Color.GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ g.setColor(Color.WHITE);
+ g.drawLine(tipX + 1, tipY, baseX2, baseY + 1);
+ g.drawLine(tipX + 1, tipY + 1, baseX2 + 1, baseY + 1);
+ }
+ }
+
+ /**
+ * Paints a right-pointing triangle. This method is called by the
+ * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method.
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the anchor point.
+ * @param y the y-coordinate for the anchor point.
+ * @param size the arrow size (depth).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ private void paintTriangleEast(Graphics g, int x, int y, int size,
+ boolean isEnabled)
+ {
+ int tipX = x + (size - 1);
+ int tipY = y + (size - 2) / 2;
+ int baseX = x;
+ int baseY1 = tipY - (size - 1);
+ int baseY2 = tipY + (size - 1);
+
+ Polygon triangle = new Polygon();
+ triangle.addPoint(tipX, tipY);
+ triangle.addPoint(baseX, baseY1);
+ triangle.addPoint(baseX, baseY2);
+ if (isEnabled)
+ {
+ g.setColor(Color.DARK_GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ }
+ else
+ {
+ g.setColor(Color.GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ g.setColor(Color.WHITE);
+ g.drawLine(baseX + 1, baseY2, tipX, tipY + 1);
+ g.drawLine(baseX + 1, baseY2 + 1, tipX + 1, tipY + 1);
+ }
+ }
+
+ /**
+ * Paints a left-pointing triangle. This method is called by the
+ * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method.
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the anchor point.
+ * @param y the y-coordinate for the anchor point.
+ * @param size the arrow size (depth).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ private void paintTriangleWest(Graphics g, int x, int y, int size,
+ boolean isEnabled)
+ {
+ int tipX = x;
+ int tipY = y + (size - 2) / 2;
+ int baseX = x + (size - 1);
+ int baseY1 = tipY - (size - 1);
+ int baseY2 = tipY + (size - 1);
+
+ Polygon triangle = new Polygon();
+ triangle.addPoint(tipX, tipY);
+ triangle.addPoint(baseX, baseY1);
+ triangle.addPoint(baseX, baseY2);
+ if (isEnabled)
+ {
+ g.setColor(Color.DARK_GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ }
+ else
+ {
+ g.setColor(Color.GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ g.setColor(Color.WHITE);
+ g.drawLine(baseX + 1, baseY1 + 1, baseX + 1, baseY2 + 1);
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java
new file mode 100644
index 000000000..83afc33a2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java
@@ -0,0 +1,1768 @@
+/* BasicBorders.java --
+ Copyright (C) 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.JButton;
+import javax.swing.JPopupMenu;
+import javax.swing.JSplitPane;
+import javax.swing.JToolBar;
+import javax.swing.UIManager;
+import javax.swing.border.AbstractBorder;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.Border;
+import javax.swing.plaf.BorderUIResource;
+import javax.swing.plaf.UIResource;
+import javax.swing.text.JTextComponent;
+
+/**
+ * Provides various borders for the Basic look and feel.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class BasicBorders
+{
+ /**
+ * A MarginBorder that gets shared by multiple components.
+ * Created on demand by the private helper function {@link
+ * #getMarginBorder()}.
+ */
+ private static MarginBorder sharedMarginBorder;
+
+
+ /**
+ * Returns a border for drawing push buttons.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;Button.shadow&#x201d;</code>,
+ * <code>&#x201c;Button.darkShadow&#x201d;</code>,
+ * <code>&#x201c;Button.light&#x201d;</code>, and
+ * <code>&#x201c;Button.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300"
+ * height="170" alt="[A screen shot of the returned border]" />
+ *
+ * @return a {@link
+ * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
+ * whose outer border is a {@link ButtonBorder} and whose
+ * inner border is a {@link MarginBorder}.
+ */
+ public static Border getButtonBorder()
+ {
+ Border outer;
+
+ /* The keys for UIDefaults have been determined by writing a
+ * test program that dumps the UIDefaults to stdout; that program
+ * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
+ * the key "light" is usually called "highlight", and "highlight"
+ * is usually called "lightHighlight".
+ */
+ outer = new ButtonBorder(UIManager.getColor("Button.shadow"),
+ UIManager.getColor("Button.darkShadow"),
+ UIManager.getColor("Button.light"),
+ UIManager.getColor("Button.highlight"));
+
+ /* While the inner border is shared between multiple buttons,
+ * we do not share the outer border because ButtonBorders store
+ * their border colors. We cannot guarantee that the colors
+ * (which come from UIDefaults) are unchanged between invocations
+ * of getButtonBorder. We could store the last colors, and share
+ * the button border if the colors are the same as in the last
+ * invocation, but it probably is not worth the effort.
+ */
+ return new BorderUIResource.CompoundBorderUIResource(
+ outer,
+ /* inner */ getMarginBorder());
+ }
+
+
+ /**
+ * Returns a border for drawing radio buttons.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;RadioButton.shadow&#x201d;</code>,
+ * <code>&#x201c;RadioButton.darkShadow&#x201d;</code>,
+ * <code>&#x201c;RadioButton.light&#x201d;</code>, and
+ * <code>&#x201c;RadioButton.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300"
+ * height="135" alt="[A screen shot of the returned border]" />
+ *
+ * @return a {@link
+ * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
+ * whose outer border is a {@link RadioButtonBorder} and whose
+ * inner border is a {@link MarginBorder}.
+ */
+ public static Border getRadioButtonBorder()
+ {
+ Border outer;
+
+ /* The keys for UIDefaults have been determined by writing a
+ * test program that dumps the UIDefaults to stdout; that program
+ * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
+ * the key "light" is usually called "highlight", and "highlight"
+ * is usually called "lightHighlight".
+ */
+ outer = new RadioButtonBorder(
+ UIManager.getColor("RadioButton.shadow"),
+ UIManager.getColor("RadioButton.darkShadow"),
+ UIManager.getColor("RadioButton.light"),
+ UIManager.getColor("RadioButton.highlight"));
+
+ /* While the inner border is shared between multiple buttons, we
+ * do not share the outer border because RadioButtonBorders, being
+ * ButtonBorders, store their border colors. We cannot guarantee
+ * that the colors (which come from UIDefaults) are unchanged
+ * between invocations of getButtonBorder. We could store the last
+ * colors, and share the button border if the colors are the same
+ * as in the last invocation, but it probably is not worth the
+ * effort.
+ */
+ return new BorderUIResource.CompoundBorderUIResource(
+ outer,
+ /* inner */ getMarginBorder());
+ }
+
+
+ /**
+ * Returns a border for drawing toggle buttons.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;ToggleButton.shadow&#x201d;</code>,
+ * <code>&#x201c;ToggleButton.darkShadow&#x201d;</code>,
+ * <code>&#x201c;ToggleButton.light&#x201d;</code>, and
+ * <code>&#x201c;ToggleButton.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png" width="270"
+ * height="135" alt="[A screen shot of the returned border]" />
+ *
+ * @return a {@link
+ * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
+ * whose outer border is a {@link ToggleButtonBorder} and whose
+ * inner border is a {@link MarginBorder}.
+ */
+ public static Border getToggleButtonBorder()
+ {
+ Border outer;
+
+ /* The keys for UIDefaults have been determined by writing a
+ * test program that dumps the UIDefaults to stdout; that program
+ * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
+ * the key "light" is usually called "highlight", and "highlight"
+ * is usually called "lightHighlight".
+ */
+ outer = new ToggleButtonBorder(
+ UIManager.getColor("ToggleButton.shadow"),
+ UIManager.getColor("ToggleButton.darkShadow"),
+ UIManager.getColor("ToggleButton.light"),
+ UIManager.getColor("ToggleButton.highlight"));
+
+ /* While the inner border is shared between multiple buttons, we
+ * do not share the outer border because ToggleButtonBorders, being
+ * ButtonBorders, store their border colors. We cannot guarantee
+ * that the colors (which come from UIDefaults) are unchanged
+ * between invocations of getButtonBorder. We could store the last
+ * colors, and share the button border if the colors are the same
+ * as in the last invocation, but it probably is not worth the
+ * effort.
+ */
+ return new BorderUIResource.CompoundBorderUIResource(
+ outer,
+ /* inner */ getMarginBorder());
+ }
+
+
+ /**
+ * Returns a border for drawing a two-pixel thick separator line
+ * below menu bars.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;MenuBar.shadow&#x201d;</code> and
+ * <code>&#x201c;MenuBar.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
+ * height="140" alt="[A screen shot of a JMenuBar with this border]" />
+ *
+ * @return a {@link MenuBarBorder}.
+ *
+ * @see javax.swing.JMenuBar
+ */
+ public static Border getMenuBarBorder()
+ {
+ /* See comment in methods above for why this border is not shared. */
+ return new MenuBarBorder(UIManager.getColor("MenuBar.shadow"),
+ UIManager.getColor("MenuBar.highlight"));
+ }
+
+
+ /**
+ * Returns a border for drawing a one-pixel thick border around
+ * split panes that are interrupted where the divider joins the
+ * border.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;SplitPane.darkShadow&#x201d;</code> and
+ * <code>&#x201c;SplitPane.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
+ *
+ * @return a {@link SplitPaneBorder}.
+ *
+ * @see javax.swing.JSplitPane
+ * @see #getSplitPaneDividerBorder()
+ */
+ public static Border getSplitPaneBorder()
+ {
+ /* See comment in methods above for why this border is not shared. */
+ return new SplitPaneBorder(UIManager.getColor("SplitPane.highlight"),
+ UIManager.getColor("SplitPane.darkShadow"));
+ }
+
+
+ /**
+ * Returns a border for drawing a one-pixel thick border around
+ * the divider of split panes.
+ *
+ * <p>The colors of the edges that are adjacent to the child components
+ * of the <code>JSplitPane</code> are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;SplitPane.darkShadow&#x201d;</code> and
+ * <code>&#x201c;SplitPane.highlight&#x201d;</code>. The color of the
+ * other two edges is the background color of the divider.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
+ * width="520" height="200" alt=
+ * "[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
+ *
+ * @return an instance of <code>SplitPaneDividerBorder</code>, which is
+ * not a public API class of this package.
+ *
+ * @see javax.swing.JSplitPane
+ * @see javax.swing.plaf.basic.BasicSplitPaneDivider
+ * @see #getSplitPaneBorder()
+ *
+ * @since 1.3
+ */
+ public static Border getSplitPaneDividerBorder()
+ {
+ /* See comment in methods above for why this border is not shared. */
+ return new SplitPaneDividerBorder();
+ }
+
+
+ /**
+ * Returns a border for drawing a border around a text field
+ * that makes the field appear as etched into the surface.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;TextField.shadow&#x201d;</code>,
+ * <code>&#x201c;TextField.darkShadow&#x201d;</code>,
+ * <code>&#x201c;TextField.light&#x201d;</code>, and
+ * <code>&#x201c;TextField.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500"
+ * height="200" alt="[A screen shot of a border returned by
+ * this method]" />
+ *
+ * @return an instance of {@link FieldBorder}.
+ *
+ * @see javax.swing.JTextField
+ * @see javax.swing.text.JTextComponent
+ */
+ public static Border getTextFieldBorder()
+ {
+ /* See comment in methods above for why this border is not shared. */
+ return new FieldBorder(
+ UIManager.getColor("TextField.shadow"),
+ UIManager.getColor("TextField.darkShadow"),
+ UIManager.getColor("TextField.light"),
+ UIManager.getColor("TextField.highlight"));
+ }
+
+
+ /**
+ * Returns a two-pixel thick, green
+ * <code>LineBorderUIResource</code>. This is so ugly that look and
+ * feels better use different borders for their progress bars, or
+ * they will look really terrible.
+ *
+ * <p><img src="doc-files/BasicBorders-1.png" width="120" height="80"
+ * alt="[A screen shot of a border returned by this method]" />
+ */
+ public static Border getProgressBarBorder()
+ {
+ /* There does not seem to exist a way to parametrize the color
+ * or thickness of the border through UIDefaults.
+ */
+ return new BorderUIResource.LineBorderUIResource(Color.green, 2);
+ }
+
+
+ /**
+ * Returns a border that is composed of a raised bevel border and a
+ * one-pixel thick line border.
+ *
+ * <p><img src="doc-files/BasicBorders-2.png" width="300" height="200"
+ * alt="[A screen shot of a border returned by this method]" />
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;InternalFrame.borderShadow&#x201d;</code>,
+ * <code>&#x201c;InternalFrame.borderDarkShadow&#x201d;</code>,
+ * <code>&#x201c;InternalFrame.borderLight&#x201d;</code>,
+ * <code>&#x201c;InternalFrame.borderHighlight&#x201d;</code>, and
+ * (for the inner one-pixel thick line)
+ * <code>&#x201c;InternalFrame.borderColor&#x201d;</code>.
+ */
+ public static Border getInternalFrameBorder()
+ {
+ Color shadow, darkShadow, highlight, lightHighlight, line;
+
+ /* See comment in methods above for why this border is not shared. */
+ shadow = UIManager.getColor("InternalFrame.borderShadow");
+ darkShadow = UIManager.getColor("InternalFrame.borderDarkShadow");
+ highlight = UIManager.getColor("InternalFrame.borderLight");
+ lightHighlight = UIManager.getColor("InternalFrame.borderHighlight");
+ line = UIManager.getColor("InternalFrame.borderColor");
+
+ return new BorderUIResource.CompoundBorderUIResource(
+ /* outer border */
+ new BorderUIResource.BevelBorderUIResource(
+ BevelBorder.RAISED,
+ (highlight != null) ? highlight : Color.lightGray,
+ (lightHighlight != null) ? lightHighlight : Color.white,
+ (darkShadow != null) ? darkShadow : Color.black,
+ (shadow != null) ? shadow : Color.gray),
+
+ /* inner border */
+ new BorderUIResource.LineBorderUIResource(
+ (line != null) ? line : Color.lightGray));
+ }
+
+
+ /**
+ * Returns a shared MarginBorder.
+ */
+ static Border getMarginBorder() // intentionally not public
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (sharedMarginBorder == null)
+ sharedMarginBorder = new MarginBorder();
+
+ return sharedMarginBorder;
+ }
+
+
+ /**
+ * A border whose appearance depends on the state of
+ * the enclosed button.
+ *
+ * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300"
+ * height="170" alt="[A screen shot of this border]" />
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class ButtonBorder
+ extends AbstractBorder
+ implements Serializable, UIResource
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -157053874580739687L;
+
+
+ /**
+ * The color for drawing the shaded parts of the border.
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ protected Color shadow;
+
+
+ /**
+ * The color for drawing the dark shaded parts of the border.
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ protected Color darkShadow;
+
+
+ /**
+ * The color for drawing the highlighted parts of the border.
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ protected Color highlight;
+
+
+ /**
+ * The color for drawing the bright highlighted parts of the border.
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ protected Color lightHighlight;
+
+
+ /**
+ * Constructs a new border for drawing a button in the Basic
+ * look and feel.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public ButtonBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* These colors usually come from the UIDefaults of the current
+ * look and feel. Use fallback values if the colors are not
+ * supplied. The API specification is silent about what
+ * behavior is expected for null colors, so users should not
+ * rely on this fallback (which is why it is not documented in
+ * the above Javadoc).
+ */
+ this.shadow = (shadow != null) ? shadow : Color.gray;
+ this.darkShadow = (darkShadow != null) ? darkShadow : Color.black;
+ this.highlight = (highlight != null) ? highlight : Color.lightGray;
+ this.lightHighlight = (lightHighlight != null)
+ ? lightHighlight
+ : Color.white;
+ }
+
+
+ /**
+ * Paints the ButtonBorder around a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ ButtonModel bmodel = null;
+
+ if (c instanceof AbstractButton)
+ bmodel = ((AbstractButton) c).getModel();
+
+ BasicGraphicsUtils.drawBezel(
+ g, x, y, width, height,
+ /* pressed */ (bmodel != null)
+ && /* mouse button pressed */ bmodel.isPressed()
+ && /* mouse inside */ bmodel.isArmed(),
+ /* default */ (c instanceof JButton)
+ && ((JButton) c).isDefaultButton(),
+ shadow, darkShadow, highlight, lightHighlight);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * <p>Although the thickness of the actually painted border
+ * depends on the state of the enclosed component, this
+ * measurement always returns the same amount of pixels. Indeed,
+ * it would be rather confusing if a button was appearing to
+ * change its size depending on whether it is pressed or not.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * <p>Although the thickness of the actually painted border
+ * depends on the state of the enclosed component, this
+ * measurement always returns the same amount of pixels. Indeed,
+ * it would be rather confusing if a button was appearing to
+ * change its size depending on whether it is pressed or not.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ /* The exact amount has been determined using a test program
+ * that was run on the Sun reference implementation. With
+ * Apple/Sun JDK 1.3.1 on MacOS X 10.1.5, the result is
+ * [3, 3, 3, 3]. With Sun JDK 1.4.1_01 on Linux/x86, the
+ * result is [2, 3, 3, 3]. We use the values from the 1.4.1_01
+ * release.
+ */
+ if (insets == null)
+ return new Insets(2, 3, 3, 3);
+
+ insets.top = 2;
+ insets.bottom = insets.left = insets.right = 3;
+ return insets;
+ }
+ }
+
+
+ /**
+ * A border that makes its enclosed component appear as lowered
+ * into the surface. Typically used for text fields.
+ *
+ * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500"
+ * height="200" alt="[A screen shot of this border]" />
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class FieldBorder
+ extends AbstractBorder
+ implements UIResource
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 949220756998454908L;
+
+
+ /**
+ * The color for drawing the outer half of the top and left
+ * edges.
+ */
+ protected Color shadow;
+
+
+ /**
+ * The color for drawing the inner half of the top and left
+ * edges.
+ */
+ protected Color darkShadow;
+
+
+ /**
+ * The color for drawing the inner half of the bottom and right
+ * edges.
+ */
+ protected Color highlight;
+
+
+ /**
+ * The color for drawing the outer half of the bottom and right
+ * edges.
+ */
+ protected Color lightHighlight;
+
+
+ /**
+ * Constructs a new border for drawing a text field in the Basic
+ * look and feel.
+ *
+ * @param shadow the color for drawing the outer half
+ * of the top and left edges.
+ *
+ * @param darkShadow the color for drawing the inner half
+ * of the top and left edges.
+ *
+ * @param highlight the color for drawing the inner half
+ * of the bottom and right edges.
+ *
+ * @param lightHighlight the color for drawing the outer half
+ * of the bottom and right edges.
+ */
+ public FieldBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* These colors usually come from the UIDefaults of the current
+ * look and feel. Use fallback values if the colors are not
+ * supplied. The API specification is silent about what
+ * behavior is expected for null colors, so users should not
+ * rely on this fallback (which is why it is not documented in
+ * the above Javadoc).
+ */
+ this.shadow = (shadow != null) ? shadow : Color.gray;
+ this.darkShadow = (darkShadow != null) ? darkShadow : Color.black;
+ this.highlight = (highlight != null) ? highlight : Color.lightGray;
+ this.lightHighlight = (lightHighlight != null)
+ ? lightHighlight : Color.white;
+ }
+
+
+ /**
+ * Paints the FieldBorder around a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height,
+ shadow, darkShadow,
+ highlight, lightHighlight);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ * If <code>c</code> is an instance of {@link
+ * javax.swing.text.JTextComponent}, its margin is
+ * added to the border size.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param c the component whose border is to be measured.
+ * If <code>c</code> is an instance of {@link
+ * javax.swing.text.JTextComponent}, its margin is
+ * added to the border size.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ if (insets == null)
+ insets = new Insets(2, 2, 2, 2);
+ else
+ insets.top = insets.left = insets.bottom = insets.right = 2;
+
+ if (c instanceof JTextComponent)
+ {
+ Insets margin = ((JTextComponent) c).getMargin();
+ insets.top += margin.top;
+ insets.left += margin.left;
+ insets.bottom += margin.bottom;
+ insets.right += margin.right;
+ }
+
+ return insets;
+ }
+ }
+
+
+ /**
+ * An invisible, but spacing border whose margin is determined
+ * by calling the <code>getMargin()</code> method of the enclosed
+ * component. If the enclosed component has no such method,
+ * this border will not occupy any space.
+ *
+ * <p><img src="doc-files/BasicBorders.MarginBorder-1.png" width="325"
+ * height="200" alt="[An illustration that shows how MarginBorder
+ * determines its borders]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class MarginBorder
+ extends AbstractBorder
+ implements Serializable, UIResource
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -3035848353448896090L;
+
+
+ /**
+ * Constructs a new MarginBorder.
+ */
+ public MarginBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, new Insets(0, 0, 0, 0));
+ }
+
+
+ /**
+ * Determines the insets of this border by calling the
+ * <code>getMargin()</code> method of the enclosed component. The
+ * resulting margin will be stored into the the <code>left</code>,
+ * <code>right</code>, <code>top</code> and <code>bottom</code>
+ * fields of the passed <code>insets</code> parameter.
+ *
+ * <p>Unfortunately, <code>getMargin()</code> is not a method of
+ * {@link javax.swing.JComponent} or some other common superclass
+ * of things with margins. While reflection could be used to
+ * determine the existence of this method, this would be slow on
+ * many virtual machines. Therefore, the current implementation
+ * knows about {@link javax.swing.AbstractButton#getMargin()},
+ * {@link javax.swing.JPopupMenu#getMargin()}, {@link
+ * javax.swing.JToolBar#getMargin()}, and {@link
+ * javax.swing.text.JTextComponent}. If <code>c</code> is an
+ * instance of a known class, the respective
+ * <code>getMargin()</code> method is called to determine the
+ * correct margin. Otherwise, a zero-width margin is returned.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return the same object that was passed for <code>insets</code>,
+ * but with changed fields.
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ Insets margin = null;
+
+ /* This is terrible object-oriented design. See the above Javadoc
+ * for an excuse.
+ */
+ if (c instanceof AbstractButton)
+ margin = ((AbstractButton) c).getMargin();
+ else if (c instanceof JPopupMenu)
+ margin = ((JPopupMenu) c).getMargin();
+ else if (c instanceof JToolBar)
+ margin = ((JToolBar) c).getMargin();
+ else if (c instanceof JTextComponent)
+ margin = ((JTextComponent) c).getMargin();
+
+ if (margin == null)
+ insets.top = insets.left = insets.bottom = insets.right = 0;
+ else
+ {
+ insets.top = margin.top;
+ insets.left = margin.left;
+ insets.bottom = margin.bottom;
+ insets.right = margin.right;
+ }
+
+ return insets;
+ }
+ }
+
+
+ /**
+ * A border for drawing a separator line below JMenuBar.
+ *
+ * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
+ * height="140" alt="[A screen shot of a JMenuBar with this border]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class MenuBarBorder
+ extends AbstractBorder
+ implements UIResource
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -6909056571935227506L;
+
+
+ /**
+ * The shadow color, which is used for the upper line of the
+ * two-pixel thick bottom edge.
+ */
+ private Color shadow;
+
+
+ /**
+ * The highlight color, which is used for the lower line of the
+ * two-pixel thick bottom edge.
+ */
+ private Color highlight;
+
+
+ /**
+ * Constructs a new MenuBarBorder for drawing a JMenuBar in
+ * the Basic look and feel.
+ *
+ * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
+ * height="140" alt="[A screen shot of a JMenuBar with this
+ * border]" />
+ *
+ * @param shadow the shadow color, which is used for the upper
+ * line of the two-pixel thick bottom edge.
+ *
+ * @param highlight the shadow color, which is used for the lower
+ * line of the two-pixel thick bottom edge.
+ */
+ public MenuBarBorder(Color shadow, Color highlight)
+ {
+ /* These colors usually come from the UIDefaults of the current
+ * look and feel. Use fallback values if the colors are not
+ * supplied. The API specification is silent about what
+ * behavior is expected for null colors, so users should not
+ * rely on this fallback (which is why it is not documented in
+ * the above Javadoc).
+ */
+ this.shadow = (shadow != null) ? shadow : Color.gray;
+ this.highlight = (highlight != null) ? highlight : Color.white;
+ }
+
+
+ /**
+ * Paints the MenuBarBorder around a given component.
+ *
+ * @param c the component whose border is to be painted, usually
+ * an instance of {@link javax.swing.JMenuBar}.
+ *
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ Color oldColor;
+
+ /* To understand this code, it might be helpful to look at the
+ * image "BasicBorders.MenuBarBorder-1.png" that is included
+ * with the JavaDoc. It is located in the "doc-files"
+ * subdirectory.
+ */
+ oldColor = g.getColor();
+ y = y + height - 2;
+ try
+ {
+ g.setColor(shadow);
+ g.drawLine(x, y, x + width - 2, y);
+ g.drawLine(x, y + 1, x, y + 1);
+ g.drawLine(x + width - 2, y + 1, x + width - 2, y + 1);
+
+ g.setColor(highlight);
+ g.drawLine(x + 1, y + 1, x + width - 3, y + 1);
+ g.drawLine(x + width - 1, y, x + width - 1, y + 1);
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ /* The exact amount has been determined using a test program
+ * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
+ * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [0,0,2,0],
+ * which was expected from looking at the screen shot.
+ */
+ if (insets == null)
+ return new Insets(0, 0, 2, 0);
+
+ insets.left = insets.right = insets.top = 0;
+ insets.bottom = 2;
+ return insets;
+ }
+ }
+
+
+ /**
+ * A border for drawing radio buttons in the Basic look and feel.
+ *
+ * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300"
+ * height="135" alt="[A screen shot of this border]" />
+ *
+ * <p>Note about the screen shot: Normally, the
+ * <code>borderPainted</code> property is <code>false</code> for
+ * JRadioButtons. For this screen shot, it has been set to
+ * <code>true</code> so the borders get drawn. Also, a
+ * concretization of the Basic look and would typically provide
+ * icons for the various states of radio buttons.
+ *
+ * <p>Note that the focus rectangle is invisible If the radio button
+ * is currently selected. While it might be debatable whether this
+ * makes a lot of sense, this behavior can be observed in the Sun
+ * reference implementation (in JDK 1.3.1 and 1.4.1). The Classpath
+ * implementation tries to exactly replicate the JDK appearance.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class RadioButtonBorder
+ extends ButtonBorder
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 1596945751743747369L;
+
+
+ /**
+ * Constructs a new border for drawing a JRadioButton in
+ * the Basic look and feel.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public RadioButtonBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* The superclass ButtonBorder substitutes null arguments
+ * with fallback colors.
+ */
+ super(shadow, darkShadow, highlight, lightHighlight);
+ }
+
+
+ /**
+ * Paints the RadioButtonBorder around a given component.
+ *
+ * <p>The Sun implementation always seems to draw exactly
+ * the same border, irrespective of the state of the button.
+ * This is rather surprising, but GNU Classpath emulates the
+ * observable behavior.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ AbstractButton button = null;
+ ButtonModel bmodel = null;
+ boolean lowered = false;
+ boolean focused = false;
+
+ if (c instanceof AbstractButton)
+ {
+ button = (AbstractButton) c;
+ bmodel = button.getModel();
+ }
+
+ if (bmodel != null)
+ {
+ lowered = button.isSelected()
+ || (/* mouse inside */ bmodel.isArmed() && bmodel.isPressed());
+ focused = button.hasFocus() && button.isFocusPainted();
+ }
+
+ if (lowered)
+ BasicGraphicsUtils.drawLoweredBezel(g, x, y, width, height,
+ shadow, darkShadow,
+ highlight, lightHighlight);
+ else
+ BasicGraphicsUtils.drawBezel(g, x, y, width, height,
+ /* isPressed */ false,
+ /* isPefault */ focused,
+ shadow, darkShadow,
+ highlight, lightHighlight);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ /* The exact amount has been determined using a test program
+ * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
+ * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2].
+ */
+ if (insets == null)
+ return new Insets(2, 2, 2, 2);
+
+ insets.left = insets.right = insets.top = insets.bottom = 2;
+ return insets;
+ }
+ }
+
+
+ /**
+ * A one-pixel thick border for rollover buttons, for example in
+ * tool bars.
+ *
+ * @since 1.4
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class RolloverButtonBorder
+ extends ButtonBorder
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Sun JDK 1.4.1_01 on GNU/Linux 2.4.20 for x86.
+ */
+ static final long serialVersionUID = 1976364864896996846L;
+
+
+ /**
+ * Constructs a new border for drawing a roll-over button
+ * in the Basic look and feel.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public RolloverButtonBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ super(shadow, darkShadow, highlight, lightHighlight);
+ }
+
+
+ /**
+ * Paints the border around a rollover button. If <code>c</code>
+ * is not an {@link javax.swing.AbstractButton} whose model
+ * returns <code>true</code> for {@link
+ * javax.swing.ButtonModel#isRollover}, nothing gets painted at
+ * all.
+ *
+ * @param c the button whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ ButtonModel bmodel = null;
+ boolean drawPressed;
+ Color oldColor = g.getColor();
+ int x2, y2;
+
+ if (c instanceof AbstractButton)
+ bmodel = ((AbstractButton) c).getModel();
+
+ /* Draw nothing if c is not a rollover button. */
+ if ((bmodel == null) || !bmodel.isRollover())
+ return;
+
+ /* Draw nothing if the mouse is pressed, but outside the button. */
+ if (bmodel.isPressed() && !bmodel.isArmed())
+ return;
+
+ drawPressed = bmodel.isSelected() || bmodel.isPressed();
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+
+ try
+ {
+ g.setColor(drawPressed ? shadow : lightHighlight);
+ g.drawLine(x, y, x2 - 1, y); // top edge
+ g.drawLine(x, y + 1, x, y2 - 1); // left edge
+
+ g.setColor(drawPressed ? lightHighlight : shadow);
+ g.drawLine(x, y2, x2, y2); // bottom edge
+ g.drawLine(x2, y, x2, y2 - 1); // right edge
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+ }
+
+
+ /**
+ * A border for JSplitPanes in the Basic look and feel. The divider
+ * in the middle of the JSplitPane has its own border class, of which
+ * an instance can be obtained with {@link #getSplitPaneDividerBorder()}.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
+ *
+ * <p>In contrast to the other borders of the Basic look and feel,
+ * this class is not serializable. While this might be unintended,
+ * GNU Classpath follows the specification in order to be fully
+ * compatible with the Sun reference implementation.
+ *
+ * <p>In the Sun JDK, the bottom edge of the divider also gets
+ * painted if the orientation of the enclosed JSplitPane is
+ * <code>JSplitPane.VERTICAL_SPLIT</code> (at least in versions
+ * 1.3.1 and 1.4.1). GNU Classpath does not replicate this bug. A
+ * report has been filed with Sun (bug ID 4885629).
+ *
+ * <p>Note that the bottom left pixel of the border has a different
+ * color depending on the orientation of the enclosed JSplitPane.
+ * Although this is visually inconsistent, Classpath replicates the
+ * appearance of the Sun reference implementation. A bug report has
+ * been filed with Sun (review ID 188774).
+ *
+ * @see #getSplitPaneBorder()
+ * @see #getSplitPaneDividerBorder()
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class SplitPaneBorder implements Border, UIResource
+ {
+ /**
+ * Indicates that the top edge shall be not be painted
+ * by {@link #paintRect}.
+ */
+ private static final int SUPPRESS_TOP = 1;
+
+
+ /**
+ * Indicates that the left edge shall be not be painted
+ * by {@link #paintRect}.
+ */
+ private static final int SUPPRESS_LEFT = 2;
+
+
+ /**
+ * Indicates that the bottom edge shall be not be painted
+ * by {@link #paintRect}.
+ */
+ private static final int SUPPRESS_BOTTOM = 4;
+
+
+ /**
+ * Indicates that the right edge shall be not be painted
+ * by {@link #paintRect}.
+ */
+ private static final int SUPPRESS_RIGHT = 8;
+
+
+ /**
+ * The color for drawing the bottom and right edges of the border.
+ */
+ protected Color highlight;
+
+
+ /**
+ * The color for drawing the top and left edges of the border.
+ */
+ protected Color shadow;
+
+
+ /**
+ * Constructs a new border for drawing a JSplitPane in the Basic
+ * look and feel. The divider in the middle of the JSplitPane has
+ * its own border class, <code>SplitPaneDividerBorder</code>.
+ *
+ * @param shadow the shadow color.
+ * @param highlight the highlight color.
+ */
+ public SplitPaneBorder(Color highlight, Color shadow)
+ {
+ /* These colors usually come from the UIDefaults of the current
+ * look and feel. Use fallback values if the colors are not
+ * supplied. The API specification is silent about what
+ * behavior is expected for null colors, so users should not
+ * rely on this fallback (which is why it is not documented in
+ * the above Javadoc).
+ */
+ this.shadow = (shadow != null) ? shadow : Color.black;
+ this.highlight = (highlight != null) ? highlight : Color.white;
+ }
+
+
+ /**
+ * Paints the border around a <code>JSplitPane</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
+ *
+ * @param c the <code>JSplitPane</code> whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ JSplitPane splitPane;
+ Component content;
+
+ if (!(c instanceof JSplitPane))
+ return;
+
+ splitPane = (JSplitPane) c;
+ switch (splitPane.getOrientation())
+ {
+ case JSplitPane.HORIZONTAL_SPLIT:
+ if ((content = splitPane.getLeftComponent()) != null)
+ paintRect(g, SUPPRESS_RIGHT, true, x, y, content.getBounds());
+ if ((content = splitPane.getRightComponent()) != null)
+ paintRect(g, SUPPRESS_LEFT, true, x, y, content.getBounds());
+ break;
+
+ case JSplitPane.VERTICAL_SPLIT:
+ if ((content = splitPane.getTopComponent()) != null)
+ paintRect(g, SUPPRESS_BOTTOM, false, x, y, content.getBounds());
+ if ((content = splitPane.getBottomComponent()) != null)
+ paintRect(g, SUPPRESS_TOP, false, x, y, content.getBounds());
+ break;
+ }
+ }
+
+
+ /**
+ * Paints a border around a child of a <code>JSplitPane</code>,
+ * omitting some of the edges.
+ *
+ * @param g the graphics for painting.
+ *
+ * @param suppress a bit mask indicating the set of suppressed
+ * edges, for example <code>SUPPRESS_TOP | SUPPRESS_RIGHT</code>.
+ *
+ * @param x the x coordinate of the SplitPaneBorder.
+ *
+ * @param y the y coordinate of the SplitPaneBorder.
+ *
+ * @param shadeBottomLeftPixel <code>true</code> to paint the
+ * bottom left pixel in the shadow color,
+ * <code>false</code> for the highlight color. The Basic
+ * look and feel uses the highlight color for the bottom
+ * left pixel of the border of a JSplitPane whose
+ * orientation is VERTICAL_SPLIT, and the shadow color
+ * otherwise. While this might be a strange distinction,
+ * Classpath tries to look identical to the reference
+ * implementation. A bug report has been filed with Sun;
+ * its review ID is 188774. We currently replicate the
+ * Sun behavior.
+ *
+ * @param rect the bounds of the child of JSplitPane whose
+ * border is to be painted.
+ */
+ private void paintRect(Graphics g, int suppress,
+ boolean shadeBottomLeftPixel,
+ int x, int y,
+ Rectangle rect)
+ {
+ if (rect == null)
+ return;
+
+ /* On each edge, the border exceeds the enclosed child by one
+ * pixel. See the image "BasicBorders.SplitPaneBorder-1.png" in
+ * the directory "doc-files".
+ */
+ x += rect.x - 1;
+ y += rect.y - 1;
+ int right = x + rect.width + 1;
+ int bottom = y + rect.height + 1;
+
+ Color oldColor = g.getColor();
+ try
+ {
+ g.setColor(shadow);
+ if ((suppress & SUPPRESS_TOP) == 0)
+ g.drawLine(x, y, right, y);
+ if ((suppress & SUPPRESS_LEFT) == 0)
+ g.drawLine(x, y, x, bottom);
+ else
+ g.drawLine(x, bottom, x, bottom); // one pixel
+
+ g.setColor(highlight);
+ if ((suppress & SUPPRESS_BOTTOM) == 0)
+ g.drawLine(x + (shadeBottomLeftPixel ? 1 : 0), bottom, right, bottom);
+ else if (!shadeBottomLeftPixel)
+ g.drawLine(x, bottom, x, bottom); // one pixel
+
+ if ((suppress & SUPPRESS_RIGHT) == 0)
+ g.drawLine(right, y, right, bottom);
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured, usually
+ * an instance of {@link javax.swing.JSplitPane}.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(1, 1, 1, 1);
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * @return <code>false</code> because this border does not
+ * paint over the pixels where the divider joins
+ * the border.
+ */
+ public boolean isBorderOpaque()
+ {
+ /* Strangely, the Sun implementation (tested with JDK 1.3.1 and
+ * 1.4.1_01) seems to always return true. It could be a bug,
+ * but without knowing the details of their implementation, it is
+ * hard to decide.
+ */
+ return false;
+ }
+ }
+
+
+ /**
+ * A border for the divider inside a JSplitPane.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
+ * width="520" height="200" alt="[A screen shot of this border]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ private static class SplitPaneDividerBorder
+ implements Border, UIResource, Serializable
+ {
+ /**
+ * Constructs a new border for drawing the divider of a JSplitPane
+ * in the Basic look and feel. The outer parts of the JSplitPane have
+ * their own border class, <code>SplitPaneBorder</code>.
+ */
+ public SplitPaneDividerBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border around the divider of a <code>JSplitPane</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
+ * width="520" height="200" alt="[A picture that shows which pixels
+ * get painted in what color]" />
+ *
+ * @param c the <code>JSplitPane</code> whose divider&#x2019;s border
+ * is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ Color highlight = UIManager.getColor("SplitPane.highlight");
+ Color shadow = UIManager.getColor("SplitPane.shadow");
+ Color oldColor, dcol;
+ int x2, y2;
+ JSplitPane sp;
+
+ sp = getSplitPane(c);
+ if (sp == null)
+ return;
+
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+ oldColor = g.getColor();
+ dcol = c.getBackground();
+ try
+ {
+ switch (sp.getOrientation())
+ {
+ case JSplitPane.HORIZONTAL_SPLIT:
+ g.setColor(dcol);
+ g.drawLine(x + 1, y, x2 - 1, y);
+ g.drawLine(x + 1, y2, x2 - 1, y2);
+ g.setColor(sp.getLeftComponent() != null ? highlight : dcol);
+ g.drawLine(x, y, x, y2);
+ g.setColor(sp.getRightComponent() != null ? shadow : dcol);
+ g.drawLine(x2, y, x2, y2);
+ break;
+
+ case JSplitPane.VERTICAL_SPLIT:
+ g.setColor(dcol);
+ g.drawLine(x, y + 1, x, y2 - 1);
+ g.drawLine(x2, y + 1, x2, y2 - 1);
+ g.setColor(sp.getTopComponent() != null ? highlight : dcol);
+ g.drawLine(x, y, x2, y);
+ g.setColor(sp.getBottomComponent() != null ? shadow : dcol);
+ g.drawLine(x, y2, x2, y2);
+ break;
+ }
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured, usually
+ * an instance of {@link javax.swing.JSplitPane}.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(1, 1, 1, 1);
+ }
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * @return <code>true</code>
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+
+ /**
+ * Determines the JSplitPane whose divider is being painted.
+ *
+ * @param c an instance of BasicSplitPaneDivider.
+ *
+ * @return a <code>JSplitPane</code>, or <code>null</code> if
+ * <code>c</code> is not an instance of {@link
+ * javax.swing.plaf.basic.BasicSplitPaneDivider}.
+ */
+ private JSplitPane getSplitPane(Component c)
+ {
+ if (c instanceof BasicSplitPaneDivider)
+ return (((BasicSplitPaneDivider) c).getBasicSplitPaneUI())
+ .getSplitPane();
+ else
+ return null;
+ }
+ }
+
+
+ /**
+ * A border for toggle buttons in the Basic look and feel.
+ *
+ * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png"
+ * width="270" height="135" alt="[A screen shot of this border]" />
+ *
+ * <p>The Sun implementation always seems to draw exactly
+ * the same border, irrespective of the state of the button.
+ * This is rather surprising, but GNU Classpath emulates the
+ * observable behavior.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class ToggleButtonBorder
+ extends ButtonBorder
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -3528666548001058394L;
+
+
+ /**
+ * Constructs a new border for drawing a JToggleButton in
+ * the Basic look and feel.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public ToggleButtonBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* The superclass ButtonBorder substitutes null arguments
+ * with fallback colors.
+ */
+ super(shadow, darkShadow, highlight, lightHighlight);
+ }
+
+
+ /**
+ * Paints the ToggleButtonBorder around a given component.
+ *
+ * <p>The Sun implementation always seems to draw exactly
+ * the same border, irrespective of the state of the button.
+ * This is rather surprising, but GNU Classpath emulates the
+ * observable behavior.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ /* The author of this code tried various variants for setting
+ * the state of the enclosed JToggleButton, but it seems that
+ * the drawn border is always identical. Weird, because this
+ * means that the user does not see whether the JToggleButton
+ * is selected or not.
+ */
+ BasicGraphicsUtils.drawBezel(g, x, y, width, height,
+ /* pressed */ false,
+ /* default */ false,
+ shadow, darkShadow,
+ highlight, lightHighlight);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ /* The exact amount has been determined using a test program
+ * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
+ * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2].
+ */
+ if (insets == null)
+ return new Insets(2, 2, 2, 2);
+
+ insets.left = insets.right = insets.top = insets.bottom = 2;
+ return insets;
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java
new file mode 100644
index 000000000..22033b6ad
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java
@@ -0,0 +1,370 @@
+/* BasicButtonListener.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractButton;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.ButtonModel;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ButtonUI;
+
+public class BasicButtonListener
+ implements MouseListener, MouseMotionListener, FocusListener, ChangeListener,
+ PropertyChangeListener
+{
+ /**
+ * Implements the keyboard action for Swing buttons.
+ */
+ private class ButtonAction
+ extends AbstractAction
+ {
+ /**
+ * The key for pressed action.
+ */
+ static final String PRESSED = "pressed";
+
+ /**
+ * The key for released action.
+ */
+ static final String RELEASED = "released";
+
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ Object cmd = getValue("__command__");
+ AbstractButton b = (AbstractButton) event.getSource();
+ ButtonModel m = b.getModel();
+ if (PRESSED.equals(cmd))
+ {
+ m.setArmed(true);
+ m.setPressed(true);
+ if (! b.isFocusOwner())
+ b.requestFocus();
+ }
+ else if (RELEASED.equals(cmd))
+ {
+ m.setPressed(false);
+ m.setArmed(false);
+ }
+ }
+
+ /**
+ * Indicates if this action is enabled.
+ *
+ * @param source the source of the action
+ *
+ * @return <code>true</code> when enabled, <code>false</code> otherwise
+ */
+ public boolean isEnabled(Object source)
+ {
+ boolean enabled = true;
+ if (source instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) source;
+ enabled = b.isEnabled();
+ }
+ return enabled;
+ }
+ }
+
+ public BasicButtonListener(AbstractButton b)
+ {
+ // Do nothing here.
+ }
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // Store the TextLayout for this in a client property for speed-up
+ // painting of the label.
+ String property = e.getPropertyName();
+ AbstractButton b = (AbstractButton) e.getSource();
+ if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
+ || property.equals("font"))
+ && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
+ == null)
+ {
+ String text = b.getText();
+ if (text == null)
+ text = "";
+ FontRenderContext frc = new FontRenderContext(new AffineTransform(),
+ false, false);
+ TextLayout layout = new TextLayout(text, b.getFont(), frc);
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
+
+ // Update HTML renderer.
+ BasicHTML.updateRenderer(b, b.getText());
+ }
+ else if (property.equals(AbstractButton.CONTENT_AREA_FILLED_CHANGED_PROPERTY))
+ {
+ checkOpacity(b);
+ }
+ }
+
+ /**
+ * Checks the <code>contentAreaFilled</code> property and updates the
+ * opaque property of the button.
+ *
+ * @param b the button to check
+ */
+ protected void checkOpacity(AbstractButton b)
+ {
+ b.setOpaque(b.isContentAreaFilled());
+ }
+
+ 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)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ if (button.isFocusPainted())
+ button.repaint();
+ }
+ }
+
+ public void installKeyboardActions(JComponent c)
+ {
+ ButtonUI ui = ((AbstractButton) c).getUI();
+ if (ui instanceof BasicButtonUI)
+ {
+ // Install InputMap.
+ BasicButtonUI basicUI = (BasicButtonUI) ui;
+ String prefix = basicUI.getPropertyPrefix();
+ InputMap focusInputMap =
+ (InputMap) UIManager.get(prefix + "focusInputMap");
+ SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED,
+ focusInputMap);
+
+ ActionMap am = (ActionMap) UIManager.get(prefix + "actionMap");
+ if (am == null)
+ {
+ am = createDefaultActionMap();
+ UIManager.put(prefix + "actionMap", am);
+ }
+ SwingUtilities.replaceUIActionMap(c, am);
+ }
+
+ 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);
+ }
+ });
+ }
+
+ /**
+ * Creates and returns the default action map for Swing buttons.
+ *
+ * @return the default action map for Swing buttons
+ */
+ private ActionMap createDefaultActionMap()
+ {
+ Action action = new ButtonAction();
+ ActionMapUIResource am = new ActionMapUIResource();
+ am.put(ButtonAction.PRESSED, action);
+ am.put(ButtonAction.RELEASED, action);
+ return am;
+ }
+
+ public void uninstallKeyboardActions(JComponent c)
+ {
+ SwingUtilities.replaceUIActionMap(c, null);
+ SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
+ }
+
+ public void stateChanged(ChangeEvent e)
+ {
+ // Need to repaint when the button state changes.
+ ((AbstractButton) e.getSource()).repaint();
+ }
+
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseDragged(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Accept a mouse press event and arm the button.
+ *
+ * @param e The mouse press event to accept
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ if (SwingUtilities.isLeftMouseButton(e))
+ {
+ // It is important that these transitions happen in this order.
+ model.setArmed(true);
+ model.setPressed(true);
+
+ if (! button.isFocusOwner() && button.isRequestFocusEnabled())
+ button.requestFocus();
+ }
+ }
+ }
+
+ /**
+ * Accept a mouse release event and set the button's
+ * "pressed" property to <code>true</code>, if the model
+ * is armed. If the model is not armed, ignore the event.
+ *
+ * @param e The mouse release event to accept
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ if (e.getButton() == MouseEvent.BUTTON1)
+ {
+ // It is important that these transitions happen in this order.
+ model.setPressed(false);
+ model.setArmed(false);
+ }
+ }
+ }
+
+ /**
+ * Accept a mouse enter event and set the button's "rollover" property to
+ * <code>true</code>, if the button's "rolloverEnabled" property is
+ * <code>true</code>. If the button is currently armed and the mouse
+ * button is not held down, this enter event will also disarm the model.
+ *
+ * @param e The mouse enter event to accept
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ if (button.isRolloverEnabled()
+ && ! SwingUtilities.isLeftMouseButton(e))
+ model.setRollover(true);
+
+ if (model.isPressed())
+ model.setArmed(true);
+ }
+ }
+
+ /**
+ * Accept a mouse exit event and set the button's model's "rollover"
+ * property to <code>false</code>, if it's "rolloverEnabled" property is
+ * <code>true</code>. Also disarm the button.
+ *
+ * @param e The mouse exit event to accept
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ if (button.isRolloverEnabled())
+ model.setRollover(false);
+ model.setArmed(false);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
new file mode 100644
index 000000000..1697c24d9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
@@ -0,0 +1,636 @@
+/* BasicButtonUI.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ButtonUI;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.text.View;
+
+/**
+ * A UI delegate for the {@link JButton} component.
+ */
+public class BasicButtonUI extends ButtonUI
+{
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle viewR = new Rectangle();
+
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle iconR = new Rectangle();
+
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle textR = new Rectangle();
+
+ /**
+ * Cached Insets instance, used in paint().
+ */
+ static Insets cachedInsets;
+
+ /**
+ * The shared button UI.
+ */
+ private static BasicButtonUI sharedUI;
+
+ /**
+ * The shared BasicButtonListener.
+ */
+ private static BasicButtonListener sharedListener;
+
+ /**
+ * 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.
+ */
+ protected int defaultTextShiftOffset;
+
+ private int textShiftOffset;
+
+ /**
+ * Factory method to create an instance of BasicButtonUI for a given
+ * {@link JComponent}, which should be an {@link AbstractButton}.
+ *
+ * @param c The component.
+ *
+ * @return A new UI capable of drawing the component
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ if (sharedUI == null)
+ sharedUI = new BasicButtonUI();
+ return sharedUI;
+ }
+
+ /**
+ * Returns the default gap between the button's text and icon (in pixels).
+ *
+ * @param b the button (ignored).
+ *
+ * @return The gap.
+ */
+ public int getDefaultTextIconGap(AbstractButton b)
+ {
+ return defaultTextIconGap;
+ }
+
+ /**
+ * Sets the text shift offset to zero.
+ *
+ * @see #setTextShiftOffset()
+ */
+ protected void clearTextShiftOffset()
+ {
+ textShiftOffset = 0;
+ }
+
+ /**
+ * Returns the text shift offset.
+ *
+ * @return The text shift offset.
+ *
+ * @see #clearTextShiftOffset()
+ * @see #setTextShiftOffset()
+ */
+ protected int getTextShiftOffset()
+ {
+ return textShiftOffset;
+ }
+
+ /**
+ * Sets the text shift offset to the value in {@link #defaultTextShiftOffset}.
+ *
+ * @see #clearTextShiftOffset()
+ */
+ protected void setTextShiftOffset()
+ {
+ textShiftOffset = defaultTextShiftOffset;
+ }
+
+ /**
+ * Returns the prefix for the UI defaults property for this UI class.
+ * This is &apos;Button&apos; for this class.
+ *
+ * @return the prefix for the UI defaults property
+ */
+ protected String getPropertyPrefix()
+ {
+ return "Button.";
+ }
+
+ /**
+ * Installs the default settings.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void installDefaults(AbstractButton b)
+ {
+ String prefix = getPropertyPrefix();
+ // Install colors and font.
+ LookAndFeel.installColorsAndFont(b, prefix + "background",
+ prefix + "foreground", prefix + "font");
+ // Install border.
+ LookAndFeel.installBorder(b, prefix + "border");
+
+ // Install margin property.
+ if (b.getMargin() == null || b.getMargin() instanceof UIResource)
+ b.setMargin(UIManager.getInsets(prefix + "margin"));
+
+ // Install rollover property.
+ Object rollover = UIManager.get(prefix + "rollover");
+ if (rollover != null)
+ LookAndFeel.installProperty(b, "rolloverEnabled", rollover);
+
+ // Fetch default textShiftOffset.
+ defaultTextShiftOffset = UIManager.getInt(prefix + "textShiftOffset");
+
+ // Make button opaque if needed.
+ if (b.isContentAreaFilled())
+ LookAndFeel.installProperty(b, "opaque", Boolean.TRUE);
+ else
+ LookAndFeel.installProperty(b, "opaque", Boolean.FALSE);
+ }
+
+ /**
+ * Removes the defaults added by {@link #installDefaults(AbstractButton)}.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void uninstallDefaults(AbstractButton b)
+ {
+ // The other properties aren't uninstallable.
+ LookAndFeel.uninstallBorder(b);
+ }
+
+ /**
+ * Creates and returns a new instance of {@link BasicButtonListener}. This
+ * method provides a hook to make it easy for subclasses to install a
+ * different listener.
+ *
+ * @param b the button.
+ *
+ * @return A new listener.
+ */
+ protected BasicButtonListener createButtonListener(AbstractButton b)
+ {
+ // Note: The RI always returns a new instance here. However,
+ // the BasicButtonListener class is perfectly suitable to be shared
+ // between multiple buttons, so we return a shared instance here
+ // for efficiency.
+ if (sharedListener == null)
+ sharedListener = new BasicButtonListener(b);
+ return sharedListener;
+ }
+
+ /**
+ * Installs listeners for the button.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void installListeners(AbstractButton b)
+ {
+ BasicButtonListener listener = createButtonListener(b);
+ if (listener != null)
+ {
+ b.addChangeListener(listener);
+ b.addPropertyChangeListener(listener);
+ b.addFocusListener(listener);
+ b.addMouseListener(listener);
+ b.addMouseMotionListener(listener);
+ }
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ listener.propertyChange(new PropertyChangeEvent(b, "font", null,
+ b.getFont()));
+ }
+
+ /**
+ * Uninstalls listeners for the button.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void uninstallListeners(AbstractButton b)
+ {
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ {
+ b.removeChangeListener(listener);
+ b.removePropertyChangeListener(listener);
+ b.removeFocusListener(listener);
+ b.removeMouseListener(listener);
+ b.removeMouseMotionListener(listener);
+ }
+ }
+
+ protected void installKeyboardActions(AbstractButton b)
+ {
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.installKeyboardActions(b);
+ }
+
+ protected void uninstallKeyboardActions(AbstractButton b)
+ {
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.uninstallKeyboardActions(b);
+ }
+
+ /**
+ * Install the BasicButtonUI as the UI for a particular component.
+ * This means registering all the UI's listeners with the component,
+ * and setting any properties of the button which are particular to
+ * this look and feel.
+ *
+ * @param c The component to install the UI into
+ */
+ public void installUI(final JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ installDefaults(b);
+ // It is important to install the listeners before installing
+ // the keyboard actions, because the keyboard actions
+ // are actually installed on the listener instance.
+ installListeners(b);
+ installKeyboardActions(b);
+ BasicHTML.updateRenderer(b, b.getText());
+ }
+ }
+
+ /**
+ * Uninstalls the UI from the component.
+ *
+ * @param c the component from which to uninstall the UI
+ */
+ public void uninstallUI(JComponent c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ uninstallKeyboardActions(b);
+ uninstallListeners(b);
+ uninstallDefaults(b);
+ BasicHTML.updateRenderer(b, "");
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
+ }
+ }
+
+ /**
+ * Calculates the minimum size for the specified component.
+ *
+ * @param c the component for which to compute the minimum size
+ *
+ * @return the minimum size for the specified component
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension size = getPreferredSize(c);
+ // When the HTML view has a minimum width different from the preferred
+ // width, then substract this here accordingly. The height is not
+ // affected by that.
+ View html = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ size.width -= html.getPreferredSpan(View.X_AXIS)
+ - html.getPreferredSpan(View.X_AXIS);
+ }
+ return size;
+ }
+
+ /**
+ * Calculates the maximum size for the specified component.
+ *
+ * @param c the component for which to compute the maximum size
+ *
+ * @return the maximum size for the specified component
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension size = getPreferredSize(c);
+ // When the HTML view has a maximum width different from the preferred
+ // width, then add this here accordingly. The height is not
+ // affected by that.
+ View html = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ size.width += html.getMaximumSpan(View.X_AXIS)
+ - html.getPreferredSpan(View.X_AXIS);
+ }
+ return size;
+ }
+
+ /**
+ * Calculate the preferred size of this component, by delegating to
+ * {@link BasicGraphicsUtils#getPreferredButtonSize}.
+ *
+ * @param c The component to measure
+ *
+ * @return The preferred dimensions of the component
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Dimension d = BasicGraphicsUtils.getPreferredButtonSize(b,
+ b.getIconTextGap());
+ return d;
+ }
+
+ static Icon currentIcon(AbstractButton b)
+ {
+ Icon i = b.getIcon();
+ ButtonModel model = b.getModel();
+
+ if (model.isPressed() && b.getPressedIcon() != null && b.isEnabled())
+ i = b.getPressedIcon();
+
+ else if (model.isRollover())
+ {
+ if (b.isSelected() && b.getRolloverSelectedIcon() != null)
+ i = b.getRolloverSelectedIcon();
+ else if (b.getRolloverIcon() != null)
+ i = b.getRolloverIcon();
+ }
+
+ else if (b.isSelected() && b.isEnabled())
+ {
+ if (b.isEnabled() && b.getSelectedIcon() != null)
+ i = b.getSelectedIcon();
+ else if (b.getDisabledSelectedIcon() != null)
+ i = b.getDisabledSelectedIcon();
+ }
+
+ else if (! b.isEnabled() && b.getDisabledIcon() != null)
+ i = b.getDisabledIcon();
+
+ return i;
+ }
+
+ /**
+ * Paint the component, which is an {@link AbstractButton}, according to
+ * its current state.
+ *
+ * @param g The graphics context to paint with
+ * @param c The component to paint the state of
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+
+ Insets i = c.getInsets(cachedInsets);
+ viewR.x = i.left;
+ viewR.y = i.top;
+ viewR.width = c.getWidth() - i.left - i.right;
+ viewR.height = c.getHeight() - i.top - i.bottom;
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+
+ Font f = c.getFont();
+ g.setFont(f);
+ Icon icon = b.getIcon();
+ String text = b.getText();
+ text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
+ text, icon,
+ b.getVerticalAlignment(),
+ b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(),
+ viewR, iconR, textR,
+ text == null ? 0
+ : b.getIconTextGap());
+
+ ButtonModel model = b.getModel();
+ if (model.isArmed() && model.isPressed())
+ paintButtonPressed(g, b);
+
+ if (icon != null)
+ paintIcon(g, c, iconR);
+ if (text != null)
+ {
+ View html = (View) b.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ html.paint(g, textR);
+ else
+ paintText(g, b, textR, text);
+ }
+ if (b.isFocusOwner() && b.isFocusPainted())
+ paintFocus(g, b, viewR, textR, iconR);
+ }
+
+ /**
+ * Paint any focus decoration this {@link JComponent} might have. The
+ * component, which in this case will be an {@link AbstractButton},
+ * should only have focus decoration painted if it has the focus, and its
+ * "focusPainted" property is <code>true</code>.
+ *
+ * @param g Graphics context to paint with
+ * @param b Button to paint the focus of
+ * @param vr Visible rectangle, the area in which to paint
+ * @param tr Text rectangle, contained in visible rectangle
+ * @param ir Icon rectangle, contained in visible rectangle
+ *
+ * @see AbstractButton#isFocusPainted()
+ * @see JComponent#hasFocus()
+ */
+ protected void paintFocus(Graphics g, AbstractButton b, Rectangle vr,
+ Rectangle tr, Rectangle ir)
+ {
+ // In the BasicLookAndFeel no focus border is drawn. This can be
+ // overridden in subclasses to implement such behaviour.
+ }
+
+ /**
+ * Paint the icon for this component. Depending on the state of the
+ * component and the availability of the button's various icon
+ * properties, this might mean painting one of several different icons.
+ *
+ * @param g Graphics context to paint with
+ * @param c Component to paint the icon of
+ * @param iconRect Rectangle in which the icon should be painted
+ */
+ protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Icon i = currentIcon(b);
+
+ if (i != null)
+ {
+ ButtonModel m = b.getModel();
+ if (m.isPressed() && m.isArmed())
+ {
+ int offs = getTextShiftOffset();
+ i.paintIcon(c, g, iconRect.x + offs, iconRect.y + offs);
+ }
+ else
+ i.paintIcon(c, g, iconRect.x, iconRect.y);
+ }
+ }
+
+ /**
+ * Paints the background area of an {@link AbstractButton} in the pressed
+ * state. This means filling the supplied area with a darker than normal
+ * background.
+ *
+ * @param g The graphics context to paint with
+ * @param b The button to paint the state of
+ */
+ protected void paintButtonPressed(Graphics g, AbstractButton b)
+ {
+ if (b.isContentAreaFilled() && b.isOpaque())
+ {
+ Rectangle area = new Rectangle();
+ SwingUtilities.calculateInnerArea(b, area);
+ g.setColor(UIManager.getColor(getPropertyPrefix() + "shadow"));
+ g.fillRect(area.x, area.y, area.width, area.height);
+ }
+ }
+
+ /**
+ * Paints the "text" property of an {@link AbstractButton}.
+ *
+ * @param g The graphics context to paint with
+ * @param c The component to paint the state of
+ * @param textRect The area in which to paint the text
+ * @param text The text to paint
+ */
+ protected void paintText(Graphics g, JComponent c, Rectangle textRect,
+ String text)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Font f = b.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+
+ if (b.isEnabled())
+ {
+ g.setColor(b.getForeground());
+ // FIXME: Underline mnemonic.
+ BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
+ textRect.y + fm.getAscent());
+ }
+ else
+ {
+ String prefix = getPropertyPrefix();
+ g.setColor(UIManager.getColor(prefix + "disabledText"));
+ // FIXME: Underline mnemonic.
+ BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
+ textRect.y + fm.getAscent());
+ }
+ }
+
+ /**
+ * Paints the "text" property of an {@link AbstractButton}.
+ *
+ * @param g The graphics context to paint with
+ * @param b The button to paint the state of
+ * @param textRect The area in which to paint the text
+ * @param text The text to paint
+ *
+ * @since 1.4
+ */
+ protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
+ String text)
+ {
+ paintText(g, (JComponent) b, textRect, text);
+ }
+
+ /**
+ * A helper method that finds the BasicButtonListener for the specified
+ * button. This is there because this UI class is stateless and
+ * shared for all buttons, and thus can't store the listener
+ * as instance field. (We store our shared instance in sharedListener,
+ * however, subclasses may override createButtonListener() and we would
+ * be lost in this case).
+ *
+ * @param b the button
+ *
+ * @return the UI event listener
+ */
+ private BasicButtonListener getButtonListener(AbstractButton b)
+ {
+ // The listener gets installed as PropertyChangeListener,
+ // so look for it in the list of property change listeners.
+ PropertyChangeListener[] listeners = b.getPropertyChangeListeners();
+ BasicButtonListener l = null;
+ for (int i = 0; listeners != null && l == null && i < listeners.length;
+ i++)
+ {
+ if (listeners[i] instanceof BasicButtonListener)
+ l = (BasicButtonListener) listeners[i];
+ }
+ return l;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java
new file mode 100644
index 000000000..79ce7b834
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java
@@ -0,0 +1,102 @@
+/* BasicCheckBoxMenuItemUI.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.event.MouseEvent;
+
+import javax.swing.JComponent;
+import javax.swing.JMenuItem;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * DOCUMENT ME!
+ */
+public class BasicCheckBoxMenuItemUI extends BasicMenuItemUI
+{
+
+ /**
+ * Creates a new BasicCheckBoxMenuItemUI object.
+ */
+ public BasicCheckBoxMenuItemUI()
+ {
+ super();
+ }
+
+ /**
+ * Factory method to create a BasicCheckBoxMenuItemUI for the given {@link
+ * JComponent}, which should be a JCheckBoxMenuItem
+ *
+ * @param c The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicCheckBoxMenuItemUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ return new BasicCheckBoxMenuItemUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "CheckBoxMenuItem"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "CheckBoxMenuItem";
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param item DOCUMENT ME!
+ * @param e DOCUMENT ME!
+ * @param path DOCUMENT ME!
+ * @param manager DOCUMENT ME!
+ */
+ public void processMouseEvent(JMenuItem item, MouseEvent e,
+ MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // TODO: May not be implemented properly.
+ item.processMouseEvent(e, path, manager);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java
new file mode 100644
index 000000000..0a4da00b2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java
@@ -0,0 +1,75 @@
+/* BasicCheckBoxUI.java
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate for the {@link JCheckBox} component.
+ */
+public class BasicCheckBoxUI extends BasicRadioButtonUI
+{
+
+ /**
+ * Returns a UI delegate (that is, an instance of this class) for the
+ * specified component.
+ *
+ * @param c the component (this should be a {@link JCheckBox}).
+ *
+ * @return A new instance of <code>BasicCheckBoxUI</code>.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicCheckBoxUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIManager} defaults table
+ * (<code>"CheckBox."</code> in this case).
+ *
+ * @return "CheckBox."
+ */
+ public String getPropertyPrefix()
+ {
+ return "CheckBox.";
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java
new file mode 100644
index 000000000..10b56431a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java
@@ -0,0 +1,344 @@
+/* BasicColorChooserUI.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.BorderLayout;
+import java.awt.Container;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JColorChooser;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.LookAndFeel;
+import javax.swing.colorchooser.AbstractColorChooserPanel;
+import javax.swing.colorchooser.ColorChooserComponentFactory;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ColorChooserUI;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * This is the UI Class for the JColorChooser in the Basic Look and Feel.
+ */
+public class BasicColorChooserUI extends ColorChooserUI
+{
+ /**
+ * This helper class handles property changes from the JColorChooser.
+ */
+ public class PropertyHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when any of the properties of the JColorChooser
+ * change.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName() == JColorChooser.CHOOSER_PANELS_PROPERTY)
+ makeTabs(chooser.getChooserPanels());
+ else if (e.getPropertyName() == JColorChooser.PREVIEW_PANEL_PROPERTY)
+ updatePreviewPanel(chooser.getPreviewPanel());
+ else if (e.getPropertyName() == JColorChooser.SELECTION_MODEL_PROPERTY)
+ ((AbstractColorChooserPanel) pane.getSelectedComponent())
+ .updateChooser();
+
+ chooser.repaint();
+ }
+ }
+
+ /**
+ * This is a helper class that listens to the Model of the JColorChooser for
+ * color change events so it can update the preview panel.
+ */
+ private class PreviewListener implements ChangeListener
+ {
+ /**
+ * This method is called whenever the JColorChooser's color changes.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ if (pane != null)
+ {
+ AbstractColorChooserPanel panel = (AbstractColorChooserPanel) pane
+ .getSelectedComponent();
+ if (panel != null)
+ panel.updateChooser();
+ }
+ chooser.repaint();
+ }
+ }
+
+ /**
+ * This helper class listens to the JTabbedPane that is used for tab
+ * changes.
+ */
+ private class TabPaneListener implements ChangeListener
+ {
+ /**
+ * This method is called whenever a different tab is selected in the
+ * JTabbedPane.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Need to do this because we don't update all the tabs when they're not
+ // visible, so they are not informed of new colors when they're hidden.
+ AbstractColorChooserPanel comp = (AbstractColorChooserPanel) pane
+ .getSelectedComponent();
+ comp.updateChooser();
+ }
+ }
+
+ /** An array of default choosers to use in the JColorChooser. */
+ protected AbstractColorChooserPanel[] defaultChoosers;
+
+ /** The listener for the preview panel. */
+ protected ChangeListener previewListener;
+
+ /** The PropertyChangeListener for the JColorChooser. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /**
+ * The JColorChooser this is installed on.
+ */
+ protected JColorChooser chooser;
+
+ /** The JTabbedPane that is used. */
+ JTabbedPane pane;
+
+ /** The Container that holds the preview panel. */
+ private Container prevContainer;
+
+ /**
+ * Creates a new BasicColorChooserUI object.
+ */
+ public BasicColorChooserUI()
+ {
+ super();
+ }
+
+ /**
+ * This method creates a new UI Component for the given JComponent.
+ *
+ * @param c The JComponent to create an UI for.
+ *
+ * @return A new BasicColorChooserUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicColorChooserUI();
+ }
+
+ /**
+ * This method creates the default chooser panels for the JColorChooser.
+ *
+ * @return The default chooser panels.
+ */
+ protected AbstractColorChooserPanel[] createDefaultChoosers()
+ {
+ return ColorChooserComponentFactory.getDefaultChooserPanels();
+ }
+
+ /**
+ * This method installs the UI Component for the given JComponent.
+ *
+ * @param c The JComponent to install this UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JColorChooser)
+ {
+ chooser = (JColorChooser) c;
+ chooser.setLayout(new BorderLayout());
+
+ // Do this first, so we avoid doing work for property change events.
+ defaultChoosers = createDefaultChoosers();
+ chooser.setChooserPanels(defaultChoosers);
+ pane = new JTabbedPane();
+
+ pane.addChangeListener(new ChangeListener()
+ {
+ public void stateChanged(ChangeEvent e)
+ {
+ pane.repaint();
+ }
+ });
+
+ makeTabs(defaultChoosers);
+
+ chooser.add(pane, BorderLayout.NORTH);
+
+ installPreviewPanel();
+
+ installDefaults();
+ installListeners();
+ }
+ }
+
+ /**
+ * This method adds tabs to the JTabbedPane for the chooserPanels defined in
+ * the JColorChooser.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param panels The Panels that need tabs to be made for them.
+ */
+ void makeTabs(AbstractColorChooserPanel[] panels)
+ {
+ pane.removeAll();
+ for (int i = 0; i < panels.length; i++)
+ pane.addTab(panels[i].getDisplayName(), panels[i].getSmallDisplayIcon(),
+ panels[i]);
+ }
+
+ /**
+ * This method uninstalls this UI for the given JComponent.
+ *
+ * @param c The JComponent that will have this UI removed.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallDefaultChoosers();
+
+ pane = null;
+ chooser = null;
+ }
+
+ /**
+ * Uninstalls the default color choosers that have been installed by this UI.
+ */
+ protected void uninstallDefaultChoosers()
+ {
+ defaultChoosers = null;
+ }
+
+ /**
+ * This method installs the preview panel for the JColorChooser.
+ */
+ protected void installPreviewPanel()
+ {
+ updatePreviewPanel(ColorChooserComponentFactory.getPreviewPanel());
+ }
+
+ /**
+ * This is a helper method that swaps the existing preview panel with the
+ * given panel.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param preview The new preview panel.
+ */
+ void updatePreviewPanel(JComponent preview)
+ {
+ if (prevContainer == null)
+ {
+ prevContainer = new JPanel();
+ prevContainer.setLayout(new BorderLayout());
+ chooser.add(prevContainer, BorderLayout.CENTER);
+ }
+ prevContainer.removeAll();
+ prevContainer.add(preview, BorderLayout.CENTER);
+ }
+
+ /**
+ * This method installs the default properties given by the Basic Look and
+ * Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(chooser, "ColorChooser.background",
+ "ColorChooser.foreground",
+ "ColorChooser.font");
+ }
+
+ /**
+ * This method uninstalls the default properties given by the Basic Look and
+ * Feel.
+ */
+ protected void uninstallDefaults()
+ {
+ chooser.setBackground(null);
+ chooser.setForeground(null);
+ chooser.setFont(null);
+ }
+
+ /**
+ * This method installs any listeners required for this UI to function.
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+ previewListener = new PreviewListener();
+
+ chooser.addPropertyChangeListener(propertyChangeListener);
+ chooser.getSelectionModel().addChangeListener(previewListener);
+
+ pane.addChangeListener(new TabPaneListener());
+ }
+
+ /**
+ * This method creates the PropertyChangeListener used for listening to the
+ * JColorChooser.
+ *
+ * @return A PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyHandler();
+ }
+
+ /**
+ * This method uninstalls any listeners that were previously installed by
+ * the UI.
+ */
+ protected void uninstallListeners()
+ {
+ chooser.removePropertyChangeListener(propertyChangeListener);
+ chooser.getSelectionModel().removeChangeListener(previewListener);
+
+ previewListener = null;
+ propertyChangeListener = null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java
new file mode 100644
index 000000000..b04f3fa01
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java
@@ -0,0 +1,181 @@
+/* BasicComboBoxEditor.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+
+import javax.swing.ComboBoxEditor;
+import javax.swing.JTextField;
+
+/**
+ * An editor used by the {@link BasicComboBoxUI} class. This editor uses a
+ * {@link JTextField} as the editor component.
+ *
+ * @author Olga Rodimina
+ */
+public class BasicComboBoxEditor extends Object implements ComboBoxEditor,
+ FocusListener
+{
+ /** The editor component. */
+ protected JTextField editor;
+
+ /**
+ * Creates a new <code>BasicComboBoxEditor</code> instance.
+ */
+ public BasicComboBoxEditor()
+ {
+ editor = new JTextField();
+ editor.setBorder(null);
+ editor.setColumns(9);
+ }
+
+ /**
+ * Returns the component that will be used by the combo box to display and
+ * edit the currently selected item in the combo box.
+ *
+ * @return The editor component, which is a {@link JTextField} in this case.
+ */
+ public Component getEditorComponent()
+ {
+ return editor;
+ }
+
+ /**
+ * Sets item that should be edited when any editing operation is performed
+ * by the user. The value is always equal to the currently selected value
+ * in the combo box. Thus whenever a different value is selected from the
+ * combo box list then this method should be called to change editing
+ * item to the new selected item.
+ *
+ * @param item item that is currently selected in the combo box
+ */
+ public void setItem(Object item)
+ {
+ if (item == null)
+ editor.setText("");
+ else
+ editor.setText(item.toString());
+ }
+
+ /**
+ * Returns the text from the editor component.
+ *
+ * @return The text from the editor component.
+ */
+ public Object getItem()
+ {
+ return editor.getText();
+ }
+
+ /**
+ * Selects all the text in the editor component.
+ */
+ public void selectAll()
+ {
+ editor.selectAll();
+ }
+
+ /**
+ * This method is called when textfield gains focus. This will enable
+ * editing of the selected item.
+ *
+ * @param e the FocusEvent describing change in focus.
+ */
+ public void focusGained(FocusEvent e)
+ {
+ // FIXME: Need to implement
+ }
+
+ /**
+ * This method is called when textfield loses focus. If during this time any
+ * editting operation was performed by the user, then it will be cancelled
+ * and selected item will not be changed.
+ *
+ * @param e the FocusEvent describing change in focus
+ */
+ public void focusLost(FocusEvent e)
+ {
+ // FIXME: Need to implement
+ }
+
+ /**
+ * Adds an {@link ActionListener} to the editor component. If the user will
+ * edit currently selected item in the textfield and pressEnter, then action
+ * will be performed. The actionPerformed of this ActionListener should
+ * change the selected item of the comboBox to the newly editted selected
+ * item.
+ *
+ * @param l the ActionListener responsible for changing selected item of the
+ * combo box when it is editted by the user.
+ */
+ public void addActionListener(ActionListener l)
+ {
+ editor.addActionListener(l);
+ }
+
+ /**
+ * Removes the {@link ActionListener} from the editor component.
+ *
+ * @param l the listener to remove.
+ */
+ public void removeActionListener(ActionListener l)
+ {
+ editor.removeActionListener(l);
+ }
+
+ /**
+ * A subclass of {@link BasicComboBoxEditor} that implements the
+ * {@link UIResource} interface.
+ */
+ public static class UIResource extends BasicComboBoxEditor
+ implements javax.swing.plaf.UIResource
+ {
+ /**
+ * Creates a new <code>BasicComboBoxEditor.UIResource</code> instance.
+ */
+ public UIResource()
+ {
+ // Nothing to do here.
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java
new file mode 100644
index 000000000..761d7d11f
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java
@@ -0,0 +1,151 @@
+/* BasicComboBoxRenderer.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Dimension;
+import java.io.Serializable;
+
+import javax.swing.Icon;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.border.Border;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * A renderer for a {@link JComboBox}.
+ *
+ * @author Olga Rodimina
+ */
+public class BasicComboBoxRenderer
+ extends JLabel
+ implements ListCellRenderer, Serializable
+{
+ /**
+ * A shared border instance for all renderers.
+ */
+ protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+
+ /**
+ * Creates a new <code>BasicComboBoxRenderer</code> object.
+ */
+ public BasicComboBoxRenderer()
+ {
+ setOpaque(true);
+ setBorder(noFocusBorder);
+ }
+
+ /**
+ * Returns preferredSize of the renderer
+ *
+ * @return preferredSize of the renderer
+ */
+ public Dimension getPreferredSize()
+ {
+ if (this.getText() != null && ! this.getText().equals(""))
+ return super.getPreferredSize();
+ else
+ {
+ // If the combo box option's text is empty or null, it won't size
+ // properly (ie, it'll be way too short)... so we throw in a dummy
+ // space to trick the superclass's sizing methods.
+ String oldText = this.getText();
+ this.setText(" ");
+ Dimension d = super.getPreferredSize();
+ this.setText(oldText);
+ return d;
+ }
+ }
+
+ /**
+ * Returns a component that has been configured to display the given
+ * <code>value</code>.
+ *
+ * @param list List of items for which to the background and foreground
+ * colors
+ * @param value object that should be rendered in the cell
+ * @param index index of the cell in the list of items.
+ * @param isSelected draw cell highlighted if isSelected is true
+ * @param cellHasFocus draw focus rectangle around cell if the cell has
+ * focus
+ *
+ * @return Component that will be used to draw the desired cell.
+ */
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected,
+ boolean cellHasFocus)
+ {
+ if (isSelected)
+ {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ }
+ else
+ {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+ setFont(list.getFont());
+
+ if (value instanceof Icon)
+ setIcon((Icon) value);
+ else
+ setText(value == null ? "" : value.toString());
+
+ return this;
+ }
+
+ /**
+ * A subclass of {@link BasicComboBoxRenderer} that implements the
+ * {@link javax.swing.plaf.UIResource} interface.
+ */
+ public static class UIResource extends BasicComboBoxRenderer
+ implements javax.swing.plaf.UIResource
+ {
+ /**
+ * Creates a new <code>UIResource</code> object.
+ */
+ public UIResource()
+ {
+ // Nothing to do here.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java
new file mode 100644
index 000000000..07d4f42cd
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java
@@ -0,0 +1,1410 @@
+/* BasicComboBoxUI.java --
+ Copyright (C) 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.swing.CellRendererPane;
+import javax.swing.ComboBoxEditor;
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.plaf.ComboBoxUI;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A UI delegate for the {@link JComboBox} component.
+ *
+ * @author Olga Rodimina
+ * @author Robert Schuster
+ */
+public class BasicComboBoxUI extends ComboBoxUI
+{
+ /**
+ * The arrow button that is displayed in the right side of JComboBox. This
+ * button is used to hide and show combo box's list of items.
+ */
+ protected JButton arrowButton;
+
+ /**
+ * The combo box represented by this UI delegate.
+ */
+ protected JComboBox comboBox;
+
+ /**
+ * The component that is responsible for displaying/editing the selected
+ * item of the combo box.
+ *
+ * @see BasicComboBoxEditor#getEditorComponent()
+ */
+ protected Component editor;
+
+ /**
+ * A listener listening to focus events occurring in the {@link JComboBox}.
+ */
+ protected FocusListener focusListener;
+
+ /**
+ * A flag indicating whether JComboBox currently has the focus.
+ */
+ protected boolean hasFocus;
+
+ /**
+ * A listener listening to item events fired by the {@link JComboBox}.
+ */
+ protected ItemListener itemListener;
+
+ /**
+ * A listener listening to key events that occur while {@link JComboBox} has
+ * the focus.
+ */
+ protected KeyListener keyListener;
+
+ /**
+ * List used when rendering selected item of the combo box. The selection
+ * and foreground colors for combo box renderer are configured from this
+ * list.
+ */
+ protected JList listBox;
+
+ /**
+ * ListDataListener listening to JComboBox model
+ */
+ protected ListDataListener listDataListener;
+
+ /**
+ * Popup list containing the combo box's menu items.
+ */
+ protected ComboPopup popup;
+
+ protected KeyListener popupKeyListener;
+
+ protected MouseListener popupMouseListener;
+
+ protected MouseMotionListener popupMouseMotionListener;
+
+ /**
+ * Listener listening to changes in the bound properties of JComboBox
+ */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /* Size of the largest item in the comboBox
+ * This is package-private to avoid an accessor method.
+ */
+ Dimension displaySize = new Dimension();
+
+ /**
+ * Used to render the combo box values.
+ */
+ protected CellRendererPane currentValuePane;
+
+ /**
+ * The current minimum size if isMinimumSizeDirty is false.
+ * Setup by getMinimumSize() and invalidated by the various listeners.
+ */
+ protected Dimension cachedMinimumSize;
+
+ /**
+ * Indicates whether or not the cachedMinimumSize field is valid or not.
+ */
+ protected boolean isMinimumSizeDirty = true;
+
+ /**
+ * Creates a new <code>BasicComboBoxUI</code> object.
+ */
+ public BasicComboBoxUI()
+ {
+ currentValuePane = new CellRendererPane();
+ cachedMinimumSize = new Dimension();
+ }
+
+ /**
+ * A factory method to create a UI delegate for the given
+ * {@link JComponent}, which should be a {@link JComboBox}.
+ *
+ * @param c The {@link JComponent} a UI is being created for.
+ *
+ * @return A UI delegate for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicComboBoxUI();
+ }
+
+ /**
+ * Installs the UI for the given {@link JComponent}.
+ *
+ * @param c the JComponent to install a UI for.
+ *
+ * @see #uninstallUI(JComponent)
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ if (c instanceof JComboBox)
+ {
+ isMinimumSizeDirty = true;
+ comboBox = (JComboBox) c;
+ installDefaults();
+ popup = createPopup();
+ listBox = popup.getList();
+
+ // Set editor and renderer for the combo box. Editor is used
+ // only if combo box becomes editable, otherwise renderer is used
+ // to paint the selected item; combobox is not editable by default.
+ ListCellRenderer renderer = comboBox.getRenderer();
+ if (renderer == null || renderer instanceof UIResource)
+ comboBox.setRenderer(createRenderer());
+
+ ComboBoxEditor currentEditor = comboBox.getEditor();
+ if (currentEditor == null || currentEditor instanceof UIResource)
+ {
+ currentEditor = createEditor();
+ comboBox.setEditor(currentEditor);
+ }
+
+ installComponents();
+ installListeners();
+ comboBox.setLayout(createLayoutManager());
+ comboBox.setFocusable(true);
+ installKeyboardActions();
+ comboBox.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP,
+ Boolean.TRUE);
+ }
+ }
+
+ /**
+ * Uninstalls the UI for the given {@link JComponent}.
+ *
+ * @param c The JComponent that is having this UI removed.
+ *
+ * @see #installUI(JComponent)
+ */
+ public void uninstallUI(JComponent c)
+ {
+ setPopupVisible(comboBox, false);
+ popup.uninstallingUI();
+ uninstallKeyboardActions();
+ comboBox.setLayout(null);
+ uninstallComponents();
+ uninstallListeners();
+ uninstallDefaults();
+ comboBox = null;
+ }
+
+ /**
+ * Installs the defaults that are defined in the {@link BasicLookAndFeel}
+ * for this {@link JComboBox}.
+ *
+ * @see #uninstallDefaults()
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(comboBox, "ComboBox.background",
+ "ComboBox.foreground", "ComboBox.font");
+ LookAndFeel.installBorder(comboBox, "ComboBox.border");
+ }
+
+ /**
+ * Creates and installs the listeners for this UI.
+ *
+ * @see #uninstallListeners()
+ */
+ protected void installListeners()
+ {
+ // install combo box's listeners
+ propertyChangeListener = createPropertyChangeListener();
+ comboBox.addPropertyChangeListener(propertyChangeListener);
+
+ focusListener = createFocusListener();
+ comboBox.addFocusListener(focusListener);
+
+ itemListener = createItemListener();
+ comboBox.addItemListener(itemListener);
+
+ keyListener = createKeyListener();
+ comboBox.addKeyListener(keyListener);
+
+ // install listeners that listen to combo box model
+ listDataListener = createListDataListener();
+ comboBox.getModel().addListDataListener(listDataListener);
+
+ // Install mouse and key listeners from the popup.
+ popupMouseListener = popup.getMouseListener();
+ comboBox.addMouseListener(popupMouseListener);
+
+ popupMouseMotionListener = popup.getMouseMotionListener();
+ comboBox.addMouseMotionListener(popupMouseMotionListener);
+
+ popupKeyListener = popup.getKeyListener();
+ comboBox.addKeyListener(popupKeyListener);
+ }
+
+ /**
+ * Uninstalls the defaults and sets any objects created during
+ * install to <code>null</code>.
+ *
+ * @see #installDefaults()
+ */
+ protected void uninstallDefaults()
+ {
+ if (comboBox.getFont() instanceof UIResource)
+ comboBox.setFont(null);
+
+ if (comboBox.getForeground() instanceof UIResource)
+ comboBox.setForeground(null);
+
+ if (comboBox.getBackground() instanceof UIResource)
+ comboBox.setBackground(null);
+
+ LookAndFeel.uninstallBorder(comboBox);
+ }
+
+ /**
+ * Detaches all the listeners we attached in {@link #installListeners}.
+ *
+ * @see #installListeners()
+ */
+ protected void uninstallListeners()
+ {
+ comboBox.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+
+ comboBox.removeFocusListener(focusListener);
+ listBox.removeFocusListener(focusListener);
+ focusListener = null;
+
+ comboBox.removeItemListener(itemListener);
+ itemListener = null;
+
+ comboBox.removeKeyListener(keyListener);
+ keyListener = null;
+
+ comboBox.getModel().removeListDataListener(listDataListener);
+ listDataListener = null;
+
+ if (popupMouseListener != null)
+ comboBox.removeMouseListener(popupMouseListener);
+ popupMouseListener = null;
+
+ if (popupMouseMotionListener != null)
+ comboBox.removeMouseMotionListener(popupMouseMotionListener);
+ popupMouseMotionListener = null;
+
+ if (popupKeyListener != null)
+ comboBox.removeKeyListener(popupKeyListener);
+ popupKeyListener = null;
+ }
+
+ /**
+ * Creates the popup that will contain list of combo box's items.
+ *
+ * @return popup containing list of combo box's items
+ */
+ protected ComboPopup createPopup()
+ {
+ return new BasicComboPopup(comboBox);
+ }
+
+ /**
+ * Creates a {@link KeyListener} to listen to key events.
+ *
+ * @return KeyListener that listens to key events.
+ */
+ protected KeyListener createKeyListener()
+ {
+ return new KeyHandler();
+ }
+
+ /**
+ * Creates the {@link FocusListener} that will listen to changes in this
+ * JComboBox's focus.
+ *
+ * @return the FocusListener.
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * Creates a {@link ListDataListener} to listen to the combo box's data model.
+ *
+ * @return The new listener.
+ */
+ protected ListDataListener createListDataListener()
+ {
+ return new ListDataHandler();
+ }
+
+ /**
+ * Creates an {@link ItemListener} that will listen to the changes in
+ * the JComboBox's selection.
+ *
+ * @return The ItemListener
+ */
+ protected ItemListener createItemListener()
+ {
+ return new ItemHandler();
+ }
+
+ /**
+ * Creates a {@link PropertyChangeListener} to listen to the changes in
+ * the JComboBox's bound properties.
+ *
+ * @return The PropertyChangeListener
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates and returns a layout manager for the combo box. Subclasses can
+ * override this method to provide a different layout.
+ *
+ * @return a layout manager for the combo box.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new ComboBoxLayoutManager();
+ }
+
+ /**
+ * Creates a component that will be responsible for rendering the
+ * selected component in the combo box.
+ *
+ * @return A renderer for the combo box.
+ */
+ protected ListCellRenderer createRenderer()
+ {
+ return new BasicComboBoxRenderer.UIResource();
+ }
+
+ /**
+ * Creates the component that will be responsible for displaying/editing
+ * the selected item in the combo box. This editor is used only when combo
+ * box is editable.
+ *
+ * @return A new component that will be responsible for displaying/editing
+ * the selected item in the combo box.
+ */
+ protected ComboBoxEditor createEditor()
+ {
+ return new BasicComboBoxEditor.UIResource();
+ }
+
+ /**
+ * Installs the components for this JComboBox. ArrowButton, main
+ * part of combo box (upper part) and popup list of items are created and
+ * configured here.
+ */
+ protected void installComponents()
+ {
+ // create and install arrow button
+ arrowButton = createArrowButton();
+ comboBox.add(arrowButton);
+ if (arrowButton != null)
+ configureArrowButton();
+
+ if (comboBox.isEditable())
+ addEditor();
+
+ comboBox.add(currentValuePane);
+ }
+
+ /**
+ * Uninstalls components from this {@link JComboBox}.
+ *
+ * @see #installComponents()
+ */
+ protected void uninstallComponents()
+ {
+ // Unconfigure arrow button.
+ if (arrowButton != null)
+ {
+ unconfigureArrowButton();
+ }
+
+ // Unconfigure editor.
+ if (editor != null)
+ {
+ unconfigureEditor();
+ }
+
+ comboBox.removeAll();
+ arrowButton = null;
+ }
+
+ /**
+ * Adds the current editor to the combo box.
+ */
+ public void addEditor()
+ {
+ removeEditor();
+ editor = comboBox.getEditor().getEditorComponent();
+ if (editor != null)
+ {
+ configureEditor();
+ comboBox.add(editor);
+ }
+ }
+
+ /**
+ * Removes the current editor from the combo box.
+ */
+ public void removeEditor()
+ {
+ if (editor != null)
+ {
+ unconfigureEditor();
+ comboBox.remove(editor);
+ }
+ }
+
+ /**
+ * Configures the editor for this combo box.
+ */
+ protected void configureEditor()
+ {
+ editor.setFont(comboBox.getFont());
+ if (popupKeyListener != null)
+ editor.addKeyListener(popupKeyListener);
+ if (keyListener != null)
+ editor.addKeyListener(keyListener);
+ comboBox.configureEditor(comboBox.getEditor(),
+ comboBox.getSelectedItem());
+ }
+
+ /**
+ * Unconfigures the editor for this combo box.
+ */
+ protected void unconfigureEditor()
+ {
+ if (popupKeyListener != null)
+ editor.removeKeyListener(popupKeyListener);
+ if (keyListener != null)
+ editor.removeKeyListener(keyListener);
+ }
+
+ /**
+ * Configures the arrow button.
+ *
+ * @see #configureArrowButton()
+ */
+ public void configureArrowButton()
+ {
+ if (arrowButton != null)
+ {
+ arrowButton.setEnabled(comboBox.isEnabled());
+ arrowButton.setFocusable(false);
+ arrowButton.addMouseListener(popup.getMouseListener());
+ arrowButton.addMouseMotionListener(popup.getMouseMotionListener());
+
+ // Mark the button as not closing the popup, we handle this ourselves.
+ arrowButton.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP,
+ Boolean.TRUE);
+ }
+ }
+
+ /**
+ * Unconfigures the arrow button.
+ *
+ * @see #configureArrowButton()
+ *
+ * @specnote The specification says this method is implementation specific
+ * and should not be used or overridden.
+ */
+ public void unconfigureArrowButton()
+ {
+ if (arrowButton != null)
+ {
+ if (popupMouseListener != null)
+ arrowButton.removeMouseListener(popupMouseListener);
+ if (popupMouseMotionListener != null)
+ arrowButton.removeMouseMotionListener(popupMouseMotionListener);
+ }
+ }
+
+ /**
+ * Creates an arrow button for this {@link JComboBox}. The arrow button is
+ * displayed at the right end of the combo box and is used to display/hide
+ * the drop down list of items.
+ *
+ * @return A new button.
+ */
+ protected JButton createArrowButton()
+ {
+ return new BasicArrowButton(BasicArrowButton.SOUTH);
+ }
+
+ /**
+ * Returns <code>true</code> if the popup is visible, and <code>false</code>
+ * otherwise.
+ *
+ * @param c The JComboBox to check
+ *
+ * @return <code>true</code> if popup part of the JComboBox is visible and
+ * <code>false</code> otherwise.
+ */
+ public boolean isPopupVisible(JComboBox c)
+ {
+ return popup.isVisible();
+ }
+
+ /**
+ * Displays/hides the {@link JComboBox}'s list of items on the screen.
+ *
+ * @param c The combo box, for which list of items should be
+ * displayed/hidden
+ * @param v true if show popup part of the jcomboBox and false to hide.
+ */
+ public void setPopupVisible(JComboBox c, boolean v)
+ {
+ if (v)
+ popup.show();
+ else
+ popup.hide();
+ }
+
+ /**
+ * JComboBox is focus traversable if it is editable and not otherwise.
+ *
+ * @param c combo box for which to check whether it is focus traversable
+ *
+ * @return true if focus tranversable and false otherwise
+ */
+ public boolean isFocusTraversable(JComboBox c)
+ {
+ if (!comboBox.isEditable())
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Paints given menu item using specified graphics context
+ *
+ * @param g The graphics context used to paint this combo box
+ * @param c comboBox which needs to be painted.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ hasFocus = comboBox.hasFocus();
+ if (! comboBox.isEditable())
+ {
+ Rectangle rect = rectangleForCurrentValue();
+ paintCurrentValueBackground(g, rect, hasFocus);
+ paintCurrentValue(g, rect, hasFocus);
+ }
+ }
+
+ /**
+ * Returns preferred size for the combo box.
+ *
+ * @param c comboBox for which to get preferred size
+ *
+ * @return The preferred size for the given combo box
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return getMinimumSize(c);
+ }
+
+ /**
+ * Returns the minimum size for this {@link JComboBox} for this
+ * look and feel. Also makes sure cachedMinimimSize is setup correctly.
+ *
+ * @param c The {@link JComponent} to find the minimum size for.
+ *
+ * @return The dimensions of the minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ if (isMinimumSizeDirty)
+ {
+ Insets i = getInsets();
+ Dimension d = getDisplaySize();
+ d.width += i.left + i.right + d.height;
+ cachedMinimumSize = new Dimension(d.width, d.height + i.top + i.bottom);
+ isMinimumSizeDirty = false;
+ }
+ return new Dimension(cachedMinimumSize);
+ }
+
+ /**
+ * Returns the maximum size for this {@link JComboBox} for this
+ * look and feel.
+ *
+ * @param c The {@link JComponent} to find the maximum size for
+ *
+ * @return The maximum size (<code>Dimension(32767, 32767)</code>).
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(32767, 32767);
+ }
+
+ /**
+ * Returns the number of accessible children of the combobox.
+ *
+ * @param c the component (combobox) to check, ignored
+ *
+ * @return the number of accessible children of the combobox
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int count = 1;
+ if (comboBox.isEditable())
+ count = 2;
+ return count;
+ }
+
+ /**
+ * Returns the accessible child with the specified index.
+ *
+ * @param c the component, this is ignored
+ * @param i the index of the accessible child to return
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible child = null;
+ switch (i)
+ {
+ case 0: // The popup.
+ if (popup instanceof Accessible)
+ {
+ AccessibleContext ctx = ((Accessible) popup).getAccessibleContext();
+ ctx.setAccessibleParent(comboBox);
+ child = (Accessible) popup;
+ }
+ break;
+ case 1: // The editor, if any.
+ if (comboBox.isEditable() && editor instanceof Accessible)
+ {
+ AccessibleContext ctx =
+ ((Accessible) editor).getAccessibleContext();
+ ctx.setAccessibleParent(comboBox);
+ child = (Accessible) editor;
+ }
+ break;
+ }
+ return child;
+ }
+
+ /**
+ * Returns true if the specified key is a navigation key and false otherwise
+ *
+ * @param keyCode a key for which to check whether it is navigation key or
+ * not.
+ *
+ * @return true if the specified key is a navigation key and false otherwis
+ */
+ protected boolean isNavigationKey(int keyCode)
+ {
+ return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN
+ || keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT
+ || keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_ESCAPE
+ || keyCode == KeyEvent.VK_TAB;
+ }
+
+ /**
+ * Selects next possible item relative to the current selection
+ * to be next selected item in the combo box.
+ */
+ protected void selectNextPossibleValue()
+ {
+ int index = comboBox.getSelectedIndex();
+ if (index != comboBox.getItemCount() - 1)
+ comboBox.setSelectedIndex(index + 1);
+ }
+
+ /**
+ * Selects previous item relative to current selection to be
+ * next selected item.
+ */
+ protected void selectPreviousPossibleValue()
+ {
+ int index = comboBox.getSelectedIndex();
+ if (index > 0)
+ comboBox.setSelectedIndex(index - 1);
+ }
+
+ /**
+ * Displays combo box popup if the popup is not currently shown
+ * on the screen and hides it if it is currently shown
+ */
+ protected void toggleOpenClose()
+ {
+ setPopupVisible(comboBox, ! isPopupVisible(comboBox));
+ }
+
+ /**
+ * Returns the bounds in which comboBox's selected item will be
+ * displayed.
+ *
+ * @return rectangle bounds in which comboBox's selected Item will be
+ * displayed
+ */
+ protected Rectangle rectangleForCurrentValue()
+ {
+ int w = comboBox.getWidth();
+ int h = comboBox.getHeight();
+ Insets i = comboBox.getInsets();
+ int arrowSize = h - (i.top + i.bottom);
+ if (arrowButton != null)
+ arrowSize = arrowButton.getWidth();
+ return new Rectangle(i.left, i.top, w - (i.left + i.right + arrowSize),
+ h - (i.top + i.left));
+ }
+
+ /**
+ * Returns the insets of the current border.
+ *
+ * @return Insets representing space between combo box and its border
+ */
+ protected Insets getInsets()
+ {
+ return comboBox.getInsets();
+ }
+
+ /**
+ * Paints currently selected value in the main part of the combo
+ * box (part without popup).
+ *
+ * @param g graphics context
+ * @param bounds Rectangle representing the size of the area in which
+ * selected item should be drawn
+ * @param hasFocus true if combo box has focus and false otherwise
+ */
+ public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus)
+ {
+ /* Gets the component to be drawn for the current value.
+ * If there is currently no selected item we will take an empty
+ * String as replacement.
+ */
+ ListCellRenderer renderer = comboBox.getRenderer();
+ if (comboBox.getSelectedIndex() != -1)
+ {
+ Component comp;
+ if (hasFocus && ! isPopupVisible(comboBox))
+ {
+ comp = renderer.getListCellRendererComponent(listBox,
+ comboBox.getSelectedItem(), -1, true, false);
+ }
+ else
+ {
+ comp = renderer.getListCellRendererComponent(listBox,
+ comboBox.getSelectedItem(), -1, false, false);
+ Color bg = UIManager.getColor("ComboBox.disabledForeground");
+ comp.setBackground(bg);
+ }
+ comp.setFont(comboBox.getFont());
+ if (hasFocus && ! isPopupVisible(comboBox))
+ {
+ comp.setForeground(listBox.getSelectionForeground());
+ comp.setBackground(listBox.getSelectionBackground());
+ }
+ else if (comboBox.isEnabled())
+ {
+ comp.setForeground(comboBox.getForeground());
+ comp.setBackground(comboBox.getBackground());
+ }
+ else
+ {
+ Color fg = UIManager.getColor("ComboBox.disabledForeground");
+ comp.setForeground(fg);
+ Color bg = UIManager.getColor("ComboBox.disabledBackground");
+ comp.setBackground(bg);
+ }
+ currentValuePane.paintComponent(g, comp, comboBox, bounds.x, bounds.y,
+ bounds.width, bounds.height);
+ }
+ }
+
+ /**
+ * Paints the background of part of the combo box, where currently
+ * selected value is displayed. If the combo box has focus this method
+ * should also paint focus rectangle around the combo box.
+ *
+ * @param g graphics context
+ * @param bounds Rectangle representing the size of the largest item in the
+ * comboBox
+ * @param hasFocus true if combo box has fox and false otherwise
+ */
+ public void paintCurrentValueBackground(Graphics g, Rectangle bounds,
+ boolean hasFocus)
+ {
+ Color saved = g.getColor();
+ if (comboBox.isEnabled())
+ g.setColor(UIManager.getColor("UIManager.background"));
+ else
+ g.setColor(UIManager.getColor("UIManager.disabledBackground"));
+ g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
+ g.setColor(saved);
+ }
+
+ private static final ListCellRenderer DEFAULT_RENDERER
+ = new DefaultListCellRenderer();
+
+ /**
+ * Returns the default size for the display area of a combo box that does
+ * not contain any elements. This method returns the width and height of
+ * a single space in the current font, plus a margin of 1 pixel.
+ *
+ * @return The default display size.
+ *
+ * @see #getDisplaySize()
+ */
+ protected Dimension getDefaultSize()
+ {
+ Component comp = DEFAULT_RENDERER.getListCellRendererComponent(listBox,
+ " ", -1, false, false);
+ currentValuePane.add(comp);
+ comp.setFont(comboBox.getFont());
+ Dimension d = comp.getPreferredSize();
+ currentValuePane.remove(comp);
+ return d;
+ }
+
+ /**
+ * Returns the size of the display area for the combo box. This size will be
+ * the size of the combo box, not including the arrowButton.
+ *
+ * @return The size of the display area for the combo box.
+ */
+ protected Dimension getDisplaySize()
+ {
+ Dimension dim = new Dimension();
+ ListCellRenderer renderer = comboBox.getRenderer();
+ if (renderer == null)
+ {
+ renderer = DEFAULT_RENDERER;
+ }
+
+ Object prototype = comboBox.getPrototypeDisplayValue();
+ if (prototype != null)
+ {
+ Component comp = renderer.getListCellRendererComponent(listBox,
+ prototype, -1, false, false);
+ currentValuePane.add(comp);
+ comp.setFont(comboBox.getFont());
+ Dimension renderSize = comp.getPreferredSize();
+ currentValuePane.remove(comp);
+ dim.height = renderSize.height;
+ dim.width = renderSize.width;
+ }
+ else
+ {
+ ComboBoxModel model = comboBox.getModel();
+ int size = model.getSize();
+ if (size > 0)
+ {
+ for (int i = 0; i < size; ++i)
+ {
+ Component comp = renderer.getListCellRendererComponent(listBox,
+ model.getElementAt(i), -1, false, false);
+ currentValuePane.add(comp);
+ comp.setFont(comboBox.getFont());
+ Dimension renderSize = comp.getPreferredSize();
+ currentValuePane.remove(comp);
+ dim.width = Math.max(dim.width, renderSize.width);
+ dim.height = Math.max(dim.height, renderSize.height);
+ }
+ }
+ else
+ {
+ dim = getDefaultSize();
+ if (comboBox.isEditable())
+ dim.width = 100;
+ }
+ }
+ if (comboBox.isEditable())
+ {
+ Dimension editSize = editor.getPreferredSize();
+ dim.width = Math.max(dim.width, editSize.width);
+ dim.height = Math.max(dim.height, editSize.height);
+ }
+ displaySize.setSize(dim.width, dim.height);
+ return dim;
+ }
+
+ /**
+ * Installs the keyboard actions for the {@link JComboBox} as specified
+ * by the look and feel.
+ */
+ protected void installKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(comboBox,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ (InputMap) UIManager.get("ComboBox.ancestorInputMap"));
+ // Install any action maps here.
+ }
+
+ /**
+ * Uninstalls the keyboard actions for the {@link JComboBox} there were
+ * installed by in {@link #installListeners}.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(comboBox,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ // Uninstall any action maps here.
+ }
+
+ /**
+ * A {@link LayoutManager} used to position the sub-components of the
+ * {@link JComboBox}.
+ *
+ * @see BasicComboBoxUI#createLayoutManager()
+ */
+ public class ComboBoxLayoutManager implements LayoutManager
+ {
+ /**
+ * Creates a new ComboBoxLayoutManager object.
+ */
+ public ComboBoxLayoutManager()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds a component to the layout. This method does nothing, since the
+ * layout manager doesn't need to track the components.
+ *
+ * @param name the name to associate the component with (ignored).
+ * @param comp the component (ignored).
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Do nothing
+ }
+
+ /**
+ * Removes a component from the layout. This method does nothing, since
+ * the layout manager doesn't need to track the components.
+ *
+ * @param comp the component.
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Do nothing
+ }
+
+ /**
+ * Returns preferred layout size of the JComboBox.
+ *
+ * @param parent the Container for which the preferred size should be
+ * calculated.
+ *
+ * @return The preferred size for the given container
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ return parent.getPreferredSize();
+ }
+
+ /**
+ * Returns the minimum layout size.
+ *
+ * @param parent the container.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return parent.getMinimumSize();
+ }
+
+ /**
+ * Arranges the components in the container. It puts arrow
+ * button right end part of the comboBox. If the comboBox is editable
+ * then editor is placed to the left of arrow button, starting from the
+ * beginning.
+ *
+ * @param parent Container that should be layed out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ // Position editor component to the left of arrow button if combo box is
+ // editable
+ Insets i = getInsets();
+ int arrowSize = comboBox.getHeight() - (i.top + i.bottom);
+
+ if (arrowButton != null)
+ arrowButton.setBounds(comboBox.getWidth() - (i.right + arrowSize),
+ i.top, arrowSize, arrowSize);
+ if (editor != null)
+ editor.setBounds(rectangleForCurrentValue());
+ }
+ }
+
+ /**
+ * Handles focus changes occuring in the combo box. This class is
+ * responsible for repainting combo box whenever focus is gained or lost
+ * and also for hiding popup list of items whenever combo box loses its
+ * focus.
+ */
+ public class FocusHandler extends Object implements FocusListener
+ {
+ /**
+ * Creates a new FocusHandler object.
+ */
+ public FocusHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when combo box gains focus. It repaints main
+ * part of combo box accordingly.
+ *
+ * @param e the FocusEvent
+ */
+ public void focusGained(FocusEvent e)
+ {
+ hasFocus = true;
+ comboBox.repaint();
+ }
+
+ /**
+ * Invoked when the combo box loses focus. It repaints the main part
+ * of the combo box accordingly and hides the popup list of items.
+ *
+ * @param e the FocusEvent
+ */
+ public void focusLost(FocusEvent e)
+ {
+ hasFocus = false;
+ if (! e.isTemporary() && comboBox.isLightWeightPopupEnabled())
+ setPopupVisible(comboBox, false);
+ comboBox.repaint();
+ }
+ }
+
+ /**
+ * Handles {@link ItemEvent}s fired by the {@link JComboBox} when its
+ * selected item changes.
+ */
+ public class ItemHandler extends Object implements ItemListener
+ {
+ /**
+ * Creates a new ItemHandler object.
+ */
+ public ItemHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when selected item becomes deselected or when
+ * new item becomes selected.
+ *
+ * @param e the ItemEvent representing item's state change.
+ */
+ public void itemStateChanged(ItemEvent e)
+ {
+ ComboBoxModel model = comboBox.getModel();
+ Object v = model.getSelectedItem();
+ if (editor != null)
+ comboBox.configureEditor(comboBox.getEditor(), v);
+ comboBox.repaint();
+ }
+ }
+
+ /**
+ * KeyHandler handles key events occuring while JComboBox has focus.
+ */
+ public class KeyHandler extends KeyAdapter
+ {
+ public KeyHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked whenever key is pressed while JComboBox is in focus.
+ */
+ public void keyPressed(KeyEvent e)
+ {
+ if (comboBox.getModel().getSize() != 0 && comboBox.isEnabled())
+ {
+ if (! isNavigationKey(e.getKeyCode()))
+ {
+ if (! comboBox.isEditable())
+ if (comboBox.selectWithKeyChar(e.getKeyChar()))
+ e.consume();
+ }
+ else
+ {
+ if (e.getKeyCode() == KeyEvent.VK_UP && comboBox.isPopupVisible())
+ selectPreviousPossibleValue();
+ else if (e.getKeyCode() == KeyEvent.VK_DOWN)
+ {
+ if (comboBox.isPopupVisible())
+ selectNextPossibleValue();
+ else
+ comboBox.showPopup();
+ }
+ else if (e.getKeyCode() == KeyEvent.VK_ENTER
+ || e.getKeyCode() == KeyEvent.VK_ESCAPE)
+ popup.hide();
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles the changes occurring in the JComboBox's data model.
+ */
+ public class ListDataHandler extends Object implements ListDataListener
+ {
+ /**
+ * Creates a new ListDataHandler object.
+ */
+ public ListDataHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked if the content's of JComboBox's data model are changed.
+ *
+ * @param e ListDataEvent describing the change.
+ */
+ public void contentsChanged(ListDataEvent e)
+ {
+ if (e.getIndex0() != -1 || e.getIndex1() != -1)
+ {
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ }
+ if (editor != null)
+ comboBox.configureEditor(comboBox.getEditor(),
+ comboBox.getSelectedItem());
+ comboBox.repaint();
+ }
+
+ /**
+ * Invoked when items are added to the JComboBox's data model.
+ *
+ * @param e ListDataEvent describing the change.
+ */
+ public void intervalAdded(ListDataEvent e)
+ {
+ int start = e.getIndex0();
+ int end = e.getIndex1();
+ if (start == 0 && comboBox.getItemCount() - (end - start + 1) == 0)
+ contentsChanged(e);
+ else if (start != -1 || end != -1)
+ {
+ ListCellRenderer renderer = comboBox.getRenderer();
+ ComboBoxModel model = comboBox.getModel();
+ int w = displaySize.width;
+ int h = displaySize.height;
+ // TODO: Optimize using prototype here.
+ for (int i = start; i <= end; ++i)
+ {
+ Component comp = renderer.getListCellRendererComponent(listBox,
+ model.getElementAt(i), -1, false, false);
+ currentValuePane.add(comp);
+ comp.setFont(comboBox.getFont());
+ Dimension dim = comp.getPreferredSize();
+ w = Math.max(w, dim.width);
+ h = Math.max(h, dim.height);
+ currentValuePane.remove(comp);
+ }
+ if (displaySize.width < w || displaySize.height < h)
+ {
+ if (displaySize.width < w)
+ displaySize.width = w;
+ if (displaySize.height < h)
+ displaySize.height = h;
+ comboBox.revalidate();
+ if (editor != null)
+ {
+ comboBox.configureEditor(comboBox.getEditor(),
+ comboBox.getSelectedItem());
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Invoked when items are removed from the JComboBox's
+ * data model.
+ *
+ * @param e ListDataEvent describing the change.
+ */
+ public void intervalRemoved(ListDataEvent e)
+ {
+ contentsChanged(e);
+ }
+ }
+
+ /**
+ * Handles {@link PropertyChangeEvent}s fired by the {@link JComboBox}.
+ */
+ public class PropertyChangeHandler extends Object
+ implements PropertyChangeListener
+ {
+ /**
+ * Creates a new instance.
+ */
+ public PropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked whenever bound property of JComboBox changes.
+ *
+ * @param e the event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // Lets assume every change invalidates the minimumsize.
+ String propName = e.getPropertyName();
+ if (propName.equals("enabled"))
+ {
+ boolean enabled = comboBox.isEnabled();
+ if (editor != null)
+ editor.setEnabled(enabled);
+ if (arrowButton != null)
+ arrowButton.setEnabled(enabled);
+
+ comboBox.repaint();
+ }
+ else if (propName.equals("editor") && comboBox.isEditable())
+ {
+ addEditor();
+ comboBox.revalidate();
+ }
+ else if (e.getPropertyName().equals("editable"))
+ {
+ if (comboBox.isEditable())
+ {
+ addEditor();
+ }
+ else
+ {
+ removeEditor();
+ }
+
+ comboBox.revalidate();
+ }
+ else if (propName.equals("model"))
+ {
+ // remove ListDataListener from old model and add it to new model
+ ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
+ if (oldModel != null && listDataListener != null)
+ oldModel.removeListDataListener(listDataListener);
+
+ ComboBoxModel newModel = (ComboBoxModel) e.getNewValue();
+ if (newModel != null && listDataListener != null)
+ comboBox.getModel().addListDataListener(listDataListener);
+
+ if (editor != null)
+ {
+ comboBox.configureEditor(comboBox.getEditor(),
+ comboBox.getSelectedItem());
+ }
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ comboBox.repaint();
+ }
+ else if (propName.equals("font"))
+ {
+ Font font = (Font) e.getNewValue();
+ if (editor != null)
+ {
+ editor.setFont(font);
+ }
+ listBox.setFont(font);
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ }
+ else if (propName.equals("prototypeDisplayValue"))
+ {
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ }
+ else if (propName.equals("renderer"))
+ {
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ }
+ // FIXME: Need to handle changes in other bound properties.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java
new file mode 100644
index 000000000..3e54ca7c6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java
@@ -0,0 +1,1104 @@
+/* BasicComboPopup.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionAdapter;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.ComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JList;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.ListCellRenderer;
+import javax.swing.ListSelectionModel;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+
+/**
+ * UI Delegate for ComboPopup
+ *
+ * @author Olga Rodimina
+ */
+public class BasicComboPopup extends JPopupMenu implements ComboPopup
+{
+ /* Timer for autoscrolling */
+ protected Timer autoscrollTimer;
+
+ /** ComboBox associated with this popup */
+ protected JComboBox comboBox;
+
+ /** FIXME: Need to document */
+ protected boolean hasEntered;
+
+ /**
+ * Indicates whether the scroll bar located in popup menu with comboBox's
+ * list of items is currently autoscrolling. This happens when mouse event
+ * originated in the combo box and is dragged outside of its bounds
+ */
+ protected boolean isAutoScrolling;
+
+ /** ItemListener listening to the selection changes in the combo box */
+ protected ItemListener itemListener;
+
+ /** This listener is not used */
+ protected KeyListener keyListener;
+
+ /** JList which is used to display item is the combo box */
+ protected JList list;
+
+ /** This listener is not used */
+ protected ListDataListener listDataListener;
+
+ /**
+ * MouseListener listening to mouse events occuring in the combo box's
+ * list.
+ */
+ protected MouseListener listMouseListener;
+
+ /**
+ * MouseMotionListener listening to mouse motion events occuring in the
+ * combo box's list
+ */
+ protected MouseMotionListener listMouseMotionListener;
+
+ /** This listener is not used */
+ protected ListSelectionListener listSelectionListener;
+
+ /** MouseListener listening to mouse events occuring in the combo box */
+ protected MouseListener mouseListener;
+
+ /**
+ * MouseMotionListener listening to mouse motion events occuring in the
+ * combo box
+ */
+ protected MouseMotionListener mouseMotionListener;
+
+ /**
+ * PropertyChangeListener listening to changes occuring in the bound
+ * properties of the combo box
+ */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** direction for scrolling down list of combo box's items */
+ protected static final int SCROLL_DOWN = 1;
+
+ /** direction for scrolling up list of combo box's items */
+ protected static final int SCROLL_UP = 0;
+
+ /** Indicates auto scrolling direction */
+ protected int scrollDirection;
+
+ /** JScrollPane that contains list portion of the combo box */
+ protected JScrollPane scroller;
+
+ /** This field is not used */
+ protected boolean valueIsAdjusting;
+
+ /**
+ * Creates a new BasicComboPopup object.
+ *
+ * @param comboBox the combo box with which this popup should be associated
+ */
+ public BasicComboPopup(JComboBox comboBox)
+ {
+ this.comboBox = comboBox;
+ mouseListener = createMouseListener();
+ mouseMotionListener = createMouseMotionListener();
+ keyListener = createKeyListener();
+
+ list = createList();
+ configureList();
+ scroller = createScroller();
+ configureScroller();
+ configurePopup();
+ installComboBoxListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * This method displays drow down list of combo box items on the screen.
+ */
+ public void show()
+ {
+ Dimension size = comboBox.getSize();
+ size.height = getPopupHeightForRowCount(comboBox.getMaximumRowCount());
+ Insets i = getInsets();
+ size.width -= i.left + i.right;
+ Rectangle bounds = computePopupBounds(0, comboBox.getBounds().height,
+ size.width, size.height);
+
+ scroller.setMaximumSize(bounds.getSize());
+ scroller.setPreferredSize(bounds.getSize());
+ scroller.setMinimumSize(bounds.getSize());
+ list.invalidate();
+
+ syncListSelection();
+
+ list.ensureIndexIsVisible(list.getSelectedIndex());
+ setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
+ show(comboBox, bounds.x, bounds.y);
+ }
+
+ /**
+ * This method hides drop down list of items
+ */
+ public void hide()
+ {
+ MenuSelectionManager menuSelectionManager =
+ MenuSelectionManager.defaultManager();
+ javax.swing.MenuElement[] menuElements =
+ menuSelectionManager.getSelectedPath();
+ for (int i = 0; i < menuElements.length; i++)
+ {
+ if (menuElements[i] == this)
+ {
+ menuSelectionManager.clearSelectedPath();
+ break;
+ }
+ }
+ comboBox.repaint();
+ }
+
+ /**
+ * Return list cointaining JComboBox's items
+ *
+ * @return list cointaining JComboBox's items
+ */
+ public JList getList()
+ {
+ return list;
+ }
+
+ /**
+ * Returns MouseListener that is listening to mouse events occuring in the
+ * combo box.
+ *
+ * @return MouseListener
+ */
+ public MouseListener getMouseListener()
+ {
+ return mouseListener;
+ }
+
+ /**
+ * Returns MouseMotionListener that is listening to mouse motion events
+ * occuring in the combo box.
+ *
+ * @return MouseMotionListener
+ */
+ public MouseMotionListener getMouseMotionListener()
+ {
+ return mouseMotionListener;
+ }
+
+ /**
+ * Returns KeyListener listening to key events occuring in the combo box.
+ * This method returns null because KeyHandler is not longer used.
+ *
+ * @return KeyListener
+ */
+ public KeyListener getKeyListener()
+ {
+ return keyListener;
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ */
+ public void uninstallingUI()
+ {
+ if (propertyChangeListener != null)
+ {
+ comboBox.removePropertyChangeListener(propertyChangeListener);
+ }
+ if (itemListener != null)
+ {
+ comboBox.removeItemListener(itemListener);
+ }
+ uninstallComboBoxModelListeners(comboBox.getModel());
+ uninstallKeyboardActions();
+ uninstallListListeners();
+ }
+
+ /**
+ * This method uninstalls listeners that were listening to changes occuring
+ * in the comb box's data model
+ *
+ * @param model data model for the combo box from which to uninstall
+ * listeners
+ */
+ protected void uninstallComboBoxModelListeners(ComboBoxModel model)
+ {
+ model.removeListDataListener(listDataListener);
+ }
+
+ /**
+ * This method uninstalls keyboard actions installed by the UI.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method fires PopupMenuEvent indicating that combo box's popup list
+ * of items will become visible
+ */
+ protected void firePopupMenuWillBecomeVisible()
+ {
+ PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].popupMenuWillBecomeVisible(new PopupMenuEvent(comboBox));
+ }
+
+ /**
+ * This method fires PopupMenuEvent indicating that combo box's popup list
+ * of items will become invisible.
+ */
+ protected void firePopupMenuWillBecomeInvisible()
+ {
+ PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].popupMenuWillBecomeInvisible(new PopupMenuEvent(comboBox));
+ }
+
+ /**
+ * This method fires PopupMenuEvent indicating that combo box's popup list
+ * of items was closed without selection.
+ */
+ protected void firePopupMenuCanceled()
+ {
+ PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].popupMenuCanceled(new PopupMenuEvent(comboBox));
+ }
+
+ /**
+ * Creates MouseListener to listen to mouse events occuring in the combo
+ * box. Note that this listener doesn't listen to mouse events occuring in
+ * the popup portion of the combo box, it only listens to main combo box
+ * part.
+ *
+ * @return new MouseMotionListener that listens to mouse events occuring in
+ * the combo box
+ */
+ protected MouseListener createMouseListener()
+ {
+ return new InvocationMouseHandler();
+ }
+
+ /**
+ * Create Mouse listener that listens to mouse dragging events occuring in
+ * the combo box. This listener is responsible for changing the selection
+ * in the combo box list to the component over which mouse is being
+ * currently dragged
+ *
+ * @return new MouseMotionListener that listens to mouse dragging events
+ * occuring in the combo box
+ */
+ protected MouseMotionListener createMouseMotionListener()
+ {
+ return new InvocationMouseMotionHandler();
+ }
+
+ /**
+ * KeyListener created in this method is not used anymore.
+ *
+ * @return KeyListener that does nothing
+ */
+ protected KeyListener createKeyListener()
+ {
+ return new InvocationKeyHandler();
+ }
+
+ /**
+ * ListSelectionListener created in this method is not used anymore
+ *
+ * @return ListSelectionListener that does nothing
+ */
+ protected ListSelectionListener createListSelectionListener()
+ {
+ return new ListSelectionHandler();
+ }
+
+ /**
+ * Creates ListDataListener. This method returns null, because
+ * ListDataHandler class is obsolete and is no longer used.
+ *
+ * @return null
+ */
+ protected ListDataListener createListDataListener()
+ {
+ return null;
+ }
+
+ /**
+ * This method creates ListMouseListener to listen to mouse events occuring
+ * in the combo box's item list.
+ *
+ * @return MouseListener to listen to mouse events occuring in the combo
+ * box's items list.
+ */
+ protected MouseListener createListMouseListener()
+ {
+ return new ListMouseHandler();
+ }
+
+ /**
+ * Creates ListMouseMotionlistener to listen to mouse motion events occuring
+ * in the combo box's list. This listener is responsible for highlighting
+ * items in the list when mouse is moved over them.
+ *
+ * @return MouseMotionListener that handles mouse motion events occuring in
+ * the list of the combo box.
+ */
+ protected MouseMotionListener createListMouseMotionListener()
+ {
+ return new ListMouseMotionHandler();
+ }
+
+ /**
+ * Creates PropertyChangeListener to handle changes in the JComboBox's bound
+ * properties.
+ *
+ * @return PropertyChangeListener to handle changes in the JComboBox's bound
+ * properties.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates new ItemListener that will listen to ItemEvents occuring in the
+ * combo box.
+ *
+ * @return ItemListener to listen to ItemEvents occuring in the combo box.
+ */
+ protected ItemListener createItemListener()
+ {
+ return new ItemHandler();
+ }
+
+ /**
+ * Creates JList that will be used to display items in the combo box.
+ *
+ * @return JList that will be used to display items in the combo box.
+ */
+ protected JList createList()
+ {
+ JList l = new JList(comboBox.getModel());
+ return l;
+ }
+
+ /**
+ * This method configures the list of comboBox's items by setting default
+ * properties and installing listeners.
+ */
+ protected void configureList()
+ {
+ list.setFont(comboBox.getFont());
+ list.setForeground(comboBox.getForeground());
+ list.setBackground(comboBox.getBackground());
+ Color sfg = UIManager.getColor("ComboBox.selectionForeground");
+ list.setSelectionForeground(sfg);
+ Color sbg = UIManager.getColor("ComboBox.selectionBackground");
+ list.setSelectionBackground(sbg);
+ list.setBorder(null);
+ list.setCellRenderer(comboBox.getRenderer());
+ list.setFocusable(false);
+ list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ installListListeners();
+ }
+
+ /**
+ * This method installs list listeners.
+ */
+ protected void installListListeners()
+ {
+ // mouse listener listening to mouse events occuring in the
+ // combo box's list of items.
+ listMouseListener = createListMouseListener();
+ list.addMouseListener(listMouseListener);
+
+ // mouse listener listening to mouse motion events occuring in the
+ // combo box's list of items
+ listMouseMotionListener = createListMouseMotionListener();
+ list.addMouseMotionListener(listMouseMotionListener);
+
+ listSelectionListener = createListSelectionListener();
+ list.addListSelectionListener(listSelectionListener);
+ }
+
+ /**
+ * This method creates scroll pane that will contain the list of comboBox's
+ * items inside of it.
+ *
+ * @return JScrollPane
+ */
+ protected JScrollPane createScroller()
+ {
+ return new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ }
+
+ /**
+ * This method configures scroll pane to contain list of comboBox's items
+ */
+ protected void configureScroller()
+ {
+ scroller.setBorder(null);
+ scroller.setFocusable(false);
+ scroller.getVerticalScrollBar().setFocusable(false);
+ }
+
+ /**
+ * This method configures popup menu that will be used to display Scrollpane
+ * with list of items inside of it.
+ */
+ protected void configurePopup()
+ {
+ setBorderPainted(true);
+ setBorder(BorderFactory.createLineBorder(Color.BLACK));
+ setOpaque(false);
+ add(scroller);
+ setFocusable(false);
+ }
+
+ /*
+ * This method installs listeners that will listen to changes occuring
+ * in the combo box.
+ */
+ protected void installComboBoxListeners()
+ {
+ // item listener listenening to selection events in the combo box
+ itemListener = createItemListener();
+ comboBox.addItemListener(itemListener);
+
+ propertyChangeListener = createPropertyChangeListener();
+ comboBox.addPropertyChangeListener(propertyChangeListener);
+
+ installComboBoxModelListeners(comboBox.getModel());
+ }
+
+ /**
+ * This method installs listeners that will listen to changes occuring in
+ * the comb box's data model
+ *
+ * @param model data model for the combo box for which to install listeners
+ */
+ protected void installComboBoxModelListeners(ComboBoxModel model)
+ {
+ // list data listener to listen for ListDataEvents in combo box.
+ // This listener is now obsolete and nothing is done here
+ listDataListener = createListDataListener();
+ comboBox.getModel().addListDataListener(listDataListener);
+ }
+
+ /**
+ * Installs the keyboard actions.
+ */
+ protected void installKeyboardActions()
+ {
+ // Nothing to do here
+ }
+
+ /**
+ * This method always returns false to indicate that items in the combo box
+ * list are not focus traversable.
+ *
+ * @return false
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * This method start scrolling combo box's list of items either up or down
+ * depending on the specified 'direction'
+ *
+ * @param direction of the scrolling.
+ */
+ protected void startAutoScrolling(int direction)
+ {
+ // FIXME: add timer
+ isAutoScrolling = true;
+
+ if (direction == SCROLL_UP)
+ autoScrollUp();
+ else
+ autoScrollDown();
+ }
+
+ /**
+ * This method stops scrolling the combo box's list of items
+ */
+ protected void stopAutoScrolling()
+ {
+ // FIXME: add timer
+ isAutoScrolling = false;
+ }
+
+ /**
+ * This method scrolls up list of combo box's items up and highlights that
+ * just became visible.
+ */
+ protected void autoScrollUp()
+ {
+ // scroll up the scroll bar to make the item above visible
+ JScrollBar scrollbar = scroller.getVerticalScrollBar();
+ int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
+ SwingConstants.VERTICAL,
+ SCROLL_UP);
+
+ scrollbar.setValue(scrollbar.getValue() - scrollToNext);
+
+ // If we haven't reached the begging of the combo box's list of items,
+ // then highlight next element above currently highlighted element
+ if (list.getSelectedIndex() != 0)
+ list.setSelectedIndex(list.getSelectedIndex() - 1);
+ }
+
+ /**
+ * This method scrolls down list of combo box's and highlights item in the
+ * list that just became visible.
+ */
+ protected void autoScrollDown()
+ {
+ // scroll scrollbar down to make next item visible
+ JScrollBar scrollbar = scroller.getVerticalScrollBar();
+ int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
+ SwingConstants.VERTICAL,
+ SCROLL_DOWN);
+ scrollbar.setValue(scrollbar.getValue() + scrollToNext);
+
+ // If we haven't reached the end of the combo box's list of items
+ // then highlight next element below currently highlighted element
+ if (list.getSelectedIndex() + 1 != comboBox.getItemCount())
+ list.setSelectedIndex(list.getSelectedIndex() + 1);
+ }
+
+ /**
+ * This method helps to delegate focus to the right component in the
+ * JComboBox. If the comboBox is editable then focus is sent to
+ * ComboBoxEditor, otherwise it is delegated to JComboBox.
+ *
+ * @param e MouseEvent
+ */
+ protected void delegateFocus(MouseEvent e)
+ {
+ if (comboBox.isEditable())
+ comboBox.getEditor().getEditorComponent().requestFocus();
+ else
+ comboBox.requestFocus();
+ }
+
+ /**
+ * This method displays combo box popup if the popup is not currently shown
+ * on the screen and hides it if it is currently visible
+ */
+ protected void togglePopup()
+ {
+ if (isVisible())
+ hide();
+ else
+ show();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param e DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected MouseEvent convertMouseEvent(MouseEvent e)
+ {
+ Point point = SwingUtilities.convertPoint((Component) e.getSource(),
+ e.getPoint(), list);
+ MouseEvent newEvent = new MouseEvent((Component) e.getSource(),
+ e.getID(), e.getWhen(),
+ e.getModifiers(), point.x, point.y,
+ e.getModifiers(),
+ e.isPopupTrigger());
+ return newEvent;
+ }
+
+ /**
+ * Returns required height of the popup such that number of items visible in
+ * it are equal to the maximum row count. By default
+ * comboBox.maximumRowCount=8
+ *
+ * @param maxRowCount number of maximum visible rows in the combo box's
+ * popup list of items
+ *
+ * @return height of the popup required to fit number of items equal to
+ * JComboBox.maximumRowCount.
+ */
+ protected int getPopupHeightForRowCount(int maxRowCount)
+ {
+ int totalHeight = 0;
+ ListCellRenderer rend = list.getCellRenderer();
+
+ if (comboBox.getItemCount() < maxRowCount)
+ maxRowCount = comboBox.getItemCount();
+
+ for (int i = 0; i < maxRowCount; i++)
+ {
+ Component comp = rend.getListCellRendererComponent(list,
+ comboBox.getModel()
+ .getElementAt(i),
+ -1, false, false);
+ Dimension dim = comp.getPreferredSize();
+ totalHeight += dim.height;
+ }
+
+ return totalHeight == 0 ? 100 : totalHeight;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param px DOCUMENT ME!
+ * @param py DOCUMENT ME!
+ * @param pw DOCUMENT ME!
+ * @param ph DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected Rectangle computePopupBounds(int px, int py, int pw, int ph)
+ {
+ return new Rectangle(px, py, pw, ph);
+ }
+
+ /**
+ * This method changes the selection in the list to the item over which the
+ * mouse is currently located.
+ *
+ * @param anEvent MouseEvent
+ * @param shouldScroll DOCUMENT ME!
+ */
+ protected void updateListBoxSelectionForEvent(MouseEvent anEvent,
+ boolean shouldScroll)
+ {
+ Point point = anEvent.getPoint();
+ if (list != null)
+ {
+ int index = list.locationToIndex(point);
+ if (index == -1)
+ {
+ if (point.y < 0)
+ index = 0;
+ else
+ index = comboBox.getModel().getSize() - 1;
+ }
+ if (list.getSelectedIndex() != index)
+ {
+ list.setSelectedIndex(index);
+ if (shouldScroll)
+ list.ensureIndexIsVisible(index);
+ }
+ }
+ }
+
+ /**
+ * InvocationMouseHandler is a listener that listens to mouse events
+ * occuring in the combo box. Note that this listener doesn't listen to
+ * mouse events occuring in the popup portion of the combo box, it only
+ * listens to main combo box part(area that displays selected item). This
+ * listener is responsible for showing and hiding popup portion of the
+ * combo box.
+ */
+ protected class InvocationMouseHandler extends MouseAdapter
+ {
+ /**
+ * Creates a new InvocationMouseHandler object.
+ */
+ protected InvocationMouseHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked whenever mouse is being pressed over the main
+ * part of the combo box. This method will show popup if the popup is
+ * not shown on the screen right now, and it will hide popup otherwise.
+ *
+ * @param e MouseEvent that should be handled
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (SwingUtilities.isLeftMouseButton(e) && comboBox.isEnabled())
+ {
+ delegateFocus(e);
+ togglePopup();
+ }
+ }
+
+ /**
+ * This method is invoked whenever mouse event was originated in the combo
+ * box and released either in the combBox list of items or in the combo
+ * box itself.
+ *
+ * @param e MouseEvent that should be handled
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ Component component = (Component) e.getSource();
+ Dimension size = component.getSize();
+ Rectangle bounds = new Rectangle(0, 0, size.width - 1, size.height - 1);
+ // If mouse was released inside the bounds of combo box then do nothing,
+ // Otherwise if mouse was released inside the list of combo box items
+ // then change selection and close popup
+ if (! bounds.contains(e.getPoint()))
+ {
+ MouseEvent convEvent = convertMouseEvent(e);
+ Point point = convEvent.getPoint();
+ Rectangle visRect = new Rectangle();
+ list.computeVisibleRect(visRect);
+ if (visRect.contains(point))
+ {
+ updateListBoxSelectionForEvent(convEvent, false);
+ comboBox.setSelectedIndex(list.getSelectedIndex());
+ }
+ hide();
+ }
+ hasEntered = false;
+ stopAutoScrolling();
+ }
+ }
+
+ /**
+ * InvocationMouseMotionListener is a mouse listener that listens to mouse
+ * dragging events occuring in the combo box.
+ */
+ protected class InvocationMouseMotionHandler extends MouseMotionAdapter
+ {
+ /**
+ * Creates a new InvocationMouseMotionHandler object.
+ */
+ protected InvocationMouseMotionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is responsible for highlighting item in the drop down list
+ * over which the mouse is currently being dragged.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ if (isVisible())
+ {
+ MouseEvent convEvent = convertMouseEvent(e);
+ Rectangle visRect = new Rectangle();
+ list.computeVisibleRect(visRect);
+ if (convEvent.getPoint().y >= visRect.y
+ && (convEvent.getPoint().y <= visRect.y + visRect.height - 1))
+ {
+ hasEntered = true;
+ if (isAutoScrolling)
+ stopAutoScrolling();
+ Point point = convEvent.getPoint();
+ if (visRect.contains(point))
+ {
+ valueIsAdjusting = true;
+ updateListBoxSelectionForEvent(convEvent, false);
+ valueIsAdjusting = false;
+ }
+ }
+ else if (hasEntered)
+ {
+ int dir = convEvent.getPoint().y < visRect.y ? SCROLL_UP
+ : SCROLL_DOWN;
+ if (isAutoScrolling && scrollDirection != dir)
+ {
+ stopAutoScrolling();
+ startAutoScrolling(dir);
+ }
+ else if (!isAutoScrolling)
+ startAutoScrolling(dir);
+ }
+ else if (e.getPoint().y < 0)
+ {
+ hasEntered = true;
+ startAutoScrolling(SCROLL_UP);
+ }
+ }
+ }
+ }
+
+ /**
+ * ItemHandler is an item listener that listens to selection events occuring
+ * in the combo box. FIXME: should specify here what it does when item is
+ * selected or deselected in the combo box list.
+ */
+ protected class ItemHandler extends Object implements ItemListener
+ {
+ /**
+ * Creates a new ItemHandler object.
+ */
+ protected ItemHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method responds to the selection events occuring in the combo box.
+ *
+ * @param e ItemEvent specifying the combo box's selection
+ */
+ public void itemStateChanged(ItemEvent e)
+ {
+ if (e.getStateChange() == ItemEvent.SELECTED && ! valueIsAdjusting)
+ {
+ valueIsAdjusting = true;
+ syncListSelection();
+ valueIsAdjusting = false;
+ list.ensureIndexIsVisible(comboBox.getSelectedIndex());
+ }
+ }
+ }
+
+ /**
+ * ListMouseHandler is a listener that listens to mouse events occuring in
+ * the combo box's list of items. This class is responsible for hiding
+ * popup portion of the combo box if the mouse is released inside the combo
+ * box's list.
+ */
+ protected class ListMouseHandler extends MouseAdapter
+ {
+ protected ListMouseHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void mousePressed(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseReleased(MouseEvent anEvent)
+ {
+ comboBox.setSelectedIndex(list.getSelectedIndex());
+ hide();
+ }
+ }
+
+ /**
+ * ListMouseMotionHandler listens to mouse motion events occuring in the
+ * combo box's list. This class is responsible for highlighting items in
+ * the list when mouse is moved over them
+ */
+ protected class ListMouseMotionHandler extends MouseMotionAdapter
+ {
+ protected ListMouseMotionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseMoved(MouseEvent anEvent)
+ {
+ Point point = anEvent.getPoint();
+ Rectangle visRect = new Rectangle();
+ list.computeVisibleRect(visRect);
+ if (visRect.contains(point))
+ {
+ valueIsAdjusting = true;
+ updateListBoxSelectionForEvent(anEvent, false);
+ valueIsAdjusting = false;
+ }
+ }
+ }
+
+ /**
+ * This class listens to changes occuring in the bound properties of the
+ * combo box
+ */
+ protected class PropertyChangeHandler extends Object
+ implements PropertyChangeListener
+ {
+ protected PropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("renderer"))
+ {
+ list.setCellRenderer(comboBox.getRenderer());
+ if (isVisible())
+ hide();
+ }
+ if (e.getPropertyName().equals("model"))
+ {
+ ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
+ uninstallComboBoxModelListeners(oldModel);
+ ComboBoxModel newModel = (ComboBoxModel) e.getNewValue();
+ list.setModel(newModel);
+ installComboBoxModelListeners(newModel);
+ if (comboBox.getItemCount() > 0)
+ comboBox.setSelectedIndex(0);
+ if (isVisible())
+ hide();
+ }
+ }
+ }
+
+ // ------ private helper methods --------------------
+
+ /**
+ * This method uninstalls Listeners registered with combo boxes list of
+ * items
+ */
+ private void uninstallListListeners()
+ {
+ list.removeMouseListener(listMouseListener);
+ listMouseListener = null;
+
+ list.removeMouseMotionListener(listMouseMotionListener);
+ listMouseMotionListener = null;
+ }
+
+ void syncListSelection()
+ {
+ int index = comboBox.getSelectedIndex();
+ if (index == -1)
+ list.clearSelection();
+ else
+ list.setSelectedIndex(index);
+ }
+
+ // --------------------------------------------------------------------
+ // The following classes are here only for backwards API compatibility
+ // They aren't used.
+ // --------------------------------------------------------------------
+
+ /**
+ * This class is not used any more.
+ */
+ public class ListDataHandler extends Object implements ListDataListener
+ {
+ public ListDataHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void contentsChanged(ListDataEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void intervalAdded(ListDataEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void intervalRemoved(ListDataEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This class is not used anymore
+ */
+ protected class ListSelectionHandler extends Object
+ implements ListSelectionListener
+ {
+ protected ListSelectionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void valueChanged(ListSelectionEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This class is not used anymore
+ */
+ public class InvocationKeyHandler extends KeyAdapter
+ {
+ public InvocationKeyHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void keyReleased(KeyEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java
new file mode 100644
index 000000000..e52293812
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java
@@ -0,0 +1,592 @@
+/* BasicDesktopIconUI.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JInternalFrame.JDesktopIcon;
+import javax.swing.SwingConstants;
+import javax.swing.border.Border;
+import javax.swing.event.MouseInputAdapter;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.DesktopIconUI;
+
+/**
+ * This class acts as the UI delegate for JDesktopIcons for the Basic look and feel.
+ */
+public class BasicDesktopIconUI extends DesktopIconUI
+{
+ /**
+ * This helper class handles mouse events that occur on the JDesktopIcon.
+ */
+ public class MouseInputHandler extends MouseInputAdapter
+ {
+ /** The x offset from the MouseEvent coordinates to the top left corner. */
+ private transient int xOffset;
+
+ /** The y offset fromt he MouseEvent coordinates to the top left corner. */
+ private transient int yOffset;
+
+ /** A cached value of the JDesktopPane that parents this JDesktopIcon. */
+ private transient JDesktopPane pane;
+
+ /**
+ * This method is called when the mouse is dragged in the JDesktopIcon.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ Rectangle b = desktopIcon.getBounds();
+
+ moveAndRepaint(desktopIcon, b.x + e.getX() - xOffset,
+ b.y + e.getY() - yOffset, b.width, b.height);
+ }
+
+ /**
+ * This method is called when the mouse is moved in the JDesktopIcon.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * This method is called when the mouse is pressed in the JDesktopIcon.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ xOffset = e.getX();
+ yOffset = e.getY();
+ pane = frame.getDesktopPane();
+ if (pane != null)
+ pane.getDesktopManager().beginDraggingFrame(desktopIcon);
+ }
+
+ /**
+ * This method is called when the mouse is released in the JDesktopIcon.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (pane != null)
+ pane.getDesktopManager().endDraggingFrame(desktopIcon);
+ xOffset = 0;
+ yOffset = 0;
+ }
+
+ /**
+ * This method moves and repaints the JDesktopIcon to the given bounds.
+ *
+ * @param f The JComponent to move and repaint.
+ * @param newX The new x coordinate.
+ * @param newY The new y coordinate.
+ * @param newWidth The new width.
+ * @param newHeight The new height.
+ */
+ public void moveAndRepaint(JComponent f, int newX, int newY, int newWidth,
+ int newHeight)
+ {
+ if (pane != null)
+ pane.getDesktopManager().dragFrame(f, newX, newY);
+ else
+ desktopIcon.setBounds(newX, newY, newWidth, newHeight);
+ }
+ }
+
+ /**
+ * This class acts as the border for the JDesktopIcon.
+ */
+ private class DesktopIconBorder implements Border
+ {
+ /** The left inset value. */
+ int left = 10;
+
+ /** The top inset value. */
+ int top = 4;
+
+ /** The right inset value. */
+ int right = top;
+
+ /** The bottom inset value. */
+ int bottom = top;
+
+ /**
+ * This method returns the insets of the border.
+ *
+ * @param c The Component to find border insets for.
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(top, left, bottom, right);
+ }
+
+ /**
+ * This method returns whether the border is opaque.
+ *
+ * @return Whether the border is opaque.
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * This method paints the border.
+ *
+ * @param c The Component the border is in.
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate of the Component.
+ * @param y The y coordinate of the Component.
+ * @param width The width of the Component.
+ * @param height The height of the Component.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+
+ g.setColor(Color.LIGHT_GRAY);
+
+ g.fillRect(0, 0, left, height);
+ g.fillRect(0, 0, width, top);
+ g.fillRect(0, height - bottom, width, bottom);
+ g.fillRect(width - right, 0, right, height);
+
+ g.setColor(Color.BLACK);
+ g.drawRect(0, 0, width - 1, height - 1);
+
+ int fHeight = height / 4;
+ int hLeft = left / 2;
+
+ g.setColor(Color.BLACK);
+ g.fillRect(hLeft, fHeight, 2, 2);
+ g.fillRect(hLeft, fHeight * 2, 2, 2);
+ g.fillRect(hLeft, fHeight * 3, 2, 2);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ }
+
+ /** The static width and height of the iconSize. */
+ private static final int iconSize = 16;
+
+ /**
+ * This class represents the default frame icon when none
+ * is supplied by the JInternalFrame.
+ */
+ static class InternalFrameDefaultMenuIcon implements Icon
+ {
+ /**
+ * This returns the icon height.
+ *
+ * @return The icon height.
+ */
+ public int getIconHeight()
+ {
+ return iconSize;
+ }
+
+ /**
+ * This returns the icon width.
+ *
+ * @return The icon width.
+ */
+ public int getIconWidth()
+ {
+ return iconSize;
+ }
+
+ /**
+ * This method paints the icon.
+ *
+ * @param c The Component this icon belongs to.
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate to paint at.
+ * @param y The y coordinate to paint at.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+
+ g.setColor(Color.BLUE);
+ g.fillRect(0, 0, iconSize, (int) ((double) iconSize / 3) + 1);
+
+ g.setColor(Color.WHITE);
+ g.fillRect(0, (int) ((double) iconSize / 3), iconSize, iconSize * 5 / 6);
+
+ g.setColor(Color.GRAY);
+ g.drawRect(0, 0, iconSize, iconSize);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ }
+
+ /** The default JDesktopIcon width. */
+ private static final int iconWidth = 160;
+
+ /** The default JDesktopIcon height */
+ private static final int iconHeight = 35;
+
+ /** The JDesktopIcon this UI delegate represents. */
+ protected JDesktopIcon desktopIcon;
+
+ /** The JInternalFrame associated with the JDesktopIcon. */
+ protected JInternalFrame frame;
+
+ /** The MouseListener responsible for reacting to MouseEvents on the JDesktopIcon. */
+ private transient MouseInputListener mouseHandler;
+
+ /** The Button in the JDesktopIcon responsible for deiconifying it.
+ * This is package-private to avoid an accessor method. */
+ transient BoundButton button;
+
+ /** The PropertyChangeListener listening to the JDesktopIcon. */
+ private transient PropertyChangeListener propertyHandler;
+
+ /** The default icon used when no frame icon is given by the JInternalFrame. */
+ static Icon defaultIcon = new InternalFrameDefaultMenuIcon();
+
+ /**
+ * This is a helper class that is used in JDesktopIcon and gives the Button a predetermined size.
+ */
+ private class BoundButton extends JButton
+ {
+ /**
+ * Creates a new BoundButton object.
+ *
+ * @param title The title of the button.
+ */
+ public BoundButton(String title)
+ {
+ super(title);
+ }
+
+ /**
+ * This method returns a standard size (based on the defaults of the JDesktopIcon) and the insets.
+ *
+ * @return The preferred size of the JDesktopIcon.
+ */
+ public Dimension getPreferredSize()
+ {
+ Insets insets = desktopIcon.getInsets();
+ return new Dimension(iconWidth - insets.left - insets.right,
+ iconHeight - insets.top - insets.bottom);
+ }
+
+ /**
+ * This method returns the minimum size of the button.
+ *
+ * @return The minimum size of the button.
+ */
+ public Dimension getMinimumSize()
+ {
+ return getPreferredSize();
+ }
+
+ /**
+ * This method returns the maximum size of the button.
+ *
+ * @return The maximum size of the button.
+ */
+ public Dimension getMaximumSize()
+ {
+ return getPreferredSize();
+ }
+ }
+
+ /**
+ * Creates a new BasicDesktopIconUI object.
+ */
+ public BasicDesktopIconUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method creates a new BasicDesktopIconUI for the given JComponent.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicDesktopIconUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicDesktopIconUI();
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install this UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JDesktopIcon)
+ {
+ desktopIcon = (JDesktopIcon) c;
+ desktopIcon.setLayout(new BorderLayout());
+ frame = desktopIcon.getInternalFrame();
+
+ installDefaults();
+ installComponents();
+ installListeners();
+
+ desktopIcon.setOpaque(true);
+ }
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall this UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ desktopIcon.setOpaque(false);
+
+ uninstallListeners();
+ uninstallComponents();
+ uninstallDefaults();
+
+ frame = null;
+ desktopIcon.setLayout(null);
+ desktopIcon = null;
+ }
+
+ /**
+ * This method installs the necessary sub components for the JDesktopIcon.
+ */
+ protected void installComponents()
+ {
+ // Try to create a button based on what the frame's
+ // state is currently
+ button = new BoundButton(frame.getTitle());
+ button.setHorizontalAlignment(SwingConstants.LEFT);
+ button.setHorizontalTextPosition(SwingConstants.TRAILING);
+
+ Icon use = frame.getFrameIcon();
+ if (use == null)
+ use = defaultIcon;
+ button.setIcon(use);
+
+ desktopIcon.add(button, SwingConstants.CENTER);
+ }
+
+ /**
+ * This method uninstalls the sub components for the JDesktopIcon.
+ */
+ protected void uninstallComponents()
+ {
+ desktopIcon.remove(button);
+
+ button = null;
+ }
+
+ /**
+ * This method installs the listeners needed by this UI.
+ */
+ protected void installListeners()
+ {
+ mouseHandler = createMouseInputListener();
+
+ desktopIcon.addMouseMotionListener(mouseHandler);
+ desktopIcon.addMouseListener(mouseHandler);
+
+ propertyHandler = new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(JInternalFrame.TITLE_PROPERTY))
+ button.setText(desktopIcon.getInternalFrame().getTitle());
+ else if (e.getPropertyName().equals(JInternalFrame.FRAME_ICON_PROPERTY))
+ {
+ Icon use = desktopIcon.getInternalFrame().getFrameIcon();
+ if (use == null)
+ use = defaultIcon;
+ button.setIcon(use);
+ }
+ desktopIcon.revalidate();
+ desktopIcon.repaint();
+ }
+ };
+ frame.addPropertyChangeListener(propertyHandler);
+
+ button.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ deiconize();
+ }
+ });
+ }
+
+ /**
+ * This method uninstalls the listeners needed by the UI.
+ */
+ protected void uninstallListeners()
+ {
+ // button is nulled so no need to remove it.
+
+ frame.removePropertyChangeListener(propertyHandler);
+ propertyHandler = null;
+
+ desktopIcon.removeMouseMotionListener(mouseHandler);
+ desktopIcon.removeMouseListener(mouseHandler);
+ }
+
+ /**
+ * This method installs the defaults for the JDesktopIcon.
+ */
+ protected void installDefaults()
+ {
+ // FIXME: Move border to defaults.
+ desktopIcon.setBorder(new DesktopIconBorder());
+ }
+
+ /**
+ * This method uninstalls the defaults for the JDesktopIcon.
+ */
+ protected void uninstallDefaults()
+ {
+ desktopIcon.setBorder(null);
+ }
+
+ /**
+ * This method creates a new MouseInputListener for the JDesktopIcon.
+ *
+ * @return A new MouseInputListener.
+ */
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * This method returns the preferred size for the given JComponent.
+ *
+ * @param c The JComponent to find a preferred size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return new Dimension(iconWidth, iconHeight);
+ }
+
+ /**
+ * This method returns the minimum size for the given JComponent.
+ *
+ * @param c The JComponent to find a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the maximum size for the given JComponent.
+ *
+ * @param c The JComponent to find a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the insets of the given JComponent.
+ *
+ * @param c The JComponent to find insets for.
+ *
+ * @return The insets of the given JComponent.
+ */
+ public Insets getInsets(JComponent c)
+ {
+ return c.getInsets();
+ }
+
+ /**
+ * This method deiconizes the JInternalFrame associated with the JDesktopIcon.
+ */
+ public void deiconize()
+ {
+ try
+ {
+ frame.setIcon(false);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java
new file mode 100644
index 000000000..cbc3f9f89
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java
@@ -0,0 +1,470 @@
+/* BasicDesktopPaneUI.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.beans.PropertyVetoException;
+
+import javax.swing.AbstractAction;
+import javax.swing.DefaultDesktopManager;
+import javax.swing.DesktopManager;
+import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JInternalFrame;
+import javax.swing.KeyStroke;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.DesktopPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * This class is the UI delegate for JDesktopPane for the Basic look and feel.
+ */
+public class BasicDesktopPaneUI extends DesktopPaneUI
+{
+ /**
+ * This helper class is used to handle key events that cause JInternalFrames
+ * to be closed.
+ */
+ protected class CloseAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (desktop.getSelectedFrame() != null)
+ {
+ try
+ {
+ desktop.getSelectedFrame().setClosed(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempts has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ if (desktop.getSelectedFrame() != null)
+ return desktop.getSelectedFrame().isClosable();
+ return false;
+ }
+ }
+
+ /**
+ * This helper class is used to handle key events that cause JInternalFrames
+ * to be maximized.
+ */
+ protected class MaximizeAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (desktop.getSelectedFrame() != null)
+ {
+ try
+ {
+ desktop.getSelectedFrame().setMaximum(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempts has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ if (desktop.getSelectedFrame() != null)
+ return desktop.getSelectedFrame().isMaximizable();
+ return false;
+ }
+ }
+
+ /**
+ * This helper class is used to handle key events that cause JInternalFrames
+ * to be minimized.
+ */
+ protected class MinimizeAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (desktop.getSelectedFrame() != null)
+ {
+ try
+ {
+ desktop.getSelectedFrame().setIcon(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ if (desktop.getSelectedFrame() != null)
+ return desktop.getSelectedFrame().isIconifiable();
+ return false;
+ }
+ }
+
+ /**
+ * This helper class is used to handle key events that pass the SELECTED
+ * property to the next JInternalFrame in the JDesktopPane's list of
+ * children.
+ */
+ protected class NavigateAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // This is supposed to set the next selected frame.
+ JInternalFrame[] frames = desktop.getAllFrames();
+ if (frames.length == 0)
+ return;
+
+ JInternalFrame sFrame = frames[0];
+ if (desktop.getSelectedFrame() != null)
+ sFrame = desktop.getSelectedFrame();
+
+ int i = 0;
+ for (; i < frames.length; i++)
+ if (frames[i] == sFrame)
+ break;
+
+ // FIXME: Navigate actions go reverse too.
+ if (i == frames.length)
+ i = 0;
+
+ desktop.setSelectedFrame(frames[i]);
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether this action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ // Always true.
+ return true;
+ }
+ }
+
+ /**
+ * This helper class is used to restore the JInternalFrame to its original
+ * size before maximizing or iconifying.
+ */
+ protected class OpenAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JInternalFrame frame = desktop.getSelectedFrame();
+ if (frame != null)
+ {
+ try
+ {
+ if (frame.isIcon())
+ frame.setIcon(false);
+ else if (frame.isMaximum())
+ frame.setMaximum(false);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether this action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ // JInternalFrames are always restorable.
+ return true;
+ }
+ }
+
+ /**
+ * The KeyStroke associated with closing JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke closeKey;
+
+ /**
+ * The KeyStroke associated with maximizing JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke maximizeKey;
+
+ /**
+ * The KeyStroke associated with minimizing JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke minimizeKey;
+
+ /**
+ * The KeyStroke associated with navigating (forward?) through
+ * JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke navigateKey;
+
+ /**
+ * The KeyStroke associated with navigating (backward?) through
+ * JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke navigateKey2;
+
+ /** The default desktop manager used with JDesktopPane. */
+ protected DesktopManager desktopManager;
+
+ /** The JDesktopPane this UI is used with. */
+ protected JDesktopPane desktop;
+
+ /**
+ * Creates a new BasicDesktopPaneUI object.
+ */
+ public BasicDesktopPaneUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method creates a BasicDesktopPaneUI for the given JComponent.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicDesktopPaneUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicDesktopPaneUI();
+ }
+
+ /**
+ * This method returns the maximum size for the given JComponent.
+ *
+ * @param c The JComponent to find a maximum size for.
+ *
+ * @return The maximum size for the given JComponent.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the minimum size for the given JComponent.
+ *
+ * @param c The JComponent to find a minimum size for.
+ *
+ * @return The minimum size for the given JComponent.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the preferred size for the given JComponent.
+ *
+ * @param c The JComponent to find a preferred size for.
+ *
+ * @return The preferred size for the given JComponent.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ // return null because JDesktopPanes don't have preferred sizes.
+ return null;
+ }
+
+ /**
+ * This method installs the defaults for the JDesktopPane provided by the
+ * current look and feel.
+ */
+ protected void installDefaults()
+ {
+ Color bg = desktop.getBackground();
+ if (bg == null || bg instanceof UIResource)
+ desktop.setBackground(UIManager.getColor("desktop"));
+ }
+
+ /**
+ * This method installs the desktop manager for the JDesktopPane.
+ */
+ protected void installDesktopManager()
+ {
+ desktopManager = new DefaultDesktopManager();
+ desktop.setDesktopManager(desktopManager);
+ }
+
+ /**
+ * This method installs the keyboard actions for the JDesktopPane.
+ */
+ protected void installKeyboardActions()
+ {
+ // FIXME: create actions and keystrokes.
+ registerKeyboardActions();
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install this UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JDesktopPane)
+ {
+ desktop = (JDesktopPane) c;
+
+ installDefaults();
+ installDesktopManager();
+ installKeyboardActions();
+ }
+ }
+
+ /**
+ * This method registers the actions to the appropriate Action and Input
+ * maps.
+ */
+ protected void registerKeyboardActions()
+ {
+ // FIXME: Do the binding.
+ // XXX: the gtk windows tend to intercept a lot of the
+ // key events for themselves. must figure a way past that
+ // before binding
+ }
+
+ /**
+ * This method reverses the work done by the installDefaults method.
+ */
+ protected void uninstallDefaults()
+ {
+ desktop.setBackground(null);
+ }
+
+ /**
+ * This method reverses the work done by the installDesktopManager method.
+ */
+ protected void uninstallDesktopManager()
+ {
+ desktopManager = null;
+ desktop.setDesktopManager(null);
+ }
+
+ /**
+ * This method reverses the work done by the installKeyboardActions method.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ unregisterKeyboardActions();
+ // FIXME: null the actions and keystrokes.
+ }
+
+ /**
+ * This method reverses the work done by the registerKeyboardActions method.
+ */
+ protected void unregisterKeyboardActions()
+ {
+ // FIXME: unmap the keystrokes
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent. It should reverse
+ * all the work done by the installUI method.
+ *
+ * @param c The JComponent to uninstall this UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallDesktopManager();
+ uninstallDefaults();
+
+ desktop = null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java
new file mode 100644
index 000000000..62657ad86
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java
@@ -0,0 +1,586 @@
+/* BasicDirectoryModel.java --
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import javax.swing.AbstractListModel;
+import javax.swing.JFileChooser;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ListDataEvent;
+import javax.swing.filechooser.FileSystemView;
+
+
+/**
+ * Implements an AbstractListModel for directories where the source
+ * of the files is a JFileChooser object.
+ *
+ * This class is used for sorting and ordering the file list in
+ * a JFileChooser L&F object.
+ */
+public class BasicDirectoryModel extends AbstractListModel
+ implements PropertyChangeListener
+{
+ /** The list of files itself */
+ private Vector contents;
+
+ /**
+ * The directories in the list.
+ */
+ private Vector directories;
+
+ /**
+ * The files in the list.
+ */
+ private Vector files;
+
+ /** The listing mode of the associated JFileChooser,
+ either FILES_ONLY, DIRECTORIES_ONLY or FILES_AND_DIRECTORIES */
+ private int listingMode;
+
+ /** The JFileCooser associated with this model */
+ private JFileChooser filechooser;
+
+ /**
+ * The thread that loads the file view.
+ */
+ private DirectoryLoadThread loadThread;
+
+ /**
+ * This thread is responsible for loading file lists from the
+ * current directory and updating the model.
+ */
+ private class DirectoryLoadThread extends Thread
+ {
+
+ /**
+ * Updates the Swing list model.
+ */
+ private class UpdateSwingRequest
+ implements Runnable
+ {
+
+ private List added;
+ private int addIndex;
+ private List removed;
+ private int removeIndex;
+ private boolean cancel;
+
+ UpdateSwingRequest(List add, int ai, List rem, int ri)
+ {
+ added = add;
+ addIndex = ai;
+ removed = rem;
+ removeIndex = ri;
+ cancel = false;
+ }
+
+ public void run()
+ {
+ if (! cancel)
+ {
+ int numRemoved = removed == null ? 0 : removed.size();
+ int numAdded = added == null ? 0 : added.size();
+ synchronized (contents)
+ {
+ if (numRemoved > 0)
+ contents.removeAll(removed);
+ if (numAdded > 0)
+ contents.addAll(added);
+
+ files = null;
+ directories = null;
+ }
+ if (numRemoved > 0 && numAdded == 0)
+ fireIntervalRemoved(BasicDirectoryModel.this, removeIndex,
+ removeIndex + numRemoved - 1);
+ else if (numRemoved == 0 && numAdded > 0)
+ fireIntervalAdded(BasicDirectoryModel.this, addIndex,
+ addIndex + numAdded - 1);
+ else
+ fireContentsChanged();
+ }
+ }
+
+ void cancel()
+ {
+ cancel = true;
+ }
+ }
+
+ /**
+ * The directory beeing loaded.
+ */
+ File directory;
+
+ /**
+ * Stores all UpdateSwingRequests that are sent to the event queue.
+ */
+ private UpdateSwingRequest pending;
+
+ /**
+ * Creates a new DirectoryLoadThread that loads the specified
+ * directory.
+ *
+ * @param dir the directory to load
+ */
+ DirectoryLoadThread(File dir)
+ {
+ super("Basic L&F directory loader");
+ directory = dir;
+ }
+
+ public void run()
+ {
+ FileSystemView fsv = filechooser.getFileSystemView();
+ File[] files = fsv.getFiles(directory,
+ filechooser.isFileHidingEnabled());
+
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ // Check list for accepted files.
+ Vector accepted = new Vector();
+ for (int i = 0; i < files.length; i++)
+ {
+ if (filechooser.accept(files[i]))
+ accepted.add(files[i]);
+ }
+
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ // Sort list.
+ sort(accepted);
+
+ // Now split up directories from files so that we get the directories
+ // listed before the files.
+ Vector newFiles = new Vector();
+ Vector newDirectories = new Vector();
+ for (Iterator i = accepted.iterator(); i.hasNext();)
+ {
+ File f = (File) i.next();
+ boolean traversable = filechooser.isTraversable(f);
+ if (traversable)
+ newDirectories.add(f);
+ else if (! traversable && filechooser.isFileSelectionEnabled())
+ newFiles.add(f);
+
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ }
+
+ // Build up new file cache. Try to update only the changed elements.
+ // This will be important for actions like adding new files or
+ // directories inside a large file list.
+ Vector newCache = new Vector(newDirectories);
+ newCache.addAll(newFiles);
+
+ int newSize = newCache.size();
+ int oldSize = contents.size();
+ if (newSize < oldSize)
+ {
+ // Check for removed interval.
+ int start = -1;
+ int end = -1;
+ boolean found = false;
+ for (int i = 0; i < newSize && !found; i++)
+ {
+ if (! newCache.get(i).equals(contents.get(i)))
+ {
+ start = i;
+ end = i + oldSize - newSize;
+ found = true;
+ }
+ }
+ if (start >= 0 && end > start
+ && contents.subList(end, oldSize)
+ .equals(newCache.subList(start, newSize)))
+ {
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ Vector removed = new Vector(contents.subList(start, end));
+ UpdateSwingRequest r = new UpdateSwingRequest(null, 0,
+ removed, start);
+ invokeLater(r);
+ newCache = null;
+ }
+ }
+ else if (newSize > oldSize)
+ {
+ // Check for inserted interval.
+ int start = oldSize;
+ int end = newSize;
+ boolean found = false;
+ for (int i = 0; i < oldSize && ! found; i++)
+ {
+ if (! newCache.get(i).equals(contents.get(i)))
+ {
+ start = i;
+ boolean foundEnd = false;
+ for (int j = i; j < newSize && ! foundEnd; j++)
+ {
+ if (newCache.get(j).equals(contents.get(i)))
+ {
+ end = j;
+ foundEnd = true;
+ }
+ }
+ end = i + oldSize - newSize;
+ }
+ }
+ if (start >= 0 && end > start
+ && newCache.subList(end, newSize)
+ .equals(contents.subList(start, oldSize)))
+ {
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ List added = newCache.subList(start, end);
+ UpdateSwingRequest r = new UpdateSwingRequest(added, start,
+ null, 0);
+ invokeLater(r);
+ newCache = null;
+ }
+ }
+
+ // Handle complete list changes (newCache != null).
+ if (newCache != null && ! contents.equals(newCache))
+ {
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+ UpdateSwingRequest r = new UpdateSwingRequest(newCache, 0,
+ contents, 0);
+ invokeLater(r);
+ }
+ }
+
+ /**
+ * Wraps SwingUtilities.invokeLater() and stores the request in
+ * a Vector so that we can still cancel it later.
+ *
+ * @param update the request to invoke
+ */
+ private void invokeLater(UpdateSwingRequest update)
+ {
+ pending = update;
+ SwingUtilities.invokeLater(update);
+ }
+
+ /**
+ * Cancels all pending update requests that might be in the AWT
+ * event queue.
+ */
+ void cancelPending()
+ {
+ if (pending != null)
+ pending.cancel();
+ }
+ }
+
+ /** A Comparator class/object for sorting the file list. */
+ private Comparator comparator = new Comparator()
+ {
+ public int compare(Object o1, Object o2)
+ {
+ if (lt((File) o1, (File) o2))
+ return -1;
+ else
+ return 1;
+ }
+ };
+
+ /**
+ * Creates a new BasicDirectoryModel object.
+ *
+ * @param filechooser DOCUMENT ME!
+ */
+ public BasicDirectoryModel(JFileChooser filechooser)
+ {
+ this.filechooser = filechooser;
+ filechooser.addPropertyChangeListener(this);
+ listingMode = filechooser.getFileSelectionMode();
+ contents = new Vector();
+ validateFileCache();
+ }
+
+ /**
+ * Returns whether a given (File) object is included in the list.
+ *
+ * @param o - The file object to test.
+ *
+ * @return <code>true</code> if the list contains the given object.
+ */
+ public boolean contains(Object o)
+ {
+ return contents.contains(o);
+ }
+
+ /**
+ * Fires a content change event.
+ */
+ public void fireContentsChanged()
+ {
+ fireContentsChanged(this, 0, getSize() - 1);
+ }
+
+ /**
+ * Returns a Vector of (java.io.File) objects containing
+ * the directories in this list.
+ *
+ * @return a Vector
+ */
+ public Vector<File> getDirectories()
+ {
+ // Synchronize this with the UpdateSwingRequest for the case when
+ // contents is modified.
+ synchronized (contents)
+ {
+ Vector dirs = directories;
+ if (dirs == null)
+ {
+ // Initializes this in getFiles().
+ getFiles();
+ dirs = directories;
+ }
+ return dirs;
+ }
+ }
+
+ /**
+ * Returns the (java.io.File) object at
+ * an index in the list.
+ *
+ * @param index The list index
+ * @return a File object
+ */
+ public Object getElementAt(int index)
+ {
+ if (index > getSize() - 1)
+ return null;
+ return contents.elementAt(index);
+ }
+
+ /**
+ * Returns a Vector of (java.io.File) objects containing
+ * the files in this list.
+ *
+ * @return a Vector
+ */
+ public Vector<File> getFiles()
+ {
+ synchronized (contents)
+ {
+ Vector f = files;
+ if (f == null)
+ {
+ f = new Vector();
+ Vector d = new Vector(); // Directories;
+ for (Iterator i = contents.iterator(); i.hasNext();)
+ {
+ File file = (File) i.next();
+ if (filechooser.isTraversable(file))
+ d.add(file);
+ else
+ f.add(file);
+ }
+ files = f;
+ directories = d;
+ }
+ return f;
+ }
+ }
+
+ /**
+ * Returns the size of the list, which only includes directories
+ * if the JFileChooser is set to DIRECTORIES_ONLY.
+ *
+ * Otherwise, both directories and files are included in the count.
+ *
+ * @return The size of the list.
+ */
+ public int getSize()
+ {
+ return contents.size();
+ }
+
+ /**
+ * Returns the index of an (java.io.File) object in the list.
+ *
+ * @param o The object - normally a File.
+ *
+ * @return the index of that object, or -1 if it is not in the list.
+ */
+ public int indexOf(Object o)
+ {
+ return contents.indexOf(o);
+ }
+
+ /**
+ * Obsoleted method which does nothing.
+ */
+ public void intervalAdded(ListDataEvent e)
+ {
+ // obsoleted
+ }
+
+ /**
+ * Obsoleted method which does nothing.
+ */
+ public void intervalRemoved(ListDataEvent e)
+ {
+ // obsoleted
+ }
+
+ /**
+ * Obsoleted method which does nothing.
+ */
+ public void invalidateFileCache()
+ {
+ // obsoleted
+ }
+
+ /**
+ * Less than, determine the relative order in the list of two files
+ * for sorting purposes.
+ *
+ * The order is: directories < files, and thereafter alphabetically,
+ * using the default locale collation.
+ *
+ * @param a the first file
+ * @param b the second file
+ *
+ * @return <code>true</code> if a > b, <code>false</code> if a < b.
+ */
+ protected boolean lt(File a, File b)
+ {
+ boolean aTrav = filechooser.isTraversable(a);
+ boolean bTrav = filechooser.isTraversable(b);
+
+ if (aTrav == bTrav)
+ {
+ String aname = a.getName().toLowerCase();
+ String bname = b.getName().toLowerCase();
+ return (aname.compareTo(bname) < 0) ? true : false;
+ }
+ else
+ {
+ if (aTrav)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ /**
+ * Listens for a property change; the change in file selection mode of the
+ * associated JFileChooser. Reloads the file cache on that event.
+ *
+ * @param e - A PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String property = e.getPropertyName();
+ if (property.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)
+ || property.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)
+ || property.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)
+ || property.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)
+ || property.equals(JFileChooser.FILE_VIEW_CHANGED_PROPERTY)
+ )
+ {
+ validateFileCache();
+ }
+ }
+
+ /**
+ * Renames a file - However, does <I>not</I> re-sort the list
+ * or replace the old file with the new one in the list.
+ *
+ * @param oldFile The old file
+ * @param newFile The new file name
+ *
+ * @return <code>true</code> if the rename succeeded
+ */
+ public boolean renameFile(File oldFile, File newFile)
+ {
+ return oldFile.renameTo( newFile );
+ }
+
+ /**
+ * Sorts a Vector of File objects.
+ *
+ * @param v The Vector to sort.
+ */
+ protected void sort(Vector<? extends File> v)
+ {
+ Collections.sort(v, comparator);
+ }
+
+ /**
+ * Re-loads the list of files
+ */
+ public void validateFileCache()
+ {
+ File dir = filechooser.getCurrentDirectory();
+ if (dir != null)
+ {
+ // Cancel all pending requests.
+ if (loadThread != null)
+ {
+ loadThread.interrupt();
+ loadThread.cancelPending();
+ }
+ loadThread = new DirectoryLoadThread(dir);
+ loadThread.start();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java
new file mode 100644
index 000000000..6ddd2513b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java
@@ -0,0 +1,96 @@
+/* BasicEditorPaneUI.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.JEditorPane;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.EditorKit;
+import javax.swing.text.JTextComponent;
+
+/**
+ * The UI class for {@link JEditorPane}s.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class BasicEditorPaneUI extends BasicTextUI
+{
+ /**
+ * Creates an instance of <code>BasicEditorPaneUI</code> for the text
+ * component <code>comp</code>.
+ *
+ * @param comp the component for which to create an UI
+ *
+ * @return the UI for <code>comp</code>
+ */
+ public static ComponentUI createUI(JComponent comp)
+ {
+ return new BasicEditorPaneUI();
+ }
+
+ /**
+ * Creates a new <code>BasicEditorPaneUI</code>
+ */
+ public BasicEditorPaneUI()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Returns the property prefix to be used by this UI class. This is
+ * <code>EditorPane</code> in this case.
+ *
+ * @return <code>EditorPane</code>
+ */
+ protected String getPropertyPrefix()
+ {
+ return "EditorPane";
+ }
+
+ /**
+ * Gets the EditorKit for the text component.
+ *
+ * @param textComponent the text component for which to fetch the editor kit
+ */
+ public EditorKit getEditorKit(JTextComponent textComponent)
+ {
+ return ((JEditorPane) textComponent).getEditorKit();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java
new file mode 100644
index 000000000..347686d6e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java
@@ -0,0 +1,1437 @@
+/* BasicFileChooserUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileSystemView;
+import javax.swing.filechooser.FileView;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.FileChooserUI;
+import javax.swing.plaf.metal.MetalIconFactory;
+
+
+/**
+ * A UI delegate for the {@link JFileChooser} component under the
+ * {@link BasicLookAndFeel}.
+ */
+public class BasicFileChooserUI extends FileChooserUI
+{
+ /**
+ * A file filter that accepts all files.
+ */
+ protected class AcceptAllFileFilter extends FileFilter
+ {
+ /**
+ * Creates a new instance.
+ */
+ public AcceptAllFileFilter()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns <code>true</code> always, as all files are accepted by this
+ * filter.
+ *
+ * @param f the file.
+ *
+ * @return Always <code>true</code>.
+ */
+ public boolean accept(File f)
+ {
+ return true;
+ }
+
+ /**
+ * Returns a description for this filter.
+ *
+ * @return A description for the file filter.
+ */
+ public String getDescription()
+ {
+ return acceptAllFileFilterText;
+ }
+ }
+
+ /**
+ * Handles a user action to approve the dialog selection.
+ *
+ * @see BasicFileChooserUI#getApproveSelectionAction()
+ */
+ protected class ApproveSelectionAction extends AbstractAction
+ {
+ /**
+ * Creates a new ApproveSelectionAction object.
+ */
+ protected ApproveSelectionAction()
+ {
+ super("approveSelection");
+ }
+
+ /**
+ * Sets the current selection and closes the dialog.
+ *
+ * @param e the action event.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ Object obj = null;
+ if (parentPath != null)
+ obj = new String(parentPath + getFileName());
+ else
+ obj = filechooser.getSelectedFile();
+ if (obj != null)
+ {
+ File f = filechooser.getFileSystemView().createFileObject(obj.toString());
+ File currSelected = filechooser.getSelectedFile();
+ if (filechooser.isTraversable(f))
+ {
+ filechooser.setCurrentDirectory(currSelected);
+ filechooser.rescanCurrentDirectory();
+ }
+ else
+ {
+ filechooser.approveSelection();
+ closeDialog();
+ }
+ }
+ else
+ {
+ File f = new File(filechooser.getCurrentDirectory(), getFileName());
+ if ( selectedDir != null )
+ f = selectedDir;
+ if (filechooser.isTraversable(f))
+ {
+ filechooser.setCurrentDirectory(f);
+ filechooser.rescanCurrentDirectory();
+ }
+ else
+ {
+ filechooser.setSelectedFile(f);
+ filechooser.approveSelection();
+ closeDialog();
+ }
+ }
+ }
+ }
+
+ /**
+ * Provides presentation information about files and directories.
+ */
+ protected class BasicFileView extends FileView
+ {
+ /** Storage for cached icons. */
+ protected Hashtable<File, Icon> iconCache = new Hashtable<File, Icon>();
+
+ /**
+ * Creates a new instance.
+ */
+ public BasicFileView()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds an icon to the cache, associating it with the given file/directory.
+ *
+ * @param f the file/directory.
+ * @param i the icon.
+ */
+ public void cacheIcon(File f, Icon i)
+ {
+ iconCache.put(f, i);
+ }
+
+ /**
+ * Clears the icon cache.
+ */
+ public void clearIconCache()
+ {
+ iconCache.clear();
+ }
+
+ /**
+ * Retrieves the icon associated with the specified file/directory, if
+ * there is one.
+ *
+ * @param f the file/directory.
+ *
+ * @return The cached icon (or <code>null</code>).
+ */
+ public Icon getCachedIcon(File f)
+ {
+ return (Icon) iconCache.get(f);
+ }
+
+ /**
+ * Returns a description of the given file/directory. In this
+ * implementation, the description is the same as the name returned by
+ * {@link #getName(File)}.
+ *
+ * @param f the file/directory.
+ *
+ * @return A description of the given file/directory.
+ */
+ public String getDescription(File f)
+ {
+ return getName(f);
+ }
+
+ /**
+ * Returns an icon appropriate for the given file or directory.
+ *
+ * @param f the file/directory.
+ *
+ * @return An icon.
+ */
+ public Icon getIcon(File f)
+ {
+ Icon val = getCachedIcon(f);
+ if (val != null)
+ return val;
+ if (filechooser.isTraversable(f))
+ val = directoryIcon;
+ else
+ val = fileIcon;
+ cacheIcon(f, val);
+ return val;
+ }
+
+ /**
+ * Returns the name for the given file/directory.
+ *
+ * @param f the file/directory.
+ *
+ * @return The name of the file/directory.
+ */
+ public String getName(File f)
+ {
+ String name = null;
+ if (f != null)
+ {
+ JFileChooser c = getFileChooser();
+ FileSystemView v = c.getFileSystemView();
+ name = v.getSystemDisplayName(f);
+ }
+ return name;
+ }
+
+ /**
+ * Returns a localised description for the type of file/directory.
+ *
+ * @param f the file/directory.
+ *
+ * @return A type description for the given file/directory.
+ */
+ public String getTypeDescription(File f)
+ {
+ if (filechooser.isTraversable(f))
+ return dirDescText;
+ else
+ return fileDescText;
+ }
+
+ /**
+ * Returns {@link Boolean#TRUE} if the given file/directory is hidden,
+ * and {@link Boolean#FALSE} otherwise.
+ *
+ * @param f the file/directory.
+ *
+ * @return {@link Boolean#TRUE} or {@link Boolean#FALSE}.
+ */
+ public Boolean isHidden(File f)
+ {
+ return Boolean.valueOf(filechooser.getFileSystemView().isHiddenFile(f));
+ }
+ }
+
+ /**
+ * Handles an action to cancel the file chooser.
+ *
+ * @see BasicFileChooserUI#getCancelSelectionAction()
+ */
+ protected class CancelSelectionAction extends AbstractAction
+ {
+ /**
+ * Creates a new <code>CancelSelectionAction</code> object.
+ */
+ protected CancelSelectionAction()
+ {
+ super(null);
+ }
+
+ /**
+ * Cancels the selection and closes the dialog.
+ *
+ * @param e the action event (ignored).
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ filechooser.setSelectedFile(null);
+ filechooser.setSelectedFiles(null);
+ filechooser.cancelSelection();
+ closeDialog();
+ }
+ }
+
+ /**
+ * An action to handle changes to the parent directory (for example, via
+ * a click on the "up folder" button).
+ *
+ * @see BasicFileChooserUI#getChangeToParentDirectoryAction()
+ */
+ protected class ChangeToParentDirectoryAction extends AbstractAction
+ {
+ /**
+ * Creates a new <code>ChangeToParentDirectoryAction</code> object.
+ */
+ protected ChangeToParentDirectoryAction()
+ {
+ super("Go Up");
+ }
+
+ /**
+ * Handles the action event.
+ *
+ * @param e the action event.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ filechooser.changeToParentDirectory();
+ filechooser.revalidate();
+ filechooser.repaint();
+ }
+ }
+
+ /**
+ * A mouse listener that handles double-click events.
+ *
+ * @see BasicFileChooserUI#createDoubleClickListener(JFileChooser, JList)
+ */
+ protected class DoubleClickListener extends MouseAdapter
+ {
+
+ /** DOCUMENT ME! */
+ private Object lastSelected;
+
+ /** DOCUMENT ME! */
+ private JList list;
+
+ /**
+ * Creates a new DoubleClickListener object.
+ *
+ * @param list DOCUMENT ME!
+ */
+ public DoubleClickListener(JList list)
+ {
+ this.list = list;
+ lastSelected = list.getSelectedValue();
+ setDirectorySelected(false);
+ }
+
+ /**
+ * Handles a mouse click event.
+ *
+ * @param e the event.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ Object p = list.getSelectedValue();
+ if (p == null)
+ return;
+ FileSystemView fsv = filechooser.getFileSystemView();
+ if (e.getClickCount() >= 2 && lastSelected != null &&
+ p.toString().equals(lastSelected.toString()))
+ {
+ File f = fsv.createFileObject(lastSelected.toString());
+ if (filechooser.isTraversable(f))
+ {
+ filechooser.setCurrentDirectory(f);
+ filechooser.rescanCurrentDirectory();
+ }
+ else
+ {
+ filechooser.setSelectedFile(f);
+ filechooser.approveSelection();
+ closeDialog();
+ }
+ }
+ else // single click
+ {
+ String path = p.toString();
+ File f = fsv.createFileObject(path);
+ filechooser.setSelectedFile(f);
+
+ if (filechooser.isMultiSelectionEnabled())
+ {
+ int[] inds = list.getSelectedIndices();
+ File[] allFiles = new File[inds.length];
+ for (int i = 0; i < inds.length; i++)
+ allFiles[i] = (File) list.getModel().getElementAt(inds[i]);
+ filechooser.setSelectedFiles(allFiles);
+ }
+
+ if (filechooser.isTraversable(f))
+ {
+ setDirectorySelected(true);
+ setDirectory(f);
+ }
+ else
+ {
+ setDirectorySelected(false);
+ setDirectory(null);
+ }
+ lastSelected = path;
+ parentPath = f.getParent();
+
+ if (f.isFile())
+ setFileName(f.getName());
+ else if (filechooser.getFileSelectionMode() !=
+ JFileChooser.FILES_ONLY)
+ setFileName(path);
+ }
+ }
+
+ /**
+ * Handles a mouse entered event (NOT IMPLEMENTED).
+ *
+ * @param e the mouse event.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // FIXME: Implement
+ }
+ }
+
+ /**
+ * An action that changes the file chooser to display the user's home
+ * directory.
+ *
+ * @see BasicFileChooserUI#getGoHomeAction()
+ */
+ protected class GoHomeAction extends AbstractAction
+ {
+ /**
+ * Creates a new <code>GoHomeAction</code> object.
+ */
+ protected GoHomeAction()
+ {
+ super("Go Home");
+ }
+
+ /**
+ * Sets the directory to the user's home directory, and repaints the
+ * file chooser component.
+ *
+ * @param e the action event (ignored).
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ filechooser.setCurrentDirectory(filechooser.getFileSystemView()
+ .getHomeDirectory());
+ filechooser.revalidate();
+ filechooser.repaint();
+ }
+ }
+
+ /**
+ * An action that handles the creation of a new folder/directory.
+ *
+ * @see BasicFileChooserUI#getNewFolderAction()
+ */
+ protected class NewFolderAction extends AbstractAction
+ {
+ /**
+ * Creates a new <code>NewFolderAction</code> object.
+ */
+ protected NewFolderAction()
+ {
+ super("New Folder");
+ }
+
+ /**
+ * Handles the event by creating a new folder.
+ *
+ * @param e the action event (ignored).
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ try
+ {
+ filechooser.getFileSystemView().createNewFolder(filechooser
+ .getCurrentDirectory());
+ }
+ catch (IOException ioe)
+ {
+ return;
+ }
+ filechooser.rescanCurrentDirectory();
+ filechooser.repaint();
+ }
+ }
+
+ /**
+ * A listener for selection events in the file list.
+ *
+ * @see BasicFileChooserUI#createListSelectionListener(JFileChooser)
+ */
+ protected class SelectionListener implements ListSelectionListener
+ {
+ /**
+ * Creates a new <code>SelectionListener</code> object.
+ */
+ protected SelectionListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the JFileChooser to the selected file on an update
+ *
+ * @param e DOCUMENT ME!
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ JList list = (JList) e.getSource();
+ Object f = list.getSelectedValue();
+ if (f == null)
+ return;
+ File file = filechooser.getFileSystemView().createFileObject(f.toString());
+ if (! filechooser.isTraversable(file))
+ {
+ selectedDir = null;
+ filechooser.setSelectedFile(file);
+ }
+ else
+ {
+ selectedDir = file;
+ filechooser.setSelectedFile(null);
+ }
+ }
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @see BasicFileChooserUI#getUpdateAction()
+ */
+ protected class UpdateAction extends AbstractAction
+ {
+ /**
+ * Creates a new UpdateAction object.
+ */
+ protected UpdateAction()
+ {
+ super(null);
+ }
+
+ /**
+ * NOT YET IMPLEMENTED.
+ *
+ * @param e the action event.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // FIXME: implement this
+ }
+ }
+
+ /** The localised mnemonic for the cancel button. */
+ protected int cancelButtonMnemonic;
+
+ /** The localised text for the cancel button. */
+ protected String cancelButtonText;
+
+ /** The localised tool tip text for the cancel button. */
+ protected String cancelButtonToolTipText;
+
+ /** An icon representing a computer. */
+ protected Icon computerIcon;
+
+ /** An icon for the "details view" button. */
+ protected Icon detailsViewIcon;
+
+ /** An icon representing a directory. */
+ protected Icon directoryIcon;
+
+ /** The localised Mnemonic for the open button. */
+ protected int directoryOpenButtonMnemonic;
+
+ /** The localised text for the open button. */
+ protected String directoryOpenButtonText;
+
+ /** The localised tool tip text for the open button. */
+ protected String directoryOpenButtonToolTipText;
+
+ /** An icon representing a file. */
+ protected Icon fileIcon;
+
+ /** An icon representing a floppy drive. */
+ protected Icon floppyDriveIcon;
+
+ /** An icon representing a hard drive. */
+ protected Icon hardDriveIcon;
+
+ /** The localised mnemonic for the "help" button. */
+ protected int helpButtonMnemonic;
+
+ /** The localised text for the "help" button. */
+ protected String helpButtonText;
+
+ /** The localised tool tip text for the help button. */
+ protected String helpButtonToolTipText;
+
+ /** An icon representing the user's home folder. */
+ protected Icon homeFolderIcon;
+
+ /** An icon for the "list view" button. */
+ protected Icon listViewIcon;
+
+ /** An icon for the "new folder" button. */
+ protected Icon newFolderIcon = directoryIcon;
+
+ /** The localised mnemonic for the "open" button. */
+ protected int openButtonMnemonic;
+
+ /** The localised text for the "open" button. */
+ protected String openButtonText;
+
+ /** The localised tool tip text for the "open" button. */
+ protected String openButtonToolTipText;
+
+ /** The localised mnemonic for the "save" button. */
+ protected int saveButtonMnemonic;
+
+ /** The localised text for the "save" button. */
+ protected String saveButtonText;
+
+ /** The localised tool tip text for the save button. */
+ protected String saveButtonToolTipText;
+
+ /** The localised mnemonic for the "update" button. */
+ protected int updateButtonMnemonic;
+
+ /** The localised text for the "update" button. */
+ protected String updateButtonText;
+
+ /** The localised tool tip text for the "update" button. */
+ protected String updateButtonToolTipText;
+
+ /** An icon for the "up folder" button. */
+ protected Icon upFolderIcon;
+
+ // -- begin private, but package local since used in inner classes --
+
+ /** The file chooser component represented by this UI delegate. */
+ JFileChooser filechooser;
+
+ /** The model for the directory list. */
+ BasicDirectoryModel model;
+
+ /** The file filter for all files. */
+ FileFilter acceptAll = new AcceptAllFileFilter();
+
+ /** The default file view. */
+ FileView fv = new BasicFileView();
+
+ /** The accept (open/save) button. */
+ JButton accept;
+
+ /** An optional accessory panel. */
+ JPanel accessoryPanel = new JPanel();
+
+ /** A property change listener. */
+ PropertyChangeListener propertyChangeListener;
+
+ /** The text describing the filter for "all files". */
+ String acceptAllFileFilterText;
+
+ /** The text describing a directory type. */
+ String dirDescText;
+
+ /** The text describing a file type. */
+ String fileDescText;
+
+ /** Is a directory selected? */
+ boolean dirSelected;
+
+ /** The current directory. */
+ File currDir;
+
+ // FIXME: describe what is contained in the bottom panel
+ /** The bottom panel. */
+ JPanel bottomPanel;
+
+ /** The close panel. */
+ JPanel closePanel;
+
+ /** Text box that displays file name */
+ JTextField entry;
+
+ /** Current parent path */
+ String parentPath;
+
+ /**
+ * The action for the 'approve' button.
+ * @see #getApproveSelectionAction()
+ */
+ private ApproveSelectionAction approveSelectionAction;
+
+ /**
+ * The action for the 'cancel' button.
+ * @see #getCancelSelectionAction()
+ */
+ private CancelSelectionAction cancelSelectionAction;
+
+ /**
+ * The action for the 'go home' control button.
+ * @see #getGoHomeAction()
+ */
+ private GoHomeAction goHomeAction;
+
+ /**
+ * The action for the 'up folder' control button.
+ * @see #getChangeToParentDirectoryAction()
+ */
+ private ChangeToParentDirectoryAction changeToParentDirectoryAction;
+
+ /**
+ * The action for the 'new folder' control button.
+ * @see #getNewFolderAction()
+ */
+ private NewFolderAction newFolderAction;
+
+ /**
+ * The action for ???. // FIXME: what is this?
+ * @see #getUpdateAction()
+ */
+ private UpdateAction updateAction;
+
+ /**
+ * When in FILES_ONLY, mode a directory cannot be selected, so
+ * we save a reference to any it here. This is used to enter
+ * the directory on "Open" when in that mode.
+ */
+ private File selectedDir;
+
+ // -- end private --
+
+ /**
+ * Closes the dialog.
+ */
+ void closeDialog()
+ {
+ Window owner = SwingUtilities.windowForComponent(filechooser);
+ if (owner instanceof JDialog)
+ ((JDialog) owner).dispose();
+ }
+
+ /**
+ * Creates a new <code>BasicFileChooserUI</code> object.
+ *
+ * @param b the file chooser component.
+ */
+ public BasicFileChooserUI(JFileChooser b)
+ {
+ }
+
+ /**
+ * Returns a UI delegate for the given component.
+ *
+ * @param c the component (should be a {@link JFileChooser}).
+ *
+ * @return A new UI delegate.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicFileChooserUI((JFileChooser) c);
+ }
+
+ /**
+ * Installs the UI for the specified component.
+ *
+ * @param c the component (should be a {@link JFileChooser}).
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JFileChooser)
+ {
+ JFileChooser fc = (JFileChooser) c;
+ this.filechooser = fc;
+ fc.resetChoosableFileFilters();
+ createModel();
+ clearIconCache();
+ installDefaults(fc);
+ installComponents(fc);
+ installListeners(fc);
+
+ File path = filechooser.getCurrentDirectory();
+ if (path != null)
+ parentPath = path.getParent();
+ }
+ }
+
+ /**
+ * Uninstalls this UI from the given component.
+ *
+ * @param c the component (should be a {@link JFileChooser}).
+ */
+ public void uninstallUI(JComponent c)
+ {
+ model = null;
+ uninstallListeners(filechooser);
+ uninstallComponents(filechooser);
+ uninstallDefaults(filechooser);
+ filechooser = null;
+ }
+
+ // FIXME: Indent the entries in the combobox
+ // Made this method package private to access it from within inner classes
+ // with better performance
+ void boxEntries()
+ {
+ ArrayList parentFiles = new ArrayList();
+ File parent = filechooser.getCurrentDirectory();
+ if (parent == null)
+ parent = filechooser.getFileSystemView().getDefaultDirectory();
+ while (parent != null)
+ {
+ String name = parent.getName();
+ if (name.equals(""))
+ name = parent.getAbsolutePath();
+
+ parentFiles.add(parentFiles.size(), name);
+ parent = parent.getParentFile();
+ }
+
+ if (parentFiles.size() == 0)
+ return;
+
+ }
+
+ /**
+ * Creates and install the subcomponents for the file chooser.
+ *
+ * @param fc the file chooser.
+ */
+ public void installComponents(JFileChooser fc)
+ {
+ }
+
+ /**
+ * Uninstalls the components from the file chooser.
+ *
+ * @param fc the file chooser.
+ */
+ public void uninstallComponents(JFileChooser fc)
+ {
+ }
+
+ /**
+ * Installs the listeners required by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void installListeners(JFileChooser fc)
+ {
+ propertyChangeListener = createPropertyChangeListener(filechooser);
+ if (propertyChangeListener != null)
+ filechooser.addPropertyChangeListener(propertyChangeListener);
+ fc.addPropertyChangeListener(getModel());
+ }
+
+ /**
+ * Uninstalls the listeners previously installed by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void uninstallListeners(JFileChooser fc)
+ {
+ if (propertyChangeListener != null)
+ {
+ filechooser.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ }
+ fc.removePropertyChangeListener(getModel());
+ }
+
+ /**
+ * Installs the defaults for this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void installDefaults(JFileChooser fc)
+ {
+ installIcons(fc);
+ installStrings(fc);
+ }
+
+ /**
+ * Uninstalls the defaults previously added by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void uninstallDefaults(JFileChooser fc)
+ {
+ uninstallStrings(fc);
+ uninstallIcons(fc);
+ }
+
+ /**
+ * Installs the icons for this UI delegate.
+ *
+ * @param fc the file chooser (ignored).
+ */
+ protected void installIcons(JFileChooser fc)
+ {
+ UIDefaults defaults = UIManager.getLookAndFeelDefaults();
+ computerIcon = MetalIconFactory.getTreeComputerIcon();
+ detailsViewIcon = defaults.getIcon("FileChooser.detailsViewIcon");
+ directoryIcon = new MetalIconFactory.TreeFolderIcon();
+ fileIcon = new MetalIconFactory.TreeLeafIcon();
+ floppyDriveIcon = MetalIconFactory.getTreeFloppyDriveIcon();
+ hardDriveIcon = MetalIconFactory.getTreeHardDriveIcon();
+ homeFolderIcon = defaults.getIcon("FileChooser.homeFolderIcon");
+ listViewIcon = defaults.getIcon("FileChooser.listViewIcon");
+ newFolderIcon = defaults.getIcon("FileChooser.newFolderIcon");
+ upFolderIcon = defaults.getIcon("FileChooser.upFolderIcon");
+ }
+
+ /**
+ * Uninstalls the icons previously added by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void uninstallIcons(JFileChooser fc)
+ {
+ computerIcon = null;
+ detailsViewIcon = null;
+ directoryIcon = null;
+ fileIcon = null;
+ floppyDriveIcon = null;
+ hardDriveIcon = null;
+ homeFolderIcon = null;
+ listViewIcon = null;
+ newFolderIcon = null;
+ upFolderIcon = null;
+ }
+
+ /**
+ * Installs the strings used by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void installStrings(JFileChooser fc)
+ {
+ UIDefaults defaults = UIManager.getLookAndFeelDefaults();
+
+ dirDescText = defaults.getString("FileChooser.directoryDescriptionText");
+ fileDescText = defaults.getString("FileChooser.fileDescriptionText");
+
+ acceptAllFileFilterText = defaults.getString("FileChooser.acceptAllFileFilterText");
+ cancelButtonText = "Cancel";
+ cancelButtonToolTipText = "Abort file chooser dialog";
+ cancelButtonMnemonic = new Integer((String) UIManager.get("FileChooser.cancelButtonMnemonic")).intValue();
+
+ directoryOpenButtonText = "Open";
+ directoryOpenButtonToolTipText = "Open selected directory";
+ directoryOpenButtonMnemonic
+ = new Integer((String) UIManager.get("FileChooser.directoryOpenButtonMnemonic")).intValue();
+
+ helpButtonText = "Help";
+ helpButtonToolTipText = "FileChooser help";
+ helpButtonMnemonic = new Integer((String) UIManager.get("FileChooser.helpButtonMnemonic")).intValue();
+
+ openButtonText = "Open";
+ openButtonToolTipText = "Open selected file";
+ openButtonMnemonic = new Integer((String) UIManager.get("FileChooser.openButtonMnemonic")).intValue();
+
+ saveButtonText = "Save";
+ saveButtonToolTipText = "Save selected file";
+ saveButtonMnemonic = new Integer((String) UIManager.get("FileChooser.saveButtonMnemonic")).intValue();
+
+ updateButtonText = "Update";
+ updateButtonToolTipText = "Update directory listing";
+ updateButtonMnemonic = new Integer((String) UIManager.get("FileChooser.updateButtonMnemonic")).intValue();
+ }
+
+ /**
+ * Uninstalls the strings previously added by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void uninstallStrings(JFileChooser fc)
+ {
+ acceptAllFileFilterText = null;
+ dirDescText = null;
+ fileDescText = null;
+
+ cancelButtonText = null;
+ cancelButtonToolTipText = null;
+
+ directoryOpenButtonText = null;
+ directoryOpenButtonToolTipText = null;
+
+ helpButtonText = null;
+ helpButtonToolTipText = null;
+
+ openButtonText = null;
+ openButtonToolTipText = null;
+
+ saveButtonText = null;
+ saveButtonToolTipText = null;
+
+ updateButtonText = null;
+ updateButtonToolTipText = null;
+ }
+
+ /**
+ * Creates a new directory model.
+ */
+ protected void createModel()
+ {
+ model = new BasicDirectoryModel(filechooser);
+ }
+
+ /**
+ * Returns the directory model.
+ *
+ * @return The directory model.
+ */
+ public BasicDirectoryModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Creates a listener to handle changes to the properties of the given
+ * file chooser component.
+ *
+ * @param fc the file chooser component.
+ *
+ * @return A new listener.
+ */
+ public PropertyChangeListener createPropertyChangeListener(JFileChooser fc)
+ {
+ // The RI returns null here, so do we.
+ return null;
+ }
+
+ /**
+ * Returns the current file name.
+ *
+ * @return The current file name.
+ */
+ public String getFileName()
+ {
+ return entry.getText();
+ }
+
+ /**
+ * Returns the current directory name.
+ *
+ * @return The directory name.
+ *
+ * @see #setDirectoryName(String)
+ */
+ public String getDirectoryName()
+ {
+ // XXX: I don't see a case where the thing returns something non-null..
+ return null;
+ }
+
+ /**
+ * Sets the file name.
+ *
+ * @param filename the file name.
+ *
+ * @see #getFileName()
+ */
+ public void setFileName(String filename)
+ {
+ // FIXME: it might be the case that this method provides an access
+ // point for the JTextField (or whatever) a subclass is using...
+ //this.filename = filename;
+ }
+
+ /**
+ * Sets the directory name (NOT IMPLEMENTED).
+ *
+ * @param dirname the directory name.
+ *
+ * @see #getDirectoryName()
+ */
+ public void setDirectoryName(String dirname)
+ {
+ // FIXME: Implement
+ }
+
+ /**
+ * Rescans the current directory.
+ *
+ * @param fc the file chooser.
+ */
+ public void rescanCurrentDirectory(JFileChooser fc)
+ {
+ getModel().validateFileCache();
+ }
+
+ /**
+ * NOT YET IMPLEMENTED.
+ *
+ * @param fc the file chooser.
+ * @param f the file.
+ */
+ public void ensureFileIsVisible(JFileChooser fc, File f)
+ {
+ // XXX: Not sure what this does.
+ }
+
+ /**
+ * Returns the {@link JFileChooser} component that this UI delegate
+ * represents.
+ *
+ * @return The component represented by this UI delegate.
+ */
+ public JFileChooser getFileChooser()
+ {
+ return filechooser;
+ }
+
+ /**
+ * Returns the optional accessory panel.
+ *
+ * @return The optional accessory panel.
+ */
+ public JPanel getAccessoryPanel()
+ {
+ return accessoryPanel;
+ }
+
+ /**
+ * Returns the approve (open or save) button for the dialog.
+ *
+ * @param fc the file chooser.
+ *
+ * @return The button.
+ */
+ protected JButton getApproveButton(JFileChooser fc)
+ {
+ return accept;
+ }
+
+ /**
+ * Returns the tool tip text for the approve (open/save) button. This first
+ * checks the file chooser to see if a value has been explicitly set - if
+ * not, a default value appropriate for the type of file chooser is
+ * returned.
+ *
+ * @param fc the file chooser.
+ *
+ * @return The tool tip text.
+ */
+ public String getApproveButtonToolTipText(JFileChooser fc)
+ {
+ if (fc.getApproveButtonToolTipText() != null)
+ return fc.getApproveButtonToolTipText();
+ else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
+ return saveButtonToolTipText;
+ else
+ return openButtonToolTipText;
+ }
+
+ /**
+ * Clears the icon cache.
+ */
+ public void clearIconCache()
+ {
+ if (fv instanceof BasicFileView)
+ ((BasicFileView) fv).clearIconCache();
+ }
+
+ /**
+ * Creates a new listener to handle selections in the file list.
+ *
+ * @param fc the file chooser component.
+ *
+ * @return A new instance of {@link SelectionListener}.
+ */
+ public ListSelectionListener createListSelectionListener(JFileChooser fc)
+ {
+ return new SelectionListener();
+ }
+
+ /**
+ * Creates a new listener to handle double-click events.
+ *
+ * @param fc the file chooser component.
+ * @param list the list.
+ *
+ * @return A new instance of {@link DoubleClickListener}.
+ */
+ protected MouseListener createDoubleClickListener(JFileChooser fc, JList list)
+ {
+ return new DoubleClickListener(list);
+ }
+
+ /**
+ * Returns <code>true</code> if a directory is selected, and
+ * <code>false</code> otherwise.
+ *
+ * @return A boolean.
+ */
+ protected boolean isDirectorySelected()
+ {
+ return dirSelected;
+ }
+
+ /**
+ * Sets the flag that indicates whether the current directory is selected.
+ *
+ * @param selected the new flag value.
+ */
+ protected void setDirectorySelected(boolean selected)
+ {
+ dirSelected = selected;
+ }
+
+ /**
+ * Returns the current directory.
+ *
+ * @return The current directory.
+ */
+ protected File getDirectory()
+ {
+ return currDir;
+ }
+
+ /**
+ * Sets the current directory.
+ *
+ * @param f the directory.
+ */
+ protected void setDirectory(File f)
+ {
+ currDir = f;
+ }
+
+ /**
+ * Returns the "accept all" file filter.
+ *
+ * @param fc the file chooser component.
+ *
+ * @return The "accept all" file filter.
+ */
+ public FileFilter getAcceptAllFileFilter(JFileChooser fc)
+ {
+ return acceptAll;
+ }
+
+ /**
+ * Returns the default file view (NOT the file view from the file chooser,
+ * if there is one).
+ *
+ * @param fc the file chooser component.
+ *
+ * @return The file view.
+ *
+ * @see JFileChooser#getFileView()
+ */
+ public FileView getFileView(JFileChooser fc)
+ {
+ return fv;
+ }
+
+ /**
+ * Returns the dialog title.
+ *
+ * @param fc the file chooser (<code>null</code> not permitted).
+ *
+ * @return The dialog title.
+ *
+ * @see JFileChooser#getDialogTitle()
+ */
+ public String getDialogTitle(JFileChooser fc)
+ {
+ String result = fc.getDialogTitle();
+ if (result == null)
+ result = getApproveButtonText(fc);
+ return result;
+ }
+
+ /**
+ * Returns the approve button mnemonic.
+ *
+ * @param fc the file chooser (<code>null</code> not permitted).
+ *
+ * @return The approve button mnemonic.
+ *
+ * @see JFileChooser#getApproveButtonMnemonic()
+ */
+ public int getApproveButtonMnemonic(JFileChooser fc)
+ {
+ if (fc.getApproveButtonMnemonic() != 0)
+ return fc.getApproveButtonMnemonic();
+ else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
+ return saveButtonMnemonic;
+ else
+ return openButtonMnemonic;
+ }
+
+ /**
+ * Returns the approve button text.
+ *
+ * @param fc the file chooser (<code>null</code> not permitted).
+ *
+ * @return The approve button text.
+ *
+ * @see JFileChooser#getApproveButtonText()
+ */
+ public String getApproveButtonText(JFileChooser fc)
+ {
+ String result = fc.getApproveButtonText();
+ if (result == null)
+ {
+ if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
+ result = saveButtonText;
+ else
+ result = openButtonText;
+ }
+ return result;
+ }
+
+ /**
+ * Creates and returns a new action that will be used with the "new folder"
+ * button.
+ *
+ * @return A new instance of {@link NewFolderAction}.
+ */
+ public Action getNewFolderAction()
+ {
+ if (newFolderAction == null)
+ newFolderAction = new NewFolderAction();
+ return newFolderAction;
+ }
+
+ /**
+ * Creates and returns a new action that will be used with the "home folder"
+ * button.
+ *
+ * @return A new instance of {@link GoHomeAction}.
+ */
+ public Action getGoHomeAction()
+ {
+ if (goHomeAction == null)
+ goHomeAction = new GoHomeAction();
+ return goHomeAction;
+ }
+
+ /**
+ * Returns the action that handles events for the "up folder" control button.
+ *
+ * @return An instance of {@link ChangeToParentDirectoryAction}.
+ */
+ public Action getChangeToParentDirectoryAction()
+ {
+ if (changeToParentDirectoryAction == null)
+ changeToParentDirectoryAction = new ChangeToParentDirectoryAction();
+ return changeToParentDirectoryAction;
+ }
+
+ /**
+ * Returns the action that handles events for the "approve" button.
+ *
+ * @return An instance of {@link ApproveSelectionAction}.
+ */
+ public Action getApproveSelectionAction()
+ {
+ if (approveSelectionAction == null)
+ approveSelectionAction = new ApproveSelectionAction();
+ return approveSelectionAction;
+ }
+
+ /**
+ * Returns the action that handles events for the "cancel" button.
+ *
+ * @return An instance of {@link CancelSelectionAction}.
+ */
+ public Action getCancelSelectionAction()
+ {
+ if (cancelSelectionAction == null)
+ cancelSelectionAction = new CancelSelectionAction();
+ return cancelSelectionAction;
+ }
+
+ /**
+ * Returns the update action (an instance of {@link UpdateAction}).
+ *
+ * @return An action.
+ */
+ public Action getUpdateAction()
+ {
+ if (updateAction == null)
+ updateAction = new UpdateAction();
+ return updateAction;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java
new file mode 100644
index 000000000..555921435
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java
@@ -0,0 +1,69 @@
+/* BasicFormattedTextFieldUI.java
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * @since 1.4
+ */
+public class BasicFormattedTextFieldUI extends BasicTextFieldUI
+{
+ public BasicFormattedTextFieldUI()
+ {
+ // Nothing to do here.
+ }
+
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicFormattedTextFieldUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "FormattedTextField"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "FormattedTextField";
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java
new file mode 100644
index 000000000..f270d3335
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java
@@ -0,0 +1,821 @@
+/* BasicGraphicsUtils.java
+ Copyright (C) 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineMetrics;
+import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
+
+import javax.swing.AbstractButton;
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+
+
+/**
+ * A utility class providing commonly used drawing and measurement
+ * routines.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class BasicGraphicsUtils
+{
+ /**
+ * Used as a key for a client property to store cached TextLayouts in. This
+ * is used for speed-up drawing of text in
+ * {@link #drawString(Graphics, String, int, int, int)}.
+ */
+ static final String CACHED_TEXT_LAYOUT =
+ "BasicGraphicsUtils.cachedTextLayout";
+
+ /**
+ * Constructor. It is utterly unclear why this class should
+ * be constructable, but this is what the API specification
+ * says.
+ */
+ public BasicGraphicsUtils()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Draws a rectangle that appears etched into the surface, given
+ * four colors that are used for drawing.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-1.png" width="360"
+ * height="200" alt="[An illustration that shows which pixels
+ * get painted in what color]" />
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ *
+ * @param shadow the color that will be used for painting
+ * the outer side of the top and left edges.
+ *
+ * @param darkShadow the color that will be used for painting
+ * the inner side of the top and left edges.
+ *
+ * @param highlight the color that will be used for painting
+ * the inner side of the bottom and right edges.
+ *
+ * @param lightHighlight the color that will be used for painting
+ * the outer side of the bottom and right edges.
+ *
+ * @see #getEtchedInsets()
+ * @see javax.swing.border.EtchedBorder
+ */
+ public static void drawEtchedRect(Graphics g,
+ int x, int y, int width, int height,
+ Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ Color oldColor;
+ int x2, y2;
+
+ oldColor = g.getColor();
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+
+ try
+ {
+ /* To understand this code, it might be helpful to look at the
+ * image "BasicGraphicsUtils-1.png" that is included with the
+ * JavaDoc. The file is located in the "doc-files" subdirectory.
+ *
+ * (x2, y2) is the coordinate of the most right and bottom pixel
+ * to be painted.
+ */
+ g.setColor(shadow);
+ g.drawLine(x, y, x2 - 1, y); // top, outer
+ g.drawLine(x, y + 1, x, y2 - 1); // left, outer
+
+ g.setColor(darkShadow);
+ g.drawLine(x + 1, y + 1, x2 - 2, y + 1); // top, inner
+ g.drawLine(x + 1, y + 2, x + 1, y2 - 2); // left, inner
+
+ g.setColor(highlight);
+ g.drawLine(x + 1, y2 - 1, x2 - 1, y2 - 1); // bottom, inner
+ g.drawLine(x2 - 1, y + 1, x2 - 1, y2 - 2); // right, inner
+
+ g.setColor(lightHighlight);
+ g.drawLine(x, y2, x2, y2); // bottom, outer
+ g.drawLine(x2, y, x2, y2 - 1); // right, outer
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Determines the width of the border that gets painted by
+ * {@link #drawEtchedRect}.
+ *
+ * @return an <code>Insets</code> object whose <code>top</code>,
+ * <code>left</code>, <code>bottom</code> and
+ * <code>right</code> field contain the border width at the
+ * respective edge in pixels.
+ */
+ public static Insets getEtchedInsets()
+ {
+ return new Insets(2, 2, 2, 2);
+ }
+
+
+ /**
+ * Draws a rectangle that appears etched into the surface, given
+ * two colors that are used for drawing.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-2.png" width="360"
+ * height="200" alt="[An illustration that shows which pixels
+ * get painted in what color]" />
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ *
+ * @param shadow the color that will be used for painting the outer
+ * side of the top and left edges, and for the inner side of
+ * the bottom and right ones.
+ *
+ * @param highlight the color that will be used for painting the
+ * inner side of the top and left edges, and for the outer
+ * side of the bottom and right ones.
+ *
+ * @see #getGrooveInsets()
+ * @see javax.swing.border.EtchedBorder
+ */
+ public static void drawGroove(Graphics g,
+ int x, int y, int width, int height,
+ Color shadow, Color highlight)
+ {
+ /* To understand this, it might be helpful to look at the image
+ * "BasicGraphicsUtils-2.png" that is included with the JavaDoc,
+ * and to compare it with "BasicGraphicsUtils-1.png" which shows
+ * the pixels painted by drawEtchedRect. These image files are
+ * located in the "doc-files" subdirectory.
+ */
+ drawEtchedRect(g, x, y, width, height,
+ /* outer topLeft */ shadow,
+ /* inner topLeft */ highlight,
+ /* inner bottomRight */ shadow,
+ /* outer bottomRight */ highlight);
+ }
+
+
+ /**
+ * Determines the width of the border that gets painted by
+ * {@link #drawGroove}.
+ *
+ * @return an <code>Insets</code> object whose <code>top</code>,
+ * <code>left</code>, <code>bottom</code> and
+ * <code>right</code> field contain the border width at the
+ * respective edge in pixels.
+ */
+ public static Insets getGrooveInsets()
+ {
+ return new Insets(2, 2, 2, 2);
+ }
+
+
+ /**
+ * Draws a border that is suitable for buttons of the Basic look and
+ * feel.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-3.png" width="500"
+ * height="300" alt="[An illustration that shows which pixels
+ * get painted in what color]" />
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ *
+ * @param isPressed <code>true</code> to draw the button border
+ * with a pressed-in appearance; <code>false</code> for
+ * normal (unpressed) appearance.
+ *
+ * @param isDefault <code>true</code> to draw the border with
+ * the appearance it has when hitting the enter key in a
+ * dialog will simulate a click to this button;
+ * <code>false</code> for normal appearance.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public static void drawBezel(Graphics g,
+ int x, int y, int width, int height,
+ boolean isPressed, boolean isDefault,
+ Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ Color oldColor = g.getColor();
+
+ /* To understand this, it might be helpful to look at the image
+ * "BasicGraphicsUtils-3.png" that is included with the JavaDoc,
+ * and to compare it with "BasicGraphicsUtils-1.png" which shows
+ * the pixels painted by drawEtchedRect. These image files are
+ * located in the "doc-files" subdirectory.
+ */
+ try
+ {
+ if ((isPressed == false) && (isDefault == false))
+ {
+ drawEtchedRect(g, x, y, width, height,
+ lightHighlight, highlight,
+ shadow, darkShadow);
+ }
+
+ if ((isPressed == true) && (isDefault == false))
+ {
+ g.setColor(shadow);
+ g.drawRect(x + 1, y + 1, width - 2, height - 2);
+ }
+
+ if ((isPressed == false) && (isDefault == true))
+ {
+ g.setColor(darkShadow);
+ g.drawRect(x, y, width - 1, height - 1);
+ drawEtchedRect(g, x + 1, y + 1, width - 2, height - 2,
+ lightHighlight, highlight,
+ shadow, darkShadow);
+ }
+
+ if ((isPressed == true) && (isDefault == true))
+ {
+ g.setColor(darkShadow);
+ g.drawRect(x, y, width - 1, height - 1);
+ g.setColor(shadow);
+ g.drawRect(x + 1, y + 1, width - 3, height - 3);
+ }
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Draws a rectangle that appears lowered into the surface, given
+ * four colors that are used for drawing.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-4.png" width="360"
+ * height="200" alt="[An illustration that shows which pixels
+ * get painted in what color]" />
+ *
+ * <p><strong>Compatibility with the Sun reference
+ * implementation:</strong> The Sun reference implementation seems
+ * to ignore the <code>x</code> and <code>y</code> arguments, at
+ * least in JDK 1.3.1 and 1.4.1_01. The method always draws the
+ * rectangular area at location (0, 0). A bug report has been filed
+ * with Sun; its &#x201c;bug ID&#x201d; is 4880003. The GNU Classpath
+ * implementation behaves correctly, thus not replicating this bug.
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ *
+ * @param shadow the color that will be used for painting
+ * the inner side of the top and left edges.
+ *
+ * @param darkShadow the color that will be used for painting
+ * the outer side of the top and left edges.
+ *
+ * @param highlight the color that will be used for painting
+ * the inner side of the bottom and right edges.
+ *
+ * @param lightHighlight the color that will be used for painting
+ * the outer side of the bottom and right edges.
+ */
+ public static void drawLoweredBezel(Graphics g,
+ int x, int y, int width, int height,
+ Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* Like drawEtchedRect, but swapping darkShadow and shadow.
+ *
+ * To understand this, it might be helpful to look at the image
+ * "BasicGraphicsUtils-4.png" that is included with the JavaDoc,
+ * and to compare it with "BasicGraphicsUtils-1.png" which shows
+ * the pixels painted by drawEtchedRect. These image files are
+ * located in the "doc-files" subdirectory.
+ */
+ drawEtchedRect(g, x, y, width, height,
+ darkShadow, shadow,
+ highlight, lightHighlight);
+ }
+
+
+ /**
+ * Draws a String at the given location, underlining the first
+ * occurence of a specified character. The algorithm for determining
+ * the underlined position is not sensitive to case. If the
+ * character is not part of <code>text</code>, the text will be
+ * drawn without underlining. Drawing is performed in the current
+ * color and font of <code>g</code>.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
+ * height="100" alt="[An illustration showing how to use the
+ * method]" />
+ *
+ * @param g the graphics into which the String is drawn.
+ *
+ * @param text the String to draw.
+ *
+ * @param underlinedChar the character whose first occurence in
+ * <code>text</code> will be underlined. It is not clear
+ * why the API specification declares this argument to be
+ * of type <code>int</code> instead of <code>char</code>.
+ * While this would allow to pass Unicode characters outside
+ * Basic Multilingual Plane 0 (U+0000 .. U+FFFE), at least
+ * the GNU Classpath implementation does not underline
+ * anything if <code>underlinedChar</code> is outside
+ * the range of <code>char</code>.
+ *
+ * @param x the x coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ *
+ * @param y the y coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ */
+ public static void drawString(Graphics g, String text,
+ int underlinedChar, int x, int y)
+ {
+ int index = -1;
+
+ /* It is intentional that lower case is used. In some languages,
+ * the set of lowercase characters is larger than the set of
+ * uppercase ones. Therefore, it is good practice to use lowercase
+ * for such comparisons (which really means that the author of this
+ * code can vaguely remember having read some Unicode techreport
+ * with this recommendation, but is too lazy to look for the URL).
+ */
+ if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
+ index = text.toLowerCase().indexOf(
+ Character.toLowerCase((char) underlinedChar));
+
+ drawStringUnderlineCharAt(g, text, index, x, y);
+ }
+
+
+ /**
+ * Draws a String at the given location, underlining the character
+ * at the specified index. Drawing is performed in the current color
+ * and font of <code>g</code>.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
+ * height="100" alt="[An illustration showing how to use the
+ * method]" />
+ *
+ * @param g the graphics into which the String is drawn.
+ *
+ * @param text the String to draw.
+ *
+ * @param underlinedIndex the index of the underlined character in
+ * <code>text</code>. If <code>underlinedIndex</code> falls
+ * outside the range <code>[0, text.length() - 1]</code>, the
+ * text will be drawn without underlining anything.
+ *
+ * @param x the x coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ *
+ * @param y the y coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ *
+ * @since 1.4
+ */
+ public static void drawStringUnderlineCharAt(Graphics g, String text,
+ int underlinedIndex,
+ int x, int y)
+ {
+ Graphics2D g2;
+ Rectangle2D.Double underline;
+ FontRenderContext frc;
+ FontMetrics fmet;
+ LineMetrics lineMetrics;
+ Font font;
+ TextLayout layout;
+ double underlineX1, underlineX2;
+ boolean drawUnderline;
+ int textLength;
+
+ textLength = text.length();
+ if (textLength == 0)
+ return;
+
+ drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
+
+ // FIXME: unfortunately pango and cairo can't agree on metrics
+ // so for the time being we continue to *not* use TextLayouts.
+ if (true || !(g instanceof Graphics2D))
+ {
+ /* Fall-back. This is likely to produce garbage for any text
+ * containing right-to-left (Hebrew or Arabic) characters, even
+ * if the underlined character is left-to-right.
+ */
+ g.drawString(text, x, y);
+ if (drawUnderline)
+ {
+ fmet = g.getFontMetrics();
+ g.fillRect(
+ /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
+ /* y */ y + fmet.getDescent() - 1,
+ /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
+ /* height */ 1);
+ }
+
+ return;
+ }
+
+ g2 = (Graphics2D) g;
+ font = g2.getFont();
+ frc = g2.getFontRenderContext();
+ lineMetrics = font.getLineMetrics(text, frc);
+ layout = new TextLayout(text, font, frc);
+
+ /* Draw the text. */
+ layout.draw(g2, x, y);
+ if (!drawUnderline)
+ return;
+
+ underlineX1 = x + layout.getLogicalHighlightShape(
+ underlinedIndex, underlinedIndex).getBounds2D().getX();
+ underlineX2 = x + layout.getLogicalHighlightShape(
+ underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
+
+ underline = new Rectangle2D.Double();
+ if (underlineX1 < underlineX2)
+ {
+ underline.x = underlineX1;
+ underline.width = underlineX2 - underlineX1;
+ }
+ else
+ {
+ underline.x = underlineX2;
+ underline.width = underlineX1 - underlineX2;
+ }
+
+
+ underline.height = lineMetrics.getUnderlineThickness();
+ underline.y = lineMetrics.getUnderlineOffset();
+ if (underline.y == 0)
+ {
+ /* Some fonts do not specify an underline offset, although they
+ * actually should do so. In that case, the result of calling
+ * lineMetrics.getUnderlineOffset() will be zero. Since it would
+ * look very ugly if the underline was be positioned immediately
+ * below the baseline, we check for this and move the underline
+ * below the descent, as shown in the following ASCII picture:
+ *
+ * ##### ##### #
+ * # # # #
+ * # # # #
+ * # # # #
+ * ##### ###### ---- baseline (0)
+ * #
+ * #
+ * ------------------###----------- lineMetrics.getDescent()
+ */
+ underline.y = lineMetrics.getDescent();
+ }
+
+ underline.y += y;
+ g2.fill(underline);
+ }
+
+ /**
+ * Draws a string on the specified component.
+ *
+ * @param c the component
+ * @param g the Graphics context
+ * @param text the string
+ * @param underlinedChar the character to be underlined
+ * @param x the X location
+ * @param y the Y location
+ */
+ static void drawString(JComponent c, Graphics g, String text,
+ int underlinedChar, int x, int y)
+ {
+ int index = -1;
+
+ /* It is intentional that lower case is used. In some languages,
+ * the set of lowercase characters is larger than the set of
+ * uppercase ones. Therefore, it is good practice to use lowercase
+ * for such comparisons (which really means that the author of this
+ * code can vaguely remember having read some Unicode techreport
+ * with this recommendation, but is too lazy to look for the URL).
+ */
+ if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
+ index = text.toLowerCase().indexOf(
+ Character.toLowerCase((char) underlinedChar));
+
+ drawStringUnderlineCharAt(c, g, text, index, x, y);
+ }
+
+
+ /**
+ * Draws a String at the given location, underlining the character
+ * at the specified index. Drawing is performed in the current color
+ * and font of <code>g</code>.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
+ * height="100" alt="[An illustration showing how to use the
+ * method]" />
+ *
+ * This is an accelerated version of the method with the same name. It
+ * uses a pre-laid out TextLayout stored in a client property.
+ *
+ * @param c the component that is drawn
+ * @param g the graphics into which the String is drawn.
+ *
+ * @param text the String to draw.
+ *
+ * @param underlinedIndex the index of the underlined character in
+ * <code>text</code>. If <code>underlinedIndex</code> falls
+ * outside the range <code>[0, text.length() - 1]</code>, the
+ * text will be drawn without underlining anything.
+ *
+ * @param x the x coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ *
+ * @param y the y coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ */
+ static void drawStringUnderlineCharAt(JComponent c, Graphics g, String text,
+ int underlinedIndex,
+ int x, int y)
+ {
+ Graphics2D g2;
+ Rectangle2D.Double underline;
+ FontRenderContext frc;
+ FontMetrics fmet;
+ LineMetrics lineMetrics;
+ Font font;
+ TextLayout layout;
+ double underlineX1, underlineX2;
+ boolean drawUnderline;
+ int textLength;
+
+ textLength = text.length();
+ if (textLength == 0)
+ return;
+
+ drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
+
+ // FIXME: unfortunately pango and cairo can't agree on metrics
+ // so for the time being we continue to *not* use TextLayouts.
+ if (!(g instanceof Graphics2D)
+ || SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") != null)
+ {
+ /* Fall-back. This is likely to produce garbage for any text
+ * containing right-to-left (Hebrew or Arabic) characters, even
+ * if the underlined character is left-to-right.
+ */
+ g.drawString(text, x, y);
+ if (drawUnderline)
+ {
+ fmet = g.getFontMetrics();
+ g.fillRect(
+ /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
+ /* y */ y + 1,
+ /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
+ /* height */ 1);
+ }
+
+ return;
+ }
+
+ g2 = (Graphics2D) g;
+ font = g2.getFont();
+ frc = g2.getFontRenderContext();
+ lineMetrics = font.getLineMetrics(text, frc);
+ layout = (TextLayout) c.getClientProperty(CACHED_TEXT_LAYOUT);
+ if (layout == null)
+ {
+ layout = new TextLayout(text, font, frc);
+ System.err.println("Unable to use cached TextLayout for: " + text);
+ }
+
+ /* Draw the text. */
+ layout.draw(g2, x, y);
+ if (!drawUnderline)
+ return;
+
+ underlineX1 = x + layout.getLogicalHighlightShape(
+ underlinedIndex, underlinedIndex).getBounds2D().getX();
+ underlineX2 = x + layout.getLogicalHighlightShape(
+ underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
+
+ underline = new Rectangle2D.Double();
+ if (underlineX1 < underlineX2)
+ {
+ underline.x = underlineX1;
+ underline.width = underlineX2 - underlineX1;
+ }
+ else
+ {
+ underline.x = underlineX2;
+ underline.width = underlineX1 - underlineX2;
+ }
+
+
+ underline.height = lineMetrics.getUnderlineThickness();
+ underline.y = lineMetrics.getUnderlineOffset();
+ if (underline.y == 0)
+ {
+ /* Some fonts do not specify an underline offset, although they
+ * actually should do so. In that case, the result of calling
+ * lineMetrics.getUnderlineOffset() will be zero. Since it would
+ * look very ugly if the underline was be positioned immediately
+ * below the baseline, we check for this and move the underline
+ * below the descent, as shown in the following ASCII picture:
+ *
+ * ##### ##### #
+ * # # # #
+ * # # # #
+ * # # # #
+ * ##### ###### ---- baseline (0)
+ * #
+ * #
+ * ------------------###----------- lineMetrics.getDescent()
+ */
+ underline.y = lineMetrics.getDescent();
+ }
+
+ underline.y += y;
+ g2.fill(underline);
+ }
+
+ /**
+ * Draws a rectangle, simulating a dotted stroke by painting only
+ * every second pixel along the one-pixel thick edge. The color of
+ * those pixels is the current color of the Graphics <code>g</code>.
+ * Any other pixels are left unchanged.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-7.png" width="360"
+ * height="200" alt="[An illustration that shows which pixels
+ * get painted]" />
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ */
+ public static void drawDashedRect(Graphics g,
+ int x, int y, int width, int height)
+ {
+ int right = x + width - 1;
+ int bottom = y + height - 1;
+
+ /* Draw the top and bottom edge of the dotted rectangle. */
+ for (int i = x; i <= right; i += 2)
+ {
+ g.drawLine(i, y, i, y);
+ g.drawLine(i, bottom, i, bottom);
+ }
+
+ /* Draw the left and right edge of the dotted rectangle. */
+ for (int i = y; i <= bottom; i += 2)
+ {
+ g.drawLine(x, i, x, i);
+ g.drawLine(right, i, right, i);
+ }
+ }
+
+ /**
+ * Determines the preferred width and height of an AbstractButton,
+ * given the gap between the button&#x2019;s text and icon.
+ *
+ * @param b the button whose preferred size is determined.
+ *
+ * @param textIconGap the gap between the button&#x2019;s text and
+ * icon.
+ *
+ * @return a <code>Dimension</code> object whose <code>width</code>
+ * and <code>height</code> fields indicate the preferred
+ * extent in pixels.
+ *
+ * @see javax.swing.SwingUtilities#layoutCompoundLabel(JComponent,
+ * FontMetrics, String, Icon, int, int, int, int, Rectangle, Rectangle,
+ * Rectangle, int)
+ */
+ public static Dimension getPreferredButtonSize(AbstractButton b,
+ int textIconGap)
+ {
+ // These cached rectangles are use here and in BasicButtonUI.paint(),
+ // so these two methods must never be executed concurrently. Maybe
+ // we must use other Rectangle instances here. OTOH, Swing is
+ // designed to be not thread safe, and every layout and paint operation
+ // should be performed from the EventDispatchThread, so it _should_ be
+ // OK to do this optimization.
+ Rectangle viewRect = BasicButtonUI.viewR;
+ viewRect.x = 0;
+ viewRect.y = 0;
+ viewRect.width = Short.MAX_VALUE;
+ viewRect.height = Short.MAX_VALUE;
+ Rectangle iconRect = BasicButtonUI.iconR;
+ iconRect.x = 0;
+ iconRect.y = 0;
+ iconRect.width = 0;
+ iconRect.height = 0;
+ Rectangle textRect = BasicButtonUI.textR;
+ textRect.x = 0;
+ textRect.y = 0;
+ textRect.width = 0;
+ textRect.height = 0;
+
+ SwingUtilities.layoutCompoundLabel(
+ b, // for the component orientation
+ b.getFontMetrics(b.getFont()), // see comment above
+ b.getText(),
+ b.getIcon(),
+ b.getVerticalAlignment(),
+ b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(),
+ viewRect, iconRect, textRect,
+ textIconGap);
+
+ /* +------------------------+ +------------------------+
+ * | | | |
+ * | ICON | | CONTENTCONTENTCONTENT |
+ * | TEXTTEXTTEXT | --> | CONTENTCONTENTCONTENT |
+ * | TEXTTEXTTEXT | | CONTENTCONTENTCONTENT |
+ * +------------------------+ +------------------------+
+ */
+
+ Rectangle contentRect =
+ SwingUtilities.computeUnion(textRect.x, textRect.y, textRect.width,
+ textRect.height, iconRect);
+
+ Insets insets = b.getInsets();
+ return new Dimension(insets.left + contentRect.width + insets.right,
+ insets.top + contentRect.height + insets.bottom);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java
new file mode 100644
index 000000000..a7ce8b15b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java
@@ -0,0 +1,471 @@
+/* BasicHTML.java -- Provides HTML support to ComponentUI implementations
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Container;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.io.IOException;
+import java.io.StringReader;
+
+import javax.swing.JComponent;
+import javax.swing.SwingConstants;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.Position;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
+
+/**
+ * Provides support for HTML rendering to {@link javax.swing.plaf.ComponentUI}
+ * implementations.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class BasicHTML
+{
+
+ /**
+ * This class serves as the root view for HTML rendering components.
+ * Its purpose and implementation is similar to the BasicTextUI.RootView
+ * class, only that is implements some stuff differently due to the nature
+ * of not beeing inside a JTextComponent.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class HTMLRootView extends View
+ {
+ /**
+ * The real root view.
+ */
+ private View view;
+
+ /**
+ * The component on which to render the view.
+ */
+ private JComponent component;
+
+ /**
+ * The EditorKit.
+ */
+ private EditorKit editorKit;
+
+ /**
+ * The document to use.
+ */
+ private Document document;
+
+ /**
+ * Creates a new RootView.
+ */
+ public HTMLRootView(JComponent c, View view, EditorKit kit, Document doc)
+ {
+ super(null);
+ component = c;
+ editorKit = kit;
+ document = doc;
+ setView(view);
+ setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS));
+ }
+
+ /**
+ * Returns the ViewFactory for this RootView. If the current EditorKit
+ * provides a ViewFactory, this is used. Otherwise the TextUI itself
+ * is returned as a ViewFactory.
+ *
+ * @return the ViewFactory for this RootView
+ */
+ public ViewFactory getViewFactory()
+ {
+ return editorKit.getViewFactory();
+ }
+
+ /**
+ * Indicates that the preferences of one of the child view has changed.
+ * This calls revalidate on the text component.
+ *
+ * @param v the child view which's preference has changed
+ * @param width <code>true</code> if the width preference has changed
+ * @param height <code>true</code> if the height preference has changed
+ */
+ public void preferenceChanged(View v, boolean width, boolean height)
+ {
+ component.revalidate();
+ }
+
+ /**
+ * Sets the real root view.
+ *
+ * @param v the root view to set
+ */
+ public void setView(View v)
+ {
+ if (view != null)
+ view.setParent(null);
+
+ if (v != null)
+ v.setParent(this);
+
+ view = v;
+ }
+
+ /**
+ * Overridden to forward to real view.
+ */
+ public void setSize(float w, float h)
+ {
+ view.setSize(w, h);
+ }
+
+ /**
+ * Returns the real root view, regardless of the index.
+ *
+ * @param index not used here
+ *
+ * @return the real root view, regardless of the index.
+ */
+ public View getView(int index)
+ {
+ return view;
+ }
+
+ /**
+ * Returns <code>1</code> since the RootView always contains one
+ * child, that is the real root of the View hierarchy.
+ *
+ * @return <code>1</code> since the RootView always contains one
+ * child, that is the real root of the View hierarchy
+ */
+ public int getViewCount()
+ {
+ int count = 0;
+ if (view != null)
+ count = 1;
+ return count;
+ }
+
+ /**
+ * Returns the <code>Container</code> that contains this view. This
+ * normally will be the text component that is managed by this TextUI.
+ *
+ * @return the <code>Container</code> that contains this view
+ */
+ public Container getContainer()
+ {
+ return component;
+ }
+
+ /**
+ * Returns the preferred span along the specified <code>axis</code>.
+ * This is delegated to the real root view.
+ *
+ * @param axis the axis for which the preferred span is queried
+ *
+ * @return the preferred span along the axis
+ */
+ public float getPreferredSpan(int axis)
+ {
+ if (view != null)
+ return view.getPreferredSpan(axis);
+
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Paints the view. This is delegated to the real root view.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ * @param s the allocation for the View
+ */
+ public void paint(Graphics g, Shape s)
+ {
+ if (view != null)
+ {
+ Rectangle b = s.getBounds();
+ view.setSize(b.width, b.height);
+ view.paint(g, s);
+ }
+ }
+
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * This is delegated to the real root view.
+ *
+ * @param position the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param bias either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Shape modelToView(int position, Shape a, Position.Bias bias)
+ throws BadLocationException
+ {
+ return view.modelToView(position, a, bias);
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ return view.viewToModel(x, y, a, b);
+ }
+
+ /**
+ * Notification about text insertions. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ view.insertUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Notification about text removals. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ view.removeUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Notification about text changes. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ view.changedUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param a the allocation for the view
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
+ int d, Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ return view.getNextVisualPositionFrom(pos, b, a, d, biasRet);
+ }
+
+ public int getStartOffset()
+ {
+ return 0;
+ }
+
+ public int getEndOffset()
+ {
+ return getDocument().getLength();
+ }
+
+ public Document getDocument()
+ {
+ return document;
+ }
+
+ /**
+ * Overridden to return null, as a RootView has no attributes on its own.
+ */
+ public AttributeSet getAttributes()
+ {
+ return null;
+ }
+
+ /**
+ * Overridden to provide an element for the view.
+ */
+ public Element getElement()
+ {
+ return view.getElement();
+ }
+ }
+
+ /**
+ * The key that is used to store a HTML view in a JComponent's client
+ * properties.
+ */
+ public static final String propertyKey = "html";
+
+ /**
+ * The key that is used to store the document base in a JComponent's client
+ * properties. The document base is used to resolve relative references
+ * in HTML.
+ */
+ public static final String documentBaseKey = "html.base";
+
+ /**
+ * Creates a new instance of BasicHTML. This should not be necessary since
+ * all methods in this class are static.
+ */
+ public BasicHTML()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a {@link View} instance that can be used by the component
+ * <code>c</code> to render the HTML string <code>html</code>.
+ *
+ * @param c the component that needs to render the HTML string
+ * @param html the HTML string to be rendered
+ *
+ * @return a view that can render the HTML string
+ */
+ public static View createHTMLView(JComponent c, String html)
+ {
+ // TODO: This might be wrong. Lets see if it turns out good when
+ // the javax.swing.text.html package is in a good shape.
+ HTMLDocument doc = new HTMLDocument();
+ HTMLEditorKit kit = new HTMLEditorKit();
+ StringReader reader = new StringReader(html);
+ try
+ {
+ kit.read(reader, doc, 0);
+ }
+ catch (IOException ex)
+ {
+ AssertionError err = new AssertionError("unexpected IOException");
+ err.initCause(ex);
+ throw err;
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err =
+ new AssertionError("unexpected BadLocationException");
+ err.initCause(ex);
+ throw err;
+ }
+ ViewFactory vf = kit.getViewFactory();
+ Element root = doc.getDefaultRootElement();
+ View view = vf.create(root);
+ HTMLRootView rootView = new HTMLRootView(c, view, kit, doc);
+ return rootView;
+ }
+
+ /**
+ * Returns <code>true</code> if <code>s</code> is HTML, <code>false</code>
+ * otherwise.
+ *
+ * @param s the string to test
+ *
+ * @return <code>true</code> if <code>s</code> is HTML, <code>false</code>
+ * otherwise
+ */
+ public static boolean isHTMLString(String s)
+ {
+ // We consider a string to be HTML if it contains both the '<' and '>'
+ // character at least once.
+ return (s != null) && s.contains("<") && s.contains(">");
+ }
+
+ /**
+ * Stores a HTML renderer in <code>c</code>'s client property if
+ * <code>text</code> is HTML, otherwise it clears the corresponding client
+ * property. This is useful for {@link javax.swing.plaf.ComponentUI}
+ * implementations that are shared between it's components.
+ *
+ * @param c the component to update the renderer for
+ * @param text the string to be rendered
+ */
+ public static void updateRenderer(JComponent c, String text)
+ {
+ if (isHTMLString(text))
+ c.putClientProperty(propertyKey, createHTMLView(c, text));
+ else
+ c.putClientProperty(propertyKey, null);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java b/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java
new file mode 100644
index 000000000..1b5afa7f1
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java
@@ -0,0 +1,328 @@
+/* BasicIconFactory.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.io.Serializable;
+
+import javax.swing.Icon;
+import javax.swing.JCheckBoxMenuItem;
+
+/**
+ * Creates icons for the {@link BasicLookAndFeel}.
+ */
+public class BasicIconFactory implements Serializable
+{
+ static final long serialVersionUID = 5605588811185324383L;
+
+ private static class DummyIcon
+ implements Icon
+ {
+ public int getIconHeight()
+ {
+ return 10;
+ }
+ public int getIconWidth()
+ {
+ return 10;
+ }
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color save = g.getColor();
+ g.setColor(c.getForeground());
+ g.drawRect(x, y, 10, 10);
+ g.setColor(save);
+ }
+ }
+
+ /**
+ * The icon used for CheckBoxes in the BasicLookAndFeel. This is an empty
+ * icon with a size of 13x13 pixels.
+ */
+ static class CheckBoxIcon
+ implements Icon
+ {
+ /**
+ * Returns the height of the icon. The BasicLookAndFeel CheckBox icon
+ * has a height of 13 pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconHeight()
+ {
+ return 13;
+ }
+
+ /**
+ * Returns the width of the icon. The BasicLookAndFeel CheckBox icon
+ * has a width of 13 pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconWidth()
+ {
+ return 13;
+ }
+
+ /**
+ * Paints the icon. The BasicLookAndFeel CheckBox icon is empty and does
+ * not need to be painted.
+ *
+ * @param c the component to be painted
+ * @param g the Graphics context to be painted with
+ * @param x the x position of the icon
+ * @param y the y position of the icon
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // The icon is empty and needs no painting.
+ }
+ }
+
+ /**
+ * The icon used for {@link JCheckBoxMenuItem}s in the
+ * {@link BasicLookAndFeel}. This icon has a size of 9x9 pixels.
+ */
+ static class CheckBoxMenuItemIcon
+ implements Icon
+ {
+ /**
+ * Returns the height of the icon in pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconHeight()
+ {
+ return 9;
+ }
+
+ /**
+ * Returns the width of the icon in pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconWidth()
+ {
+ return 9;
+ }
+
+ /**
+ * Paints the icon.
+ *
+ * @param c the component to be painted
+ * @param g the Graphics context to be painted with
+ * @param x the x position of the icon
+ * @param y the y position of the icon
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ JCheckBoxMenuItem item = (JCheckBoxMenuItem) c;
+ if (item.isSelected())
+ {
+ // paint the check...
+ g.setColor(Color.black);
+ g.drawLine(x + 1, y + 3, x + 1, y + 4);
+ g.drawLine(x + 2, y + 4, x + 2, y + 5);
+ for (int i = 0; i < 5; i++)
+ g.drawLine(x + 3 + i, y + 5 - i, x + 3 + i, y + 6 - i);
+ }
+ }
+ }
+
+ /**
+ * The icon used for RadioButtons in the BasicLookAndFeel. This is an empty
+ * icon with a size of 13x13 pixels.
+ */
+ static class RadioButtonIcon
+ implements Icon
+ {
+ /**
+ * Returns the height of the icon. The BasicLookAndFeel RadioButton icon
+ * has a height of 13 pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconHeight()
+ {
+ return 13;
+ }
+
+ /**
+ * Returns the width of the icon. The BasicLookAndFeel RadioButton icon
+ * has a width of 13 pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconWidth()
+ {
+ return 13;
+ }
+
+ /**
+ * Paints the icon. The BasicLookAndFeel RadioButton icon is empty and does
+ * not need to be painted.
+ *
+ * @param c the component to be painted
+ * @param g the Graphics context to be painted with
+ * @param x the x position of the icon
+ * @param y the y position of the icon
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // The icon is empty and needs no painting.
+ }
+ }
+ /** The cached CheckBoxIcon instance. */
+ private static CheckBoxIcon checkBoxIcon;
+
+ /** The cached RadioButtonIcon instance. */
+ private static RadioButtonIcon radioButtonIcon;
+
+ public static Icon getMenuItemCheckIcon()
+ {
+ return new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 13;
+ }
+
+ public int getIconWidth()
+ {
+ return 13;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+ g.drawLine(3 + x, 5 + y, 3 + x, 9 + y);
+ g.drawLine(4 + x, 5 + y, 4 + x, 9 + y);
+ g.drawLine(5 + x, 7 + y, 9 + x, 3 + y);
+ g.drawLine(5 + x, 8 + y, 9 + x, 4 + y);
+ g.setColor(saved);
+ }
+ };
+ }
+ public static Icon getMenuItemArrowIcon()
+ {
+ return new DummyIcon();
+ }
+
+ /**
+ * Returns a new instance of a 4 x 8 icon showing a small black triangle that
+ * points to the right. This is displayed in menu items that have a
+ * sub menu.
+ *
+ * @return The icon.
+ */
+ public static Icon getMenuArrowIcon()
+ {
+ return new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 8;
+ }
+ public int getIconWidth()
+ {
+ return 4;
+ }
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+ for (int i = 0; i < 4; i++)
+ g.drawLine(x + i, y + i, x + i, y + 7 - i);
+ g.setColor(saved);
+ }
+ };
+ }
+
+ /**
+ * Returns an icon for CheckBoxes in the BasicLookAndFeel. CheckBox icons
+ * in the Basic L&amp;F are empty and have a size of 13x13 pixels.
+ * This method returns a shared single instance of this icon.
+ *
+ * @return an icon for CheckBoxes in the BasicLookAndFeel
+ */
+ public static Icon getCheckBoxIcon()
+ {
+ if (checkBoxIcon == null)
+ checkBoxIcon = new CheckBoxIcon();
+ return checkBoxIcon;
+ }
+
+ /**
+ * Returns an icon for RadioButtons in the BasicLookAndFeel. RadioButton
+ * icons in the Basic L&amp;F are empty and have a size of 13x13 pixels.
+ * This method returns a shared single instance of this icon.
+ *
+ * @return an icon for RadioButtons in the BasicLookAndFeel
+ */
+ public static Icon getRadioButtonIcon()
+ {
+ if (radioButtonIcon == null)
+ radioButtonIcon = new RadioButtonIcon();
+ return radioButtonIcon;
+ }
+
+ /**
+ * Creates and returns an icon used when rendering {@link JCheckBoxMenuItem}
+ * components.
+ *
+ * @return An icon.
+ */
+ public static Icon getCheckBoxMenuItemIcon()
+ {
+ return new CheckBoxMenuItemIcon();
+ }
+
+ public static Icon getRadioButtonMenuItemIcon()
+ {
+ return getRadioButtonIcon();
+ }
+
+ public static Icon createEmptyFrameIcon()
+ {
+ return new DummyIcon();
+ }
+} // class BasicIconFactory
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java
new file mode 100644
index 000000000..484660501
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java
@@ -0,0 +1,1015 @@
+/* BasicInternalFrameTitlePane.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+/**
+ * This class acts as a titlebar for JInternalFrames.
+ */
+public class BasicInternalFrameTitlePane extends JComponent
+{
+ /**
+ * The Action responsible for closing the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class CloseAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public CloseAction()
+ {
+ super("Close");
+ }
+
+ /**
+ * This method is called when something closes the JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (frame.isClosable())
+ {
+ try
+ {
+ frame.setClosed(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+ }
+
+ /**
+ * This Action is responsible for iconifying the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class IconifyAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public IconifyAction()
+ {
+ super("Minimize");
+ }
+
+ /**
+ * This method is called when the user wants to iconify the
+ * JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (frame.isIconifiable() && ! frame.isIcon())
+ {
+ try
+ {
+ frame.setIcon(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+ }
+
+ /**
+ * This Action is responsible for maximizing the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class MaximizeAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public MaximizeAction()
+ {
+ super("Maximize");
+ }
+ /**
+ * This method is called when the user wants to maximize the
+ * JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ try
+ {
+ if (frame.isMaximizable() && ! frame.isMaximum())
+ {
+ frame.setMaximum(true);
+ maxButton.setIcon(minIcon);
+ }
+ else if (frame.isMaximum())
+ {
+ frame.setMaximum(false);
+ maxButton.setIcon(maxIcon);
+ }
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This Action is responsible for dragging the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class MoveAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public MoveAction()
+ {
+ super("Move");
+ }
+ /**
+ * This method is called when the user wants to drag the JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // FIXME: Implement keyboard driven? move actions.
+ }
+ }
+
+ /**
+ * This Action is responsible for restoring the JInternalFrame. Restoring
+ * the JInternalFrame is the same as setting the maximum property to false.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class RestoreAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public RestoreAction()
+ {
+ super("Restore");
+ }
+ /**
+ * This method is called when the user wants to restore the
+ * JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (frame.isMaximum())
+ {
+ try
+ {
+ frame.setMaximum(false);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+ }
+
+ /**
+ * This action is responsible for sizing the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class SizeAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public SizeAction()
+ {
+ super("Size");
+ }
+ /**
+ * This method is called when the user wants to resize the JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // FIXME: Not sure how size actions should be handled.
+ }
+ }
+
+ /**
+ * This class is responsible for handling property change events from the
+ * JInternalFrame and adjusting the Title Pane as necessary.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when a PropertyChangeEvent is received by the
+ * Title Pane.
+ *
+ * @param evt The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ String propName = evt.getPropertyName();
+ if (propName.equals("closable"))
+ {
+ if (evt.getNewValue().equals(Boolean.TRUE))
+ closeButton.setVisible(true);
+ else
+ closeButton.setVisible(false);
+ }
+ else if (propName.equals("iconable"))
+ {
+ if (evt.getNewValue().equals(Boolean.TRUE))
+ iconButton.setVisible(true);
+ else
+ iconButton.setVisible(false);
+ }
+ else if (propName.equals("maximizable"))
+ {
+ if (evt.getNewValue().equals(Boolean.TRUE))
+ maxButton.setVisible(true);
+ else
+ maxButton.setVisible(false);
+ }
+ enableActions();
+ }
+ }
+
+ /**
+ * This class acts as the MenuBar for the TitlePane. Clicking on the Frame
+ * Icon in the top left corner will activate it.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class SystemMenuBar extends JMenuBar
+ {
+ /**
+ * This method returns true if it can receive focus.
+ *
+ * @return True if this Component can receive focus.
+ */
+ public boolean isFocusTraversable()
+ {
+ return true;
+ }
+
+ /**
+ * This method returns true if this Component is expected to paint all of
+ * itself.
+ *
+ * @return True if this Component is expect to paint all of itself.
+ */
+ public boolean isOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * This method paints this Component.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ Icon frameIcon = frame.getFrameIcon();
+ if (frameIcon == null)
+ frameIcon = BasicDesktopIconUI.defaultIcon;
+ frameIcon.paintIcon(this, g, 0, 0);
+ }
+
+ /**
+ * This method requests that focus be given to this Component.
+ */
+ public void requestFocus()
+ {
+ super.requestFocus();
+ }
+ }
+
+ /**
+ * This class acts as the Layout Manager for the TitlePane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TitlePaneLayout implements LayoutManager
+ {
+ /**
+ * Creates a new <code>TitlePaneLayout</code> object.
+ */
+ public TitlePaneLayout()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called when adding a Component to the Container.
+ *
+ * @param name The name to reference the added Component by.
+ * @param c The Component to add.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the children of the Title Pane.
+ *
+ * @param c The Container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+ Dimension size = c.getSize();
+ Insets insets = c.getInsets();
+ int width = size.width - insets.left - insets.right;
+ int height = size.height - insets.top - insets.bottom;
+
+ // MenuBar is always present and located at the top left corner.
+ Dimension menupref = menuBar.getPreferredSize();
+ menuBar.setBounds(insets.left, insets.top, menupref.width, height);
+
+ int loc = width + insets.left - 1;
+ int top = insets.top + 1;
+ int buttonHeight = height - 4;
+ if (closeButton.isVisible())
+ {
+ int buttonWidth = closeIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ closeButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (maxButton.isVisible())
+ {
+ int buttonWidth = maxIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ maxButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (iconButton.isVisible())
+ {
+ int buttonWidth = iconIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ iconButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (title != null)
+ title.setBounds(insets.left + menupref.width, insets.top,
+ loc - menupref.width - insets.left, height);
+ }
+
+ /**
+ * This method returns the minimum size of the given Container given the
+ * children that it has.
+ *
+ * @param c The Container to get a minimum size for.
+ *
+ * @return The minimum size of the Container.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return preferredLayoutSize(c);
+ }
+
+ /**
+ * This method returns the preferred size of the given Container taking
+ * into account the children that it has.
+ *
+ * @param c The Container to lay out.
+ *
+ * @return The preferred size of the Container.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ return new Dimension(22, 18);
+ }
+
+ /**
+ * This method is called when removing a Component from the Container.
+ *
+ * @param c The Component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This helper class is used to create the minimize, maximize and close
+ * buttons in the top right corner of the Title Pane. These buttons are
+ * special since they cannot be given focus and have no border.
+ */
+ private class PaneButton extends JButton
+ {
+ /**
+ * Creates a new PaneButton object with the given Action.
+ *
+ * @param a The Action that the button uses.
+ */
+ public PaneButton(Action a)
+ {
+ super(a);
+ setMargin(new Insets(0, 0, 0, 0));
+ }
+
+ /**
+ * This method returns true if the Component can be focused.
+ *
+ * @return false.
+ */
+ public boolean isFocusable()
+ {
+ // These buttons cannot be given focus.
+ return false;
+ }
+
+ }
+
+ /** The action command for the Close action. */
+ protected static final String CLOSE_CMD;
+
+ /** The action command for the Minimize action. */
+ protected static final String ICONIFY_CMD;
+
+ /** The action command for the Maximize action. */
+ protected static final String MAXIMIZE_CMD;
+
+ /** The action command for the Move action. */
+ protected static final String MOVE_CMD;
+
+ /** The action command for the Restore action. */
+ protected static final String RESTORE_CMD;
+
+ /** The action command for the Size action. */
+ protected static final String SIZE_CMD;
+
+ /** The action associated with closing the JInternalFrame. */
+ protected Action closeAction;
+
+ /** The action associated with iconifying the JInternalFrame. */
+ protected Action iconifyAction;
+
+ /** The action associated with maximizing the JInternalFrame. */
+ protected Action maximizeAction;
+
+ /** The action associated with moving the JInternalFrame. */
+ protected Action moveAction;
+
+ /** The action associated with restoring the JInternalFrame. */
+ protected Action restoreAction;
+
+ /** The action associated with resizing the JInternalFrame. */
+ protected Action sizeAction;
+
+ /** The button that closes the JInternalFrame. */
+ protected JButton closeButton;
+
+ /** The button that iconifies the JInternalFrame. */
+ protected JButton iconButton;
+
+ /** The button that maximizes the JInternalFrame. */
+ protected JButton maxButton;
+
+ /** The icon displayed in the restore button. */
+ protected Icon minIcon = BasicIconFactory.createEmptyFrameIcon();
+
+ /** The icon displayed in the maximize button. */
+ protected Icon maxIcon = BasicIconFactory.createEmptyFrameIcon();
+
+ /** The icon displayed in the iconify button. */
+ protected Icon iconIcon = BasicIconFactory.createEmptyFrameIcon();
+
+ /** The icon displayed in the close button. */
+ protected Icon closeIcon;
+
+ /** The JInternalFrame that this TitlePane is used in. */
+ protected JInternalFrame frame;
+
+ /** The JMenuBar that is located at the top left of the Title Pane. */
+ protected JMenuBar menuBar;
+
+ /** The JMenu inside the menuBar. */
+ protected JMenu windowMenu;
+
+ /**
+ * The text color of the TitlePane when the JInternalFrame is not selected.
+ */
+ protected Color notSelectedTextColor;
+
+ /**
+ * The background color of the TitlePane when the JInternalFrame is not
+ * selected.
+ */
+ protected Color notSelectedTitleColor;
+
+ /** The text color of the titlePane when the JInternalFrame is selected. */
+ protected Color selectedTextColor;
+
+ /**
+ * The background color of the TitlePane when the JInternalFrame is
+ * selected.
+ */
+ protected Color selectedTitleColor;
+
+ /** The Property Change listener that listens to the JInternalFrame. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /**
+ * The label used to display the title. This label is not added to the
+ * TitlePane.
+ * This is package-private to avoid an accessor method.
+ */
+ transient JLabel title;
+
+ static
+ {
+ // not constants in JDK
+ CLOSE_CMD = "Close";
+ ICONIFY_CMD = "Minimize";
+ MAXIMIZE_CMD = "Maximize";
+ MOVE_CMD = "Move";
+ RESTORE_CMD = "Restore";
+ SIZE_CMD = "Size";
+ }
+
+ /**
+ * Creates a new BasicInternalFrameTitlePane object that is used in the
+ * given JInternalFrame.
+ *
+ * @param f The JInternalFrame this BasicInternalFrameTitlePane will be used
+ * in.
+ */
+ public BasicInternalFrameTitlePane(JInternalFrame f)
+ {
+ frame = f;
+ setLayout(createLayout());
+ title = new JLabel();
+ title.setHorizontalAlignment(SwingConstants.LEFT);
+ title.setHorizontalTextPosition(SwingConstants.LEFT);
+ title.setOpaque(false);
+ setOpaque(true);
+
+ setBackground(Color.LIGHT_GRAY);
+ setOpaque(true);
+
+ installTitlePane();
+ }
+
+ /**
+ * This method installs the TitlePane onto the JInternalFrameTitlePane. It
+ * also creates any children components that need to be created and adds
+ * listeners to the appropriate components.
+ */
+ protected void installTitlePane()
+ {
+ installDefaults();
+ installListeners();
+ createActions();
+
+ assembleSystemMenu();
+
+ createButtons();
+ setButtonIcons();
+ addSubComponents();
+ enableActions();
+ }
+
+ /**
+ * This method adds the sub components to the TitlePane.
+ */
+ protected void addSubComponents()
+ {
+ add(menuBar);
+
+ add(closeButton);
+ add(iconButton);
+ add(maxButton);
+ }
+
+ /**
+ * This method creates the actions that are used to manipulate the
+ * JInternalFrame.
+ */
+ protected void createActions()
+ {
+ closeAction = new CloseAction();
+ closeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, CLOSE_CMD);
+
+ iconifyAction = new IconifyAction();
+ iconifyAction.putValue(AbstractAction.ACTION_COMMAND_KEY, ICONIFY_CMD);
+
+ maximizeAction = new MaximizeAction();
+ maximizeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, MAXIMIZE_CMD);
+
+ sizeAction = new SizeAction();
+ sizeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, SIZE_CMD);
+
+ restoreAction = new RestoreAction();
+ restoreAction.putValue(AbstractAction.ACTION_COMMAND_KEY, RESTORE_CMD);
+
+ moveAction = new MoveAction();
+ moveAction.putValue(AbstractAction.ACTION_COMMAND_KEY, MOVE_CMD);
+ }
+
+ /**
+ * This method is used to install the listeners.
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+ frame.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * This method is used to uninstall the listeners.
+ */
+ protected void uninstallListeners()
+ {
+ frame.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ }
+
+ /**
+ * This method installs the defaults determined by the look and feel.
+ */
+ protected void installDefaults()
+ {
+ title.setFont(UIManager.getFont("InternalFrame.titleFont"));
+ selectedTextColor = UIManager.getColor("InternalFrame.activeTitleForeground");
+ selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground");
+ notSelectedTextColor = UIManager.getColor("InternalFrame.inactiveTitleForeground");
+ notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground");
+
+ closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
+ iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
+ maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
+ }
+
+ /**
+ * This method uninstalls the defaults.
+ */
+ protected void uninstallDefaults()
+ {
+ setFont(null);
+ selectedTextColor = null;
+ selectedTitleColor = null;
+ notSelectedTextColor = null;
+ notSelectedTitleColor = null;
+
+ closeIcon = null;
+ iconIcon = null;
+ maxIcon = null;
+ }
+
+ /**
+ * This method creates the buttons used in the TitlePane.
+ */
+ protected void createButtons()
+ {
+ closeButton = new PaneButton(closeAction);
+ closeButton.setText(null);
+ if (!frame.isClosable())
+ closeButton.setVisible(false);
+ iconButton = new PaneButton(iconifyAction);
+ iconButton.setText(null);
+ if (!frame.isIconifiable())
+ iconButton.setVisible(false);
+ maxButton = new PaneButton(maximizeAction);
+ maxButton.setText(null);
+ if (!frame.isMaximizable())
+ maxButton.setVisible(false);
+ }
+
+ /**
+ * Set icons for the minimize-, maximize- and close-buttons.
+ */
+ protected void setButtonIcons()
+ {
+ if (closeIcon != null && closeButton != null)
+ closeButton.setIcon(closeIcon);
+ if (iconIcon != null && iconButton != null)
+ iconButton.setIcon(iconIcon);
+ if (maxIcon != null && maxButton != null)
+ maxButton.setIcon(maxIcon);
+ }
+
+ /**
+ * This method creates the MenuBar used in the TitlePane.
+ */
+ protected void assembleSystemMenu()
+ {
+ menuBar = createSystemMenuBar();
+ windowMenu = createSystemMenu();
+
+ menuBar.add(windowMenu);
+
+ addSystemMenuItems(windowMenu);
+ enableActions();
+ }
+
+ /**
+ * This method adds the MenuItems to the given JMenu.
+ *
+ * @param systemMenu The JMenu to add MenuItems to.
+ */
+ protected void addSystemMenuItems(JMenu systemMenu)
+ {
+ JMenuItem tmp;
+
+ tmp = new JMenuItem(RESTORE_CMD);
+ tmp.addActionListener(restoreAction);
+ tmp.setMnemonic(KeyEvent.VK_R);
+ systemMenu.add(tmp);
+
+ tmp = new JMenuItem(MOVE_CMD);
+ tmp.addActionListener(moveAction);
+ tmp.setMnemonic(KeyEvent.VK_M);
+ systemMenu.add(tmp);
+
+ tmp = new JMenuItem(SIZE_CMD);
+ tmp.addActionListener(sizeAction);
+ tmp.setMnemonic(KeyEvent.VK_S);
+ systemMenu.add(tmp);
+
+ tmp = new JMenuItem(ICONIFY_CMD);
+ tmp.addActionListener(iconifyAction);
+ tmp.setMnemonic(KeyEvent.VK_N);
+ systemMenu.add(tmp);
+
+ tmp = new JMenuItem(MAXIMIZE_CMD);
+ tmp.addActionListener(maximizeAction);
+ tmp.setMnemonic(KeyEvent.VK_X);
+ systemMenu.add(tmp);
+
+ systemMenu.addSeparator();
+
+ tmp = new JMenuItem(CLOSE_CMD);
+ tmp.addActionListener(closeAction);
+ tmp.setMnemonic(KeyEvent.VK_C);
+ systemMenu.add(tmp);
+ }
+
+ /**
+ * This method creates a new JMenubar.
+ *
+ * @return A new JMenuBar.
+ */
+ protected JMenuBar createSystemMenuBar()
+ {
+ if (menuBar == null)
+ menuBar = new SystemMenuBar();
+ menuBar.removeAll();
+ return menuBar;
+ }
+
+ /**
+ * This method creates a new JMenu.
+ *
+ * @return A new JMenu.
+ */
+ protected JMenu createSystemMenu()
+ {
+ if (windowMenu == null)
+ windowMenu = new JMenu();
+ windowMenu.removeAll();
+ return windowMenu;
+ }
+
+ /**
+ * This method programmatically shows the JMenu.
+ */
+ protected void showSystemMenu()
+ {
+ // FIXME: Untested as KeyEvents are not hooked up.
+ menuBar.getMenu(1).getPopupMenu().show();
+ }
+
+ /**
+ * This method paints the TitlePane.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paintComponent(Graphics g)
+ {
+ paintTitleBackground(g);
+ if (frame.getTitle() != null && title != null)
+ {
+ Color saved = g.getColor();
+ Font f = title.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+ if (frame.isSelected())
+ g.setColor(selectedTextColor);
+ else
+ g.setColor(notSelectedTextColor);
+ title.setText(getTitle(frame.getTitle(), fm, title.getBounds().width));
+ SwingUtilities.paintComponent(g, title, null, title.getBounds());
+ g.setColor(saved);
+ }
+ }
+
+ /**
+ * This method paints the TitlePane's background.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintTitleBackground(Graphics g)
+ {
+ if (!isOpaque())
+ return;
+
+ Color saved = g.getColor();
+ Dimension dims = getSize();
+
+ Color bg = getBackground();
+ if (frame.isSelected())
+ bg = selectedTitleColor;
+ else
+ bg = notSelectedTitleColor;
+ g.setColor(bg);
+ g.fillRect(0, 0, dims.width, dims.height);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method returns the title string based on the available width and the
+ * font metrics.
+ *
+ * @param text The desired title.
+ * @param fm The FontMetrics of the font used.
+ * @param availableWidth The available width.
+ *
+ * @return The allowable string.
+ */
+ protected String getTitle(String text, FontMetrics fm, int availableWidth)
+ {
+ Rectangle vr = new Rectangle(0, 0, availableWidth, fm.getHeight());
+ Rectangle ir = new Rectangle();
+ Rectangle tr = new Rectangle();
+ String value = SwingUtilities.layoutCompoundLabel(this, fm, text, null,
+ SwingConstants.CENTER,
+ SwingConstants.LEFT,
+ SwingConstants.CENTER,
+ SwingConstants.LEFT, vr,
+ ir, tr, 0);
+ return value;
+ }
+
+ /**
+ * This method fires something similar to a WINDOW_CLOSING event.
+ *
+ * @param frame The JInternalFrame that is being closed.
+ */
+ protected void postClosingEvent(JInternalFrame frame)
+ {
+ // FIXME: Implement postClosingEvent when I figure out what
+ // it's supposed to do.
+ // It says that this fires an WINDOW_CLOSING like event.
+ // So the closest thing is some kind of InternalFrameEvent.
+ // But none is fired.
+ // Can't see it called or anything.
+ }
+
+ /**
+ * This method enables the actions for the TitlePane given the frame's
+ * properties.
+ */
+ protected void enableActions()
+ {
+ closeAction.setEnabled(frame.isClosable());
+
+ iconifyAction.setEnabled(frame.isIconifiable());
+ // The maximize action is responsible for restoring it
+ // as well, if clicked from the button
+ maximizeAction.setEnabled(frame.isMaximizable());
+
+ // The restoring action is only active when selected
+ // from the menu.
+ restoreAction.setEnabled(frame.isMaximum());
+
+ sizeAction.setEnabled(frame.isResizable());
+
+ // FIXME: Tie MoveAction enabled status to a variable.
+ moveAction.setEnabled(false);
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method creates a new LayoutManager for the TitlePane.
+ *
+ * @return A new LayoutManager.
+ */
+ protected LayoutManager createLayout()
+ {
+ return new TitlePaneLayout();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java
new file mode 100644
index 000000000..da37e2bd8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java
@@ -0,0 +1,1786 @@
+/* BasicInternalFrameUI.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.LayoutManager2;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.DefaultDesktopManager;
+import javax.swing.DesktopManager;
+import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JInternalFrame;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.AbstractBorder;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
+import javax.swing.event.MouseInputAdapter;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.InternalFrameUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * This is the UI delegate for the Basic look and feel for JInternalFrames.
+ */
+public class BasicInternalFrameUI extends InternalFrameUI
+{
+ /**
+ * This is a helper class that listens to the JInternalFrame for
+ * InternalFrameEvents.
+ */
+ protected class BasicInternalFrameListener implements InternalFrameListener
+ {
+ /**
+ * This method is called when the JInternalFrame is activated.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameActivated(InternalFrameEvent e)
+ {
+ frame.getGlassPane().setVisible(false);
+ }
+
+ /**
+ * This method is called when the JInternalFrame is closed.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameClosed(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method is called when the JInternalFrame is closing.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameClosing(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method is called when the JInternalFrame is deactivated.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameDeactivated(InternalFrameEvent e)
+ {
+ frame.getGlassPane().setVisible(true);
+ }
+
+ /**
+ * This method is called when the JInternalFrame is deiconified.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameDeiconified(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method is called when the JInternalFrame is iconified.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameIconified(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method is called when the JInternalFrame is opened.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameOpened(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+ }
+
+ /**
+ * This helper class listens to the edges of the JInternalFrame and the
+ * TitlePane for mouse events. It is responsible for dragging and resizing
+ * the JInternalFrame in response to the MouseEvents.
+ */
+ protected class BorderListener extends MouseInputAdapter
+ implements SwingConstants
+ {
+ /**
+ * The current shape of the cursor.
+ */
+ transient int showingCursor;
+
+ /** FIXME: Use for something. */
+ protected final int RESIZE_NONE = 0;
+
+ /** The x offset from the top left corner of the JInternalFrame. */
+ private transient int xOffset;
+
+ /** The y offset from the top left corner of the JInternalFrame. */
+ private transient int yOffset;
+
+ /** The direction that the resize is occuring in. */
+ private transient int direction = -1;
+
+ /** Cache rectangle that can be reused. */
+ private transient Rectangle cacheRect = new Rectangle();
+
+ /**
+ * This method is called when the mouse is clicked.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ // Do minimization/maximization when double-clicking in the title pane.
+ if (e.getSource() == titlePane && e.getClickCount() == 2)
+ try
+ {
+ if (frame.isMaximizable() && ! frame.isMaximum())
+ frame.setMaximum(true);
+ else if (frame.isMaximum())
+ frame.setMaximum(false);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+
+ // There is nothing to do when the mouse is clicked
+ // on the border.
+ }
+
+ /**
+ * This method is called when the mouse is dragged. This method is
+ * responsible for resizing or dragging the JInternalFrame.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ // If the frame is maximized, there is nothing that
+ // can be dragged around.
+ if (frame.isMaximum())
+ return;
+ DesktopManager dm = getDesktopManager();
+ Rectangle b = frame.getBounds();
+ Dimension min = frame.getMinimumSize();
+ if (min == null)
+ min = new Dimension(0, 0);
+ Insets insets = frame.getInsets();
+ int x = e.getX();
+ int y = e.getY();
+ if (e.getSource() == frame && frame.isResizable())
+ {
+ switch (direction)
+ {
+ case Cursor.N_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, Math.min(b.y + y, b.y + b.height
+ - min.height),
+ b.width, b.height - y);
+ break;
+ case Cursor.NE_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, Math.min(b.y + y, b.y + b.height
+ - min.height), x + 1,
+ b.height - y);
+ break;
+ case Cursor.E_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, b.y, x + 1, b.height);
+ break;
+ case Cursor.SE_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, b.y, x + 1, y + 1);
+ break;
+ case Cursor.S_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, b.y, b.width, y + 1);
+ break;
+ case Cursor.SW_RESIZE_CURSOR:
+ cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width),
+ b.y, b.width - x, y + 1);
+ break;
+ case Cursor.W_RESIZE_CURSOR:
+ cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width),
+ b.y, b.width - x, b.height);
+ break;
+ case Cursor.NW_RESIZE_CURSOR:
+ cacheRect.setBounds(
+ Math.min(b.x + x, b.x + b.width - min.width),
+ Math.min(b.y + y, b.y + b.height - min.height),
+ b.width - x, b.height - y);
+ break;
+ }
+ dm.resizeFrame(frame, cacheRect.x, cacheRect.y,
+ Math.max(min.width, cacheRect.width),
+ Math.max(min.height, cacheRect.height));
+ setCursor(e);
+ }
+ else if (e.getSource() == titlePane)
+ {
+ Rectangle fBounds = frame.getBounds();
+ frame.putClientProperty("bufferedDragging", Boolean.TRUE);
+ dm.dragFrame(frame, e.getX() - xOffset + b.x, e.getY() - yOffset
+ + b.y);
+ }
+ }
+
+ /**
+ * This method is called when the mouse exits the JInternalFrame.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ if (showingCursor != Cursor.DEFAULT_CURSOR)
+ {
+ frame.setCursor(Cursor.getDefaultCursor());
+ showingCursor = Cursor.DEFAULT_CURSOR;
+ }
+ }
+
+ /**
+ * This method is called when the mouse is moved inside the JInternalFrame.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Turn off the resize cursor if we are in the frame header.
+ if (showingCursor != Cursor.DEFAULT_CURSOR && e.getSource() != frame)
+ {
+ frame.setCursor(Cursor.getDefaultCursor());
+ showingCursor = Cursor.DEFAULT_CURSOR;
+ }
+ else if (e.getSource() == frame && frame.isResizable())
+ {
+ setCursor(e);
+ }
+ }
+
+ /**
+ * Set the mouse cursor, how applicable.
+ *
+ * @param e the current mouse event.
+ */
+ void setCursor(MouseEvent e)
+ {
+ int cursor = sectionOfClick(e.getX(), e.getY());
+ if (cursor != showingCursor)
+ {
+ Cursor resize = Cursor.getPredefinedCursor(cursor);
+ frame.setCursor(resize);
+ showingCursor = cursor;
+ }
+ }
+
+ /**
+ * This method is called when the mouse is pressed.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ activateFrame(frame);
+ DesktopManager dm = getDesktopManager();
+ int x = e.getX();
+ int y = e.getY();
+ Insets insets = frame.getInsets();
+
+ if (e.getSource() == frame && frame.isResizable())
+ {
+ direction = sectionOfClick(x, y);
+ dm.beginResizingFrame(frame, direction);
+ }
+ else if (e.getSource() == titlePane)
+ {
+ Rectangle tBounds = titlePane.getBounds();
+
+ xOffset = e.getX() - tBounds.x + insets.left;
+ yOffset = e.getY() - tBounds.y + insets.top;
+
+ dm.beginDraggingFrame(frame);
+ }
+ }
+
+ /**
+ * This method is called when the mouse is released.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ DesktopManager dm = getDesktopManager();
+ xOffset = 0;
+ yOffset = 0;
+ if (e.getSource() == frame && frame.isResizable())
+ dm.endResizingFrame(frame);
+ else if (e.getSource() == titlePane)
+ {
+ dm.endDraggingFrame(frame);
+ frame.putClientProperty("bufferedDragging", null);
+ }
+
+ setCursor(e);
+ }
+
+ /**
+ * This method determines the direction of the resize based on the
+ * coordinates and the size of the JInternalFrame.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ *
+ * @return The cursor constant, determining the resizing direction.
+ */
+ private int sectionOfClick(int x, int y)
+ {
+ Rectangle b = frame.getBounds();
+ int corner = InternalFrameBorder.cornerSize;
+
+ if (x < corner && y < corner)
+ return Cursor.NW_RESIZE_CURSOR;
+ else if (x > b.width - corner && y < corner)
+ return Cursor.NE_RESIZE_CURSOR;
+ else if (x > b.width - corner && y > b.height - corner)
+ return Cursor.SE_RESIZE_CURSOR;
+ else if (x < corner && y > b.height - corner)
+ return Cursor.SW_RESIZE_CURSOR;
+ else if (y < corner)
+ return Cursor.N_RESIZE_CURSOR;
+ else if (x < corner)
+ return Cursor.W_RESIZE_CURSOR;
+ else if (y > b.height - corner)
+ return Cursor.S_RESIZE_CURSOR;
+ else if (x > b.width - corner)
+ return Cursor.E_RESIZE_CURSOR;
+
+ return Cursor.DEFAULT_CURSOR;
+ }
+ }
+
+ /**
+ * This helper class listens to the JDesktopPane that parents this
+ * JInternalFrame and listens for resize events and resizes the
+ * JInternalFrame appropriately.
+ */
+ protected class ComponentHandler implements ComponentListener
+ {
+ /**
+ * This method is called when the JDesktopPane is hidden.
+ *
+ * @param e
+ * The ComponentEvent fired.
+ */
+ public void componentHidden(ComponentEvent e)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called when the JDesktopPane is moved.
+ *
+ * @param e
+ * The ComponentEvent fired.
+ */
+ public void componentMoved(ComponentEvent e)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called when the JDesktopPane is resized.
+ *
+ * @param e
+ * The ComponentEvent fired.
+ */
+ public void componentResized(ComponentEvent e)
+ {
+ if (frame.isMaximum())
+ {
+ Container parent = frame.getParent();
+ Insets i = parent.getInsets();
+ int width = parent.getWidth() - i.left - i.right;
+ int height = parent.getHeight() - i.top - i.bottom;
+ frame.setBounds(0, 0, width, height);
+ }
+ }
+
+ /**
+ * This method is called when the JDesktopPane is shown.
+ *
+ * @param e
+ * The ComponentEvent fired.
+ */
+ public void componentShown(ComponentEvent e)
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * This helper class acts as the LayoutManager for JInternalFrames.
+ */
+ public class InternalFrameLayout implements LayoutManager
+ {
+ /**
+ * This method is called when the given Component is added to the
+ * JInternalFrame.
+ *
+ * @param name
+ * The name of the Component.
+ * @param c
+ * The Component added.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is used to set the bounds of the children of the
+ * JInternalFrame.
+ *
+ * @param c
+ * The Container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+ Dimension dims = frame.getSize();
+ Insets insets = frame.getInsets();
+
+ dims.width -= insets.left + insets.right;
+ dims.height -= insets.top + insets.bottom;
+
+ int nh = 0;
+ int sh = 0;
+ int ew = 0;
+ int ww = 0;
+
+ if (northPane != null)
+ {
+ Dimension nDims = northPane.getPreferredSize();
+ nh = Math.min(nDims.height, dims.height);
+
+ northPane.setBounds(insets.left, insets.top, dims.width, nh);
+ }
+
+ if (southPane != null)
+ {
+ Dimension sDims = southPane.getPreferredSize();
+ sh = Math.min(sDims.height, dims.height - nh);
+
+ southPane.setBounds(insets.left, insets.top + dims.height - sh,
+ dims.width, sh);
+ }
+
+ int remHeight = dims.height - sh - nh;
+
+ if (westPane != null)
+ {
+ Dimension wDims = westPane.getPreferredSize();
+ ww = Math.min(dims.width, wDims.width);
+
+ westPane.setBounds(insets.left, insets.top + nh, ww, remHeight);
+ }
+
+ if (eastPane != null)
+ {
+ Dimension eDims = eastPane.getPreferredSize();
+ ew = Math.min(eDims.width, dims.width - ww);
+
+ eastPane.setBounds(insets.left + dims.width - ew, insets.top + nh,
+ ew, remHeight);
+ }
+
+ int remWidth = dims.width - ww - ew;
+
+ frame.getRootPane().setBounds(insets.left + ww, insets.top + nh,
+ remWidth, remHeight);
+ }
+
+ /**
+ * This method returns the minimum layout size.
+ *
+ * @param c
+ * The Container to find a minimum layout size for.
+ * @return The minimum dimensions for the JInternalFrame.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return getSize(c, true);
+ }
+
+ /**
+ * Th8is method returns the preferred layout size.
+ *
+ * @param c
+ * The Container to find a preferred layout size for.
+ * @return The preferred dimensions for the JInternalFrame.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ return getSize(c, false);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param c
+ * DOCUMENT ME!
+ * @param min
+ * DOCUMENT ME!
+ * @return DOCUMENT ME!
+ */
+ private Dimension getSize(Container c, boolean min)
+ {
+ Insets insets = frame.getInsets();
+
+ Dimension contentDims = frame.getContentPane().getPreferredSize();
+ if (min)
+ contentDims.width = contentDims.height = 0;
+ int nWidth = 0;
+ int nHeight = 0;
+ int sWidth = 0;
+ int sHeight = 0;
+ int eWidth = 0;
+ int eHeight = 0;
+ int wWidth = 0;
+ int wHeight = 0;
+ Dimension dims;
+
+ if (northPane != null)
+ {
+ dims = northPane.getPreferredSize();
+ if (dims != null)
+ {
+ nWidth = dims.width;
+ nHeight = dims.height;
+ }
+ }
+
+ if (southPane != null)
+ {
+ dims = southPane.getPreferredSize();
+ if (dims != null)
+ {
+ sWidth = dims.width;
+ sHeight = dims.height;
+ }
+ }
+
+ if (eastPane != null)
+ {
+ dims = eastPane.getPreferredSize();
+ if (dims != null)
+ {
+ sWidth = dims.width;
+ sHeight = dims.height;
+ }
+ }
+
+ if (westPane != null)
+ {
+ dims = westPane.getPreferredSize();
+ if (dims != null)
+ {
+ wWidth = dims.width;
+ wHeight = dims.height;
+ }
+ }
+
+ int width = Math.max(sWidth, nWidth);
+ width = Math.max(width, contentDims.width + eWidth + wWidth);
+
+ int height = Math.max(eHeight, wHeight);
+ height = Math.max(height, contentDims.height);
+ height += nHeight + sHeight;
+
+ width += insets.left + insets.right;
+ height += insets.top + insets.bottom;
+
+ return new Dimension(width, height);
+ }
+
+ /**
+ * This method is called when a Component is removed from the
+ * JInternalFrame.
+ *
+ * @param c The Component that was removed.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This helper class is used to listen to the JDesktopPane's glassPane for
+ * MouseEvents. The JInternalFrame can then be selected if a click is
+ * detected on its children.
+ */
+ protected class GlassPaneDispatcher implements MouseInputListener
+ {
+ /** The MouseEvent target. */
+ private transient Component mouseEventTarget;
+
+ private Component dragTarget;
+
+ /**
+ * Indicates if we are currently in a dragging operation or not.
+ */
+ private boolean isDragging;
+
+ /**
+ * This method is called when the mouse enters the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is clicked on the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is dragged in the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse exits the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is moved in the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is pressed in the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ // Experiments show that this seems to call the
+ // borderListener.mousePressed() method to activate the frame.
+ if (borderListener != null)
+ borderListener.mousePressed(e);
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is released in the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This is a helper method that dispatches the GlassPane MouseEvents to the
+ * proper component.
+ *
+ * @param e the mouse event to be dispatched
+ */
+ private void handleEvent(MouseEvent e)
+ {
+ // Find candidate component inside the JInternalFrame.
+ Component target = frame.getLayeredPane().findComponentAt(e.getX(),
+ e.getY());
+
+ // Now search upwards to find a component that actually has
+ // a MouseListener attached.
+ while (target != null
+ && target.getMouseListeners().length == 0
+ && target.getMouseMotionListeners().length == 0
+ && target.getMouseWheelListeners().length == 0)
+ {
+ target = target.getParent();
+ }
+
+ if (target != null)
+ {
+ int id = e.getID();
+ switch (id)
+ {
+ case MouseEvent.MOUSE_ENTERED:
+ // Now redispatch the thing.
+ if (! isDragging || frame.isSelected())
+ {
+ mouseEventTarget = target;
+ redispatch(id, e, mouseEventTarget);
+ }
+ break;
+ case MouseEvent.MOUSE_EXITED:
+ if (! isDragging || frame.isSelected())
+ {
+ redispatch(id, e, mouseEventTarget);
+ }
+ break;
+ case MouseEvent.MOUSE_PRESSED:
+ mouseEventTarget = target;
+ redispatch(id, e, mouseEventTarget);
+ // Start dragging.
+ dragTarget = target;
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ if (isDragging)
+ {
+ redispatch(id, e, dragTarget);
+ isDragging = false;
+ }
+ else
+ redispatch(id, e, mouseEventTarget);
+ break;
+ case MouseEvent.MOUSE_CLICKED:
+ redispatch(id, e, mouseEventTarget);
+ break;
+ case MouseEvent.MOUSE_MOVED:
+ if (target != mouseEventTarget)
+ {
+ // Create additional MOUSE_EXITED/MOUSE_ENTERED pairs.
+ redispatch(MouseEvent.MOUSE_EXITED, e, mouseEventTarget);
+ mouseEventTarget = target;
+ redispatch(MouseEvent.MOUSE_ENTERED, e, mouseEventTarget);
+ }
+ redispatch(id, e, mouseEventTarget);
+ break;
+ case MouseEvent.MOUSE_DRAGGED:
+ if (! isDragging)
+ isDragging = true;
+ redispatch(id, e, mouseEventTarget);
+ break;
+ case MouseEvent.MOUSE_WHEEL:
+ redispatch(id, e, mouseEventTarget);
+ break;
+ default:
+ assert false : "Must not reach here";
+ }
+ }
+ }
+
+ /**
+ * Redispatches the event to the real target with the specified id.
+ *
+ * @param id the new event ID
+ * @param e the original event
+ * @param target the real event target
+ */
+ private void redispatch(int id, MouseEvent e, Component target)
+ {
+ Point p = SwingUtilities.convertPoint(frame.getLayeredPane(), e.getX(),
+ e.getY(), target);
+ MouseEvent ev = new MouseEvent(target, id, e.getWhen(),
+ e.getModifiers() | e.getModifiersEx(),
+ p.x, p.y, e.getClickCount(),
+ e.isPopupTrigger());
+ target.dispatchEvent(ev);
+ }
+ }
+
+ /**
+ * This helper class listens for PropertyChangeEvents from the
+ * JInternalFrame.
+ */
+ public class InternalFramePropertyChangeListener
+ implements PropertyChangeListener
+ {
+
+ /**
+ * This method is called when one of the JInternalFrame's properties change.
+ *
+ * @param evt
+ * The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ String property = evt.getPropertyName();
+ if (property.equals(JInternalFrame.IS_MAXIMUM_PROPERTY))
+ {
+ if (frame.isMaximum())
+ maximizeFrame(frame);
+ else
+ minimizeFrame(frame);
+ }
+ else if (property.equals(JInternalFrame.IS_ICON_PROPERTY))
+ {
+ if (frame.isIcon())
+ iconifyFrame(frame);
+ else
+ deiconifyFrame(frame);
+ }
+ else if (property.equals(JInternalFrame.IS_SELECTED_PROPERTY))
+ {
+ Component glassPane = frame.getGlassPane();
+ if (frame.isSelected())
+ {
+ activateFrame(frame);
+ glassPane.setVisible(false);
+ }
+ else
+ {
+ deactivateFrame(frame);
+ glassPane.setVisible(true);
+ }
+ }
+ else if (property.equals(JInternalFrame.ROOT_PANE_PROPERTY)
+ || property.equals(JInternalFrame.GLASS_PANE_PROPERTY))
+ {
+ Component old = (Component) evt.getOldValue();
+ if (old != null)
+ {
+ old.removeMouseListener(glassPaneDispatcher);
+ old.removeMouseMotionListener(glassPaneDispatcher);
+ }
+
+ Component newPane = (Component) evt.getNewValue();
+ if (newPane != null)
+ {
+ newPane.addMouseListener(glassPaneDispatcher);
+ newPane.addMouseMotionListener(glassPaneDispatcher);
+ }
+
+ frame.revalidate();
+ }
+ else if (property.equals(JInternalFrame.IS_CLOSED_PROPERTY))
+ {
+ if (evt.getNewValue() == Boolean.TRUE)
+ {
+ Container parent = frame.getParent();
+ if (parent != null)
+ parent.removeComponentListener(componentListener);
+ closeFrame(frame);
+ }
+ }
+ else if (property.equals("ancestor"))
+ {
+ Container newParent = (Container) evt.getNewValue();
+ Container oldParent = (Container) evt.getOldValue();
+ if (newParent != null)
+ {
+ newParent.addComponentListener(componentListener);
+ }
+ else if (oldParent != null)
+ {
+ oldParent.removeComponentListener(componentListener);
+ }
+ }
+ }
+ }
+
+ /**
+ * This helper class is the border for the JInternalFrame.
+ */
+ class InternalFrameBorder extends AbstractBorder implements
+ UIResource
+ {
+ /**
+ * The width of the border.
+ */
+ static final int bSize = 5;
+
+ /**
+ * The size of the corners (also used by the mouse listener).
+ */
+ static final int cornerSize = 10;
+
+ /**
+ * This method returns whether the border is opaque.
+ *
+ * @return Whether the border is opaque.
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * This method returns the insets of the border.
+ *
+ * @param c
+ * The Component to find border insets for.
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(bSize, bSize, bSize, bSize);
+ }
+
+ /**
+ * This method paints the border.
+ *
+ * @param c
+ * The Component that owns the border.
+ * @param g
+ * The Graphics object to paint with.
+ * @param x
+ * The x coordinate to paint at.
+ * @param y
+ * The y coordinate to paint at.
+ * @param width
+ * The width of the Component.
+ * @param height
+ * The height of the Component.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+ Rectangle b = frame.getBounds();
+
+ Color d = c.getBackground();
+ g.setColor(d);
+ g.fillRect(0, 0, bSize, b.height);
+ g.fillRect(0, 0, b.width, bSize);
+ g.fillRect(0, b.height - bSize, b.width, bSize);
+ g.fillRect(b.width - bSize, 0, bSize, b.height);
+
+ int x1 = 0;
+ int x2 = bSize;
+ int x3 = b.width - bSize;
+ int x4 = b.width;
+
+ int y1 = 0;
+ int y2 = bSize;
+ int y3 = b.height - bSize;
+ int y4 = b.height;
+
+ g.setColor(Color.GRAY);
+ g.fillRect(0, 0, bSize, y4);
+ g.fillRect(0, 0, x4, bSize);
+ g.fillRect(0, y3, b.width, bSize);
+ g.fillRect(x3, 0, bSize, b.height);
+
+ g.fill3DRect(0, cornerSize, bSize, b.height - 2 * cornerSize, false);
+ g.fill3DRect(cornerSize, 0, b.width - 2 * cornerSize, bSize, false);
+ g.fill3DRect(cornerSize, b.height - bSize, b.width - 2 * cornerSize,
+ bSize, false);
+ g.fill3DRect(b.width - bSize, cornerSize, bSize,
+ b.height - 2 * cornerSize, false);
+
+ g.translate(-x, -y);
+ g.setColor(saved);
+ }
+ }
+
+ /**
+ * This action triggers the system menu.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class ShowSystemMenuAction
+ extends AbstractAction
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ if (titlePane != null)
+ {
+ titlePane.showSystemMenu();
+ }
+ }
+ }
+
+ /**
+ * The MouseListener that is responsible for dragging and resizing the
+ * JInternalFrame in response to MouseEvents.
+ */
+ protected MouseInputAdapter borderListener;
+
+ /**
+ * The ComponentListener that is responsible for resizing the JInternalFrame
+ * in response to ComponentEvents from the JDesktopPane.
+ */
+ protected ComponentListener componentListener;
+
+ /**
+ * The MouseListener that is responsible for activating the JInternalFrame
+ * when the mouse press activates one of its descendents.
+ */
+ protected MouseInputListener glassPaneDispatcher;
+
+ /**
+ * The PropertyChangeListener that is responsible for listening to
+ * PropertyChangeEvents from the JInternalFrame.
+ */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The InternalFrameListener that listens to the JInternalFrame. */
+ private transient BasicInternalFrameListener internalFrameListener;
+
+ /** The JComponent placed at the east region of the JInternalFrame. */
+ protected JComponent eastPane;
+
+ /** The JComponent placed at the north region of the JInternalFrame. */
+ protected JComponent northPane;
+
+ /** The JComponent placed at the south region of the JInternalFrame. */
+ protected JComponent southPane;
+
+ /** The JComponent placed at the west region of the JInternalFrame. */
+ protected JComponent westPane;
+
+ /**
+ * The Keystroke bound to open the menu.
+ * @deprecated
+ */
+ protected KeyStroke openMenuKey;
+
+ /** The TitlePane displayed at the top of the JInternalFrame. */
+ protected BasicInternalFrameTitlePane titlePane;
+
+ /** The JInternalFrame this UI is responsible for. */
+ protected JInternalFrame frame;
+
+ /** The LayoutManager used in the JInternalFrame. */
+ protected LayoutManager internalFrameLayout;
+
+ /** The JDesktopPane that is the parent of the JInternalFrame. */
+ private transient JDesktopPane desktopPane;
+
+ /**
+ * Creates a new BasicInternalFrameUI object.
+ *
+ * @param b The JInternalFrame this UI will represent.
+ */
+ public BasicInternalFrameUI(JInternalFrame b)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method will create a new BasicInternalFrameUI for the given
+ * JComponent.
+ *
+ * @param b The JComponent to create a BasicInternalFrameUI for.
+ *
+ * @return A new BasicInternalFrameUI.
+ */
+ public static ComponentUI createUI(JComponent b)
+ {
+ return new BasicInternalFrameUI((JInternalFrame) b);
+ }
+
+ /**
+ * This method installs a UI for the JInternalFrame.
+ *
+ * @param c The JComponent to install this UI on.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JInternalFrame)
+ {
+ frame = (JInternalFrame) c;
+
+ installDefaults();
+ installListeners();
+ installComponents();
+ installKeyboardActions();
+
+ if (! frame.isSelected())
+ frame.getGlassPane().setVisible(true);
+ }
+ }
+
+ /**
+ * This method reverses the work done by installUI.
+ *
+ * @param c The JComponent to uninstall this UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallComponents();
+ uninstallListeners();
+ uninstallDefaults();
+
+ frame.getRootPane().getGlassPane().setVisible(false);
+ frame = null;
+ }
+
+ /**
+ * This method installs the defaults specified by the look and feel.
+ */
+ protected void installDefaults()
+ {
+ internalFrameLayout = createLayoutManager();
+ frame.setLayout(internalFrameLayout);
+ LookAndFeel.installBorder(frame, "InternalFrame.border");
+ frame.setFrameIcon(UIManager.getIcon("InternalFrame.icon"));
+
+ // Let the content pane inherit the background color from its
+ // frame by setting the background to null.
+ Component contentPane = frame.getContentPane();
+ if (contentPane != null
+ && contentPane.getBackground() instanceof UIResource)
+ {
+ contentPane.setBackground(null);
+ }
+ }
+
+ /**
+ * This method installs the keyboard actions for the JInternalFrame.
+ */
+ protected void installKeyboardActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ am.put("showSystemMenu", new ShowSystemMenuAction());
+
+ // The RI impl installs the audio actions as parent of the UI action map,
+ // so do we.
+ BasicLookAndFeel blaf = (BasicLookAndFeel) UIManager.getLookAndFeel();
+ ActionMap audioActionMap = blaf.getAudioActionMap();
+ am.setParent(audioActionMap);
+
+ SwingUtilities.replaceUIActionMap(frame, am);
+ }
+
+ /**
+ * This method installs the Components for the JInternalFrame.
+ */
+ protected void installComponents()
+ {
+ setNorthPane(createNorthPane(frame));
+ setSouthPane(createSouthPane(frame));
+ setEastPane(createEastPane(frame));
+ setWestPane(createWestPane(frame));
+ }
+
+ /**
+ * This method installs the listeners for the JInternalFrame.
+ */
+ protected void installListeners()
+ {
+ glassPaneDispatcher = createGlassPaneDispatcher();
+ createInternalFrameListener();
+ borderListener = createBorderListener(frame);
+ componentListener = createComponentListener();
+ propertyChangeListener = createPropertyChangeListener();
+
+ frame.addMouseListener(borderListener);
+ frame.addMouseMotionListener(borderListener);
+ frame.addInternalFrameListener(internalFrameListener);
+ frame.addPropertyChangeListener(propertyChangeListener);
+ frame.getRootPane().getGlassPane().addMouseListener(glassPaneDispatcher);
+ frame.getRootPane().getGlassPane().addMouseMotionListener(glassPaneDispatcher);
+
+ Container parent = frame.getParent();
+ if (parent != null)
+ {
+ parent.addComponentListener(componentListener);
+ }
+ }
+
+ /**
+ * This method uninstalls the defaults for the JInternalFrame.
+ */
+ protected void uninstallDefaults()
+ {
+ frame.setBorder(null);
+ frame.setLayout(null);
+ internalFrameLayout = null;
+ }
+
+ /**
+ * This method uninstalls the Components for the JInternalFrame.
+ */
+ protected void uninstallComponents()
+ {
+ setNorthPane(null);
+ setSouthPane(null);
+ setEastPane(null);
+ setWestPane(null);
+ }
+
+ /**
+ * This method uninstalls the listeners for the JInternalFrame.
+ */
+ protected void uninstallListeners()
+ {
+
+ Container parent = frame.getParent();
+ if (parent != null)
+ {
+ parent.removeComponentListener(componentListener);
+ }
+ componentListener = null;
+
+ frame.getRootPane().getGlassPane().removeMouseMotionListener(glassPaneDispatcher);
+ frame.getRootPane().getGlassPane().removeMouseListener(glassPaneDispatcher);
+
+ frame.removePropertyChangeListener(propertyChangeListener);
+ frame.removeInternalFrameListener(internalFrameListener);
+ frame.removeMouseMotionListener(borderListener);
+ frame.removeMouseListener(borderListener);
+
+ propertyChangeListener = null;
+
+ borderListener = null;
+ internalFrameListener = null;
+ glassPaneDispatcher = null;
+ }
+
+ /**
+ * This method uninstalls the keyboard actions for the JInternalFrame.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIActionMap(frame, null);
+ SwingUtilities.replaceUIInputMap(frame, JComponent.WHEN_IN_FOCUSED_WINDOW,
+ null);
+ }
+
+ /**
+ * This method creates a new LayoutManager for the JInternalFrame.
+ *
+ * @return A new LayoutManager for the JInternalFrame.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new InternalFrameLayout();
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener for the JInternalFrame.
+ *
+ * @return A new PropertyChangeListener for the JInternalFrame.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new InternalFramePropertyChangeListener();
+ }
+
+ /**
+ * This method returns the preferred size of the given JComponent.
+ *
+ * @param x The JComponent to find a preferred size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent x)
+ {
+ Dimension pref = null;
+ LayoutManager layout = frame.getLayout();
+ if (frame == x && layout != null)
+ pref = layout.preferredLayoutSize(frame);
+ else
+ pref = new Dimension(100, 100);
+ return pref;
+ }
+
+ /**
+ * This method returns the minimum size of the given JComponent.
+ *
+ * @param x The JComponent to find a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent x)
+ {
+ Dimension min = null;
+ LayoutManager layout = frame.getLayout();
+ if (frame == x && layout != null)
+ min = layout.minimumLayoutSize(frame);
+ else
+ min = new Dimension(0, 0);
+ return min;
+ }
+
+ /**
+ * This method returns the maximum size of the given JComponent.
+ *
+ * @param x The JComponent to find a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent x)
+ {
+ Dimension max = null;
+ LayoutManager layout = frame.getLayout();
+ if (frame == x && layout != null && layout instanceof LayoutManager2)
+ max = ((LayoutManager2) layout).maximumLayoutSize(frame);
+ else
+ max = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ return max;
+ }
+
+ /**
+ * This method replaces the currentPane with the newPane. When replacing it
+ * also removes the MouseHandlers for the old pane and installs them on
+ * the new pane.
+ *
+ * @param currentPane The old pane to remove.
+ * @param newPane The new pane to install.
+ */
+ protected void replacePane(JComponent currentPane, JComponent newPane)
+ {
+ if (currentPane != null)
+ {
+ deinstallMouseHandlers(currentPane);
+ frame.remove(currentPane);
+ }
+
+ if (newPane != null)
+ {
+ installMouseHandlers(newPane);
+ frame.add(newPane);
+ }
+ }
+
+ /**
+ * This method removes the necessary MouseListeners from the given
+ * JComponent.
+ *
+ * @param c The JComponent to remove MouseListeners from.
+ */
+ protected void deinstallMouseHandlers(JComponent c)
+ {
+ c.removeMouseListener(borderListener);
+ c.removeMouseMotionListener(borderListener);
+ }
+
+ /**
+ * This method installs the necessary MouseListeners from the given
+ * JComponent.
+ *
+ * @param c The JComponent to install MouseListeners on.
+ */
+ protected void installMouseHandlers(JComponent c)
+ {
+ c.addMouseListener(borderListener);
+ c.addMouseMotionListener(borderListener);
+ }
+
+ /**
+ * This method creates the north pane used in the JInternalFrame.
+ *
+ * @param w The JInternalFrame to create a north pane for.
+ *
+ * @return The north pane.
+ */
+ protected JComponent createNorthPane(JInternalFrame w)
+ {
+ titlePane = new BasicInternalFrameTitlePane(w);
+ return titlePane;
+ }
+
+ /**
+ * This method creates the west pane used in the JInternalFrame.
+ *
+ * @param w The JInternalFrame to create a west pane for.
+ *
+ * @return The west pane.
+ */
+ protected JComponent createWestPane(JInternalFrame w)
+ {
+ return null;
+ }
+
+ /**
+ * This method creates the south pane used in the JInternalFrame.
+ *
+ * @param w The JInternalFrame to create a south pane for.
+ *
+ * @return The south pane.
+ */
+ protected JComponent createSouthPane(JInternalFrame w)
+ {
+ return null;
+ }
+
+ /**
+ * This method creates the east pane used in the JInternalFrame.
+ *
+ * @param w The JInternalFrame to create an east pane for.
+ *
+ * @return The east pane.
+ */
+ protected JComponent createEastPane(JInternalFrame w)
+ {
+ return null;
+ }
+
+ /**
+ * This method returns a new BorderListener for the given JInternalFrame.
+ *
+ * @param w The JIntenalFrame to create a BorderListener for.
+ *
+ * @return A new BorderListener.
+ */
+ protected MouseInputAdapter createBorderListener(JInternalFrame w)
+ {
+ return new BorderListener();
+ }
+
+ /**
+ * This method creates a new InternalFrameListener for the JInternalFrame.
+ */
+ protected void createInternalFrameListener()
+ {
+ internalFrameListener = new BasicInternalFrameListener();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected final boolean isKeyBindingRegistered()
+ {
+ // FIXME: Implement.
+ return false;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param b DOCUMENT ME!
+ */
+ protected final void setKeyBindingRegistered(boolean b)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public final boolean isKeyBindingActive()
+ {
+ // FIXME: Implement.
+ return false;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param b DOCUMENT ME!
+ */
+ protected final void setKeyBindingActive(boolean b)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ protected void setupMenuOpenKey()
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ protected void setupMenuCloseKey()
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method returns the north pane.
+ *
+ * @return The north pane.
+ */
+ public JComponent getNorthPane()
+ {
+ return northPane;
+ }
+
+ /**
+ * This method sets the north pane to be the given JComponent.
+ *
+ * @param c The new north pane.
+ */
+ public void setNorthPane(JComponent c)
+ {
+ replacePane(northPane, c);
+ northPane = c;
+ // the following is needed to make internal frames draggable when using
+ // the JGoodies PlasticLookAndFeel, because it overrides the
+ // createNorthPane() method and doesn't assign anything to the titlePane
+ // field. It is possible there is another way to make this work, but
+ // I didn't find it...
+ if (c instanceof BasicInternalFrameTitlePane)
+ titlePane = (BasicInternalFrameTitlePane) c;
+ }
+
+ /**
+ * This method returns the south pane.
+ *
+ * @return The south pane.
+ */
+ public JComponent getSouthPane()
+ {
+ return southPane;
+ }
+
+ /**
+ * This method sets the south pane to be the given JComponent.
+ *
+ * @param c The new south pane.
+ */
+ public void setSouthPane(JComponent c)
+ {
+ replacePane(southPane, c);
+ southPane = c;
+ }
+
+ /**
+ * This method sets the east pane to be the given JComponent.
+ *
+ * @param c The new east pane.
+ */
+ public void setEastPane(JComponent c)
+ {
+ replacePane(eastPane, c);
+ eastPane = c;
+ }
+
+ /**
+ * This method returns the east pane.
+ *
+ * @return The east pane.
+ */
+ public JComponent getEastPane()
+ {
+ return eastPane;
+ }
+
+ /**
+ * This method sets the west pane to be the given JComponent.
+ *
+ * @param c The new west pane.
+ */
+ public void setWestPane(JComponent c)
+ {
+ replacePane(westPane, c);
+ westPane = c;
+ }
+
+ /**
+ * This method returns the west pane.
+ *
+ * @return The west pane.
+ */
+ public JComponent getWestPane()
+ {
+ return westPane;
+ }
+
+ /**
+ * This method returns the DesktopManager to use with the JInternalFrame.
+ *
+ * @return The DesktopManager to use with the JInternalFrame.
+ */
+ protected DesktopManager getDesktopManager()
+ {
+ DesktopManager value = null;
+ JDesktopPane pane = frame.getDesktopPane();
+ if (pane != null)
+ value = frame.getDesktopPane().getDesktopManager();
+ if (value == null)
+ value = createDesktopManager();
+ return value;
+ }
+
+ /**
+ * This method returns a default DesktopManager that can be used with this
+ * JInternalFrame.
+ *
+ * @return A default DesktopManager that can be used with this
+ * JInternalFrame.
+ */
+ protected DesktopManager createDesktopManager()
+ {
+ return new DefaultDesktopManager();
+ }
+
+ /**
+ * This is a convenience method that closes the JInternalFrame.
+ *
+ * @param f The JInternalFrame to close.
+ */
+ protected void closeFrame(JInternalFrame f)
+ {
+ getDesktopManager().closeFrame(f);
+ }
+
+ /**
+ * This is a convenience method that maximizes the JInternalFrame.
+ *
+ * @param f The JInternalFrame to maximize.
+ */
+ protected void maximizeFrame(JInternalFrame f)
+ {
+ getDesktopManager().maximizeFrame(f);
+ }
+
+ /**
+ * This is a convenience method that minimizes the JInternalFrame.
+ *
+ * @param f The JInternalFrame to minimize.
+ */
+ protected void minimizeFrame(JInternalFrame f)
+ {
+ getDesktopManager().minimizeFrame(f);
+ }
+
+ /**
+ * This is a convenience method that iconifies the JInternalFrame.
+ *
+ * @param f The JInternalFrame to iconify.
+ */
+ protected void iconifyFrame(JInternalFrame f)
+ {
+ getDesktopManager().iconifyFrame(f);
+ }
+
+ /**
+ * This is a convenience method that deiconifies the JInternalFrame.
+ *
+ * @param f The JInternalFrame to deiconify.
+ */
+ protected void deiconifyFrame(JInternalFrame f)
+ {
+ getDesktopManager().deiconifyFrame(f);
+ }
+
+ /**
+ * This is a convenience method that activates the JInternalFrame.
+ *
+ * @param f The JInternalFrame to activate.
+ */
+ protected void activateFrame(JInternalFrame f)
+ {
+ getDesktopManager().activateFrame(f);
+ }
+
+ /**
+ * This is a convenience method that deactivates the JInternalFrame.
+ *
+ * @param f the JInternalFrame to deactivate
+ */
+ protected void deactivateFrame(JInternalFrame f)
+ {
+ getDesktopManager().deactivateFrame(f);
+ }
+
+ /**
+ * This method returns a new ComponentListener for the JDesktopPane.
+ *
+ * @return A new ComponentListener.
+ */
+ protected ComponentListener createComponentListener()
+ {
+ return new ComponentHandler();
+ }
+
+ /**
+ * This method returns a new GlassPaneDispatcher.
+ *
+ * @return A new GlassPaneDispatcher.
+ */
+ protected MouseInputListener createGlassPaneDispatcher()
+ {
+ return new GlassPaneDispatcher();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java
new file mode 100644
index 000000000..9469b5695
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java
@@ -0,0 +1,542 @@
+/* BasicLabelUI.java
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 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.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.LabelUI;
+import javax.swing.text.View;
+
+/**
+ * This is the Basic Look and Feel class for the JLabel. One BasicLabelUI
+ * object is used to paint all JLabels that utilize the Basic Look and Feel.
+ */
+public class BasicLabelUI extends LabelUI implements PropertyChangeListener
+{
+ /** The labelUI that is shared by all labels. */
+ protected static BasicLabelUI labelUI;
+
+ /**
+ * These fields hold the rectangles for the whole label,
+ * the icon and the text.
+ */
+ private Rectangle vr;
+ private Rectangle ir;
+ private Rectangle tr;
+
+ /**
+ * A cached Insets object for reuse in the label layout methods.
+ */
+ private Insets cachedInsets;
+
+ /**
+ * Creates a new BasicLabelUI object.
+ */
+ public BasicLabelUI()
+ {
+ super();
+ vr = new Rectangle();
+ ir = new Rectangle();
+ tr = new Rectangle();
+ }
+
+ /**
+ * Creates and returns a UI for the label. Since one UI is shared by all
+ * labels, this means creating only if necessary and returning the shared
+ * UI.
+ *
+ * @param c The {@link JComponent} that a UI is being created for.
+ *
+ * @return A label UI for the Basic Look and Feel.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ if (labelUI == null)
+ labelUI = new BasicLabelUI();
+ return labelUI;
+ }
+
+ /**
+ * Returns the preferred size of this component as calculated by the
+ * {@link #layoutCL(JLabel, FontMetrics, String, Icon, Rectangle, Rectangle,
+ * Rectangle)} method.
+ *
+ * @param c This {@link JComponent} to get a preferred size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ JLabel lab = (JLabel) c;
+ Insets insets = lab.getInsets();
+ int insetsX = insets.left + insets.right;
+ int insetsY = insets.top + insets.bottom;
+ Icon icon = lab.getIcon();
+ String text = lab.getText();
+ Dimension ret;
+ if (icon == null && text == null)
+ ret = new Dimension(insetsX, insetsY);
+ else if (icon != null && text == null)
+ ret = new Dimension(icon.getIconWidth() + insetsX,
+ icon.getIconHeight() + insetsY);
+ else
+ {
+ FontMetrics fm = getFontMetrics(lab);
+ ir.x = 0;
+ ir.y = 0;
+ ir.width = 0;
+ ir.height = 0;
+ tr.x = 0;
+ tr.y = 0;
+ tr.width = 0;
+ tr.height = 0;
+ vr.x = 0;
+ vr.y = 0;
+ vr.width = Short.MAX_VALUE;
+ vr.height = Short.MAX_VALUE;
+ layoutCL(lab, fm, text, icon, vr, ir, tr);
+ Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width,
+ tr.height, ir);
+ ret = new Dimension(cr.width + insetsX, cr.height + insetsY);
+ }
+ return ret;
+ }
+
+ /**
+ * This method returns the minimum size of the {@link JComponent} given. If
+ * this method returns null, then it is up to the Layout Manager to give
+ * this component a minimum size.
+ *
+ * @param c The {@link JComponent} to get a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the maximum size of the {@link JComponent} given. If
+ * this method returns null, then it is up to the Layout Manager to give
+ * this component a maximum size.
+ *
+ * @param c The {@link JComponent} to get a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * The method that paints the label according to its current state.
+ *
+ * @param g The {@link Graphics} object to paint with.
+ * @param c The {@link JComponent} to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ JLabel b = (JLabel) c;
+ Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon();
+ String text = b.getText();
+ if (icon != null || (text != null && ! text.equals("")))
+ {
+ FontMetrics fm = getFontMetrics(b);
+ Insets i = c.getInsets(cachedInsets);
+ vr.x = i.left;
+ vr.y = i.right;
+ vr.width = c.getWidth() - i.left - i.right;
+ vr.height = c.getHeight() - i.top - i.bottom;
+ ir.x = 0;
+ ir.y = 0;
+ ir.width = 0;
+ ir.height = 0;
+ tr.x = 0;
+ tr.y = 0;
+ tr.width = 0;
+ tr.height = 0;
+
+ text = layoutCL(b, fm, text, icon, vr, ir, tr);
+
+ if (icon != null)
+ icon.paintIcon(b, g, ir.x, ir.y);
+
+ if (text != null && ! text.equals(""))
+ {
+ Object htmlRenderer = b.getClientProperty(BasicHTML.propertyKey);
+ if (htmlRenderer == null)
+ {
+ if (b.isEnabled())
+ paintEnabledText(b, g, text, tr.x, tr.y + fm.getAscent());
+ else
+ paintDisabledText(b, g, text, tr.x, tr.y + fm.getAscent());
+ }
+ else
+ {
+ ((View) htmlRenderer).paint(g, tr);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method is simply calls SwingUtilities's layoutCompoundLabel.
+ *
+ * @param label The label to lay out.
+ * @param fontMetrics The FontMetrics for the font used.
+ * @param text The text to paint.
+ * @param icon The icon to draw.
+ * @param viewR The entire viewable rectangle.
+ * @param iconR The icon bounds rectangle.
+ * @param textR The text bounds rectangle.
+ *
+ * @return A possibly clipped version of the text.
+ */
+ protected String layoutCL(JLabel label, FontMetrics fontMetrics, String text,
+ Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR)
+ {
+ return SwingUtilities.layoutCompoundLabel(label, fontMetrics, text, icon,
+ label.getVerticalAlignment(), label.getHorizontalAlignment(), label
+ .getVerticalTextPosition(), label.getHorizontalTextPosition(),
+ viewR, iconR, textR, label.getIconTextGap());
+ }
+
+ /**
+ * Paints the text if the label is disabled. By default, this paints the
+ * clipped text returned by layoutCompoundLabel using the
+ * background.brighter() color. It also paints the same text using the
+ * background.darker() color one pixel to the right and one pixel down.
+ *
+ * @param l The {@link JLabel} being painted.
+ * @param g The {@link Graphics} object to paint with.
+ * @param s The String to paint.
+ * @param textX The x coordinate of the start of the baseline.
+ * @param textY The y coordinate of the start of the baseline.
+ */
+ protected void paintDisabledText(JLabel l, Graphics g, String s, int textX,
+ int textY)
+ {
+ g.setColor(l.getBackground().brighter());
+
+ int mnemIndex = l.getDisplayedMnemonicIndex();
+
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX,
+ textY);
+ else
+ g.drawString(s, textX, textY);
+
+ g.setColor(l.getBackground().darker());
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX + 1,
+ textY + 1);
+ else
+ g.drawString(s, textX + 1, textY + 1);
+ }
+
+ /**
+ * Paints the text if the label is enabled. The text is painted using the
+ * foreground color.
+ *
+ * @param l The {@link JLabel} being painted.
+ * @param g The {@link Graphics} object to paint with.
+ * @param s The String to paint.
+ * @param textX The x coordinate of the start of the baseline.
+ * @param textY The y coordinate of the start of the baseline.
+ */
+ protected void paintEnabledText(JLabel l, Graphics g, String s, int textX,
+ int textY)
+ {
+ g.setColor(l.getForeground());
+
+ int mnemIndex = l.getDisplayedMnemonicIndex();
+
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX,
+ textY);
+ else
+ g.drawString(s, textX, textY);
+ }
+
+ /**
+ * This method installs the UI for the given {@link JComponent}. This
+ * method will install the component, defaults, listeners, and keyboard
+ * actions.
+ *
+ * @param c The {@link JComponent} that this UI is being installed on.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JLabel)
+ {
+ JLabel l = (JLabel) c;
+
+ installComponents(l);
+ installDefaults(l);
+ installListeners(l);
+ installKeyboardActions(l);
+ }
+ }
+
+ /**
+ * This method uninstalls the UI for the given {@link JComponent}. This
+ * method will uninstall the component, defaults, listeners, and keyboard
+ * actions.
+ *
+ * @param c The {@link JComponent} that this UI is being installed on.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ if (c instanceof JLabel)
+ {
+ JLabel l = (JLabel) c;
+
+ uninstallKeyboardActions(l);
+ uninstallListeners(l);
+ uninstallDefaults(l);
+ uninstallComponents(l);
+ }
+ }
+
+ /**
+ * This method installs the components for this {@link JLabel}.
+ *
+ * @param c The {@link JLabel} to install components for.
+ */
+ protected void installComponents(JLabel c)
+ {
+ BasicHTML.updateRenderer(c, c.getText());
+ }
+
+ /**
+ * This method uninstalls the components for this {@link JLabel}.
+ *
+ * @param c The {@link JLabel} to uninstall components for.
+ */
+ protected void uninstallComponents(JLabel c)
+ {
+ c.putClientProperty(BasicHTML.propertyKey, null);
+ c.putClientProperty(BasicHTML.documentBaseKey, null);
+ }
+
+ /**
+ * This method installs the defaults that are defined in the Basic look and
+ * feel for this {@link JLabel}.
+ *
+ * @param c The {@link JLabel} to install defaults for.
+ */
+ protected void installDefaults(JLabel c)
+ {
+ LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground",
+ "Label.font");
+ //XXX: There are properties we don't use called disabledForeground
+ //and disabledShadow.
+ }
+
+ /**
+ * This method uninstalls the defaults that are defined in the Basic look
+ * and feel for this {@link JLabel}.
+ *
+ * @param c The {@link JLabel} to uninstall defaults for.
+ */
+ protected void uninstallDefaults(JLabel c)
+ {
+ c.setForeground(null);
+ c.setBackground(null);
+ c.setFont(null);
+ }
+
+ /**
+ * Installs the keyboard actions for the given {@link JLabel}.
+ *
+ * @param l The {@link JLabel} to install keyboard actions for.
+ */
+ protected void installKeyboardActions(JLabel l)
+ {
+ Component c = l.getLabelFor();
+ if (c != null)
+ {
+ int mnemonic = l.getDisplayedMnemonic();
+ if (mnemonic > 0)
+ {
+ // add a keystroke for the given mnemonic mapping to 'press';
+ InputMap keyMap = new InputMap();
+ keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.VK_ALT),
+ "press");
+ SwingUtilities.replaceUIInputMap(l,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, keyMap);
+
+ // add an action to focus the component when 'press' happens
+ ActionMap map = new ActionMap();
+ map.put("press", new AbstractAction() {
+ public void actionPerformed(ActionEvent event)
+ {
+ JLabel label = (JLabel) event.getSource();
+ Component c = label.getLabelFor();
+ if (c != null)
+ c.requestFocus();
+ }
+ });
+ SwingUtilities.replaceUIActionMap(l, map);
+ }
+ }
+ }
+
+ /**
+ * This method uninstalls the keyboard actions for the given {@link JLabel}.
+ *
+ * @param l The {@link JLabel} to uninstall keyboard actions for.
+ */
+ protected void uninstallKeyboardActions(JLabel l)
+ {
+ SwingUtilities.replaceUIActionMap(l, null);
+ SwingUtilities.replaceUIInputMap(l, JComponent.WHEN_IN_FOCUSED_WINDOW,
+ null);
+ }
+
+ /**
+ * This method installs the listeners for the given {@link JLabel}. The UI
+ * delegate only listens to the label.
+ *
+ * @param c The {@link JLabel} to install listeners for.
+ */
+ protected void installListeners(JLabel c)
+ {
+ c.addPropertyChangeListener(this);
+ }
+
+ /**
+ * This method uninstalls the listeners for the given {@link JLabel}. The UI
+ * delegate only listens to the label.
+ *
+ * @param c The {@link JLabel} to uninstall listeners for.
+ */
+ protected void uninstallListeners(JLabel c)
+ {
+ c.removePropertyChangeListener(this);
+ }
+
+ /**
+ * This method is called whenever any JLabel's that use this UI has one of
+ * their properties change.
+ *
+ * @param e The {@link PropertyChangeEvent} that describes the change.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("text"))
+ {
+ String text = (String) e.getNewValue();
+ JLabel l = (JLabel) e.getSource();
+ BasicHTML.updateRenderer(l, text);
+ }
+ else if (e.getPropertyName().equals("displayedMnemonic"))
+ {
+ // update the key to action mapping
+ JLabel label = (JLabel) e.getSource();
+ if (label.getLabelFor() != null)
+ {
+ int oldMnemonic = ((Integer) e.getOldValue()).intValue();
+ int newMnemonic = ((Integer) e.getNewValue()).intValue();
+ InputMap keyMap = label.getInputMap(
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ keyMap.put(KeyStroke.getKeyStroke(oldMnemonic,
+ KeyEvent.ALT_DOWN_MASK), null);
+ keyMap.put(KeyStroke.getKeyStroke(newMnemonic,
+ KeyEvent.ALT_DOWN_MASK), "press");
+ }
+ }
+ else if (e.getPropertyName().equals("labelFor"))
+ {
+ JLabel label = (JLabel) e.getSource();
+ InputMap keyMap = label.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+ int mnemonic = label.getDisplayedMnemonic();
+ if (mnemonic > 0)
+ keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.ALT_DOWN_MASK),
+ "press");
+ }
+ }
+
+ /**
+ * Fetches a font metrics object for the specified label. This first
+ * tries to get it from the label object itself by calling
+ * {@link Component#getFontMetrics(Font)}, and if that does not work
+ * (for instance, when we are in the initialization and have no parent yet),
+ * it asks the Toolkit for a font metrics object.
+ *
+ * @param l the label
+ *
+ * @return a suitable font metrics object
+ */
+ private FontMetrics getFontMetrics(JLabel l)
+ {
+ Font font = l.getFont();
+ FontMetrics fm = l.getFontMetrics(font);
+ if (fm == null)
+ {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ fm = tk.getFontMetrics(font);
+ }
+ return fm;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java
new file mode 100644
index 000000000..0e33957f4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java
@@ -0,0 +1,1421 @@
+/* BasicListUI.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+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.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.CellRendererPane;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.ListModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.TransferHandler;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ListUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * The Basic Look and Feel UI delegate for the
+ * JList.
+ */
+public class BasicListUI extends ListUI
+{
+
+ /**
+ * A helper class which listens for {@link FocusEvent}s
+ * from the JList.
+ */
+ public class FocusHandler implements FocusListener
+ {
+ /**
+ * Called when the JList acquires focus.
+ *
+ * @param e The FocusEvent representing focus acquisition
+ */
+ public void focusGained(FocusEvent e)
+ {
+ repaintCellFocus();
+ }
+
+ /**
+ * Called when the JList loses focus.
+ *
+ * @param e The FocusEvent representing focus loss
+ */
+ public void focusLost(FocusEvent e)
+ {
+ repaintCellFocus();
+ }
+
+ /**
+ * Helper method to repaint the focused cell's
+ * lost or acquired focus state.
+ */
+ protected void repaintCellFocus()
+ {
+ // TODO: Implement this properly.
+ }
+ }
+
+ /**
+ * A helper class which listens for {@link ListDataEvent}s generated by
+ * the {@link JList}'s {@link ListModel}.
+ *
+ * @see javax.swing.JList#getModel()
+ */
+ public class ListDataHandler implements ListDataListener
+ {
+ /**
+ * Called when a general change has happened in the model which cannot
+ * be represented in terms of a simple addition or deletion.
+ *
+ * @param e The event representing the change
+ */
+ public void contentsChanged(ListDataEvent e)
+ {
+ updateLayoutStateNeeded |= modelChanged;
+ list.revalidate();
+ }
+
+ /**
+ * Called when an interval of objects has been added to the model.
+ *
+ * @param e The event representing the addition
+ */
+ public void intervalAdded(ListDataEvent e)
+ {
+ updateLayoutStateNeeded |= modelChanged;
+ list.revalidate();
+ }
+
+ /**
+ * Called when an inteval of objects has been removed from the model.
+ *
+ * @param e The event representing the removal
+ */
+ public void intervalRemoved(ListDataEvent e)
+ {
+ updateLayoutStateNeeded |= modelChanged;
+ list.revalidate();
+ }
+ }
+
+ /**
+ * A helper class which listens for {@link ListSelectionEvent}s
+ * from the {@link JList}'s {@link ListSelectionModel}.
+ */
+ public class ListSelectionHandler implements ListSelectionListener
+ {
+ /**
+ * Called when the list selection changes.
+ *
+ * @param e The event representing the change
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ int index1 = e.getFirstIndex();
+ int index2 = e.getLastIndex();
+ Rectangle damaged = getCellBounds(list, index1, index2);
+ if (damaged != null)
+ list.repaint(damaged);
+ }
+ }
+
+ /**
+ * This class is used to mimmic the behaviour of the JDK when registering
+ * keyboard actions. It is the same as the private class used in JComponent
+ * for the same reason. This class receives an action event and dispatches
+ * it to the true receiver after altering the actionCommand property of the
+ * event.
+ */
+ 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);
+ }
+ }
+
+ /**
+ * Implements the action for the JList's keyboard commands.
+ */
+ private class ListAction
+ extends AbstractAction
+ {
+ // TODO: Maybe make a couple of classes out of this bulk Action.
+ // Form logical groups of Actions when doing this.
+
+ /**
+ * Creates a new ListAction for the specified command.
+ *
+ * @param cmd the actionCommand to set
+ */
+ ListAction(String cmd)
+ {
+ putValue(ACTION_COMMAND_KEY, cmd);
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ int lead = list.getLeadSelectionIndex();
+ int max = list.getModel().getSize() - 1;
+ DefaultListSelectionModel selModel
+ = (DefaultListSelectionModel) list.getSelectionModel();
+ String command = e.getActionCommand();
+ // Do nothing if list is empty
+ if (max == -1)
+ return;
+
+ if (command.equals("selectNextRow"))
+ {
+ selectNextIndex();
+ }
+ else if (command.equals("selectPreviousRow"))
+ {
+ selectPreviousIndex();
+ }
+ else if (command.equals("clearSelection"))
+ {
+ list.clearSelection();
+ }
+ else if (command.equals("selectAll"))
+ {
+ list.setSelectionInterval(0, max);
+ // this next line is to restore the lead selection index to the old
+ // position, because select-all should not change the lead index
+ list.addSelectionInterval(lead, lead);
+ }
+ else if (command.equals("selectLastRow"))
+ {
+ list.setSelectedIndex(list.getModel().getSize() - 1);
+ }
+ else if (command.equals("selectLastRowChangeLead"))
+ {
+ selModel.moveLeadSelectionIndex(list.getModel().getSize() - 1);
+ }
+ else if (command.equals("scrollDownExtendSelection"))
+ {
+ int target;
+ if (lead == list.getLastVisibleIndex())
+ {
+ target = Math.min(max, lead + (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getLastVisibleIndex();
+ selModel.setLeadSelectionIndex(target);
+ }
+ else if (command.equals("scrollDownChangeLead"))
+ {
+ int target;
+ if (lead == list.getLastVisibleIndex())
+ {
+ target = Math.min(max, lead + (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getLastVisibleIndex();
+ selModel.moveLeadSelectionIndex(target);
+ }
+ else if (command.equals("scrollUpExtendSelection"))
+ {
+ int target;
+ if (lead == list.getFirstVisibleIndex())
+ {
+ target = Math.max(0, lead - (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getFirstVisibleIndex();
+ selModel.setLeadSelectionIndex(target);
+ }
+ else if (command.equals("scrollUpChangeLead"))
+ {
+ int target;
+ if (lead == list.getFirstVisibleIndex())
+ {
+ target = Math.max(0, lead - (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getFirstVisibleIndex();
+ selModel.moveLeadSelectionIndex(target);
+ }
+ else if (command.equals("selectNextRowExtendSelection"))
+ {
+ selModel.setLeadSelectionIndex(Math.min(lead + 1, max));
+ }
+ else if (command.equals("selectFirstRow"))
+ {
+ list.setSelectedIndex(0);
+ }
+ else if (command.equals("selectFirstRowChangeLead"))
+ {
+ selModel.moveLeadSelectionIndex(0);
+ }
+ else if (command.equals("selectFirstRowExtendSelection"))
+ {
+ selModel.setLeadSelectionIndex(0);
+ }
+ else if (command.equals("selectPreviousRowExtendSelection"))
+ {
+ selModel.setLeadSelectionIndex(Math.max(0, lead - 1));
+ }
+ else if (command.equals("scrollUp"))
+ {
+ int target;
+ if (lead == list.getFirstVisibleIndex())
+ {
+ target = Math.max(0, lead - (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getFirstVisibleIndex();
+ list.setSelectedIndex(target);
+ }
+ else if (command.equals("selectLastRowExtendSelection"))
+ {
+ selModel.setLeadSelectionIndex(list.getModel().getSize() - 1);
+ }
+ else if (command.equals("scrollDown"))
+ {
+ int target;
+ if (lead == list.getLastVisibleIndex())
+ {
+ target = Math.min(max, lead + (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getLastVisibleIndex();
+ list.setSelectedIndex(target);
+ }
+ else if (command.equals("selectNextRowChangeLead"))
+ {
+ if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ selectNextIndex();
+ else
+ {
+ selModel.moveLeadSelectionIndex(Math.min(max, lead + 1));
+ }
+ }
+ else if (command.equals("selectPreviousRowChangeLead"))
+ {
+ if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ selectPreviousIndex();
+ else
+ {
+ selModel.moveLeadSelectionIndex(Math.max(0, lead - 1));
+ }
+ }
+ else if (command.equals("addToSelection"))
+ {
+ list.addSelectionInterval(lead, lead);
+ }
+ else if (command.equals("extendTo"))
+ {
+ selModel.setSelectionInterval(selModel.getAnchorSelectionIndex(),
+ lead);
+ }
+ else if (command.equals("toggleAndAnchor"))
+ {
+ if (!list.isSelectedIndex(lead))
+ list.addSelectionInterval(lead, lead);
+ else
+ list.removeSelectionInterval(lead, lead);
+ selModel.setAnchorSelectionIndex(lead);
+ }
+ else
+ {
+ // DEBUG: uncomment the following line to print out
+ // key bindings that aren't implemented yet
+
+ // System.out.println ("not implemented: "+e.getActionCommand());
+ }
+
+ list.ensureIndexIsVisible(list.getLeadSelectionIndex());
+ }
+ }
+
+ /**
+ * A helper class which listens for {@link MouseEvent}s
+ * from the {@link JList}.
+ */
+ public class MouseInputHandler implements MouseInputListener
+ {
+ /**
+ * Called when a mouse button press/release cycle completes
+ * on the {@link JList}
+ *
+ * @param event The event representing the mouse click
+ */
+ public void mouseClicked(MouseEvent event)
+ {
+ Point click = event.getPoint();
+ int index = locationToIndex(list, click);
+ if (index == -1)
+ return;
+ if (event.isShiftDown())
+ {
+ if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
+ list.setSelectedIndex(index);
+ else if (list.getSelectionMode() ==
+ ListSelectionModel.SINGLE_INTERVAL_SELECTION)
+ // COMPAT: the IBM VM is compatible with the following line of code.
+ // However, compliance with Sun's VM would correspond to replacing
+ // getAnchorSelectionIndex() with getLeadSelectionIndex().This is
+ // both unnatural and contradictory to the way they handle other
+ // similar UI interactions.
+ list.setSelectionInterval(list.getAnchorSelectionIndex(), index);
+ else
+ // COMPAT: both Sun and IBM are compatible instead with:
+ // list.setSelectionInterval
+ // (list.getLeadSelectionIndex(),index);
+ // Note that for IBM this is contradictory to what they did in
+ // the above situation for SINGLE_INTERVAL_SELECTION.
+ // The most natural thing to do is the following:
+ if (list.isSelectedIndex(list.getAnchorSelectionIndex()))
+ list.getSelectionModel().setLeadSelectionIndex(index);
+ else
+ list.addSelectionInterval(list.getAnchorSelectionIndex(), index);
+ }
+ else if (event.isControlDown())
+ {
+ if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
+ list.setSelectedIndex(index);
+ else if (list.isSelectedIndex(index))
+ list.removeSelectionInterval(index, index);
+ else
+ list.addSelectionInterval(index, index);
+ }
+ else
+ list.setSelectedIndex(index);
+
+ list.ensureIndexIsVisible(list.getLeadSelectionIndex());
+ }
+
+ /**
+ * Called when a mouse button is pressed down on the
+ * {@link JList}.
+ *
+ * @param event The event representing the mouse press
+ */
+ public void mousePressed(MouseEvent event)
+ {
+ // We need to explicitly request focus.
+ list.requestFocusInWindow();
+ }
+
+ /**
+ * Called when a mouse button is released on
+ * the {@link JList}
+ *
+ * @param event The event representing the mouse press
+ */
+ public void mouseReleased(MouseEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Called when the mouse pointer enters the area bounded
+ * by the {@link JList}
+ *
+ * @param event The event representing the mouse entry
+ */
+ public void mouseEntered(MouseEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Called when the mouse pointer leaves the area bounded
+ * by the {@link JList}
+ *
+ * @param event The event representing the mouse exit
+ */
+ public void mouseExited(MouseEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Called when the mouse pointer moves over the area bounded
+ * by the {@link JList} while a button is held down.
+ *
+ * @param event The event representing the mouse drag
+ */
+ public void mouseDragged(MouseEvent event)
+ {
+ Point click = event.getPoint();
+ int index = locationToIndex(list, click);
+ if (index == -1)
+ return;
+ if (!event.isShiftDown() && !event.isControlDown())
+ list.setSelectedIndex(index);
+
+ list.ensureIndexIsVisible(list.getLeadSelectionIndex());
+ }
+
+ /**
+ * Called when the mouse pointer moves over the area bounded
+ * by the {@link JList}.
+ *
+ * @param event The event representing the mouse move
+ */
+ public void mouseMoved(MouseEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * Helper class which listens to {@link PropertyChangeEvent}s
+ * from the {@link JList}.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Called when the {@link JList} changes one of its bound properties.
+ *
+ * @param e The event representing the property change
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("model"))
+ {
+ if (e.getOldValue() != null && e.getOldValue() instanceof ListModel)
+ {
+ ListModel oldModel = (ListModel) e.getOldValue();
+ oldModel.removeListDataListener(listDataListener);
+ }
+ if (e.getNewValue() != null && e.getNewValue() instanceof ListModel)
+ {
+ ListModel newModel = (ListModel) e.getNewValue();
+ newModel.addListDataListener(BasicListUI.this.listDataListener);
+ }
+
+ updateLayoutStateNeeded |= modelChanged;
+ }
+ else if (e.getPropertyName().equals("selectionModel"))
+ updateLayoutStateNeeded |= selectionModelChanged;
+ else if (e.getPropertyName().equals("font"))
+ updateLayoutStateNeeded |= fontChanged;
+ else if (e.getPropertyName().equals("fixedCellWidth"))
+ updateLayoutStateNeeded |= fixedCellWidthChanged;
+ else if (e.getPropertyName().equals("fixedCellHeight"))
+ updateLayoutStateNeeded |= fixedCellHeightChanged;
+ else if (e.getPropertyName().equals("prototypeCellValue"))
+ updateLayoutStateNeeded |= prototypeCellValueChanged;
+ else if (e.getPropertyName().equals("cellRenderer"))
+ updateLayoutStateNeeded |= cellRendererChanged;
+ }
+ }
+
+ /**
+ * A constant to indicate that the model has changed.
+ */
+ protected static final int modelChanged = 1;
+
+ /**
+ * A constant to indicate that the selection model has changed.
+ */
+ protected static final int selectionModelChanged = 2;
+
+ /**
+ * A constant to indicate that the font has changed.
+ */
+ protected static final int fontChanged = 4;
+
+ /**
+ * A constant to indicate that the fixedCellWidth has changed.
+ */
+ protected static final int fixedCellWidthChanged = 8;
+
+ /**
+ * A constant to indicate that the fixedCellHeight has changed.
+ */
+ protected static final int fixedCellHeightChanged = 16;
+
+ /**
+ * A constant to indicate that the prototypeCellValue has changed.
+ */
+ protected static final int prototypeCellValueChanged = 32;
+
+ /**
+ * A constant to indicate that the cellRenderer has changed.
+ */
+ protected static final int cellRendererChanged = 64;
+
+ /**
+ * Creates a new BasicListUI for the component.
+ *
+ * @param c The component to create a UI for
+ *
+ * @return A new UI
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ return new BasicListUI();
+ }
+
+ /** The current focus listener. */
+ protected FocusListener focusListener;
+
+ /** The data listener listening to the model. */
+ protected ListDataListener listDataListener;
+
+ /** The selection listener listening to the selection model. */
+ protected ListSelectionListener listSelectionListener;
+
+ /** The mouse listener listening to the list. */
+ protected MouseInputListener mouseInputListener;
+
+ /** The property change listener listening to the list. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** Saved reference to the list this UI was created for. */
+ protected JList list;
+
+ /**
+ * The height of a single cell in the list. This field is used when the
+ * fixedCellHeight property of the list is set. Otherwise this field is
+ * set to <code>-1</code> and {@link #cellHeights} is used instead.
+ */
+ protected int cellHeight;
+
+ /** The width of a single cell in the list. */
+ protected int cellWidth;
+
+ /**
+ * An array of varying heights of cells in the list, in cases where each
+ * cell might have a different height. This field is used when the
+ * <code>fixedCellHeight</code> property of the list is not set. Otherwise
+ * this field is <code>null</code> and {@link #cellHeight} is used.
+ */
+ protected int[] cellHeights;
+
+ /**
+ * A bitmask that indicates which properties of the JList have changed.
+ * When nonzero, indicates that the UI class is out of
+ * date with respect to the underlying list, and must recalculate the
+ * list layout before painting or performing size calculations.
+ *
+ * @see #modelChanged
+ * @see #selectionModelChanged
+ * @see #fontChanged
+ * @see #fixedCellWidthChanged
+ * @see #fixedCellHeightChanged
+ * @see #prototypeCellValueChanged
+ * @see #cellRendererChanged
+ */
+ protected int updateLayoutStateNeeded;
+
+ /**
+ * The {@link CellRendererPane} that is used for painting.
+ */
+ protected CellRendererPane rendererPane;
+
+ /** The action bound to KeyStrokes. */
+ ListAction action;
+
+ /**
+ * Calculate the height of a particular row. If there is a fixed {@link
+ * #cellHeight}, return it; otherwise return the specific row height
+ * requested from the {@link #cellHeights} array. If the requested row
+ * is invalid, return <code>-1</code>.
+ *
+ * @param row The row to get the height of
+ *
+ * @return The height, in pixels, of the specified row
+ */
+ protected int getRowHeight(int row)
+ {
+ int height;
+ if (cellHeights == null)
+ height = cellHeight;
+ else
+ {
+ if (row < 0 || row >= cellHeights.length)
+ height = -1;
+ else
+ height = cellHeights[row];
+ }
+ return height;
+ }
+
+ /**
+ * Calculate the bounds of a particular cell, considering the upper left
+ * corner of the list as the origin position <code>(0,0)</code>.
+ *
+ * @param l Ignored; calculates over <code>this.list</code>
+ * @param index1 The first row to include in the bounds
+ * @param index2 The last row to incude in the bounds
+ *
+ * @return A rectangle encompassing the range of rows between
+ * <code>index1</code> and <code>index2</code> inclusive, or null
+ * such a rectangle couldn't be calculated for the given indexes.
+ */
+ public Rectangle getCellBounds(JList l, int index1, int index2)
+ {
+ maybeUpdateLayoutState();
+
+ if (l != list || cellWidth == -1)
+ return null;
+
+ int minIndex = Math.min(index1, index2);
+ int maxIndex = Math.max(index1, index2);
+ Point loc = indexToLocation(list, minIndex);
+
+ // When the layoutOrientation is VERTICAL, then the width == the list
+ // width. Otherwise the cellWidth field is used.
+ int width = cellWidth;
+ if (l.getLayoutOrientation() == JList.VERTICAL)
+ width = l.getWidth();
+
+ Rectangle bounds = new Rectangle(loc.x, loc.y, width,
+ getCellHeight(minIndex));
+ for (int i = minIndex + 1; i <= maxIndex; i++)
+ {
+ Point hiLoc = indexToLocation(list, i);
+ bounds = SwingUtilities.computeUnion(hiLoc.x, hiLoc.y, width,
+ getCellHeight(i), bounds);
+ }
+
+ return bounds;
+ }
+
+ /**
+ * Calculates the maximum cell height.
+ *
+ * @param index the index of the cell
+ *
+ * @return the maximum cell height
+ */
+ private int getCellHeight(int index)
+ {
+ int height = cellHeight;
+ if (height <= 0)
+ {
+ if (list.getLayoutOrientation() == JList.VERTICAL)
+ height = getRowHeight(index);
+ else
+ {
+ for (int j = 0; j < cellHeights.length; j++)
+ height = Math.max(height, cellHeights[j]);
+ }
+ }
+ return height;
+ }
+
+ /**
+ * Calculate the Y coordinate of the upper edge of a particular row,
+ * considering the Y coordinate <code>0</code> to occur at the top of the
+ * list.
+ *
+ * @param row The row to calculate the Y coordinate of
+ *
+ * @return The Y coordinate of the specified row, or <code>-1</code> if
+ * the specified row number is invalid
+ */
+ protected int convertRowToY(int row)
+ {
+ int y = 0;
+ for (int i = 0; i < row; ++i)
+ {
+ int h = getRowHeight(i);
+ if (h == -1)
+ return -1;
+ y += h;
+ }
+ return y;
+ }
+
+ /**
+ * Calculate the row number containing a particular Y coordinate,
+ * considering the Y coodrinate <code>0</code> to occur at the top of the
+ * list.
+ *
+ * @param y0 The Y coordinate to calculate the row number for
+ *
+ * @return The row number containing the specified Y value, or <code>-1</code>
+ * if the list model is empty
+ *
+ * @specnote This method is specified to return -1 for an invalid Y
+ * coordinate. However, some simple tests show that the behaviour
+ * is to return the index of the last list element for an Y
+ * coordinate that lies outside of the list bounds (even for
+ * negative indices). <code>-1</code>
+ * is only returned if the list model is empty.
+ */
+ protected int convertYToRow(int y0)
+ {
+ if (list.getModel().getSize() == 0)
+ return -1;
+
+ // When y0 < 0, then the JDK returns the maximum row index of the list. So
+ // do we.
+ if (y0 < 0)
+ return list.getModel().getSize() - 1;
+
+ // Update the layout if necessary.
+ maybeUpdateLayoutState();
+
+ int index = list.getModel().getSize() - 1;
+
+ // If a fixed cell height is set, then we can work more efficient.
+ if (cellHeight > 0)
+ index = Math.min(y0 / cellHeight, index);
+ // If we have no fixed cell height, we must add up each cell height up
+ // to y0.
+ else
+ {
+ int h = 0;
+ for (int row = 0; row < cellHeights.length; ++row)
+ {
+ h += cellHeights[row];
+ if (y0 < h)
+ {
+ index = row;
+ break;
+ }
+ }
+ }
+ return index;
+ }
+
+ /**
+ * Recomputes the {@link #cellHeights}, {@link #cellHeight}, and {@link
+ * #cellWidth} properties by examining the variouis properties of the
+ * {@link JList}.
+ */
+ protected void updateLayoutState()
+ {
+ int nrows = list.getModel().getSize();
+ cellHeight = -1;
+ cellWidth = -1;
+ if (cellHeights == null || cellHeights.length != nrows)
+ cellHeights = new int[nrows];
+ ListCellRenderer rend = list.getCellRenderer();
+ // Update the cellHeight(s) fields.
+ int fixedCellHeight = list.getFixedCellHeight();
+ if (fixedCellHeight > 0)
+ {
+ cellHeight = fixedCellHeight;
+ cellHeights = null;
+ }
+ else
+ {
+ cellHeight = -1;
+ for (int i = 0; i < nrows; ++i)
+ {
+ Component flyweight =
+ rend.getListCellRendererComponent(list,
+ list.getModel().getElementAt(i),
+ i, list.isSelectedIndex(i),
+ list.getSelectionModel().getAnchorSelectionIndex() == i);
+ Dimension dim = flyweight.getPreferredSize();
+ cellHeights[i] = dim.height;
+ }
+ }
+
+ // Update the cellWidth field.
+ int fixedCellWidth = list.getFixedCellWidth();
+ if (fixedCellWidth > 0)
+ cellWidth = fixedCellWidth;
+ else
+ {
+ for (int i = 0; i < nrows; ++i)
+ {
+ Component flyweight =
+ rend.getListCellRendererComponent(list,
+ list.getModel().getElementAt(i),
+ i, list.isSelectedIndex(i),
+ list.getSelectionModel().getAnchorSelectionIndex() == i);
+ Dimension dim = flyweight.getPreferredSize();
+ cellWidth = Math.max(cellWidth, dim.width);
+ }
+ }
+ }
+
+ /**
+ * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded}
+ * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero.
+ */
+ protected void maybeUpdateLayoutState()
+ {
+ if (updateLayoutStateNeeded != 0 || !list.isValid())
+ {
+ updateLayoutState();
+ updateLayoutStateNeeded = 0;
+ }
+ }
+
+ /**
+ * Creates a new BasicListUI object.
+ */
+ public BasicListUI()
+ {
+ updateLayoutStateNeeded = 1;
+ rendererPane = new CellRendererPane();
+ }
+
+ /**
+ * Installs various default settings (mostly colors) from the {@link
+ * UIDefaults} into the {@link JList}
+ *
+ * @see #uninstallDefaults
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(list, "List.background",
+ "List.foreground", "List.font");
+ list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
+ list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
+ list.setOpaque(true);
+ }
+
+ /**
+ * Resets to <code>null</code> those defaults which were installed in
+ * {@link #installDefaults}
+ */
+ protected void uninstallDefaults()
+ {
+ list.setForeground(null);
+ list.setBackground(null);
+ list.setSelectionForeground(null);
+ list.setSelectionBackground(null);
+ }
+
+ /**
+ * Attaches all the listeners we have in the UI class to the {@link
+ * JList}, its model and its selection model.
+ *
+ * @see #uninstallListeners
+ */
+ protected void installListeners()
+ {
+ if (focusListener == null)
+ focusListener = createFocusListener();
+ list.addFocusListener(focusListener);
+ if (listDataListener == null)
+ listDataListener = createListDataListener();
+ list.getModel().addListDataListener(listDataListener);
+ if (listSelectionListener == null)
+ listSelectionListener = createListSelectionListener();
+ list.addListSelectionListener(listSelectionListener);
+ if (mouseInputListener == null)
+ mouseInputListener = createMouseInputListener();
+ list.addMouseListener(mouseInputListener);
+ list.addMouseMotionListener(mouseInputListener);
+ if (propertyChangeListener == null)
+ propertyChangeListener = createPropertyChangeListener();
+ list.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Detaches all the listeners we attached in {@link #installListeners}.
+ */
+ protected void uninstallListeners()
+ {
+ list.removeFocusListener(focusListener);
+ list.getModel().removeListDataListener(listDataListener);
+ list.removeListSelectionListener(listSelectionListener);
+ list.removeMouseListener(mouseInputListener);
+ list.removeMouseMotionListener(mouseInputListener);
+ list.removePropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Installs keyboard actions for this UI in the {@link JList}.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install UI InputMap.
+ InputMap focusInputMap = (InputMap) UIManager.get("List.focusInputMap");
+ SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
+ focusInputMap);
+
+ // Install UI ActionMap.
+ ActionMap am = (ActionMap) UIManager.get("List.actionMap");
+ if (am == null)
+ {
+ // Create the actionMap once and store it in the current UIDefaults
+ // for use in other components.
+ am = new ActionMapUIResource();
+ ListAction action;
+ action = new ListAction("selectPreviousRow");
+ am.put("selectPreviousRow", action);
+ action = new ListAction("selectNextRow");
+ am.put("selectNextRow", action);
+ action = new ListAction("selectPreviousRowExtendSelection");
+ am.put("selectPreviousRowExtendSelection", action);
+ action = new ListAction("selectNextRowExtendSelection");
+ am.put("selectNextRowExtendSelection", action);
+
+ action = new ListAction("selectPreviousColumn");
+ am.put("selectPreviousColumn", action);
+ action = new ListAction("selectNextColumn");
+ am.put("selectNextColumn", action);
+ action = new ListAction("selectPreviousColumnExtendSelection");
+ am.put("selectPreviousColumnExtendSelection", action);
+ action = new ListAction("selectNextColumnExtendSelection");
+ am.put("selectNextColumnExtendSelection", action);
+
+ action = new ListAction("selectFirstRow");
+ am.put("selectFirstRow", action);
+ action = new ListAction("selectLastRow");
+ am.put("selectLastRow", action);
+ action = new ListAction("selectFirstRowExtendSelection");
+ am.put("selectFirstRowExtendSelection", action);
+ action = new ListAction("selectLastRowExtendSelection");
+ am.put("selectLastRowExtendSelection", action);
+
+ action = new ListAction("scrollUp");
+ am.put("scrollUp", action);
+ action = new ListAction("scrollUpExtendSelection");
+ am.put("scrollUpExtendSelection", action);
+ action = new ListAction("scrollDown");
+ am.put("scrollDown", action);
+ action = new ListAction("scrollDownExtendSelection");
+ am.put("scrollDownExtendSelection", action);
+
+ action = new ListAction("selectAll");
+ am.put("selectAll", action);
+ action = new ListAction("clearSelection");
+ am.put("clearSelection", action);
+
+ am.put("copy", TransferHandler.getCopyAction());
+ am.put("cut", TransferHandler.getCutAction());
+ am.put("paste", TransferHandler.getPasteAction());
+
+ UIManager.put("List.actionMap", am);
+ }
+
+ SwingUtilities.replaceUIActionMap(list, am);
+ }
+
+ /**
+ * Uninstalls keyboard actions for this UI in the {@link JList}.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ // Uninstall the InputMap.
+ InputMap im = SwingUtilities.getUIInputMap(list, JComponent.WHEN_FOCUSED);
+ if (im instanceof UIResource)
+ SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
+
+ // Uninstall the ActionMap.
+ if (SwingUtilities.getUIActionMap(list) instanceof UIResource)
+ SwingUtilities.replaceUIActionMap(list, null);
+ }
+
+ /**
+ * Installs the various aspects of the UI in the {@link JList}. In
+ * particular, calls {@link #installDefaults}, {@link #installListeners}
+ * and {@link #installKeyboardActions}. Also saves a reference to the
+ * provided component, cast to a {@link JList}.
+ *
+ * @param c The {@link JList} to install the UI into
+ */
+ public void installUI(final JComponent c)
+ {
+ super.installUI(c);
+ list = (JList) c;
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+ maybeUpdateLayoutState();
+ }
+
+ /**
+ * Uninstalls all the aspects of the UI which were installed in {@link
+ * #installUI}. When finished uninstalling, drops the saved reference to
+ * the {@link JList}.
+ *
+ * @param c Ignored; the UI is uninstalled from the {@link JList}
+ * reference saved during the call to {@link #installUI}
+ */
+ public void uninstallUI(final JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallDefaults();
+ list = null;
+ }
+
+ /**
+ * Gets the size this list would prefer to assume. This is calculated by
+ * calling {@link #getCellBounds} over the entire list.
+ *
+ * @param c Ignored; uses the saved {@link JList} reference
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ maybeUpdateLayoutState();
+ int size = list.getModel().getSize();
+ int visibleRows = list.getVisibleRowCount();
+ int layoutOrientation = list.getLayoutOrientation();
+
+ int h;
+ int w;
+ int maxCellHeight = cellHeight;
+ if (maxCellHeight <= 0)
+ {
+ for (int i = 0; i < cellHeights.length; i++)
+ maxCellHeight = Math.max(maxCellHeight, cellHeights[i]);
+ }
+ if (layoutOrientation == JList.HORIZONTAL_WRAP)
+ {
+ if (visibleRows > 0)
+ {
+ // We cast to double here to force double divisions.
+ double modelSize = size;
+ int neededColumns = (int) Math.ceil(modelSize / visibleRows);
+ int adjustedRows = (int) Math.ceil(modelSize / neededColumns);
+ h = maxCellHeight * adjustedRows;
+ w = cellWidth * neededColumns;
+ }
+ else
+ {
+ int neededColumns = Math.min(1, list.getWidth() / cellWidth);
+ h = size / neededColumns * maxCellHeight;
+ w = neededColumns * cellWidth;
+ }
+ }
+ else if (layoutOrientation == JList.VERTICAL_WRAP)
+ {
+ if (visibleRows > 0)
+ h = visibleRows * maxCellHeight;
+ else
+ h = Math.max(list.getHeight(), maxCellHeight);
+ int neededColumns = h / maxCellHeight;
+ w = cellWidth * neededColumns;
+ }
+ else
+ {
+ if (list.getFixedCellWidth() > 0)
+ w = list.getFixedCellWidth();
+ else
+ w = cellWidth;
+ if (list.getFixedCellHeight() > 0)
+ // FIXME: We need to add some cellVerticalMargins here, according
+ // to the specs.
+ h = list.getFixedCellHeight() * size;
+ else
+ h = maxCellHeight * size;
+ }
+ Insets insets = list.getInsets();
+ Dimension retVal = new Dimension(w + insets.left + insets.right,
+ h + insets.top + insets.bottom);
+ return retVal;
+ }
+
+ /**
+ * Paints a single cell in the list.
+ *
+ * @param g The graphics context to paint in
+ * @param row The row number to paint
+ * @param bounds The bounds of the cell to paint, assuming a coordinate
+ * system beginning at <code>(0,0)</code> in the upper left corner of the
+ * list
+ * @param rend A cell renderer to paint with
+ * @param data The data to provide to the cell renderer
+ * @param sel A selection model to provide to the cell renderer
+ * @param lead The lead selection index of the list
+ */
+ protected void paintCell(Graphics g, int row, Rectangle bounds,
+ ListCellRenderer rend, ListModel data,
+ ListSelectionModel sel, int lead)
+ {
+ boolean isSel = list.isSelectedIndex(row);
+ boolean hasFocus = (list.getLeadSelectionIndex() == row) && BasicListUI.this.list.hasFocus();
+ Component comp = rend.getListCellRendererComponent(list,
+ data.getElementAt(row),
+ row, isSel, hasFocus);
+ rendererPane.paintComponent(g, comp, list, bounds);
+ }
+
+ /**
+ * Paints the list by repeatedly calling {@link #paintCell} for each visible
+ * cell in the list.
+ *
+ * @param g The graphics context to paint with
+ * @param c Ignored; uses the saved {@link JList} reference
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ int nrows = list.getModel().getSize();
+ if (nrows == 0)
+ return;
+
+ maybeUpdateLayoutState();
+ ListCellRenderer render = list.getCellRenderer();
+ ListModel model = list.getModel();
+ ListSelectionModel sel = list.getSelectionModel();
+ int lead = sel.getLeadSelectionIndex();
+ Rectangle clip = g.getClipBounds();
+
+ int startIndex = locationToIndex(list, new Point(clip.x, clip.y));
+ int endIndex = locationToIndex(list, new Point(clip.x + clip.width,
+ clip.y + clip.height));
+
+ for (int row = startIndex; row <= endIndex; ++row)
+ {
+ Rectangle bounds = getCellBounds(list, row, row);
+ if (bounds != null && bounds.intersects(clip))
+ paintCell(g, row, bounds, render, model, sel, lead);
+ }
+ }
+
+ /**
+ * Computes the index of a list cell given a point within the list. If the
+ * location lies outside the bounds of the list, the greatest index in the
+ * list model is returned.
+ *
+ * @param l the list which on which the computation is based on
+ * @param location the coordinates
+ *
+ * @return the index of the list item that is located at the given
+ * coordinates or <code>-1</code> if the list model is empty
+ */
+ public int locationToIndex(JList l, Point location)
+ {
+ int layoutOrientation = list.getLayoutOrientation();
+ int index = -1;
+ switch (layoutOrientation)
+ {
+ case JList.VERTICAL:
+ index = convertYToRow(location.y);
+ break;
+ case JList.HORIZONTAL_WRAP:
+ // determine visible rows and cells per row
+ int maxCellHeight = getCellHeight(0);
+ int visibleRows = list.getHeight() / maxCellHeight;
+ int cellsPerRow = -1;
+ int numberOfItems = list.getModel().getSize();
+ cellsPerRow = numberOfItems / visibleRows + 1;
+
+ // determine index for the given location
+ int cellsPerColumn = numberOfItems / cellsPerRow + 1;
+ int gridX = Math.min(location.x / cellWidth, cellsPerRow - 1);
+ int gridY = Math.min(location.y / maxCellHeight, cellsPerColumn);
+ index = gridX + gridY * cellsPerRow;
+ break;
+ case JList.VERTICAL_WRAP:
+ // determine visible rows and cells per column
+ int maxCellHeight2 = getCellHeight(0);
+ int visibleRows2 = list.getHeight() / maxCellHeight2;
+ int numberOfItems2 = list.getModel().getSize();
+ int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1;
+
+ int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1);
+ int gridY2 = Math.min(location.y / maxCellHeight2, visibleRows2);
+ index = gridY2 + gridX2 * visibleRows2;
+ break;
+ }
+ return index;
+ }
+
+ public Point indexToLocation(JList l, int index)
+ {
+ int layoutOrientation = list.getLayoutOrientation();
+ Point loc = null;
+ switch (layoutOrientation)
+ {
+ case JList.VERTICAL:
+ loc = new Point(0, convertRowToY(index));
+ break;
+ case JList.HORIZONTAL_WRAP:
+ // determine visible rows and cells per row
+ int maxCellHeight = getCellHeight(0);
+ int visibleRows = list.getHeight() / maxCellHeight;
+ int numberOfCellsPerRow = -1;
+ int numberOfItems = list.getModel().getSize();
+ numberOfCellsPerRow = numberOfItems / visibleRows + 1;
+
+ // compute coordinates inside the grid
+ int gridX = index % numberOfCellsPerRow;
+ int gridY = index / numberOfCellsPerRow;
+ int locX = gridX * cellWidth;
+ int locY;
+ locY = gridY * maxCellHeight;
+ loc = new Point(locX, locY);
+ break;
+ case JList.VERTICAL_WRAP:
+ // determine visible rows and cells per column
+ int maxCellHeight2 = getCellHeight(0);
+ int visibleRows2 = list.getHeight() / maxCellHeight2;
+ // compute coordinates inside the grid
+ if (visibleRows2 > 0)
+ {
+ int gridY2 = index % visibleRows2;
+ int gridX2 = index / visibleRows2;
+ int locX2 = gridX2 * cellWidth;
+ int locY2 = gridY2 * maxCellHeight2;
+ loc = new Point(locX2, locY2);
+ }
+ else
+ loc = new Point(0, convertRowToY(index));
+ break;
+ }
+ return loc;
+ }
+
+ /**
+ * Creates and returns the focus listener for this UI.
+ *
+ * @return the focus listener for this UI
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * Creates and returns the list data listener for this UI.
+ *
+ * @return the list data listener for this UI
+ */
+ protected ListDataListener createListDataListener()
+ {
+ return new ListDataHandler();
+ }
+
+ /**
+ * Creates and returns the list selection listener for this UI.
+ *
+ * @return the list selection listener for this UI
+ */
+ protected ListSelectionListener createListSelectionListener()
+ {
+ return new ListSelectionHandler();
+ }
+
+ /**
+ * Creates and returns the mouse input listener for this UI.
+ *
+ * @return the mouse input listener for this UI
+ */
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * Creates and returns the property change listener for this UI.
+ *
+ * @return the property change listener for this UI
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Selects the next list item and force it to be visible.
+ */
+ protected void selectNextIndex()
+ {
+ int index = list.getSelectionModel().getLeadSelectionIndex();
+ if (index < list.getModel().getSize() - 1)
+ {
+ index++;
+ list.setSelectedIndex(index);
+ }
+ list.ensureIndexIsVisible(index);
+ }
+
+ /**
+ * Selects the previous list item and force it to be visible.
+ */
+ protected void selectPreviousIndex()
+ {
+ int index = list.getSelectionModel().getLeadSelectionIndex();
+ if (index > 0)
+ {
+ index--;
+ list.setSelectedIndex(index);
+ }
+ list.ensureIndexIsVisible(index);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java
new file mode 100644
index 000000000..9a1f544f6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java
@@ -0,0 +1,1734 @@
+/* BasicLookAndFeel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.AWTEvent;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.SystemColor;
+import java.awt.Toolkit;
+import java.awt.event.AWTEventListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuSelectionManager;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.Border;
+import javax.swing.plaf.BorderUIResource;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.DimensionUIResource;
+import javax.swing.plaf.FontUIResource;
+import javax.swing.plaf.IconUIResource;
+import javax.swing.plaf.InsetsUIResource;
+
+/**
+ * A basic implementation of Swing's Look and Feel framework. This can serve
+ * as a base for custom look and feel implementations.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class BasicLookAndFeel extends LookAndFeel
+ implements Serializable
+{
+
+ /**
+ * Helps closing menu popups when the user clicks outside of any menu area.
+ * This is implemented as an AWTEventListener that listens on the event
+ * queue directly, grabs all mouse events from there and finds out of they
+ * are targetted at a menu/submenu/menubar or not. If not,
+ * the MenuSelectionManager is messaged to close the currently opened menus,
+ * if any.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class PopupHelper implements AWTEventListener
+ {
+
+ /**
+ * Receives an event from the event queue.
+ *
+ * @param event
+ */
+ public void eventDispatched(AWTEvent event)
+ {
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent mouseEvent = (MouseEvent) event;
+ if (mouseEvent.getID() == MouseEvent.MOUSE_PRESSED)
+ mousePressed(mouseEvent);
+ }
+ }
+
+ /**
+ * Handles mouse pressed events from the event queue.
+ *
+ * @param ev the mouse pressed event
+ */
+ private void mousePressed(MouseEvent ev)
+ {
+ // Autoclose all menus managed by the MenuSelectionManager.
+ MenuSelectionManager m = MenuSelectionManager.defaultManager();
+ Component target = ev.getComponent();
+ if (target instanceof Container)
+ target = ((Container) target).findComponentAt(ev.getPoint());
+ if (m.getSelectedPath().length > 0
+ && ! m.isComponentPartOfCurrentMenu(target)
+ && (((JComponent)target).getClientProperty(DONT_CANCEL_POPUP) == null
+ || !((JComponent)target).getClientProperty(DONT_CANCEL_POPUP).equals(Boolean.TRUE)))
+ {
+ m.clearSelectedPath();
+ }
+ }
+
+ }
+
+ /**
+ * An action that can play an audio file.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class AudioAction extends AbstractAction
+ {
+ /**
+ * The UIDefaults key that specifies the sound.
+ */
+ Object key;
+
+ /**
+ * Creates a new AudioAction.
+ *
+ * @param key the key that describes the audio action, normally a filename
+ * of an audio file relative to the current package
+ */
+ AudioAction(Object key)
+ {
+ this.key = key;
+ }
+
+ /**
+ * Plays the sound represented by this action.
+ *
+ * @param event the action event that triggers this audio action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // We only can handle strings for now.
+ if (key instanceof String)
+ {
+ String name = UIManager.getString(key);
+ InputStream stream = getClass().getResourceAsStream(name);
+ try
+ {
+ Clip clip = AudioSystem.getClip();
+ AudioInputStream audioStream =
+ AudioSystem.getAudioInputStream(stream);
+ clip.open(audioStream);
+ }
+ catch (LineUnavailableException ex)
+ {
+ // Nothing we can do about it.
+ }
+ catch (IOException ex)
+ {
+ // Nothing we can do about it.
+ }
+ catch (UnsupportedAudioFileException e)
+ {
+ // Nothing we can do about it.
+ }
+ }
+ }
+ }
+
+ static final long serialVersionUID = -6096995660290287879L;
+
+ /**
+ * This is a key for a client property that tells the PopupHelper that
+ * it shouldn't close popups when the mouse event target has this
+ * property set. This is used when the component handles popup closing
+ * itself.
+ */
+ static final String DONT_CANCEL_POPUP = "noCancelPopup";
+
+ /**
+ * Helps closing menu popups when user clicks outside of the menu area.
+ */
+ private transient PopupHelper popupHelper;
+
+ /**
+ * Maps the audio actions for this l&f.
+ */
+ private ActionMap audioActionMap;
+
+ /**
+ * Creates a new instance of the Basic look and feel.
+ */
+ public BasicLookAndFeel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates and returns a new instance of the default resources for this look
+ * and feel.
+ *
+ * @return The UI defaults.
+ */
+ public UIDefaults getDefaults()
+ {
+ // Variables
+ UIDefaults def = new UIDefaults();
+ // Initialize Class Defaults
+ initClassDefaults(def);
+ // Initialize System Colour Defaults
+ initSystemColorDefaults(def);
+ // Initialize Component Defaults
+ initComponentDefaults(def);
+ // Return UI Defaults
+ return def;
+ }
+
+ /**
+ * Populates the <code>defaults</code> table with mappings between class IDs
+ * and fully qualified class names for the UI delegates.
+ *
+ * @param defaults the defaults table (<code>null</code> not permitted).
+ */
+ protected void initClassDefaults(UIDefaults defaults)
+ {
+ // Variables
+ Object[] uiDefaults;
+ // Initialize Class Defaults
+ uiDefaults = new Object[] {
+ "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI",
+ "CheckBoxMenuItemUI", "javax.swing.plaf.basic.BasicCheckBoxMenuItemUI",
+ "CheckBoxUI", "javax.swing.plaf.basic.BasicCheckBoxUI",
+ "ColorChooserUI", "javax.swing.plaf.basic.BasicColorChooserUI",
+ "ComboBoxUI", "javax.swing.plaf.basic.BasicComboBoxUI",
+ "DesktopIconUI", "javax.swing.plaf.basic.BasicDesktopIconUI",
+ "DesktopPaneUI", "javax.swing.plaf.basic.BasicDesktopPaneUI",
+ "EditorPaneUI", "javax.swing.plaf.basic.BasicEditorPaneUI",
+ "FileChooserUI", "javax.swing.plaf.basic.BasicFileChooserUI",
+ "FormattedTextFieldUI", "javax.swing.plaf.basic.BasicFormattedTextFieldUI",
+ "InternalFrameUI", "javax.swing.plaf.basic.BasicInternalFrameUI",
+ "LabelUI", "javax.swing.plaf.basic.BasicLabelUI",
+ "ListUI", "javax.swing.plaf.basic.BasicListUI",
+ "MenuBarUI", "javax.swing.plaf.basic.BasicMenuBarUI",
+ "MenuItemUI", "javax.swing.plaf.basic.BasicMenuItemUI",
+ "MenuUI", "javax.swing.plaf.basic.BasicMenuUI",
+ "OptionPaneUI", "javax.swing.plaf.basic.BasicOptionPaneUI",
+ "PanelUI", "javax.swing.plaf.basic.BasicPanelUI",
+ "PasswordFieldUI", "javax.swing.plaf.basic.BasicPasswordFieldUI",
+ "PopupMenuSeparatorUI", "javax.swing.plaf.basic.BasicPopupMenuSeparatorUI",
+ "PopupMenuUI", "javax.swing.plaf.basic.BasicPopupMenuUI",
+ "ProgressBarUI", "javax.swing.plaf.basic.BasicProgressBarUI",
+ "RadioButtonMenuItemUI", "javax.swing.plaf.basic.BasicRadioButtonMenuItemUI",
+ "RadioButtonUI", "javax.swing.plaf.basic.BasicRadioButtonUI",
+ "RootPaneUI", "javax.swing.plaf.basic.BasicRootPaneUI",
+ "ScrollBarUI", "javax.swing.plaf.basic.BasicScrollBarUI",
+ "ScrollPaneUI", "javax.swing.plaf.basic.BasicScrollPaneUI",
+ "SeparatorUI", "javax.swing.plaf.basic.BasicSeparatorUI",
+ "SliderUI", "javax.swing.plaf.basic.BasicSliderUI",
+ "SplitPaneUI", "javax.swing.plaf.basic.BasicSplitPaneUI",
+ "SpinnerUI", "javax.swing.plaf.basic.BasicSpinnerUI",
+ "StandardDialogUI", "javax.swing.plaf.basic.BasicStandardDialogUI",
+ "TabbedPaneUI", "javax.swing.plaf.basic.BasicTabbedPaneUI",
+ "TableHeaderUI", "javax.swing.plaf.basic.BasicTableHeaderUI",
+ "TableUI", "javax.swing.plaf.basic.BasicTableUI",
+ "TextPaneUI", "javax.swing.plaf.basic.BasicTextPaneUI",
+ "TextAreaUI", "javax.swing.plaf.basic.BasicTextAreaUI",
+ "TextFieldUI", "javax.swing.plaf.basic.BasicTextFieldUI",
+ "ToggleButtonUI", "javax.swing.plaf.basic.BasicToggleButtonUI",
+ "ToolBarSeparatorUI", "javax.swing.plaf.basic.BasicToolBarSeparatorUI",
+ "ToolBarUI", "javax.swing.plaf.basic.BasicToolBarUI",
+ "ToolTipUI", "javax.swing.plaf.basic.BasicToolTipUI",
+ "TreeUI", "javax.swing.plaf.basic.BasicTreeUI",
+ "ViewportUI", "javax.swing.plaf.basic.BasicViewportUI"
+ };
+ // Add Class Defaults to UI Defaults table
+ defaults.putDefaults(uiDefaults);
+ }
+
+ /**
+ * Populates the <code>defaults</code> table with system color defaults.
+ *
+ * This sets up a couple of default values and passes them to
+ * {@link #loadSystemColors(UIDefaults, String[], boolean)}. If the
+ * look and feel is a native look and feel, these defaults may be overridden
+ * by the corresponding SystemColor constants.
+ *
+ * @param defaults the defaults table (<code>null</code> not permitted).
+ */
+ protected void initSystemColorDefaults(UIDefaults defaults)
+ {
+ String[] defaultColors = new String[] {
+ "activeCaption", "#000080",
+ "activeCaptionBorder", "#C0C0C0",
+ "activeCaptionText", "#FFFFFF",
+ "control", "#C0C0C0",
+ "controlDkShadow", "#000000",
+ "controlHighlight", "#C0C0C0",
+ "controlLtHighlight", "#FFFFFF",
+ "controlShadow", "#808080",
+ "controlText", "#000000",
+ "desktop", "#005C5C",
+ "inactiveCaption", "#808080",
+ "inactiveCaptionBorder", "#C0C0C0",
+ "inactiveCaptionText", "#C0C0C0",
+ "info", "#FFFFE1",
+ "infoText", "#000000",
+ "menu", "#C0C0C0",
+ "menuText", "#000000",
+ "scrollbar", "#E0E0E0",
+ "text", "#C0C0C0",
+ "textHighlight", "#000080",
+ "textHighlightText", "#FFFFFF",
+ "textInactiveText", "#808080",
+ "textText", "#000000",
+ "window", "#FFFFFF",
+ "windowBorder", "#000000",
+ "windowText", "#000000"
+ };
+ loadSystemColors(defaults, defaultColors, isNativeLookAndFeel());
+ }
+
+ /**
+ * Populates the <code>defaults</code> table with the system colors. If
+ * <code>useNative</code> is <code>true</code>, the table is populated
+ * with the constants in {@link SystemColor}, otherwise the
+ * <code>systemColors</code> parameter is decoded into the defaults table.
+ * The system colors array is made up of pairs, where the first entry is the
+ * name of the system color, and the second entry is a string denoting
+ * an RGB color value like &quot;#C0C0C0&quot;, which is decoded using
+ * {@link Color#decode(String)}.
+ *
+ * @param defaults the defaults table (<code>null</code> not permitted).
+ * @param systemColors defaults to use when <code>useNative</code> is
+ * <code>false</code>
+ * @param useNative when <code>true</code>, installs the values of the
+ * SystemColor constants, when <code>false</code>, install the values
+ * from <code>systemColors</code>
+ */
+ protected void loadSystemColors(UIDefaults defaults, String[] systemColors,
+ boolean useNative)
+ {
+ if (useNative)
+ {
+ defaults.put("activeCaption",
+ new ColorUIResource(SystemColor.ACTIVE_CAPTION));
+ defaults.put("activeCaptionBorder",
+ new ColorUIResource(SystemColor.ACTIVE_CAPTION_BORDER));
+ defaults.put("activeCaptionText",
+ new ColorUIResource(SystemColor.ACTIVE_CAPTION_TEXT));
+ defaults.put("control",
+ new ColorUIResource(SystemColor.CONTROL));
+ defaults.put("controlDkShadow",
+ new ColorUIResource(SystemColor.CONTROL_DK_SHADOW));
+ defaults.put("controlHighlight",
+ new ColorUIResource(SystemColor.CONTROL_HIGHLIGHT));
+ defaults.put("controlLtHighlight",
+ new ColorUIResource(SystemColor.CONTROL_LT_HIGHLIGHT));
+ defaults.put("controlShadow",
+ new ColorUIResource(SystemColor.CONTROL_SHADOW));
+ defaults.put("controlText",
+ new ColorUIResource(SystemColor.CONTROL_TEXT));
+ defaults.put("desktop",
+ new ColorUIResource(SystemColor.DESKTOP));
+ defaults.put("inactiveCaption",
+ new ColorUIResource(SystemColor.INACTIVE_CAPTION));
+ defaults.put("inactiveCaptionBorder",
+ new ColorUIResource(SystemColor.INACTIVE_CAPTION_BORDER));
+ defaults.put("inactiveCaptionText",
+ new ColorUIResource(SystemColor.INACTIVE_CAPTION_TEXT));
+ defaults.put("info",
+ new ColorUIResource(SystemColor.INFO));
+ defaults.put("infoText",
+ new ColorUIResource(SystemColor.INFO_TEXT));
+ defaults.put("menu",
+ new ColorUIResource(SystemColor.MENU));
+ defaults.put("menuText",
+ new ColorUIResource(SystemColor.MENU_TEXT));
+ defaults.put("scrollbar",
+ new ColorUIResource(SystemColor.SCROLLBAR));
+ defaults.put("text",
+ new ColorUIResource(SystemColor.TEXT));
+ defaults.put("textHighlight",
+ new ColorUIResource(SystemColor.TEXT_HIGHLIGHT));
+ defaults.put("textHighlightText",
+ new ColorUIResource(SystemColor.TEXT_HIGHLIGHT_TEXT));
+ defaults.put("textInactiveText",
+ new ColorUIResource(SystemColor.TEXT_INACTIVE_TEXT));
+ defaults.put("textText",
+ new ColorUIResource(SystemColor.TEXT_TEXT));
+ defaults.put("window",
+ new ColorUIResource(SystemColor.WINDOW));
+ defaults.put("windowBorder",
+ new ColorUIResource(SystemColor.WINDOW_BORDER));
+ defaults.put("windowText",
+ new ColorUIResource(SystemColor.WINDOW_TEXT));
+ }
+ else
+ {
+ for (int i = 0; i < systemColors.length; i += 2)
+ {
+ Color color = Color.BLACK;
+ try
+ {
+ color = Color.decode(systemColors[i + 1]);
+ }
+ catch (NumberFormatException e)
+ {
+ e.printStackTrace();
+ }
+ defaults.put(systemColors[i], new ColorUIResource(color));
+ }
+ }
+ }
+
+ /**
+ * Loads the resource bundle in 'resources/basic' and adds the contained
+ * key/value pairs to the <code>defaults</code> table.
+ *
+ * @param defaults the UI defaults to load the resources into
+ */
+ // FIXME: This method is not used atm and private and thus could be removed.
+ // However, I consider this method useful for providing localized
+ // descriptions and similar stuff and therefore think that we should use it
+ // instead and provide the resource bundles.
+ private void loadResourceBundle(UIDefaults defaults)
+ {
+ ResourceBundle bundle;
+ Enumeration e;
+ String key;
+ String value;
+ bundle = ResourceBundle.getBundle("resources/basic");
+ // Process Resources
+ e = bundle.getKeys();
+ while (e.hasMoreElements())
+ {
+ key = (String) e.nextElement();
+ value = bundle.getString(key);
+ defaults.put(key, value);
+ }
+ }
+
+ /**
+ * Populates the <code>defaults</code> table with UI default values for
+ * colors, fonts, keybindings and much more.
+ *
+ * @param defaults the defaults table (<code>null</code> not permitted).
+ */
+ protected void initComponentDefaults(UIDefaults defaults)
+ {
+ Object[] uiDefaults;
+
+ Color highLight = new Color(249, 247, 246);
+ Color light = new Color(239, 235, 231);
+ Color shadow = new Color(139, 136, 134);
+ Color darkShadow = new Color(16, 16, 16);
+
+ uiDefaults = new Object[] {
+
+ "AbstractUndoableEdit.undoText", "Undo",
+ "AbstractUndoableEdit.redoText", "Redo",
+ "Button.background", new ColorUIResource(Color.LIGHT_GRAY),
+ "Button.border",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ return BasicBorders.getButtonBorder();
+ }
+ },
+ "Button.darkShadow", new ColorUIResource(Color.BLACK),
+ "Button.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Button.foreground", new ColorUIResource(Color.BLACK),
+ "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("SPACE"), "pressed",
+ KeyStroke.getKeyStroke("released SPACE"), "released"
+ }),
+ "Button.highlight", new ColorUIResource(Color.WHITE),
+ "Button.light", new ColorUIResource(Color.LIGHT_GRAY),
+ "Button.margin", new InsetsUIResource(2, 14, 2, 14),
+ "Button.shadow", new ColorUIResource(Color.GRAY),
+ "Button.textIconGap", new Integer(4),
+ "Button.textShiftOffset", new Integer(0),
+ "CheckBox.background", new ColorUIResource(new Color(204, 204, 204)),
+ "CheckBox.border", new BorderUIResource.CompoundBorderUIResource(null,
+ null),
+ "CheckBox.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("SPACE"), "pressed",
+ KeyStroke.getKeyStroke("released SPACE"), "released"
+ }),
+ "CheckBox.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "CheckBox.foreground", new ColorUIResource(darkShadow),
+ "CheckBox.icon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return BasicIconFactory.getCheckBoxIcon();
+ }
+ },
+ "CheckBox.checkIcon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return BasicIconFactory.getMenuItemCheckIcon();
+ }
+ },
+ "CheckBox.margin", new InsetsUIResource(2, 2, 2, 2),
+ "CheckBox.textIconGap", new Integer(4),
+ "CheckBox.textShiftOffset", new Integer(0),
+ "CheckBoxMenuItem.acceleratorFont", new FontUIResource("Dialog",
+ Font.PLAIN, 12),
+ "CheckBoxMenuItem.acceleratorForeground",
+ new ColorUIResource(new Color(16, 16, 16)),
+ "CheckBoxMenuItem.acceleratorSelectionForeground",
+ new ColorUIResource(Color.white),
+ "CheckBoxMenuItem.arrowIcon", BasicIconFactory.getMenuItemArrowIcon(),
+ "CheckBoxMenuItem.background", new ColorUIResource(light),
+ "CheckBoxMenuItem.border", new BasicBorders.MarginBorder(),
+ "CheckBoxMenuItem.borderPainted", Boolean.FALSE,
+ "CheckBoxMenuItem.checkIcon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return BasicIconFactory.getCheckBoxMenuItemIcon();
+ }
+ },
+ "CheckBoxMenuItem.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "CheckBoxMenuItem.foreground", new ColorUIResource(darkShadow),
+ "CheckBoxMenuItem.margin", new InsetsUIResource(2, 2, 2, 2),
+ "CheckBoxMenuItem.selectionBackground", new ColorUIResource(Color.black),
+ "CheckBoxMenuItem.selectionForeground", new ColorUIResource(Color.white),
+ "ColorChooser.background", new ColorUIResource(light),
+ "ColorChooser.cancelText", "Cancel",
+ "ColorChooser.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ColorChooser.foreground", new ColorUIResource(darkShadow),
+ "ColorChooser.hsbBlueText", "B",
+ "ColorChooser.hsbBrightnessText", "B",
+ "ColorChooser.hsbGreenText", "G",
+ "ColorChooser.hsbHueText", "H",
+ "ColorChooser.hsbNameText", "HSB",
+ "ColorChooser.hsbRedText", "R",
+ "ColorChooser.hsbSaturationText", "S",
+ "ColorChooser.okText", "OK",
+ "ColorChooser.previewText", "Preview",
+ "ColorChooser.resetText", "Reset",
+ "ColorChooser.rgbBlueMnemonic", "66",
+ "ColorChooser.rgbBlueText", "Blue",
+ "ColorChooser.rgbGreenMnemonic", "78",
+ "ColorChooser.rgbGreenText", "Green",
+ "ColorChooser.rgbNameText", "RGB",
+ "ColorChooser.rgbRedMnemonic", "68",
+ "ColorChooser.rgbRedText", "Red",
+ "ColorChooser.sampleText", "Sample Text Sample Text",
+ "ColorChooser.swatchesDefaultRecentColor", new ColorUIResource(light),
+ "ColorChooser.swatchesNameText", "Swatches",
+ "ColorChooser.swatchesRecentSwatchSize", new Dimension(10, 10),
+ "ColorChooser.swatchesRecentText", "Recent:",
+ "ColorChooser.swatchesSwatchSize", new Dimension(10, 10),
+ "ComboBox.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ESCAPE", "hidePopup",
+ "PAGE_UP", "pageUpPassThrough",
+ "PAGE_DOWN", "pageDownPassThrough",
+ "HOME", "homePassThrough",
+ "END", "endPassThrough"
+ }),
+ "ComboBox.background", new ColorUIResource(Color.white),
+ "ComboBox.buttonBackground", new ColorUIResource(light),
+ "ComboBox.buttonDarkShadow", new ColorUIResource(darkShadow),
+ "ComboBox.buttonHighlight", new ColorUIResource(highLight),
+ "ComboBox.buttonShadow", new ColorUIResource(shadow),
+ "ComboBox.disabledBackground", new ColorUIResource(light),
+ "ComboBox.disabledForeground", new ColorUIResource(Color.gray),
+ "ComboBox.font", new FontUIResource("SansSerif", Font.PLAIN, 12),
+ "ComboBox.foreground", new ColorUIResource(Color.black),
+ "ComboBox.selectionBackground", new ColorUIResource(0, 0, 128),
+ "ComboBox.selectionForeground", new ColorUIResource(Color.white),
+ "Desktop.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "KP_LEFT", "left",
+ "KP_RIGHT", "right",
+ "ctrl F5", "restore",
+ "LEFT", "left",
+ "ctrl alt F6", "selectNextFrame",
+ "UP", "up",
+ "ctrl F6", "selectNextFrame",
+ "RIGHT", "right",
+ "DOWN", "down",
+ "ctrl F7", "move",
+ "ctrl F8", "resize",
+ "ESCAPE", "escape",
+ "ctrl TAB", "selectNextFrame",
+ "ctrl F9", "minimize",
+ "KP_UP", "up",
+ "ctrl F4", "close",
+ "KP_DOWN", "down",
+ "ctrl F10", "maximize",
+ "ctrl alt shift F6", "selectPreviousFrame"
+ }),
+ "DesktopIcon.border", new BorderUIResource.CompoundBorderUIResource(null,
+ null),
+ "EditorPane.background", new ColorUIResource(Color.white),
+ "EditorPane.border", BasicBorders.getMarginBorder(),
+ "EditorPane.caretBlinkRate", new Integer(500),
+ "EditorPane.caretForeground", new ColorUIResource(Color.black),
+ "EditorPane.font", new FontUIResource("Serif", Font.PLAIN, 12),
+ "EditorPane.foreground", new ColorUIResource(Color.black),
+ "EditorPane.inactiveForeground", new ColorUIResource(Color.gray),
+ "EditorPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("shift UP"), "selection-up",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selection-up",
+ KeyStroke.getKeyStroke("DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up",
+ KeyStroke.getKeyStroke("KP_UP"), "caret-up",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("ctrl END"), "caret-end",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ENTER"), "insert-break",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left",
+ KeyStroke.getKeyStroke("shift DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("KP_DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selection-end",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl T"), "next-link-action",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("TAB"), "insert-tab",
+ KeyStroke.getKeyStroke("UP"), "caret-up",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("PAGE_UP"), "page-up",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard"
+ }),
+ "EditorPane.margin", new InsetsUIResource(3, 3, 3, 3),
+ "EditorPane.selectionBackground", new ColorUIResource(Color.black),
+ "EditorPane.selectionForeground", new ColorUIResource(Color.white),
+ "FileChooser.acceptAllFileFilterText", "All Files (*.*)",
+ "FileChooser.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ESCAPE", "cancelSelection"
+ }),
+ "FileChooser.cancelButtonMnemonic", "67",
+ "FileChooser.cancelButtonText", "Cancel",
+ "FileChooser.cancelButtonToolTipText", "Abort file chooser dialog",
+ "FileChooser.directoryDescriptionText", "Directory",
+ "FileChooser.fileDescriptionText", "Generic File",
+ "FileChooser.directoryOpenButtonMnemonic", "79",
+ "FileChooser.helpButtonMnemonic", "72",
+ "FileChooser.helpButtonText", "Help",
+ "FileChooser.helpButtonToolTipText", "FileChooser help",
+ "FileChooser.newFolderErrorSeparator", ":",
+ "FileChooser.newFolderErrorText", "Error creating new folder",
+ "FileChooser.openButtonMnemonic", "79",
+ "FileChooser.openButtonText", "Open",
+ "FileChooser.openButtonToolTipText", "Open selected file",
+ "FileChooser.saveButtonMnemonic", "83",
+ "FileChooser.saveButtonText", "Save",
+ "FileChooser.saveButtonToolTipText", "Save selected file",
+ "FileChooser.updateButtonMnemonic", "85",
+ "FileChooser.updateButtonText", "Update",
+ "FileChooser.updateButtonToolTipText", "Update directory listing",
+ "FocusManagerClassName", "TODO",
+ "FormattedTextField.background", new ColorUIResource(light),
+ "FormattedTextField.caretForeground", new ColorUIResource(Color.black),
+ "FormattedTextField.margin", new InsetsUIResource(0, 0, 0, 0),
+ "FormattedTextField.caretBlinkRate", new Integer(500),
+ "FormattedTextField.font",
+ new FontUIResource("SansSerif", Font.PLAIN, 12),
+ "FormattedTextField.foreground", new ColorUIResource(Color.black),
+ "FormattedTextField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("KP_UP"), "increment",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("KP_DOWN"), "decrement",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("UP"), "increment",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("ESCAPE"), "reset-field-edit",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("DOWN"), "decrement",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ENTER"), "notify-field-accept",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward"
+ }),
+ "FormattedTextField.inactiveBackground", new ColorUIResource(light),
+ "FormattedTextField.inactiveForeground", new ColorUIResource(Color.gray),
+ "FormattedTextField.selectionBackground",
+ new ColorUIResource(Color.black),
+ "FormattedTextField.selectionForeground",
+ new ColorUIResource(Color.white),
+ "FormView.resetButtonText", "Reset",
+ "FormView.submitButtonText", "Submit Query",
+ "InternalFrame.activeTitleBackground", new ColorUIResource(0, 0, 128),
+ "InternalFrame.activeTitleForeground", new ColorUIResource(Color.white),
+ "InternalFrame.border",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ Color lineColor = new Color(238, 238, 238);
+ Border inner = BorderFactory.createLineBorder(lineColor, 1);
+ Color shadowInner = new Color(184, 207, 229);
+ Color shadowOuter = new Color(122, 138, 153);
+ Border outer = BorderFactory.createBevelBorder(BevelBorder.RAISED,
+ Color.WHITE,
+ Color.WHITE,
+ shadowOuter,
+ shadowInner);
+ Border border = new BorderUIResource.CompoundBorderUIResource(outer,
+ inner);
+ return border;
+ }
+ },
+ "InternalFrame.borderColor", new ColorUIResource(light),
+ "InternalFrame.borderDarkShadow", new ColorUIResource(Color.BLACK),
+ "InternalFrame.borderHighlight", new ColorUIResource(Color.WHITE),
+ "InternalFrame.borderLight", new ColorUIResource(Color.LIGHT_GRAY),
+ "InternalFrame.borderShadow", new ColorUIResource(Color.GRAY),
+ "InternalFrame.closeIcon", BasicIconFactory.createEmptyFrameIcon(),
+ "InternalFrame.icon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return new IconUIResource(BasicIconFactory.createEmptyFrameIcon());
+ }
+ },
+ "InternalFrame.iconifyIcon", BasicIconFactory.createEmptyFrameIcon(),
+ "InternalFrame.inactiveTitleBackground", new ColorUIResource(Color.gray),
+ "InternalFrame.inactiveTitleForeground",
+ new ColorUIResource(Color.lightGray),
+ "InternalFrame.maximizeIcon", BasicIconFactory.createEmptyFrameIcon(),
+ "InternalFrame.minimizeIcon", BasicIconFactory.createEmptyFrameIcon(),
+ "InternalFrame.titleFont", new FontUIResource("Dialog", Font.BOLD, 12),
+ "InternalFrame.windowBindings", new Object[] {
+ "shift ESCAPE", "showSystemMenu",
+ "ctrl SPACE", "showSystemMenu",
+ "ESCAPE", "showSystemMenu"
+ },
+ "Label.background", new ColorUIResource(light),
+ "Label.disabledForeground", new ColorUIResource(Color.white),
+ "Label.disabledShadow", new ColorUIResource(shadow),
+ "Label.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Label.foreground", new ColorUIResource(darkShadow),
+ "List.background", new ColorUIResource(Color.white),
+ "List.border", new BasicBorders.MarginBorder(),
+ "List.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("ctrl DOWN"), "selectNextRowChangeLead",
+ KeyStroke.getKeyStroke("shift UP"), "selectPreviousRowExtendSelection",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "selectNextColumnChangeLead",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selectPreviousColumnExtendSelection",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selectPreviousRowExtendSelection",
+ KeyStroke.getKeyStroke("DOWN"), "selectNextRow",
+ KeyStroke.getKeyStroke("ctrl UP"), "selectPreviousRowChangeLead",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "selectPreviousColumnChangeLead",
+ KeyStroke.getKeyStroke("CUT"), "cut",
+ KeyStroke.getKeyStroke("END"), "selectLastRow",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "scrollUpExtendSelection",
+ KeyStroke.getKeyStroke("KP_UP"), "selectPreviousRow",
+ KeyStroke.getKeyStroke("shift ctrl UP"), "selectPreviousRowExtendSelection",
+ KeyStroke.getKeyStroke("ctrl HOME"), "selectFirstRowChangeLead",
+ KeyStroke.getKeyStroke("shift LEFT"), "selectPreviousColumnExtendSelection",
+ KeyStroke.getKeyStroke("ctrl END"), "selectLastRowChangeLead",
+ KeyStroke.getKeyStroke("ctrl PAGE_DOWN"), "scrollDownChangeLead",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selectNextColumnExtendSelection",
+ KeyStroke.getKeyStroke("LEFT"), "selectPreviousColumn",
+ KeyStroke.getKeyStroke("ctrl PAGE_UP"), "scrollUpChangeLead",
+ KeyStroke.getKeyStroke("KP_LEFT"), "selectPreviousColumn",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selectNextColumnExtendSelection",
+ KeyStroke.getKeyStroke("SPACE"), "addToSelection",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "toggleAndAnchor",
+ KeyStroke.getKeyStroke("shift SPACE"), "extendTo",
+ KeyStroke.getKeyStroke("shift ctrl SPACE"), "moveSelectionTo",
+ KeyStroke.getKeyStroke("shift ctrl DOWN"), "selectNextRowExtendSelection",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "clearSelection",
+ KeyStroke.getKeyStroke("shift HOME"), "selectFirstRowExtendSelection",
+ KeyStroke.getKeyStroke("RIGHT"), "selectNextColumn",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "scrollUpExtendSelection",
+ KeyStroke.getKeyStroke("shift DOWN"), "selectNextRowExtendSelection",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "scrollDown",
+ KeyStroke.getKeyStroke("shift ctrl KP_UP"), "selectPreviousRowExtendSelection",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selectPreviousColumnExtendSelection",
+ KeyStroke.getKeyStroke("ctrl X"), "cut",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "scrollDownExtendSelection",
+ KeyStroke.getKeyStroke("ctrl SLASH"), "selectAll",
+ KeyStroke.getKeyStroke("ctrl C"), "copy",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "selectNextColumnChangeLead",
+ KeyStroke.getKeyStroke("shift END"), "selectLastRowExtendSelection",
+ KeyStroke.getKeyStroke("shift ctrl KP_DOWN"), "selectNextRowExtendSelection",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "selectPreviousColumnChangeLead",
+ KeyStroke.getKeyStroke("HOME"), "selectFirstRow",
+ KeyStroke.getKeyStroke("ctrl V"), "paste",
+ KeyStroke.getKeyStroke("KP_DOWN"), "selectNextRow",
+ KeyStroke.getKeyStroke("ctrl KP_DOWN"), "selectNextRowChangeLead",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selectNextColumnExtendSelection",
+ KeyStroke.getKeyStroke("ctrl A"), "selectAll",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selectLastRowExtendSelection",
+ KeyStroke.getKeyStroke("COPY"), "copy",
+ KeyStroke.getKeyStroke("ctrl KP_UP"), "selectPreviousRowChangeLead",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selectPreviousColumnExtendSelection",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selectNextRowExtendSelection",
+ KeyStroke.getKeyStroke("UP"), "selectPreviousRow",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selectFirstRowExtendSelection",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "scrollDownExtendSelection",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "selectNextColumn",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selectNextColumnExtendSelection",
+ KeyStroke.getKeyStroke("PAGE_UP"), "scrollUp",
+ KeyStroke.getKeyStroke("PASTE"), "paste"
+ }),
+ "List.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "List.foreground", new ColorUIResource(Color.black),
+ "List.selectionBackground", new ColorUIResource(0, 0, 128),
+ "List.selectionForeground", new ColorUIResource(Color.white),
+ "List.focusCellHighlightBorder",
+ new BorderUIResource.
+ LineBorderUIResource(new ColorUIResource(Color.yellow)),
+ "Menu.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Menu.crossMenuMnemonic", Boolean.TRUE,
+ "Menu.acceleratorForeground", new ColorUIResource(darkShadow),
+ "Menu.acceleratorSelectionForeground", new ColorUIResource(Color.white),
+ "Menu.arrowIcon", BasicIconFactory.getMenuArrowIcon(),
+ "Menu.background", new ColorUIResource(light),
+ "Menu.border", new BasicBorders.MarginBorder(),
+ "Menu.borderPainted", Boolean.FALSE,
+ "Menu.checkIcon", BasicIconFactory.getMenuItemCheckIcon(),
+ "Menu.consumesTabs", Boolean.TRUE,
+ "Menu.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Menu.foreground", new ColorUIResource(darkShadow),
+ "Menu.margin", new InsetsUIResource(2, 2, 2, 2),
+ "Menu.selectedWindowInputMapBindings", new Object[] {
+ "ESCAPE", "cancel",
+ "DOWN", "selectNext",
+ "KP_DOWN", "selectNext",
+ "UP", "selectPrevious",
+ "KP_UP", "selectPrevious",
+ "LEFT", "selectParent",
+ "KP_LEFT", "selectParent",
+ "RIGHT", "selectChild",
+ "KP_RIGHT", "selectChild",
+ "ENTER", "return",
+ "SPACE", "return"
+ },
+ "Menu.menuPopupOffsetX", new Integer(0),
+ "Menu.menuPopupOffsetY", new Integer(0),
+ "Menu.submenuPopupOffsetX", new Integer(0),
+ "Menu.submenuPopupOffsetY", new Integer(0),
+ "Menu.selectionBackground", new ColorUIResource(Color.black),
+ "Menu.selectionForeground", new ColorUIResource(Color.white),
+ "MenuBar.background", new ColorUIResource(light),
+ "MenuBar.border", new BasicBorders.MenuBarBorder(null, null),
+ "MenuBar.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "MenuBar.foreground", new ColorUIResource(darkShadow),
+ "MenuBar.highlight", new ColorUIResource(highLight),
+ "MenuBar.shadow", new ColorUIResource(shadow),
+ "MenuBar.windowBindings", new Object[] {
+ "F10", "takeFocus"
+ },
+ "MenuItem.acceleratorDelimiter", "+",
+ "MenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "MenuItem.acceleratorForeground", new ColorUIResource(darkShadow),
+ "MenuItem.acceleratorSelectionForeground",
+ new ColorUIResource(Color.white),
+ "MenuItem.arrowIcon", BasicIconFactory.getMenuItemArrowIcon(),
+ "MenuItem.background", new ColorUIResource(light),
+ "MenuItem.border", new BasicBorders.MarginBorder(),
+ "MenuItem.borderPainted", Boolean.FALSE,
+ "MenuItem.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "MenuItem.foreground", new ColorUIResource(darkShadow),
+ "MenuItem.margin", new InsetsUIResource(2, 2, 2, 2),
+ "MenuItem.selectionBackground", new ColorUIResource(Color.black),
+ "MenuItem.selectionForeground", new ColorUIResource(Color.white),
+ "OptionPane.background", new ColorUIResource(light),
+ "OptionPane.border",
+ new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0),
+ "OptionPane.buttonAreaBorder",
+ new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0),
+ "OptionPane.buttonClickThreshhold", new Integer(500),
+ "OptionPane.cancelButtonText", "Cancel",
+ "OptionPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "OptionPane.foreground", new ColorUIResource(darkShadow),
+ "OptionPane.messageAreaBorder",
+ new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0),
+ "OptionPane.messageForeground", new ColorUIResource(darkShadow),
+ "OptionPane.minimumSize",
+ new DimensionUIResource(BasicOptionPaneUI.MinimumWidth,
+ BasicOptionPaneUI.MinimumHeight),
+ "OptionPane.noButtonText", "No",
+ "OptionPane.okButtonText", "OK",
+ "OptionPane.windowBindings", new Object[] {
+ "ESCAPE", "close"
+ },
+ "OptionPane.yesButtonText", "Yes",
+ "Panel.background", new ColorUIResource(light),
+ "Panel.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Panel.foreground", new ColorUIResource(Color.black),
+ "PasswordField.background", new ColorUIResource(light),
+ "PasswordField.border", new BasicBorders.FieldBorder(null, null,
+ null, null),
+ "PasswordField.caretBlinkRate", new Integer(500),
+ "PasswordField.caretForeground", new ColorUIResource(Color.black),
+ "PasswordField.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12),
+ "PasswordField.foreground", new ColorUIResource(Color.black),
+ "PasswordField.inactiveBackground", new ColorUIResource(light),
+ "PasswordField.inactiveForeground", new ColorUIResource(Color.gray),
+ "PasswordField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-end-line",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-begin-line",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-end-line",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-begin-line",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-begin-line",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-end-line",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ENTER"), "notify-field-accept",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward"
+ }),
+ "PasswordField.margin", new InsetsUIResource(0, 0, 0, 0),
+ "PasswordField.selectionBackground", new ColorUIResource(Color.black),
+ "PasswordField.selectionForeground", new ColorUIResource(Color.white),
+ "PopupMenu.background", new ColorUIResource(light),
+ "PopupMenu.border", new BorderUIResource.BevelBorderUIResource(0),
+ "PopupMenu.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "PopupMenu.foreground", new ColorUIResource(darkShadow),
+ "PopupMenu.selectedWindowInputMapBindings",
+ new Object[] {"ESCAPE", "cancel",
+ "DOWN", "selectNext",
+ "KP_DOWN", "selectNext",
+ "UP", "selectPrevious",
+ "KP_UP", "selectPrevious",
+ "LEFT", "selectParent",
+ "KP_LEFT", "selectParent",
+ "RIGHT", "selectChild",
+ "KP_RIGHT", "selectChild",
+ "ENTER", "return",
+ "SPACE", "return"
+ },
+ "PopupMenu.selectedWindowInputMapBindings.RightToLeft",
+ new Object[] {"LEFT", "selectChild",
+ "KP_LEFT", "selectChild",
+ "RIGHT", "selectParent",
+ "KP_RIGHT", "selectParent",
+ },
+ "ProgressBar.background", new ColorUIResource(Color.LIGHT_GRAY),
+ "ProgressBar.border",
+ new BorderUIResource.LineBorderUIResource(Color.GREEN, 2),
+ "ProgressBar.cellLength", new Integer(1),
+ "ProgressBar.cellSpacing", new Integer(0),
+ "ProgressBar.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ProgressBar.foreground", new ColorUIResource(0, 0, 128),
+ "ProgressBar.selectionBackground", new ColorUIResource(0, 0, 128),
+ "ProgressBar.selectionForeground", new ColorUIResource(Color.LIGHT_GRAY),
+ "ProgressBar.repaintInterval", new Integer(50),
+ "ProgressBar.cycleTime", new Integer(3000),
+ "RadioButton.background", new ColorUIResource(light),
+ "RadioButton.border", BasicBorders.getRadioButtonBorder(),
+ "RadioButton.darkShadow", new ColorUIResource(shadow),
+ "RadioButton.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("SPACE"), "pressed",
+ KeyStroke.getKeyStroke("released SPACE"), "released"
+ }),
+ "RadioButton.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "RadioButton.foreground", new ColorUIResource(darkShadow),
+ "RadioButton.highlight", new ColorUIResource(highLight),
+ "RadioButton.icon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return BasicIconFactory.getRadioButtonIcon();
+ }
+ },
+ "RadioButton.light", new ColorUIResource(highLight),
+ "RadioButton.margin", new InsetsUIResource(2, 2, 2, 2),
+ "RadioButton.shadow", new ColorUIResource(shadow),
+ "RadioButton.textIconGap", new Integer(4),
+ "RadioButton.textShiftOffset", new Integer(0),
+ "RadioButtonMenuItem.acceleratorFont",
+ new FontUIResource("Dialog", Font.PLAIN, 12),
+ "RadioButtonMenuItem.acceleratorForeground",
+ new ColorUIResource(darkShadow),
+ "RadioButtonMenuItem.acceleratorSelectionForeground",
+ new ColorUIResource(Color.white),
+ "RadioButtonMenuItem.arrowIcon", BasicIconFactory.getMenuItemArrowIcon(),
+ "RadioButtonMenuItem.background", new ColorUIResource(light),
+ "RadioButtonMenuItem.border", new BasicBorders.MarginBorder(),
+ "RadioButtonMenuItem.borderPainted", Boolean.FALSE,
+ "RadioButtonMenuItem.checkIcon", BasicIconFactory.getRadioButtonMenuItemIcon(),
+ "RadioButtonMenuItem.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "RadioButtonMenuItem.foreground", new ColorUIResource(darkShadow),
+ "RadioButtonMenuItem.margin", new InsetsUIResource(2, 2, 2, 2),
+ "RadioButtonMenuItem.selectionBackground",
+ new ColorUIResource(Color.black),
+ "RadioButtonMenuItem.selectionForeground",
+ new ColorUIResource(Color.white),
+ "RootPane.defaultButtonWindowKeyBindings", new Object[] {
+ "ENTER", "press",
+ "released ENTER", "release",
+ "ctrl ENTER", "press",
+ "ctrl released ENTER", "release"
+ },
+ "ScrollBar.background", new ColorUIResource(224, 224, 224),
+ "ScrollBar.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "PAGE_UP", "negativeBlockIncrement",
+ "PAGE_DOWN", "positiveBlockIncrement",
+ "END", "maxScroll",
+ "HOME", "minScroll",
+ "LEFT", "negativeUnitIncrement",
+ "KP_UP", "negativeUnitIncrement",
+ "KP_DOWN", "positiveUnitIncrement",
+ "UP", "negativeUnitIncrement",
+ "RIGHT", "positiveUnitIncrement",
+ "KP_LEFT", "negativeUnitIncrement",
+ "DOWN", "positiveUnitIncrement",
+ "KP_RIGHT", "positiveUnitIncrement"
+ }),
+ "ScrollBar.foreground", new ColorUIResource(light),
+ "ScrollBar.maximumThumbSize", new DimensionUIResource(4096, 4096),
+ "ScrollBar.minimumThumbSize", new DimensionUIResource(8, 8),
+ "ScrollBar.thumb", new ColorUIResource(light),
+ "ScrollBar.thumbDarkShadow", new ColorUIResource(shadow),
+ "ScrollBar.thumbHighlight", new ColorUIResource(highLight),
+ "ScrollBar.thumbShadow", new ColorUIResource(shadow),
+ "ScrollBar.track", new ColorUIResource(light),
+ "ScrollBar.trackHighlight", new ColorUIResource(shadow),
+ "ScrollBar.width", new Integer(16),
+ "ScrollPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "PAGE_UP", "scrollUp",
+ "KP_LEFT", "unitScrollLeft",
+ "ctrl PAGE_DOWN", "scrollRight",
+ "PAGE_DOWN", "scrollDown",
+ "KP_RIGHT", "unitScrollRight",
+ "LEFT", "unitScrollLeft",
+ "ctrl END", "scrollEnd",
+ "UP", "unitScrollUp",
+ "RIGHT", "unitScrollRight",
+ "DOWN", "unitScrollDown",
+ "ctrl HOME", "scrollHome",
+ "ctrl PAGE_UP", "scrollLeft",
+ "KP_UP", "unitScrollUp",
+ "KP_DOWN", "unitScrollDown"
+ }),
+ "ScrollPane.background", new ColorUIResource(light),
+ "ScrollPane.border", new BorderUIResource.EtchedBorderUIResource(),
+ "ScrollPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ScrollPane.foreground", new ColorUIResource(darkShadow),
+ "Separator.background", new ColorUIResource(highLight),
+ "Separator.foreground", new ColorUIResource(shadow),
+ "Separator.highlight", new ColorUIResource(highLight),
+ "Separator.shadow", new ColorUIResource(shadow),
+ "Slider.background", new ColorUIResource(light),
+ "Slider.focus", new ColorUIResource(shadow),
+ "Slider.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ctrl PAGE_DOWN", "negativeBlockIncrement",
+ "PAGE_DOWN", "negativeBlockIncrement",
+ "PAGE_UP", "positiveBlockIncrement",
+ "ctrl PAGE_UP", "positiveBlockIncrement",
+ "KP_RIGHT", "positiveUnitIncrement",
+ "DOWN", "negativeUnitIncrement",
+ "KP_LEFT", "negativeUnitIncrement",
+ "RIGHT", "positiveUnitIncrement",
+ "KP_DOWN", "negativeUnitIncrement",
+ "UP", "positiveUnitIncrement",
+ "KP_UP", "positiveUnitIncrement",
+ "LEFT", "negativeUnitIncrement",
+ "HOME", "minScroll",
+ "END", "maxScroll"
+ }),
+ "Slider.focusInsets", new InsetsUIResource(2, 2, 2, 2),
+ "Slider.foreground", new ColorUIResource(light),
+ "Slider.highlight", new ColorUIResource(highLight),
+ "Slider.shadow", new ColorUIResource(shadow),
+ "Slider.thumbHeight", new Integer(20),
+ "Slider.thumbWidth", new Integer(11),
+ "Slider.tickHeight", new Integer(12),
+ "Slider.horizontalSize", new Dimension(200, 21),
+ "Slider.verticalSize", new Dimension(21, 200),
+ "Slider.minimumHorizontalSize", new Dimension(36, 21),
+ "Slider.minimumVerticalSize", new Dimension(21, 36),
+ "Spinner.background", new ColorUIResource(light),
+ "Spinner.foreground", new ColorUIResource(light),
+ "Spinner.arrowButtonSize", new DimensionUIResource(16, 5),
+ "Spinner.editorBorderPainted", Boolean.FALSE,
+ "Spinner.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12),
+ "SplitPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "F6", "toggleFocus",
+ "F8", "startResize",
+ "END", "selectMax",
+ "HOME", "selectMin",
+ "LEFT", "negativeIncrement",
+ "KP_UP", "negativeIncrement",
+ "KP_DOWN", "positiveIncrement",
+ "UP", "negativeIncrement",
+ "RIGHT", "positiveIncrement",
+ "KP_LEFT", "negativeIncrement",
+ "DOWN", "positiveIncrement",
+ "KP_RIGHT", "positiveIncrement",
+ "shift ctrl pressed TAB", "focusOutBackward",
+ "ctrl pressed TAB", "focusOutForward"
+ }),
+ "SplitPane.background", new ColorUIResource(light),
+ "SplitPane.border", new BasicBorders.SplitPaneBorder(null, null),
+ "SplitPane.darkShadow", new ColorUIResource(shadow),
+ "SplitPane.dividerSize", new Integer(7),
+ "SplitPane.highlight", new ColorUIResource(highLight),
+ "SplitPane.shadow", new ColorUIResource(shadow),
+ "SplitPaneDivider.border", BasicBorders.getSplitPaneDividerBorder(),
+ "SplitPaneDivider.draggingColor", new ColorUIResource(Color.DARK_GRAY),
+ "TabbedPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ctrl PAGE_DOWN", "navigatePageDown",
+ "ctrl PAGE_UP", "navigatePageUp",
+ "ctrl UP", "requestFocus",
+ "ctrl KP_UP", "requestFocus"
+ }),
+ "TabbedPane.background", new ColorUIResource(192, 192, 192),
+ "TabbedPane.contentBorderInsets", new InsetsUIResource(2, 2, 3, 3),
+ "TabbedPane.darkShadow", new ColorUIResource(Color.black),
+ "TabbedPane.focus", new ColorUIResource(Color.black),
+ "TabbedPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("ctrl DOWN"), "requestFocusForVisibleComponent",
+ KeyStroke.getKeyStroke("KP_UP"), "navigateUp",
+ KeyStroke.getKeyStroke("LEFT"), "navigateLeft",
+ KeyStroke.getKeyStroke("ctrl KP_DOWN"), "requestFocusForVisibleComponent",
+ KeyStroke.getKeyStroke("UP"), "navigateUp",
+ KeyStroke.getKeyStroke("KP_DOWN"), "navigateDown",
+ KeyStroke.getKeyStroke("KP_LEFT"), "navigateLeft",
+ KeyStroke.getKeyStroke("RIGHT"), "navigateRight",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "navigateRight",
+ KeyStroke.getKeyStroke("DOWN"), "navigateDown"
+ }),
+ "TabbedPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "TabbedPane.foreground", new ColorUIResource(Color.black),
+ "TabbedPane.highlight", new ColorUIResource(Color.white),
+ "TabbedPane.light", new ColorUIResource(192, 192, 192),
+ "TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1),
+ "TabbedPane.shadow", new ColorUIResource(128, 128, 128),
+ "TabbedPane.tabsOpaque", Boolean.TRUE,
+ "TabbedPane.tabAreaInsets", new InsetsUIResource(3, 2, 0, 2),
+ "TabbedPane.tabInsets", new InsetsUIResource(0, 4, 1, 4),
+ "TabbedPane.tabRunOverlay", new Integer(2),
+ "TabbedPane.tabsOverlapBorder", Boolean.FALSE,
+ "TabbedPane.textIconGap", new Integer(4),
+ "Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ctrl DOWN", "selectNextRowChangeLead",
+ "ctrl RIGHT", "selectNextColumnChangeLead",
+ "ctrl UP", "selectPreviousRowChangeLead",
+ "ctrl LEFT", "selectPreviousColumnChangeLead",
+ "CUT", "cut",
+ "SPACE", "addToSelection",
+ "ctrl SPACE", "toggleAndAnchor",
+ "shift SPACE", "extendTo",
+ "shift ctrl SPACE", "moveSelectionTo",
+ "ctrl X", "cut",
+ "ctrl C", "copy",
+ "ctrl KP_RIGHT", "selectNextColumnChangeLead",
+ "ctrl KP_LEFT", "selectPreviousColumnChangeLead",
+ "ctrl V", "paste",
+ "ctrl KP_DOWN", "selectNextRowChangeLead",
+ "COPY", "copy",
+ "ctrl KP_UP", "selectPreviousRowChangeLead",
+ "PASTE", "paste",
+ "shift PAGE_DOWN", "scrollDownExtendSelection",
+ "PAGE_DOWN", "scrollDownChangeSelection",
+ "END", "selectLastColumn",
+ "shift END", "selectLastColumnExtendSelection",
+ "HOME", "selectFirstColumn",
+ "ctrl END", "selectLastRow",
+ "ctrl shift END", "selectLastRowExtendSelection",
+ "LEFT", "selectPreviousColumn",
+ "shift HOME", "selectFirstColumnExtendSelection",
+ "UP", "selectPreviousRow",
+ "RIGHT", "selectNextColumn",
+ "ctrl HOME", "selectFirstRow",
+ "shift LEFT", "selectPreviousColumnExtendSelection",
+ "DOWN", "selectNextRow",
+ "ctrl shift HOME", "selectFirstRowExtendSelection",
+ "shift UP", "selectPreviousRowExtendSelection",
+ "F2", "startEditing",
+ "shift RIGHT", "selectNextColumnExtendSelection",
+ "TAB", "selectNextColumnCell",
+ "shift DOWN", "selectNextRowExtendSelection",
+ "ENTER", "selectNextRowCell",
+ "KP_UP", "selectPreviousRow",
+ "KP_DOWN", "selectNextRow",
+ "KP_LEFT", "selectPreviousColumn",
+ "KP_RIGHT", "selectNextColumn",
+ "shift TAB", "selectPreviousColumnCell",
+ "ctrl A", "selectAll",
+ "shift ENTER", "selectPreviousRowCell",
+ "shift KP_DOWN", "selectNextRowExtendSelection",
+ "shift KP_LEFT", "selectPreviousColumnExtendSelection",
+ "ESCAPE", "cancel",
+ "ctrl shift PAGE_UP", "scrollLeftExtendSelection",
+ "shift KP_RIGHT", "selectNextColumnExtendSelection",
+ "ctrl PAGE_UP", "scrollLeftChangeSelection",
+ "shift PAGE_UP", "scrollUpExtendSelection",
+ "ctrl shift PAGE_DOWN", "scrollRightExtendSelection",
+ "ctrl PAGE_DOWN", "scrollRightChangeSelection",
+ "PAGE_UP", "scrollUpChangeSelection",
+ "ctrl shift LEFT", "selectPreviousColumnExtendSelection",
+ "shift KP_UP", "selectPreviousRowExtendSelection",
+ "ctrl shift UP", "selectPreviousRowExtendSelection",
+ "ctrl shift RIGHT", "selectNextColumnExtendSelection",
+ "ctrl shift KP_RIGHT", "selectNextColumnExtendSelection",
+ "ctrl shift DOWN", "selectNextRowExtendSelection",
+ "ctrl BACK_SLASH", "clearSelection",
+ "ctrl shift KP_UP", "selectPreviousRowExtendSelection",
+ "ctrl shift KP_LEFT", "selectPreviousColumnExtendSelection",
+ "ctrl SLASH", "selectAll",
+ "ctrl shift KP_DOWN", "selectNextRowExtendSelection",
+ }),
+ "Table.background", new ColorUIResource(new ColorUIResource(255, 255, 255)),
+ "Table.focusCellBackground", new ColorUIResource(new ColorUIResource(255, 255, 255)),
+ "Table.focusCellForeground", new ColorUIResource(new ColorUIResource(0, 0, 0)),
+ "Table.focusCellHighlightBorder",
+ new BorderUIResource.LineBorderUIResource(
+ new ColorUIResource(255, 255, 0)),
+ "Table.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Table.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)),
+ "Table.gridColor", new ColorUIResource(new ColorUIResource(128, 128, 128)),
+ "Table.scrollPaneBorder", new BorderUIResource.BevelBorderUIResource(0),
+ "Table.selectionBackground", new ColorUIResource(new ColorUIResource(0, 0, 128)),
+ "Table.selectionForeground", new ColorUIResource(new ColorUIResource(255, 255, 255)),
+ "TableHeader.background", new ColorUIResource(new ColorUIResource(192, 192, 192)),
+ "TableHeader.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "TableHeader.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)),
+
+ "TextArea.background", new ColorUIResource(light),
+ "TextArea.border", new BorderUIResource(BasicBorders.getMarginBorder()),
+ "TextArea.caretBlinkRate", new Integer(500),
+ "TextArea.caretForeground", new ColorUIResource(Color.black),
+ "TextArea.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12),
+ "TextArea.foreground", new ColorUIResource(Color.black),
+ "TextArea.inactiveForeground", new ColorUIResource(Color.gray),
+ "TextArea.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("shift UP"), "selection-up",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selection-up",
+ KeyStroke.getKeyStroke("DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up",
+ KeyStroke.getKeyStroke("KP_UP"), "caret-up",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("ctrl END"), "caret-end",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ENTER"), "insert-break",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left",
+ KeyStroke.getKeyStroke("shift DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("KP_DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selection-end",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl T"), "next-link-action",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("TAB"), "insert-tab",
+ KeyStroke.getKeyStroke("UP"), "caret-up",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("PAGE_UP"), "page-up",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard"
+ }),
+ "TextArea.margin", new InsetsUIResource(0, 0, 0, 0),
+ "TextArea.selectionBackground", new ColorUIResource(Color.black),
+ "TextArea.selectionForeground", new ColorUIResource(Color.white),
+ "TextField.background", new ColorUIResource(light),
+ "TextField.border", new BasicBorders.FieldBorder(null, null, null, null),
+ "TextField.caretBlinkRate", new Integer(500),
+ "TextField.caretForeground", new ColorUIResource(Color.black),
+ "TextField.darkShadow", new ColorUIResource(shadow),
+ "TextField.font", new FontUIResource("SansSerif", Font.PLAIN, 12),
+ "TextField.foreground", new ColorUIResource(Color.black),
+ "TextField.highlight", new ColorUIResource(highLight),
+ "TextField.inactiveBackground", new ColorUIResource(Color.LIGHT_GRAY),
+ "TextField.inactiveForeground", new ColorUIResource(Color.GRAY),
+ "TextField.light", new ColorUIResource(highLight),
+ "TextField.highlight", new ColorUIResource(light),
+ "TextField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("ENTER"), "notify-field-accept",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word"
+ }),
+ "TextField.margin", new InsetsUIResource(0, 0, 0, 0),
+ "TextField.selectionBackground", new ColorUIResource(Color.black),
+ "TextField.selectionForeground", new ColorUIResource(Color.white),
+ "TextPane.background", new ColorUIResource(Color.white),
+ "TextPane.border", BasicBorders.getMarginBorder(),
+ "TextPane.caretBlinkRate", new Integer(500),
+ "TextPane.caretForeground", new ColorUIResource(Color.black),
+ "TextPane.font", new FontUIResource("Serif", Font.PLAIN, 12),
+ "TextPane.foreground", new ColorUIResource(Color.black),
+ "TextPane.inactiveForeground", new ColorUIResource(Color.gray),
+ "TextPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("shift UP"), "selection-up",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selection-up",
+ KeyStroke.getKeyStroke("DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up",
+ KeyStroke.getKeyStroke("KP_UP"), "caret-up",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("ctrl END"), "caret-end",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ENTER"), "insert-break",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left",
+ KeyStroke.getKeyStroke("shift DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("KP_DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selection-end",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl T"), "next-link-action",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("TAB"), "insert-tab",
+ KeyStroke.getKeyStroke("UP"), "caret-up",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("PAGE_UP"), "page-up",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard"
+ }),
+ "TextPane.margin", new InsetsUIResource(3, 3, 3, 3),
+ "TextPane.selectionBackground", new ColorUIResource(Color.black),
+ "TextPane.selectionForeground", new ColorUIResource(Color.white),
+ "TitledBorder.border", new BorderUIResource.EtchedBorderUIResource(),
+ "TitledBorder.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "TitledBorder.titleColor", new ColorUIResource(darkShadow),
+ "ToggleButton.background", new ColorUIResource(light),
+ "ToggleButton.border",
+ new BorderUIResource.CompoundBorderUIResource(null, null),
+ "ToggleButton.darkShadow", new ColorUIResource(shadow),
+ "ToggleButton.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("SPACE"), "pressed",
+ KeyStroke.getKeyStroke("released SPACE"), "released"
+ }),
+ "ToggleButton.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ToggleButton.foreground", new ColorUIResource(darkShadow),
+ "ToggleButton.highlight", new ColorUIResource(highLight),
+ "ToggleButton.light", new ColorUIResource(light),
+ "ToggleButton.margin", new InsetsUIResource(2, 14, 2, 14),
+ "ToggleButton.shadow", new ColorUIResource(shadow),
+ "ToggleButton.textIconGap", new Integer(4),
+ "ToggleButton.textShiftOffset", new Integer(0),
+ "ToolBar.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "UP", "navigateUp",
+ "KP_UP", "navigateUp",
+ "DOWN", "navigateDown",
+ "KP_DOWN", "navigateDown",
+ "LEFT", "navigateLeft",
+ "KP_LEFT", "navigateLeft",
+ "RIGHT", "navigateRight",
+ "KP_RIGHT", "navigateRight"
+ }),
+ "ToolBar.background", new ColorUIResource(light),
+ "ToolBar.border", new BorderUIResource.EtchedBorderUIResource(),
+ "ToolBar.darkShadow", new ColorUIResource(shadow),
+ "ToolBar.dockingBackground", new ColorUIResource(light),
+ "ToolBar.dockingForeground", new ColorUIResource(Color.red),
+ "ToolBar.floatingBackground", new ColorUIResource(light),
+ "ToolBar.floatingForeground", new ColorUIResource(Color.darkGray),
+ "ToolBar.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ToolBar.foreground", new ColorUIResource(darkShadow),
+ "ToolBar.highlight", new ColorUIResource(highLight),
+ "ToolBar.light", new ColorUIResource(highLight),
+ "ToolBar.separatorSize", new DimensionUIResource(10, 10),
+ "ToolBar.shadow", new ColorUIResource(shadow),
+ "ToolTip.background", new ColorUIResource(light),
+ "ToolTip.border", new BorderUIResource.LineBorderUIResource(Color.lightGray),
+ "ToolTip.font", new FontUIResource("SansSerif", Font.PLAIN, 12),
+ "ToolTip.foreground", new ColorUIResource(darkShadow),
+ "Tree.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ESCAPE", "cancel"
+ }),
+ "Tree.background", new ColorUIResource(new Color(255, 255, 255)),
+ "Tree.changeSelectionWithFocus", Boolean.TRUE,
+ "Tree.drawsFocusBorderAroundIcon", Boolean.FALSE,
+ "Tree.editorBorder", new BorderUIResource.LineBorderUIResource(Color.lightGray),
+ "Tree.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("ctrl DOWN"), "selectNextChangeLead",
+ KeyStroke.getKeyStroke("shift UP"), "selectPreviousExtendSelection",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "scrollRight",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selectPreviousExtendSelection",
+ KeyStroke.getKeyStroke("DOWN"), "selectNext",
+ KeyStroke.getKeyStroke("ctrl UP"), "selectPreviousChangeLead",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "scrollLeft",
+ KeyStroke.getKeyStroke("CUT"), "cut",
+ KeyStroke.getKeyStroke("END"), "selectLast",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "scrollUpExtendSelection",
+ KeyStroke.getKeyStroke("KP_UP"), "selectPrevious",
+ KeyStroke.getKeyStroke("shift ctrl UP"), "selectPreviousExtendSelection",
+ KeyStroke.getKeyStroke("ctrl HOME"), "selectFirstChangeLead",
+ KeyStroke.getKeyStroke("ctrl END"), "selectLastChangeLead",
+ KeyStroke.getKeyStroke("ctrl PAGE_DOWN"), "scrollDownChangeLead",
+ KeyStroke.getKeyStroke("LEFT"), "selectParent",
+ KeyStroke.getKeyStroke("ctrl PAGE_UP"), "scrollUpChangeLead",
+ KeyStroke.getKeyStroke("KP_LEFT"), "selectParent",
+ KeyStroke.getKeyStroke("SPACE"), "addToSelection",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "toggleAndAnchor",
+ KeyStroke.getKeyStroke("shift SPACE"), "extendTo",
+ KeyStroke.getKeyStroke("shift ctrl SPACE"), "moveSelectionTo",
+ KeyStroke.getKeyStroke("ADD"), "expand",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "clearSelection",
+ KeyStroke.getKeyStroke("shift ctrl DOWN"), "selectNextExtendSelection",
+ KeyStroke.getKeyStroke("shift HOME"), "selectFirstExtendSelection",
+ KeyStroke.getKeyStroke("RIGHT"), "selectChild",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "scrollUpExtendSelection",
+ KeyStroke.getKeyStroke("shift DOWN"), "selectNextExtendSelection",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "scrollDownChangeSelection",
+ KeyStroke.getKeyStroke("shift ctrl KP_UP"), "selectPreviousExtendSelection",
+ KeyStroke.getKeyStroke("SUBTRACT"), "collapse",
+ KeyStroke.getKeyStroke("ctrl X"), "cut",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "scrollDownExtendSelection",
+ KeyStroke.getKeyStroke("ctrl SLASH"), "selectAll",
+ KeyStroke.getKeyStroke("ctrl C"), "copy",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "scrollRight",
+ KeyStroke.getKeyStroke("shift END"), "selectLastExtendSelection",
+ KeyStroke.getKeyStroke("shift ctrl KP_DOWN"), "selectNextExtendSelection",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "scrollLeft",
+ KeyStroke.getKeyStroke("HOME"), "selectFirst",
+ KeyStroke.getKeyStroke("ctrl V"), "paste",
+ KeyStroke.getKeyStroke("KP_DOWN"), "selectNext",
+ KeyStroke.getKeyStroke("ctrl A"), "selectAll",
+ KeyStroke.getKeyStroke("ctrl KP_DOWN"), "selectNextChangeLead",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selectLastExtendSelection",
+ KeyStroke.getKeyStroke("COPY"), "copy",
+ KeyStroke.getKeyStroke("ctrl KP_UP"), "selectPreviousChangeLead",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selectNextExtendSelection",
+ KeyStroke.getKeyStroke("UP"), "selectPrevious",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selectFirstExtendSelection",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "scrollDownExtendSelection",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "selectChild",
+ KeyStroke.getKeyStroke("F2"), "startEditing",
+ KeyStroke.getKeyStroke("PAGE_UP"), "scrollUpChangeSelection",
+ KeyStroke.getKeyStroke("PASTE"), "paste"
+ }),
+ "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Tree.foreground", new ColorUIResource(Color.black),
+ "Tree.hash", new ColorUIResource(new Color(184, 207, 228)),
+ "Tree.leftChildIndent", new Integer(7),
+ "Tree.rightChildIndent", new Integer(13),
+ "Tree.rowHeight", new Integer(16),
+ "Tree.scrollsOnExpand", Boolean.TRUE,
+ "Tree.selectionBackground", new ColorUIResource(Color.black),
+ "Tree.nonSelectionBackground", new ColorUIResource(new Color(255, 255, 255)),
+ "Tree.selectionBorderColor", new ColorUIResource(Color.black),
+ "Tree.selectionBorder", new BorderUIResource.LineBorderUIResource(Color.black),
+ "Tree.selectionForeground", new ColorUIResource(new Color(255, 255, 255)),
+ "Viewport.background", new ColorUIResource(light),
+ "Viewport.foreground", new ColorUIResource(Color.black),
+ "Viewport.font", new FontUIResource("Dialog", Font.PLAIN, 12)
+ };
+ defaults.putDefaults(uiDefaults);
+ }
+
+ /**
+ * Returns the <code>ActionMap</code> that stores all the actions that are
+ * responsibly for rendering auditory cues.
+ *
+ * @return the action map that stores all the actions that are
+ * responsibly for rendering auditory cues
+ *
+ * @see #createAudioAction
+ * @see #playSound
+ *
+ * @since 1.4
+ */
+ protected ActionMap getAudioActionMap()
+ {
+ if (audioActionMap != null)
+ audioActionMap = new ActionMap();
+ return audioActionMap;
+ }
+
+ /**
+ * Creates an <code>Action</code> that can play an auditory cue specified by
+ * the key. The UIDefaults value for the key is normally a String that points
+ * to an audio file relative to the current package.
+ *
+ * @param key a UIDefaults key that specifies the sound
+ *
+ * @return an action that can play the sound
+ *
+ * @see #playSound
+ *
+ * @since 1.4
+ */
+ protected Action createAudioAction(Object key)
+ {
+ return new AudioAction(key);
+ }
+
+ /**
+ * Plays the sound of the action if it is listed in
+ * <code>AuditoryCues.playList</code>.
+ *
+ * @param audioAction the audio action to play
+ *
+ * @since 1.4
+ */
+ protected void playSound(Action audioAction)
+ {
+ if (audioAction instanceof AudioAction)
+ {
+ Object[] playList = (Object[]) UIManager.get("AuditoryCues.playList");
+ for (int i = 0; i < playList.length; ++i)
+ {
+ if (playList[i].equals(((AudioAction) audioAction).key))
+ {
+ ActionEvent ev = new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED,
+ (String) playList[i]);
+ audioAction.actionPerformed(ev);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Initializes the Look and Feel.
+ */
+ public void initialize()
+ {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ popupHelper = new PopupHelper();
+ toolkit.addAWTEventListener(popupHelper, AWTEvent.MOUSE_EVENT_MASK);
+ }
+
+ /**
+ * Uninitializes the Look and Feel.
+ */
+ public void uninitialize()
+ {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ toolkit.removeAWTEventListener(popupHelper);
+ popupHelper = null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java
new file mode 100644
index 000000000..b9e29128a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java
@@ -0,0 +1,481 @@
+/* BasicMenuBarUI.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BoxLayout;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuBarUI;
+
+/**
+ * UI Delegate for JMenuBar.
+ */
+public class BasicMenuBarUI extends MenuBarUI
+{
+
+ /**
+ * This action is performed for the action command 'takeFocus'.
+ */
+ private static class FocusAction
+ extends AbstractAction
+ {
+
+ /**
+ * Creates a new FocusAction.
+ */
+ FocusAction()
+ {
+ super("takeFocus");
+ }
+
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // In the JDK this action seems to pop up the first menu of the
+ // menu bar.
+ JMenuBar menuBar = (JMenuBar) event.getSource();
+ MenuSelectionManager defaultManager =
+ MenuSelectionManager.defaultManager();
+ MenuElement me[];
+ MenuElement subElements[];
+ JMenu menu = menuBar.getMenu(0);
+ if (menu != null)
+ {
+ me = new MenuElement[3];
+ me[0] = (MenuElement) menuBar;
+ me[1] = (MenuElement) menu;
+ me[2] = (MenuElement) menu.getPopupMenu();
+ defaultManager.setSelectedPath(me);
+ }
+ }
+
+ }
+
+ protected ChangeListener changeListener;
+
+ /*ContainerListener that listens to the ContainerEvents fired from menu bar*/
+ protected ContainerListener containerListener;
+
+ /*Property change listeners that listener to PropertyChangeEvent from menu bar*/
+ private PropertyChangeListener propertyChangeListener;
+
+ /* menu bar for which this UI delegate is for*/
+ protected JMenuBar menuBar;
+
+ /* MouseListener that listens to the mouseEvents fired from menu bar*/
+ private MouseInputListener mouseListener;
+
+ /**
+ * Creates a new BasicMenuBarUI object.
+ */
+ public BasicMenuBarUI()
+ {
+ changeListener = createChangeListener();
+ containerListener = createContainerListener();
+ propertyChangeListener = new PropertyChangeHandler();
+ mouseListener = new MouseInputHandler();
+ }
+
+ /**
+ * Creates ChangeListener
+ *
+ * @return The ChangeListener
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new ChangeHandler();
+ }
+
+ /**
+ * Creates ContainerListener() to listen for ContainerEvents
+ * fired by JMenuBar.
+ *
+ * @return The ContainerListener
+ */
+ protected ContainerListener createContainerListener()
+ {
+ return new ContainerHandler();
+ }
+
+ /**
+ * Factory method to create a BasicMenuBarUI for the given {@link
+ * JComponent}, which should be a {@link JMenuBar}.
+ *
+ * @param x The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicMenuBarUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicMenuBarUI();
+ }
+
+ /**
+ * Returns maximum size for the specified menu bar
+ *
+ * @param c component for which to get maximum size
+ *
+ * @return Maximum size for the specified menu bar
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ // let layout manager calculate its size
+ return null;
+ }
+
+ /**
+ * Returns maximum allowed size of JMenuBar.
+ *
+ * @param c menuBar for which to return maximum size
+ *
+ * @return Maximum size of the give menu bar.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ // let layout manager calculate its size
+ return null;
+ }
+
+ /**
+ * Returns preferred size of JMenuBar.
+ *
+ * @param c menuBar for which to return preferred size
+ *
+ * @return Preferred size of the give menu bar.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ // let layout manager calculate its size
+ return null;
+ }
+
+ /**
+ * Initializes any default properties that this UI has from the defaults for
+ * the Basic look and feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installBorder(menuBar, "MenuBar.border");
+ LookAndFeel.installColorsAndFont(menuBar, "MenuBar.background",
+ "MenuBar.foreground", "MenuBar.font");
+ menuBar.setOpaque(true);
+ }
+
+ /**
+ * This method installs the keyboard actions for the JMenuBar.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install InputMap.
+ Object[] bindings =
+ (Object[]) SharedUIDefaults.get("MenuBar.windowBindings");
+ InputMap inputMap = LookAndFeel.makeComponentInputMap(menuBar, bindings);
+ SwingUtilities.replaceUIInputMap(menuBar,
+ JComponent.WHEN_IN_FOCUSED_WINDOW,
+ inputMap);
+
+ // Install ActionMap.
+ SwingUtilities.replaceUIActionMap(menuBar, getActionMap());
+ }
+
+ /**
+ * Creates and returns the shared action map for JTrees.
+ *
+ * @return the shared action map for JTrees
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("MenuBar.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("MenuBar.actionMap", am);
+ }
+ return am;
+ }
+
+ /**
+ * Creates the default actions when there are none specified by the L&F.
+ *
+ * @return the default actions
+ */
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new FocusAction();
+ am.put(action.getValue(Action.NAME), action);
+ return am;
+ }
+
+ /**
+ * This method installs the listeners needed for this UI to function.
+ */
+ protected void installListeners()
+ {
+ menuBar.addContainerListener(containerListener);
+ menuBar.addPropertyChangeListener(propertyChangeListener);
+ menuBar.addMouseListener(mouseListener);
+ }
+
+ /**
+ * Installs and initializes all fields for this UI delegate. Any properties
+ * of the UI that need to be initialized and/or set to defaults will be
+ * done now. It will also install any listeners necessary.
+ *
+ * @param c The {@link JComponent} that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ menuBar = (JMenuBar) c;
+ menuBar.setLayout(new BoxLayout(menuBar, BoxLayout.X_AXIS));
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * This method uninstalls the defaults and nulls any objects created during
+ * install.
+ */
+ protected void uninstallDefaults()
+ {
+ menuBar.setBackground(null);
+ menuBar.setBorder(null);
+ menuBar.setFont(null);
+ menuBar.setForeground(null);
+ }
+
+ /**
+ * This method reverses the work done in installKeyboardActions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(menuBar,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, null);
+ SwingUtilities.replaceUIActionMap(menuBar, null);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using.
+ */
+ protected void uninstallListeners()
+ {
+ menuBar.removeContainerListener(containerListener);
+ menuBar.removePropertyChangeListener(propertyChangeListener);
+ menuBar.removeMouseListener(mouseListener);
+ }
+
+ /**
+ * Performs the opposite of installUI. Any properties or resources that need
+ * to be cleaned up will be done now. It will also uninstall any listeners
+ * it has. In addition, any properties of this UI will be nulled.
+ *
+ * @param c The {@link JComponent} that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallDefaults();
+ uninstallListeners();
+ uninstallKeyboardActions();
+ menuBar = null;
+ }
+
+ private class ChangeHandler implements ChangeListener
+ {
+ public void stateChanged(ChangeEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * This class handles ContainerEvents fired by JMenuBar. It revalidates
+ * and repaints menu bar whenever menu is added or removed from it.
+ */
+ private class ContainerHandler implements ContainerListener
+ {
+ /**
+ * This method is called whenever menu is added to the menu bar
+ *
+ * @param e The ContainerEvent.
+ */
+ public void componentAdded(ContainerEvent e)
+ {
+ menuBar.revalidate();
+ menuBar.repaint();
+ }
+
+ /**
+ * This method is called whenever menu is removed from the menu bar.
+ *
+ * @param e The ContainerEvent.
+ */
+ public void componentRemoved(ContainerEvent e)
+ {
+ menuBar.revalidate();
+ menuBar.repaint();
+ }
+ }
+
+ /**
+ * This class handles PropertyChangeEvents fired from the JMenuBar
+ */
+ private class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called whenever one of the properties of the MenuBar
+ * changes.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("borderPainted"))
+ menuBar.repaint();
+ if (e.getPropertyName().equals("margin"))
+ menuBar.repaint();
+ }
+ }
+
+ private class MouseInputHandler implements MouseInputListener
+ {
+ /**
+ * Handles mouse clicked event
+ *
+ * @param e Mouse event
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ MenuElement[] me = menuBar.getSubElements();
+
+ for (int i = 0; i < me.length; i++)
+ {
+ JMenu menu = menuBar.getMenu(i);
+ if (menu != null)
+ menu.setSelected(false);
+ }
+ }
+
+ /**
+ * Handles mouse pressed event
+ *
+ * @param e Mouse event
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse released event
+ *
+ * @param e Mouse event
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse exited event
+ *
+ * @param e Mouse event
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse dragged event
+ *
+ * @param e Mouse event
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse moved event
+ *
+ * @param e Mouse event
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse entered event
+ *
+ * @param e Mouse event
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
new file mode 100644
index 000000000..40b539378
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
@@ -0,0 +1,1339 @@
+/* BasicMenuItemUI.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractButton;
+import javax.swing.ActionMap;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComponent;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.MenuDragMouseEvent;
+import javax.swing.event.MenuDragMouseListener;
+import javax.swing.event.MenuKeyEvent;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentInputMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuItemUI;
+import javax.swing.text.View;
+
+/**
+ * UI Delegate for JMenuItem.
+ */
+public class BasicMenuItemUI extends MenuItemUI
+{
+ /**
+ * Font to be used when displaying menu item's accelerator.
+ */
+ protected Font acceleratorFont;
+
+ /**
+ * Color to be used when displaying menu item's accelerator.
+ */
+ protected Color acceleratorForeground;
+
+ /**
+ * Color to be used when displaying menu item's accelerator when menu item is
+ * selected.
+ */
+ protected Color acceleratorSelectionForeground;
+
+ /**
+ * Icon that is displayed after the text to indicated that this menu contains
+ * submenu.
+ */
+ protected Icon arrowIcon;
+
+ /**
+ * Icon that is displayed before the text. This icon is only used in
+ * JCheckBoxMenuItem or JRadioBoxMenuItem.
+ */
+ protected Icon checkIcon;
+
+ /**
+ * Number of spaces between icon and text.
+ */
+ protected int defaultTextIconGap = 4;
+
+ /**
+ * Color of the text when menu item is disabled
+ */
+ protected Color disabledForeground;
+
+ /**
+ * The menu Drag mouse listener listening to the menu item.
+ */
+ protected MenuDragMouseListener menuDragMouseListener;
+
+ /**
+ * The menu item itself
+ */
+ protected JMenuItem menuItem;
+
+ /**
+ * Menu Key listener listening to the menu item.
+ */
+ protected MenuKeyListener menuKeyListener;
+
+ /**
+ * mouse input listener listening to menu item.
+ */
+ protected MouseInputListener mouseInputListener;
+
+ /**
+ * Indicates if border should be painted
+ */
+ protected boolean oldBorderPainted;
+
+ /**
+ * Color of text that is used when menu item is selected
+ */
+ protected Color selectionBackground;
+
+ /**
+ * Color of the text that is used when menu item is selected.
+ */
+ protected Color selectionForeground;
+
+ /**
+ * String that separates description of the modifiers and the key
+ */
+ private String acceleratorDelimiter;
+
+ /**
+ * ItemListener to listen for item changes in the menu item
+ */
+ private ItemListener itemListener;
+
+ /**
+ * A PropertyChangeListener to make UI updates after property changes.
+ */
+ private PropertyChangeHandler propertyChangeListener;
+
+ /**
+ * The view rectangle used for layout of the menu item.
+ */
+ private Rectangle viewRect;
+
+ /**
+ * The rectangle that holds the area of the label.
+ */
+ private Rectangle textRect;
+
+ /**
+ * The rectangle that holds the area of the accelerator.
+ */
+ private Rectangle accelRect;
+
+ /**
+ * The rectangle that holds the area of the icon.
+ */
+ private Rectangle iconRect;
+
+ /**
+ * The rectangle that holds the area of the icon.
+ */
+ private Rectangle arrowIconRect;
+
+ /**
+ * The rectangle that holds the area of the check icon.
+ */
+ private Rectangle checkIconRect;
+
+ /**
+ * A rectangle used for temporary storage to avoid creation of new
+ * rectangles.
+ */
+ private Rectangle cachedRect;
+
+ /**
+ * A class to handle PropertChangeEvents for the JMenuItem
+ * @author Anthony Balkissoon abalkiss at redhat dot com.
+ */
+ class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when a property of the menuItem is changed.
+ * Currently it is only used to update the accelerator key bindings.
+ *
+ * @param e
+ * the PropertyChangeEvent
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String property = e.getPropertyName();
+ if (property.equals("accelerator"))
+ {
+ InputMap map = SwingUtilities.getUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (map != null)
+ map.remove((KeyStroke) e.getOldValue());
+ else
+ map = new ComponentInputMapUIResource(menuItem);
+
+ KeyStroke accelerator = (KeyStroke) e.getNewValue();
+ if (accelerator != null)
+ map.put(accelerator, "doClick");
+ }
+ // TextLayout caching for speed-up drawing of text.
+ else if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
+ || property.equals("font"))
+ && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
+ == null)
+ {
+ AbstractButton b = (AbstractButton) e.getSource();
+ String text = b.getText();
+ if (text == null)
+ text = "";
+ FontRenderContext frc = new FontRenderContext(new AffineTransform(),
+ false, false);
+ TextLayout layout = new TextLayout(text, b.getFont(), frc);
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
+ }
+ }
+ }
+
+ /**
+ * A class to handle accelerator keys. This is the Action we will
+ * perform when the accelerator key for this JMenuItem is pressed.
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ */
+ class ClickAction extends AbstractAction
+ {
+ /**
+ * This is what is done when the accelerator key for the JMenuItem is
+ * pressed.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ doClick(MenuSelectionManager.defaultManager());
+ }
+ }
+
+ /**
+ * Creates a new BasicMenuItemUI object.
+ */
+ public BasicMenuItemUI()
+ {
+ mouseInputListener = createMouseInputListener(menuItem);
+ menuDragMouseListener = createMenuDragMouseListener(menuItem);
+ menuKeyListener = createMenuKeyListener(menuItem);
+ itemListener = new ItemHandler();
+ propertyChangeListener = new PropertyChangeHandler();
+
+ // Initialize rectangles for layout.
+ viewRect = new Rectangle();
+ textRect = new Rectangle();
+ iconRect = new Rectangle();
+ arrowIconRect = new Rectangle();
+ checkIconRect = new Rectangle();
+ accelRect = new Rectangle();
+ cachedRect = new Rectangle();
+ }
+
+ /**
+ * Create MenuDragMouseListener to listen for mouse dragged events.
+ *
+ * @param c
+ * menu item to listen to
+ * @return The MenuDragMouseListener
+ */
+ protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
+ {
+ return new MenuDragMouseHandler();
+ }
+
+ /**
+ * Creates MenuKeyListener to listen to key events occuring when menu item is
+ * visible on the screen.
+ *
+ * @param c
+ * menu item to listen to
+ * @return The MenuKeyListener
+ */
+ protected MenuKeyListener createMenuKeyListener(JComponent c)
+ {
+ return new MenuKeyHandler();
+ }
+
+ /**
+ * Handles mouse input events occuring for this menu item
+ *
+ * @param c
+ * menu item to listen to
+ * @return The MouseInputListener
+ */
+ protected MouseInputListener createMouseInputListener(JComponent c)
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * Factory method to create a BasicMenuItemUI for the given {@link
+ * JComponent}, which should be a {@link JMenuItem}.
+ *
+ * @param c
+ * The {@link JComponent} a UI is being created for.
+ * @return A BasicMenuItemUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicMenuItemUI();
+ }
+
+ /**
+ * Programatically clicks menu item.
+ *
+ * @param msm
+ * MenuSelectionManager for the menu hierarchy
+ */
+ protected void doClick(MenuSelectionManager msm)
+ {
+ menuItem.doClick(0);
+ msm.clearSelectedPath();
+ }
+
+ /**
+ * Returns maximum size for the specified menu item
+ *
+ * @param c
+ * component for which to get maximum size
+ * @return Maximum size for the specified menu item.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * Returns minimum size for the specified menu item
+ *
+ * @param c
+ * component for which to get minimum size
+ * @return Minimum size for the specified menu item.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * Returns path to this menu item.
+ *
+ * @return $MenuElement[]$ Returns array of menu elements that constitute a
+ * path to this menu item.
+ */
+ public MenuElement[] getPath()
+ {
+ ArrayList path = new ArrayList();
+
+ Component c = menuItem;
+ while (c instanceof MenuElement)
+ {
+ path.add(0, c);
+
+ if (c instanceof JPopupMenu)
+ c = ((JPopupMenu) c).getInvoker();
+ else
+ c = c.getParent();
+ }
+
+ MenuElement[] pathArray = new MenuElement[path.size()];
+ path.toArray(pathArray);
+ return pathArray;
+ }
+
+ /**
+ * Returns preferred size for the given menu item.
+ *
+ * @param c
+ * menu item for which to get preferred size
+ * @param checkIcon
+ * check icon displayed in the given menu item
+ * @param arrowIcon
+ * arrow icon displayed in the given menu item
+ * @param defaultTextIconGap
+ * space between icon and text in the given menuItem
+ * @return $Dimension$ preferred size for the given menu item
+ */
+ protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon,
+ Icon arrowIcon,
+ int defaultTextIconGap)
+ {
+ JMenuItem m = (JMenuItem) c;
+ String accelText = getAcceleratorString(m);
+
+ // Layout the menu item. The result gets stored in the rectangle
+ // fields of this class.
+ resetRectangles(null);
+ layoutMenuItem(m, accelText);
+
+ // The union of the text and icon areas is the label area.
+ cachedRect.setBounds(textRect);
+ Rectangle pref = SwingUtilities.computeUnion(iconRect.x, iconRect.y,
+ iconRect.width,
+ iconRect.height,
+ cachedRect);
+
+ // Find the widest menu item text and accelerator and store it in
+ // client properties of the parent, so that we can align the accelerators
+ // properly. Of course, we only need can do this, if the parent is
+ // a JComponent and this menu item is not a toplevel menu.
+ Container parent = m.getParent();
+ if (parent != null && parent instanceof JComponent
+ && !(m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ JComponent p = (JComponent) parent;
+
+ // The widest text so far.
+ Integer maxTextWidth = (Integer) p.getClientProperty("maxTextWidth");
+ int maxTextValue = maxTextWidth == null ? 0 : maxTextWidth.intValue();
+ if (pref.width < maxTextValue)
+ pref.width = maxTextValue;
+ else
+ p.putClientProperty("maxTextWidth", new Integer(pref.width));
+
+ // The widest accelerator so far.
+ Integer maxAccelWidth = (Integer) p.getClientProperty("maxAccelWidth");
+ int maxAccelValue = maxAccelWidth == null ? 0
+ : maxAccelWidth.intValue();
+ if (accelRect.width > maxAccelValue)
+ {
+ maxAccelValue = accelRect.width;
+ p.putClientProperty("maxAccelWidth", new Integer(accelRect.width));
+ }
+ pref.width += maxAccelValue;
+ pref.width += defaultTextIconGap;
+ }
+
+ // Add arrow and check size if appropriate.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ pref.width += checkIconRect.width;
+ pref.width += defaultTextIconGap;
+ pref.width += arrowIconRect.width;
+ pref.width += defaultTextIconGap;
+ }
+
+ // Add a gap ~2 times as wide as the defaultTextIconGap.
+ pref.width += 2 * defaultTextIconGap;
+
+ // Respect the insets of the menu item.
+ Insets i = m.getInsets();
+ pref.width += i.left + i.right;
+ pref.height += i.top + i.bottom;
+
+ // Return a copy, so that nobody messes with our textRect.
+ return pref.getSize();
+ }
+
+ /**
+ * Returns preferred size of the given component
+ *
+ * @param c
+ * component for which to return preferred size
+ * @return $Dimension$ preferred size for the given component
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return getPreferredMenuItemSize(c, checkIcon, arrowIcon,
+ defaultTextIconGap);
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "MenuItem"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "MenuItem";
+ }
+
+ /**
+ * This method installs the components for this {@link JMenuItem}.
+ *
+ * @param menuItem
+ * The {@link JMenuItem} to install components for.
+ */
+ protected void installComponents(JMenuItem menuItem)
+ {
+ // FIXME: Need to implement
+ }
+
+ /**
+ * This method installs the defaults that are defined in the Basic look and
+ * feel for this {@link JMenuItem}.
+ */
+ protected void installDefaults()
+ {
+ String prefix = getPropertyPrefix();
+ LookAndFeel.installBorder(menuItem, prefix + ".border");
+ LookAndFeel.installColorsAndFont(menuItem, prefix + ".background",
+ prefix + ".foreground", prefix + ".font");
+ menuItem.setMargin(UIManager.getInsets(prefix + ".margin"));
+ acceleratorFont = UIManager.getFont(prefix + ".acceleratorFont");
+ acceleratorForeground = UIManager.getColor(prefix
+ + ".acceleratorForeground");
+ acceleratorSelectionForeground = UIManager.getColor(prefix
+ + ".acceleratorSelectionForeground");
+ selectionBackground = UIManager.getColor(prefix + ".selectionBackground");
+ selectionForeground = UIManager.getColor(prefix + ".selectionForeground");
+ acceleratorDelimiter = UIManager.getString(prefix + ".acceleratorDelimiter");
+ checkIcon = UIManager.getIcon(prefix + ".checkIcon");
+
+ menuItem.setHorizontalTextPosition(SwingConstants.TRAILING);
+ menuItem.setHorizontalAlignment(SwingConstants.LEADING);
+ }
+
+ /**
+ * This method installs the keyboard actions for this {@link JMenuItem}.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap focusedWindowMap = SwingUtilities.getUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (focusedWindowMap == null)
+ focusedWindowMap = new ComponentInputMapUIResource(menuItem);
+ KeyStroke accelerator = menuItem.getAccelerator();
+ if (accelerator != null)
+ focusedWindowMap.put(accelerator, "doClick");
+ SwingUtilities.replaceUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, focusedWindowMap);
+
+ ActionMap UIActionMap = SwingUtilities.getUIActionMap(menuItem);
+ if (UIActionMap == null)
+ UIActionMap = new ActionMapUIResource();
+ UIActionMap.put("doClick", new ClickAction());
+ SwingUtilities.replaceUIActionMap(menuItem, UIActionMap);
+ }
+
+ /**
+ * This method installs the listeners for the {@link JMenuItem}.
+ */
+ protected void installListeners()
+ {
+ menuItem.addMouseListener(mouseInputListener);
+ menuItem.addMouseMotionListener(mouseInputListener);
+ menuItem.addMenuDragMouseListener(menuDragMouseListener);
+ menuItem.addMenuKeyListener(menuKeyListener);
+ menuItem.addItemListener(itemListener);
+ menuItem.addPropertyChangeListener(propertyChangeListener);
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ propertyChangeListener.propertyChange(new PropertyChangeEvent(menuItem,
+ "font", null,
+ menuItem.getFont()));
+ }
+
+ /**
+ * Installs and initializes all fields for this UI delegate. Any properties of
+ * the UI that need to be initialized and/or set to defaults will be done now.
+ * It will also install any listeners necessary.
+ *
+ * @param c
+ * The {@link JComponent} that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ menuItem = (JMenuItem) c;
+ installDefaults();
+ installComponents(menuItem);
+ installListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * Paints given menu item using specified graphics context
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param c
+ * Menu Item to paint
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ paintMenuItem(g, c, checkIcon, arrowIcon, selectionBackground,
+ c.getForeground(), defaultTextIconGap);
+ }
+
+ /**
+ * Paints background of the menu item
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param menuItem
+ * menu item to paint
+ * @param bgColor
+ * Background color to use when painting menu item
+ */
+ protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor)
+ {
+ // Menu item is considered to be highlighted when it is selected.
+ // But we don't want to paint the background of JCheckBoxMenuItems
+ ButtonModel mod = menuItem.getModel();
+ Color saved = g.getColor();
+ if (mod.isArmed() || ((menuItem instanceof JMenu) && mod.isSelected()))
+ {
+ g.setColor(bgColor);
+ g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
+ }
+ else if (menuItem.isOpaque())
+ {
+ g.setColor(menuItem.getBackground());
+ g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
+ }
+ g.setColor(saved);
+ }
+
+ /**
+ * Paints specified menu item
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param c
+ * menu item to paint
+ * @param checkIcon
+ * check icon to use when painting menu item
+ * @param arrowIcon
+ * arrow icon to use when painting menu item
+ * @param background
+ * Background color of the menu item
+ * @param foreground
+ * Foreground color of the menu item
+ * @param defaultTextIconGap
+ * space to use between icon and text when painting menu item
+ */
+ protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
+ Icon arrowIcon, Color background,
+ Color foreground, int defaultTextIconGap)
+ {
+ JMenuItem m = (JMenuItem) c;
+
+ // Fetch fonts.
+ Font oldFont = g.getFont();
+ Font font = c.getFont();
+ g.setFont(font);
+ FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
+
+ // Create accelerator string.
+ String accelText = getAcceleratorString(m);
+
+ // Layout menu item. The result gets stored in the rectangle fields
+ // of this class.
+ resetRectangles(m);
+
+ layoutMenuItem(m, accelText);
+
+ // Paint the background.
+ paintBackground(g, m, background);
+
+ Color oldColor = g.getColor();
+
+ // Paint the check icon.
+ if (checkIcon != null)
+ {
+ checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y);
+ }
+
+ // Paint the icon.
+ ButtonModel model = m.getModel();
+ if (m.getIcon() != null)
+ {
+ // Determine icon depending on the menu item
+ // state (normal/disabled/pressed).
+ Icon icon;
+ if (! m.isEnabled())
+ {
+ icon = m.getDisabledIcon();
+ }
+ else if (model.isPressed() && model.isArmed())
+ {
+ icon = m.getPressedIcon();
+ if (icon == null)
+ {
+ icon = m.getIcon();
+ }
+ }
+ else
+ {
+ icon = m.getIcon();
+ }
+
+ if (icon != null)
+ {
+ icon.paintIcon(m, g, iconRect.x, iconRect.y);
+ }
+ }
+
+ // Paint the text.
+ String text = m.getText();
+ if (text != null)
+ {
+ // Handle HTML.
+ View html = (View) m.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ html.paint(g, textRect);
+ }
+ else
+ {
+ paintText(g, m, textRect, text);
+ }
+ }
+
+ // Paint accelerator text.
+ if (! accelText.equals(""))
+ {
+ // Align the accelerator text. In getPreferredMenuItemSize() we
+ // store a client property 'maxAccelWidth' in the parent which holds
+ // the maximum accelerator width for the children of this parent.
+ // We use this here to align the accelerators properly.
+ int accelOffset = 0;
+ Container parent = m.getParent();
+ if (parent != null && parent instanceof JComponent)
+ {
+ JComponent p = (JComponent) parent;
+ Integer maxAccelWidth =
+ (Integer) p.getClientProperty("maxAccelWidth");
+ int maxAccelValue = maxAccelWidth == null ? 0
+ : maxAccelWidth.intValue();
+ accelOffset = maxAccelValue - accelRect.width;
+ }
+
+ g.setFont(acceleratorFont);
+ if (! m.isEnabled())
+ {
+ // Paint accelerator disabled.
+ g.setColor(disabledForeground);
+ }
+ else
+ {
+ if (m.isArmed() || (m instanceof JMenu && m.isSelected()))
+ g.setColor(acceleratorSelectionForeground);
+ else
+ g.setColor(acceleratorForeground);
+ }
+ g.drawString(accelText, accelRect.x - accelOffset,
+ accelRect.y + accelFm.getAscent());
+ }
+
+ // Paint arrow.
+ if (arrowIcon != null
+ && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y);
+ }
+
+ g.setFont(oldFont);
+ g.setColor(oldColor);
+
+ }
+
+ /**
+ * Paints label for the given menu item
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param menuItem
+ * menu item for which to draw its label
+ * @param textRect
+ * rectangle specifiying position of the text relative to the given
+ * menu item
+ * @param text
+ * label of the menu item
+ */
+ protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect,
+ String text)
+ {
+ Font f = menuItem.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+
+ if (text != null && !text.equals(""))
+ {
+ if (menuItem.isEnabled())
+ {
+ // Menu item is considered to be highlighted when it is selected.
+ // But not if it's a JCheckBoxMenuItem
+ ButtonModel mod = menuItem.getModel();
+ if ((menuItem.isSelected() && checkIcon == null)
+ || (mod != null && mod.isArmed())
+ && (menuItem.getParent() instanceof MenuElement))
+ g.setColor(selectionForeground);
+ else
+ g.setColor(menuItem.getForeground());
+ }
+ else
+ // FIXME: should fix this to use 'disabledForeground', but its
+ // default value in BasicLookAndFeel is null.
+
+ // FIXME: should there be different foreground colours for selected
+ // or deselected, when disabled?
+ g.setColor(Color.gray);
+
+ int mnemonicIndex = menuItem.getDisplayedMnemonicIndex();
+
+ if (mnemonicIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(menuItem, g, text,
+ mnemonicIndex,
+ textRect.x,
+ textRect.y
+ + fm.getAscent());
+ else
+ BasicGraphicsUtils.drawString(menuItem, g, text, 0, textRect.x,
+ textRect.y + fm.getAscent());
+ }
+ }
+
+ /**
+ * This method uninstalls the components for this {@link JMenuItem}.
+ *
+ * @param menuItem
+ * The {@link JMenuItem} to uninstall components for.
+ */
+ protected void uninstallComponents(JMenuItem menuItem)
+ {
+ // FIXME: need to implement
+ }
+
+ /**
+ * This method uninstalls the defaults and sets any objects created during
+ * install to null
+ */
+ protected void uninstallDefaults()
+ {
+ menuItem.setForeground(null);
+ menuItem.setBackground(null);
+ menuItem.setBorder(null);
+ menuItem.setMargin(null);
+ menuItem.setBackground(null);
+ menuItem.setBorder(null);
+ menuItem.setFont(null);
+ menuItem.setForeground(null);
+ menuItem.setMargin(null);
+ acceleratorFont = null;
+ acceleratorForeground = null;
+ acceleratorSelectionForeground = null;
+ arrowIcon = null;
+ selectionBackground = null;
+ selectionForeground = null;
+ acceleratorDelimiter = null;
+ }
+
+ /**
+ * Uninstalls any keyboard actions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, null);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using.
+ */
+ protected void uninstallListeners()
+ {
+ menuItem.removeMouseListener(mouseInputListener);
+ menuItem.removeMenuDragMouseListener(menuDragMouseListener);
+ menuItem.removeMenuKeyListener(menuKeyListener);
+ menuItem.removeItemListener(itemListener);
+ menuItem.removePropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Performs the opposite of installUI. Any properties or resources that need
+ * to be cleaned up will be done now. It will also uninstall any listeners it
+ * has. In addition, any properties of this UI will be nulled.
+ *
+ * @param c
+ * The {@link JComponent} that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallComponents(menuItem);
+ c.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
+ menuItem = null;
+ }
+
+ /**
+ * This method calls paint.
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param c
+ * The menu item to paint
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ paint(g, c);
+ }
+
+ /**
+ * This class handles mouse events occuring inside the menu item. Most of the
+ * events are forwarded for processing to MenuSelectionManager of the current
+ * menu hierarchy.
+ */
+ protected class MouseInputHandler implements MouseInputListener
+ {
+ /**
+ * Creates a new MouseInputHandler object.
+ */
+ protected MouseInputHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when mouse is clicked on the menu item. It forwards
+ * this event to MenuSelectionManager.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse is dragged inside the menu item. It
+ * forwards this event to MenuSelectionManager.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse enters menu item. When this happens menu
+ * item is considered to be selected and selection path in
+ * MenuSelectionManager is set. This event is also forwarded to
+ * MenuSelection Manager for further processing.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ Component source = (Component) e.getSource();
+ if (source.getParent() instanceof MenuElement)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.setSelectedPath(getPath());
+ manager.processMouseEvent(e);
+ }
+ }
+
+ /**
+ * This method is called when mouse exits menu item. The event is forwarded
+ * to MenuSelectionManager for processing.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse is inside the menu item. This event is
+ * forwarder to MenuSelectionManager for further processing.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse is pressed. This event is forwarded to
+ * MenuSelectionManager for further processing.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse is released. If the mouse is released
+ * inside this menuItem, then this menu item is considered to be chosen and
+ * the menu hierarchy should be closed.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ int x = e.getX();
+ int y = e.getY();
+ if (x > 0 && x < menuItem.getWidth() && y > 0
+ && y < menuItem.getHeight())
+ {
+ doClick(manager);
+ }
+ else
+ manager.processMouseEvent(e);
+ }
+ }
+
+ /**
+ * This class handles mouse dragged events.
+ */
+ private class MenuDragMouseHandler implements MenuDragMouseListener
+ {
+ /**
+ * Tbis method is invoked when mouse is dragged over the menu item.
+ *
+ * @param e
+ * The MenuDragMouseEvent
+ */
+ public void menuDragMouseDragged(MenuDragMouseEvent e)
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ manager.setSelectedPath(e.getPath());
+ }
+
+ /**
+ * Tbis method is invoked when mouse enters the menu item while it is being
+ * dragged.
+ *
+ * @param e
+ * The MenuDragMouseEvent
+ */
+ public void menuDragMouseEntered(MenuDragMouseEvent e)
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ manager.setSelectedPath(e.getPath());
+ }
+
+ /**
+ * Tbis method is invoked when mouse exits the menu item while it is being
+ * dragged
+ *
+ * @param e the MenuDragMouseEvent
+ */
+ public void menuDragMouseExited(MenuDragMouseEvent e)
+ {
+ // Nothing to do here yet.
+ }
+
+ /**
+ * Tbis method is invoked when mouse was dragged and released inside the
+ * menu item.
+ *
+ * @param e
+ * The MenuDragMouseEvent
+ */
+ public void menuDragMouseReleased(MenuDragMouseEvent e)
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ int x = e.getX();
+ int y = e.getY();
+ if (x >= 0 && x < menuItem.getWidth() && y >= 0
+ && y < menuItem.getHeight())
+ doClick(manager);
+ else
+ manager.clearSelectedPath();
+ }
+ }
+
+ /**
+ * This class handles key events occuring when menu item is visible on the
+ * screen.
+ */
+ private class MenuKeyHandler implements MenuKeyListener
+ {
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e
+ * A {@link MenuKeyEvent}.
+ */
+ public void menuKeyPressed(MenuKeyEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e
+ * A {@link MenuKeyEvent}.
+ */
+ public void menuKeyReleased(MenuKeyEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * This method is invoked when key has been typed It handles the mnemonic
+ * key for the menu item.
+ *
+ * @param e
+ * A {@link MenuKeyEvent}.
+ */
+ public void menuKeyTyped(MenuKeyEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * Helper class that listens for item changes to the properties of the {@link
+ * JMenuItem}.
+ */
+ private class ItemHandler implements ItemListener
+ {
+ /**
+ * This method is called when one of the menu item changes.
+ *
+ * @param evt A {@link ItemEvent}.
+ */
+ public void itemStateChanged(ItemEvent evt)
+ {
+ boolean state = false;
+ if (menuItem instanceof JCheckBoxMenuItem)
+ {
+ if (evt.getStateChange() == ItemEvent.SELECTED)
+ state = true;
+ ((JCheckBoxMenuItem) menuItem).setState(state);
+ }
+ menuItem.revalidate();
+ menuItem.repaint();
+ }
+ }
+
+ /**
+ * A helper method to create the accelerator string from the menu item's
+ * accelerator property. The returned string is empty if there is
+ * no accelerator defined.
+ *
+ * @param m the menu item
+ *
+ * @return the accelerator string, not null
+ */
+ private String getAcceleratorString(JMenuItem m)
+ {
+ // Create accelerator string.
+ KeyStroke accel = m.getAccelerator();
+ String accelText = "";
+ if (accel != null)
+ {
+ int mods = accel.getModifiers();
+ if (mods > 0)
+ {
+ accelText = KeyEvent.getKeyModifiersText(mods);
+ accelText += acceleratorDelimiter;
+ }
+ int keycode = accel.getKeyCode();
+ if (keycode != 0)
+ accelText += KeyEvent.getKeyText(keycode);
+ else
+ accelText += accel.getKeyChar();
+ }
+ return accelText;
+ }
+
+ /**
+ * Resets the cached layout rectangles. If <code>i</code> is not null, then
+ * the view rectangle is set to the inner area of the component, otherwise
+ * it is set to (0, 0, Short.MAX_VALUE, Short.MAX_VALUE), this is needed
+ * for layouting.
+ *
+ * @param i the component for which to initialize the rectangles
+ */
+ private void resetRectangles(JMenuItem i)
+ {
+ // Reset rectangles.
+ iconRect.setBounds(0, 0, 0, 0);
+ textRect.setBounds(0, 0, 0, 0);
+ accelRect.setBounds(0, 0, 0, 0);
+ checkIconRect.setBounds(0, 0, 0, 0);
+ arrowIconRect.setBounds(0, 0, 0, 0);
+ if (i == null)
+ viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
+ else
+ {
+ Insets insets = i.getInsets();
+ viewRect.setBounds(insets.left, insets.top,
+ i.getWidth() - insets.left - insets.right,
+ i.getHeight() - insets.top - insets.bottom);
+ }
+ }
+
+ /**
+ * A helper method that lays out the menu item. The layout is stored
+ * in the fields of this class.
+ *
+ * @param m the menu item to layout
+ * @param accelText the accelerator text
+ */
+ private void layoutMenuItem(JMenuItem m, String accelText)
+ {
+ // Fetch the fonts.
+ Font font = m.getFont();
+ FontMetrics fm = m.getFontMetrics(font);
+ FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
+
+ String text = m.getText();
+ SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(),
+ m.getVerticalAlignment(),
+ m.getHorizontalAlignment(),
+ m.getVerticalTextPosition(),
+ m.getHorizontalTextPosition(),
+ viewRect, iconRect, textRect,
+ defaultTextIconGap);
+
+ // Initialize accelerator width and height.
+ if (! accelText.equals(""))
+ {
+ accelRect.width = accelFm.stringWidth(accelText);
+ accelRect.height = accelFm.getHeight();
+ }
+
+ // Initialize check and arrow icon width and height.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ if (checkIcon != null)
+ {
+ checkIconRect.width = checkIcon.getIconWidth();
+ checkIconRect.height = checkIcon.getIconHeight();
+ }
+ if (arrowIcon != null)
+ {
+ arrowIconRect.width = arrowIcon.getIconWidth();
+ arrowIconRect.height = arrowIcon.getIconHeight();
+ }
+ }
+
+ // The union of the icon and text of the menu item is the 'label area'.
+ cachedRect.setBounds(textRect);
+ Rectangle labelRect = SwingUtilities.computeUnion(iconRect.x,
+ iconRect.y,
+ iconRect.width,
+ iconRect.height,
+ cachedRect);
+ textRect.x += defaultTextIconGap;
+ iconRect.x += defaultTextIconGap;
+
+ // Layout accelerator rect.
+ accelRect.x = viewRect.x + viewRect.width - arrowIconRect.width
+ - defaultTextIconGap - accelRect.width;
+ // Layout check and arrow icons only when not in toplevel menu.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ checkIconRect.x = viewRect.x + defaultTextIconGap;
+ textRect.x += defaultTextIconGap + checkIconRect.width;
+ iconRect.x += defaultTextIconGap + checkIconRect.width;
+ arrowIconRect.x = viewRect.x + viewRect.width - defaultTextIconGap
+ - arrowIconRect.width;
+ }
+
+ // Align the accelerator text and all the icons vertically centered to
+ // the menu text.
+ accelRect.y = labelRect.y + (labelRect.height / 2)
+ - (accelRect.height / 2);
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ arrowIconRect.y = labelRect.y + (labelRect.height / 2)
+ - (arrowIconRect.height / 2);
+ checkIconRect.y = labelRect.y + (labelRect.height / 2)
+ - (checkIconRect.height / 2);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java
new file mode 100644
index 000000000..4897ee4a0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java
@@ -0,0 +1,678 @@
+/* BasicMenuUI.java
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.NotImplementedException;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.JComponent;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JPopupMenu;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.Timer;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MenuDragMouseEvent;
+import javax.swing.event.MenuDragMouseListener;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuKeyEvent;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.event.MenuListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * UI Delegate for JMenu
+ */
+public class BasicMenuUI extends BasicMenuItemUI
+{
+ /**
+ * Selects a menu. This is used to delay menu selection.
+ */
+ class SelectMenuAction
+ extends AbstractAction
+ {
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JMenu menu = (JMenu) menuItem;
+ MenuSelectionManager defaultManager =
+ MenuSelectionManager.defaultManager();
+ MenuElement path[] = defaultManager.getSelectedPath();
+ if(path.length > 0 && path[path.length - 1] == menu)
+ {
+ MenuElement newPath[] = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ defaultManager.setSelectedPath(newPath);
+ }
+ }
+
+ }
+
+ protected ChangeListener changeListener;
+
+ /* MenuListener listens to MenuEvents fired by JMenu */
+ protected MenuListener menuListener;
+
+ /* PropertyChangeListner that listens to propertyChangeEvents occuring in JMenu*/
+ protected PropertyChangeListener propertyChangeListener;
+
+ /**
+ * Creates a new BasicMenuUI object.
+ */
+ public BasicMenuUI()
+ {
+ mouseInputListener = createMouseInputListener((JMenu) menuItem);
+ menuListener = createMenuListener((JMenu) menuItem);
+ propertyChangeListener = createPropertyChangeListener((JMenu) menuItem);
+ }
+
+ /**
+ * This method creates a new ChangeListener.
+ *
+ * @return A new ChangeListener.
+ */
+ protected ChangeListener createChangeListener(JComponent c)
+ {
+ return new ChangeHandler((JMenu) c, this);
+ }
+
+ /**
+ * This method creates new MenuDragMouseListener to listen to mouse dragged events
+ * occuring in the Menu
+ *
+ * @param c the menu to listen to
+ *
+ * @return The MenuDrageMouseListener
+ */
+ protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
+ {
+ return new MenuDragMouseHandler();
+ }
+
+ /**
+ * This method creates new MenuDragKeyListener to listen to key events
+ *
+ * @param c the menu to listen to
+ *
+ * @return The MenuKeyListener
+ */
+ protected MenuKeyListener createMenuKeyListener(JComponent c)
+ {
+ return new MenuKeyHandler();
+ }
+
+ /**
+ * This method creates new MenuListener to listen to menu events
+ * occuring in the Menu
+ *
+ * @param c the menu to listen to
+ *
+ * @return The MenuListener
+ */
+ protected MenuListener createMenuListener(JComponent c)
+ {
+ return new MenuHandler();
+ }
+
+ /**
+ * This method creates new MouseInputListener to listen to mouse input events
+ * occuring in the Menu
+ *
+ * @param c the menu to listen to
+ *
+ * @return The MouseInputListener
+ */
+ protected MouseInputListener createMouseInputListener(JComponent c)
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * This method creates newPropertyChangeListener to listen to property changes
+ * occuring in the Menu
+ *
+ * @param c the menu to listen to
+ *
+ * @return The PropertyChangeListener
+ */
+ protected PropertyChangeListener createPropertyChangeListener(JComponent c)
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method creates a new BasicMenuUI.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicMenuUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicMenuUI();
+ }
+
+ /**
+ * Get the component's maximum size.
+ *
+ * @param c The JComponent for which to get maximum size
+ *
+ * @return The maximum size of the component
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return c.getPreferredSize();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "Menu"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "Menu";
+ }
+
+ /**
+ * Initializes any default properties that this UI has from the defaults for
+ * the Basic look and feel.
+ */
+ protected void installDefaults()
+ {
+
+ LookAndFeel.installBorder(menuItem, "Menu.border");
+ LookAndFeel.installColorsAndFont(menuItem, "Menu.background",
+ "Menu.foreground", "Menu.font");
+ menuItem.setMargin(UIManager.getInsets("Menu.margin"));
+ acceleratorFont = UIManager.getFont("Menu.acceleratorFont");
+ acceleratorForeground = UIManager.getColor("Menu.acceleratorForeground");
+ acceleratorSelectionForeground = UIManager.getColor("Menu.acceleratorSelectionForeground");
+ selectionBackground = UIManager.getColor("Menu.selectionBackground");
+ selectionForeground = UIManager.getColor("Menu.selectionForeground");
+ arrowIcon = UIManager.getIcon("Menu.arrowIcon");
+ oldBorderPainted = UIManager.getBoolean("Menu.borderPainted");
+ ((JMenu) menuItem).setDelay(200);
+ }
+
+ /**
+ * Installs any keyboard actions. The list of keys that need to be bound are
+ * listed in Basic look and feel's defaults.
+ *
+ */
+ protected void installKeyboardActions()
+ {
+ super.installKeyboardActions();
+ }
+
+ /**
+ * Creates and registers all the listeners for this UI delegate.
+ */
+ protected void installListeners()
+ {
+ super.installListeners();
+ ((JMenu) menuItem).addMenuListener(menuListener);
+ }
+
+ protected void setupPostTimer(JMenu menu)
+ {
+ Timer timer = new Timer(menu.getDelay(), new SelectMenuAction());
+ timer.setRepeats(false);
+ timer.start();
+ }
+
+ /**
+ * This method uninstalls the defaults and sets any objects created during
+ * install to null
+ */
+ protected void uninstallDefaults()
+ {
+ menuItem.setBackground(null);
+ menuItem.setBorder(null);
+ menuItem.setFont(null);
+ menuItem.setForeground(null);
+ menuItem.setMargin(null);
+ acceleratorFont = null;
+ acceleratorForeground = null;
+ acceleratorSelectionForeground = null;
+ selectionBackground = null;
+ selectionForeground = null;
+ arrowIcon = null;
+ }
+
+ /**
+ * Uninstalls any keyboard actions. The list of keys used are listed in
+ * Basic look and feel's defaults.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ super.installKeyboardActions();
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using. In
+ * addition, it will also null any listeners that it was using.
+ */
+ protected void uninstallListeners()
+ {
+ super.uninstallListeners();
+ ((JMenu) menuItem).removeMenuListener(menuListener);
+ }
+
+ /**
+ * This class is used by menus to handle mouse events occuring in the
+ * menu.
+ */
+ protected class MouseInputHandler implements MouseInputListener
+ {
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseDragged(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ private boolean popupVisible()
+ {
+ JMenuBar mb = (JMenuBar) ((JMenu) menuItem).getParent();
+ // check if mb.isSelected because if no menus are selected
+ // we don't have to look through the list for popup menus
+ if (!mb.isSelected())
+ return false;
+ for (int i = 0; i < mb.getMenuCount(); i++)
+ {
+ JMenu m = mb.getMenu(i);
+ if (m != null && m.isPopupMenuVisible())
+ return true;
+ }
+ return false;
+ }
+
+ public void mouseEntered(MouseEvent e)
+ {
+ JMenu menu = (JMenu) menuItem;
+ if (menu.isEnabled())
+ {
+ MenuSelectionManager manager =
+ MenuSelectionManager.defaultManager();
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if (! menu.isTopLevelMenu())
+ {
+ // Open the menu immediately or delayed, depending on the
+ // delay value.
+ if(! (selectedPath.length > 0
+ && selectedPath[selectedPath.length - 1] == menu.getPopupMenu()))
+ {
+ if(menu.getDelay() == 0)
+ {
+ MenuElement[] path = getPath();
+ MenuElement[] newPath = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ manager.setSelectedPath(getPath());
+ setupPostTimer(menu);
+ }
+ }
+ }
+ else
+ {
+ if(selectedPath.length > 0
+ && selectedPath[0] == menu.getParent())
+ {
+ MenuElement[] newPath = new MenuElement[3];
+ newPath[0] = (MenuElement) menu.getParent();
+ newPath[1] = menu;
+ newPath[2] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ }
+ }
+ }
+
+ public void mouseExited(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mousePressed(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ JMenu menu = (JMenu) menuItem;
+ if (menu.isEnabled())
+ {
+ // Open up the menu immediately if it's a toplevel menu.
+ // But not yet the popup, which might be opened delayed, see below.
+ if (menu.isTopLevelMenu())
+ {
+ if (menu.isSelected())
+ manager.clearSelectedPath();
+ else
+ {
+ Container cnt = menu.getParent();
+ if (cnt != null && cnt instanceof JMenuBar)
+ {
+ MenuElement[] me = new MenuElement[2];
+ me[0] = (MenuElement) cnt;
+ me[1] = menu;
+ manager.setSelectedPath(me);
+ }
+ }
+ }
+
+ // Open the menu's popup. Either do that immediately if delay == 0,
+ // or delayed when delay > 0.
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if (selectedPath.length > 0
+ && selectedPath[selectedPath.length - 1] != menu.getPopupMenu())
+ {
+ if(menu.isTopLevelMenu() || menu.getDelay() == 0)
+ {
+ MenuElement[] newPath =
+ new MenuElement[selectedPath.length + 1];
+ System.arraycopy(selectedPath, 0, newPath, 0,
+ selectedPath.length);
+ newPath[selectedPath.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ setupPostTimer(menu);
+ }
+ }
+
+ }
+ }
+
+ public void mouseReleased(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+ }
+
+ /**
+ * This class handles MenuEvents fired by the JMenu
+ */
+ private class MenuHandler implements MenuListener
+ {
+ /**
+ * This method is called when menu is cancelled. The menu is cancelled
+ * when its popup menu is closed without selection. It clears selected index
+ * in the selectionModel of the menu parent.
+ *
+ * @param e The MenuEvent.
+ */
+ public void menuCanceled(MenuEvent e)
+ {
+ menuDeselected(e);
+ }
+
+ /**
+ * This method is called when menu is deselected. It clears selected index
+ * in the selectionModel of the menu parent.
+ *
+ * @param e The MenuEvent.
+ */
+ public void menuDeselected(MenuEvent e)
+ {
+ JMenu menu = (JMenu) menuItem;
+ if (menu.getParent() != null)
+ {
+ if (menu.isTopLevelMenu())
+ ((JMenuBar) menu.getParent()).getSelectionModel().clearSelection();
+ else
+ ((JPopupMenu) menu.getParent()).getSelectionModel().clearSelection();
+ }
+ }
+
+ /**
+ * This method is called when menu is selected. It sets selected index
+ * in the selectionModel of the menu parent.
+ *
+ * @param e The MenuEvent.
+ */
+ public void menuSelected(MenuEvent e)
+ {
+ JMenu menu = (JMenu) menuItem;
+ if (menu.isTopLevelMenu())
+ ((JMenuBar) menu.getParent()).setSelected(menu);
+ else
+ ((JPopupMenu) menu.getParent()).setSelected(menu);
+ }
+ }
+
+ /**
+ * Obsolete as of JDK1.4.
+ */
+ public class ChangeHandler implements ChangeListener
+ {
+ /**
+ * Not used.
+ */
+ public boolean isSelected;
+
+ /**
+ * Not used.
+ */
+ public JMenu menu;
+
+ /**
+ * Not used.
+ */
+ public BasicMenuUI ui;
+
+ /**
+ * Not used.
+ */
+ public Component wasFocused;
+
+ /**
+ * Not used.
+ */
+ public ChangeHandler(JMenu m, BasicMenuUI ui)
+ {
+ menu = m;
+ this.ui = ui;
+ }
+
+ /**
+ * Not used.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Not used.
+ }
+ }
+
+ /**
+ * This class handles mouse dragged events occuring in the menu.
+ */
+ private class MenuDragMouseHandler implements MenuDragMouseListener
+ {
+ /**
+ * This method is invoked when mouse is dragged over the menu item.
+ *
+ * @param e The MenuDragMouseEvent
+ */
+ public void menuDragMouseDragged(MenuDragMouseEvent e)
+ {
+ if (menuItem.isEnabled())
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ MenuElement path[] = e.getPath();
+
+ Point p = e.getPoint();
+ if(p.x >= 0 && p.x < menuItem.getWidth()
+ && p.y >= 0 && p.y < menuItem.getHeight())
+ {
+ JMenu menu = (JMenu) menuItem;
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if(! (selectedPath.length > 0
+ && selectedPath[selectedPath.length-1]
+ == menu.getPopupMenu()))
+ {
+ if(menu.isTopLevelMenu() || menu.getDelay() == 0
+ || e.getID() == MouseEvent.MOUSE_DRAGGED)
+ {
+ MenuElement[] newPath = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ manager.setSelectedPath(path);
+ setupPostTimer(menu);
+ }
+ }
+ }
+ else if (e.getID() == MouseEvent.MOUSE_RELEASED)
+ {
+ Component comp = manager.componentForPoint(e.getComponent(),
+ e.getPoint());
+ if (comp == null)
+ manager.clearSelectedPath();
+ }
+ }
+ }
+
+ /**
+ * This method is invoked when mouse enters the menu item while it is
+ * being dragged.
+ *
+ * @param e The MenuDragMouseEvent
+ */
+ public void menuDragMouseEntered(MenuDragMouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked when mouse exits the menu item while
+ * it is being dragged
+ *
+ * @param e The MenuDragMouseEvent
+ */
+ public void menuDragMouseExited(MenuDragMouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked when mouse was dragged and released
+ * inside the menu item.
+ *
+ * @param e The MenuDragMouseEvent
+ */
+ public void menuDragMouseReleased(MenuDragMouseEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This class handles key events occuring when menu item is visible on the
+ * screen.
+ */
+ private class MenuKeyHandler implements MenuKeyListener
+ {
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e A {@link MenuKeyEvent}.
+ */
+ public void menuKeyPressed(MenuKeyEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e A {@link MenuKeyEvent}.
+ */
+ public void menuKeyReleased(MenuKeyEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked when key has been typed
+ * It handles the mnemonic key for the menu item.
+ *
+ * @param e A {@link MenuKeyEvent}.
+ */
+ public void menuKeyTyped(MenuKeyEvent e)
+ throws NotImplementedException
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java
new file mode 100644
index 000000000..a03d224d2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java
@@ -0,0 +1,1404 @@
+/* BasicOptionPaneUI.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Polygon;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.OptionPaneUI;
+
+/**
+ * This class is the UI delegate for JOptionPane in the Basic Look and Feel.
+ */
+public class BasicOptionPaneUI extends OptionPaneUI
+{
+ /**
+ * Implements the "close" keyboard action.
+ */
+ static class OptionPaneCloseAction
+ extends AbstractAction
+ {
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JOptionPane op = (JOptionPane) event.getSource();
+ op.setValue(new Integer(JOptionPane.CLOSED_OPTION));
+ }
+
+ }
+
+ /**
+ * This is a helper class that listens to the buttons located at the bottom
+ * of the JOptionPane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ButtonActionListener implements ActionListener
+ {
+ /** The index of the option this button represents. */
+ protected int buttonIndex;
+
+ /**
+ * Creates a new ButtonActionListener object with the given buttonIndex.
+ *
+ * @param buttonIndex The index of the option this button represents.
+ */
+ public ButtonActionListener(int buttonIndex)
+ {
+ this.buttonIndex = buttonIndex;
+ }
+
+ /**
+ * This method is called when one of the option buttons are pressed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ Object value = new Integer(JOptionPane.CLOSED_OPTION);
+ Object[] options = optionPane.getOptions();
+ if (options != null)
+ value = new Integer(buttonIndex);
+ else
+ {
+ String text = ((JButton) e.getSource()).getText();
+ if (text.equals(OK_STRING))
+ value = new Integer(JOptionPane.OK_OPTION);
+ if (text.equals(CANCEL_STRING))
+ value = new Integer(JOptionPane.CANCEL_OPTION);
+ if (text.equals(YES_STRING))
+ value = new Integer(JOptionPane.YES_OPTION);
+ if (text.equals(NO_STRING))
+ value = new Integer(JOptionPane.NO_OPTION);
+ }
+ optionPane.setValue(value);
+ resetInputValue();
+
+ Window owner = SwingUtilities.windowForComponent(optionPane);
+
+ if (owner instanceof JDialog)
+ ((JDialog) owner).dispose();
+
+ //else we probably have some kind of internal frame.
+ JInternalFrame inf = (JInternalFrame) SwingUtilities.getAncestorOfClass(
+ JInternalFrame.class, optionPane);
+ if (inf != null)
+ {
+ try
+ {
+ inf.setClosed(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if attempt has been vetoed.
+ }
+ }
+ }
+ }
+
+ /**
+ * This helper layout manager is responsible for the layout of the button
+ * area. The button area is the panel that holds the buttons which
+ * represent the options.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public static class ButtonAreaLayout implements LayoutManager
+ {
+ /** Whether this layout will center the buttons. */
+ protected boolean centersChildren = true;
+
+ /** The space between the buttons. */
+ protected int padding;
+
+ /** Whether the buttons will share the same widths. */
+ protected boolean syncAllWidths;
+
+ /** The width of the widest button. */
+ private transient int widthOfWidestButton;
+
+ /** The height of the tallest button. */
+ private transient int tallestButton;
+
+ /**
+ * Creates a new ButtonAreaLayout object with the given sync widths
+ * property and padding.
+ *
+ * @param syncAllWidths Whether the buttons will share the same widths.
+ * @param padding The padding between the buttons.
+ */
+ public ButtonAreaLayout(boolean syncAllWidths, int padding)
+ {
+ this.syncAllWidths = syncAllWidths;
+ this.padding = padding;
+ }
+
+ /**
+ * This method is called when a component is added to the container.
+ *
+ * @param string The constraints string.
+ * @param comp The component added.
+ */
+ public void addLayoutComponent(String string, Component comp)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method returns whether the children will be centered.
+ *
+ * @return Whether the children will be centered.
+ */
+ public boolean getCentersChildren()
+ {
+ return centersChildren;
+ }
+
+ /**
+ * This method returns the amount of space between components.
+ *
+ * @return The amount of space between components.
+ */
+ public int getPadding()
+ {
+ return padding;
+ }
+
+ /**
+ * This method returns whether all components will share widths (set to
+ * largest width).
+ *
+ * @return Whether all components will share widths.
+ */
+ public boolean getSyncAllWidths()
+ {
+ return syncAllWidths;
+ }
+
+ /**
+ * This method lays out the given container.
+ *
+ * @param container The container to lay out.
+ */
+ public void layoutContainer(Container container)
+ {
+ Component[] buttonList = container.getComponents();
+ int x = container.getInsets().left;
+ if (getCentersChildren())
+ x += (int) ((double) (container.getSize().width) / 2
+ - (double) (buttonRowLength(container)) / 2);
+ for (int i = 0; i < buttonList.length; i++)
+ {
+ Dimension dims = buttonList[i].getPreferredSize();
+ if (syncAllWidths)
+ {
+ buttonList[i].setBounds(x, 0, widthOfWidestButton, dims.height);
+ x += widthOfWidestButton + getPadding();
+ }
+ else
+ {
+ buttonList[i].setBounds(x, 0, dims.width, dims.height);
+ x += dims.width + getPadding();
+ }
+ }
+ }
+
+ /**
+ * This method returns the width of the given container taking into
+ * consideration the padding and syncAllWidths.
+ *
+ * @param c The container to calculate width for.
+ *
+ * @return The width of the given container.
+ */
+ private int buttonRowLength(Container c)
+ {
+ Component[] buttonList = c.getComponents();
+
+ int buttonLength = 0;
+ int widest = 0;
+ int tallest = 0;
+
+ for (int i = 0; i < buttonList.length; i++)
+ {
+ Dimension dims = buttonList[i].getPreferredSize();
+ buttonLength += dims.width + getPadding();
+ widest = Math.max(widest, dims.width);
+ tallest = Math.max(tallest, dims.height);
+ }
+
+ widthOfWidestButton = widest;
+ tallestButton = tallest;
+
+ int width;
+ if (getSyncAllWidths())
+ width = widest * buttonList.length
+ + getPadding() * (buttonList.length - 1);
+ else
+ width = buttonLength;
+
+ Insets insets = c.getInsets();
+ width += insets.left + insets.right;
+
+ return width;
+ }
+
+ /**
+ * This method returns the minimum layout size for the given container.
+ *
+ * @param c The container to measure.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return preferredLayoutSize(c);
+ }
+
+ /**
+ * This method returns the preferred size of the given container.
+ *
+ * @param c The container to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ int w = buttonRowLength(c);
+
+ return new Dimension(w, tallestButton);
+ }
+
+ /**
+ * This method removes the given component from the layout manager's
+ * knowledge.
+ *
+ * @param c The component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method sets whether the children will be centered.
+ *
+ * @param newValue Whether the children will be centered.
+ */
+ public void setCentersChildren(boolean newValue)
+ {
+ centersChildren = newValue;
+ }
+
+ /**
+ * This method sets the amount of space between each component.
+ *
+ * @param newPadding The padding between components.
+ */
+ public void setPadding(int newPadding)
+ {
+ padding = newPadding;
+ }
+
+ /**
+ * This method sets whether the widths will be synced.
+ *
+ * @param newValue Whether the widths will be synced.
+ */
+ public void setSyncAllWidths(boolean newValue)
+ {
+ syncAllWidths = newValue;
+ }
+ }
+
+ /**
+ * This helper class handles property change events from the JOptionPane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when one of the properties of the JOptionPane
+ * changes.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String property = e.getPropertyName();
+ if (property.equals(JOptionPane.ICON_PROPERTY)
+ || property.equals(JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY)
+ || property.equals(JOptionPane.INITIAL_VALUE_PROPERTY)
+ || property.equals(JOptionPane.MESSAGE_PROPERTY)
+ || property.equals(JOptionPane.MESSAGE_TYPE_PROPERTY)
+ || property.equals(JOptionPane.OPTION_TYPE_PROPERTY)
+ || property.equals(JOptionPane.OPTIONS_PROPERTY)
+ || property.equals(JOptionPane.WANTS_INPUT_PROPERTY))
+ {
+ uninstallComponents();
+ installComponents();
+ optionPane.validate();
+ }
+ }
+ }
+
+ /**
+ * The minimum width for JOptionPanes.
+ */
+ public static final int MinimumWidth = 262;
+
+ /**
+ * The minimum height for JOptionPanes.
+ */
+ public static final int MinimumHeight = 90;
+
+ /** Whether the JOptionPane contains custom components. */
+ protected boolean hasCustomComponents;
+
+ // The initialFocusComponent seems to always be set to a button (even if
+ // I try to set initialSelectionValue). This is different from what the
+ // javadocs state (which should switch this reference to the input component
+ // if one is present since that is what's going to get focus).
+
+ /**
+ * The button that will receive focus based on initialValue when no input
+ * component is present. If an input component is present, then the input
+ * component will receive focus instead.
+ */
+ protected Component initialFocusComponent;
+
+ /** The component that receives input when the JOptionPane needs it. */
+ protected JComponent inputComponent;
+
+ /** The minimum dimensions of the JOptionPane. */
+ protected Dimension minimumSize;
+
+ /** The propertyChangeListener for the JOptionPane. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The JOptionPane this UI delegate is used for. */
+ protected JOptionPane optionPane;
+
+ /** The size of the icons. */
+ private static final int ICON_SIZE = 36;
+
+ /** The string used to describe OK buttons. */
+ private static final String OK_STRING = "OK";
+
+ /** The string used to describe Yes buttons. */
+ private static final String YES_STRING = "Yes";
+
+ /** The string used to describe No buttons. */
+ private static final String NO_STRING = "No";
+
+ /** The string used to describe Cancel buttons. */
+ private static final String CANCEL_STRING = "Cancel";
+
+ /** The container for the message area.
+ * This is package-private to avoid an accessor method. */
+ transient Container messageAreaContainer;
+
+ /** The container for the buttons.
+ * This is package-private to avoid an accessor method. */
+ transient Container buttonContainer;
+
+ /**
+ * A helper class that implements Icon. This is used temporarily until
+ * ImageIcons are fixed.
+ */
+ private static class MessageIcon implements Icon
+ {
+ /**
+ * This method returns the width of the icon.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return ICON_SIZE;
+ }
+
+ /**
+ * This method returns the height of the icon.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return ICON_SIZE;
+ }
+
+ /**
+ * This method paints the icon as a part of the given component using the
+ * given graphics and the given x and y position.
+ *
+ * @param c The component that owns this icon.
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /** The icon displayed for ERROR_MESSAGE. */
+ private static MessageIcon errorIcon = new MessageIcon()
+ {
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Polygon oct = new Polygon(new int[] { 0, 0, 9, 27, 36, 36, 27, 9 },
+ new int[] { 9, 27, 36, 36, 27, 9, 0, 0 }, 8);
+ g.translate(x, y);
+
+ Color saved = g.getColor();
+ g.setColor(Color.RED);
+
+ g.fillPolygon(oct);
+
+ g.setColor(Color.BLACK);
+ g.drawRect(13, 16, 10, 4);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ };
+
+ /** The icon displayed for INFORMATION_MESSAGE. */
+ private static MessageIcon infoIcon = new MessageIcon()
+ {
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+
+ // Should be purple.
+ g.setColor(Color.RED);
+
+ g.fillOval(0, 0, ICON_SIZE, ICON_SIZE);
+
+ g.setColor(Color.BLACK);
+ g.drawOval(16, 6, 4, 4);
+
+ Polygon bottomI = new Polygon(new int[] { 15, 15, 13, 13, 23, 23, 21, 21 },
+ new int[] { 12, 28, 28, 30, 30, 28, 28, 12 },
+ 8);
+ g.drawPolygon(bottomI);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ };
+
+ /** The icon displayed for WARNING_MESSAGE. */
+ private static MessageIcon warningIcon = new MessageIcon()
+ {
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+ g.setColor(Color.YELLOW);
+
+ Polygon triangle = new Polygon(new int[] { 0, 18, 36 },
+ new int[] { 36, 0, 36 }, 3);
+ g.fillPolygon(triangle);
+
+ g.setColor(Color.BLACK);
+
+ Polygon excl = new Polygon(new int[] { 15, 16, 20, 21 },
+ new int[] { 8, 26, 26, 8 }, 4);
+ g.drawPolygon(excl);
+ g.drawOval(16, 30, 4, 4);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ };
+
+ /** The icon displayed for MESSAGE_ICON. */
+ private static MessageIcon questionIcon = new MessageIcon()
+ {
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+ g.setColor(Color.GREEN);
+
+ g.fillRect(0, 0, ICON_SIZE, ICON_SIZE);
+
+ g.setColor(Color.BLACK);
+
+ g.drawOval(11, 2, 16, 16);
+ g.drawOval(14, 5, 10, 10);
+
+ g.setColor(Color.GREEN);
+ g.fillRect(0, 10, ICON_SIZE, ICON_SIZE - 10);
+
+ g.setColor(Color.BLACK);
+
+ g.drawLine(11, 10, 14, 10);
+
+ g.drawLine(24, 10, 17, 22);
+ g.drawLine(27, 10, 20, 22);
+ g.drawLine(17, 22, 20, 22);
+
+ g.drawOval(17, 25, 3, 3);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ };
+
+ /**
+ * Creates a new BasicOptionPaneUI object.
+ */
+ public BasicOptionPaneUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is messaged to add the buttons to the given container.
+ *
+ * @param container The container to add components to.
+ * @param buttons The buttons to add. (If it is an instance of component,
+ * the Object is added directly. If it is an instance of Icon, it is
+ * packed into a label and added. For all other cases, the string
+ * representation of the Object is retreived and packed into a
+ * label.)
+ * @param initialIndex The index of the component that is the initialValue.
+ */
+ protected void addButtonComponents(Container container, Object[] buttons,
+ int initialIndex)
+ {
+ if (buttons == null)
+ return;
+ for (int i = 0; i < buttons.length; i++)
+ {
+ if (buttons[i] != null)
+ {
+ Component toAdd;
+ if (buttons[i] instanceof Component)
+ toAdd = (Component) buttons[i];
+ else
+ {
+ if (buttons[i] instanceof Icon)
+ toAdd = new JButton((Icon) buttons[i]);
+ else
+ toAdd = new JButton(buttons[i].toString());
+ hasCustomComponents = true;
+ }
+ if (toAdd instanceof JButton)
+ ((JButton) toAdd).addActionListener(createButtonActionListener(i));
+ if (i == initialIndex)
+ initialFocusComponent = toAdd;
+ container.add(toAdd);
+ }
+ }
+ selectInitialValue(optionPane);
+ }
+
+ /**
+ * This method adds the appropriate icon the given container.
+ *
+ * @param top The container to add an icon to.
+ */
+ protected void addIcon(Container top)
+ {
+ JLabel iconLabel = null;
+ Icon icon = getIcon();
+ if (icon != null)
+ {
+ iconLabel = new JLabel(icon);
+ configureLabel(iconLabel);
+ top.add(iconLabel, BorderLayout.WEST);
+ }
+ }
+
+ /**
+ * A helper method that returns an instance of GridBagConstraints to be used
+ * for creating the message area.
+ *
+ * @return An instance of GridBagConstraints.
+ */
+ private static GridBagConstraints createConstraints()
+ {
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.gridx = GridBagConstraints.REMAINDER;
+ constraints.gridy = GridBagConstraints.REMAINDER;
+ constraints.gridwidth = 0;
+ constraints.anchor = GridBagConstraints.LINE_START;
+ constraints.fill = GridBagConstraints.NONE;
+ constraints.insets = new Insets(0, 0, 3, 0);
+
+ return constraints;
+ }
+
+ /**
+ * This method creates the proper object (if necessary) to represent msg.
+ * (If msg is an instance of Component, it will add it directly. If it is
+ * an icon, then it will pack it in a label and add it. Otherwise, it gets
+ * treated as a string. If the string is longer than maxll, a box is
+ * created and the burstStringInto is called with the box as the container.
+ * The box is then added to the given container. Otherwise, the string is
+ * packed in a label and placed in the given container.) This method is
+ * also used for adding the inputComponent to the container.
+ *
+ * @param container The container to add to.
+ * @param cons The constraints when adding.
+ * @param msg The message to add.
+ * @param maxll The max line length.
+ * @param internallyCreated Whether the msg is internally created.
+ */
+ protected void addMessageComponents(Container container,
+ GridBagConstraints cons, Object msg,
+ int maxll, boolean internallyCreated)
+ {
+ if (msg == null)
+ return;
+ hasCustomComponents = internallyCreated;
+ if (msg instanceof Object[])
+ {
+ Object[] arr = (Object[]) msg;
+ for (int i = 0; i < arr.length; i++)
+ addMessageComponents(container, cons, arr[i], maxll,
+ internallyCreated);
+ return;
+ }
+ else if (msg instanceof Component)
+ {
+ container.add((Component) msg, cons);
+ cons.gridy++;
+ }
+ else if (msg instanceof Icon)
+ {
+ JLabel label = new JLabel((Icon) msg);
+ configureLabel(label);
+ container.add(label, cons);
+ cons.gridy++;
+ }
+ else
+ {
+ // Undocumented behaviour.
+ // if msg.toString().length greater than maxll
+ // it will create a box and burst the string.
+ // otherwise, it will just create a label and re-call
+ // this method with the label o.O
+ if (msg.toString().length() > maxll || msg.toString().contains("\n"))
+ {
+ Box tmp = new Box(BoxLayout.Y_AXIS);
+ burstStringInto(tmp, msg.toString(), maxll);
+ addMessageComponents(container, cons, tmp, maxll, true);
+ }
+ else
+ {
+ JLabel label = new JLabel(msg.toString());
+ configureLabel(label);
+ addMessageComponents(container, cons, label, maxll, true);
+ }
+ }
+ }
+
+ /**
+ * This method creates instances of d (recursively if necessary based on
+ * maxll) and adds to c.
+ *
+ * @param c The container to add to.
+ * @param d The string to burst.
+ * @param maxll The max line length.
+ */
+ protected void burstStringInto(Container c, String d, int maxll)
+ {
+ if (d == null || c == null)
+ return;
+
+ int newlineIndex = d.indexOf('\n');
+ String line;
+ String remainder;
+ if (newlineIndex >= 0 && newlineIndex < maxll)
+ {
+ line = d.substring(0, newlineIndex);
+ remainder = d.substring(newlineIndex + 1);
+ }
+ else
+ {
+ line = d.substring(0, maxll);
+ remainder = d.substring(maxll);
+ }
+ JLabel label = new JLabel(line);
+ configureLabel(label);
+ c.add(label);
+
+ // If there is nothing left to burst, then we can stop.
+ if (remainder.length() == 0)
+ return;
+
+ // Recursively call ourselves to burst the remainder of the string,
+ if (remainder.length() > maxll || remainder.contains("\n"))
+ burstStringInto(c, remainder, maxll);
+ else
+ {
+ // Add the remainder to the container and be done.
+ JLabel l = new JLabel(remainder);
+ configureLabel(l);
+ c.add(l);
+ }
+ }
+
+ /**
+ * This method returns true if the given JOptionPane contains custom
+ * components.
+ *
+ * @param op The JOptionPane to check.
+ *
+ * @return True if the JOptionPane contains custom components.
+ */
+ public boolean containsCustomComponents(JOptionPane op)
+ {
+ return hasCustomComponents;
+ }
+
+ /**
+ * This method creates a button action listener for the given button index.
+ *
+ * @param buttonIndex The index of the button in components.
+ *
+ * @return A new ButtonActionListener.
+ */
+ protected ActionListener createButtonActionListener(int buttonIndex)
+ {
+ return new ButtonActionListener(buttonIndex);
+ }
+
+ /**
+ * This method creates the button area.
+ *
+ * @return A new Button Area.
+ */
+ protected Container createButtonArea()
+ {
+ JPanel buttonPanel = new JPanel();
+ Border b = UIManager.getBorder("OptionPane.buttonAreaBorder");
+ if (b != null)
+ buttonPanel.setBorder(b);
+
+ buttonPanel.setLayout(createLayoutManager());
+ addButtonComponents(buttonPanel, getButtons(), getInitialValueIndex());
+
+ return buttonPanel;
+ }
+
+ /**
+ * This method creates a new LayoutManager for the button area.
+ *
+ * @return A new LayoutManager for the button area.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new ButtonAreaLayout(getSizeButtonsToSameWidth(), 6);
+ }
+
+ /**
+ * This method creates the message area.
+ *
+ * @return A new message area.
+ */
+ protected Container createMessageArea()
+ {
+ JPanel messageArea = new JPanel();
+ Border messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder");
+ if (messageBorder != null)
+ messageArea.setBorder(messageBorder);
+
+ messageArea.setLayout(new BorderLayout());
+ addIcon(messageArea);
+
+ JPanel rightSide = new JPanel();
+ rightSide.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ rightSide.setLayout(new GridBagLayout());
+ GridBagConstraints con = createConstraints();
+
+ addMessageComponents(rightSide, con, getMessage(),
+ getMaxCharactersPerLineCount(), false);
+
+ if (optionPane.getWantsInput())
+ {
+ Object[] selection = optionPane.getSelectionValues();
+
+ if (selection == null)
+ inputComponent = new JTextField(15);
+ else if (selection.length < 20)
+ inputComponent = new JComboBox(selection);
+ else
+ inputComponent = new JList(selection);
+ if (inputComponent != null)
+ {
+ addMessageComponents(rightSide, con, inputComponent,
+ getMaxCharactersPerLineCount(), false);
+ resetSelectedValue();
+ selectInitialValue(optionPane);
+ }
+ }
+
+ messageArea.add(rightSide, BorderLayout.CENTER);
+
+ return messageArea;
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener for listening to the
+ * JOptionPane.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method creates a Container that will separate the message and button
+ * areas.
+ *
+ * @return A Container that will separate the message and button areas.
+ */
+ protected Container createSeparator()
+ {
+ // The reference implementation returns null here. When overriding
+ // to return something non-null, the component gets added between
+ // the message area and the button area. See installComponents().
+ return null;
+ }
+
+ /**
+ * This method creates a new BasicOptionPaneUI for the given component.
+ *
+ * @param x The component to create a UI for.
+ *
+ * @return A new BasicOptionPaneUI.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicOptionPaneUI();
+ }
+
+ /**
+ * This method returns the buttons for the JOptionPane. If no options are
+ * set, a set of options will be created based upon the optionType.
+ *
+ * @return The buttons that will be added.
+ */
+ protected Object[] getButtons()
+ {
+ if (optionPane.getOptions() != null)
+ return optionPane.getOptions();
+ switch (optionPane.getOptionType())
+ {
+ case JOptionPane.YES_NO_OPTION:
+ return new Object[] { YES_STRING, NO_STRING };
+ case JOptionPane.YES_NO_CANCEL_OPTION:
+ return new Object[] { YES_STRING, NO_STRING, CANCEL_STRING };
+ case JOptionPane.OK_CANCEL_OPTION:
+ return new Object[] { OK_STRING, CANCEL_STRING };
+ case JOptionPane.DEFAULT_OPTION:
+ return (optionPane.getWantsInput()) ?
+ new Object[] { OK_STRING, CANCEL_STRING } :
+ (optionPane.getMessageType() == JOptionPane.QUESTION_MESSAGE) ?
+ new Object[] { YES_STRING, NO_STRING, CANCEL_STRING } :
+ // ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE
+ new Object[] { OK_STRING };
+ }
+ return null;
+ }
+
+ /**
+ * This method will return the icon the user has set or the icon that will
+ * be used based on message type.
+ *
+ * @return The icon to use in the JOptionPane.
+ */
+ protected Icon getIcon()
+ {
+ if (optionPane.getIcon() != null)
+ return optionPane.getIcon();
+ else
+ return getIconForType(optionPane.getMessageType());
+ }
+
+ /**
+ * This method returns the icon for the given messageType.
+ *
+ * @param messageType The type of message.
+ *
+ * @return The icon for the given messageType.
+ */
+ protected Icon getIconForType(int messageType)
+ {
+ Icon tmp = null;
+ switch (messageType)
+ {
+ case JOptionPane.ERROR_MESSAGE:
+ tmp = errorIcon;
+ break;
+ case JOptionPane.INFORMATION_MESSAGE:
+ tmp = infoIcon;
+ break;
+ case JOptionPane.WARNING_MESSAGE:
+ tmp = warningIcon;
+ break;
+ case JOptionPane.QUESTION_MESSAGE:
+ tmp = questionIcon;
+ break;
+ }
+ return tmp;
+ // FIXME: Don't cast till the default icons are in.
+ // return new IconUIResource(tmp);
+ }
+
+ /**
+ * This method returns the index of the initialValue in the options array.
+ *
+ * @return The index of the initalValue.
+ */
+ protected int getInitialValueIndex()
+ {
+ Object[] buttons = getButtons();
+
+ if (buttons == null)
+ return -1;
+
+ Object select = optionPane.getInitialValue();
+
+ for (int i = 0; i < buttons.length; i++)
+ {
+ if (select == buttons[i])
+ return i;
+ }
+ return 0;
+ }
+
+ /**
+ * This method returns the maximum number of characters that should be
+ * placed on a line.
+ *
+ * @return The maximum number of characteres that should be placed on a
+ * line.
+ */
+ protected int getMaxCharactersPerLineCount()
+ {
+ return optionPane.getMaxCharactersPerLineCount();
+ }
+
+ /**
+ * This method returns the maximum size.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the message of the JOptionPane.
+ *
+ * @return The message.
+ */
+ protected Object getMessage()
+ {
+ return optionPane.getMessage();
+ }
+
+ /**
+ * This method returns the minimum size of the JOptionPane.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumOptionPaneSize()
+ {
+ return minimumSize;
+ }
+
+ /**
+ * This method returns the minimum size.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the preferred size of the JOptionPane. The preferred
+ * size is the maximum of the size desired by the layout and the minimum
+ * size.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension d = optionPane.getLayout().preferredLayoutSize(optionPane);
+ Dimension d2 = getMinimumOptionPaneSize();
+
+ int w = Math.max(d.width, d2.width);
+ int h = Math.max(d.height, d2.height);
+ return new Dimension(w, h);
+ }
+
+ /**
+ * This method returns whether all buttons should have the same width.
+ *
+ * @return Whether all buttons should have the same width.
+ */
+ protected boolean getSizeButtonsToSameWidth()
+ {
+ return true;
+ }
+
+ /**
+ * This method installs components for the JOptionPane.
+ */
+ protected void installComponents()
+ {
+ // First thing is the message area.
+ optionPane.add(createMessageArea());
+
+ // Add separator when createSeparator() is overridden to return
+ // something other than null.
+ Container sep = createSeparator();
+ if (sep != null)
+ optionPane.add(sep);
+
+ // Last thing is the button area.
+ optionPane.add(createButtonArea());
+ }
+
+ /**
+ * This method installs defaults for the JOptionPane.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background",
+ "OptionPane.foreground",
+ "OptionPane.font");
+ LookAndFeel.installBorder(optionPane, "OptionPane.border");
+ optionPane.setOpaque(true);
+
+ minimumSize = UIManager.getDimension("OptionPane.minimumSize");
+
+ // FIXME: Image icons don't seem to work properly right now.
+ // Once they do, replace the synthetic icons with these ones.
+
+ /*
+ warningIcon = (IconUIResource) defaults.getIcon("OptionPane.warningIcon");
+ infoIcon = (IconUIResource) defaults.getIcon("OptionPane.informationIcon");
+ errorIcon = (IconUIResource) defaults.getIcon("OptionPane.errorIcon");
+ questionIcon = (IconUIResource) defaults.getIcon("OptionPane.questionIcon");
+ */
+ }
+
+ /**
+ * This method installs keyboard actions for the JOptionpane.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install the input map.
+ Object[] bindings =
+ (Object[]) SharedUIDefaults.get("OptionPane.windowBindings");
+ InputMap inputMap = LookAndFeel.makeComponentInputMap(optionPane,
+ bindings);
+ SwingUtilities.replaceUIInputMap(optionPane,
+ JComponent.WHEN_IN_FOCUSED_WINDOW,
+ inputMap);
+
+ // FIXME: The JDK uses a LazyActionMap for parentActionMap
+ SwingUtilities.replaceUIActionMap(optionPane, getActionMap());
+ }
+
+ /**
+ * Fetches the action map from the UI defaults, or create a new one
+ * if the action map hasn't been initialized.
+ *
+ * @return the action map
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("OptionPane.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("OptionPane.actionMap", am);
+ }
+ return am;
+ }
+
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new OptionPaneCloseAction();
+
+ am.put("close", action);
+ return am;
+ }
+
+ /**
+ * This method installs listeners for the JOptionPane.
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+
+ optionPane.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * This method installs the UI for the JOptionPane.
+ *
+ * @param c The JComponent to install the UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JOptionPane)
+ {
+ optionPane = (JOptionPane) c;
+
+ installDefaults();
+ installComponents();
+ installListeners();
+ installKeyboardActions();
+ }
+ }
+
+ /**
+ * Changes the inputValue property in the JOptionPane based on the current
+ * value of the inputComponent.
+ */
+ protected void resetInputValue()
+ {
+ if (optionPane.getWantsInput() && inputComponent != null)
+ {
+ Object output = null;
+ if (inputComponent instanceof JTextField)
+ output = ((JTextField) inputComponent).getText();
+ else if (inputComponent instanceof JComboBox)
+ output = ((JComboBox) inputComponent).getSelectedItem();
+ else if (inputComponent instanceof JList)
+ output = ((JList) inputComponent).getSelectedValue();
+
+ if (output != null)
+ optionPane.setInputValue(output);
+ }
+ }
+
+ /**
+ * This method requests focus to the inputComponent (if one is present) and
+ * the initialFocusComponent otherwise.
+ *
+ * @param op The JOptionPane.
+ */
+ public void selectInitialValue(JOptionPane op)
+ {
+ if (inputComponent != null)
+ {
+ inputComponent.requestFocus();
+ return;
+ }
+ if (initialFocusComponent != null)
+ initialFocusComponent.requestFocus();
+ }
+
+ /**
+ * This method resets the value in the inputComponent to the
+ * initialSelectionValue property.
+ * This is package-private to avoid an accessor method.
+ */
+ void resetSelectedValue()
+ {
+ if (inputComponent != null)
+ {
+ Object init = optionPane.getInitialSelectionValue();
+ if (init == null)
+ return;
+ if (inputComponent instanceof JTextField)
+ ((JTextField) inputComponent).setText((String) init);
+ else if (inputComponent instanceof JComboBox)
+ ((JComboBox) inputComponent).setSelectedItem(init);
+ else if (inputComponent instanceof JList)
+ {
+ // ((JList) inputComponent).setSelectedValue(init, true);
+ }
+ }
+ }
+
+ /**
+ * This method uninstalls all the components in the JOptionPane.
+ */
+ protected void uninstallComponents()
+ {
+ optionPane.removeAll();
+ buttonContainer = null;
+ messageAreaContainer = null;
+ }
+
+ /**
+ * This method uninstalls the defaults for the JOptionPane.
+ */
+ protected void uninstallDefaults()
+ {
+ optionPane.setFont(null);
+ optionPane.setForeground(null);
+ optionPane.setBackground(null);
+
+ minimumSize = null;
+
+ // FIXME: ImageIcons don't seem to work properly
+
+ /*
+ warningIcon = null;
+ errorIcon = null;
+ questionIcon = null;
+ infoIcon = null;
+ */
+ }
+
+ /**
+ * This method uninstalls keyboard actions for the JOptionPane.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(optionPane, JComponent.
+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ SwingUtilities.replaceUIActionMap(optionPane, null);
+ }
+
+ /**
+ * This method uninstalls listeners for the JOptionPane.
+ */
+ protected void uninstallListeners()
+ {
+ optionPane.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallComponents();
+ uninstallDefaults();
+
+ optionPane = null;
+ }
+
+ /**
+ * Applies the proper UI configuration to labels that are added to
+ * the OptionPane.
+ *
+ * @param l the label to configure
+ */
+ private void configureLabel(JLabel l)
+ {
+ Color c = UIManager.getColor("OptionPane.messageForeground");
+ if (c != null)
+ l.setForeground(c);
+ Font f = UIManager.getFont("OptionPane.messageFont");
+ if (f != null)
+ l.setFont(f);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java
new file mode 100644
index 000000000..959462a78
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java
@@ -0,0 +1,132 @@
+/* BasicPanelUI.java
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.JPanel;
+import javax.swing.LookAndFeel;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.PanelUI;
+
+/**
+ * A UI delegate for the {@link JPanel} component.
+ */
+public class BasicPanelUI extends PanelUI
+{
+ /**
+ * A UI delegate that can be shared by all panels (because the delegate is
+ * stateless).
+ */
+ static BasicPanelUI sharedUI;
+
+ /**
+ * Returns a UI delegate for the specified component.
+ *
+ * @param panel the panel.
+ */
+ public static ComponentUI createUI(JComponent panel)
+ {
+ if (sharedUI == null)
+ sharedUI = new BasicPanelUI();
+ return sharedUI;
+ }
+
+ /**
+ * Installs this UI delegate in the specified component.
+ *
+ * @param c the component (should be a {@link JPanel}, <code>null</code> not
+ * permitted).
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JPanel)
+ {
+ JPanel p = (JPanel) c;
+ installDefaults(p);
+ }
+ }
+
+ /**
+ * Installs the defaults for this UI delegate in the specified panel.
+ *
+ * @param p the panel (<code>null</code> not permitted).
+ */
+ protected void installDefaults(JPanel p)
+ {
+ LookAndFeel.installColorsAndFont(p, "Panel.background", "Panel.foreground",
+ "Panel.font");
+
+ // A test against the reference implementation shows that this method will
+ // install a border if one is defined in the UIDefaults table (even though
+ // the BasicLookAndFeel doesn't actually define a "Panel.border"). This
+ // test was written after discovering that a null argument to
+ // uninstallDefaults throws a NullPointerException in
+ // LookAndFeel.uninstallBorder()...
+ LookAndFeel.installBorder(p, "Panel.border");
+ }
+
+ /**
+ * Uninstalls this UI delegate from the specified component.
+ *
+ * @param c the component (<code>null</code> not permitted).
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallDefaults((JPanel) c);
+ }
+
+ /**
+ * Uninstalls the UI defaults for the specified panel.
+ *
+ * @param p the panel (<code>null</code> not permitted).
+ */
+ protected void uninstallDefaults(JPanel p)
+ {
+ // Tests on the reference implementation showed this method:
+ // (1) doesn't actually remove the installed colors and font installed
+ // by installDefaults(), it isn't necessary;
+ // (2) throws a NullPointerException in LookAndFeel.uninstallBorder() if
+ // p is null. Strangely, no border is installed by the
+ // BasicLookAndFeel - perhaps this is needed by another LAF?
+
+ LookAndFeel.uninstallBorder(p);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java
new file mode 100644
index 000000000..cc839179f
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java
@@ -0,0 +1,74 @@
+/* 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.Element;
+import javax.swing.text.PasswordView;
+import javax.swing.text.View;
+
+public class BasicPasswordFieldUI extends BasicTextFieldUI
+{
+ public BasicPasswordFieldUI()
+ {
+ // Nothing to do here.
+ }
+
+ public View create(Element elem)
+ {
+ return new PasswordView(elem);
+ }
+
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicPasswordFieldUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "PasswordField"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "PasswordField";
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java
new file mode 100644
index 000000000..b62947722
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java
@@ -0,0 +1,114 @@
+/* BasicPopupMenuSeparatorUI.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.JComponent;
+import javax.swing.JPopupMenu;
+import javax.swing.SwingUtilities;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * The Basic Look and Feel UI delegate for JPopupMenu.Separator.
+ */
+public class BasicPopupMenuSeparatorUI extends BasicSeparatorUI
+{
+ /**
+ * Creates a new BasicPopupMenuSeparatorUI object.
+ */
+ public BasicPopupMenuSeparatorUI()
+ {
+ super();
+ }
+
+ /**
+ * Creates a new UI delegate for the given JComponent.
+ *
+ * @param c The JComponent to create a delegate for.
+ *
+ * @return A new BasicPopupMenuSeparatorUI
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicPopupMenuSeparatorUI();
+ }
+
+ /**
+ * The Popup Menu Separator has two lines. The top line will be
+ * painted using highlight color and the bottom using shadow color.
+ *
+ * @param g The Graphics object to paint with
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ if (! (c instanceof JPopupMenu.Separator))
+ return;
+
+ Rectangle r = new Rectangle();
+ SwingUtilities.calculateInnerArea(c, r);
+ Color saved = g.getColor();
+
+ int midAB = r.width / 2 + r.x;
+ int midAD = r.height / 2 + r.y;
+
+ g.setColor(highlight);
+ g.drawLine(r.x, midAD, r.x + r.width, midAD);
+
+ g.setColor(shadow);
+ g.drawLine(r.x, midAD + 1, r.x + r.width, midAD + 1);
+ }
+
+ /**
+ * This method returns the preferred size of the
+ * JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return super.getPreferredSize(c);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java
new file mode 100644
index 000000000..6cd433b33
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java
@@ -0,0 +1,1019 @@
+/* BasicPopupMenuUI.java
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Dimension;
+import java.awt.KeyboardFocusManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.MouseEvent;
+import java.util.EventListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BoxLayout;
+import javax.swing.InputMap;
+import javax.swing.JApplet;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JRootPane;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.PopupMenuUI;
+
+/**
+ * UI Delegate for JPopupMenu
+ */
+public class BasicPopupMenuUI extends PopupMenuUI
+{
+ /**
+ * Handles keyboard navigation through menus.
+ */
+ private static class NavigateAction
+ extends AbstractAction
+ {
+
+ /**
+ * Creates a new NavigateAction instance.
+ *
+ * @param name the name of the action
+ */
+ NavigateAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Actually performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ String name = (String) getValue(Action.NAME);
+ if (name.equals("selectNext"))
+ navigateNextPrevious(true);
+ else if (name.equals("selectPrevious"))
+ navigateNextPrevious(false);
+ else if (name.equals("selectChild"))
+ navigateParentChild(true);
+ else if (name.equals("selectParent"))
+ navigateParentChild(false);
+ else if (name.equals("cancel"))
+ cancel();
+ else if (name.equals("return"))
+ doReturn();
+ else
+ assert false : "Must not reach here";
+ }
+
+ /**
+ * Navigates to the next or previous menu item.
+ *
+ * @param dir <code>true</code>: navigate to next, <code>false</code>:
+ * navigate to previous
+ */
+ private void navigateNextPrevious(boolean dir)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement path[] = msm.getSelectedPath();
+ int len = path.length;
+ if (len >= 2)
+ {
+
+ if (path[0] instanceof JMenuBar &&
+ path[1] instanceof JMenu && len == 2)
+ {
+
+ // A toplevel menu is selected, but its popup not yet shown.
+ // Show the popup and select the first item
+ JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
+ MenuElement next =
+ findEnabledChild(popup.getSubElements(), -1, true);
+ MenuElement[] newPath;
+
+ if (next != null)
+ {
+ newPath = new MenuElement[4];
+ newPath[3] = next;
+ }
+ else
+ {
+ // Menu has no enabled items, show the popup anyway.
+ newPath = new MenuElement[3];
+ }
+ System.arraycopy(path, 0, newPath, 0, 2);
+ newPath[2] = popup;
+ msm.setSelectedPath(newPath);
+ }
+ else if (path[len - 1] instanceof JPopupMenu &&
+ path[len - 2] instanceof JMenu)
+ {
+ // Select next item in already shown popup menu.
+ JMenu menu = (JMenu) path[len - 2];
+ JPopupMenu popup = menu.getPopupMenu();
+ MenuElement next =
+ findEnabledChild(popup.getSubElements(), -1, dir);
+
+ if (next != null)
+ {
+ MenuElement[] newPath = new MenuElement[len + 1];
+ System.arraycopy(path, 0, newPath, 0, len);
+ newPath[len] = next;
+ msm.setSelectedPath(newPath);
+ }
+ else
+ {
+ // All items in the popup are disabled.
+ // Find the parent popup menu and select
+ // its next item. If there's no parent popup menu , do nothing.
+ if (len > 2 && path[len - 3] instanceof JPopupMenu)
+ {
+ popup = ((JPopupMenu) path[len - 3]);
+ next = findEnabledChild(popup.getSubElements(),
+ menu, dir);
+ if (next != null && next != menu)
+ {
+ MenuElement[] newPath = new MenuElement[len - 1];
+ System.arraycopy(path, 0, newPath, 0, len - 2);
+ newPath[len - 2] = next;
+ msm.setSelectedPath(newPath);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Only select the next item.
+ MenuElement subs[] = path[len - 2].getSubElements();
+ MenuElement nextChild =
+ findEnabledChild(subs, path[len - 1], dir);
+ if (nextChild == null)
+ {
+ nextChild = findEnabledChild(subs, -1, dir);
+ }
+ if (nextChild != null)
+ {
+ path[len-1] = nextChild;
+ msm.setSelectedPath(path);
+ }
+ }
+ }
+ }
+
+ private MenuElement findEnabledChild(MenuElement[] children,
+ MenuElement start, boolean dir)
+ {
+ MenuElement found = null;
+ for (int i = 0; i < children.length && found == null; i++)
+ {
+ if (children[i] == start)
+ {
+ found = findEnabledChild(children, i, dir);
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Searches the next or previous enabled child menu element.
+ *
+ * @param children the children to search through
+ * @param start the index at which to start
+ * @param dir the direction (true == forward, false == backward)
+ *
+ * @return the found element or null
+ */
+ private MenuElement findEnabledChild(MenuElement[] children,
+ int start, boolean dir)
+ {
+ MenuElement result = null;
+ if (dir)
+ {
+ result = findNextEnabledChild(children, start + 1, children.length-1);
+ if (result == null)
+ result = findNextEnabledChild(children, 0, start - 1);
+ }
+ else
+ {
+ result = findPreviousEnabledChild(children, start - 1, 0);
+ if (result == null)
+ result = findPreviousEnabledChild(children, children.length-1,
+ start + 1);
+ }
+ return result;
+ }
+
+ /**
+ * Finds the next child element that is enabled and visible.
+ *
+ * @param children the children to search through
+ * @param start the start index
+ * @param end the end index
+ *
+ * @return the found child, or null
+ */
+ private MenuElement findNextEnabledChild(MenuElement[] children, int start,
+ int end)
+ {
+ MenuElement found = null;
+ for (int i = start; i <= end && found == null; i++)
+ {
+ if (children[i] != null)
+ {
+ Component comp = children[i].getComponent();
+ if (comp != null && comp.isEnabled() && comp.isVisible())
+ {
+ found = children[i];
+ }
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Finds the previous child element that is enabled and visible.
+ *
+ * @param children the children to search through
+ * @param start the start index
+ * @param end the end index
+ *
+ * @return the found child, or null
+ */
+ private MenuElement findPreviousEnabledChild(MenuElement[] children,
+ int start, int end)
+ {
+ MenuElement found = null;
+ for (int i = start; i >= end && found == null; i--)
+ {
+ if (children[i] != null)
+ {
+ Component comp = children[i].getComponent();
+ if (comp != null && comp.isEnabled() && comp.isVisible())
+ {
+ found = children[i];
+ }
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Navigates to the parent or child menu item.
+ *
+ * @param selectChild <code>true</code>: navigate to child,
+ * <code>false</code>: navigate to parent
+ */
+ private void navigateParentChild(boolean selectChild)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement path[] = msm.getSelectedPath();
+ int len = path.length;
+
+ if (selectChild)
+ {
+ if (len > 0 && path[len - 1] instanceof JMenu
+ && ! ((JMenu) path[len-1]).isTopLevelMenu())
+ {
+ // We have a submenu, open it.
+ JMenu menu = (JMenu) path[len - 1];
+ JPopupMenu popup = menu.getPopupMenu();
+ MenuElement[] subs = popup.getSubElements();
+ MenuElement item = findEnabledChild(subs, -1, true);
+ MenuElement[] newPath;
+
+ if (item == null)
+ {
+ newPath = new MenuElement[len + 1];
+ }
+ else
+ {
+ newPath = new MenuElement[len + 2];
+ newPath[len + 1] = item;
+ }
+ System.arraycopy(path, 0, newPath, 0, len);
+ newPath[len] = popup;
+ msm.setSelectedPath(newPath);
+ return;
+ }
+ }
+ else
+ {
+ int popupIndex = len-1;
+ if (len > 2
+ && (path[popupIndex] instanceof JPopupMenu
+ || path[--popupIndex] instanceof JPopupMenu)
+ && ! ((JMenu) path[popupIndex - 1]).isTopLevelMenu())
+ {
+ // We have a submenu, close it.
+ MenuElement newPath[] = new MenuElement[popupIndex];
+ System.arraycopy(path, 0, newPath, 0, popupIndex);
+ msm.setSelectedPath(newPath);
+ return;
+ }
+ }
+
+ // If we got here, we have not selected a child or parent.
+ // Check if we have a toplevel menu selected. If so, then select
+ // another one.
+ if (len > 1 && path[0] instanceof JMenuBar)
+ {
+ MenuElement currentMenu = path[1];
+ MenuElement nextMenu = findEnabledChild(path[0].getSubElements(),
+ currentMenu, selectChild);
+
+ if (nextMenu != null && nextMenu != currentMenu)
+ {
+ MenuElement newSelection[];
+ if (len == 2)
+ {
+ // Menu is selected but its popup not shown.
+ newSelection = new MenuElement[2];
+ newSelection[0] = path[0];
+ newSelection[1] = nextMenu;
+ }
+ else
+ {
+ // Menu is selected and its popup is shown.
+ newSelection = new MenuElement[3];
+ newSelection[0] = path[0];
+ newSelection[1] = nextMenu;
+ newSelection[2] = ((JMenu) nextMenu).getPopupMenu();
+ }
+ msm.setSelectedPath(newSelection);
+ }
+ }
+ }
+
+ /**
+ * Handles cancel requests (ESC key).
+ */
+ private void cancel()
+ {
+ // Fire popup menu cancelled event. Unfortunately the
+ // firePopupMenuCancelled() is protected in JPopupMenu so we work
+ // around this limitation by fetching the listeners and notifying them
+ // directly.
+ JPopupMenu lastPopup = (JPopupMenu) getLastPopup();
+ EventListener[] ll = lastPopup.getListeners(PopupMenuListener.class);
+ for (int i = 0; i < ll.length; i++)
+ {
+ PopupMenuEvent ev = new PopupMenuEvent(lastPopup);
+ ((PopupMenuListener) ll[i]).popupMenuCanceled(ev);
+ }
+
+ // Close the last popup or the whole selection if there's only one
+ // popup left.
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement path[] = msm.getSelectedPath();
+ if(path.length > 4)
+ {
+ MenuElement newPath[] = new MenuElement[path.length - 2];
+ System.arraycopy(path,0,newPath,0,path.length-2);
+ MenuSelectionManager.defaultManager().setSelectedPath(newPath);
+ }
+ else
+ msm.clearSelectedPath();
+ }
+
+ /**
+ * Returns the last popup menu in the current selection or null.
+ *
+ * @return the last popup menu in the current selection or null
+ */
+ private JPopupMenu getLastPopup()
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] p = msm.getSelectedPath();
+ JPopupMenu popup = null;
+ for(int i = p.length - 1; popup == null && i >= 0; i--)
+ {
+ if (p[i] instanceof JPopupMenu)
+ popup = (JPopupMenu) p[i];
+ }
+ return popup;
+ }
+
+ /**
+ * Handles ENTER key requests. This normally opens submenus on JMenu
+ * items, or activates the menu item as if it's been clicked on it.
+ */
+ private void doReturn()
+ {
+ KeyboardFocusManager fmgr =
+ KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ Component focusOwner = fmgr.getFocusOwner();
+ if((focusOwner == null || (focusOwner instanceof JRootPane)))
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement path[] = msm.getSelectedPath();
+ MenuElement lastElement;
+ if(path.length > 0)
+ {
+ lastElement = path[path.length - 1];
+ if(lastElement instanceof JMenu)
+ {
+ MenuElement newPath[] = new MenuElement[path.length + 1];
+ System.arraycopy(path,0,newPath,0,path.length);
+ newPath[path.length] = ((JMenu) lastElement).getPopupMenu();
+ msm.setSelectedPath(newPath);
+ }
+ else if(lastElement instanceof JMenuItem)
+ {
+ JMenuItem mi = (JMenuItem)lastElement;
+ if (mi.getUI() instanceof BasicMenuItemUI)
+ {
+ ((BasicMenuItemUI)mi.getUI()).doClick(msm);
+ }
+ else
+ {
+ msm.clearSelectedPath();
+ mi.doClick(0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Installs keyboard actions when a popup is opened, and uninstalls the
+ * keyboard actions when closed. This listens on the default
+ * MenuSelectionManager.
+ */
+ private class KeyboardHelper
+ implements ChangeListener
+ {
+ private MenuElement[] lastSelectedPath = new MenuElement[0];
+ private Component lastFocused;
+ private JRootPane invokerRootPane;
+
+ public void stateChanged(ChangeEvent event)
+ {
+ MenuSelectionManager msm = (MenuSelectionManager) event.getSource();
+ MenuElement[] p = msm.getSelectedPath();
+ JPopupMenu popup = getActivePopup(p);
+ if (popup == null || popup.isFocusable())
+ {
+ if (lastSelectedPath.length != 0 && p.length != 0 )
+ {
+ if (! invokerEquals(p[0], lastSelectedPath[0]))
+ {
+ uninstallKeyboardActionsImpl();
+ lastSelectedPath = new MenuElement[0];
+ }
+ }
+
+ if (lastSelectedPath.length == 0 && p.length > 0)
+ {
+ JComponent invoker;
+ if (popup == null)
+ {
+ if (p.length == 2 && p[0] instanceof JMenuBar
+ && p[1] instanceof JMenu)
+ {
+ // A menu has been selected but not opened.
+ invoker = (JComponent)p[1];
+ popup = ((JMenu)invoker).getPopupMenu();
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ Component c = popup.getInvoker();
+ if(c instanceof JFrame)
+ {
+ invoker = ((JFrame) c).getRootPane();
+ }
+ else if(c instanceof JApplet)
+ {
+ invoker = ((JApplet) c).getRootPane();
+ }
+ else
+ {
+ while (!(c instanceof JComponent))
+ {
+ if (c == null)
+ {
+ return;
+ }
+ c = c.getParent();
+ }
+ invoker = (JComponent)c;
+ }
+ }
+
+ // Remember current focus owner.
+ lastFocused = KeyboardFocusManager.
+ getCurrentKeyboardFocusManager().getFocusOwner();
+
+ // Install keybindings used for menu navigation.
+ invokerRootPane = SwingUtilities.getRootPane(invoker);
+ if (invokerRootPane != null)
+ {
+ invokerRootPane.requestFocus(true);
+ installKeyboardActionsImpl();
+ }
+ }
+ else if (lastSelectedPath.length != 0 && p.length == 0)
+ {
+ // menu hidden -- return focus to where it had been before
+ // and uninstall menu keybindings
+ uninstallKeyboardActionsImpl();
+ }
+ }
+
+ // Remember the last path selected
+ lastSelectedPath = p;
+ }
+
+ private JPopupMenu getActivePopup(MenuElement[] path)
+ {
+ JPopupMenu active = null;
+ for (int i = path.length - 1; i >= 0 && active == null; i--)
+ {
+ MenuElement elem = path[i];
+ if (elem instanceof JPopupMenu)
+ {
+ active = (JPopupMenu) elem;
+ }
+ }
+ return active;
+ }
+
+ private boolean invokerEquals(MenuElement el1, MenuElement el2)
+ {
+ Component invoker1 = el1.getComponent();
+ Component invoker2 = el2.getComponent();
+ if (invoker1 instanceof JPopupMenu)
+ invoker1 = ((JPopupMenu) invoker1).getInvoker();
+ if (invoker2 instanceof JPopupMenu)
+ invoker2 = ((JPopupMenu) invoker2).getInvoker();
+ return invoker1 == invoker2;
+ }
+ }
+
+ /* popupMenu for which this UI delegate is for*/
+ protected JPopupMenu popupMenu;
+
+ /* PopupMenuListener listens to popup menu events fired by JPopupMenu*/
+ private transient PopupMenuListener popupMenuListener;
+
+ /* ComponentListener listening to popupMenu's invoker.
+ * This is package-private to avoid an accessor method. */
+ TopWindowListener topWindowListener;
+
+ /**
+ * Counts how many popup menus are handled by this UI or a subclass.
+ * This is used to install a KeyboardHelper on the MenuSelectionManager
+ * for the first popup, and uninstall this same KeyboardHelper when the
+ * last popup is uninstalled.
+ */
+ private static int numPopups;
+
+ /**
+ * This is the KeyboardHelper that listens on the MenuSelectionManager.
+ */
+ private static KeyboardHelper keyboardHelper;
+
+ /**
+ * Creates a new BasicPopupMenuUI object.
+ */
+ public BasicPopupMenuUI()
+ {
+ popupMenuListener = new PopupMenuHandler();
+ topWindowListener = new TopWindowListener();
+ }
+
+ /**
+ * Factory method to create a BasicPopupMenuUI for the given {@link
+ * JComponent}, which should be a {@link JMenuItem}.
+ *
+ * @param x The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicPopupMenuUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicPopupMenuUI();
+ }
+
+ /**
+ * Installs and initializes all fields for this UI delegate. Any properties
+ * of the UI that need to be initialized and/or set to defaults will be
+ * done now. It will also install any listeners necessary.
+ *
+ * @param c The {@link JComponent} that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ // Install KeyboardHelper when the first popup is initialized.
+ if (numPopups == 0)
+ {
+ keyboardHelper = new KeyboardHelper();
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ msm.addChangeListener(keyboardHelper);
+ }
+ numPopups++;
+
+ popupMenu = (JPopupMenu) c;
+ popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS));
+ popupMenu.setBorderPainted(true);
+ JPopupMenu.setDefaultLightWeightPopupEnabled(true);
+
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * This method installs the defaults that are defined in the Basic look
+ * and feel for this {@link JPopupMenu}.
+ */
+ public void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(popupMenu, "PopupMenu.background",
+ "PopupMenu.foreground", "PopupMenu.font");
+ LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
+ popupMenu.setOpaque(true);
+ }
+
+ /**
+ * This method installs the listeners for the {@link JMenuItem}.
+ */
+ protected void installListeners()
+ {
+ popupMenu.addPopupMenuListener(popupMenuListener);
+ }
+
+ /**
+ * This method installs the keyboard actions for this {@link JPopupMenu}.
+ */
+ protected void installKeyboardActions()
+ {
+ // We can't install the keyboard actions here, because then all
+ // popup menus would have their actions registered in the KeyboardManager.
+ // So we install it when the popup menu is opened, and uninstall it
+ // when it's closed. This is done in the KeyboardHelper class.
+ // Install InputMap.
+ }
+
+ /**
+ * Called by the KeyboardHandler when a popup is made visible.
+ */
+ void installKeyboardActionsImpl()
+ {
+ Object[] bindings;
+ if (popupMenu.getComponentOrientation().isLeftToRight())
+ {
+ bindings = (Object[])
+ SharedUIDefaults.get("PopupMenu.selectedWindowInputMapBindings");
+ }
+ else
+ {
+ bindings = (Object[]) SharedUIDefaults.get
+ ("PopupMenu.selectedWindowInputMapBindings.RightToLeft");
+ }
+ InputMap inputMap = LookAndFeel.makeComponentInputMap(popupMenu, bindings);
+ SwingUtilities.replaceUIInputMap(popupMenu,
+ JComponent.WHEN_IN_FOCUSED_WINDOW,
+ inputMap);
+
+ // Install ActionMap.
+ SwingUtilities.replaceUIActionMap(popupMenu, getActionMap());
+ }
+
+ /**
+ * Creates and returns the shared action map for JTrees.
+ *
+ * @return the shared action map for JTrees
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("PopupMenu.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("PopupMenu.actionMap", am);
+ }
+ return am;
+ }
+
+ /**
+ * Creates the default actions when there are none specified by the L&F.
+ *
+ * @return the default actions
+ */
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new NavigateAction("selectNext");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("selectPrevious");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("selectParent");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("selectChild");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("return");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("cancel");
+ am.put(action.getValue(Action.NAME), action);
+
+ return am;
+ }
+
+ /**
+ * Performs the opposite of installUI. Any properties or resources that need
+ * to be cleaned up will be done now. It will also uninstall any listeners
+ * it has. In addition, any properties of this UI will be nulled.
+ *
+ * @param c The {@link JComponent} that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallKeyboardActions();
+ popupMenu = null;
+
+ // Install KeyboardHelper when the first popup is initialized.
+ numPopups--;
+ if (numPopups == 0)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ msm.removeChangeListener(keyboardHelper);
+ }
+
+ }
+
+ /**
+ * This method uninstalls the defaults and sets any objects created during
+ * install to null
+ */
+ protected void uninstallDefaults()
+ {
+ popupMenu.setBackground(null);
+ popupMenu.setBorder(null);
+ popupMenu.setFont(null);
+ popupMenu.setForeground(null);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using.
+ */
+ protected void uninstallListeners()
+ {
+ popupMenu.removePopupMenuListener(popupMenuListener);
+ }
+
+ /**
+ * Uninstalls any keyboard actions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ // We can't install the keyboard actions here, because then all
+ // popup menus would have their actions registered in the KeyboardManager.
+ // So we install it when the popup menu is opened, and uninstall it
+ // when it's closed. This is done in the KeyboardHelper class.
+ // Install InputMap.
+ }
+
+ /**
+ * Called by the KeyboardHandler when a popup is made invisible.
+ */
+ void uninstallKeyboardActionsImpl()
+ {
+ SwingUtilities.replaceUIInputMap(popupMenu,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, null);
+ SwingUtilities.replaceUIActionMap(popupMenu, null);
+ }
+
+ /**
+ * This method returns the minimum size of the JPopupMenu.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * This method returns the preferred size of the JPopupMenu.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * This method returns the minimum size of the JPopupMenu.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * Return true if given mouse event is a platform popup trigger, and false
+ * otherwise
+ *
+ * @param e MouseEvent that is to be checked for popup trigger event
+ *
+ * @return true if given mouse event is a platform popup trigger, and false
+ * otherwise
+ */
+ public boolean isPopupTrigger(MouseEvent e)
+ {
+ return false;
+ }
+
+ /**
+ * This listener handles PopupMenuEvents fired by JPopupMenu
+ */
+ private class PopupMenuHandler implements PopupMenuListener
+ {
+ /**
+ * This method is invoked when JPopupMenu is cancelled.
+ *
+ * @param event the PopupMenuEvent
+ */
+ public void popupMenuCanceled(PopupMenuEvent event)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+
+ /**
+ * This method is invoked when JPopupMenu becomes invisible
+ *
+ * @param event the PopupMenuEvent
+ */
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent event)
+ {
+ // remove listener that listens to component events fired
+ // by the top - level window that this popup belongs to.
+ Component invoker = popupMenu.getInvoker();
+ Component rootContainer = SwingUtilities.getRoot(invoker);
+ if (rootContainer != null)
+ rootContainer.removeComponentListener(topWindowListener);
+ }
+
+ /**
+ * This method is invoked when JPopupMenu becomes visible
+ *
+ * @param event the PopupMenuEvent
+ */
+ public void popupMenuWillBecomeVisible(PopupMenuEvent event)
+ {
+ // Adds topWindowListener to top-level window to listener to
+ // ComponentEvents fired by it. We need to cancel this popup menu
+ // if topWindow to which this popup belongs was resized or moved.
+ Component invoker = popupMenu.getInvoker();
+ Component rootContainer = SwingUtilities.getRoot(invoker);
+ if (rootContainer != null)
+ rootContainer.addComponentListener(topWindowListener);
+
+ // if this popup menu is a free floating popup menu,
+ // then by default its first element should be always selected when
+ // this popup menu becomes visible.
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+
+ if (manager.getSelectedPath().length == 0)
+ {
+ // Set selected path to point to the first item in the popup menu
+ MenuElement[] path = new MenuElement[2];
+ path[0] = popupMenu;
+ Component[] comps = popupMenu.getComponents();
+ if (comps.length != 0 && comps[0] instanceof MenuElement)
+ {
+ path[1] = (MenuElement) comps[0];
+ manager.setSelectedPath(path);
+ }
+ }
+ }
+ }
+
+ /**
+ * ComponentListener that listens to Component Events fired by the top -
+ * level window to which popup menu belongs. If top-level window was
+ * resized, moved or hidded then popup menu will be hidded and selected
+ * path of current menu hierarchy will be set to null.
+ */
+ private class TopWindowListener implements ComponentListener
+ {
+ /**
+ * This method is invoked when top-level window is resized. This method
+ * closes current menu hierarchy.
+ *
+ * @param e The ComponentEvent
+ */
+ public void componentResized(ComponentEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+
+ /**
+ * This method is invoked when top-level window is moved. This method
+ * closes current menu hierarchy.
+ *
+ * @param e The ComponentEvent
+ */
+ public void componentMoved(ComponentEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+
+ /**
+ * This method is invoked when top-level window is shown This method
+ * does nothing by default.
+ *
+ * @param e The ComponentEvent
+ */
+ public void componentShown(ComponentEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+
+ /**
+ * This method is invoked when top-level window is hidden This method
+ * closes current menu hierarchy.
+ *
+ * @param e The ComponentEvent
+ */
+ public void componentHidden(ComponentEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java
new file mode 100644
index 000000000..bff6385ea
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java
@@ -0,0 +1,962 @@
+/* BasicProgressBarUI.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.geom.AffineTransform;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JComponent;
+import javax.swing.JProgressBar;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ProgressBarUI;
+
+/**
+ * The Basic Look and Feel UI delegate for the
+ * JProgressBar.
+ */
+public class BasicProgressBarUI extends ProgressBarUI
+{
+ /**
+ * A helper class that listens for ChangeEvents
+ * from the progressBar's model.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ChangeHandler implements ChangeListener
+ {
+ /**
+ * Called every time the state of the model changes.
+ *
+ * @param e The ChangeEvent given by the model.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Nothing to do but repaint.
+ progressBar.repaint();
+ }
+ }
+
+ /**
+ * This helper class is used to listen for
+ * PropertyChangeEvents from the progressBar.
+ */
+ private class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Called every time the properties of the
+ * progressBar change.
+ *
+ * @param e The PropertyChangeEvent given by the progressBar.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // Only need to listen for indeterminate changes.
+ // All other things are done on a repaint.
+ if (e.getPropertyName().equals("indeterminate"))
+ if (((Boolean) e.getNewValue()).booleanValue()
+ && progressBar.isShowing())
+ startAnimationTimer();
+ else
+ stopAnimationTimer();
+ }
+ }
+
+ /**
+ * Receives notification when the progressbar is becoming visible or
+ * invisible and starts/stops the animation timer accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class AncestorHandler implements AncestorListener
+ {
+
+ /**
+ * Receives notification when the progressbar is becoming visible. This
+ * starts the animation timer if the progressbar is indeterminate.
+ *
+ * @param event the ancestor event
+ */
+ public void ancestorAdded(AncestorEvent event)
+ {
+ if (progressBar.isIndeterminate())
+ startAnimationTimer();
+ }
+
+ /**
+ * Receives notification when the progressbar is becoming invisible. This
+ * stops the animation timer if the progressbar is indeterminate.
+ *
+ * @param event the ancestor event
+ */
+ public void ancestorRemoved(AncestorEvent event)
+ {
+ stopAnimationTimer();
+ }
+
+ /**
+ * Receives notification when an ancestor has been moved. We don't need to
+ * do anything here.
+ */
+ public void ancestorMoved(AncestorEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ }
+
+ /**
+ * This helper class is used to listen for
+ * the animationTimer's intervals. On every interval,
+ * the bouncing box should move.
+ */
+ private class Animator implements ActionListener
+ {
+ /**
+ * Called every time the animationTimer reaches
+ * its interval.
+ *
+ * @param e The ActionEvent given by the timer.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // Incrementing the animation index will cause
+ // a repaint.
+ incrementAnimationIndex();
+ }
+ }
+
+ /**
+ * Receives notification when the size of the progress bar changes and
+ * invalidates the layout information for the box calculation in
+ * {@link BasicProgressBarUI#getBox(Rectangle)}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class ComponentHandler extends ComponentAdapter
+ {
+ /**
+ * Receives notification when the size of the progress bar changes and
+ * invalidates the layout information for the box calculation in
+ * {@link BasicProgressBarUI#getBox}.
+ *
+ * @param e the component event
+ */
+ public void componentResized(ComponentEvent e)
+ {
+ boxDependent = -1;
+ boxIndependent = -1;
+ incr = -1;
+ }
+ }
+
+ /**
+ * Holds the value of the bouncing box that is returned by {@link #getBox}.
+ *
+ * @since 1.5
+ */
+ protected Rectangle boxRect;
+
+ /** The timer used to move the bouncing box. */
+ private transient Timer animationTimer;
+
+ // The total number of frames must be an even number.
+ // The total number of frames is calculated from
+ // the cycleTime and repaintInterval given by
+ // the basic Look and Feel defaults.
+ //
+ // +-----------------------------------------------+
+ // | frame0 | frame1 | frame2 | frame 3 | frame 4 |
+ // | | frame7 | frame6 | frame 5 | |
+ // +-----------------------------------------------+
+
+ /** The current animation index. */
+ private transient int animationIndex;
+
+ /** The total number of frames.*/
+ private transient int numFrames;
+
+ /** The helper that moves the bouncing box. */
+ private transient Animator animation;
+
+ /** The helper that listens for property change events. */
+ private transient PropertyChangeHandler propertyListener;
+
+ /** The Listener for the model. */
+ protected ChangeListener changeListener;
+
+ /** The progressBar for this UI. */
+ protected JProgressBar progressBar;
+
+
+ /**
+ * The size of the box returned by {@link #getBox} in the orientation
+ * direction of the progress bar. This is package private to avoid accessor
+ * method.
+ */
+ transient double boxDependent = - 1;
+
+ /**
+ * The size of the box returned by {@link #getBox} against the orientation
+ * direction of the progress bar. This is package private to avoid accessor
+ * method.
+ */
+ transient int boxIndependent = - 1;
+
+ /**
+ * The increment for box animation. This is package private to avoid accessor
+ * method.
+ */
+ transient double incr = -1;
+
+ /** The length of the cell. The cell is the painted part. */
+ private transient int cellLength;
+
+ /** The gap between cells. */
+ private transient int cellSpacing;
+
+ /** The color of the text when the bar is not over it.*/
+ private transient Color selectionBackground;
+
+ /** The color of the text when the bar is over it. */
+ private transient Color selectionForeground;
+
+ /**
+ * Listens for notification when the component becomes showing and
+ * starts/stops the animation timer.
+ */
+ private AncestorListener ancestorListener;
+
+ /**
+ * Listens for resize events on the progress bar and invalidates some
+ * layout info.
+ */
+ private ComponentListener componentListener;
+
+ /**
+ * Creates a new BasicProgressBarUI object.
+ */
+ public BasicProgressBarUI()
+ {
+ super();
+ }
+
+ /**
+ * Creates a new BasicProgressBarUI for the component.
+ *
+ * @param x The JComponent to create the UI for.
+ *
+ * @return A new BasicProgressBarUI.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicProgressBarUI();
+ }
+
+ /**
+ * This method returns the length of the bar (from the minimum)
+ * in pixels (or units that the Graphics object draws in) based
+ * on the progressBar's getPercentComplete() value.
+ *
+ * @param b The insets of the progressBar.
+ * @param width The width of the progressBar.
+ * @param height The height of the progressBar.
+ *
+ * @return The length of the bar that should be painted in pixels.
+ */
+ protected int getAmountFull(Insets b, int width, int height)
+ {
+ double percentDone = progressBar.getPercentComplete();
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ return (int) (percentDone * (width - b.left - b.right));
+ else
+ return (int) (percentDone * (height - b.top - b.bottom));
+ }
+
+ /**
+ * The current animation index.
+ *
+ * @return The current animation index.
+ */
+ protected int getAnimationIndex()
+ {
+ return animationIndex;
+ }
+
+ /**
+ * This method returns the size and position of the bouncing box
+ * for the current animation index. It stores the values in the
+ * given rectangle and returns it. It returns null if no box should
+ * be drawn.
+ *
+ * @param r The bouncing box rectangle.
+ *
+ * @return The bouncing box rectangle.
+ */
+ protected Rectangle getBox(Rectangle r)
+ {
+ if (!progressBar.isIndeterminate())
+ return null;
+ if (r == null)
+ r = new Rectangle();
+
+ Rectangle vr = new Rectangle();
+ SwingUtilities.calculateInnerArea(progressBar, vr);
+
+ // Recalculate the metrics only when size of the progressbar has changed.
+ if (incr == -1 || boxDependent == -1 || boxIndependent == -1)
+ {
+ //numFrames has to be an even number as defined by spec.
+ int iterations = numFrames / 2;
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ {
+ boxDependent = vr.width / 6.;
+ incr = ((double) (vr.width - boxDependent)) / (double) iterations;
+ boxIndependent = vr.height;
+ }
+ else
+ {
+ boxDependent = vr.height / 6.;
+ incr = ((double) (vr.height - boxDependent)) / (double) iterations;
+ boxIndependent = vr.width;
+ }
+ }
+
+ int index = getAnimationIndex();
+ if (animationIndex > numFrames / 2)
+ index = numFrames - getAnimationIndex();
+
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ {
+ r.x = vr.x + (int) (incr * index);
+ r.y = vr.y;
+ r.width = (int) boxDependent;
+ r.height = (int) boxIndependent;
+ }
+ else
+ {
+ r.x = vr.x;
+ r.y = vr.height - (int) (incr * index) + vr.y - (int) boxDependent;
+ r.width = (int) boxIndependent;
+ r.height = (int) boxDependent;
+ }
+ return r;
+ }
+
+ /**
+ * This method returns the length of the cells.
+ *
+ * @return The cell length.
+ */
+ protected int getCellLength()
+ {
+ return cellLength;
+ }
+
+ /**
+ * This method returns the spacing between cells.
+ *
+ * @return The cell gap.
+ */
+ protected int getCellSpacing()
+ {
+ return cellSpacing;
+ }
+
+ /**
+ * This method returns the maximum size of the JComponent.
+ * If it returns null, it is up to the LayoutManager
+ * to give it a size.
+ *
+ * @param c The component to find a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Insets insets = c.getInsets();
+ Dimension ret;
+ int orientation = progressBar.getOrientation();
+ if (orientation == JProgressBar.VERTICAL)
+ {
+ ret = getPreferredInnerVertical();
+ ret.height = Short.MAX_VALUE;
+ ret.width += insets.left + insets.right;
+ }
+ else
+ {
+ ret = getPreferredInnerHorizontal();
+ ret.width = Short.MAX_VALUE;
+ ret.height += insets.top + insets.bottom;
+ }
+ return ret;
+ }
+
+ /**
+ * This method returns the minimum size of the JComponent.
+ * If it returns null, it is up to the LayoutManager to
+ * give it a size.
+ *
+ * @param c The component to find a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Insets insets = c.getInsets();
+ Dimension ret;
+ int orientation = progressBar.getOrientation();
+ if (orientation == JProgressBar.VERTICAL)
+ {
+ ret = getPreferredInnerVertical();
+ ret.height = 10;
+ ret.width += insets.left + insets.right;
+ }
+ else
+ {
+ ret = getPreferredInnerHorizontal();
+ ret.width = 10;
+ ret.height += insets.top + insets.bottom;
+ }
+ return ret;
+ }
+
+ /**
+ * This method returns the preferred size of the inner
+ * rectangle (the bounds without the insets) if the
+ * progressBar is horizontal.
+ *
+ * @return The preferred size of the progressBar minus
+ * insets if it's horizontal.
+ */
+ protected Dimension getPreferredInnerHorizontal()
+ {
+ Font font = progressBar.getFont();
+ FontMetrics fm = progressBar.getFontMetrics(font);
+
+ int stringWidth = 0;
+ String str = progressBar.getString();
+ if (str != null)
+ stringWidth = fm.stringWidth(progressBar.getString());
+ Insets i = progressBar.getInsets();
+ int prefWidth = Math.max(200 - i.left - i.right, stringWidth);
+
+ int stringHeight = 0;
+ if (str != null)
+ stringHeight = fm.getHeight();
+ int prefHeight = Math.max(16 - i.top - i.bottom, stringHeight);
+
+ return new Dimension(prefWidth, prefHeight);
+ }
+
+ /**
+ * This method returns the preferred size of the inner
+ * rectangle (the bounds without insets) if the
+ * progressBar is vertical.
+ *
+ * @return The preferred size of the progressBar minus
+ * insets if it's vertical.
+ */
+ protected Dimension getPreferredInnerVertical()
+ {
+ Font font = progressBar.getFont();
+ FontMetrics fm = progressBar.getFontMetrics(font);
+
+ int stringWidth = 0;
+ String str = progressBar.getString();
+ if (str != null)
+ stringWidth = fm.stringWidth(progressBar.getString());
+ Insets i = progressBar.getInsets();
+ int prefHeight = Math.max(200 - i.left - i.right, stringWidth);
+
+ int stringHeight = 0;
+ if (str != null)
+ stringHeight = fm.getHeight();
+ int prefWidth = Math.max(16 - i.top - i.bottom, stringHeight);
+
+ return new Dimension(prefWidth, prefHeight);
+ }
+
+ /**
+ * This method returns the preferred size of the
+ * given JComponent. If it returns null, then it
+ * is up to the LayoutManager to give it a size.
+ *
+ * @param c The component to find the preferred size for.
+ *
+ * @return The preferred size of the component.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Insets insets = c.getInsets();
+ Dimension ret;
+ int orientation = progressBar.getOrientation();
+ if (orientation == JProgressBar.VERTICAL)
+ ret = getPreferredInnerVertical();
+ else
+ ret = getPreferredInnerHorizontal();
+ ret.width += insets.left + insets.right;
+ ret.height += insets.top + insets.bottom;
+ return ret;
+ }
+
+ /**
+ * This method returns the Color that the text is shown in when the bar is
+ * not over the text.
+ *
+ * @return The color of the text when the bar is not over it.
+ */
+ protected Color getSelectionBackground()
+ {
+ return selectionBackground;
+ }
+
+ /**
+ * This method returns the Color that the text is shown in when the bar is
+ * over the text.
+ *
+ * @return The color of the text when the bar is over it.
+ */
+ protected Color getSelectionForeground()
+ {
+ return selectionForeground;
+ }
+
+ /**
+ * This method returns the point (the top left of the bounding box)
+ * where the text should be painted.
+ *
+ * @param g The Graphics object to measure FontMetrics with.
+ * @param progressString The string to paint.
+ * @param x The x coordinate of the overall bounds box.
+ * @param y The y coordinate of the overall bounds box.
+ * @param width The width of the overall bounds box.
+ * @param height The height of the overall bounds box.
+ *
+ * @return The top left of the bounding box where text should be painted.
+ */
+ protected Point getStringPlacement(Graphics g, String progressString, int x,
+ int y, int width, int height)
+ {
+ Rectangle tr = new Rectangle();
+ Rectangle vr = new Rectangle();
+ Rectangle ir = new Rectangle();
+
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ vr.setBounds(x, y, width, height);
+ else
+ vr.setBounds(y, x, height, width);
+
+ Font f = g.getFont();
+ FontMetrics fm = g.getFontMetrics(f);
+
+ SwingUtilities.layoutCompoundLabel(progressBar, fm, progressString, null,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER, vr, ir, tr, 0);
+
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ return new Point(tr.x, tr.y);
+ else
+ return new Point(tr.y, tr.x);
+ }
+
+ /**
+ * This method increments the animation index.
+ */
+ protected void incrementAnimationIndex()
+ {
+ animationIndex++;
+ //numFrames is like string length, it should be named numFrames or something
+ if (animationIndex >= numFrames)
+ animationIndex = 0;
+ progressBar.repaint();
+ }
+
+ /**
+ * This method paints the progressBar. It delegates its responsibilities
+ * to paintDeterminate and paintIndeterminate.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ if (! progressBar.isIndeterminate())
+ paintDeterminate(g, c);
+ else
+ paintIndeterminate(g, c);
+ }
+
+ /**
+ * This method is called if the painting to be done is
+ * for a determinate progressBar.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ protected void paintDeterminate(Graphics g, JComponent c)
+ {
+ Color saved = g.getColor();
+ int space = getCellSpacing();
+ int len = getCellLength();
+ int max = progressBar.getMaximum();
+ int min = progressBar.getMinimum();
+ int value = progressBar.getValue();
+
+ Rectangle vr = SwingUtilities.calculateInnerArea(c, new Rectangle());
+ Rectangle or = progressBar.getBounds();
+ Insets insets = c.getInsets();
+
+ int amountFull = getAmountFull(insets, or.width, or.height);
+
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ {
+ g.setColor(c.getForeground());
+ g.fillRect(vr.x, vr.y, amountFull, vr.height);
+ }
+ else
+ {
+ g.setColor(c.getForeground());
+ g.fillRect(vr.x, vr.y + vr.height - amountFull, vr.width,
+ amountFull);
+ }
+
+ if (progressBar.isStringPainted() && !progressBar.getString().equals(""))
+ paintString(g, 0, 0, or.width, or.height, amountFull, insets);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method is called if the painting to be done is for
+ * an indeterminate progressBar.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ protected void paintIndeterminate(Graphics g, JComponent c)
+ {
+ //need to paint the box at it's current position. no text is painted since
+ //all we're doing is bouncing back and forth
+ Color saved = g.getColor();
+ Insets insets = c.getInsets();
+
+ Rectangle or = c.getBounds();
+ Rectangle vr = new Rectangle();
+ SwingUtilities.calculateInnerArea(c, vr);
+
+ g.setColor(c.getBackground());
+ g.fillRect(vr.x, vr.y, vr.width, vr.height);
+
+ boxRect = getBox(boxRect);
+
+ g.setColor(c.getForeground());
+ g.fillRect(boxRect.x, boxRect.y, boxRect.width, boxRect.height);
+
+ if (progressBar.isStringPainted() && !progressBar.getString().equals(""))
+ paintString(g, 0, 0, or.width, or.height,
+ getAmountFull(insets, or.width, or.height), insets);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the string for the progressBar.
+ *
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate of the progressBar.
+ * @param y The y coordinate of the progressBar.
+ * @param width The width of the progressBar.
+ * @param height The height of the progressBar.
+ * @param amountFull The amount of the progressBar that has its bar filled.
+ * @param b The insets of the progressBar.
+ */
+ protected void paintString(Graphics g, int x, int y, int width, int height,
+ int amountFull, Insets b)
+ {
+ String str = progressBar.getString();
+ int full = getAmountFull(b, width, height);
+ Point placement = getStringPlacement(g, progressBar.getString(),
+ x + b.left, y + b.top,
+ width - b.left - b.right,
+ height - b.top - b.bottom);
+ Color savedColor = g.getColor();
+ Shape savedClip = g.getClip();
+ FontMetrics fm = g.getFontMetrics(progressBar.getFont());
+
+ if (progressBar.getOrientation() == JProgressBar.VERTICAL)
+ {
+ AffineTransform rotate = AffineTransform.getRotateInstance(Math.PI / 2);
+ g.setFont(progressBar.getFont().deriveFont(rotate));
+ placement.x = width - placement.x - fm.getAscent();
+ }
+ else
+ {
+ placement.y += fm.getAscent();
+ }
+
+ g.setColor(getSelectionForeground());
+ g.setClip(0, 0, full + b.left, height);
+ g.drawString(str, placement.x, placement.y);
+ g.setColor(getSelectionBackground());
+ g.setClip(full + b.left, 0, width - full, height);
+ g.drawString(str, placement.x, placement.y);
+ g.setClip(savedClip);
+ g.setColor(savedColor);
+ }
+
+ /**
+ * This method sets the current animation index. If the index is greater than
+ * the number of frames, it resets to 0.
+ *
+ * @param newValue The new animation index.
+ */
+ protected void setAnimationIndex(int newValue)
+ {
+ animationIndex = (newValue <= numFrames) ? newValue : 0;
+ progressBar.repaint();
+ }
+
+ /**
+ * This method sets the cell length.
+ *
+ * @param cellLen The cell length.
+ */
+ protected void setCellLength(int cellLen)
+ {
+ cellLength = cellLen;
+ }
+
+ /**
+ * This method sets the cell spacing.
+ *
+ * @param cellSpace The cell spacing.
+ */
+ protected void setCellSpacing(int cellSpace)
+ {
+ cellSpacing = cellSpace;
+ }
+
+ /**
+ * This method starts the animation timer. It is called
+ * when the propertyChangeListener detects that the progressBar
+ * has changed to indeterminate mode.
+ *
+ * @since 1.4
+ */
+ protected void startAnimationTimer()
+ {
+ if (animationTimer != null)
+ animationTimer.start();
+ }
+
+ /**
+ * This method stops the animation timer. It is called when
+ * the propertyChangeListener detects that the progressBar
+ * has changed to determinate mode.
+ *
+ * @since 1.4
+ */
+ protected void stopAnimationTimer()
+ {
+ if (animationTimer != null)
+ animationTimer.stop();
+ setAnimationIndex(0);
+ }
+
+ /**
+ * This method changes the settings for the progressBar to
+ * the defaults provided by the current Look and Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(progressBar, "ProgressBar.background",
+ "ProgressBar.foreground",
+ "ProgressBar.font");
+ LookAndFeel.installBorder(progressBar, "ProgressBar.border");
+ progressBar.setOpaque(true);
+
+ selectionForeground = UIManager.getColor("ProgressBar.selectionForeground");
+ selectionBackground = UIManager.getColor("ProgressBar.selectionBackground");
+ cellLength = UIManager.getInt("ProgressBar.cellLength");
+ cellSpacing = UIManager.getInt("ProgressBar.cellSpacing");
+
+ int repaintInterval = UIManager.getInt("ProgressBar.repaintInterval");
+ int cycleTime = UIManager.getInt("ProgressBar.cycleTime");
+
+ if (cycleTime % repaintInterval != 0
+ && (cycleTime / repaintInterval) % 2 != 0)
+ {
+ int div = (cycleTime / repaintInterval) + 2;
+ div /= 2;
+ div *= 2;
+ cycleTime = div * repaintInterval;
+ }
+ setAnimationIndex(0);
+ numFrames = cycleTime / repaintInterval;
+ animationTimer.setDelay(repaintInterval);
+ }
+
+ /**
+ * The method uninstalls any defaults that were
+ * set by the current Look and Feel.
+ */
+ protected void uninstallDefaults()
+ {
+ progressBar.setFont(null);
+ progressBar.setForeground(null);
+ progressBar.setBackground(null);
+
+ selectionForeground = null;
+ selectionBackground = null;
+ }
+
+ /**
+ * This method registers listeners to all the
+ * components that this UI delegate needs to listen to.
+ */
+ protected void installListeners()
+ {
+ changeListener = new ChangeHandler();
+ propertyListener = new PropertyChangeHandler();
+ animation = new Animator();
+
+ progressBar.addChangeListener(changeListener);
+ progressBar.addPropertyChangeListener(propertyListener);
+ animationTimer.addActionListener(animation);
+
+ ancestorListener = new AncestorHandler();
+ progressBar.addAncestorListener(ancestorListener);
+
+ componentListener = new ComponentHandler();
+ progressBar.addComponentListener(componentListener);
+ }
+
+ /**
+ * This method unregisters listeners to all the
+ * components that were listened to.
+ */
+ protected void uninstallListeners()
+ {
+ progressBar.removeChangeListener(changeListener);
+ progressBar.removePropertyChangeListener(propertyListener);
+ animationTimer.removeActionListener(animation);
+
+ changeListener = null;
+ propertyListener = null;
+ animation = null;
+
+ if (ancestorListener != null)
+ progressBar.removeAncestorListener(ancestorListener);
+ ancestorListener = null;
+
+ if (componentListener != null)
+ progressBar.removeComponentListener(componentListener);
+ componentListener = null;
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ * This includes setting up defaults and listeners as
+ * well as initializing any values or objects that
+ * the UI may need.
+ *
+ * @param c The JComponent that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JProgressBar)
+ {
+ progressBar = (JProgressBar) c;
+
+ animationTimer = new Timer(200, null);
+ animationTimer.setRepeats(true);
+
+ installDefaults();
+ installListeners();
+ }
+ if (progressBar.isIndeterminate())
+ startAnimationTimer();
+ }
+
+ /**
+ * This method removes the UI for the given JComponent.
+ * This includes removing any listeners or defaults
+ * that the installUI may have set up.
+ *
+ * @param c The JComponent that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ uninstallListeners();
+ uninstallDefaults();
+
+ animationTimer = null;
+ progressBar = null;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java
new file mode 100644
index 000000000..f8f62e156
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java
@@ -0,0 +1,101 @@
+/* BasicRadioButtonMenuItemUI.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.event.MouseEvent;
+
+import javax.swing.JComponent;
+import javax.swing.JMenuItem;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * UI Delegator for JRadioButtonMenuItem
+ */
+public class BasicRadioButtonMenuItemUI extends BasicMenuItemUI
+{
+ /**
+ * Creates a new BasicRadioButtonMenuItemUI object.
+ */
+ public BasicRadioButtonMenuItemUI()
+ {
+ super();
+ }
+
+ /**
+ * Factory method to create a BasicRadioButtonMenuItemUI for the given {@link
+ * JComponent}, which should be a JRadioButtonMenuItem.
+ *
+ * @param b The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicRadioButtonMenuItemUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent b)
+ {
+ return new BasicRadioButtonMenuItemUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "RadioButtonMenuItem"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "RadioButtonMenuItem";
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param item DOCUMENT ME!
+ * @param e DOCUMENT ME!
+ * @param path DOCUMENT ME!
+ * @param manager DOCUMENT ME!
+ */
+ public void processMouseEvent(JMenuItem item, MouseEvent e,
+ MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // TODO: May not be implemented properly.
+ item.processMouseEvent(e, path, manager);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java
new file mode 100644
index 000000000..ff374d1ab
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java
@@ -0,0 +1,301 @@
+/* BasicRadioButtonUI.java
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.View;
+
+/**
+ * The BasicLookAndFeel UI implementation for
+ * {@link javax.swing.JRadioButton}s.
+ */
+public class BasicRadioButtonUI extends BasicToggleButtonUI
+{
+ /**
+ * The default icon for JRadioButtons. The default icon displays the usual
+ * RadioButton and is sensible to the selection state of the button,
+ * and can be used both as normal icon as well as selectedIcon.
+ */
+ protected Icon icon;
+
+ /**
+ * Creates and returns a new instance of <code>BasicRadioButtonUI</code>.
+ *
+ * @return a new instance of <code>BasicRadioButtonUI</code>
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ return new BasicRadioButtonUI();
+ }
+
+ /**
+ * Creates a new instance of <code>BasicButtonUI</code>.
+ */
+ public BasicRadioButtonUI()
+ {
+ // nothing to do
+ }
+
+ /**
+ * Installs defaults from the Look &amp; Feel table on the specified
+ * button.
+ *
+ * @param b the button on which to install the defaults
+ */
+ protected void installDefaults(AbstractButton b)
+ {
+ super.installDefaults(b);
+ icon = UIManager.getIcon(getPropertyPrefix() + "icon");
+ }
+
+ /**
+ * Returns the prefix used for UIDefaults properties. This is
+ * <code>RadioButton</code> in this case.
+ *
+ * @return the prefix used for UIDefaults properties
+ */
+ protected String getPropertyPrefix()
+ {
+ return "RadioButton.";
+ }
+
+ /**
+ * Returns the default icon for JRadioButtons.
+ * The default icon displays the usual
+ * RadioButton and is sensible to the selection state of the button,
+ * and can be used both as normal icon as well as selectedIcon.
+ *
+ * @return the default icon for JRadioButtons
+ */
+ public Icon getDefaultIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * Paints the RadioButton.
+ *
+ * @param g the Graphics context to paint with
+ * @param c the button to paint
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Dimension size = c.getSize();
+ Insets i = b.getInsets();
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+ viewR.x = i.left;
+ viewR.y = i.right;
+ viewR.width = size.width - i.left - i.right;
+ viewR.height = size.height - i.top - i.bottom;
+
+ Font f = c.getFont();
+
+ g.setFont(f);
+
+ // This is the icon that we use for layout.
+ Icon icon = b.getIcon();
+ if (icon == null)
+ icon = getDefaultIcon();
+
+ // Figure out the correct icon.
+ Icon currentIcon = getCurrentIcon(b);
+
+ // Do the layout.
+ String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
+ b.getText(), currentIcon == null ? getDefaultIcon() : currentIcon,
+ b.getVerticalAlignment(), b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
+ viewR, iconR, textR, b.getIconTextGap());
+
+ // .. and paint it.
+ if (currentIcon != null)
+ currentIcon.paintIcon(c, g, iconR.x, iconR.y);
+
+ // Paint text and focus indicator.
+ if (text != null)
+ {
+ // Maybe render HTML in the radio button.
+ View v = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (v != null)
+ v.paint(g, textR);
+ else
+ paintText(g, b, textR, text);
+
+ // Paint focus indicator if necessary.
+ if (b.hasFocus() && b.isFocusPainted()
+ && textR.width > 0 && textR.height > 0)
+ paintFocus(g, textR, size);
+ }
+ }
+
+ /**
+ * Determines the icon to be displayed for the specified radio button.
+ *
+ * @param b the radio button
+ *
+ * @return the icon
+ */
+ private Icon getCurrentIcon(AbstractButton b)
+ {
+ ButtonModel m = b.getModel();
+ Icon currentIcon = b.getIcon();
+
+ if (currentIcon == null)
+ {
+ currentIcon = getDefaultIcon();
+ }
+ else
+ {
+ if (! m.isEnabled())
+ {
+ if (m.isSelected())
+ currentIcon = b.getDisabledSelectedIcon();
+ else
+ currentIcon = b.getDisabledIcon();
+ }
+ else if (m.isPressed() && m.isArmed())
+ {
+ currentIcon = b.getPressedIcon();
+ if (currentIcon == null)
+ currentIcon = b.getSelectedIcon();
+ }
+ else if (m.isSelected())
+ {
+ if (b.isRolloverEnabled() && m.isRollover())
+ {
+ currentIcon = b.getRolloverSelectedIcon();
+ if (currentIcon == null)
+ currentIcon = b.getSelectedIcon();
+ }
+ else
+ currentIcon = b.getSelectedIcon();
+ }
+ else if (b.isRolloverEnabled() && m.isRollover())
+ {
+ currentIcon = b.getRolloverIcon();
+ }
+ if (currentIcon == null)
+ currentIcon = b.getIcon();
+ }
+ return currentIcon;
+ }
+
+ public Dimension getPreferredSize(JComponent c)
+ {
+ // This is basically the same code as in
+ // BasicGraphicsUtils.getPreferredButtonSize() but takes the default icon
+ // property into account. JRadioButton and subclasses always have an icon:
+ // the check box. If the user explicitly changes it with setIcon() that
+ // one will be used for layout calculations and painting instead.
+ // The other icon properties are ignored.
+ AbstractButton b = (AbstractButton) c;
+
+ Insets insets = b.getInsets();
+
+ String text = b.getText();
+ Icon i = b.getIcon();
+ if (i == null)
+ i = getDefaultIcon();
+
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+ viewR.x = 0;
+ viewR.y = 0;
+ viewR.width = Short.MAX_VALUE;
+ viewR.height = Short.MAX_VALUE;
+
+ SwingUtilities.layoutCompoundLabel(b, // for the component orientation
+ b.getFontMetrics(b.getFont()),
+ text, i, b.getVerticalAlignment(),
+ b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(),
+ viewR, iconR, textR,
+ text == null ? 0 : b.getIconTextGap());
+
+ Rectangle r = SwingUtilities.computeUnion(textR.x, textR.y, textR.width,
+ textR.height, iconR);
+
+ return new Dimension(insets.left + r.width + insets.right,
+ insets.top + r.height + insets.bottom);
+ }
+
+ /**
+ * Paints the focus indicator for JRadioButtons.
+ *
+ * @param g the graphics context
+ * @param tr the rectangle for the text label
+ * @param size the size of the <code>JRadioButton</code> component.
+ */
+ protected void paintFocus(Graphics g, Rectangle tr, Dimension size)
+ {
+ Color focusColor = UIManager.getColor(getPropertyPrefix() + ".focus");
+ Color saved = g.getColor();
+ g.setColor(focusColor);
+ g.drawRect(tr.x, tr.y, tr.width, tr.height);
+ g.setColor(saved);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java
new file mode 100644
index 000000000..26c7a532d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java
@@ -0,0 +1,292 @@
+/* BasicRootPaneUI.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.event.ActionEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ButtonModel;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JRootPane;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentInputMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.RootPaneUI;
+
+public class BasicRootPaneUI extends RootPaneUI
+ implements PropertyChangeListener
+{
+
+ /**
+ * Performed when the user activates the default button inside the JRootPane,
+ * usually by pressing 'ENTER'.
+ */
+ private class DefaultPressAction
+ extends AbstractAction
+ {
+ /**
+ * The JRootPane for which this action should be installed.
+ */
+ private JRootPane rootPane;
+
+ /**
+ * Creates a new DefaultPressAction for the specified JRootPane.
+ */
+ DefaultPressAction(JRootPane rp)
+ {
+ rootPane = rp;
+ }
+
+ /**
+ * Performes the action.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ JButton b = rootPane.getDefaultButton();
+ if (b != null)
+ {
+ ButtonModel m = b.getModel();
+ m.setArmed(true);
+ m.setPressed(true);
+ }
+ }
+ }
+
+ /**
+ * Performed when the user activates the default button inside the JRootPane,
+ * usually by releasing 'ENTER'.
+ */
+ private class DefaultReleaseAction
+ extends AbstractAction
+ {
+ /**
+ * The JRootPane for which this action should be installed.
+ */
+ private JRootPane rootPane;
+
+ /**
+ * Creates a new DefaultReleaseAction for the specified JRootPane.
+ */
+ DefaultReleaseAction(JRootPane rp)
+ {
+ rootPane = rp;
+ }
+
+ /**
+ * Performes the action.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ JButton b = rootPane.getDefaultButton();
+ if (b != null)
+ {
+ ButtonModel m = b.getModel();
+ m.setPressed(false);
+ m.setArmed(false);
+ }
+ }
+ }
+
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicRootPaneUI();
+ }
+
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JRootPane)
+ {
+ JRootPane rp = (JRootPane) c;
+ installDefaults(rp);
+ installComponents(rp);
+ installListeners(rp);
+ installKeyboardActions(rp);
+ }
+ }
+
+ /**
+ * Installs the look and feel defaults for JRootPane.
+ *
+ * @param rp the root pane to install the defaults to
+ */
+ protected void installDefaults(JRootPane rp)
+ {
+ // TODO: What to do here, if anything? (might be a hook method)
+ }
+
+ /**
+ * Installs additional look and feel components to the root pane.
+ *
+ * @param rp the root pane to install the components to
+ */
+ protected void installComponents(JRootPane rp)
+ {
+ // All components are initialized in the JRootPane constructor, and since
+ // the createXXXPane methods are protected, I see no reasonable way,
+ // and no need to initialize them here. This method is here anyway
+ // for compatibility and to provide the necessary hooks to subclasses.
+ }
+
+ /**
+ * Installs any look and feel specific listeners on the root pane.
+ *
+ * @param rp the root pane to install the listeners to
+ */
+ protected void installListeners(JRootPane rp)
+ {
+ rp.addPropertyChangeListener(this);
+ }
+
+ /**
+ * Installs look and feel keyboard actions on the root pane.
+ *
+ * @param rp the root pane to install the keyboard actions to
+ */
+ protected void installKeyboardActions(JRootPane rp)
+ {
+ // Install the keyboard actions.
+ ActionMapUIResource am = new ActionMapUIResource();
+ am.put("press", new DefaultPressAction(rp));
+ am.put("release", new DefaultReleaseAction(rp));
+ SwingUtilities.replaceUIActionMap(rp, am);
+
+ // Install the input map from the UIManager. It seems like the actual
+ // bindings are installed in the JRootPane only when the defaultButton
+ // property receives a value. So we also only install an empty
+ // input map here, and fill it in propertyChange.
+ ComponentInputMapUIResource im = new ComponentInputMapUIResource(rp);
+ SwingUtilities.replaceUIInputMap(rp, JComponent.WHEN_IN_FOCUSED_WINDOW,
+ im);
+ }
+
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ JRootPane source = (JRootPane) event.getSource();
+ String propertyName = event.getPropertyName();
+ if (propertyName.equals("defaultButton"))
+ {
+ Object newValue = event.getNewValue();
+ InputMap im =
+ SwingUtilities.getUIInputMap(source,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (newValue != null)
+ {
+ Object[] keybindings = (Object[]) UIManager.get(
+ "RootPane.defaultButtonWindowKeyBindings");
+ LookAndFeel.loadKeyBindings(im, keybindings);
+ }
+ else
+ {
+ im.clear();
+ }
+ }
+ }
+
+ /**
+ * Uninstalls this UI from the root pane. This calls
+ * {@link #uninstallDefaults}, {@link #uninstallComponents},
+ * {@link #uninstallListeners}, {@link #uninstallKeyboardActions}
+ * in this order.
+ *
+ * @param c the root pane to uninstall the UI from
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ if (c instanceof JRootPane)
+ {
+ JRootPane rp = (JRootPane) c;
+ uninstallDefaults(rp);
+ uninstallComponents(rp);
+ uninstallListeners(rp);
+ uninstallKeyboardActions(rp);
+ }
+ }
+
+ /**
+ * Uninstalls the look and feel defaults that have been installed in
+ * {@link #installDefaults}.
+ *
+ * @param rp the root pane to uninstall the defaults from
+ */
+ protected void uninstallDefaults(JRootPane rp)
+ {
+ // We do nothing here.
+ }
+
+ /**
+ * Uninstalls look and feel components from the root pane.
+ *
+ * @param rp the root pane to uninstall the components from
+ */
+ protected void uninstallComponents(JRootPane rp)
+ {
+ // We do nothing here.
+ }
+
+ /**
+ * Uninstalls any look and feel specific listeners from the root pane.
+ *
+ * @param rp the root pane to uninstall the listeners from
+ */
+ protected void uninstallListeners(JRootPane rp)
+ {
+ rp.removePropertyChangeListener(this);
+ }
+
+ /**
+ * Uninstalls look and feel keyboard actions from the root pane.
+ *
+ * @param rp the root pane to uninstall the keyboard actions from
+ */
+ protected void uninstallKeyboardActions(JRootPane rp)
+ {
+ SwingUtilities.replaceUIActionMap(rp, null);
+ SwingUtilities.replaceUIInputMap(rp, JComponent.WHEN_IN_FOCUSED_WINDOW,
+ null);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java
new file mode 100644
index 000000000..9f24f8aca
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java
@@ -0,0 +1,1513 @@
+/* BasicScrollBarUI.java --
+ Copyright (C) 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.BoundedRangeModel;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JScrollBar;
+import javax.swing.JSlider;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ScrollBarUI;
+
+/**
+ * The Basic Look and Feel UI delegate for JScrollBar.
+ */
+public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
+ SwingConstants
+{
+ /**
+ * A helper class that listens to the two JButtons on each end of the
+ * JScrollBar.
+ */
+ protected class ArrowButtonListener extends MouseAdapter
+ {
+
+ /**
+ * Move the thumb in the direction specified by the button's arrow. If
+ * this button is held down, then it should keep moving the thumb.
+ *
+ * @param e The MouseEvent fired by the JButton.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ scrollTimer.stop();
+ scrollListener.setScrollByBlock(false);
+ if (e.getSource() == incrButton)
+ scrollListener.setDirection(POSITIVE_SCROLL);
+ else if (e.getSource() == decrButton)
+ scrollListener.setDirection(NEGATIVE_SCROLL);
+ scrollTimer.setDelay(100);
+ scrollTimer.start();
+ }
+
+ /**
+ * Stops the thumb when the JButton is released.
+ *
+ * @param e The MouseEvent fired by the JButton.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ scrollTimer.stop();
+ scrollTimer.setDelay(300);
+ if (e.getSource() == incrButton)
+ scrollByUnit(POSITIVE_SCROLL);
+ else if (e.getSource() == decrButton)
+ scrollByUnit(NEGATIVE_SCROLL);
+ }
+ }
+
+ /**
+ * A helper class that listens to the ScrollBar's model for ChangeEvents.
+ */
+ protected class ModelListener implements ChangeListener
+ {
+ /**
+ * Called when the model changes.
+ *
+ * @param e The ChangeEvent fired by the model.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ calculatePreferredSize();
+ updateThumbRect();
+ scrollbar.repaint();
+ }
+ }
+
+ /**
+ * A helper class that listens to the ScrollBar's properties.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Called when one of the ScrollBar's properties change.
+ *
+ * @param e The PropertyChangeEvent fired by the ScrollBar.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("model"))
+ {
+ ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener);
+ scrollbar.getModel().addChangeListener(modelListener);
+ updateThumbRect();
+ }
+ else if (e.getPropertyName().equals("orientation"))
+ {
+ uninstallListeners();
+ uninstallComponents();
+ uninstallDefaults();
+ installDefaults();
+ installComponents();
+ installListeners();
+ }
+ else if (e.getPropertyName().equals("enabled"))
+ {
+ Boolean b = (Boolean) e.getNewValue();
+ if (incrButton != null)
+ incrButton.setEnabled(b.booleanValue());
+ if (decrButton != null)
+ decrButton.setEnabled(b.booleanValue());
+ }
+ }
+ }
+
+ /**
+ * A helper class that listens for events from the timer that is used to
+ * move the thumb.
+ */
+ protected class ScrollListener implements ActionListener
+ {
+ /** The direction the thumb moves in. */
+ private transient int direction;
+
+ /** Whether movement will be in blocks. */
+ private transient boolean block;
+
+ /**
+ * Creates a new ScrollListener object. The default is scrolling
+ * positively with block movement.
+ */
+ public ScrollListener()
+ {
+ direction = POSITIVE_SCROLL;
+ block = true;
+ }
+
+ /**
+ * Creates a new ScrollListener object using the given direction and
+ * block.
+ *
+ * @param dir The direction to move in.
+ * @param block Whether movement will be in blocks.
+ */
+ public ScrollListener(int dir, boolean block)
+ {
+ direction = dir;
+ this.block = block;
+ }
+
+ /**
+ * Sets the direction to scroll in.
+ *
+ * @param direction The direction to scroll in.
+ */
+ public void setDirection(int direction)
+ {
+ this.direction = direction;
+ }
+
+ /**
+ * Sets whether scrolling will be done in blocks.
+ *
+ * @param block Whether scrolling will be in blocks.
+ */
+ public void setScrollByBlock(boolean block)
+ {
+ this.block = block;
+ }
+
+ /**
+ * Called every time the timer reaches its interval.
+ *
+ * @param e The ActionEvent fired by the timer.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (block)
+ {
+ // Only need to check it if it's block scrolling
+ // We only block scroll if the click occurs
+ // in the track.
+ if (!trackListener.shouldScroll(direction))
+ {
+ trackHighlight = NO_HIGHLIGHT;
+ scrollbar.repaint();
+ return;
+ }
+ scrollByBlock(direction);
+ }
+ else
+ scrollByUnit(direction);
+ }
+ }
+
+ /**
+ * Helper class that listens for movement on the track.
+ */
+ protected class TrackListener extends MouseAdapter
+ implements MouseMotionListener
+ {
+ /** The current X coordinate of the mouse. */
+ protected int currentMouseX;
+
+ /** The current Y coordinate of the mouse. */
+ protected int currentMouseY;
+
+ /**
+ * The offset between the current mouse cursor and the current value of
+ * the scrollbar.
+ */
+ protected int offset;
+
+ /**
+ * This method is called when the mouse is being dragged.
+ *
+ * @param e The MouseEvent given.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+ if (scrollbar.getValueIsAdjusting())
+ {
+ int value;
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ value = valueForXPosition(currentMouseX) - offset;
+ else
+ value = valueForYPosition(currentMouseY) - offset;
+
+ scrollbar.setValue(value);
+ }
+ }
+
+ /**
+ * This method is called when the mouse is moved.
+ *
+ * @param e The MouseEvent given.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ if (thumbRect.contains(e.getPoint()))
+ thumbRollover = true;
+ else
+ thumbRollover = false;
+ }
+
+ /**
+ * This method is called when the mouse is pressed. When it is pressed,
+ * the thumb should move in blocks towards the cursor.
+ *
+ * @param e The MouseEvent given.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+
+ int value;
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ value = valueForXPosition(currentMouseX);
+ else
+ value = valueForYPosition(currentMouseY);
+
+ if (! thumbRect.contains(e.getPoint()))
+ {
+ scrollTimer.stop();
+ scrollListener.setScrollByBlock(true);
+ if (value > scrollbar.getValue())
+ {
+ trackHighlight = INCREASE_HIGHLIGHT;
+ scrollListener.setDirection(POSITIVE_SCROLL);
+ }
+ else
+ {
+ trackHighlight = DECREASE_HIGHLIGHT;
+ scrollListener.setDirection(NEGATIVE_SCROLL);
+ }
+ scrollTimer.setDelay(100);
+ scrollTimer.start();
+ }
+ else
+ {
+ // We'd like to keep track of where the cursor
+ // is inside the thumb.
+ // This works because the scrollbar's value represents
+ // "lower" edge of the thumb. The value at which
+ // the cursor is at must be greater or equal
+ // to that value.
+
+ scrollListener.setScrollByBlock(false);
+ scrollbar.setValueIsAdjusting(true);
+ offset = value - scrollbar.getValue();
+ }
+ scrollbar.repaint();
+ }
+
+ /**
+ * This method is called when the mouse is released. It should stop
+ * movement on the thumb
+ *
+ * @param e The MouseEvent given.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ scrollTimer.stop();
+ scrollTimer.setDelay(300);
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+
+ if (shouldScroll(POSITIVE_SCROLL))
+ scrollByBlock(POSITIVE_SCROLL);
+ else if (shouldScroll(NEGATIVE_SCROLL))
+ scrollByBlock(NEGATIVE_SCROLL);
+
+ trackHighlight = NO_HIGHLIGHT;
+ scrollListener.setScrollByBlock(false);
+ scrollbar.setValueIsAdjusting(true);
+ scrollbar.repaint();
+ }
+
+ /**
+ * A helper method that decides whether we should keep scrolling in the
+ * given direction.
+ *
+ * @param direction The direction to check for.
+ *
+ * @return Whether the thumb should keep scrolling.
+ */
+ boolean shouldScroll(int direction)
+ {
+ int value;
+ if (scrollbar.getOrientation() == HORIZONTAL)
+ value = valueForXPosition(currentMouseX);
+ else
+ value = valueForYPosition(currentMouseY);
+
+ if (thumbRect.contains(currentMouseX, currentMouseY))
+ return false;
+
+ if (direction == POSITIVE_SCROLL)
+ return value > scrollbar.getValue();
+ else
+ return value < scrollbar.getValue();
+ }
+ }
+
+ /** The listener that listens to the JButtons. */
+ protected ArrowButtonListener buttonListener;
+
+ /** The listener that listens to the model. */
+ protected ModelListener modelListener;
+
+ /** The listener that listens to the scrollbar for property changes. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The listener that listens to the timer. */
+ protected ScrollListener scrollListener;
+
+ /** The listener that listens for MouseEvents on the track. */
+ protected TrackListener trackListener;
+
+ /** The JButton that decrements the scrollbar's value. */
+ protected JButton decrButton;
+
+ /** The JButton that increments the scrollbar's value. */
+ protected JButton incrButton;
+
+ /** The dimensions of the maximum thumb size. */
+ protected Dimension maximumThumbSize;
+
+ /** The dimensions of the minimum thumb size. */
+ protected Dimension minimumThumbSize;
+
+ /** The color of the thumb. */
+ protected Color thumbColor;
+
+ /** The outer shadow of the thumb. */
+ protected Color thumbDarkShadowColor;
+
+ /** The top and left edge color for the thumb. */
+ protected Color thumbHighlightColor;
+
+ /** The outer light shadow for the thumb. */
+ protected Color thumbLightShadowColor;
+
+ /** The color that is used when the mouse press occurs in the track. */
+ protected Color trackHighlightColor;
+
+ /** The color of the track. */
+ protected Color trackColor;
+
+ /** The size and position of the track. */
+ protected Rectangle trackRect;
+
+ /** The size and position of the thumb. */
+ protected Rectangle thumbRect;
+
+ /** Indicates that the decrease highlight should be painted. */
+ protected static final int DECREASE_HIGHLIGHT = 1;
+
+ /** Indicates that the increase highlight should be painted. */
+ protected static final int INCREASE_HIGHLIGHT = 2;
+
+ /** Indicates that no highlight should be painted. */
+ protected static final int NO_HIGHLIGHT = 0;
+
+ /** Indicates that the scrolling direction is positive. */
+ private static final int POSITIVE_SCROLL = 1;
+
+ /** Indicates that the scrolling direction is negative. */
+ private static final int NEGATIVE_SCROLL = -1;
+
+ /** The cached preferred size for the scrollbar. */
+ private transient Dimension preferredSize;
+
+ /** The current highlight status. */
+ protected int trackHighlight;
+
+ /** FIXME: Use this for something (presumably mouseDragged) */
+ protected boolean isDragging;
+
+ /** The timer used to move the thumb when the mouse is held. */
+ protected Timer scrollTimer;
+
+ /** The scrollbar this UI is acting for. */
+ protected JScrollBar scrollbar;
+
+ /** True if the mouse is over the thumb. */
+ boolean thumbRollover;
+
+ /**
+ * This method adds a component to the layout.
+ *
+ * @param name The name to associate with the component that is added.
+ * @param child The Component to add.
+ */
+ public void addLayoutComponent(String name, Component child)
+ {
+ // You should not be adding stuff to this component.
+ // The contents are fixed.
+ }
+
+ /**
+ * This method configures the scrollbar's colors. This can be done by
+ * looking up the standard colors from the Look and Feel defaults.
+ */
+ protected void configureScrollBarColors()
+ {
+ trackColor = UIManager.getColor("ScrollBar.track");
+ trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
+ thumbColor = UIManager.getColor("ScrollBar.thumb");
+ thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
+ thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
+ thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
+ }
+
+ /**
+ * This method creates an ArrowButtonListener.
+ *
+ * @return A new ArrowButtonListener.
+ */
+ protected ArrowButtonListener createArrowButtonListener()
+ {
+ return new ArrowButtonListener();
+ }
+
+ /**
+ * This method creates a new JButton with the appropriate icon for the
+ * orientation.
+ *
+ * @param orientation The orientation this JButton uses.
+ *
+ * @return The increase JButton.
+ */
+ protected JButton createIncreaseButton(int orientation)
+ {
+ return new BasicArrowButton(orientation);
+ }
+
+ /**
+ * This method creates a new JButton with the appropriate icon for the
+ * orientation.
+ *
+ * @param orientation The orientation this JButton uses.
+ *
+ * @return The decrease JButton.
+ */
+ protected JButton createDecreaseButton(int orientation)
+ {
+ return new BasicArrowButton(orientation);
+ }
+
+ /**
+ * This method creates a new ModelListener.
+ *
+ * @return A new ModelListener.
+ */
+ protected ModelListener createModelListener()
+ {
+ return new ModelListener();
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method creates a new ScrollListener.
+ *
+ * @return A new ScrollListener.
+ */
+ protected ScrollListener createScrollListener()
+ {
+ return new ScrollListener();
+ }
+
+ /**
+ * This method creates a new TrackListener.
+ *
+ * @return A new TrackListener.
+ */
+ protected TrackListener createTrackListener()
+ {
+ return new TrackListener();
+ }
+
+ /**
+ * This method returns a new BasicScrollBarUI.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicScrollBarUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicScrollBarUI();
+ }
+
+ /**
+ * This method returns the maximum size for this JComponent.
+ *
+ * @param c The JComponent to measure the maximum size for.
+ *
+ * @return The maximum size for the component.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * This method returns the maximum thumb size.
+ *
+ * @return The maximum thumb size.
+ */
+ protected Dimension getMaximumThumbSize()
+ {
+ return maximumThumbSize;
+ }
+
+ /**
+ * This method returns the minimum size for this JComponent.
+ *
+ * @param c The JComponent to measure the minimum size for.
+ *
+ * @return The minimum size for the component.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the minimum thumb size.
+ *
+ * @return The minimum thumb size.
+ */
+ protected Dimension getMinimumThumbSize()
+ {
+ return minimumThumbSize;
+ }
+
+ /**
+ * This method calculates the preferred size since calling
+ * getPreferredSize() returns a cached value.
+ * This is package-private to avoid an accessor method.
+ */
+ void calculatePreferredSize()
+ {
+ int height;
+ int width;
+ height = width = 0;
+
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ {
+ width += incrButton.getPreferredSize().getWidth();
+ width += decrButton.getPreferredSize().getWidth();
+ width += 16;
+ height = UIManager.getInt("ScrollBar.width");
+ }
+ else
+ {
+ height += incrButton.getPreferredSize().getHeight();
+ height += decrButton.getPreferredSize().getHeight();
+ height += 16;
+ width = UIManager.getInt("ScrollBar.width");
+ }
+
+ Insets insets = scrollbar.getInsets();
+
+ height += insets.top + insets.bottom;
+ width += insets.left + insets.right;
+
+ preferredSize = new Dimension(width, height);
+ }
+
+ /**
+ * This method returns a cached value of the preferredSize. The only
+ * restrictions are: If the scrollbar is horizontal, the height should be
+ * the maximum of the height of the JButtons and the minimum width of the
+ * thumb. For vertical scrollbars, the calculation is similar (swap width
+ * for height and vice versa).
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferredSize.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ calculatePreferredSize();
+ return preferredSize;
+ }
+
+ /**
+ * This method returns the thumb's bounds based on the current value of the
+ * scrollbar. This method updates the cached value and returns that.
+ *
+ * @return The thumb bounds.
+ */
+ protected Rectangle getThumbBounds()
+ {
+ return thumbRect;
+ }
+
+ /**
+ * This method calculates the bounds of the track. This method updates the
+ * cached value and returns it.
+ *
+ * @return The track's bounds.
+ */
+ protected Rectangle getTrackBounds()
+ {
+ return trackRect;
+ }
+
+ /**
+ * This method installs any addition Components that are a part of or
+ * related to this scrollbar.
+ */
+ protected void installComponents()
+ {
+ int orientation = scrollbar.getOrientation();
+ switch (orientation)
+ {
+ case JScrollBar.HORIZONTAL:
+ incrButton = createIncreaseButton(EAST);
+ decrButton = createDecreaseButton(WEST);
+ break;
+ default:
+ incrButton = createIncreaseButton(SOUTH);
+ decrButton = createDecreaseButton(NORTH);
+ break;
+ }
+
+ if (incrButton != null)
+ scrollbar.add(incrButton);
+ if (decrButton != null)
+ scrollbar.add(decrButton);
+ }
+
+ /**
+ * This method installs the defaults for the scrollbar specified by the
+ * Basic Look and Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColors(scrollbar, "ScrollBar.background",
+ "ScrollBar.foreground");
+ LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
+ scrollbar.setOpaque(true);
+ scrollbar.setLayout(this);
+
+ configureScrollBarColors();
+
+ maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize");
+ minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize");
+ }
+
+ /**
+ * Installs the input map from the look and feel defaults, and a
+ * corresponding action map. Note the the keyboard bindings will only
+ * work when the {@link JScrollBar} component has the focus, which is rare.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap keyMap = getInputMap(
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ SwingUtilities.replaceUIInputMap(scrollbar,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(scrollbar, map);
+ }
+
+ /**
+ * Uninstalls the input map and action map installed by
+ * {@link #installKeyboardActions()}.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIActionMap(scrollbar, null);
+ SwingUtilities.replaceUIInputMap(scrollbar,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ }
+
+ InputMap getInputMap(int condition)
+ {
+ if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+ return (InputMap) UIManager.get("ScrollBar.focusInputMap");
+ return null;
+ }
+
+ /**
+ * Returns the action map for the {@link JScrollBar}. All scroll bars
+ * share a single action map which is created the first time this method is
+ * called, then stored in the UIDefaults table for subsequent access.
+ *
+ * @return The shared action map.
+ */
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("ScrollBar.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("ScrollBar.actionMap", map);
+ }
+ return map;
+ }
+
+ /**
+ * Creates the action map shared by all {@link JSlider} instances.
+ * This method is called once by {@link #getActionMap()} when it
+ * finds no action map in the UIDefaults table...after the map is
+ * created, it gets added to the defaults table so that subsequent
+ * calls to {@link #getActionMap()} will return the same shared
+ * instance.
+ *
+ * @return The action map.
+ */
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+ map.put("positiveUnitIncrement",
+ new AbstractAction("positiveUnitIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("positiveBlockIncrement",
+ new AbstractAction("positiveBlockIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("negativeUnitIncrement",
+ new AbstractAction("negativeUnitIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("negativeBlockIncrement",
+ new AbstractAction("negativeBlockIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("minScroll",
+ new AbstractAction("minScroll") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ sb.setValue(sb.getMinimum());
+ }
+ }
+ }
+ );
+ map.put("maxScroll",
+ new AbstractAction("maxScroll") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ sb.setValue(sb.getMaximum());
+ }
+ }
+ }
+ );
+ return map;
+ }
+
+ /**
+ * This method installs any listeners for the scrollbar. This method also
+ * installs listeners for things such as the JButtons and the timer.
+ */
+ protected void installListeners()
+ {
+ scrollListener = createScrollListener();
+ trackListener = createTrackListener();
+ buttonListener = createArrowButtonListener();
+ modelListener = createModelListener();
+ propertyChangeListener = createPropertyChangeListener();
+
+ scrollbar.addMouseMotionListener(trackListener);
+ scrollbar.addMouseListener(trackListener);
+
+ incrButton.addMouseListener(buttonListener);
+ decrButton.addMouseListener(buttonListener);
+
+ scrollbar.addPropertyChangeListener(propertyChangeListener);
+ scrollbar.getModel().addChangeListener(modelListener);
+
+ scrollTimer.addActionListener(scrollListener);
+ }
+
+ /**
+ * This method installs the UI for the component. This can include setting
+ * up listeners, defaults, and components. This also includes initializing
+ * any data objects.
+ *
+ * @param c The JComponent to install.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JScrollBar)
+ {
+ scrollbar = (JScrollBar) c;
+
+ trackRect = new Rectangle();
+ thumbRect = new Rectangle();
+
+ scrollTimer = new Timer(300, null);
+
+ installDefaults();
+ installComponents();
+ configureScrollBarColors();
+ installListeners();
+ installKeyboardActions();
+
+ calculatePreferredSize();
+ }
+ }
+
+ /**
+ * This method lays out the scrollbar.
+ *
+ * @param scrollbarContainer The Container to layout.
+ */
+ public void layoutContainer(Container scrollbarContainer)
+ {
+ if (scrollbarContainer instanceof JScrollBar)
+ {
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ layoutHScrollbar((JScrollBar) scrollbarContainer);
+ else
+ layoutVScrollbar((JScrollBar) scrollbarContainer);
+ }
+ }
+
+ /**
+ * This method lays out the scrollbar horizontally.
+ *
+ * @param sb The JScrollBar to layout.
+ */
+ protected void layoutHScrollbar(JScrollBar sb)
+ {
+ Rectangle vr = new Rectangle();
+ SwingUtilities.calculateInnerArea(scrollbar, vr);
+
+ Dimension incrDims = incrButton.getPreferredSize();
+ Dimension decrDims = decrButton.getPreferredSize();
+
+ // calculate and update the track bounds
+ SwingUtilities.calculateInnerArea(scrollbar, trackRect);
+ trackRect.width -= incrDims.getWidth();
+ trackRect.width -= decrDims.getWidth();
+ trackRect.x += decrDims.getWidth();
+
+ updateThumbRect();
+
+ decrButton.setBounds(vr.x, vr.y, decrDims.width, trackRect.height);
+ incrButton.setBounds(trackRect.x + trackRect.width, vr.y, incrDims.width,
+ trackRect.height);
+ }
+
+ /**
+ * This method lays out the scrollbar vertically.
+ *
+ * @param sb The JScrollBar to layout.
+ */
+ protected void layoutVScrollbar(JScrollBar sb)
+ {
+ Rectangle vr = new Rectangle();
+ SwingUtilities.calculateInnerArea(scrollbar, vr);
+
+ Dimension incrDims = incrButton.getPreferredSize();
+ Dimension decrDims = decrButton.getPreferredSize();
+
+ // Update rectangles
+ SwingUtilities.calculateInnerArea(scrollbar, trackRect);
+ trackRect.height -= incrDims.getHeight();
+ trackRect.height -= decrDims.getHeight();
+ trackRect.y += decrDims.getHeight();
+
+ updateThumbRect();
+
+ decrButton.setBounds(vr.x, vr.y, trackRect.width, decrDims.height);
+ incrButton.setBounds(vr.x, trackRect.y + trackRect.height,
+ trackRect.width, incrDims.height);
+ }
+
+ /**
+ * Updates the thumb rect.
+ */
+ void updateThumbRect()
+ {
+ int max = scrollbar.getMaximum();
+ int min = scrollbar.getMinimum();
+ int value = scrollbar.getValue();
+ int extent = scrollbar.getVisibleAmount();
+ if (max - extent <= min)
+ {
+ if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL)
+ {
+ thumbRect.x = trackRect.x;
+ thumbRect.y = trackRect.y;
+ thumbRect.width = getMinimumThumbSize().width;
+ thumbRect.height = trackRect.height;
+ }
+ else
+ {
+ thumbRect.x = trackRect.x;
+ thumbRect.y = trackRect.y;
+ thumbRect.width = trackRect.width;
+ thumbRect.height = getMinimumThumbSize().height;
+ }
+ }
+ else
+ {
+ if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL)
+ {
+ thumbRect.x = trackRect.x;
+ thumbRect.width = Math.max(extent * trackRect.width / (max - min),
+ getMinimumThumbSize().width);
+ int availableWidth = trackRect.width - thumbRect.width;
+ thumbRect.x += (value - min) * availableWidth / (max - min - extent);
+ thumbRect.y = trackRect.y;
+ thumbRect.height = trackRect.height;
+ }
+ else
+ {
+ thumbRect.x = trackRect.x;
+ thumbRect.height = Math.max(extent * trackRect.height / (max - min),
+ getMinimumThumbSize().height);
+ int availableHeight = trackRect.height - thumbRect.height;
+ thumbRect.y = trackRect.y
+ + (value - min) * availableHeight / (max - min - extent);
+ thumbRect.width = trackRect.width;
+ }
+ }
+
+ }
+
+ /**
+ * This method returns the minimum size required for the layout.
+ *
+ * @param scrollbarContainer The Container that is laid out.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container scrollbarContainer)
+ {
+ return preferredLayoutSize(scrollbarContainer);
+ }
+
+ /**
+ * This method is called when the component is painted.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ paintTrack(g, c, getTrackBounds());
+ paintThumb(g, c, getThumbBounds());
+
+ if (trackHighlight == INCREASE_HIGHLIGHT)
+ paintIncreaseHighlight(g);
+ else if (trackHighlight == DECREASE_HIGHLIGHT)
+ paintDecreaseHighlight(g);
+ }
+
+ /**
+ * This method is called when repainting and the mouse is pressed in the
+ * track. It paints the track below the thumb with the trackHighlight
+ * color.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintDecreaseHighlight(Graphics g)
+ {
+ Color saved = g.getColor();
+
+ g.setColor(trackHighlightColor);
+ if (scrollbar.getOrientation() == HORIZONTAL)
+ g.fillRect(trackRect.x, trackRect.y, thumbRect.x - trackRect.x,
+ trackRect.height);
+ else
+ g.fillRect(trackRect.x, trackRect.y, trackRect.width,
+ thumbRect.y - trackRect.y);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method is called when repainting and the mouse is pressed in the
+ * track. It paints the track above the thumb with the trackHighlight
+ * color.
+ *
+ * @param g The Graphics objet to paint with.
+ */
+ protected void paintIncreaseHighlight(Graphics g)
+ {
+ Color saved = g.getColor();
+
+ g.setColor(trackHighlightColor);
+ if (scrollbar.getOrientation() == HORIZONTAL)
+ g.fillRect(thumbRect.x + thumbRect.width, trackRect.y,
+ trackRect.x + trackRect.width - thumbRect.x - thumbRect.width,
+ trackRect.height);
+ else
+ g.fillRect(trackRect.x, thumbRect.y + thumbRect.height, trackRect.width,
+ trackRect.y + trackRect.height - thumbRect.y
+ - thumbRect.height);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the thumb.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The Component that is being painted.
+ * @param thumbBounds The thumb bounds.
+ */
+ protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
+ {
+ g.setColor(thumbColor);
+ g.fillRect(thumbBounds.x, thumbBounds.y, thumbBounds.width,
+ thumbBounds.height);
+
+ BasicGraphicsUtils.drawBezel(g, thumbBounds.x, thumbBounds.y,
+ thumbBounds.width, thumbBounds.height,
+ false, false, thumbDarkShadowColor,
+ thumbDarkShadowColor, thumbHighlightColor,
+ thumbHighlightColor);
+ }
+
+ /**
+ * This method paints the track.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent being painted.
+ * @param trackBounds The track's bounds.
+ */
+ protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
+ {
+ Color saved = g.getColor();
+ g.setColor(trackColor);
+ g.fill3DRect(trackBounds.x, trackBounds.y, trackBounds.width,
+ trackBounds.height, false);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method returns the preferred size for the layout.
+ *
+ * @param scrollbarContainer The Container to find a size for.
+ *
+ * @return The preferred size for the layout.
+ */
+ public Dimension preferredLayoutSize(Container scrollbarContainer)
+ {
+ if (scrollbarContainer instanceof JComponent)
+ return getPreferredSize((JComponent) scrollbarContainer);
+ else
+ return null;
+ }
+
+ /**
+ * This method removes a child component from the layout.
+ *
+ * @param child The child to remove.
+ */
+ public void removeLayoutComponent(Component child)
+ {
+ // You should not be removing stuff from this component.
+ }
+
+ /**
+ * The method scrolls the thumb by a block in the direction specified.
+ *
+ * @param direction The direction to scroll.
+ */
+ protected void scrollByBlock(int direction)
+ {
+ scrollByBlock(scrollbar, direction);
+ }
+
+ /**
+ * Scrolls the specified <code>scrollBar</code> by one block (according
+ * to the scrollable protocol) in the specified <code>direction</code>.
+ *
+ * This method is here statically to support wheel scrolling from the
+ * BasicScrollPaneUI without code duplication.
+ *
+ * @param scrollBar the scrollbar to scroll
+ * @param direction the scroll direction
+ */
+ static final void scrollByBlock(JScrollBar scrollBar, int direction)
+ {
+ int delta;
+ if (direction > 0)
+ delta = scrollBar.getBlockIncrement(direction);
+ else
+ delta = - scrollBar.getBlockIncrement(direction);
+ int oldValue = scrollBar.getValue();
+ int newValue = oldValue + delta;
+
+ // Overflow check.
+ if (delta > 0 && newValue < oldValue)
+ newValue = scrollBar.getMaximum();
+ else if (delta < 0 && newValue > oldValue)
+ newValue = scrollBar.getMinimum();
+
+ scrollBar.setValue(newValue);
+ }
+
+ /**
+ * The method scrolls the thumb by a unit in the direction specified.
+ *
+ * @param direction The direction to scroll.
+ */
+ protected void scrollByUnit(int direction)
+ {
+ scrollByUnits(scrollbar, direction, 1);
+ }
+
+ /**
+ * Scrolls the specified <code>scrollbac/code> by <code>units</code> units
+ * in the specified <code>direction</code>.
+ *
+ * This method is here statically to support wheel scrolling from the
+ * BasicScrollPaneUI without code duplication.
+ *
+ * @param scrollBar the scrollbar to scroll
+ * @param direction the direction
+ * @param units the number of units to scroll
+ */
+ static final void scrollByUnits(JScrollBar scrollBar, int direction,
+ int units)
+ {
+ // Do this inside a loop so that we don't clash with the scrollable
+ // interface, which can return different units at times. For instance,
+ // a Scrollable could return a unit of 2 pixels only to adjust the
+ // visibility of an item. If we would simply multiply this by units,
+ // then we would only get 6 pixels, which is complete crap.
+ for (int i = 0; i < units; i++)
+ {
+ int delta;
+ if (direction > 0)
+ delta = scrollBar.getUnitIncrement(direction);
+ else
+ delta = - scrollBar.getUnitIncrement(direction);
+ int oldValue = scrollBar.getValue();
+ int newValue = oldValue + delta;
+
+ // Overflow check.
+ if (delta > 0 && newValue < oldValue)
+ newValue = scrollBar.getMaximum();
+ else if (delta < 0 && newValue > oldValue)
+ newValue = scrollBar.getMinimum();
+
+ scrollBar.setValue(newValue);
+ }
+ }
+
+ /**
+ * This method sets the thumb's bounds.
+ *
+ * @param x The X position of the thumb.
+ * @param y The Y position of the thumb.
+ * @param width The width of the thumb.
+ * @param height The height of the thumb.
+ */
+ protected void setThumbBounds(int x, int y, int width, int height)
+ {
+ thumbRect.x = x;
+ thumbRect.y = y;
+ thumbRect.width = width;
+ thumbRect.height = height;
+ }
+
+ /**
+ * This method uninstalls any components that are a part of or related to
+ * this scrollbar.
+ */
+ protected void uninstallComponents()
+ {
+ if (incrButton != null)
+ scrollbar.remove(incrButton);
+ if (decrButton != null)
+ scrollbar.remove(decrButton);
+ }
+
+ /**
+ * This method uninstalls any defaults that this scrollbar acquired from the
+ * Basic Look and Feel defaults.
+ */
+ protected void uninstallDefaults()
+ {
+ scrollbar.setForeground(null);
+ scrollbar.setBackground(null);
+ LookAndFeel.uninstallBorder(scrollbar);
+ incrButton = null;
+ decrButton = null;
+ }
+
+ /**
+ * This method uninstalls any listeners that were registered during install.
+ */
+ protected void uninstallListeners()
+ {
+ if (scrollTimer != null)
+ scrollTimer.removeActionListener(scrollListener);
+
+ if (scrollbar != null)
+ {
+ scrollbar.getModel().removeChangeListener(modelListener);
+ scrollbar.removePropertyChangeListener(propertyChangeListener);
+ scrollbar.removeMouseListener(trackListener);
+ scrollbar.removeMouseMotionListener(trackListener);
+ }
+
+ if (decrButton != null)
+ decrButton.removeMouseListener(buttonListener);
+ if (incrButton != null)
+ incrButton.removeMouseListener(buttonListener);
+
+ propertyChangeListener = null;
+ modelListener = null;
+ buttonListener = null;
+ trackListener = null;
+ scrollListener = null;
+ }
+
+ /**
+ * This method uninstalls the UI. This includes removing any defaults,
+ * listeners, and components that this UI may have initialized. It also
+ * nulls any instance data.
+ *
+ * @param c The Component to uninstall for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallComponents();
+
+ scrollTimer = null;
+
+ thumbRect = null;
+ trackRect = null;
+
+ trackColor = null;
+ trackHighlightColor = null;
+ thumbColor = null;
+ thumbHighlightColor = null;
+ thumbDarkShadowColor = null;
+ thumbLightShadowColor = null;
+
+ scrollbar = null;
+ }
+
+ /**
+ * This method returns the value in the scrollbar's range given the y
+ * coordinate. If the value is out of range, it will return the closest
+ * legal value.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param yPos The y coordinate to calculate a value for.
+ *
+ * @return The value for the y coordinate.
+ */
+ int valueForYPosition(int yPos)
+ {
+ int min = scrollbar.getMinimum();
+ int max = scrollbar.getMaximum();
+ int len = trackRect.height;
+
+ int value;
+
+ // If the length is 0, you shouldn't be able to even see where the thumb is.
+ // This really shouldn't ever happen, but just in case, we'll return the middle.
+ if (len == 0)
+ return (max - min) / 2;
+
+ value = (yPos - trackRect.y) * (max - min) / len + min;
+
+ // If this isn't a legal value, then we'll have to move to one now.
+ if (value > max)
+ value = max;
+ else if (value < min)
+ value = min;
+ return value;
+ }
+
+ /**
+ * This method returns the value in the scrollbar's range given the x
+ * coordinate. If the value is out of range, it will return the closest
+ * legal value.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param xPos The x coordinate to calculate a value for.
+ *
+ * @return The value for the x coordinate.
+ */
+ int valueForXPosition(int xPos)
+ {
+ int min = scrollbar.getMinimum();
+ int max = scrollbar.getMaximum();
+ int len = trackRect.width;
+
+ int value;
+
+ // If the length is 0, you shouldn't be able to even see where the slider is.
+ // This really shouldn't ever happen, but just in case, we'll return the middle.
+ if (len == 0)
+ return (max - min) / 2;
+
+ value = (xPos - trackRect.x) * (max - min) / len + min;
+
+ // If this isn't a legal value, then we'll have to move to one now.
+ if (value > max)
+ value = max;
+ else if (value < min)
+ value = min;
+ return value;
+ }
+
+ /**
+ * Returns true if the mouse is over the thumb.
+ *
+ * @return true if the mouse is over the thumb.
+ *
+ * @since 1.5
+ */
+ public boolean isThumbRollover()
+ {
+ return thumbRollover;
+ }
+
+ /**
+ * Set thumbRollover to active. This indicates
+ * whether or not the mouse is over the thumb.
+ *
+ * @param active - true if the mouse is over the thumb.
+ *
+ * @since 1.5
+ */
+ protected void setThumbRollover(boolean active)
+ {
+ thumbRollover = active;
+ }
+
+ /**
+ * Indicates whether the user can position the thumb with
+ * a mouse click (i.e. middle button).
+ *
+ * @return true if the user can position the thumb with a mouse
+ * click.
+ *
+ * @since 1.5
+ */
+ public boolean getSupportsAbsolutePositioning()
+ {
+ // The positioning feature has not been implemented.
+ // So, false is always returned.
+ return false;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java
new file mode 100644
index 000000000..712394830
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java
@@ -0,0 +1,823 @@
+/* BasicScrollPaneUI.java
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.JViewport;
+import javax.swing.LookAndFeel;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.ScrollPaneLayout;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ScrollPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A UI delegate for the {@link JScrollPane} component.
+ */
+public class BasicScrollPaneUI extends ScrollPaneUI
+ implements ScrollPaneConstants
+{
+
+ /**
+ * Listens for changes in the state of the horizontal scrollbar's model and
+ * updates the scrollpane accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class HSBChangeListener implements ChangeListener
+ {
+
+ /**
+ * Receives notification when the state of the horizontal scrollbar
+ * model has changed.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ JScrollBar hsb = scrollpane.getHorizontalScrollBar();
+ JViewport vp = scrollpane.getViewport();
+ Point viewPosition = vp.getViewPosition();
+ viewPosition.x = hsb.getValue();
+ vp.setViewPosition(viewPosition);
+ }
+
+ }
+
+ /**
+ * Listens for changes in the state of the vertical scrollbar's model and
+ * updates the scrollpane accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class VSBChangeListener implements ChangeListener
+ {
+
+ /**
+ * Receives notification when the state of the vertical scrollbar
+ * model has changed.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ JScrollBar vsb = scrollpane.getVerticalScrollBar();
+ JViewport vp = scrollpane.getViewport();
+ Point viewPosition = vp.getViewPosition();
+ viewPosition.y = vsb.getValue();
+ vp.setViewPosition(viewPosition);
+ }
+
+ }
+
+ /**
+ * Listens for changes of the viewport's extent size and updates the
+ * scrollpane accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class ViewportChangeHandler implements ChangeListener
+ {
+
+ /**
+ * Receives notification when the view's size, position or extent size
+ * changes. When the extents size has changed, this method calls
+ * {@link BasicScrollPaneUI#syncScrollPaneWithViewport()} to adjust the
+ * scrollbars extents as well.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ syncScrollPaneWithViewport();
+ }
+
+ }
+
+ /**
+ * Listens for property changes on the scrollpane and update the view
+ * accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+
+ /**
+ * Receives notification when any of the scrollpane's bound property
+ * changes. This method calls the appropriate update method on the
+ * <code>ScrollBarUI</code>.
+ *
+ * @param e the property change event
+ *
+ * @see BasicScrollPaneUI#updateColumnHeader(PropertyChangeEvent)
+ * @see BasicScrollPaneUI#updateRowHeader(PropertyChangeEvent)
+ * @see BasicScrollPaneUI#updateScrollBarDisplayPolicy(PropertyChangeEvent)
+ * @see BasicScrollPaneUI#updateViewport(PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String propName = e.getPropertyName();
+ if (propName.equals("viewport"))
+ updateViewport(e);
+ else if (propName.equals("rowHeader"))
+ updateRowHeader(e);
+ else if (propName.equals("columnHeader"))
+ updateColumnHeader(e);
+ else if (propName.equals("horizontalScrollBarPolicy")
+ || e.getPropertyName().equals("verticalScrollBarPolicy"))
+ updateScrollBarDisplayPolicy(e);
+ else if (propName.equals("verticalScrollBar"))
+ {
+ JScrollBar oldSb = (JScrollBar) e.getOldValue();
+ oldSb.getModel().removeChangeListener(vsbChangeListener);
+ JScrollBar newSb = (JScrollBar) e.getNewValue();
+ newSb.getModel().addChangeListener(vsbChangeListener);
+ }
+ else if (propName.equals("horizontalScrollBar"))
+ {
+ JScrollBar oldSb = (JScrollBar) e.getOldValue();
+ oldSb.getModel().removeChangeListener(hsbChangeListener);
+ JScrollBar newSb = (JScrollBar) e.getNewValue();
+ newSb.getModel().addChangeListener(hsbChangeListener);
+ }
+ }
+
+ }
+
+ /**
+ * Listens for mouse wheel events and update the scrollpane accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.4
+ */
+ protected class MouseWheelHandler implements MouseWheelListener
+ {
+ /**
+ * Use to compute the visible rectangle.
+ */
+ final Rectangle rect = new Rectangle();
+
+ /**
+ * Scroll with the mouse wheel.
+ *
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+ public void mouseWheelMoved(MouseWheelEvent e)
+ {
+ if (scrollpane.isWheelScrollingEnabled() && e.getScrollAmount() != 0)
+ {
+ // Try to scroll vertically first.
+ JScrollBar scrollBar = scrollpane.getVerticalScrollBar();
+ if (scrollBar == null || ! scrollBar.isVisible())
+ scrollBar = scrollpane.getHorizontalScrollBar();
+ if (scrollBar != null && scrollBar.isVisible())
+ {
+ int direction = e.getWheelRotation() < 0 ? -1 : 1;
+ int scrollType = e.getScrollType();
+ if (scrollType == MouseWheelEvent.WHEEL_UNIT_SCROLL)
+ BasicScrollBarUI.scrollByUnits(scrollBar, direction,
+ e.getScrollAmount());
+ else if (scrollType == MouseWheelEvent.WHEEL_BLOCK_SCROLL)
+ BasicScrollBarUI.scrollByBlock(scrollBar, direction);
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds/removes the mouse wheel listener when the component is added/removed
+ * to/from the scroll pane view port.
+ *
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+ class ViewportContainerListener implements ContainerListener
+ {
+ /**
+ * Add the mouse wheel listener, allowing to scroll with the mouse.
+ */
+ public void componentAdded(ContainerEvent e)
+ {
+ e.getChild().addMouseWheelListener(mouseWheelListener);
+ }
+
+ /**
+ * Remove the mouse wheel listener.
+ */
+ public void componentRemoved(ContainerEvent e)
+ {
+ e.getChild().removeMouseWheelListener(mouseWheelListener);
+ }
+ }
+
+ /**
+ * The number of pixels by that we should scroll the content that does
+ * not implement Scrollable.
+ */
+ static int SCROLL_NON_SCROLLABLES = 10;
+
+ /**
+ * The number of rows to scroll per mouse wheel click. From impression,
+ * Sun seems using the value 3.
+ */
+ static int ROWS_PER_WHEEL_CLICK = 3;
+
+ /** The Scrollpane for which the UI is provided by this class. */
+ protected JScrollPane scrollpane;
+
+ /**
+ * The horizontal scrollbar listener.
+ */
+ protected ChangeListener hsbChangeListener;
+
+ /**
+ * The vertical scrollbar listener.
+ */
+ protected ChangeListener vsbChangeListener;
+
+ /**
+ * The viewport listener.
+ */
+ protected ChangeListener viewportChangeListener;
+
+ /**
+ * The scrollpane property change listener.
+ */
+ protected PropertyChangeListener spPropertyChangeListener;
+
+ /**
+ * The mousewheel listener for the scrollpane.
+ */
+ MouseWheelListener mouseWheelListener;
+
+ /**
+ * The listener to add and remove the mouse wheel listener to/from
+ * the component container.
+ */
+ ContainerListener containerListener;
+
+ public static ComponentUI createUI(final JComponent c)
+ {
+ return new BasicScrollPaneUI();
+ }
+
+ protected void installDefaults(JScrollPane p)
+ {
+ scrollpane = p;
+ LookAndFeel.installColorsAndFont(p, "ScrollPane.background",
+ "ScrollPane.foreground",
+ "ScrollPane.font");
+ LookAndFeel.installBorder(p, "ScrollPane.border");
+
+ // Install Viewport border.
+ Border vpBorder = p.getViewportBorder();
+ if (vpBorder == null || vpBorder instanceof UIResource)
+ {
+ vpBorder = UIManager.getBorder("ScrollPane.viewportBorder");
+ p.setViewportBorder(vpBorder);
+ }
+
+ p.setOpaque(true);
+ }
+
+ protected void uninstallDefaults(JScrollPane p)
+ {
+ LookAndFeel.uninstallBorder(p);
+ Border vpBorder = p.getViewportBorder();
+ if (vpBorder != null && vpBorder instanceof UIResource)
+ p.setViewportBorder(null);
+ }
+
+ public void installUI(final JComponent c)
+ {
+ super.installUI(c);
+ installDefaults((JScrollPane) c);
+ installListeners((JScrollPane) c);
+ installKeyboardActions((JScrollPane) c);
+ }
+
+ /**
+ * Installs the listeners on the scrollbars, the viewport and the scrollpane.
+ *
+ * @param sp the scrollpane on which to install the listeners
+ */
+ protected void installListeners(JScrollPane sp)
+ {
+ if (spPropertyChangeListener == null)
+ spPropertyChangeListener = createPropertyChangeListener();
+ sp.addPropertyChangeListener(spPropertyChangeListener);
+
+ if (hsbChangeListener == null)
+ hsbChangeListener = createHSBChangeListener();
+ sp.getHorizontalScrollBar().getModel().addChangeListener(hsbChangeListener);
+
+ if (vsbChangeListener == null)
+ vsbChangeListener = createVSBChangeListener();
+ sp.getVerticalScrollBar().getModel().addChangeListener(vsbChangeListener);
+
+ if (viewportChangeListener == null)
+ viewportChangeListener = createViewportChangeListener();
+
+ if (mouseWheelListener == null)
+ mouseWheelListener = createMouseWheelListener();
+
+ if (containerListener == null)
+ containerListener = new ViewportContainerListener();
+
+ JViewport v = sp.getViewport();
+ v.addChangeListener(viewportChangeListener);
+ v.addContainerListener(containerListener);
+
+ // Add mouse wheel listeners to the componets that are probably already
+ // in the view port.
+ for (int i = 0; i < v.getComponentCount(); i++)
+ v.getComponent(i).addMouseWheelListener(mouseWheelListener);
+ }
+
+ InputMap getInputMap(int condition)
+ {
+ if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+ return (InputMap) UIManager.get("ScrollPane.ancestorInputMap");
+ return null;
+ }
+
+ /**
+ * Returns the action map for the {@link JScrollPane}. All scroll panes
+ * share a single action map which is created the first time this method is
+ * called, then stored in the UIDefaults table for subsequent access.
+ *
+ * @return The shared action map.
+ */
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("ScrollPane.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("ScrollPane.actionMap", map);
+ }
+ return map;
+ }
+
+ /**
+ * Creates the action map shared by all {@link JSlider} instances.
+ * This method is called once by {@link #getActionMap()} when it
+ * finds no action map in the UIDefaults table...after the map is
+ * created, it gets added to the defaults table so that subsequent
+ * calls to {@link #getActionMap()} will return the same shared
+ * instance.
+ *
+ * @return The action map.
+ */
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+ map.put("scrollLeft",
+ new AbstractAction("scrollLeft") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getHorizontalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("scrollEnd",
+ new AbstractAction("scrollEnd") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb1 = sp.getHorizontalScrollBar();
+ if (sb1.isVisible())
+ {
+ sb1.setValue(sb1.getMaximum());
+ }
+ JScrollBar sb2 = sp.getVerticalScrollBar();
+ if (sb2.isVisible())
+ {
+ sb2.setValue(sb2.getMaximum());
+ }
+ }
+ }
+ );
+ map.put("unitScrollUp",
+ new AbstractAction("unitScrollUp") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getVerticalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("unitScrollLeft",
+ new AbstractAction("unitScrollLeft") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getHorizontalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("scrollUp",
+ new AbstractAction("scrollUp") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getVerticalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("scrollRight",
+ new AbstractAction("scrollRight") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getHorizontalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("scrollHome",
+ new AbstractAction("scrollHome") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb1 = sp.getHorizontalScrollBar();
+ if (sb1.isVisible())
+ {
+ sb1.setValue(sb1.getMinimum());
+ }
+ JScrollBar sb2 = sp.getVerticalScrollBar();
+ if (sb2.isVisible())
+ {
+ sb2.setValue(sb2.getMinimum());
+ }
+ }
+ }
+ );
+ map.put("scrollDown",
+ new AbstractAction("scrollDown") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getVerticalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("unitScrollDown",
+ new AbstractAction("unitScrollDown") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getVerticalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("unitScrollRight",
+ new AbstractAction("unitScrollRight") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getHorizontalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ return map;
+ }
+
+ /**
+ * Installs additional keyboard actions on the scrollpane. This is a hook
+ * method provided to subclasses in order to install their own keyboard
+ * actions.
+ *
+ * @param sp the scrollpane to install keyboard actions on
+ */
+ protected void installKeyboardActions(JScrollPane sp)
+ {
+ InputMap keyMap = getInputMap(
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ SwingUtilities.replaceUIInputMap(sp,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(sp, map);
+ }
+
+ /**
+ * Uninstalls all keyboard actions from the JScrollPane that have been
+ * installed by {@link #installKeyboardActions}. This is a hook method
+ * provided to subclasses to add their own keyboard actions.
+ *
+ * @param sp the scrollpane to uninstall keyboard actions from
+ */
+ protected void uninstallKeyboardActions(JScrollPane sp)
+ {
+ SwingUtilities.replaceUIActionMap(sp, null);
+ SwingUtilities.replaceUIInputMap(sp,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ }
+
+ /**
+ * Creates and returns the change listener for the horizontal scrollbar.
+ *
+ * @return the change listener for the horizontal scrollbar
+ */
+ protected ChangeListener createHSBChangeListener()
+ {
+ return new HSBChangeListener();
+ }
+
+ /**
+ * Creates and returns the change listener for the vertical scrollbar.
+ *
+ * @return the change listener for the vertical scrollbar
+ */
+ protected ChangeListener createVSBChangeListener()
+ {
+ return new VSBChangeListener();
+ }
+
+ /**
+ * Creates and returns the change listener for the viewport.
+ *
+ * @return the change listener for the viewport
+ */
+ protected ChangeListener createViewportChangeListener()
+ {
+ return new ViewportChangeHandler();
+ }
+
+ /**
+ * Creates and returns the property change listener for the scrollpane.
+ *
+ * @return the property change listener for the scrollpane
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates and returns the mouse wheel listener for the scrollpane.
+ *
+ * @return the mouse wheel listener for the scrollpane
+ *
+ * @since 1.4
+ */
+ protected MouseWheelListener createMouseWheelListener()
+ {
+ return new MouseWheelHandler();
+ }
+
+ public void uninstallUI(final JComponent c)
+ {
+ uninstallDefaults((JScrollPane) c);
+ uninstallListeners(c);
+ installKeyboardActions((JScrollPane) c);
+ }
+
+ /**
+ * Uninstalls all the listeners that have been installed in
+ * {@link #installListeners(JScrollPane)}.
+ *
+ * @param c the scrollpane from which to uninstall the listeners
+ */
+ protected void uninstallListeners(JComponent c)
+ {
+ JScrollPane sp = (JScrollPane) c;
+ sp.removePropertyChangeListener(spPropertyChangeListener);
+ sp.getHorizontalScrollBar().getModel()
+ .removeChangeListener(hsbChangeListener);
+ sp.getVerticalScrollBar().getModel()
+ .removeChangeListener(vsbChangeListener);
+
+ JViewport v = sp.getViewport();
+ v.removeChangeListener(viewportChangeListener);
+ v.removeContainerListener(containerListener);
+
+ for (int i = 0; i < v.getComponentCount(); i++)
+ v.getComponent(i).removeMouseWheelListener(mouseWheelListener);
+
+ }
+
+ public Dimension getMinimumSize(JComponent c)
+ {
+ JScrollPane p = (JScrollPane) c;
+ ScrollPaneLayout sl = (ScrollPaneLayout) p.getLayout();
+ return sl.minimumLayoutSize(c);
+ }
+
+ public void paint(Graphics g, JComponent c)
+ {
+ Border vpBorder = scrollpane.getViewportBorder();
+ if (vpBorder != null)
+ {
+ Rectangle r = scrollpane.getViewportBorderBounds();
+ vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
+ }
+ }
+
+ /**
+ * Synchronizes the scrollbar and header settings positions and extent
+ * with the viewport's view position and extent.
+ */
+ protected void syncScrollPaneWithViewport()
+ {
+ JViewport vp = scrollpane.getViewport();
+
+ if (vp != null)
+ {
+ Dimension extentSize = vp.getExtentSize();
+ Point viewPos = vp.getViewPosition();
+ Dimension viewSize = vp.getViewSize();
+
+ // Update the vertical scrollbar.
+ JScrollBar vsb = scrollpane.getVerticalScrollBar();
+ if (vsb != null)
+ {
+ int extent = extentSize.height;
+ int max = viewSize.height;
+ int val = Math.max(0, Math.min(viewPos.y, max - extent));
+ vsb.setValues(val, extent, 0, max);
+ }
+
+ // Update the horizontal scrollbar.
+ JScrollBar hsb = scrollpane.getHorizontalScrollBar();
+ if (hsb != null)
+ {
+ int extent = extentSize.width;
+ int max = viewSize.width;
+ int val = Math.max(0, Math.min(viewPos.x, max - extent));
+ hsb.setValues(val, extent, 0, max);
+ }
+
+ // Update the row header.
+ JViewport rowHeader = scrollpane.getRowHeader();
+ if (rowHeader != null)
+ {
+ Point p = new Point(0, viewPos.y);
+ rowHeader.setViewPosition(p);
+ }
+
+ // Update the column header.
+ JViewport colHeader = scrollpane.getColumnHeader();
+ if (colHeader != null)
+ {
+ Point p = new Point(viewPos.x, 0);
+ colHeader.setViewPosition(p);
+ }
+ }
+ }
+
+ /**
+ * Receives notification when the <code>columnHeader</code> property has
+ * changed on the scrollpane.
+ *
+ * @param ev the property change event
+ */
+ protected void updateColumnHeader(PropertyChangeEvent ev)
+ {
+ // TODO: Find out what should be done here. Or is this only a hook?
+ }
+
+ /**
+ * Receives notification when the <code>rowHeader</code> property has changed
+ * on the scrollpane.
+ *
+ * @param ev the property change event
+ */
+ protected void updateRowHeader(PropertyChangeEvent ev)
+ {
+ // TODO: Find out what should be done here. Or is this only a hook?
+ }
+
+ /**
+ * Receives notification when the <code>scrollBarDisplayPolicy</code>
+ * property has changed on the scrollpane.
+ *
+ * @param ev the property change event
+ */
+ protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev)
+ {
+ scrollpane.revalidate();
+ scrollpane.repaint();
+ }
+
+ /**
+ * Receives notification when the <code>viewport</code> property has changed
+ * on the scrollpane.
+ *
+ * This method sets removes the viewportChangeListener from the old viewport
+ * and adds it to the new viewport.
+ *
+ * @param ev the property change event
+ */
+ protected void updateViewport(PropertyChangeEvent ev)
+ {
+ JViewport oldViewport = (JViewport) ev.getOldValue();
+ oldViewport.removeChangeListener(viewportChangeListener);
+ JViewport newViewport = (JViewport) ev.getNewValue();
+ newViewport.addChangeListener(viewportChangeListener);
+ syncScrollPaneWithViewport();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java
new file mode 100644
index 000000000..2c80822a4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java
@@ -0,0 +1,252 @@
+/* BasicSeparatorUI.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.JComponent;
+import javax.swing.JSeparator;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SeparatorUI;
+
+/**
+ * The Basic Look and Feel UI delegate for JSeparator.
+ */
+public class BasicSeparatorUI extends SeparatorUI
+{
+ /** The shadow color. */
+ protected Color shadow;
+
+ /** The highlight color. */
+ protected Color highlight;
+
+ /**
+ * Creates a new UI delegate for the given JComponent.
+ *
+ * @param c The JComponent to create a delegate for.
+ *
+ * @return A new BasicSeparatorUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicSeparatorUI();
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ * This can include installing defaults, listeners, and
+ * initializing any instance data.
+ *
+ * @param c The JComponent that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ if (c instanceof JSeparator)
+ {
+ JSeparator s = (JSeparator) c;
+
+ installDefaults(s);
+ installListeners(s);
+ }
+ }
+
+ /**
+ * Uninstalls the UI for the given JComponent. This
+ * method reverses what was done when installing
+ * the UI on the JComponent.
+ *
+ * @param c The JComponent that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ if (c instanceof JSeparator)
+ {
+ JSeparator s = (JSeparator) c;
+
+ uninstallListeners(s);
+ uninstallDefaults(s);
+ }
+ }
+
+ /**
+ * This method installs the defaults that are given by
+ * the Basic Look and Feel.
+ *
+ * @param s The JSeparator that is being installed.
+ */
+ protected void installDefaults(JSeparator s)
+ {
+ shadow = UIManager.getColor("Separator.shadow");
+ highlight = UIManager.getColor("Separator.highlight");
+ s.setOpaque(false);
+ }
+
+ /**
+ * This method removes the defaults that were given
+ * by the Basic Look and Feel.
+ *
+ * @param s The JSeparator that is being uninstalled.
+ */
+ protected void uninstallDefaults(JSeparator s)
+ {
+ shadow = null;
+ highlight = null;
+ }
+
+ /**
+ * This method installs any listeners that need
+ * to be attached to the JSeparator or any of its
+ * components.
+ *
+ * @param s The JSeparator that is being installed.
+ */
+ protected void installListeners(JSeparator s)
+ {
+ // Separators don't receive events.
+ }
+
+ /**
+ * This method uninstalls any listeners that
+ * were installed during the install UI process.
+ *
+ * @param s The JSeparator that is being uninstalled.
+ */
+ protected void uninstallListeners(JSeparator s)
+ {
+ // Separators don't receive events.
+ }
+
+ /**
+ * The separator is made of two lines. The top line will be
+ * the shadow color (or left line if it's vertical). The bottom
+ * or right line will be the highlight color. The two lines will
+ * be centered inside the bounds box. If the separator is horizontal,
+ * then it will be vertically centered, or if it's vertical, it will
+ * be horizontally centered.
+ *
+ * @param g The Graphics object to paint with
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Rectangle r = new Rectangle();
+ SwingUtilities.calculateInnerArea(c, r);
+ Color saved = g.getColor();
+
+ JSeparator s;
+ if (c instanceof JSeparator)
+ s = (JSeparator) c;
+ else
+ return;
+
+ if (s.getOrientation() == JSeparator.HORIZONTAL)
+ {
+ int midAB = r.height / 2;
+ g.setColor(shadow);
+ g.drawLine(r.x, r.y + midAB - 1, r.x + r.width, r.y + midAB - 1);
+
+ g.setColor(highlight);
+ g.fillRect(r.x, r.y + midAB, r.x + r.width, r.y + midAB);
+ }
+ else
+ {
+ int midAD = r.height / 2 + r.y;
+ g.setColor(shadow);
+ g.drawLine(r.x, r.y, r.x, r.y + r.height);
+
+ g.setColor(highlight);
+ g.fillRect(r.x + midAD, r.y + r.height, r.x + midAD, r.y + r.height);
+ }
+ g.setColor(saved);
+ }
+
+ /**
+ * This method returns the preferred size of the
+ * JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension pref = new Dimension(2, 0);
+ if (c instanceof JSeparator)
+ {
+ JSeparator s = (JSeparator) c;
+ if (s.getOrientation() == JSeparator.HORIZONTAL)
+ pref = new Dimension(0, 2);
+ }
+ return pref;
+ }
+
+ /**
+ * This method returns the minimum size of the
+ * JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return new Dimension(0, 0);
+ }
+
+ /**
+ * This method returns the maximum size of the
+ * JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(Short.MAX_VALUE,
+ Short.MAX_VALUE);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java
new file mode 100644
index 000000000..b9d564372
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java
@@ -0,0 +1,2339 @@
+/* BasicSliderUI.java --
+ Copyright (C) 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.BoundedRangeModel;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JSlider;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MouseInputAdapter;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SliderUI;
+
+/**
+ * <p>
+ * BasicSliderUI.java This is the UI delegate in the Basic look and feel that
+ * paints JSliders.
+ * </p>
+ *
+ * <p>
+ * The UI delegate keeps track of 6 rectangles that place the various parts of
+ * the JSlider inside the component.
+ * </p>
+ *
+ * <p>
+ * The rectangles are organized as follows:
+ * </p>
+ * <pre>
+ * +-------------------------------------------------------+ <-- focusRect
+ * | |
+ * | +==+-------------------+==+--------------------+==+<------ contentRect
+ * | | | | |<---thumbRect | | |
+ * | | | TRACK | | |<--------- trackRect
+ * | | +-------------------+==+--------------------+ | |
+ * | | | | | |
+ * | | | TICKS GO HERE |<-------- tickRect
+ * | | | | | |
+ * | +==+-------------------------------------------+==+ |
+ * | | | | | |
+ * | | | | |<----- labelRect
+ * | | | LABELS GO HERE | | |
+ * | | | | | |
+ * | | | | | |
+ * | | | | | |
+ * | | | | | |
+ * | | | | |
+ * </pre>
+ *
+ * <p>
+ * The space between the contentRect and the focusRect are the FocusInsets.
+ * </p>
+ *
+ * <p>
+ * The space between the focusRect and the component bounds is the insetCache
+ * which are the component's insets.
+ * </p>
+ *
+ * <p>
+ * The top of the thumb is the top of the contentRect. The trackRect has to be
+ * as tall as the thumb.
+ * </p>
+ *
+ * <p>
+ * The trackRect and tickRect do not start from the left edge of the
+ * focusRect. They are trackBuffer away from each side of the focusRect. This
+ * is so that the thumb has room to move.
+ * </p>
+ *
+ * <p>
+ * The labelRect does start right against the contentRect's left and right
+ * edges and it gets all remaining space.
+ * </p>
+ */
+public class BasicSliderUI extends SliderUI
+{
+ /**
+ * Helper class that listens to the {@link JSlider}'s model for changes.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ChangeHandler implements ChangeListener
+ {
+ /**
+ * Called when the slider's model has been altered. The UI delegate should
+ * recalculate any rectangles that are dependent on the model for their
+ * positions and repaint.
+ *
+ * @param e A static {@link ChangeEvent} passed from the model.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Maximum, minimum, and extent values will be taken
+ // care of automatically when the slider is repainted.
+ // Only thing that needs recalculation is the thumb.
+ calculateThumbLocation();
+ slider.repaint();
+ }
+ }
+
+ /**
+ * Helper class that listens for resize events.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ComponentHandler extends ComponentAdapter
+ {
+ /**
+ * Called when the size of the component changes. The UI delegate should
+ * recalculate any rectangles that are dependent on the model for their
+ * positions and repaint.
+ *
+ * @param e A {@link ComponentEvent}.
+ */
+ public void componentResized(ComponentEvent e)
+ {
+ calculateGeometry();
+ slider.repaint();
+ }
+ }
+
+ /**
+ * Helper class that listens for focus events.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class FocusHandler implements FocusListener
+ {
+ /**
+ * Called when the {@link JSlider} has gained focus. It should repaint
+ * the slider with the focus drawn.
+ *
+ * @param e A {@link FocusEvent}.
+ */
+ public void focusGained(FocusEvent e)
+ {
+ slider.repaint();
+ }
+
+ /**
+ * Called when the {@link JSlider} has lost focus. It should repaint the
+ * slider without the focus drawn.
+ *
+ * @param e A {@link FocusEvent}.
+ */
+ public void focusLost(FocusEvent e)
+ {
+ slider.repaint();
+ }
+ }
+
+ /**
+ * Helper class that listens for changes to the properties of the {@link
+ * JSlider}.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Called when one of the properties change. The UI should recalculate any
+ * rectangles if necessary and repaint.
+ *
+ * @param e A {@link PropertyChangeEvent}.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // Check for orientation changes.
+ String prop = e.getPropertyName();
+ if (prop.equals("orientation")
+ || prop.equals("inverted")
+ || prop.equals("labelTable")
+ || prop.equals("majorTickSpacing")
+ || prop.equals("minorTickSpacing")
+ || prop.equals("paintTicks")
+ || prop.equals("paintTrack")
+ || prop.equals("paintLabels"))
+ {
+ calculateGeometry();
+ slider.repaint();
+ }
+ else if (e.getPropertyName().equals("model"))
+ {
+ BoundedRangeModel oldModel = (BoundedRangeModel) e.getOldValue();
+ oldModel.removeChangeListener(changeListener);
+ slider.getModel().addChangeListener(changeListener);
+ calculateThumbLocation();
+ slider.repaint();
+ }
+ }
+ }
+
+ /**
+ * Helper class that listens to our swing timer. This class is responsible
+ * for listening to the timer and moving the thumb in the proper direction
+ * every interval.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ScrollListener implements ActionListener
+ {
+ /** Indicates which direction the thumb should scroll. */
+ private transient int direction;
+
+ /** Indicates whether we should scroll in blocks or in units. */
+ private transient boolean block;
+
+ /**
+ * Creates a new ScrollListener object.
+ */
+ public ScrollListener()
+ {
+ direction = POSITIVE_SCROLL;
+ block = false;
+ }
+
+ /**
+ * Creates a new ScrollListener object.
+ *
+ * @param dir The direction to scroll in.
+ * @param block If movement will be in blocks.
+ */
+ public ScrollListener(int dir, boolean block)
+ {
+ direction = dir;
+ this.block = block;
+ }
+
+ /**
+ * Called every time the swing timer reaches its interval. If the thumb
+ * needs to move, then this method will move the thumb one block or unit
+ * in the direction desired. Otherwise, the timer can be stopped.
+ *
+ * @param e An {@link ActionEvent}.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (! trackListener.shouldScroll(direction))
+ {
+ scrollTimer.stop();
+ return;
+ }
+
+ if (block)
+ scrollByBlock(direction);
+ else
+ scrollByUnit(direction);
+ }
+
+ /**
+ * Sets the direction to scroll in.
+ *
+ * @param direction The direction to scroll in.
+ */
+ public void setDirection(int direction)
+ {
+ this.direction = direction;
+ }
+
+ /**
+ * Sets whether movement will be in blocks.
+ *
+ * @param block If movement will be in blocks.
+ */
+ public void setScrollByBlock(boolean block)
+ {
+ this.block = block;
+ }
+ }
+
+ /**
+ * Helper class that listens for mouse events.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TrackListener extends MouseInputAdapter
+ {
+ /** The current X position of the mouse. */
+ protected int currentMouseX;
+
+ /** The current Y position of the mouse. */
+ protected int currentMouseY;
+
+ /**
+ * The offset between the current slider value and the cursor's position.
+ */
+ protected int offset;
+
+ /**
+ * Called when the mouse has been dragged. This should find the mouse's
+ * current position and adjust the value of the {@link JSlider}
+ * accordingly.
+ *
+ * @param e A {@link MouseEvent}
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ dragging = true;
+ if (slider.isEnabled())
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+ if (slider.getValueIsAdjusting())
+ {
+ int value;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ value = valueForXPosition(currentMouseX) - offset;
+ else
+ value = valueForYPosition(currentMouseY) - offset;
+
+ slider.setValue(value);
+ }
+ }
+ }
+
+ /**
+ * Called when the mouse has moved over a component but no buttons have
+ * been pressed yet.
+ *
+ * @param e A {@link MouseEvent}
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Don't care that we're moved unless we're dragging.
+ }
+
+ /**
+ * Called when the mouse is pressed. When the press occurs on the thumb
+ * itself, the {@link JSlider} should have its value set to where the
+ * mouse was pressed. If the press occurs on the track, then the thumb
+ * should move one block towards the direction of the mouse.
+ *
+ * @param e A {@link MouseEvent}
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (slider.isEnabled())
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+
+ int value;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ value = valueForXPosition(currentMouseX);
+ else
+ value = valueForYPosition(currentMouseY);
+
+ if (slider.getSnapToTicks())
+ value = findClosestTick(value);
+
+ // If the thumb is hit, then we don't need to set the timers to
+ // move it.
+ if (! thumbRect.contains(e.getPoint()))
+ {
+ // The mouse has hit some other part of the slider.
+ // The value moves no matter where in the slider you hit.
+ if (value > slider.getValue())
+ scrollDueToClickInTrack(POSITIVE_SCROLL);
+ else
+ scrollDueToClickInTrack(NEGATIVE_SCROLL);
+ }
+ else
+ {
+ slider.setValueIsAdjusting(true);
+ offset = value - slider.getValue();
+ }
+ }
+ }
+
+ /**
+ * Called when the mouse is released. This should stop the timer that
+ * scrolls the thumb.
+ *
+ * @param e A {@link MouseEvent}
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ dragging = false;
+ if (slider.isEnabled())
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+
+ if (slider.getValueIsAdjusting())
+ {
+ slider.setValueIsAdjusting(false);
+ if (slider.getSnapToTicks())
+ slider.setValue(findClosestTick(slider.getValue()));
+ }
+ if (scrollTimer != null)
+ scrollTimer.stop();
+ }
+ slider.repaint();
+ }
+
+ /**
+ * Indicates whether the thumb should scroll in the given direction.
+ *
+ * @param direction The direction to check.
+ *
+ * @return True if the thumb should move in that direction.
+ */
+ public boolean shouldScroll(int direction)
+ {
+ int value;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ value = valueForXPosition(currentMouseX);
+ else
+ value = valueForYPosition(currentMouseY);
+
+ if (direction == POSITIVE_SCROLL)
+ return value > slider.getValue();
+ else
+ return value < slider.getValue();
+ }
+ }
+
+ /**
+ * This class is no longer used as of JDK1.3.
+ */
+ public class ActionScroller extends AbstractAction
+ {
+ /**
+ * Not used.
+ *
+ * @param slider not used
+ * @param dir not used
+ * @param block not used
+ */
+ public ActionScroller(JSlider slider, int dir, boolean block)
+ {
+ // Not used.
+ }
+
+ /**
+ * Not used.
+ *
+ * @param event not used
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // Not used.
+ }
+ }
+
+ /** Listener for changes from the model. */
+ protected ChangeListener changeListener;
+
+ /** Listener for changes to the {@link JSlider}. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** Listener for the scrollTimer. */
+ protected ScrollListener scrollListener;
+
+ /** Listener for component resizing. */
+ protected ComponentListener componentListener;
+
+ /** Listener for focus handling. */
+ protected FocusListener focusListener;
+
+ /** Listener for mouse events. */
+ protected TrackListener trackListener;
+
+ /** The insets between the FocusRectangle and the ContentRectangle. */
+ protected Insets focusInsets;
+
+ /** The {@link JSlider}'s insets. */
+ protected Insets insetCache;
+
+ /** Rectangle describing content bounds. See diagram above. */
+ protected Rectangle contentRect;
+
+ /** Rectangle describing focus bounds. See diagram above. */
+ protected Rectangle focusRect;
+
+ /** Rectangle describing the thumb's bounds. See diagram above. */
+ protected Rectangle thumbRect;
+
+ /** Rectangle describing the tick bounds. See diagram above. */
+ protected Rectangle tickRect;
+
+ /** Rectangle describing the label bounds. See diagram above. */
+ protected Rectangle labelRect;
+
+ /** Rectangle describing the track bounds. See diagram above. */
+ protected Rectangle trackRect;
+
+ /** FIXME: use this somewhere. */
+ public static final int MAX_SCROLL = 2;
+
+ /** FIXME: use this somewhere. */
+ public static final int MIN_SCROLL = -2;
+
+ /** A constant describing scrolling towards the minimum. */
+ public static final int NEGATIVE_SCROLL = -1;
+
+ /** A constant describing scrolling towards the maximum. */
+ public static final int POSITIVE_SCROLL = 1;
+
+ /** The gap between the edges of the contentRect and trackRect. */
+ protected int trackBuffer;
+
+ /** Whether this slider is actually drawn left to right. */
+ protected boolean leftToRightCache;
+
+ /** A timer that periodically moves the thumb. */
+ protected Timer scrollTimer;
+
+ /** A reference to the {@link JSlider} that this UI was created for. */
+ protected JSlider slider;
+
+ /** The shadow color. */
+ private transient Color shadowColor;
+
+ /** The highlight color. */
+ private transient Color highlightColor;
+
+ /** The focus color. */
+ private transient Color focusColor;
+
+ /** True if the user is dragging the slider. */
+ boolean dragging;
+
+ /**
+ * Creates a new Basic look and feel Slider UI.
+ *
+ * @param b The {@link JSlider} that this UI was created for.
+ */
+ public BasicSliderUI(JSlider b)
+ {
+ super();
+ }
+
+ /**
+ * Returns true if the user is dragging the slider.
+ *
+ * @return true if the slider is being dragged.
+ *
+ * @since 1.5
+ */
+ protected boolean isDragging()
+ {
+ return dragging;
+ }
+
+ /**
+ * Gets the shadow color to be used for this slider. The shadow color is the
+ * color used for drawing the top and left edges of the track.
+ *
+ * @return The shadow color.
+ */
+ protected Color getShadowColor()
+ {
+ return shadowColor;
+ }
+
+ /**
+ * Gets the highlight color to be used for this slider. The highlight color
+ * is the color used for drawing the bottom and right edges of the track.
+ *
+ * @return The highlight color.
+ */
+ protected Color getHighlightColor()
+ {
+ return highlightColor;
+ }
+
+ /**
+ * Gets the focus color to be used for this slider. The focus color is the
+ * color used for drawing the focus rectangle when the component gains
+ * focus.
+ *
+ * @return The focus color.
+ */
+ protected Color getFocusColor()
+ {
+ return focusColor;
+ }
+
+ /**
+ * Factory method to create a BasicSliderUI for the given {@link
+ * JComponent}, which should be a {@link JSlider}.
+ *
+ * @param b The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicSliderUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent b)
+ {
+ return new BasicSliderUI((JSlider) b);
+ }
+
+ /**
+ * Installs and initializes all fields for this UI delegate. Any properties
+ * of the UI that need to be initialized and/or set to defaults will be
+ * done now. It will also install any listeners necessary.
+ *
+ * @param c The {@link JComponent} that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JSlider)
+ {
+ slider = (JSlider) c;
+
+ focusRect = new Rectangle();
+ contentRect = new Rectangle();
+ thumbRect = new Rectangle();
+ trackRect = new Rectangle();
+ tickRect = new Rectangle();
+ labelRect = new Rectangle();
+
+ insetCache = slider.getInsets();
+ leftToRightCache = ! slider.getInverted();
+
+ scrollTimer = new Timer(200, null);
+ scrollTimer.setRepeats(true);
+
+ installDefaults(slider);
+ installListeners(slider);
+ installKeyboardActions(slider);
+
+ calculateFocusRect();
+
+ calculateContentRect();
+ calculateThumbSize();
+ calculateTrackBuffer();
+ calculateTrackRect();
+ calculateThumbLocation();
+
+ calculateTickRect();
+ calculateLabelRect();
+ }
+ }
+
+ /**
+ * Performs the opposite of installUI. Any properties or resources that need
+ * to be cleaned up will be done now. It will also uninstall any listeners
+ * it has. In addition, any properties of this UI will be nulled.
+ *
+ * @param c The {@link JComponent} that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+
+ uninstallKeyboardActions(slider);
+ uninstallListeners(slider);
+
+ scrollTimer = null;
+
+ focusRect = null;
+ contentRect = null;
+ thumbRect = null;
+ trackRect = null;
+ tickRect = null;
+ labelRect = null;
+
+ focusInsets = null;
+ }
+
+ /**
+ * Initializes any default properties that this UI has from the defaults for
+ * the Basic look and feel.
+ *
+ * @param slider The {@link JSlider} that is having this UI installed.
+ */
+ protected void installDefaults(JSlider slider)
+ {
+ LookAndFeel.installColors(slider, "Slider.background",
+ "Slider.foreground");
+ LookAndFeel.installBorder(slider, "Slider.border");
+ shadowColor = UIManager.getColor("Slider.shadow");
+ highlightColor = UIManager.getColor("Slider.highlight");
+ focusColor = UIManager.getColor("Slider.focus");
+ focusInsets = UIManager.getInsets("Slider.focusInsets");
+ slider.setOpaque(true);
+ }
+
+ /**
+ * Creates a new {@link TrackListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link TrackListener} is
+ * created for.
+ *
+ * @return A new {@link TrackListener}.
+ */
+ protected TrackListener createTrackListener(JSlider slider)
+ {
+ return new TrackListener();
+ }
+
+ /**
+ * Creates a new {@link ChangeListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link ChangeListener} is
+ * created for.
+ *
+ * @return A new {@link ChangeListener}.
+ */
+ protected ChangeListener createChangeListener(JSlider slider)
+ {
+ return new ChangeHandler();
+ }
+
+ /**
+ * Creates a new {@link ComponentListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link ComponentListener} is
+ * created for.
+ *
+ * @return A new {@link ComponentListener}.
+ */
+ protected ComponentListener createComponentListener(JSlider slider)
+ {
+ return new ComponentHandler();
+ }
+
+ /**
+ * Creates a new {@link FocusListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link FocusListener} is
+ * created for.
+ *
+ * @return A new {@link FocusListener}.
+ */
+ protected FocusListener createFocusListener(JSlider slider)
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * Creates a new {@link ScrollListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link ScrollListener} is
+ * created for.
+ *
+ * @return A new {@link ScrollListener}.
+ */
+ protected ScrollListener createScrollListener(JSlider slider)
+ {
+ return new ScrollListener();
+ }
+
+ /**
+ * Creates a new {@link PropertyChangeListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link
+ * PropertyChangeListener} is created for.
+ *
+ * @return A new {@link PropertyChangeListener}.
+ */
+ protected PropertyChangeListener createPropertyChangeListener(JSlider slider)
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates and registers all the listeners for this UI delegate. This
+ * includes creating the ScrollListener and registering it to the timer.
+ *
+ * @param slider The {@link JSlider} is having listeners installed.
+ */
+ protected void installListeners(JSlider slider)
+ {
+ propertyChangeListener = createPropertyChangeListener(slider);
+ componentListener = createComponentListener(slider);
+ trackListener = createTrackListener(slider);
+ focusListener = createFocusListener(slider);
+ changeListener = createChangeListener(slider);
+ scrollListener = createScrollListener(slider);
+
+ slider.addPropertyChangeListener(propertyChangeListener);
+ slider.addComponentListener(componentListener);
+ slider.addMouseListener(trackListener);
+ slider.addMouseMotionListener(trackListener);
+ slider.addFocusListener(focusListener);
+ slider.getModel().addChangeListener(changeListener);
+
+ scrollTimer.addActionListener(scrollListener);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using. In
+ * addition, it will also null any listeners that it was using.
+ *
+ * @param slider The {@link JSlider} that is having listeners removed.
+ */
+ protected void uninstallListeners(JSlider slider)
+ {
+ slider.removePropertyChangeListener(propertyChangeListener);
+ slider.removeComponentListener(componentListener);
+ slider.removeMouseListener(trackListener);
+ slider.removeMouseMotionListener(trackListener);
+ slider.removeFocusListener(focusListener);
+ slider.getModel().removeChangeListener(changeListener);
+
+ scrollTimer.removeActionListener(scrollListener);
+
+ propertyChangeListener = null;
+ componentListener = null;
+ trackListener = null;
+ focusListener = null;
+ changeListener = null;
+ scrollListener = null;
+ }
+
+ /**
+ * Installs any keyboard actions. The list of keys that need to be bound are
+ * listed in Basic look and feel's defaults.
+ *
+ * @param slider The {@link JSlider} that is having keyboard actions
+ * installed.
+ */
+ protected void installKeyboardActions(JSlider slider)
+ {
+ InputMap keyMap = getInputMap(JComponent.WHEN_FOCUSED);
+ SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, keyMap);
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(slider, map);
+ }
+
+ /**
+ * Uninstalls any keyboard actions. The list of keys used are listed in
+ * Basic look and feel's defaults.
+ *
+ * @param slider The {@link JSlider} that is having keyboard actions
+ * uninstalled.
+ */
+ protected void uninstallKeyboardActions(JSlider slider)
+ {
+ SwingUtilities.replaceUIActionMap(slider, null);
+ SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, null);
+ }
+
+ /* XXX: This is all after experimentation with SUN's implementation.
+
+ PreferredHorizontalSize seems to be 200x21.
+ PreferredVerticalSize seems to be 21x200.
+
+ MinimumHorizontalSize seems to be 36x21.
+ MinimumVerticalSize seems to be 21x36.
+
+ PreferredSize seems to be 200x63. Or Components.getBounds?
+
+ MinimumSize seems to be 36x63.
+
+ MaximumSize seems to be 32767x63.
+ */
+
+ /**
+ * This method returns the preferred size when the slider is horizontally
+ * oriented.
+ *
+ * @return The dimensions of the preferred horizontal size.
+ */
+ public Dimension getPreferredHorizontalSize()
+ {
+ Dimension dim = UIManager.getDimension("Slider.horizontalSize");
+ if (dim == null) // Just to be sure we mirror the default.
+ dim = new Dimension(200, 21);
+ return dim;
+ }
+
+ /**
+ * This method returns the preferred size when the slider is vertically
+ * oriented.
+ *
+ * @return The dimensions of the preferred vertical size.
+ */
+ public Dimension getPreferredVerticalSize()
+ {
+ Dimension dim = UIManager.getDimension("Slider.verticalSize");
+ if (dim == null) // Just to be sure we mirror the default.
+ dim = new Dimension(21, 200);
+ return dim;
+ }
+
+ /**
+ * This method returns the minimum size when the slider is horizontally
+ * oriented.
+ *
+ * @return The dimensions of the minimum horizontal size.
+ */
+ public Dimension getMinimumHorizontalSize()
+ {
+ Dimension dim = UIManager.getDimension("Slider.minimumHorizontalSize");
+ if (dim == null) // Just to be sure we mirror the default.
+ dim = new Dimension(36, 21);
+ return dim;
+ }
+
+ /**
+ * This method returns the minimum size of the slider when it is vertically
+ * oriented.
+ *
+ * @return The dimensions of the minimum vertical size.
+ */
+ public Dimension getMinimumVerticalSize()
+ {
+ Dimension dim = UIManager.getDimension("Slider.minimumVerticalSize");
+ if (dim == null) // Just to be sure we mirror the default.
+ dim = new Dimension(21, 36);
+ return dim;
+ }
+
+ /**
+ * This method returns the preferred size of the component. If it returns
+ * null, then it is up to the Layout Manager to give the {@link JComponent}
+ * a size.
+ *
+ * @param c The {@link JComponent} to find the preferred size for.
+ *
+ * @return The dimensions of the preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ recalculateIfInsetsChanged();
+ Dimension dim;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ // Create copy here to protect the UIManager value.
+ dim = new Dimension(getPreferredHorizontalSize());
+ dim.height = insetCache.top + insetCache.bottom;
+ dim.height += focusInsets.top + focusInsets.bottom;
+ dim.height += trackRect.height + tickRect.height + labelRect.height;
+ }
+ else
+ {
+ // Create copy here to protect the UIManager value.
+ dim = new Dimension(getPreferredVerticalSize());
+ dim.width = insetCache.left + insetCache.right;
+ dim.width += focusInsets.left + focusInsets.right;
+ dim.width += trackRect.width + tickRect.width + labelRect.width;
+ }
+ return dim;
+ }
+
+ /**
+ * This method returns the minimum size for this {@link JSlider} for this
+ * look and feel. If it returns null, then it is up to the Layout Manager
+ * to give the {@link JComponent} a size.
+ *
+ * @param c The {@link JComponent} to find the minimum size for.
+ *
+ * @return The dimensions of the minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ recalculateIfInsetsChanged();
+ Dimension dim;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ // Create copy here to protect the UIManager value.
+ dim = new Dimension(getMinimumHorizontalSize());
+ dim.height = insetCache.top + insetCache.bottom;
+ dim.height += focusInsets.top + focusInsets.bottom;
+ dim.height += trackRect.height + tickRect.height + labelRect.height;
+ }
+ else
+ {
+ // Create copy here to protect the UIManager value.
+ dim = new Dimension(getMinimumVerticalSize());
+ dim.width = insetCache.left + insetCache.right;
+ dim.width += focusInsets.left + focusInsets.right;
+ dim.width += trackRect.width + tickRect.width + labelRect.width;
+ }
+ return dim;
+ }
+
+ /**
+ * This method returns the maximum size for this {@link JSlider} for this
+ * look and feel.
+ *
+ * @param c The {@link JComponent} to find a maximum size for.
+ *
+ * @return The dimensions of the maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension dim = getPreferredSize(c);
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ dim.width = Short.MAX_VALUE;
+ else
+ dim.height = Short.MAX_VALUE;
+ return dim;
+ }
+
+ /**
+ * This method calculates all the sizes of the rectangles by delegating to
+ * the helper methods calculateXXXRect.
+ */
+ protected void calculateGeometry()
+ {
+ calculateFocusRect();
+ calculateContentRect();
+ calculateThumbSize();
+ calculateTrackBuffer();
+ calculateTrackRect();
+ calculateTickRect();
+ calculateLabelRect();
+ calculateThumbLocation();
+ }
+
+ /**
+ * This method calculates the size and position of the focusRect. This
+ * method does not need to be called if the orientation changes.
+ */
+ protected void calculateFocusRect()
+ {
+ focusRect.x = insetCache.left;
+ focusRect.y = insetCache.top;
+ focusRect.width = slider.getWidth() - insetCache.left - insetCache.right;
+ focusRect.height = slider.getHeight() - insetCache.top - insetCache.bottom;
+ }
+
+ /**
+ * Sets the width and height of the <code>thumbRect</code> field, using the
+ * dimensions returned by {@link #getThumbSize()}.
+ */
+ protected void calculateThumbSize()
+ {
+ Dimension d = getThumbSize();
+ thumbRect.width = d.width;
+ thumbRect.height = d.height;
+ }
+
+ /**
+ * Updates the <code>contentRect</code> field to an area inside the
+ * <code>focusRect</code>. This method does not need to be called if the
+ * orientation changes.
+ */
+ protected void calculateContentRect()
+ {
+ contentRect.x = focusRect.x + focusInsets.left;
+ contentRect.y = focusRect.y + focusInsets.top;
+
+ contentRect.width = focusRect.width - focusInsets.left - focusInsets.right;
+ contentRect.height = focusRect.height - focusInsets.top
+ - focusInsets.bottom;
+ }
+
+ /**
+ * Calculates the position of the thumbRect based on the current value of
+ * the slider. It must take into account the orientation of the slider.
+ */
+ protected void calculateThumbLocation()
+ {
+ int value = slider.getValue();
+
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ thumbRect.x = xPositionForValue(value) - thumbRect.width / 2;
+ thumbRect.y = trackRect.y + 1;
+ }
+ else
+ {
+ thumbRect.x = trackRect.x + 1;
+ thumbRect.y = yPositionForValue(value) - thumbRect.height / 2;
+ }
+ }
+
+ /**
+ * Calculates the gap size between the edge of the <code>contentRect</code>
+ * and the edge of the <code>trackRect</code>, storing the result in the
+ * <code>trackBuffer</code> field. Sufficient space needs to be reserved
+ * for the slider thumb and/or the labels at each end of the slider track.
+ */
+ protected void calculateTrackBuffer()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ int w = Math.max(getWidthOfLowValueLabel(), getWidthOfHighValueLabel());
+ trackBuffer = Math.max(thumbRect.width / 2, w / 2);
+
+ }
+ else
+ {
+ int h = Math.max(getHeightOfLowValueLabel(),
+ getHeightOfHighValueLabel());
+ trackBuffer = Math.max(thumbRect.height / 2, h / 2);
+ }
+ }
+
+ /**
+ * Returns the size of the slider's thumb. The size is hard coded to
+ * <code>11 x 20</code> for horizontal sliders, and <code>20 x 11</code> for
+ * vertical sliders. Note that a new instance of {@link Dimension} is
+ * returned for every call to this method (this seems wasteful, but
+ * {@link Dimension} instances are not immutable, so this is probably
+ * unavoidable).
+ *
+ * @return The size of the slider's thumb.
+ */
+ protected Dimension getThumbSize()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ return new Dimension(11, 20);
+ else
+ return new Dimension(20, 11);
+ }
+
+ /**
+ * Calculates the size and position of the trackRect. It must take into
+ * account the orientation of the slider.
+ */
+ protected void calculateTrackRect()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ int center = thumbRect.height;
+ if (slider.getPaintTicks())
+ center += getTickLength();
+ if (slider.getPaintLabels())
+ center += getHeightOfTallestLabel();
+ trackRect.x = contentRect.x + trackBuffer;
+ trackRect.y = contentRect.y + (contentRect.height - center - 1) / 2;
+ trackRect.width = contentRect.width - 2 * trackBuffer;
+ trackRect.height = thumbRect.height;
+ }
+ else
+ {
+ int center = thumbRect.width;
+ if (slider.getPaintTicks())
+ center += getTickLength();
+ if (slider.getPaintLabels())
+ center += getWidthOfWidestLabel();
+ trackRect.x = contentRect.x + (contentRect.width - center - 1) / 2;
+ trackRect.y = contentRect.y + trackBuffer;
+ trackRect.width = thumbRect.width;
+ trackRect.height = contentRect.height - 2 * trackBuffer;
+ }
+ }
+
+ /**
+ * This method returns the height of the tick area box if the slider is
+ * horizontal and the width of the tick area box is the slider is vertical.
+ * It not necessarily how long the ticks will be. If a gap between the edge
+ * of tick box and the actual tick is desired, then that will need to be
+ * handled in the tick painting methods.
+ *
+ * @return The height (or width if the slider is vertical) of the tick
+ * rectangle.
+ */
+ protected int getTickLength()
+ {
+ return 8;
+ }
+
+ /**
+ * This method calculates the size and position of the tickRect. It must
+ * take into account the orientation of the slider.
+ */
+ protected void calculateTickRect()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ tickRect.x = trackRect.x;
+ tickRect.y = trackRect.y + trackRect.height;
+ tickRect.width = trackRect.width;
+ tickRect.height = getTickLength();
+
+ // this makes our Mauve tests pass...can't explain it!
+ if (!slider.getPaintTicks())
+ {
+ tickRect.y--;
+ tickRect.height = 0;
+ }
+ }
+ else
+ {
+ tickRect.x = trackRect.x + trackRect.width;
+ tickRect.y = trackRect.y;
+ tickRect.width = getTickLength();
+ tickRect.height = trackRect.height;
+
+ // this makes our Mauve tests pass...can't explain it!
+ if (!slider.getPaintTicks())
+ {
+ tickRect.x--;
+ tickRect.width = 0;
+ }
+ }
+ }
+
+ /**
+ * Calculates the <code>labelRect</code> field, taking into account the
+ * orientation of the slider.
+ */
+ protected void calculateLabelRect()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ if (slider.getPaintLabels())
+ {
+ labelRect.x = tickRect.x - trackBuffer;
+ labelRect.y = tickRect.y + tickRect.height;
+ labelRect.width = tickRect.width + trackBuffer * 2;
+ labelRect.height = getHeightOfTallestLabel();
+ }
+ else
+ {
+ labelRect.x = tickRect.x;
+ labelRect.y = tickRect.y + tickRect.height;
+ labelRect.width = tickRect.width;
+ labelRect.height = 0;
+ }
+ }
+ else
+ {
+ if (slider.getPaintLabels())
+ {
+ labelRect.x = tickRect.x + tickRect.width;
+ labelRect.y = tickRect.y - trackBuffer;
+ labelRect.width = getWidthOfWidestLabel();
+ labelRect.height = tickRect.height + trackBuffer * 2;
+ }
+ else
+ {
+ labelRect.x = tickRect.x + tickRect.width;
+ labelRect.y = tickRect.y;
+ labelRect.width = 0;
+ labelRect.height = tickRect.height;
+ }
+ }
+ }
+
+ /**
+ * This method returns the width of the widest label in the slider's label
+ * table.
+ *
+ * @return The width of the widest label or 0 if no label table exists.
+ */
+ protected int getWidthOfWidestLabel()
+ {
+ int widest = 0;
+ Dictionary table = slider.getLabelTable();
+ if (table != null)
+ {
+ for (Enumeration list = slider.getLabelTable().elements();
+ list.hasMoreElements();)
+ {
+ Component label = (Component) list.nextElement();
+ widest = Math.max(label.getPreferredSize().width, widest);
+ }
+ }
+ return widest;
+ }
+
+ /**
+ * This method returns the height of the tallest label in the slider's label
+ * table.
+ *
+ * @return The height of the tallest label or 0 if no label table exists.
+ */
+ protected int getHeightOfTallestLabel()
+ {
+ int tallest = 0;
+ Component label;
+
+ if (slider.getLabelTable() == null)
+ return 0;
+ Dimension pref;
+ for (Enumeration list = slider.getLabelTable().elements();
+ list.hasMoreElements();)
+ {
+ Object comp = list.nextElement();
+ if (! (comp instanceof Component))
+ continue;
+ label = (Component) comp;
+ pref = label.getPreferredSize();
+ if (pref != null && pref.height > tallest)
+ tallest = pref.height;
+ }
+ return tallest;
+ }
+
+ /**
+ * Returns the width of the label whose key has the highest value, or 0 if
+ * there are no labels.
+ *
+ * @return The width of the label whose key has the highest value.
+ *
+ * @see #getHighestValueLabel()
+ */
+ protected int getWidthOfHighValueLabel()
+ {
+ Component highValueLabel = getHighestValueLabel();
+ if (highValueLabel != null)
+ return highValueLabel.getPreferredSize().width;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns the width of the label whose key has the lowest value, or 0 if
+ * there are no labels.
+ *
+ * @return The width of the label whose key has the lowest value.
+ *
+ * @see #getLowestValueLabel()
+ */
+ protected int getWidthOfLowValueLabel()
+ {
+ Component lowValueLabel = getLowestValueLabel();
+ if (lowValueLabel != null)
+ return lowValueLabel.getPreferredSize().width;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns the height of the label whose key has the highest value, or 0 if
+ * there are no labels.
+ *
+ * @return The height of the high value label or 0 if no label table exists.
+ */
+ protected int getHeightOfHighValueLabel()
+ {
+ Component highValueLabel = getHighestValueLabel();
+ if (highValueLabel != null)
+ return highValueLabel.getPreferredSize().height;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns the height of the label whose key has the lowest value, or 0 if
+ * there are no labels.
+ *
+ * @return The height of the low value label or 0 if no label table exists.
+ */
+ protected int getHeightOfLowValueLabel()
+ {
+ Component lowValueLabel = getLowestValueLabel();
+ if (lowValueLabel != null)
+ return lowValueLabel.getPreferredSize().height;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns <code>true</code> if the slider scale is to be drawn inverted,
+ * and <code>false</code> if not.
+ *
+ * @return <code>true</code> if the slider is to be drawn inverted.
+ */
+ protected boolean drawInverted()
+ {
+ return slider.getInverted();
+ }
+
+ /**
+ * This method returns the label whose key has the lowest value.
+ *
+ * @return The low value label or null if no label table exists.
+ */
+ protected Component getLowestValueLabel()
+ {
+ Integer key = new Integer(Integer.MAX_VALUE);
+ Integer tmpKey;
+ Dictionary labelTable = slider.getLabelTable();
+
+ if (labelTable == null)
+ return null;
+
+ for (Enumeration list = labelTable.keys(); list.hasMoreElements();)
+ {
+ Object value = list.nextElement();
+ if (! (value instanceof Integer))
+ continue;
+ tmpKey = (Integer) value;
+ if (tmpKey.intValue() < key.intValue())
+ key = tmpKey;
+ }
+ Object comp = labelTable.get(key);
+ if (! (comp instanceof Component))
+ return null;
+ return (Component) comp;
+ }
+
+ /**
+ * Returns the label whose key has the highest value.
+ *
+ * @return The label whose key has the highest value or <code>null</code> if
+ * no label table exists.
+ */
+ protected Component getHighestValueLabel()
+ {
+ Integer key = new Integer(Integer.MIN_VALUE);
+ Integer tmpKey;
+ Dictionary labelTable = slider.getLabelTable();
+
+ if (labelTable == null)
+ return null;
+
+ for (Enumeration list = labelTable.keys(); list.hasMoreElements();)
+ {
+ Object value = list.nextElement();
+ if (! (value instanceof Integer))
+ continue;
+ tmpKey = (Integer) value;
+ if (tmpKey.intValue() > key.intValue())
+ key = tmpKey;
+ }
+ Object comp = labelTable.get(key);
+ if (! (comp instanceof Component))
+ return null;
+ return (Component) comp;
+ }
+
+ /**
+ * This method is used to paint the {@link JSlider}. It delegates all its
+ * duties to the various paint methods like paintTicks(), paintTrack(),
+ * paintThumb(), etc.
+ *
+ * @param g The {@link Graphics} object to paint with.
+ * @param c The {@link JComponent} that is being painted.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ recalculateIfInsetsChanged();
+ recalculateIfOrientationChanged();
+ if (slider.getPaintTrack() && hitClip(g, trackRect))
+ paintTrack(g);
+ if (slider.getPaintTicks() && hitClip(g, tickRect))
+ paintTicks(g);
+ if (slider.getPaintLabels() && hitClip(g, labelRect))
+ paintLabels(g);
+ if (slider.hasFocus() && hitClip(g, focusRect))
+ paintFocus(g);
+ if (hitClip(g, thumbRect))
+ paintThumb(g);
+ }
+
+ /**
+ * This method recalculates any rectangles that need to be recalculated
+ * after the insets of the component have changed.
+ */
+ protected void recalculateIfInsetsChanged()
+ {
+ Insets insets = slider.getInsets();
+ if (! insets.equals(insetCache))
+ {
+ insetCache = insets;
+ calculateGeometry();
+ }
+ }
+
+ /**
+ * This method recalculates any rectangles that need to be recalculated
+ * after the orientation of the slider changes.
+ */
+ protected void recalculateIfOrientationChanged()
+ {
+ // Examining a test program shows that either Sun calls private
+ // methods that we don't know about, or these don't do anything.
+ calculateThumbSize();
+ calculateTrackBuffer();
+ calculateTrackRect();
+ calculateThumbLocation();
+
+ calculateTickRect();
+ calculateLabelRect();
+ }
+
+ /**
+ * This method is called during a repaint if the slider has focus. It draws
+ * an outline of the focusRect using the color returned by
+ * getFocusColor().
+ *
+ * @param g The {@link Graphics} object to draw with.
+ */
+ public void paintFocus(Graphics g)
+ {
+ Color saved_color = g.getColor();
+
+ g.setColor(getFocusColor());
+
+ g.drawRect(focusRect.x, focusRect.y, focusRect.width, focusRect.height);
+
+ g.setColor(saved_color);
+ }
+
+ /**
+ * <p>
+ * This method is called during a repaint if the track is to be drawn. It
+ * draws a 3D rectangle to represent the track. The track is not the size
+ * of the trackRect. The top and left edges of the track should be outlined
+ * with the shadow color. The bottom and right edges should be outlined
+ * with the highlight color.
+ * </p>
+ * <pre>
+ * a---d
+ * | |
+ * | | a------------------------d
+ * | | | |
+ * | | b------------------------c
+ * | |
+ * | |
+ * b---c
+ * </pre>
+ *
+ * <p>
+ * The b-a-d path needs to be drawn with the shadow color and the b-c-d path
+ * needs to be drawn with the highlight color.
+ * </p>
+ *
+ * @param g The {@link Graphics} object to draw with.
+ */
+ public void paintTrack(Graphics g)
+ {
+ Color saved_color = g.getColor();
+ int width;
+ int height;
+
+ Point a = new Point(trackRect.x, trackRect.y + 1);
+ Point b = new Point(a);
+ Point c = new Point(a);
+ Point d = new Point(a);
+
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ width = trackRect.width;
+ height = (thumbRect.height / 4 == 0) ? 1 : thumbRect.height / 4;
+
+ a.translate(0, (trackRect.height / 2) - (height / 2));
+ b.translate(0, (trackRect.height / 2) + (height / 2));
+ c.translate(trackRect.width, (trackRect.height / 2) + (height / 2));
+ d.translate(trackRect.width, (trackRect.height / 2) - (height / 2));
+ }
+ else
+ {
+ width = (thumbRect.width / 4 == 0) ? 1 : thumbRect.width / 4;
+ height = trackRect.height;
+
+ a.translate((trackRect.width / 2) - (width / 2), 0);
+ b.translate((trackRect.width / 2) - (width / 2), trackRect.height);
+ c.translate((trackRect.width / 2) + (width / 2), trackRect.height);
+ d.translate((trackRect.width / 2) + (width / 2), 0);
+ }
+ g.setColor(Color.GRAY);
+ g.fillRect(a.x, a.y, width, height);
+
+ g.setColor(getHighlightColor());
+ g.drawLine(b.x, b.y, c.x, c.y);
+ g.drawLine(c.x, c.y, d.x, d.y);
+
+ g.setColor(getShadowColor());
+ g.drawLine(b.x, b.y, a.x, a.y);
+ g.drawLine(a.x, a.y, d.x, d.y);
+
+ g.setColor(saved_color);
+ }
+
+ /**
+ * This method is called during a repaint if the ticks are to be drawn. This
+ * method must still verify that the majorTickSpacing and minorTickSpacing
+ * are greater than zero before drawing the ticks.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ */
+ public void paintTicks(Graphics g)
+ {
+ int max = slider.getMaximum();
+ int min = slider.getMinimum();
+ int majorSpace = slider.getMajorTickSpacing();
+ int minorSpace = slider.getMinorTickSpacing();
+
+ if (majorSpace > 0)
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ g.translate(0, tickRect.y);
+ for (int i = min; i <= max; i += majorSpace)
+ paintMajorTickForHorizSlider(g, tickRect, xPositionForValue(i));
+ g.translate(0, -tickRect.y);
+ }
+ else // JSlider.VERTICAL
+ {
+ g.translate(tickRect.x, 0);
+ for (int i = min; i <= max; i += majorSpace)
+ paintMajorTickForVertSlider(g, tickRect, yPositionForValue(i));
+ g.translate(-tickRect.x, 0);
+ }
+ }
+ if (minorSpace > 0)
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ g.translate(0, tickRect.y);
+ for (int i = min; i <= max; i += minorSpace)
+ paintMinorTickForHorizSlider(g, tickRect, xPositionForValue(i));
+ g.translate(0, -tickRect.y);
+ }
+ else
+ {
+ g.translate(tickRect.x, 0);
+ for (int i = min; i <= max; i += minorSpace)
+ paintMinorTickForVertSlider(g, tickRect, yPositionForValue(i));
+ g.translate(-tickRect.x, 0);
+ }
+ }
+ }
+
+ /* Minor ticks start at 1/4 of the height (or width) of the tickRect and
+ extend to 1/2 of the tickRect.
+
+ Major ticks start at 1/4 of the height and extend to 3/4.
+ */
+
+ /**
+ * This method paints a minor tick for a horizontal slider at the given x
+ * value. x represents the x coordinate to paint at.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param tickBounds The tickRect rectangle.
+ * @param x The x coordinate to draw the tick at.
+ */
+ protected void paintMinorTickForHorizSlider(Graphics g,
+ Rectangle tickBounds, int x)
+ {
+ int y = tickRect.height / 4;
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+
+ g.drawLine(x, y, x, y + tickRect.height / 4);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints a major tick for a horizontal slider at the given x
+ * value. x represents the x coordinate to paint at.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param tickBounds The tickRect rectangle.
+ * @param x The x coordinate to draw the tick at.
+ */
+ protected void paintMajorTickForHorizSlider(Graphics g,
+ Rectangle tickBounds, int x)
+ {
+ int y = tickRect.height / 4;
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+
+ g.drawLine(x, y, x, y + tickRect.height / 2);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints a minor tick for a vertical slider at the given y
+ * value. y represents the y coordinate to paint at.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param tickBounds The tickRect rectangle.
+ * @param y The y coordinate to draw the tick at.
+ */
+ protected void paintMinorTickForVertSlider(Graphics g, Rectangle tickBounds,
+ int y)
+ {
+ int x = tickRect.width / 4;
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+
+ g.drawLine(x, y, x + tickRect.width / 4, y);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints a major tick for a vertical slider at the given y
+ * value. y represents the y coordinate to paint at.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param tickBounds The tickRect rectangle.
+ * @param y The y coordinate to draw the tick at.
+ */
+ protected void paintMajorTickForVertSlider(Graphics g, Rectangle tickBounds,
+ int y)
+ {
+ int x = tickRect.width / 4;
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+
+ g.drawLine(x, y, x + tickRect.width / 2, y);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints all the labels from the slider's label table. This
+ * method must make sure that the label table is not null before painting
+ * the labels. Each entry in the label table is a (integer, component)
+ * pair. Every label is painted at the value of the integer.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ */
+ public void paintLabels(Graphics g)
+ {
+ Dictionary table = slider.getLabelTable();
+ if (table != null)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ for (Enumeration list = table.keys(); list.hasMoreElements();)
+ {
+ Integer key = (Integer) list.nextElement();
+ int value = key.intValue();
+ if (value >= min && value <= max)
+ {
+ Component label = (Component) table.get(key);
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ g.translate(0, labelRect.y);
+ paintHorizontalLabel(g, value, label);
+ g.translate(0, -labelRect.y);
+ }
+ else
+ {
+ g.translate(labelRect.x, 0);
+ paintVerticalLabel(g, value, label);
+ g.translate(-labelRect.x, 0);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This method paints the label on the horizontal slider at the value
+ * specified. The value is not a coordinate. It is a value within the range
+ * of the slider. If the value is not within the range of the slider, this
+ * method will do nothing. This method should not paint outside the
+ * boundaries of the labelRect.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param value The value to paint at.
+ * @param label The label to paint.
+ */
+ protected void paintHorizontalLabel(Graphics g, int value, Component label)
+ {
+ int center = xPositionForValue(value);
+ int left = center - label.getPreferredSize().width / 2;
+ g.translate(left, 0);
+ label.paint(g);
+ g.translate(-left, 0);
+ }
+
+ /**
+ * This method paints the label on the vertical slider at the value
+ * specified. The value is not a coordinate. It is a value within the range
+ * of the slider. If the value is not within the range of the slider, this
+ * method will do nothing. This method should not paint outside the
+ * boundaries of the labelRect.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param value The value to paint at.
+ * @param label The label to paint.
+ */
+ protected void paintVerticalLabel(Graphics g, int value, Component label)
+ {
+ int center = yPositionForValue(value);
+ int top = center - label.getPreferredSize().height / 2;
+ g.translate(0, top);
+ label.paint(g);
+ g.translate(0, -top);
+ }
+
+ /**
+ * <p>
+ * This method paints a thumb. There are two types of thumb:
+ * </p>
+ * <pre>
+ * Vertical Horizontal
+ * a---b a-----b
+ * | | | \
+ * e c | c
+ * \ / | /
+ * d e-----d
+ * </pre>
+ *
+ * <p>
+ * In the case of vertical thumbs, we highlight the path b-a-e-d and shadow
+ * the path b-c-d. In the case of horizontal thumbs, we highlight the path
+ * c-b-a-e and shadow the path c-d-e. In both cases we fill the path
+ * a-b-c-d-e before shadows and highlights are drawn.
+ * </p>
+ *
+ * @param g The graphics object to paint with
+ */
+ public void paintThumb(Graphics g)
+ {
+ Color saved_color = g.getColor();
+
+ Point a = new Point(thumbRect.x, thumbRect.y);
+ Point b = new Point(a);
+ Point c = new Point(a);
+ Point d = new Point(a);
+ Point e = new Point(a);
+
+ Polygon bright;
+ Polygon light; // light shadow
+ Polygon dark; // dark shadow
+ Polygon all;
+
+ // This will be in X-dimension if the slider is inverted and y if it isn't.
+ int turnPoint;
+
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ turnPoint = thumbRect.height * 3 / 4;
+
+ b.translate(thumbRect.width - 1, 0);
+ c.translate(thumbRect.width - 1, turnPoint);
+ d.translate(thumbRect.width / 2 - 1, thumbRect.height - 1);
+ e.translate(0, turnPoint);
+
+ bright = new Polygon(new int[] { b.x - 1, a.x, e.x, d.x },
+ new int[] { b.y, a.y, e.y, d.y }, 4);
+
+ dark = new Polygon(new int[] { b.x, c.x, d.x + 1 }, new int[] { b.y,
+ c.y - 1,
+ d.y }, 3);
+
+ light = new Polygon(new int[] { b.x - 1, c.x - 1, d.x + 1 },
+ new int[] { b.y + 1, c.y - 1, d.y - 1 }, 3);
+
+ all = new Polygon(
+ new int[] { a.x + 1, b.x - 2, c.x - 2, d.x, e.x + 1 },
+ new int[] { a.y + 1, b.y + 1, c.y - 1, d.y - 1, e.y },
+ 5);
+ }
+ else
+ {
+ turnPoint = thumbRect.width * 3 / 4 - 1;
+
+ b.translate(turnPoint, 0);
+ c.translate(thumbRect.width - 1, thumbRect.height / 2);
+ d.translate(turnPoint, thumbRect.height - 1);
+ e.translate(0, thumbRect.height - 1);
+
+ bright = new Polygon(new int[] { c.x - 1, b.x, a.x, e.x },
+ new int[] { c.y - 1, b.y, a.y, e.y - 1 }, 4);
+
+ dark = new Polygon(new int[] { c.x, d.x, e.x }, new int[] { c.y, d.y,
+ e.y }, 3);
+
+ light = new Polygon(new int[] { c.x - 1, d.x, e.x + 1 },
+ new int[] { c.y, d.y - 1, e.y - 1 }, 3);
+ all = new Polygon(new int[] { a.x + 1, b.x, c.x - 2, c.x - 2, d.x,
+ e.x + 1 }, new int[] { a.y + 1, b.y + 1,
+ c.y - 1, c.y,
+ d.y - 2, e.y - 2 },
+ 6);
+ }
+
+ g.setColor(Color.WHITE);
+ g.drawPolyline(bright.xpoints, bright.ypoints, bright.npoints);
+
+ g.setColor(Color.BLACK);
+ g.drawPolyline(dark.xpoints, dark.ypoints, dark.npoints);
+
+ g.setColor(Color.GRAY);
+ g.drawPolyline(light.xpoints, light.ypoints, light.npoints);
+
+ g.setColor(Color.LIGHT_GRAY);
+ g.drawPolyline(all.xpoints, all.ypoints, all.npoints);
+ g.fillPolygon(all);
+
+ g.setColor(saved_color);
+ }
+
+ /**
+ * This method sets the position of the thumbRect.
+ *
+ * @param x The new x position.
+ * @param y The new y position.
+ */
+ public void setThumbLocation(int x, int y)
+ {
+ Rectangle union = new Rectangle(thumbRect);
+ thumbRect.setLocation(x, y);
+ SwingUtilities.computeUnion(thumbRect.x, thumbRect.y, thumbRect.width,
+ thumbRect.height, union);
+ slider.repaint(union);
+ }
+
+ /**
+ * Moves the thumb one block in the direction specified (a block is 1/10th
+ * of the slider range). If the slider snaps to ticks, this method is
+ * responsible for snapping it to a tick after the thumb has been moved.
+ *
+ * @param direction the direction (positive values increment the thumb
+ * position by one block, zero/negative values decrement the thumb position
+ * by one block).
+ */
+ public void scrollByBlock(int direction)
+ {
+ int unit = (slider.getMaximum() - slider.getMinimum()) / 10;
+ int moveTo = slider.getValue();
+ if (direction > 0)
+ moveTo += unit;
+ else
+ moveTo -= unit;
+
+ if (slider.getSnapToTicks())
+ moveTo = findClosestTick(moveTo);
+
+ slider.setValue(moveTo);
+ }
+
+ /**
+ * Moves the thumb one unit in the specified direction. If the slider snaps
+ * to ticks, this method is responsible for snapping it to a tick after the
+ * thumb has been moved.
+ *
+ * @param direction the direction (positive values increment the thumb
+ * position by one, zero/negative values decrement the thumb position by
+ * one).
+ */
+ public void scrollByUnit(int direction)
+ {
+ int moveTo = slider.getValue();
+ if (direction > 0)
+ moveTo++;
+ else
+ moveTo--;
+
+ if (slider.getSnapToTicks())
+ moveTo = findClosestTick(moveTo);
+
+ slider.setValue(moveTo);
+ }
+
+ /**
+ * This method is called when there has been a click in the track and the
+ * thumb needs to be scrolled on regular intervals. This method is only
+ * responsible for starting the timer and not for stopping it.
+ *
+ * @param dir The direction to move in.
+ */
+ protected void scrollDueToClickInTrack(int dir)
+ {
+ scrollTimer.stop();
+
+ scrollListener.setDirection(dir);
+ scrollListener.setScrollByBlock(true);
+
+ scrollTimer.start();
+ }
+
+ /**
+ * Returns the x-coordinate (relative to the component) for the given slider
+ * value. This method assumes that the <code>trackRect</code> field is
+ * set up.
+ *
+ * @param value the slider value.
+ *
+ * @return The x-coordinate.
+ */
+ protected int xPositionForValue(int value)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int len = trackRect.width;
+ double range = max - min;
+ double pixPerVal = len / range;
+ int left = trackRect.x;
+ int right = left + trackRect.width - 1;
+ int xpos;
+ if (! drawInverted())
+ xpos = left + (int) Math.round(pixPerVal * ((double) value - min));
+ else
+ xpos = right - (int) Math.round(pixPerVal * ((double) value - min));
+ xpos = Math.max(left, xpos);
+ xpos = Math.min(right, xpos);
+ return xpos;
+ }
+
+ /**
+ * Returns the y-coordinate (relative to the component) for the given slider
+ * value. This method assumes that the <code>trackRect</code> field is
+ * set up.
+ *
+ * @param value the slider value.
+ *
+ * @return The y-coordinate.
+ */
+ protected int yPositionForValue(int value)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int len = trackRect.height;
+ double range = max - min;
+ double pixPerVal = len / range;
+ int top = trackRect.y;
+ int bottom = top + trackRect.height - 1;
+ int ypos;
+ if (! drawInverted())
+ ypos = top + (int) Math.round(pixPerVal * ((double) max - value));
+ else
+ ypos = top + (int) Math.round(pixPerVal * ((double) value - min));
+ ypos = Math.max(top, ypos);
+ ypos = Math.min(bottom, ypos);
+ return ypos;
+ }
+
+ /**
+ * This method returns the value in the slider's range given the y
+ * coordinate. If the value is out of range, it will return the closest
+ * legal value.
+ *
+ * @param yPos The y coordinate to calculate a value for.
+ *
+ * @return The value for the y coordinate.
+ */
+ public int valueForYPosition(int yPos)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int len = trackRect.height;
+
+ int value;
+
+ // If the length is 0, you shouldn't be able to even see where the slider
+ // is. This really shouldn't ever happen, but just in case, we'll return
+ // the middle.
+ if (len == 0)
+ return (max - min) / 2;
+
+ if (! drawInverted())
+ value = (len - (yPos - trackRect.y)) * (max - min) / len + min;
+ else
+ value = (yPos - trackRect.y) * (max - min) / len + min;
+
+ // If this isn't a legal value, then we'll have to move to one now.
+ if (value > max)
+ value = max;
+ else if (value < min)
+ value = min;
+ return value;
+ }
+
+ /**
+ * This method returns the value in the slider's range given the x
+ * coordinate. If the value is out of range, it will return the closest
+ * legal value.
+ *
+ * @param xPos The x coordinate to calculate a value for.
+ *
+ * @return The value for the x coordinate.
+ */
+ public int valueForXPosition(int xPos)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int len = trackRect.width;
+
+ int value;
+
+ // If the length is 0, you shouldn't be able to even see where the slider
+ // is. This really shouldn't ever happen, but just in case, we'll return
+ // the middle.
+ if (len == 0)
+ return (max - min) / 2;
+
+ if (! drawInverted())
+ value = (xPos - trackRect.x) * (max - min) / len + min;
+ else
+ value = (len - (xPos - trackRect.x)) * (max - min) / len + min;
+
+ // If this isn't a legal value, then we'll have to move to one now.
+ if (value > max)
+ value = max;
+ else if (value < min)
+ value = min;
+ return value;
+ }
+
+ /**
+ * This method finds the closest value that has a tick associated with it.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param value The value to search from.
+ *
+ * @return The closest value that has a tick associated with it.
+ */
+ int findClosestTick(int value)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int majorSpace = slider.getMajorTickSpacing();
+ int minorSpace = slider.getMinorTickSpacing();
+
+ // The default value to return is value + minor or
+ // value + major.
+ // Initializing at min - value leaves us with a default
+ // return value of min, which always has tick marks
+ // (if ticks are painted).
+ int minor = min - value;
+ int major = min - value;
+
+ // If there are no major tick marks or minor tick marks
+ // e.g. snap is set to true but no ticks are set, then
+ // we can just return the value.
+ if (majorSpace <= 0 && minorSpace <= 0)
+ return value;
+
+ // First check the major ticks.
+ if (majorSpace > 0)
+ {
+ int lowerBound = (value - min) / majorSpace;
+ int majLower = majorSpace * lowerBound + min;
+ int majHigher = majorSpace * (lowerBound + 1) + min;
+
+ if (majHigher <= max && majHigher - value <= value - majLower)
+ major = majHigher - value;
+ else
+ major = majLower - value;
+ }
+
+ if (minorSpace > 0)
+ {
+ int lowerBound = value / minorSpace;
+ int minLower = minorSpace * lowerBound;
+ int minHigher = minorSpace * (lowerBound + 1);
+
+ if (minHigher <= max && minHigher - value <= value - minLower)
+ minor = minHigher - value;
+ else
+ minor = minLower - value;
+ }
+
+ // Give preference to minor ticks
+ if (Math.abs(minor) > Math.abs(major))
+ return value + major;
+ else
+ return value + minor;
+ }
+
+ InputMap getInputMap(int condition)
+ {
+ if (condition == JComponent.WHEN_FOCUSED)
+ return (InputMap) UIManager.get("Slider.focusInputMap");
+ return null;
+ }
+
+ /**
+ * Returns the action map for the {@link JSlider}. All sliders share
+ * a single action map which is created the first time this method is
+ * called, then stored in the UIDefaults table for subsequent access.
+ *
+ * @return The shared action map.
+ */
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("Slider.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("Slider.actionMap", map);
+ }
+ return map;
+ }
+
+ /**
+ * Creates the action map shared by all {@link JSlider} instances.
+ * This method is called once by {@link #getActionMap()} when it
+ * finds no action map in the UIDefaults table...after the map is
+ * created, it gets added to the defaults table so that subsequent
+ * calls to {@link #getActionMap()} will return the same shared
+ * instance.
+ *
+ * @return The action map.
+ */
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+ map.put("positiveUnitIncrement",
+ new AbstractAction("positiveUnitIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ BasicSliderUI ui = (BasicSliderUI) slider.getUI();
+ if (slider.getInverted())
+ ui.scrollByUnit(BasicSliderUI.NEGATIVE_SCROLL);
+ else
+ ui.scrollByUnit(BasicSliderUI.POSITIVE_SCROLL);
+ }
+ }
+ );
+ map.put("negativeUnitIncrement",
+ new AbstractAction("negativeUnitIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ BasicSliderUI ui = (BasicSliderUI) slider.getUI();
+ if (slider.getInverted())
+ ui.scrollByUnit(BasicSliderUI.POSITIVE_SCROLL);
+ else
+ ui.scrollByUnit(BasicSliderUI.NEGATIVE_SCROLL);
+ }
+ }
+ );
+ map.put("positiveBlockIncrement",
+ new AbstractAction("positiveBlockIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ BasicSliderUI ui = (BasicSliderUI) slider.getUI();
+ if (slider.getInverted())
+ ui.scrollByBlock(BasicSliderUI.NEGATIVE_SCROLL);
+ else
+ ui.scrollByBlock(BasicSliderUI.POSITIVE_SCROLL);
+ }
+ }
+ );
+ map.put("negativeBlockIncrement",
+ new AbstractAction("negativeBlockIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ BasicSliderUI ui = (BasicSliderUI) slider.getUI();
+ if (slider.getInverted())
+ ui.scrollByBlock(BasicSliderUI.POSITIVE_SCROLL);
+ else
+ ui.scrollByBlock(BasicSliderUI.NEGATIVE_SCROLL);
+ }
+ }
+ );
+ map.put("minScroll",
+ new AbstractAction("minScroll") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ if (slider.getInverted())
+ slider.setValue(slider.getMaximum());
+ else
+ slider.setValue(slider.getMinimum());
+ }
+ }
+ );
+ map.put("maxScroll",
+ new AbstractAction("maxScroll") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ if (slider.getInverted())
+ slider.setValue(slider.getMinimum());
+ else
+ slider.setValue(slider.getMaximum());
+ }
+ }
+ );
+ return map;
+ }
+
+ /**
+ * Small utility method to save me from typing the hell out of myself in
+ * paint().
+ */
+ private boolean hitClip(Graphics g, Rectangle r)
+ {
+ return g.hitClip(r.x, r.y, r.width, r.height);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java
new file mode 100644
index 000000000..00c8537cc
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java
@@ -0,0 +1,561 @@
+/* BasicSpinnerUI.java --
+ Copyright (C) 2003, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JSpinner;
+import javax.swing.LookAndFeel;
+import javax.swing.Timer;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SpinnerUI;
+
+/**
+ * A UI delegate for the {@link JSpinner} component.
+ *
+ * @author Ka-Hing Cheung
+ *
+ * @since 1.4
+ */
+public class BasicSpinnerUI extends SpinnerUI
+{
+ /**
+ * Creates a new <code>BasicSpinnerUI</code> for the specified
+ * <code>JComponent</code>
+ *
+ * @param c the component (ignored).
+ *
+ * @return A new instance of {@link BasicSpinnerUI}.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicSpinnerUI();
+ }
+
+ /**
+ * Creates an editor component. Really, it just returns
+ * <code>JSpinner.getEditor()</code>
+ *
+ * @return a JComponent as an editor
+ *
+ * @see javax.swing.JSpinner#getEditor
+ */
+ protected JComponent createEditor()
+ {
+ return spinner.getEditor();
+ }
+
+ /**
+ * Creates a <code>LayoutManager</code> that layouts the sub components. The
+ * subcomponents are identifies by the constraint "Next", "Previous" and
+ * "Editor"
+ *
+ * @return a LayoutManager
+ *
+ * @see java.awt.LayoutManager
+ */
+ protected LayoutManager createLayout()
+ {
+ return new DefaultLayoutManager();
+ }
+
+ /**
+ * Creates the "Next" button
+ *
+ * @return the next button component
+ */
+ protected Component createNextButton()
+ {
+ JButton button = new BasicArrowButton(BasicArrowButton.NORTH);
+ return button;
+ }
+
+ /**
+ * Creates the "Previous" button
+ *
+ * @return the previous button component
+ */
+ protected Component createPreviousButton()
+ {
+ JButton button = new BasicArrowButton(BasicArrowButton.SOUTH);
+ return button;
+ }
+
+ /**
+ * Creates the <code>PropertyChangeListener</code> that will be attached by
+ * <code>installListeners</code>. It should watch for the "editor"
+ * property, when it's changed, replace the old editor with the new one,
+ * probably by calling <code>replaceEditor</code>
+ *
+ * @return a PropertyChangeListener
+ *
+ * @see #replaceEditor
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ // FIXME: Add check for enabled property change. Need to
+ // disable the buttons.
+ if ("editor".equals(event.getPropertyName()))
+ BasicSpinnerUI.this.replaceEditor((JComponent) event.getOldValue(),
+ (JComponent) event.getNewValue());
+ // FIXME: Handle 'font' property change
+ }
+ };
+ }
+
+ /**
+ * Called by <code>installUI</code>. This should set various defaults
+ * obtained from <code>UIManager.getLookAndFeelDefaults</code>, as well as
+ * set the layout obtained from <code>createLayout</code>
+ *
+ * @see javax.swing.UIManager#getLookAndFeelDefaults
+ * @see #createLayout
+ * @see #installUI
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(spinner, "Spinner.background",
+ "Spinner.foreground", "Spinner.font");
+ LookAndFeel.installBorder(spinner, "Spinner.border");
+ JComponent e = spinner.getEditor();
+ if (e instanceof JSpinner.DefaultEditor)
+ {
+ JSpinner.DefaultEditor de = (JSpinner.DefaultEditor) e;
+ de.getTextField().setBorder(null);
+ }
+ spinner.setLayout(createLayout());
+ spinner.setOpaque(true);
+ }
+
+ /*
+ * Called by <code>installUI</code>, which basically adds the
+ * <code>PropertyChangeListener</code> created by
+ * <code>createPropertyChangeListener</code>
+ *
+ * @see #createPropertyChangeListener
+ * @see #installUI
+ */
+ protected void installListeners()
+ {
+ spinner.addPropertyChangeListener(listener);
+ }
+
+ /*
+ * Install listeners to the next button so that it increments the model
+ */
+ protected void installNextButtonListeners(Component c)
+ {
+ c.addMouseListener(new MouseAdapter()
+ {
+ public void mousePressed(MouseEvent evt)
+ {
+ if (! spinner.isEnabled())
+ return;
+ increment();
+ timer.setInitialDelay(500);
+ timer.start();
+ }
+
+ public void mouseReleased(MouseEvent evt)
+ {
+ timer.stop();
+ }
+
+ void increment()
+ {
+ Object next = BasicSpinnerUI.this.spinner.getNextValue();
+ if (next != null)
+ BasicSpinnerUI.this.spinner.getModel().setValue(next);
+ }
+
+ volatile boolean mouseDown;
+ Timer timer = new Timer(50,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ increment();
+ }
+ });
+ });
+ }
+
+ /*
+ * Install listeners to the previous button so that it decrements the model
+ */
+ protected void installPreviousButtonListeners(Component c)
+ {
+ c.addMouseListener(new MouseAdapter()
+ {
+ public void mousePressed(MouseEvent evt)
+ {
+ if (! spinner.isEnabled())
+ return;
+ decrement();
+ timer.setInitialDelay(500);
+ timer.start();
+ }
+
+ public void mouseReleased(MouseEvent evt)
+ {
+ timer.stop();
+ }
+
+ void decrement()
+ {
+ Object prev = BasicSpinnerUI.this.spinner.getPreviousValue();
+ if (prev != null)
+ BasicSpinnerUI.this.spinner.getModel().setValue(prev);
+ }
+
+ volatile boolean mouseDown;
+ Timer timer = new Timer(50,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ decrement();
+ }
+ });
+ });
+ }
+
+ /**
+ * Install this UI to the <code>JComponent</code>, which in reality, is a
+ * <code>JSpinner</code>. Calls <code>installDefaults</code>,
+ * <code>installListeners</code>, and also adds the buttons and editor.
+ *
+ * @param c DOCUMENT ME!
+ *
+ * @see #installDefaults
+ * @see #installListeners
+ * @see #createNextButton
+ * @see #createPreviousButton
+ * @see #createEditor
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ spinner = (JSpinner) c;
+
+ installDefaults();
+ installListeners();
+
+ Component next = createNextButton();
+ Component previous = createPreviousButton();
+
+ installNextButtonListeners(next);
+ installPreviousButtonListeners(previous);
+
+ c.add(createEditor(), "Editor");
+ c.add(next, "Next");
+ c.add(previous, "Previous");
+ }
+
+ /**
+ * Replace the old editor with the new one
+ *
+ * @param oldEditor the old editor
+ * @param newEditor the new one to replace with
+ */
+ protected void replaceEditor(JComponent oldEditor, JComponent newEditor)
+ {
+ spinner.remove(oldEditor);
+ spinner.add(newEditor);
+ }
+
+ /**
+ * The reverse of <code>installDefaults</code>. Called by
+ * <code>uninstallUI</code>
+ */
+ protected void uninstallDefaults()
+ {
+ spinner.setLayout(null);
+ }
+
+ /**
+ * The reverse of <code>installListeners</code>, called by
+ * <code>uninstallUI</code>
+ */
+ protected void uninstallListeners()
+ {
+ spinner.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Called when the current L&F is replaced with another one, should call
+ * <code>uninstallDefaults</code> and <code>uninstallListeners</code> as
+ * well as remove the next/previous buttons and the editor
+ *
+ * @param c DOCUMENT ME!
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+
+ uninstallDefaults();
+ uninstallListeners();
+ c.removeAll();
+ }
+
+ /** The spinner for this UI */
+ protected JSpinner spinner;
+
+ /** DOCUMENT ME! */
+ private PropertyChangeListener listener = createPropertyChangeListener();
+
+ /**
+ * A layout manager for the {@link JSpinner} component. The spinner has
+ * three subcomponents: an editor, a 'next' button and a 'previous' button.
+ */
+ private class DefaultLayoutManager implements LayoutManager
+ {
+ /**
+ * Layout the spinners inner parts.
+ *
+ * @param parent The parent container
+ */
+ public void layoutContainer(Container parent)
+ {
+ synchronized (parent.getTreeLock())
+ {
+ Insets i = parent.getInsets();
+ boolean l2r = parent.getComponentOrientation().isLeftToRight();
+ /*
+ -------------- --------------
+ | | n | | n | |
+ | e | - | or | - | e |
+ | | p | | p | |
+ -------------- --------------
+ */
+ Dimension e = prefSize(editor);
+ Dimension n = prefSize(next);
+ Dimension p = prefSize(previous);
+ Dimension s = parent.getSize();
+
+ int x = l2r ? i.left : i.right;
+ int y = i.top;
+ int w = Math.max(p.width, n.width);
+ int h = (s.height - i.bottom) / 2;
+ int e_width = s.width - w - i.left - i.right;
+
+ if (l2r)
+ {
+ setBounds(editor, x, y, e_width, 2 * h);
+ x += e_width;
+ setBounds(next, x, y, w, h);
+ y += h;
+ setBounds(previous, x, y, w, h);
+ }
+ else
+ {
+ setBounds(next, x, y + (s.height - e.height) / 2, w, h);
+ y += h;
+ setBounds(previous, x, y + (s.height - e.height) / 2, w, h);
+ x += w;
+ y -= h;
+ setBounds(editor, x, y, e_width, e.height);
+ }
+ }
+ }
+
+ /**
+ * Calculates the minimum layout size.
+ *
+ * @param parent the parent.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ Dimension d = new Dimension();
+
+ if (editor != null)
+ {
+ Dimension tmp = editor.getMinimumSize();
+ d.width += tmp.width;
+ d.height = tmp.height;
+ }
+
+ int nextWidth = 0;
+ int previousWidth = 0;
+
+ if (next != null)
+ {
+ Dimension tmp = next.getMinimumSize();
+ nextWidth = tmp.width;
+ }
+ if (previous != null)
+ {
+ Dimension tmp = previous.getMinimumSize();
+ previousWidth = tmp.width;
+ }
+
+ d.width += Math.max(nextWidth, previousWidth);
+
+ return d;
+ }
+
+ /**
+ * Returns the preferred layout size of the container.
+ *
+ * @param parent DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ Dimension d = new Dimension();
+
+ if (editor != null)
+ {
+ Dimension tmp = editor.getPreferredSize();
+ d.width += Math.max(tmp.width, 40);
+ d.height = tmp.height;
+ }
+
+ int nextWidth = 0;
+ int previousWidth = 0;
+
+ if (next != null)
+ {
+ Dimension tmp = next.getPreferredSize();
+ nextWidth = tmp.width;
+ }
+ if (previous != null)
+ {
+ Dimension tmp = previous.getPreferredSize();
+ previousWidth = tmp.width;
+ }
+
+ d.width += Math.max(nextWidth, previousWidth);
+ Insets insets = parent.getInsets();
+ d.width = d.width + insets.left + insets.right;
+ d.height = d.height + insets.top + insets.bottom;
+ return d;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param child DOCUMENT ME!
+ */
+ public void removeLayoutComponent(Component child)
+ {
+ if (child == editor)
+ editor = null;
+ else if (child == next)
+ next = null;
+ else if (previous == child)
+ previous = null;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param name DOCUMENT ME!
+ * @param child DOCUMENT ME!
+ */
+ public void addLayoutComponent(String name, Component child)
+ {
+ if ("Editor".equals(name))
+ editor = child;
+ else if ("Next".equals(name))
+ next = child;
+ else if ("Previous".equals(name))
+ previous = child;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param c DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ private Dimension prefSize(Component c)
+ {
+ if (c == null)
+ return new Dimension();
+ else
+ return c.getPreferredSize();
+ }
+
+ /**
+ * Sets the bounds for the specified component.
+ *
+ * @param c the component.
+ * @param x the x-coordinate for the top-left of the component bounds.
+ * @param y the y-coordinate for the top-left of the component bounds.
+ * @param w the width of the bounds.
+ * @param h the height of the bounds.
+ */
+ private void setBounds(Component c, int x, int y, int w, int h)
+ {
+ if (c != null)
+ c.setBounds(x, y, w, h);
+ }
+
+ /** The editor component. */
+ private Component editor;
+
+ /** The next button. */
+ private Component next;
+
+ /** The previous button. */
+ private Component previous;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java
new file mode 100644
index 000000000..53f7db6e7
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java
@@ -0,0 +1,1077 @@
+/* BasicSplitPaneDivider.java --
+ Copyright (C) 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JSplitPane;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+
+/**
+ * The divider that separates the two parts of a JSplitPane in the Basic look
+ * and feel.
+ *
+ * <p>
+ * Implementation status: We do not have a real implementation yet. Currently,
+ * it is mostly a stub to allow compiling other parts of the
+ * javax.swing.plaf.basic package, although some parts are already
+ * functional.
+ * </p>
+ *
+ * @author Sascha Brawer (brawer_AT_dandelis.ch)
+ */
+public class BasicSplitPaneDivider extends Container
+ implements PropertyChangeListener
+{
+ /**
+ * The buttons used as one touch buttons.
+ */
+ private class BasicOneTouchButton
+ extends JButton
+ {
+ /**
+ * Denotes a left button.
+ */
+ static final int LEFT = 0;
+
+ /**
+ * Denotes a right button.
+ */
+ static final int RIGHT = 1;
+
+ /**
+ * The x points for the arrow.
+ */
+ private int[] xpoints;
+
+ /**
+ * The y points for the arrow.
+ */
+ private int[] ypoints;
+
+ /**
+ * Either LEFT or RIGHT.
+ */
+ private int direction;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param dir either LEFT or RIGHT
+ */
+ BasicOneTouchButton(int dir)
+ {
+ direction = dir;
+ xpoints = new int[3];
+ ypoints = new int[3];
+ }
+
+ /**
+ * Never allow borders.
+ */
+ public void setBorder(Border b)
+ {
+ }
+
+ /**
+ * Never allow focus traversal.
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Paints the one touch button.
+ */
+ public void paint(Graphics g)
+ {
+ if (splitPane != null)
+ {
+ // Fill background.
+ g.setColor(splitPane.getBackground());
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ // Draw arrow.
+ int size;
+ if (direction == LEFT)
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ size = Math.min(getHeight(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size / 2;
+ xpoints[2] = size;
+ ypoints[0] = size;
+ ypoints[1] = 0;
+ ypoints[2] = size;
+ }
+ else
+ {
+ size = Math.min(getWidth(), ONE_TOUCH_SIZE);
+ xpoints[0] = size;
+ xpoints[1] = 0;
+ xpoints[2] = size;
+ ypoints[0] = 0;
+ ypoints[1] = size / 2;
+ ypoints[2] = size;
+ }
+ }
+ else
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ size = Math.min(getHeight(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size / 2;
+ xpoints[2] = size;
+ ypoints[0] = 0;
+ ypoints[1] = size;
+ ypoints[2] = 0;
+ }
+ else
+ {
+ size = Math.min(getWidth(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size;
+ xpoints[2] = 0;
+ ypoints[0] = 0;
+ ypoints[1] = size / 2;
+ ypoints[2] = size;
+ }
+ }
+ g.setColor(Color.BLACK);
+ g.fillPolygon(xpoints, ypoints, 3);
+ }
+ }
+ }
+
+ /**
+ * Listens for actions on the one touch buttons.
+ */
+ private class OneTouchAction
+ implements ActionListener
+ {
+
+ public void actionPerformed(ActionEvent ev)
+ {
+ Insets insets = splitPane.getInsets();
+ int lastLoc = splitPane.getLastDividerLocation();
+ int currentLoc = splitPaneUI.getDividerLocation(splitPane);
+ int newLoc;
+
+ if (ev.getSource() == leftButton)
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ if (currentLoc
+ >= splitPane.getHeight() - insets.bottom - getHeight())
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = insets.top;
+ }
+ }
+ else
+ {
+ if (currentLoc
+ >= splitPane.getWidth() - insets.right - getWidth())
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = insets.left;
+ }
+ }
+ }
+ else
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ if (currentLoc == insets.top)
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = splitPane.getHeight() - insets.top - getHeight();
+ }
+ }
+ else
+ {
+ if (currentLoc == insets.left)
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = splitPane.getWidth() - insets.left - getWidth();
+ }
+ }
+ }
+ if (currentLoc != newLoc)
+ {
+ splitPane.setDividerLocation(newLoc);
+ splitPane.setLastDividerLocation(currentLoc);
+ }
+ }
+ }
+
+ /**
+ * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
+ * on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 1463404307042803342L;
+
+ /**
+ * The width and height of the little buttons for showing and hiding parts
+ * of a JSplitPane in a single mouse click.
+ */
+ protected static final int ONE_TOUCH_SIZE = 6;
+
+ /** The distance the one touch buttons will sit from the divider's edges. */
+ protected static final int ONE_TOUCH_OFFSET = 2;
+
+ /**
+ * An object that performs the tasks associated with an ongoing drag
+ * operation, or <code>null</code> if the user is currently not dragging
+ * the divider.
+ */
+ protected DragController dragger;
+
+ /**
+ * The delegate object that is responsible for the UI of the
+ * <code>JSplitPane</code> that contains this divider.
+ */
+ protected BasicSplitPaneUI splitPaneUI;
+
+ /** The thickness of the divider in pixels. */
+ protected int dividerSize;
+
+ /** A divider that is used for layout purposes. */
+ protected Component hiddenDivider;
+
+ /** The JSplitPane containing this divider. */
+ protected JSplitPane splitPane;
+
+ /**
+ * The listener for handling mouse events from both the divider and the
+ * containing <code>JSplitPane</code>.
+ *
+ * <p>
+ * The reason for also handling MouseEvents from the containing
+ * <code>JSplitPane</code> is that users should be able to start a drag
+ * gesture from inside the JSplitPane, but slightly outisde the divider.
+ * </p>
+ */
+ protected MouseHandler mouseHandler = new MouseHandler();
+
+ /**
+ * The current orientation of the containing <code>JSplitPane</code>, which
+ * is either {@link javax.swing.JSplitPane#HORIZONTAL_SPLIT} or {@link
+ * javax.swing.JSplitPane#VERTICAL_SPLIT}.
+ */
+ protected int orientation;
+
+ /**
+ * The button for showing and hiding the left (or top) component of the
+ * <code>JSplitPane</code>.
+ */
+ protected JButton leftButton;
+
+ /**
+ * The button for showing and hiding the right (or bottom) component of the
+ * <code>JSplitPane</code>.
+ */
+ protected JButton rightButton;
+
+ /**
+ * The border of this divider. Typically, this will be an instance of {@link
+ * javax.swing.plaf.basic.BasicBorders.SplitPaneDividerBorder}.
+ *
+ * @see #getBorder()
+ * @see #setBorder(javax.swing.border.Border)
+ */
+ private Border border;
+
+ // This is not a pixel count.
+ // This int should be able to take 3 values.
+ // left (top), middle, right(bottom)
+ // 0 1 2
+
+ /**
+ * Keeps track of where the divider should be placed when using one touch
+ * expand buttons.
+ * This is package-private to avoid an accessor method.
+ */
+ transient int currentDividerLocation = 1;
+
+ /**
+ * Indicates if the ont touch buttons are laid out centered or at the
+ * top/left.
+ *
+ * Package private to avoid accessor method.
+ */
+ boolean centerOneTouchButtons;
+
+ /**
+ * Constructs a new divider.
+ *
+ * @param ui the UI delegate of the enclosing <code>JSplitPane</code>.
+ */
+ public BasicSplitPaneDivider(BasicSplitPaneUI ui)
+ {
+ setLayout(new DividerLayout());
+ setBasicSplitPaneUI(ui);
+ setDividerSize(splitPane.getDividerSize());
+ centerOneTouchButtons =
+ UIManager.getBoolean("SplitPane.centerOneTouchButtons");
+ }
+
+ /**
+ * Sets the delegate object that is responsible for the UI of the {@link
+ * javax.swing.JSplitPane} containing this divider.
+ *
+ * @param newUI the UI delegate, or <code>null</code> to release the
+ * connection to the current delegate.
+ */
+ public void setBasicSplitPaneUI(BasicSplitPaneUI newUI)
+ {
+ /* Remove the connection to the existing JSplitPane. */
+ if (splitPane != null)
+ {
+ splitPane.removePropertyChangeListener(this);
+ removeMouseListener(mouseHandler);
+ removeMouseMotionListener(mouseHandler);
+ splitPane = null;
+ hiddenDivider = null;
+ }
+
+ /* Establish the connection to the new JSplitPane. */
+ splitPaneUI = newUI;
+ if (splitPaneUI != null)
+ splitPane = newUI.getSplitPane();
+ if (splitPane != null)
+ {
+ splitPane.addPropertyChangeListener(this);
+ addMouseListener(mouseHandler);
+ addMouseMotionListener(mouseHandler);
+ hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider();
+ orientation = splitPane.getOrientation();
+ if (splitPane.isOneTouchExpandable())
+ oneTouchExpandableChanged();
+ }
+ }
+
+ /**
+ * Returns the delegate object that is responsible for the UI of the {@link
+ * javax.swing.JSplitPane} containing this divider.
+ *
+ * @return The UI for the JSplitPane.
+ */
+ public BasicSplitPaneUI getBasicSplitPaneUI()
+ {
+ return splitPaneUI;
+ }
+
+ /**
+ * Sets the thickness of the divider.
+ *
+ * @param newSize the new width or height in pixels.
+ */
+ public void setDividerSize(int newSize)
+ {
+ this.dividerSize = newSize;
+ }
+
+ /**
+ * Retrieves the thickness of the divider.
+ *
+ * @return The thickness of the divider.
+ */
+ public int getDividerSize()
+ {
+ return dividerSize;
+ }
+
+ /**
+ * Sets the border of this divider.
+ *
+ * @param border the new border. Typically, this will be an instance of
+ * {@link
+ * javax.swing.plaf.basic.BasicBorders.SplitPaneBorder}.
+ *
+ * @since 1.3
+ */
+ public void setBorder(Border border)
+ {
+ if (border != this.border)
+ {
+ Border oldValue = this.border;
+ this.border = border;
+ firePropertyChange("border", oldValue, border);
+ }
+ }
+
+ /**
+ * Retrieves the border of this divider.
+ *
+ * @return the current border, or <code>null</code> if no border has been
+ * set.
+ *
+ * @since 1.3
+ */
+ public Border getBorder()
+ {
+ return border;
+ }
+
+ /**
+ * Retrieves the insets of the divider. If a border has been installed on
+ * the divider, the result of calling its <code>getBorderInsets</code>
+ * method is returned. Otherwise, the inherited implementation will be
+ * invoked.
+ *
+ * @see javax.swing.border.Border#getBorderInsets(java.awt.Component)
+ */
+ public Insets getInsets()
+ {
+ if (border != null)
+ return border.getBorderInsets(this);
+ else
+ return super.getInsets();
+ }
+
+ /**
+ * Returns the preferred size of this divider, which is
+ * <code>dividerSize</code> by <code>dividerSize</code> pixels.
+ *
+ * @return The preferred size of the divider.
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension d;
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ d = new Dimension(getDividerSize(), 1);
+ else
+ d = new Dimension(1, getDividerSize());
+ return d;
+ }
+
+ /**
+ * Returns the minimal size of this divider, which is
+ * <code>dividerSize</code> by <code>dividerSize</code> pixels.
+ *
+ * @return The minimal size of the divider.
+ */
+ public Dimension getMinimumSize()
+ {
+ return getPreferredSize();
+ }
+
+ /**
+ * Processes events from the <code>JSplitPane</code> that contains this
+ * divider.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(JSplitPane.ONE_TOUCH_EXPANDABLE_PROPERTY))
+ oneTouchExpandableChanged();
+ else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
+ {
+ orientation = splitPane.getOrientation();
+ invalidate();
+ if (splitPane != null)
+ splitPane.revalidate();
+ }
+ else if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
+ dividerSize = splitPane.getDividerSize();
+ }
+
+ /**
+ * Paints the divider by painting its border.
+ *
+ * @param g The Graphics Object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ Dimension dividerSize;
+
+ super.paint(g);
+ if (border != null)
+ {
+ dividerSize = getSize();
+ border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height);
+ }
+ }
+
+ /**
+ * Reacts to changes of the <code>oneToughExpandable</code> property of the
+ * containing <code>JSplitPane</code>.
+ */
+ protected void oneTouchExpandableChanged()
+ {
+ if (splitPane.isOneTouchExpandable())
+ {
+ leftButton = createLeftOneTouchButton();
+ if (leftButton != null)
+ leftButton.addActionListener(new OneTouchAction());
+
+ rightButton = createRightOneTouchButton();
+ if (rightButton != null)
+ rightButton.addActionListener(new OneTouchAction());
+
+ // Only add them when both are non-null.
+ if (leftButton != null && rightButton != null)
+ {
+ add(leftButton);
+ add(rightButton);
+ }
+ }
+ invalidate();
+ if (splitPane != null)
+ splitPane.revalidate();
+ }
+
+ /**
+ * Creates a button for showing and hiding the left (or top) part of a
+ * <code>JSplitPane</code>.
+ *
+ * @return The left one touch button.
+ */
+ protected JButton createLeftOneTouchButton()
+ {
+ JButton button = new BasicOneTouchButton(BasicOneTouchButton.LEFT);
+ button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ button.setRequestFocusEnabled(false);
+ return button;
+ }
+
+ /**
+ * Creates a button for showing and hiding the right (or bottom) part of a
+ * <code>JSplitPane</code>.
+ *
+ * @return The right one touch button.
+ */
+ protected JButton createRightOneTouchButton()
+ {
+ JButton button = new BasicOneTouchButton(BasicOneTouchButton.RIGHT);
+ button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ button.setRequestFocusEnabled(false);
+ return button;
+ }
+
+ /**
+ * Prepares the divider for dragging by calling the
+ * <code>startDragging</code> method of the UI delegate of the enclosing
+ * <code>JSplitPane</code>.
+ *
+ * @see BasicSplitPaneUI#startDragging()
+ */
+ protected void prepareForDragging()
+ {
+ if (splitPaneUI != null)
+ splitPaneUI.startDragging();
+ }
+
+ /**
+ * Drags the divider to a given location by calling the
+ * <code>dragDividerTo</code> method of the UI delegate of the enclosing
+ * <code>JSplitPane</code>.
+ *
+ * @param location the new location of the divider.
+ *
+ * @see BasicSplitPaneUI#dragDividerTo(int location)
+ */
+ protected void dragDividerTo(int location)
+ {
+ if (splitPaneUI != null)
+ splitPaneUI.dragDividerTo(location);
+ }
+
+ /**
+ * Finishes a dragging gesture by calling the <code>finishDraggingTo</code>
+ * method of the UI delegate of the enclosing <code>JSplitPane</code>.
+ *
+ * @param location the new, final location of the divider.
+ *
+ * @see BasicSplitPaneUI#finishDraggingTo(int location)
+ */
+ protected void finishDraggingTo(int location)
+ {
+ if (splitPaneUI != null)
+ splitPaneUI.finishDraggingTo(location);
+ }
+
+ /**
+ * This helper method moves the divider to one of the three locations when
+ * using one touch expand buttons. Location 0 is the left (or top) most
+ * location. Location 1 is the middle. Location 2 is the right (or bottom)
+ * most location.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param locationIndex The location to move to.
+ */
+ void moveDividerTo(int locationIndex)
+ {
+ Insets insets = splitPane.getInsets();
+ switch (locationIndex)
+ {
+ case 1:
+ splitPane.setDividerLocation(splitPane.getLastDividerLocation());
+ break;
+ case 0:
+ int top = (orientation == JSplitPane.HORIZONTAL_SPLIT) ? insets.left
+ : insets.top;
+ splitPane.setDividerLocation(top);
+ break;
+ case 2:
+ int bottom;
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ bottom = splitPane.getBounds().width - insets.right - dividerSize;
+ else
+ bottom = splitPane.getBounds().height - insets.bottom - dividerSize;
+ splitPane.setDividerLocation(bottom);
+ break;
+ }
+ }
+
+ /**
+ * The listener for handling mouse events from both the divider and the
+ * containing <code>JSplitPane</code>.
+ *
+ * <p>
+ * The reason for also handling MouseEvents from the containing
+ * <code>JSplitPane</code> is that users should be able to start a drag
+ * gesture from inside the JSplitPane, but slightly outisde the divider.
+ * </p>
+ *
+ * @author Sascha Brawer (brawer_AT_dandelis.ch)
+ */
+ protected class MouseHandler extends MouseAdapter
+ implements MouseMotionListener
+ {
+ /** Keeps track of whether a drag is occurring. */
+ private transient boolean isDragging;
+
+ /**
+ * This method is called when the mouse is pressed.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ isDragging = true;
+ currentDividerLocation = 1;
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ dragger = new DragController(e);
+ else
+ dragger = new VerticalDragController(e);
+ prepareForDragging();
+ }
+
+ /**
+ * This method is called when the mouse is released.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (isDragging)
+ dragger.completeDrag(e);
+ isDragging = false;
+ }
+
+ /**
+ * Repeatedly invoked when the user is dragging the mouse cursor while
+ * having pressed a mouse button.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ if (dragger != null)
+ dragger.continueDrag(e);
+ }
+
+ /**
+ * Repeatedly invoked when the user is dragging the mouse cursor without
+ * having pressed a mouse button.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * Performs the tasks associated with an ongoing drag operation.
+ *
+ * @author Sascha Brawer (brawer_AT_dandelis.ch)
+ */
+ protected class DragController
+ {
+ /**
+ * The difference between where the mouse is clicked and the initial
+ * divider location.
+ */
+ transient int offset;
+
+ /**
+ * Creates a new DragController object.
+ *
+ * @param e The MouseEvent to initialize with.
+ */
+ protected DragController(MouseEvent e)
+ {
+ offset = e.getX();
+ }
+
+ /**
+ * This method returns true if the divider can move.
+ *
+ * @return True if dragging is allowed.
+ */
+ protected boolean isValid()
+ {
+ // Views can always be resized?
+ return true;
+ }
+
+ /**
+ * Returns a position for the divider given the MouseEvent.
+ *
+ * @param e MouseEvent.
+ *
+ * @return The position for the divider to move to.
+ */
+ protected int positionForMouseEvent(MouseEvent e)
+ {
+ return e.getX() + getX() - offset;
+ }
+
+ /**
+ * This method returns one of the two paramters for the orientation. In
+ * this case, it returns x.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ *
+ * @return The x coordinate.
+ */
+ protected int getNeededLocation(int x, int y)
+ {
+ return x;
+ }
+
+ /**
+ * This method is called to pass on the drag information to the UI through
+ * dragDividerTo.
+ *
+ * @param newX The x coordinate of the MouseEvent.
+ * @param newY The y coordinate of the MouseEvent.
+ */
+ protected void continueDrag(int newX, int newY)
+ {
+ if (isValid())
+ dragDividerTo(adjust(newX, newY));
+ }
+
+ /**
+ * This method is called to pass on the drag information to the UI
+ * through dragDividerTo.
+ *
+ * @param e The MouseEvent.
+ */
+ protected void continueDrag(MouseEvent e)
+ {
+ if (isValid())
+ dragDividerTo(positionForMouseEvent(e));
+ }
+
+ /**
+ * This method is called to finish the drag session by calling
+ * finishDraggingTo.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ */
+ protected void completeDrag(int x, int y)
+ {
+ finishDraggingTo(adjust(x, y));
+ }
+
+ /**
+ * This method is called to finish the drag session by calling
+ * finishDraggingTo.
+ *
+ * @param e The MouseEvent.
+ */
+ protected void completeDrag(MouseEvent e)
+ {
+ finishDraggingTo(positionForMouseEvent(e));
+ }
+
+ /**
+ * This is a helper method that includes the offset in the needed
+ * location.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ *
+ * @return The needed location adjusted by the offsets.
+ */
+ int adjust(int x, int y)
+ {
+ return getNeededLocation(x, y) + getX() - offset;
+ }
+ }
+
+ /**
+ * This is a helper class that controls dragging when the orientation is
+ * VERTICAL_SPLIT.
+ */
+ protected class VerticalDragController extends DragController
+ {
+ /**
+ * Creates a new VerticalDragController object.
+ *
+ * @param e The MouseEvent to initialize with.
+ */
+ protected VerticalDragController(MouseEvent e)
+ {
+ super(e);
+ offset = e.getY();
+ }
+
+ /**
+ * This method returns one of the two parameters given the orientation. In
+ * this case, it returns y.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ *
+ * @return The y coordinate.
+ */
+ protected int getNeededLocation(int x, int y)
+ {
+ return y;
+ }
+
+ /**
+ * This method returns the new location of the divider given a MouseEvent.
+ *
+ * @param e The MouseEvent.
+ *
+ * @return The new location of the divider.
+ */
+ protected int positionForMouseEvent(MouseEvent e)
+ {
+ return e.getY() + getY() - offset;
+ }
+
+ /**
+ * This is a helper method that includes the offset in the needed
+ * location.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ *
+ * @return The needed location adjusted by the offsets.
+ */
+ int adjust(int x, int y)
+ {
+ return getNeededLocation(x, y) + getY() - offset;
+ }
+ }
+
+ /**
+ * This helper class acts as the Layout Manager for the divider.
+ */
+ protected class DividerLayout implements LayoutManager
+ {
+ /**
+ * Creates a new DividerLayout object.
+ */
+ protected DividerLayout()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when a Component is added.
+ *
+ * @param string The constraints string.
+ * @param c The Component to add.
+ */
+ public void addLayoutComponent(String string, Component c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the container.
+ *
+ * @param c The container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+ if (leftButton != null && rightButton != null
+ && c == BasicSplitPaneDivider.this)
+ {
+ if (splitPane.isOneTouchExpandable())
+ {
+ Insets insets = getInsets();
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ {
+ int size = getWidth() - insets.left - insets.right;
+ size = Math.max(size, 0);
+ size = Math.min(size, ONE_TOUCH_SIZE);
+ int x, y;
+ if (centerOneTouchButtons)
+ {
+ y = insets.top;
+ x = (getWidth() - size) / 2;
+ }
+ else
+ {
+ x = insets.left;
+ y = 0;
+ }
+
+ leftButton.setBounds(x, y + ONE_TOUCH_OFFSET, size,
+ size * 2);
+ rightButton.setBounds(x, y + ONE_TOUCH_OFFSET
+ + ONE_TOUCH_SIZE * 2, size, size * 2);
+ }
+ else
+ {
+ int size = getHeight() - insets.top - insets.bottom;
+ size = Math.max(size, 0);
+ size = Math.min(size, ONE_TOUCH_SIZE);
+ int x, y;
+ if (centerOneTouchButtons)
+ {
+ x = insets.left;
+ y = (getHeight() - size) / 2;
+ }
+ else
+ {
+ x = 0;
+ y = insets.top;
+ }
+ leftButton.setBounds(x + ONE_TOUCH_OFFSET, y, size * 2,
+ size);
+ rightButton.setBounds(x + ONE_TOUCH_OFFSET
+ + ONE_TOUCH_SIZE * 2, y, size * 2,
+ size);
+ }
+ }
+ else
+ {
+ // The JDK sets this bounds for disabled one touch buttons, so
+ // do we.
+ leftButton.setBounds(-5, -5, 1, 1);
+ rightButton.setBounds(-5, -5, 1, 1);
+ }
+ }
+ }
+
+ /**
+ * This method returns the minimum layout size.
+ *
+ * @param c The container to calculate for.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return preferredLayoutSize(c);
+ }
+
+ /**
+ * This method returns the preferred layout size.
+ *
+ * @param c The container to calculate for.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ return new Dimension(dividerSize, dividerSize);
+ }
+
+ /**
+ * This method is called when a component is removed.
+ *
+ * @param c The component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Do nothing.
+ }
+
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java
new file mode 100644
index 000000000..ca9607515
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java
@@ -0,0 +1,1623 @@
+/* BasicSplitPaneUI.java --
+ Copyright (C) 2003, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Canvas;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager2;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JSlider;
+import javax.swing.JSplitPane;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SplitPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * This is the Basic Look and Feel implementation of the SplitPaneUI class.
+ */
+public class BasicSplitPaneUI extends SplitPaneUI
+{
+ /**
+ * This Layout Manager controls the position and size of the components when
+ * the JSplitPane's orientation is HORIZONTAL_SPLIT.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class BasicHorizontalLayoutManager implements LayoutManager2
+ {
+ // 3 components at a time.
+ // LEFT/TOP = 0
+ // RIGHT/BOTTOM = 1
+ // DIVIDER = 2
+
+ /**
+ * This array contains the components in the JSplitPane. The left/top
+ * component is at index 0, the right/bottom is at 1, and the divider is
+ * at 2.
+ */
+ protected Component[] components = new Component[3];
+
+ // These are the _current_ widths of the associated component.
+
+ /**
+ * This array contains the current width (for HORIZONTAL_SPLIT) or height
+ * (for VERTICAL_SPLIT) of the components. The indices are the same as
+ * for components.
+ */
+ protected int[] sizes = new int[3];
+
+ /**
+ * This is used to determine if we are vertical or horizontal layout.
+ * In the JDK, the BasicVerticalLayoutManager seems to have no more
+ * methods implemented (as of JDK5), so we keep this state here.
+ */
+ private int axis;
+
+ /**
+ * Creates a new instance. This is package private because the reference
+ * implementation has no public constructor either. Still, we need to
+ * call it from BasicVerticalLayoutManager.
+ */
+ BasicHorizontalLayoutManager()
+ {
+ this(SwingConstants.HORIZONTAL);
+ }
+
+ /**
+ * Creates a new instance for a specified axis. This is provided for
+ * compatibility, since the BasicVerticalLayoutManager seems to have
+ * no more implementation in the RI, according to the specs. So
+ * we handle all the axis specific stuff here.
+ *
+ * @param a the axis, either SwingConstants#HORIZONTAL,
+ * or SwingConstants#VERTICAL
+ */
+ BasicHorizontalLayoutManager(int a)
+ {
+ axis = a;
+ }
+
+ /**
+ * This method adds the component given to the JSplitPane. The position of
+ * the component is given by the constraints object.
+ *
+ * @param comp The Component to add.
+ * @param constraints The constraints that bind the object.
+ */
+ public void addLayoutComponent(Component comp, Object constraints)
+ {
+ addLayoutComponent((String) constraints, comp);
+ }
+
+ /**
+ * This method is called to add a Component to the JSplitPane. The
+ * placement string determines where the Component will be placed. The
+ * string should be one of LEFT, RIGHT, TOP, BOTTOM or null (signals that
+ * the component is the divider).
+ *
+ * @param place The placement of the Component.
+ * @param component The Component to add.
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
+ */
+ public void addLayoutComponent(String place, Component component)
+ {
+ int i = 0;
+ if (place == null)
+ i = 2;
+ else if (place.equals(JSplitPane.TOP) || place.equals(JSplitPane.LEFT))
+ i = 0;
+ else if (place.equals(JSplitPane.BOTTOM)
+ || place.equals(JSplitPane.RIGHT))
+ i = 1;
+ else
+ throw new IllegalArgumentException("Illegal placement in JSplitPane");
+ components[i] = component;
+ resetSizeAt(i);
+ splitPane.revalidate();
+ splitPane.repaint();
+ }
+
+ /**
+ * This method returns the width of the JSplitPane minus the insets.
+ *
+ * @param containerSize The Dimensions of the JSplitPane.
+ * @param insets The Insets of the JSplitPane.
+ *
+ * @return The width of the JSplitPane minus the insets.
+ */
+ protected int getAvailableSize(Dimension containerSize, Insets insets)
+ {
+ int size;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = containerSize.width - insets.left - insets.right;
+ else
+ size = containerSize.height - insets.top - insets.bottom;
+ return size;
+ }
+
+ /**
+ * This method returns the given insets left value. If the given inset is
+ * null, then 0 is returned.
+ *
+ * @param insets The Insets to use with the JSplitPane.
+ *
+ * @return The inset's left value.
+ */
+ protected int getInitialLocation(Insets insets)
+ {
+ int loc = 0;
+ if (insets != null)
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ loc = insets.left;
+ else
+ loc = insets.top;
+ }
+ return loc;
+ }
+
+ /**
+ * This specifies how a component is aligned with respect to other
+ * components in the x fdirection.
+ *
+ * @param target The container.
+ *
+ * @return The component's alignment.
+ */
+ public float getLayoutAlignmentX(Container target)
+ {
+ return 0.0f;
+ }
+
+ /**
+ * This specifies how a component is aligned with respect to other
+ * components in the y direction.
+ *
+ * @param target The container.
+ *
+ * @return The component's alignment.
+ */
+ public float getLayoutAlignmentY(Container target)
+ {
+ return 0.0f;
+ }
+
+ /**
+ * This method returns the preferred width of the component.
+ *
+ * @param c The component to measure.
+ *
+ * @return The preferred width of the component.
+ */
+ protected int getPreferredSizeOfComponent(Component c)
+ {
+ int size = 0;
+ Dimension dims = c.getPreferredSize();
+ if (axis == SwingConstants.HORIZONTAL)
+ {
+ if (dims != null)
+ size = dims.width;
+ }
+ else
+ {
+ if (dims != null)
+ size = dims.height;
+ }
+ return size;
+ }
+
+ /**
+ * This method returns the current width of the component.
+ *
+ * @param c The component to measure.
+ *
+ * @return The width of the component.
+ */
+ protected int getSizeOfComponent(Component c)
+ {
+ int size;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = c.getHeight();
+ else
+ size = c.getWidth();
+ return size;
+ }
+
+ /**
+ * This method returns the sizes array.
+ *
+ * @return The sizes array.
+ */
+ protected int[] getSizes()
+ {
+ return sizes;
+ }
+
+ /**
+ * This method invalidates the layout. It does nothing.
+ *
+ * @param c The container to invalidate.
+ */
+ public void invalidateLayout(Container c)
+ {
+ // DO NOTHING
+ }
+
+ /**
+ * This method lays out the components in the container.
+ *
+ * @param container The container to lay out.
+ */
+ public void layoutContainer(Container container)
+ {
+ if (container instanceof JSplitPane)
+ {
+ JSplitPane split = (JSplitPane) container;
+ distributeExtraSpace();
+ Insets insets = split.getInsets();
+ Dimension dims = split.getSize();
+ int loc = getInitialLocation(insets);
+ int available = getAvailableSize(dims, insets);
+ sizes[0] = split.getDividerLocation();
+ sizes[1] = available - sizes[0] - sizes[2];
+
+ // According to a Mauve test we only honour the minimum
+ // size of the components, when the dividerLocation hasn't
+ // been excplicitly set.
+ if (! dividerLocationSet)
+ {
+ sizes[0] = Math.max(sizes[0], minimumSizeOfComponent(0));
+ sizes[1] = Math.max(sizes[1], minimumSizeOfComponent(1));
+ }
+ // The size of the divider won't change.
+
+ // Layout component#1.
+ setComponentToSize(components[0], sizes[0], loc, insets, dims);
+ // Layout divider.
+ loc += sizes[0];
+ setComponentToSize(components[2], sizes[2], loc, insets, dims);
+ // Layout component#2.
+ loc += sizes[2];
+ setComponentToSize(components[1], sizes[1], loc, insets, dims);
+ }
+ }
+
+ /**
+ * This method returns the maximum size for the container given the
+ * components. It returns a new Dimension object that has width and
+ * height equal to Integer.MAX_VALUE.
+ *
+ * @param target The container to measure.
+ *
+ * @return The maximum size.
+ */
+ public Dimension maximumLayoutSize(Container target)
+ {
+ return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * This method returns the container's minimum size. The minimum width is
+ * the sum of all the component's minimum widths. The minimum height is
+ * the maximum of all the components' minimum heights.
+ *
+ * @param target The container to measure.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container target)
+ {
+ Dimension dim = new Dimension();
+ if (target instanceof JSplitPane)
+ {
+ int primary = 0;
+ int secondary = 0;
+ for (int i = 0; i < components.length; i++)
+ {
+ if (components[i] != null)
+ {
+ Dimension dims = components[i].getMinimumSize();
+ primary += axis == SwingConstants.HORIZONTAL ? dims.width
+ : dims.height;
+ int sec = axis == SwingConstants.HORIZONTAL ? dims.height
+ : dims.width;
+ secondary = Math.max(sec, secondary);
+ }
+ }
+ int width = axis == SwingConstants.HORIZONTAL ? primary : secondary;
+ int height = axis == SwingConstants.VERTICAL ? secondary : primary;
+
+ Insets i = splitPane.getInsets();
+ dim.setSize(width + i.left + i.right, height + i.top + i.bottom);
+ }
+ return dim;
+ }
+
+ /**
+ * This method returns the container's preferred size. The preferred width
+ * is the sum of all the component's preferred widths. The preferred
+ * height is the maximum of all the components' preferred heights.
+ *
+ * @param target The container to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension preferredLayoutSize(Container target)
+ {
+ Dimension dim = new Dimension();
+ if (target instanceof JSplitPane)
+ {
+ int primary = 0;
+ int secondary = 0;
+ for (int i = 0; i < components.length; i++)
+ {
+ if (components[i] != null)
+ {
+ Dimension dims = components[i].getPreferredSize();
+ primary += axis == SwingConstants.HORIZONTAL ? dims.width
+ : dims.height;
+ int sec = axis == SwingConstants.HORIZONTAL ? dims.height
+ : dims.width;
+ secondary = Math.max(sec, secondary);
+ }
+ }
+ int width = axis == SwingConstants.HORIZONTAL ? primary : secondary;
+ int height = axis == SwingConstants.VERTICAL ? secondary : primary;
+
+ Insets i = splitPane.getInsets();
+ dim.setSize(width + i.left + i.right, height + i.top + i.bottom);
+ }
+ return dim;
+ }
+
+ /**
+ * This method removes the component from the layout.
+ *
+ * @param component The component to remove from the layout.
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ for (int i = 0; i < components.length; i++)
+ {
+ if (component == components[i])
+ {
+ components[i] = null;
+ sizes[i] = 0;
+ }
+ }
+ }
+
+ /**
+ * This method resets the size of Component to the preferred size.
+ *
+ * @param index The index of the component to reset.
+ */
+ protected void resetSizeAt(int index)
+ {
+ if (components[index] != null)
+ sizes[index] = getPreferredSizeOfComponent(components[index]);
+ }
+
+ /**
+ * This method resets the sizes of all the components.
+ */
+ public void resetToPreferredSizes()
+ {
+ for (int i = 0; i < components.length; i++)
+ resetSizeAt(i);
+ }
+
+ /**
+ * This methods sets the bounds of the given component. The width is the
+ * size. The height is the container size minus the top and bottom
+ * inset. The x coordinate is the location given. The y coordinate is
+ * the top inset.
+ *
+ * @param c The component to set.
+ * @param size The width of the component.
+ * @param location The x coordinate.
+ * @param insets The insets to use.
+ * @param containerSize The height of the container.
+ */
+ protected void setComponentToSize(Component c, int size, int location,
+ Insets insets, Dimension containerSize)
+ {
+ if (insets != null)
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ c.setBounds(location, insets.top, size,
+ containerSize.height - insets.top - insets.bottom);
+ else
+ c.setBounds(insets.left, location,
+ containerSize.width - insets.left - insets.right,
+ size);
+ }
+ else
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ c.setBounds(location, 0, size, containerSize.height);
+ else
+ c.setBounds(0, location, containerSize.width, size);
+ }
+ }
+
+ /**
+ * This method stores the given int array as the new sizes array.
+ *
+ * @param newSizes The array to use as sizes.
+ */
+ protected void setSizes(int[] newSizes)
+ {
+ sizes = newSizes;
+ }
+
+ /**
+ * This method determines the size of each component. It should be called
+ * when a new Layout Manager is created for an existing JSplitPane.
+ */
+ protected void updateComponents()
+ {
+ Component left = splitPane.getLeftComponent();
+ Component right = splitPane.getRightComponent();
+
+ if (left != null)
+ {
+ components[0] = left;
+ resetSizeAt(0);
+ }
+ if (right != null)
+ {
+ components[1] = right;
+ resetSizeAt(1);
+ }
+ components[2] = divider;
+ }
+
+ /**
+ * This method resizes the left and right components to fit inside the
+ * JSplitPane when there is extra space.
+ */
+ void distributeExtraSpace()
+ {
+ // FIXME: This needs to be reimplemented correctly.
+ }
+
+ /**
+ * This method returns the minimum width of the component at the given
+ * index.
+ *
+ * @param index The index to check.
+ *
+ * @return The minimum width.
+ */
+ int minimumSizeOfComponent(int index)
+ {
+ Dimension dims = components[index].getMinimumSize();
+ int size = 0;
+ if (dims != null)
+ if (axis == SwingConstants.HORIZONTAL)
+ size = dims.width;
+ else
+ size = dims.height;
+ return size;
+ }
+ } //end BasicHorizontalLayoutManager
+
+ /**
+ * This class is the Layout Manager for the JSplitPane when the orientation
+ * is VERTICAL_SPLIT.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class BasicVerticalLayoutManager
+ extends BasicHorizontalLayoutManager
+ {
+ /**
+ * Creates a new instance.
+ */
+ public BasicVerticalLayoutManager()
+ {
+ super(SwingConstants.VERTICAL);
+ }
+ }
+
+ /**
+ * This class handles FocusEvents from the JComponent.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class FocusHandler extends FocusAdapter
+ {
+ /**
+ * This method is called when the JSplitPane gains focus.
+ *
+ * @param ev The FocusEvent.
+ */
+ public void focusGained(FocusEvent ev)
+ {
+ // repaint the divider because its background color may change due to
+ // the focus state...
+ divider.repaint();
+ }
+
+ /**
+ * This method is called when the JSplitPane loses focus.
+ *
+ * @param ev The FocusEvent.
+ */
+ public void focusLost(FocusEvent ev)
+ {
+ // repaint the divider because its background color may change due to
+ // the focus state...
+ divider.repaint();
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handling down
+ * and right key presses.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardDownRightHandler implements ActionListener
+ {
+ /**
+ * This method is called when the down or right keys are pressed.
+ *
+ * @param ev The ActionEvent
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handling end
+ * key presses.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardEndHandler implements ActionListener
+ {
+ /**
+ * This method is called when the end key is pressed.
+ *
+ * @param ev The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handling home
+ * key presses.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardHomeHandler implements ActionListener
+ {
+ /**
+ * This method is called when the home key is pressed.
+ *
+ * @param ev The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handling resize
+ * toggles.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardResizeToggleHandler implements ActionListener
+ {
+ /**
+ * This method is called when a resize is toggled.
+ *
+ * @param ev The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handler up and
+ * left key presses.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardUpLeftHandler implements ActionListener
+ {
+ /**
+ * This method is called when the left or up keys are pressed.
+ *
+ * @param ev The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This helper class handles PropertyChangeEvents from the JSplitPane. When
+ * a property changes, this will update the UI accordingly.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class PropertyHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called whenever one of the JSplitPane's properties
+ * change.
+ *
+ * @param e DOCUMENT ME!
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
+ {
+ int newSize = splitPane.getDividerSize();
+ int[] tmpSizes = layoutManager.getSizes();
+ dividerSize = tmpSizes[2];
+ int newSpace = newSize - tmpSizes[2];
+ tmpSizes[2] = newSize;
+
+ tmpSizes[0] += newSpace / 2;
+ tmpSizes[1] += newSpace / 2;
+
+ layoutManager.setSizes(tmpSizes);
+ }
+ else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
+ {
+ int max = layoutManager.getAvailableSize(splitPane.getSize(),
+ splitPane.getInsets());
+ int dividerLoc = getDividerLocation(splitPane);
+ double prop = ((double) dividerLoc) / max;
+
+ resetLayoutManager();
+ if (prop <= 1 && prop >= 0)
+ splitPane.setDividerLocation(prop);
+ }
+ // Don't have to deal with continuous_layout - only
+ // necessary in dragging modes (and it's checked
+ // every time you drag there)
+ // Don't have to deal with resize_weight (as there
+ // will be no extra space associated with this
+ // event - the changes to the weighting will
+ // be taken into account the next time the
+ // sizes change.)
+ // Don't have to deal with divider_location
+ // The method in JSplitPane calls our setDividerLocation
+ // so we'll know about those anyway.
+ // Don't have to deal with last_divider_location
+ // Although I'm not sure why, it doesn't seem to
+ // have any effect on Sun's JSplitPane.
+ // one_touch_expandable changes are dealt with
+ // by our divider.
+ }
+ }
+
+ /** The location of the divider when dragging began. */
+ protected int beginDragDividerLocation;
+
+ /** The size of the divider while dragging. */
+ protected int dividerSize;
+
+ /** The location where the last drag location ended. */
+ transient int lastDragLocation = -1;
+
+ /** The distance the divider is moved when moved by keyboard actions. */
+ // Sun defines this as 3
+ protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3;
+
+ /** The divider that divides this JSplitPane. */
+ protected BasicSplitPaneDivider divider;
+
+ /** The listener that listens for PropertyChangeEvents from the JSplitPane. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The JSplitPane's focus handler. */
+ protected FocusListener focusListener;
+
+ /** @deprecated The handler for down and right key presses. */
+ protected ActionListener keyboardDownRightListener;
+
+ /** @deprecated The handler for end key presses. */
+ protected ActionListener keyboardEndListener;
+
+ /** @deprecated The handler for home key presses. */
+ protected ActionListener keyboardHomeListener;
+
+ /** @deprecated The handler for toggling resizes. */
+ protected ActionListener keyboardResizeToggleListener;
+
+ /** @deprecated The handler for up and left key presses. */
+ protected ActionListener keyboardUpLeftListener;
+
+ /** The JSplitPane's current layout manager. */
+ protected BasicHorizontalLayoutManager layoutManager;
+
+ /** @deprecated The divider resize toggle key. */
+ protected KeyStroke dividerResizeToggleKey;
+
+ /** @deprecated The down key. */
+ protected KeyStroke downKey;
+
+ /** @deprecated The end key. */
+ protected KeyStroke endKey;
+
+ /** @deprecated The home key. */
+ protected KeyStroke homeKey;
+
+ /** @deprecated The left key. */
+ protected KeyStroke leftKey;
+
+ /** @deprecated The right key. */
+ protected KeyStroke rightKey;
+
+ /** @deprecated The up key. */
+ protected KeyStroke upKey;
+
+ /** Set to true when dragging heavy weight components. */
+ protected boolean draggingHW;
+
+ /**
+ * The constraints object used when adding the non-continuous divider to the
+ * JSplitPane.
+ */
+ protected static final String NON_CONTINUOUS_DIVIDER
+ = "nonContinuousDivider";
+
+ /** The dark divider used when dragging in non-continuous layout mode. */
+ protected Component nonContinuousLayoutDivider;
+
+ /** The JSplitPane that this UI draws. */
+ protected JSplitPane splitPane;
+
+ /**
+ * True, when setDividerLocation() has been called at least
+ * once on the JSplitPane, false otherwise.
+ *
+ * This is package private to avoid a synthetic accessor method.
+ */
+ boolean dividerLocationSet;
+
+ /**
+ * Creates a new BasicSplitPaneUI object.
+ */
+ public BasicSplitPaneUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method creates a new BasicSplitPaneUI for the given JComponent.
+ *
+ * @param x The JComponent to create a UI for.
+ *
+ * @return A new BasicSplitPaneUI.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicSplitPaneUI();
+ }
+
+ /**
+ * This method installs the BasicSplitPaneUI for the given JComponent.
+ *
+ * @param c The JComponent to install the UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JSplitPane)
+ {
+ splitPane = (JSplitPane) c;
+ dividerLocationSet = false;
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+ }
+ }
+
+ /**
+ * This method uninstalls the BasicSplitPaneUI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall the UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallDefaults();
+
+ dividerLocationSet = false;
+ splitPane = null;
+ }
+
+ /**
+ * This method installs the defaults given by the Look and Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColors(splitPane, "SplitPane.background",
+ "SplitPane.foreground");
+ LookAndFeel.installBorder(splitPane, "SplitPane.border");
+ divider = createDefaultDivider();
+ divider.setBorder(UIManager.getBorder("SplitPaneDivider.border"));
+ resetLayoutManager();
+ nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider();
+ splitPane.add(divider, JSplitPane.DIVIDER);
+
+ // There is no need to add the nonContinuousLayoutDivider.
+ dividerSize = UIManager.getInt("SplitPane.dividerSize");
+ splitPane.setDividerSize(dividerSize);
+ divider.setDividerSize(dividerSize);
+ splitPane.setOpaque(true);
+ }
+
+ /**
+ * This method uninstalls the defaults and nulls any objects created during
+ * install.
+ */
+ protected void uninstallDefaults()
+ {
+ layoutManager = null;
+ splitPane.remove(divider);
+ divider = null;
+ nonContinuousLayoutDivider = null;
+
+ if (splitPane.getBackground() instanceof UIResource)
+ splitPane.setBackground(null);
+ if (splitPane.getBorder() instanceof UIResource)
+ splitPane.setBorder(null);
+ }
+
+ /**
+ * This method installs the listeners needed for this UI to function.
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+ focusListener = createFocusListener();
+
+ splitPane.addPropertyChangeListener(propertyChangeListener);
+ splitPane.addFocusListener(focusListener);
+ }
+
+ /**
+ * This method uninstalls all listeners registered for the UI.
+ */
+ protected void uninstallListeners()
+ {
+ splitPane.removePropertyChangeListener(propertyChangeListener);
+ splitPane.removeFocusListener(focusListener);
+
+ focusListener = null;
+ propertyChangeListener = null;
+ }
+
+ /**
+ * Returns the input map for the specified condition.
+ *
+ * @param condition the condition.
+ *
+ * @return The input map.
+ */
+ InputMap getInputMap(int condition)
+ {
+ if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+ return (InputMap) UIManager.get("SplitPane.ancestorInputMap");
+ return null;
+ }
+
+ /**
+ * Returns the action map for the {@link JSplitPane}. All sliders share
+ * a single action map which is created the first time this method is
+ * called, then stored in the UIDefaults table for subsequent access.
+ *
+ * @return The shared action map.
+ */
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("SplitPane.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("SplitPane.actionMap", map);
+ }
+ return map;
+ }
+
+ /**
+ * Creates the action map shared by all {@link JSlider} instances.
+ * This method is called once by {@link #getActionMap()} when it
+ * finds no action map in the UIDefaults table...after the map is
+ * created, it gets added to the defaults table so that subsequent
+ * calls to {@link #getActionMap()} will return the same shared
+ * instance.
+ *
+ * @return The action map.
+ */
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+ map.put("toggleFocus",
+ new AbstractAction("toggleFocus") {
+ public void actionPerformed(ActionEvent event)
+ {
+ // FIXME: What to do here?
+ }
+ }
+ );
+ map.put("startResize",
+ new AbstractAction("startResize") {
+ public void actionPerformed(ActionEvent event)
+ {
+ splitPane.requestFocus();
+ }
+ }
+ );
+ map.put("selectMax",
+ new AbstractAction("selectMax") {
+ public void actionPerformed(ActionEvent event)
+ {
+ splitPane.setDividerLocation(1.0);
+ }
+ }
+ );
+ map.put("selectMin",
+ new AbstractAction("selectMin") {
+ public void actionPerformed(ActionEvent event)
+ {
+ splitPane.setDividerLocation(0.0);
+ }
+ }
+ );
+ map.put("negativeIncrement",
+ new AbstractAction("negativeIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ int oldLoc = splitPane.getDividerLocation();
+ int newLoc =
+ Math.max(oldLoc - KEYBOARD_DIVIDER_MOVE_OFFSET, 0);
+ splitPane.setDividerLocation(newLoc);
+ }
+ }
+ );
+ map.put("positiveIncrement",
+ new AbstractAction("positiveIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ int oldLoc = splitPane.getDividerLocation();
+ int newLoc =
+ Math.max(oldLoc + KEYBOARD_DIVIDER_MOVE_OFFSET, 0);
+ splitPane.setDividerLocation(newLoc);
+ }
+ }
+ );
+ map.put("focusOutBackward",
+ new AbstractAction("focusOutBackward") {
+ public void actionPerformed(ActionEvent event)
+ {
+ // FIXME: implement this
+ }
+ }
+ );
+ map.put("focusOutForward",
+ new AbstractAction("focusOutForward") {
+ public void actionPerformed(ActionEvent event)
+ {
+ // FIXME: implement this
+ }
+ }
+ );
+ return map;
+ }
+
+ /**
+ * Installs any keyboard actions. The list of keys that need to be bound are
+ * listed in Basic look and feel's defaults.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap keyMap = getInputMap(
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ SwingUtilities.replaceUIInputMap(splitPane,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(splitPane, map);
+ }
+
+ /**
+ * This method reverses the work done in installKeyboardActions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIActionMap(splitPane, null);
+ SwingUtilities.replaceUIInputMap(splitPane,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyHandler();
+ }
+
+ /**
+ * This method creates a new FocusListener.
+ *
+ * @return A new FocusListener.
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for up and left key presses.
+ *
+ * @return A new ActionListener for up and left keys.
+ *
+ * @deprecated 1.3
+ */
+ protected ActionListener createKeyboardUpLeftListener()
+ {
+ return new KeyboardUpLeftHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for down and right key presses.
+ *
+ * @return A new ActionListener for down and right keys.
+ *
+ * @deprecated 1.3
+ */
+ protected ActionListener createKeyboardDownRightListener()
+ {
+ return new KeyboardDownRightHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for home key presses.
+ *
+ * @return A new ActionListener for home keys.
+ *
+ * @deprecated
+ */
+ protected ActionListener createKeyboardHomeListener()
+ {
+ return new KeyboardHomeHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for end key presses.i
+ *
+ * @return A new ActionListener for end keys.
+ *
+ * @deprecated 1.3
+ */
+ protected ActionListener createKeyboardEndListener()
+ {
+ return new KeyboardEndHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for resize toggle key events.
+ *
+ * @return A new ActionListener for resize toggle keys.
+ *
+ * @deprecated 1.3
+ */
+ protected ActionListener createKeyboardResizeToggleListener()
+ {
+ return new KeyboardResizeToggleHandler();
+ }
+
+ /**
+ * This method returns the orientation of the JSplitPane.
+ *
+ * @return The orientation of the JSplitPane.
+ */
+ public int getOrientation()
+ {
+ return splitPane.getOrientation();
+ }
+
+ /**
+ * This method sets the orientation of the JSplitPane.
+ *
+ * @param orientation The new orientation of the JSplitPane.
+ */
+ public void setOrientation(int orientation)
+ {
+ splitPane.setOrientation(orientation);
+ }
+
+ /**
+ * This method returns true if the JSplitPane is using continuous layout.
+ *
+ * @return True if the JSplitPane is using continuous layout.
+ */
+ public boolean isContinuousLayout()
+ {
+ return splitPane.isContinuousLayout();
+ }
+
+ /**
+ * This method sets the continuous layout property of the JSplitPane.
+ *
+ * @param b True if the JsplitPane is to use continuous layout.
+ */
+ public void setContinuousLayout(boolean b)
+ {
+ splitPane.setContinuousLayout(b);
+ }
+
+ /**
+ * This method returns the last location the divider was dragged to.
+ *
+ * @return The last location the divider was dragged to.
+ */
+ public int getLastDragLocation()
+ {
+ return lastDragLocation;
+ }
+
+ /**
+ * This method sets the last location the divider was dragged to.
+ *
+ * @param l The last location the divider was dragged to.
+ */
+ public void setLastDragLocation(int l)
+ {
+ lastDragLocation = l;
+ }
+
+ /**
+ * This method returns the BasicSplitPaneDivider that divides this
+ * JSplitPane.
+ *
+ * @return The divider for the JSplitPane.
+ */
+ public BasicSplitPaneDivider getDivider()
+ {
+ return divider;
+ }
+
+ /**
+ * This method creates a nonContinuousLayoutDivider for use with the
+ * JSplitPane in nonContinousLayout mode. The default divider is a gray
+ * Canvas.
+ *
+ * @return The default nonContinousLayoutDivider.
+ */
+ protected Component createDefaultNonContinuousLayoutDivider()
+ {
+ if (nonContinuousLayoutDivider == null)
+ {
+ nonContinuousLayoutDivider = new Canvas();
+ Color c = UIManager.getColor("SplitPaneDivider.draggingColor");
+ nonContinuousLayoutDivider.setBackground(c);
+ }
+ return nonContinuousLayoutDivider;
+ }
+
+ /**
+ * This method sets the component to use as the nonContinuousLayoutDivider.
+ *
+ * @param newDivider The component to use as the nonContinuousLayoutDivider.
+ */
+ protected void setNonContinuousLayoutDivider(Component newDivider)
+ {
+ setNonContinuousLayoutDivider(newDivider, true);
+ }
+
+ /**
+ * This method sets the component to use as the nonContinuousLayoutDivider.
+ *
+ * @param newDivider The component to use as the nonContinuousLayoutDivider.
+ * @param rememberSizes FIXME: document.
+ */
+ protected void setNonContinuousLayoutDivider(Component newDivider,
+ boolean rememberSizes)
+ {
+ // FIXME: use rememberSizes for something
+ nonContinuousLayoutDivider = newDivider;
+ }
+
+ /**
+ * This method returns the nonContinuousLayoutDivider.
+ *
+ * @return The nonContinuousLayoutDivider.
+ */
+ public Component getNonContinuousLayoutDivider()
+ {
+ return nonContinuousLayoutDivider;
+ }
+
+ /**
+ * This method returns the JSplitPane that this BasicSplitPaneUI draws.
+ *
+ * @return The JSplitPane.
+ */
+ public JSplitPane getSplitPane()
+ {
+ return splitPane;
+ }
+
+ /**
+ * This method creates the divider used normally with the JSplitPane.
+ *
+ * @return The default divider.
+ */
+ public BasicSplitPaneDivider createDefaultDivider()
+ {
+ if (divider == null)
+ divider = new BasicSplitPaneDivider(this);
+ return divider;
+ }
+
+ /**
+ * This method is called when JSplitPane's resetToPreferredSizes is called.
+ * It resets the sizes of all components in the JSplitPane.
+ *
+ * @param jc The JSplitPane to reset.
+ */
+ public void resetToPreferredSizes(JSplitPane jc)
+ {
+ layoutManager.resetToPreferredSizes();
+ }
+
+ /**
+ * This method sets the location of the divider.
+ *
+ * @param jc The JSplitPane to set the divider location in.
+ * @param location The new location of the divider.
+ */
+ public void setDividerLocation(JSplitPane jc, int location)
+ {
+ dividerLocationSet = true;
+ splitPane.revalidate();
+ splitPane.repaint();
+ }
+
+ /**
+ * This method returns the location of the divider.
+ *
+ * @param jc The JSplitPane to retrieve the location for.
+ *
+ * @return The location of the divider.
+ */
+ public int getDividerLocation(JSplitPane jc)
+ {
+ int loc;
+ if (jc.getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
+ loc = divider.getX();
+ else
+ loc = divider.getY();
+ return loc;
+ }
+
+ /**
+ * This method returns the smallest value possible for the location of the
+ * divider.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The minimum divider location.
+ */
+ public int getMinimumDividerLocation(JSplitPane jc)
+ {
+ int value = layoutManager.getInitialLocation(jc.getInsets());
+ if (layoutManager.components[0] != null)
+ value += layoutManager.minimumSizeOfComponent(0);
+ return value;
+ }
+
+ /**
+ * This method returns the largest value possible for the location of the
+ * divider.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The maximum divider location.
+ */
+ public int getMaximumDividerLocation(JSplitPane jc)
+ {
+ int value = layoutManager.getInitialLocation(jc.getInsets())
+ + layoutManager.getAvailableSize(jc.getSize(), jc.getInsets())
+ - splitPane.getDividerSize();
+ if (layoutManager.components[1] != null)
+ value -= layoutManager.minimumSizeOfComponent(1);
+ return value;
+ }
+
+ /**
+ * This method is called after the children of the JSplitPane are painted.
+ *
+ * @param jc The JSplitPane.
+ * @param g The Graphics object to paint with.
+ */
+ public void finishedPaintingChildren(JSplitPane jc, Graphics g)
+ {
+ if (! splitPane.isContinuousLayout() && nonContinuousLayoutDivider != null
+ && nonContinuousLayoutDivider.isVisible())
+ javax.swing.SwingUtilities.paintComponent(g, nonContinuousLayoutDivider,
+ null,
+ nonContinuousLayoutDivider
+ .getBounds());
+ }
+
+ /**
+ * This method is called to paint the JSplitPane.
+ *
+ * @param g The Graphics object to paint with.
+ * @param jc The JSplitPane to paint.
+ */
+ public void paint(Graphics g, JComponent jc)
+ {
+ // TODO: What should be done here?
+ }
+
+ /**
+ * This method returns the preferred size of the JSplitPane.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The preferred size of the JSplitPane.
+ */
+ public Dimension getPreferredSize(JComponent jc)
+ {
+ return layoutManager.preferredLayoutSize(jc);
+ }
+
+ /**
+ * This method returns the minimum size of the JSplitPane.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The minimum size of the JSplitPane.
+ */
+ public Dimension getMinimumSize(JComponent jc)
+ {
+ return layoutManager.minimumLayoutSize(jc);
+ }
+
+ /**
+ * This method returns the maximum size of the JSplitPane.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The maximum size of the JSplitPane.
+ */
+ public Dimension getMaximumSize(JComponent jc)
+ {
+ return layoutManager.maximumLayoutSize(jc);
+ }
+
+ /**
+ * This method returns the border insets of the current border.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The current border insets.
+ */
+ public Insets getInsets(JComponent jc)
+ {
+ return splitPane.getBorder().getBorderInsets(splitPane);
+ }
+
+ /**
+ * This method resets the current layout manager. The type of layout manager
+ * is dependent on the current orientation.
+ */
+ protected void resetLayoutManager()
+ {
+ if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
+ layoutManager = new BasicHorizontalLayoutManager();
+ else
+ layoutManager = new BasicVerticalLayoutManager();
+ getSplitPane().setLayout(layoutManager);
+ layoutManager.updateComponents();
+
+ // invalidating by itself does not invalidate the layout.
+ getSplitPane().revalidate();
+ }
+
+ /**
+ * This method is called when dragging starts. It resets lastDragLocation
+ * and dividerSize.
+ */
+ protected void startDragging()
+ {
+ Component left = splitPane.getLeftComponent();
+ Component right = splitPane.getRightComponent();
+ dividerSize = divider.getDividerSize();
+ setLastDragLocation(-1);
+
+ if ((left != null && !left.isLightweight())
+ || (right != null && !right.isLightweight()))
+ draggingHW = true;
+
+ if (splitPane.isContinuousLayout())
+ nonContinuousLayoutDivider.setVisible(false);
+ else
+ {
+ nonContinuousLayoutDivider.setVisible(true);
+ nonContinuousLayoutDivider.setBounds(divider.getBounds());
+ }
+ }
+
+ /**
+ * This method is called whenever the divider is dragged. If the JSplitPane
+ * is in continuousLayout mode, the divider needs to be moved and the
+ * JSplitPane needs to be laid out.
+ *
+ * @param location The new location of the divider.
+ */
+ protected void dragDividerTo(int location)
+ {
+ location = validLocation(location);
+ if (beginDragDividerLocation == -1)
+ beginDragDividerLocation = location;
+
+ if (splitPane.isContinuousLayout())
+ splitPane.setDividerLocation(location);
+ else
+ {
+ Point p = nonContinuousLayoutDivider.getLocation();
+ if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
+ p.x = location;
+ else
+ p.y = location;
+ nonContinuousLayoutDivider.setLocation(p);
+ }
+ setLastDragLocation(location);
+ splitPane.repaint();
+ }
+
+ /**
+ * This method is called when the dragging is finished.
+ *
+ * @param location The location where the drag finished.
+ */
+ protected void finishDraggingTo(int location)
+ {
+ if (nonContinuousLayoutDivider != null)
+ nonContinuousLayoutDivider.setVisible(false);
+ draggingHW = false;
+ location = validLocation(location);
+ splitPane.setDividerLocation(location);
+ splitPane.setLastDividerLocation(beginDragDividerLocation);
+ beginDragDividerLocation = -1;
+ }
+
+ /**
+ * This method returns the width of one of the sides of the divider's border.
+ *
+ * @return The width of one side of the divider's border.
+ *
+ * @deprecated 1.3
+ */
+ protected int getDividerBorderSize()
+ {
+ if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
+ return divider.getBorder().getBorderInsets(divider).left;
+ else
+ return divider.getBorder().getBorderInsets(divider).top;
+ }
+
+ /**
+ * This is a helper method that returns a valid location for the divider
+ * when dragging.
+ *
+ * @param location The location to check.
+ *
+ * @return A valid location.
+ */
+ private int validLocation(int location)
+ {
+ int min = getMinimumDividerLocation(splitPane);
+ int max = getMaximumDividerLocation(splitPane);
+ if (min > 0 && location < min)
+ return min;
+ if (max > 0 && location > max)
+ return max;
+ return location;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java
new file mode 100644
index 000000000..c42f9caed
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java
@@ -0,0 +1,4003 @@
+/* BasicTabbedPaneUI.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.JViewport;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TabbedPaneUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.text.View;
+
+/**
+ * This is the Basic Look and Feel's UI delegate for JTabbedPane.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ * @author Kim Ho (kho@redhat.com)
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
+{
+
+ static class NavigateAction extends AbstractAction
+ {
+ int direction;
+
+ NavigateAction(String name, int dir)
+ {
+ super(name);
+ direction = dir;
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTabbedPane tp = (JTabbedPane) event.getSource();
+ BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
+
+ ui.navigateSelectedTab(direction);
+ }
+
+ }
+
+ static class NavigatePageDownAction extends AbstractAction
+ {
+
+ public NavigatePageDownAction()
+ {
+ super("navigatePageDown");
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTabbedPane tp = (JTabbedPane) event.getSource();
+ BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
+
+ int i = tp.getSelectedIndex();
+
+ if (i < 0)
+ i = 0;
+
+ ui.selectNextTabInRun(i);
+ }
+
+ }
+
+ static class NavigatePageUpAction extends AbstractAction
+ {
+
+ public NavigatePageUpAction()
+ {
+ super("navigatePageUp");
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTabbedPane tp = (JTabbedPane) event.getSource();
+ BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
+
+ int i = tp.getSelectedIndex();
+
+ if (i < 0)
+ i = 0;
+
+ ui.selectPreviousTabInRun(i);
+
+ }
+ }
+
+ static class RequestFocusAction extends AbstractAction
+ {
+
+ public RequestFocusAction()
+ {
+ super("requestFocus");
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ ((JTabbedPane) event.getSource()).requestFocus();
+ }
+
+ }
+
+ static class RequestFocusForVisibleComponentAction extends AbstractAction
+ {
+
+ public RequestFocusForVisibleComponentAction()
+ {
+ super("requestFocusForVisibleComponent");
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTabbedPane tp = (JTabbedPane) event.getSource();
+
+ // FIXME: This should select a suitable component within
+ // the tab content. However I dont know whether we have
+ // to search for this component or wether the called is
+ // supposed to do that.
+ tp.getSelectedComponent().requestFocus();
+ }
+
+ }
+
+ /**
+ * A helper class that handles focus.
+ * <p>The purpose of this class is to implement a more flexible focus
+ * handling for the tabbed pane, which is used to determine whether the
+ * focus indicator should be painted or not. When in scrolling layout
+ * mode the area containing the tabs is a scrollpane, so simply testing
+ * whether the tabbed pane has the focus does not work.</p>
+ * <p>The <code>FocusHandler</code> is installed on the scrollpane and
+ * the tabbed pane and sets the variable <code>hasFocus</code> to
+ * <code>false</code> only when both components do not hold the focus.</p>
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class FocusHandler extends FocusAdapter
+ {
+ /**
+ * This method is called when the component gains focus.
+ *
+ * @param e The FocusEvent.
+ */
+ public void focusGained(FocusEvent e)
+ {
+ Object source = e.getSource();
+ if (source == panel )
+ tabPane.requestFocus();
+ else if (source == tabPane)
+ tabPane.repaint();
+ }
+
+ /**
+ * This method is called when the component loses focus.
+ *
+ * @param e The FocusEvent.
+ */
+ public void focusLost(FocusEvent e)
+ {
+ if (e.getOppositeComponent() == tabPane.getSelectedComponent())
+ tabPane.requestFocus();
+ else if (e.getSource() == tabPane)
+ tabPane.repaint();
+ }
+ }
+
+ /**
+ * A helper class for determining if mouse presses occur inside tabs and
+ * sets the index appropriately. In SCROLL_TAB_MODE, this class also
+ * handles the mouse clicks on the scrolling buttons.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class MouseHandler extends MouseAdapter
+ {
+ public void mouseReleased(MouseEvent e)
+ {
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+ }
+
+ /**
+ * This method is called when the mouse is pressed. The index cannot
+ * change to a tab that is not enabled.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
+ int placement = tabPane.getTabPlacement();
+
+ if (s == incrButton)
+ {
+ if(!incrButton.isEnabled())
+ return;
+
+ currentScrollLocation++;
+
+ switch (placement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ currentScrollOffset = getTabAreaInsets(placement).left;
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].width;
+ break;
+ default:
+ currentScrollOffset = getTabAreaInsets(placement).top;
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].height;
+ break;
+ }
+
+ updateViewPosition();
+ updateButtons();
+
+ tabPane.repaint();
+ }
+ else if (s == decrButton)
+ {
+ if(!decrButton.isEnabled())
+ return;
+
+ // The scroll location may be zero but the offset
+ // greater than zero because of an adjustement to
+ // make a partially visible tab completely visible.
+ if (currentScrollLocation > 0)
+ currentScrollLocation--;
+
+ // Set the offset back to 0 and recompute it.
+ currentScrollOffset = 0;
+
+ switch (placement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ // Take the tab area inset into account.
+ if (currentScrollLocation > 0)
+ currentScrollOffset = getTabAreaInsets(placement).left;
+ // Recompute scroll offset.
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].width;
+ break;
+ default:
+ // Take the tab area inset into account.
+ if (currentScrollLocation > 0)
+ currentScrollOffset = getTabAreaInsets(placement).top;
+
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].height;
+ }
+
+ updateViewPosition();
+ updateButtons();
+
+ tabPane.repaint();
+ }
+ else if (tabPane.isEnabled())
+ {
+ int index = tabForCoordinate(tabPane, e.getX(), e.getY());
+ if (!tabPane.isEnabledAt(index))
+ return;
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT
+ && s == panel)
+ {
+ scrollTab(index, placement);
+
+ tabPane.setSelectedIndex(index);
+ tabPane.repaint();
+ }
+ else
+ {
+ tabPane.setSelectedIndex(index);
+ tabPane.revalidate();
+ tabPane.repaint();
+ }
+
+ }
+
+ }
+
+ /**
+ * Receives notification when the mouse pointer has entered the tabbed
+ * pane.
+ *
+ * @param e the mouse event
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
+ int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
+ setRolloverTab(tabIndex);
+ }
+
+ /**
+ * Receives notification when the mouse pointer has exited the tabbed
+ * pane.
+ *
+ * @param e the mouse event
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
+ setRolloverTab(-1);
+ }
+
+ /**
+ * Receives notification when the mouse pointer has moved over the tabbed
+ * pane.
+ *
+ * @param ev the mouse event
+ */
+ public void mouseMoved(MouseEvent ev)
+ {
+ Object s = ev.getSource();
+
+ if (tabPane != ev.getSource())
+ {
+ ev.setSource(tabPane);
+ tabPane.dispatchEvent(ev);
+
+ ev.setSource(s);
+ }
+
+ int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY());
+ setRolloverTab(tabIndex);
+ }
+
+ /** Modifies the mouse event to originate from
+ * the tabbed pane and redispatches it.
+ *
+ * @param me
+ */
+ void redispatchEvent(MouseEvent me)
+ {
+ me.setSource(tabPane);
+ Point viewPos = viewport.getViewPosition();
+ viewPos.x -= viewport.getX();
+ viewPos.y -= viewport.getY();
+ me.translatePoint(-viewPos.x, -viewPos.y);
+ tabPane.dispatchEvent(me);
+
+ me.translatePoint(viewPos.x, viewPos.y);
+ }
+
+ }
+
+ /**
+ * This class handles PropertyChangeEvents fired from the JTabbedPane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called whenever one of the properties of the JTabbedPane
+ * changes.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ out:
+ {
+ if (e.getPropertyName().equals("tabLayoutPolicy"))
+ {
+ currentScrollLocation = currentScrollOffset = 0;
+
+ layoutManager = createLayoutManager();
+
+ tabPane.setLayout(layoutManager);
+ }
+ else if (e.getPropertyName().equals("tabPlacement")
+ && tabPane.getTabLayoutPolicy()
+ == JTabbedPane.SCROLL_TAB_LAYOUT)
+ {
+ incrButton = createIncreaseButton();
+ decrButton = createDecreaseButton();
+
+ // If the tab placement value was changed of a tabbed pane
+ // in SCROLL_TAB_LAYOUT mode we investigate the change to
+ // implement the following behavior which was observed in
+ // the RI:
+ // The scrolling offset will be reset if we change to
+ // a direction which is orthogonal to the current
+ // direction and stays the same if it is parallel.
+
+ int oldPlacement = ((Integer) e.getOldValue()).intValue();
+ int newPlacement = ((Integer) e.getNewValue()).intValue();
+ switch (newPlacement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ if (oldPlacement == JTabbedPane.TOP
+ || oldPlacement == JTabbedPane.BOTTOM)
+ break out;
+
+ currentScrollOffset = getTabAreaInsets(newPlacement).left;
+ break;
+ default:
+ if (oldPlacement == JTabbedPane.LEFT
+ || oldPlacement == JTabbedPane.RIGHT)
+ break out;
+
+ currentScrollOffset = getTabAreaInsets(newPlacement).top;
+ }
+
+ updateViewPosition();
+ updateButtons();
+ }
+ }
+
+ tabPane.revalidate();
+ tabPane.repaint();
+ }
+ }
+
+ /**
+ * A LayoutManager responsible for placing all the tabs and the visible
+ * component inside the JTabbedPane. This class is only used for
+ * WRAP_TAB_LAYOUT.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TabbedPaneLayout implements LayoutManager
+ {
+ /**
+ * This method is called when a component is added to the JTabbedPane.
+ *
+ * @param name The name of the component.
+ * @param comp The component being added.
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called when the rectangles need to be calculated. It
+ * also fixes the size of the visible component.
+ */
+ public void calculateLayoutInfo()
+ {
+ int count = tabPane.getTabCount();
+ assureRectsCreated(count);
+ calculateTabRects(tabPane.getTabPlacement(), count);
+ tabRunsDirty = false;
+ }
+
+ /**
+ * This method calculates the size of the the JTabbedPane.
+ *
+ * @param minimum Whether the JTabbedPane will try to be as small as it
+ * can.
+ *
+ * @return The desired size of the JTabbedPane.
+ */
+ protected Dimension calculateSize(boolean minimum)
+ {
+ int tabPlacement = tabPane.getTabPlacement();
+
+ int width = 0;
+ int height = 0;
+ Component c;
+ Dimension dims;
+
+ // Find out the minimum/preferred size to display the largest child
+ // of the tabbed pane.
+ int count = tabPane.getTabCount();
+ for (int i = 0; i < count; i++)
+ {
+ c = tabPane.getComponentAt(i);
+ if (c == null)
+ continue;
+ dims = minimum ? c.getMinimumSize() : c.getPreferredSize();
+ if (dims != null)
+ {
+ height = Math.max(height, dims.height);
+ width = Math.max(width, dims.width);
+ }
+ }
+
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ width = Math.max(calculateMaxTabWidth(tabPlacement), width);
+
+ height += preferredTabAreaHeight(tabPlacement,
+ width - tabAreaInsets.left
+ - tabAreaInsets.right);
+ }
+ else
+ {
+ height = Math.max(calculateMaxTabHeight(tabPlacement), height);
+
+ width += preferredTabAreaWidth(tabPlacement,
+ height - tabAreaInsets.top
+ - tabAreaInsets.bottom);
+ }
+
+ Insets tabPaneInsets = tabPane.getInsets();
+ return new Dimension(width + tabPaneInsets.left + tabPaneInsets.right,
+ height + tabPaneInsets.top + tabPaneInsets.bottom);
+ }
+
+ // if tab placement is LEFT OR RIGHT, they share width.
+ // if tab placement is TOP OR BOTTOM, they share height
+ // PRE STEP: finds the default sizes for the labels as well as their
+ // locations.
+ // AND where they will be placed within the run system.
+ // 1. calls normalizeTab Runs.
+ // 2. calls rotate tab runs.
+ // 3. pads the tab runs.
+ // 4. pads the selected tab.
+
+ /**
+ * This method is called to calculate the tab rectangles. This method
+ * will calculate the size and position of all rectangles (taking into
+ * account which ones should be in which tab run). It will pad them and
+ * normalize them as necessary.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabCount The run the current selection is in.
+ */
+ protected void calculateTabRects(int tabPlacement, int tabCount)
+ {
+ Insets insets = tabPane.getInsets();
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ Dimension size = tabPane.getSize();
+
+ // The coordinates of the upper left corner of the tab area.
+ int x;
+ int y;
+ // The location at which the runs must be broken.
+ int breakAt;
+
+ // Calculate the bounds for the tab area.
+ switch (tabPlacement)
+ {
+ case LEFT:
+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ x = insets.left + tabAreaInsets.left;
+ y = insets.top + tabAreaInsets.top;
+ breakAt = size.height - (insets.bottom + tabAreaInsets.bottom);
+ break;
+ case RIGHT:
+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ x = size.width - (insets.right + tabAreaInsets.right)
+ - maxTabWidth - 1;
+ y = insets.top + tabAreaInsets.top;
+ breakAt = size.height - (insets.bottom + tabAreaInsets.bottom);
+ break;
+ case BOTTOM:
+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ x = insets.left + tabAreaInsets.left;
+ y = size.height - (insets.bottom + tabAreaInsets.bottom)
+ - maxTabHeight - 1;
+ breakAt = size.width - (insets.right + tabAreaInsets.right);
+ break;
+ case TOP:
+ default:
+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ x = insets.left + tabAreaInsets.left;
+ y = insets.top + tabAreaInsets.top;
+ breakAt = size.width - (insets.right + tabAreaInsets.right);
+ break;
+ }
+
+ if (tabCount == 0)
+ return;
+
+ FontMetrics fm = getFontMetrics();
+ runCount = 0;
+ selectedRun = -1;
+ int selectedIndex = tabPane.getSelectedIndex();
+ if (selectedIndex < 0)
+ selectedIndex = 0;
+
+ Rectangle rect;
+
+ // Go through all the tabs and build the tab runs.
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ for (int i = 0; i < tabCount; i++)
+ {
+ rect = rects[i];
+ if (i > 0)
+ {
+ rect.x = rects[i - 1].x + rects[i - 1].width;
+ }
+ else
+ {
+ tabRuns[0] = 0;
+ runCount = 1;
+ maxTabWidth = 0;
+ rect.x = x;
+ }
+ rect.width = calculateTabWidth(tabPlacement, i, fm);
+ maxTabWidth = Math.max(maxTabWidth, rect.width);
+
+ if (rect.x != 2 + insets.left && rect.x + rect.width > breakAt)
+ {
+ if (runCount > tabRuns.length - 1)
+ expandTabRunsArray();
+ tabRuns[runCount] = i;
+ runCount++;
+ rect.x = x;
+ }
+
+ rect.y = y;
+ rect.height = maxTabHeight;
+ if (i == selectedIndex)
+ selectedRun = runCount - 1;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < tabCount; i++)
+ {
+ rect = rects[i];
+ if (i > 0)
+ {
+ rect.y = rects[i - 1].y + rects[i - 1].height;
+ }
+ else
+ {
+ tabRuns[0] = 0;
+ runCount = 1;
+ maxTabHeight = 0;
+ rect.y = y;
+ }
+ rect.height = calculateTabHeight(tabPlacement, i,
+ fm.getHeight());
+ maxTabHeight = Math.max(maxTabHeight, rect.height);
+
+ if (rect.y != 2 + insets.top && rect.y + rect.height > breakAt)
+ {
+ if (runCount > tabRuns.length - 1)
+ expandTabRunsArray();
+ tabRuns[runCount] = i;
+ runCount++;
+ rect.y = y;
+ }
+
+ rect.x = x;
+ rect.width = maxTabWidth;
+
+ if (i == selectedIndex)
+ selectedRun = runCount - 1;
+ }
+ }
+
+ if (runCount > 1)
+ {
+ int start;
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ start = x;
+ else
+ start = y;
+ normalizeTabRuns(tabPlacement, tabCount, start, breakAt);
+ selectedRun = getRunForTab(tabCount, selectedIndex);
+ if (shouldRotateTabRuns(tabPlacement))
+ {
+ rotateTabRuns(tabPlacement, selectedRun);
+ }
+ }
+
+ // Suppress padding if we have only one tab run.
+ if (runCount == 1)
+ return;
+
+ // Pad the runs.
+ int tabRunOverlay = getTabRunOverlay(tabPlacement);
+ for (int i = runCount - 1; i >= 0; --i)
+ {
+ int start = tabRuns[i];
+ int nextIndex;
+ if (i == runCount - 1)
+ nextIndex = 0;
+ else
+ nextIndex = i + 1;
+ int next = tabRuns[nextIndex];
+ int end = next != 0 ? next - 1 : tabCount - 1;
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ for (int j = start; j <= end; ++j)
+ {
+ rect = rects[j];
+ rect.y = y;
+ rect.x += getTabRunIndent(tabPlacement, i);
+ }
+ if (shouldPadTabRun(tabPlacement, i))
+ {
+ padTabRun(tabPlacement, start, end, breakAt);
+ }
+ if (tabPlacement == BOTTOM)
+ y -= maxTabHeight - tabRunOverlay;
+ else
+ y += maxTabHeight - tabRunOverlay;
+ }
+ else
+ {
+ for (int j = start; j <= end; ++j)
+ {
+ rect = rects[j];
+ rect.x = x;
+ rect.y += getTabRunIndent(tabPlacement, i);
+ }
+ if (shouldPadTabRun(tabPlacement, i))
+ {
+ padTabRun(tabPlacement, start, end, breakAt);
+ }
+ if (tabPlacement == RIGHT)
+ x -= maxTabWidth - tabRunOverlay;
+ else
+ x += maxTabWidth - tabRunOverlay;
+
+ }
+ }
+ padSelectedTab(tabPlacement, selectedIndex);
+ }
+
+ /**
+ * This method is called when the JTabbedPane is laid out in
+ * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions
+ * of all its components.
+ *
+ * @param parent The Container to lay out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ calculateLayoutInfo();
+
+ int tabPlacement = tabPane.getTabPlacement();
+ Insets insets = tabPane.getInsets();
+
+ int selectedIndex = tabPane.getSelectedIndex();
+
+ Component selectedComponent = null;
+ if (selectedIndex >= 0)
+ selectedComponent = tabPane.getComponentAt(selectedIndex);
+ // The RI doesn't seem to change the component if the new selected
+ // component == null. This is probably so that applications can add
+ // one single component for every tab.
+ if (selectedComponent != null)
+ {
+ setVisibleComponent(selectedComponent);
+ }
+
+ int childCount = tabPane.getComponentCount();
+ if (childCount > 0)
+ {
+ int compX;
+ int compY;
+ int tabAreaWidth = 0;
+ int tabAreaHeight = 0;
+ switch (tabPlacement)
+ {
+ case LEFT:
+ tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount,
+ maxTabWidth);
+ compX = tabAreaWidth + insets.left + contentBorderInsets.left;
+ compY = insets.top + contentBorderInsets.top;
+ break;
+ case RIGHT:
+ tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount,
+ maxTabWidth);
+ compX = insets.left + contentBorderInsets.left;
+ compY = insets.top + contentBorderInsets.top;
+ break;
+ case BOTTOM:
+ tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount,
+ maxTabHeight);
+ compX = insets.left + contentBorderInsets.left;
+ compY = insets.top + contentBorderInsets.top;
+ break;
+ case TOP:
+ default:
+ tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount,
+ maxTabHeight);
+
+ compX = insets.left + contentBorderInsets.left;
+ compY = tabAreaHeight + insets.top + contentBorderInsets.top;
+ }
+ Rectangle bounds = tabPane.getBounds();
+ int compWidth = bounds.width - tabAreaWidth - insets.left
+ - insets.right - contentBorderInsets.left
+ - contentBorderInsets.right;
+ int compHeight = bounds.height - tabAreaHeight - insets.top
+ - insets.bottom - contentBorderInsets.top
+ - contentBorderInsets.bottom;
+
+
+ for (int i = 0; i < childCount; ++i)
+ {
+ Component c = tabPane.getComponent(i);
+ c.setBounds(compX, compY, compWidth, compHeight);
+ }
+ }
+ }
+
+ /**
+ * This method returns the minimum layout size for the given container.
+ *
+ * @param parent The container that is being sized.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return calculateSize(true);
+ }
+
+ // If there is more free space in an adjacent run AND the tab
+ // in the run can fit in the adjacent run, move it. This method
+ // is not perfect, it is merely an approximation.
+ // If you play around with Sun's JTabbedPane, you'll see that
+ // it does do some pretty strange things with regards to not moving tabs
+ // that should be moved.
+ // start = the x position where the tabs will begin
+ // max = the maximum position of where the tabs can go to
+ // (tabAreaInsets.left + the width of the tab area)
+
+ /**
+ * This method tries to "even out" the number of tabs in each run based on
+ * their widths.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabCount The number of tabs.
+ * @param start The x position where the tabs will begin.
+ * @param max The maximum x position where the tab can run to.
+ */
+ protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
+ int max)
+ {
+ boolean horizontal = tabPlacement == TOP || tabPlacement == BOTTOM;
+ int currentRun = runCount - 1;
+ double weight = 1.25;
+ for (boolean adjust = true; adjust == true;)
+ {
+ int last = lastTabInRun(tabCount, currentRun);
+ int prevLast = lastTabInRun(tabCount, currentRun - 1);
+ int end;
+ int prevLength;
+ if (horizontal)
+ {
+ end = rects[last].x + rects[last].width;
+ prevLength = (int) (maxTabWidth * weight);
+ }
+ else
+ {
+ end = rects[last].y + rects[last].height;
+ prevLength = (int) (maxTabWidth * weight * 2);
+ }
+ if (max - end > prevLength)
+ {
+ tabRuns[currentRun] = prevLast;
+ if (horizontal)
+ rects[prevLast].x = start;
+ else
+ rects[prevLast].y = start;
+ for (int i = prevLast + 1; i <= last; i++)
+ {
+ if (horizontal)
+ rects[i].x = rects[i - 1].x + rects[i - 1].width;
+ else
+ rects[i].y = rects[i - 1].y + rects[i - 1].height;
+ }
+ }
+ else if (currentRun == runCount - 1)
+ adjust = false;
+ if (currentRun - 1 > 0)
+ currentRun -= 1;
+ else
+ {
+ // Check again, but with higher ratio to avoid
+ // clogging up the last run.
+ currentRun = runCount - 1;
+ weight += 0.25;
+ }
+ }
+ }
+
+ /**
+ * This method pads the tab at the selected index by the selected tab pad
+ * insets (so that it looks larger).
+ *
+ * @param tabPlacement The placement of the tabs.
+ * @param selectedIndex The selected index.
+ */
+ protected void padSelectedTab(int tabPlacement, int selectedIndex)
+ {
+ Insets insets = getSelectedTabPadInsets(tabPlacement);
+ rects[selectedIndex].x -= insets.left;
+ rects[selectedIndex].y -= insets.top;
+ rects[selectedIndex].width += insets.left + insets.right;
+ rects[selectedIndex].height += insets.top + insets.bottom;
+ }
+
+ // If the tabs on the run don't fill the width of the window, make it
+ // fit now.
+ // start = starting index of the run
+ // end = last index of the run
+ // max = tabAreaInsets.left + width (or equivalent)
+ // assert start <= end.
+
+ /**
+ * This method makes each tab in the run larger so that the tabs expand
+ * to fill the runs width/height (depending on tabPlacement).
+ *
+ * @param tabPlacement The placement of the tabs.
+ * @param start The index of the first tab.
+ * @param end The last index of the tab
+ * @param max The amount of space in the run (width for TOP and BOTTOM
+ * tabPlacement).
+ */
+ protected void padTabRun(int tabPlacement, int start, int end, int max)
+ {
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ int runWidth = rects[end].x + rects[end].width;
+ int spaceRemaining = max - runWidth;
+ int numTabs = end - start + 1;
+
+ // now divvy up the space.
+ int spaceAllocated = spaceRemaining / numTabs;
+ int currX = rects[start].x;
+ for (int i = start; i <= end; i++)
+ {
+ rects[i].x = currX;
+ rects[i].width += spaceAllocated;
+
+ currX += rects[i].width;
+ // This is used because since the spaceAllocated
+ // variable is an int, it rounds down. Sometimes,
+ // we don't fill an entire row, so we make it do
+ // so now.
+
+ if (i == end && rects[i].x + rects[i].width != max)
+ rects[i].width = max - rects[i].x;
+ }
+ }
+ else
+ {
+ int runHeight = rects[end].y + rects[end].height;
+ int spaceRemaining = max - runHeight;
+ int numTabs = end - start + 1;
+
+ int spaceAllocated = spaceRemaining / numTabs;
+ int currY = rects[start].y;
+ for (int i = start; i <= end; i++)
+ {
+ rects[i].y = currY;
+ rects[i].height += spaceAllocated;
+ currY += rects[i].height;
+ if (i == end && rects[i].y + rects[i].height != max)
+ rects[i].height = max - rects[i].y;
+ }
+ }
+ }
+
+ /**
+ * This method returns the preferred layout size for the given container.
+ *
+ * @param parent The container to size.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ return calculateSize(false);
+ }
+
+ /**
+ * This method returns the preferred tab height given a tabPlacement and
+ * width.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param width The expected width.
+ *
+ * @return The preferred tab area height.
+ */
+ protected int preferredTabAreaHeight(int tabPlacement, int width)
+ {
+ if (tabPane.getTabCount() == 0)
+ return calculateTabAreaHeight(tabPlacement, 0, 0);
+
+ int runs = 0;
+ int runWidth = 0;
+ int tabWidth = 0;
+
+ FontMetrics fm = getFontMetrics();
+
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ Insets insets = tabPane.getInsets();
+
+ // Only interested in width, this is a messed up rectangle now.
+ width -= tabAreaInsets.left + tabAreaInsets.right + insets.left
+ + insets.right;
+
+ // The reason why we can't use runCount:
+ // This method is only called to calculate the size request
+ // for the tabbedPane. However, this size request is dependent on
+ // our desired width. We need to find out what the height would
+ // be IF we got our desired width.
+ for (int i = 0; i < tabPane.getTabCount(); i++)
+ {
+ tabWidth = calculateTabWidth(tabPlacement, i, fm);
+ if (runWidth + tabWidth > width)
+ {
+ runWidth = tabWidth;
+ runs++;
+ }
+ else
+ runWidth += tabWidth;
+ }
+ runs++;
+
+ int maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
+ maxTabHeight);
+ return tabAreaHeight;
+ }
+
+ /**
+ * This method calculates the preferred tab area width given a tab
+ * placement and height.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param height The expected height.
+ *
+ * @return The preferred tab area width.
+ */
+ protected int preferredTabAreaWidth(int tabPlacement, int height)
+ {
+ if (tabPane.getTabCount() == 0)
+ return calculateTabAreaHeight(tabPlacement, 0, 0);
+
+ int runs = 0;
+ int runHeight = 0;
+ int tabHeight = 0;
+
+ FontMetrics fm = getFontMetrics();
+
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ Insets insets = tabPane.getInsets();
+
+ height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top
+ + insets.bottom;
+ int fontHeight = fm.getHeight();
+
+ for (int i = 0; i < tabPane.getTabCount(); i++)
+ {
+ tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
+ if (runHeight + tabHeight > height)
+ {
+ runHeight = tabHeight;
+ runs++;
+ }
+ else
+ runHeight += tabHeight;
+ }
+ runs++;
+
+ int maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs,
+ maxTabWidth);
+ return tabAreaWidth;
+ }
+
+ /**
+ * This method rotates the places each run in the correct place the
+ * tabRuns array. See the comment for tabRuns for how the runs are placed
+ * in the array.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedRun The run the current selection is in.
+ */
+ protected void rotateTabRuns(int tabPlacement, int selectedRun)
+ {
+ if (runCount == 1 || selectedRun == 0 || selectedRun == -1)
+ return;
+ int[] newTabRuns = new int[tabRuns.length];
+ int currentRun = selectedRun;
+ int i = 0;
+ do
+ {
+ newTabRuns[i] = tabRuns[currentRun];
+ currentRun = getNextTabRun(currentRun);
+ i++;
+ }
+ while (i < runCount);
+
+ tabRuns = newTabRuns;
+ BasicTabbedPaneUI.this.selectedRun = 1;
+ }
+
+ /**
+ * This method is called when a component is removed from the
+ * JTabbedPane.
+ *
+ * @param comp The component removed.
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * This class acts as the LayoutManager for the JTabbedPane in
+ * SCROLL_TAB_MODE.
+ */
+ private class TabbedPaneScrollLayout extends TabbedPaneLayout
+ {
+ /**
+ * This method returns the preferred layout size for the given container.
+ *
+ * @param parent The container to calculate a size for.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ return super.calculateSize(false);
+ }
+
+ /**
+ * This method returns the minimum layout size for the given container.
+ *
+ * @param parent The container to calculate a size for.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return super.calculateSize(true);
+ }
+
+ /**
+ * This method calculates the tab area height given a desired width.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param width The expected width.
+ *
+ * @return The tab area height given the width.
+ */
+ protected int preferredTabAreaHeight(int tabPlacement, int width)
+ {
+ if (tabPane.getTabCount() == 0)
+ return calculateTabAreaHeight(tabPlacement, 0, 0);
+
+ int runs = 1;
+
+ int maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
+ maxTabHeight);
+ return tabAreaHeight;
+ }
+
+ /**
+ * This method calculates the tab area width given a desired height.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param height The expected height.
+ *
+ * @return The tab area width given the height.
+ */
+ protected int preferredTabAreaWidth(int tabPlacement, int height)
+ {
+ if (tabPane.getTabCount() == 0)
+ return calculateTabAreaHeight(tabPlacement, 0, 0);
+
+ int runs = 1;
+
+ int maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
+ return tabAreaWidth;
+ }
+
+ /**
+ * This method is called to calculate the tab rectangles. This method
+ * will calculate the size and position of all rectangles (taking into
+ * account which ones should be in which tab run). It will pad them and
+ * normalize them as necessary.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabCount The number of tabs.
+ */
+ protected void calculateTabRects(int tabPlacement, int tabCount)
+ {
+ if (tabCount == 0)
+ return;
+
+ FontMetrics fm = getFontMetrics();
+ SwingUtilities.calculateInnerArea(tabPane, calcRect);
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ Insets insets = tabPane.getInsets();
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ int maxHeight = calculateMaxTabHeight(tabPlacement);
+ calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
+ int width = 0;
+ int runWidth = tabAreaInsets.left + insets.left;
+ int top = insets.top + tabAreaInsets.top;
+ for (int i = 0; i < tabCount; i++)
+ {
+ width = calculateTabWidth(tabPlacement, i, fm);
+
+ // The proper instances should exists because
+ // assureRectsCreated() was being run already.
+ rects[i].setBounds(runWidth, top, width, maxHeight);
+
+ runWidth += width;
+ }
+ tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
+ tabAreaRect.height = maxTabHeight + tabAreaInsets.top
+ + tabAreaInsets.bottom;
+ contentRect.width = tabAreaRect.width;
+ contentRect.height = tabPane.getHeight() - insets.top
+ - insets.bottom - tabAreaRect.height;
+ contentRect.x = insets.left;
+ tabAreaRect.x = insets.left;
+ if (tabPlacement == SwingConstants.BOTTOM)
+ {
+ contentRect.y = insets.top;
+ tabAreaRect.y = contentRect.y + contentRect.height;
+ }
+ else
+ {
+ tabAreaRect.y = insets.top;
+ contentRect.y = tabAreaRect.y + tabAreaRect.height;
+ }
+ }
+ else
+ {
+ int maxWidth = calculateMaxTabWidth(tabPlacement);
+
+ calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
+ int height = 0;
+ int runHeight = tabAreaInsets.top + insets.top;
+ int fontHeight = fm.getHeight();
+ int left = insets.left + tabAreaInsets.left;
+ for (int i = 0; i < tabCount; i++)
+ {
+ height = calculateTabHeight(tabPlacement, i, fontHeight);
+
+ // The proper instances should exists because
+ // assureRectsCreated() was being run already.
+ rects[i].setBounds(left, runHeight, maxWidth, height);
+ runHeight += height;
+ }
+ tabAreaRect.width = maxTabWidth + tabAreaInsets.left
+ + tabAreaInsets.right;
+ tabAreaRect.height = tabPane.getHeight() - insets.top
+ - insets.bottom;
+ tabAreaRect.y = insets.top;
+ contentRect.width = tabPane.getWidth() - insets.left - insets.right
+ - tabAreaRect.width;
+ contentRect.height = tabAreaRect.height;
+ contentRect.y = insets.top;
+ if (tabPlacement == SwingConstants.LEFT)
+ {
+ tabAreaRect.x = insets.left;
+ contentRect.x = tabAreaRect.x + tabAreaRect.width;
+ }
+ else
+ {
+ contentRect.x = insets.left;
+ tabAreaRect.x = contentRect.x + contentRect.width;
+ }
+ }
+
+ // Unlike the behavior in the WRAP_TAB_LAYOUT the selected
+ // tab is not padded specially.
+ }
+
+ /**
+ * This method is called when the JTabbedPane is laid out in
+ * SCROLL_TAB_LAYOUT. It finds the position for all components in the
+ * JTabbedPane.
+ *
+ * @param pane The JTabbedPane to be laid out.
+ */
+ public void layoutContainer(Container pane)
+ {
+ super.layoutContainer(pane);
+ int tabCount = tabPane.getTabCount();
+ if (tabCount == 0)
+ return;
+ int tabPlacement = tabPane.getTabPlacement();
+
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x
+ + rects[tabCount - 1].width)
+ {
+ Dimension incrDims = incrButton.getPreferredSize();
+ Dimension decrDims = decrButton.getPreferredSize();
+
+ if (tabPlacement == SwingConstants.BOTTOM)
+ {
+ // Align scroll buttons with the bottom border of the tabbed
+ // pane's content area.
+ decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width - decrDims.width,
+ tabAreaRect.y, decrDims.width,
+ decrDims.height);
+ incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width, tabAreaRect.y,
+ incrDims.width, incrDims.height);
+ }
+ else
+ {
+ // Align scroll buttons with the top border of the tabbed
+ // pane's content area.
+ decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width - decrDims.width,
+ tabAreaRect.y + tabAreaRect.height
+ - decrDims.height, decrDims.width,
+ decrDims.height);
+ incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height,
+ incrDims.width, incrDims.height);
+ }
+
+ tabAreaRect.width -= decrDims.width + incrDims.width;
+
+ updateButtons();
+
+ incrButton.setVisible(true);
+ decrButton.setVisible(true);
+ }
+ else
+ {
+ incrButton.setVisible(false);
+ decrButton.setVisible(false);
+
+ currentScrollOffset = 0;
+ currentScrollLocation = 0;
+ }
+ }
+
+ if (tabPlacement == SwingConstants.LEFT
+ || tabPlacement == SwingConstants.RIGHT)
+ {
+ if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y
+ + rects[tabCount - 1].height)
+ {
+ Dimension incrDims = incrButton.getPreferredSize();
+ Dimension decrDims = decrButton.getPreferredSize();
+
+ if (tabPlacement == SwingConstants.RIGHT)
+ {
+ // Align scroll buttons with the right border of the tabbed
+ // pane's content area.
+ decrButton.setBounds(tabAreaRect.x,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height - decrDims.height,
+ decrDims.width, decrDims.height);
+ incrButton.setBounds(tabAreaRect.x,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height, incrDims.width,
+ incrDims.height);
+ }
+ else
+ {
+ // Align scroll buttons with the left border of the tabbed
+ // pane's content area.
+ decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - decrDims.width,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height - decrDims.height,
+ decrDims.width, decrDims.height);
+ incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height, incrDims.width,
+ incrDims.height);
+ }
+
+ tabAreaRect.height -= decrDims.height + incrDims.height;
+
+ incrButton.setVisible(true);
+ decrButton.setVisible(true);
+ }
+ else
+ {
+ incrButton.setVisible(false);
+ decrButton.setVisible(false);
+
+ currentScrollOffset = 0;
+ currentScrollLocation = 0;
+ }
+ }
+ viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width,
+ tabAreaRect.height);
+
+ updateViewPosition();
+
+ viewport.repaint();
+ }
+ }
+
+ /**
+ * This class handles ChangeEvents from the JTabbedPane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TabSelectionHandler implements ChangeListener
+ {
+ /**
+ * This method is called whenever a ChangeEvent is fired from the
+ * JTabbedPane.
+ *
+ * @param e The ChangeEvent fired.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ selectedRun = getRunForTab(tabPane.getTabCount(),
+ tabPane.getSelectedIndex());
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
+ tabPane.revalidate();
+ tabPane.repaint();
+ }
+ }
+
+ /**
+ * This helper class is a JPanel that fits inside the ScrollViewport. This
+ * panel's sole job is to paint the tab rectangles inside the viewport so
+ * that it's clipped correctly.
+ */
+ private class ScrollingPanel extends JPanel
+ {
+ /**
+ * This is a private UI class for our panel.
+ */
+ private class ScrollingPanelUI extends BasicPanelUI
+ {
+ /**
+ * This method overrides the default paint method. It paints the tab
+ * rectangles for the JTabbedPane in the panel.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ int placement = tabPane.getTabPlacement();
+ g.setColor(highlight);
+ if (placement == SwingUtilities.TOP
+ || placement == SwingUtilities.BOTTOM)
+ g.fillRect(currentScrollOffset, 0,
+ tabAreaRect.width, tabAreaRect.height);
+ else
+ g.fillRect(0, currentScrollOffset,
+ tabAreaRect.width, tabAreaRect.height);
+
+ paintTabArea(g, placement, tabPane.getSelectedIndex());
+ }
+ }
+
+ /**
+ * This method overrides the updateUI method. It makes the default UI for
+ * this ScrollingPanel to be a ScrollingPanelUI.
+ */
+ public void updateUI()
+ {
+ setUI(new ScrollingPanelUI());
+ }
+ }
+
+ /**
+ * This is a helper class that paints the panel that paints tabs. This
+ * custom JViewport is used so that the tabs painted in the panel will be
+ * clipped. This class implements UIResource so tabs are not added when
+ * this objects of this class are added to the JTabbedPane.
+ */
+ private class ScrollingViewport extends JViewport implements UIResource
+ {
+ // TODO: Maybe remove this inner class.
+ }
+
+ /**
+ * This is a helper class that implements UIResource so it is not added as a
+ * tab when an object of this class is added to the JTabbedPane.
+ */
+ private class ScrollingButton extends BasicArrowButton implements UIResource
+ {
+ /**
+ * Creates a ScrollingButton given the direction.
+ *
+ * @param dir The direction to point in.
+ */
+ public ScrollingButton(int dir)
+ {
+ super(dir);
+ }
+ }
+
+ /** The button that increments the current scroll location.
+ * This is package-private to avoid an accessor method. */
+ transient ScrollingButton incrButton;
+
+ /** The button that decrements the current scroll location.
+ * This is package-private to avoid an accessor method. */
+ transient ScrollingButton decrButton;
+
+ /** The viewport used to display the tabs.
+ * This is package-private to avoid an accessor method. */
+ transient ScrollingViewport viewport;
+
+ /** The panel inside the viewport that paints the tabs.
+ * This is package-private to avoid an accessor method. */
+ transient ScrollingPanel panel;
+
+ /** The starting visible tab in the run in SCROLL_TAB_MODE.
+ * This is package-private to avoid an accessor method. */
+ transient int currentScrollLocation;
+
+ transient int currentScrollOffset;
+
+ /** A reusable rectangle. */
+ protected Rectangle calcRect;
+
+ /** An array of Rectangles keeping track of the tabs' area and position. */
+ protected Rectangle[] rects;
+
+ /** The insets around the content area. */
+ protected Insets contentBorderInsets;
+
+ /** The extra insets around the selected tab. */
+ protected Insets selectedTabPadInsets;
+
+ /** The insets around the tab area. */
+ protected Insets tabAreaInsets;
+
+ /** The insets around each and every tab. */
+ protected Insets tabInsets;
+
+ /**
+ * The outer bottom and right edge color for both the tab and content
+ * border.
+ */
+ protected Color darkShadow;
+
+ /** The color of the focus outline on the selected tab. */
+ protected Color focus;
+
+ /** FIXME: find a use for this. */
+ protected Color highlight;
+
+ /** The top and left edge color for both the tab and content border. */
+ protected Color lightHighlight;
+
+ /** The inner bottom and right edge color for the tab and content border. */
+ protected Color shadow;
+
+ /** The maximum tab height. */
+ protected int maxTabHeight;
+
+ /** The maximum tab width. */
+ protected int maxTabWidth;
+
+ /** The number of runs in the JTabbedPane. */
+ protected int runCount;
+
+ /** The index of the run that the selected index is in. */
+ protected int selectedRun;
+
+ /** The amount of space each run overlaps the previous by. */
+ protected int tabRunOverlay;
+
+ /** The gap between text and label */
+ protected int textIconGap;
+
+ /** This array keeps track of which tabs are in which run.
+ * <p>The value at index i denotes the index of the first tab in run i.</p>
+ * <p>If the value for any index (i > 0) is 0 then (i - 1) is the last
+ * run.</p>
+ */
+ protected int[] tabRuns;
+
+ /**
+ * Indicates if the layout of the tab runs is ok or not. This is package
+ * private to avoid a synthetic accessor method.
+ */
+ boolean tabRunsDirty;
+
+ /**
+ * This is the keystroke for moving down.
+ *
+ * @deprecated 1.3
+ */
+ protected KeyStroke downKey;
+
+ /**
+ * This is the keystroke for moving left.
+ *
+ * @deprecated 1.3
+ */
+ protected KeyStroke leftKey;
+
+ /**
+ * This is the keystroke for moving right.
+ *
+ * @deprecated 1.3
+ */
+ protected KeyStroke rightKey;
+
+ /**
+ * This is the keystroke for moving up.
+ *
+ * @deprecated 1.3
+ */
+ protected KeyStroke upKey;
+
+ /** The listener that listens for focus events. */
+ protected FocusListener focusListener;
+
+ /** The listener that listens for mouse events. */
+ protected MouseListener mouseListener;
+
+ /** The listener that listens for property change events. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The listener that listens for change events. */
+ protected ChangeListener tabChangeListener;
+
+ /** The tab pane that this UI paints. */
+ protected JTabbedPane tabPane;
+
+ /** The current layout manager for the tabPane.
+ * This is package-private to avoid an accessor method. */
+ transient LayoutManager layoutManager;
+
+ /** The rectangle that describes the tab area's position and size.
+ * This is package-private to avoid an accessor method. */
+ transient Rectangle tabAreaRect;
+
+ /** The rectangle that describes the content area's position and
+ * size. This is package-private to avoid an accessor method. */
+ transient Rectangle contentRect;
+
+ /**
+ * The index over which the mouse is currently moving.
+ */
+ private int rolloverTab;
+
+ /**
+ * Determines if tabs are painted opaque or not. This can be adjusted using
+ * the UIManager property 'TabbedPane.tabsOpaque'.
+ */
+ private boolean tabsOpaque;
+
+ /**
+ * The currently visible component.
+ */
+ private Component visibleComponent;
+
+ private Color selectedColor;
+
+ private Rectangle tempTextRect = new Rectangle();
+
+ private Rectangle tempIconRect = new Rectangle();
+
+ /**
+ * Creates a new BasicTabbedPaneUI object.
+ */
+ public BasicTabbedPaneUI()
+ {
+ super();
+ rects = new Rectangle[0];
+ tabRuns = new int[10];
+ }
+
+ /**
+ * This method creates a ScrollingButton that points in the appropriate
+ * direction for an increasing button.
+ * This is package-private to avoid an accessor method.
+ *
+ * @return The increase ScrollingButton.
+ */
+ ScrollingButton createIncreaseButton()
+ {
+ if (incrButton == null)
+ incrButton = new ScrollingButton(SwingConstants.NORTH);
+ if (tabPane.getTabPlacement() == SwingConstants.TOP
+ || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
+ incrButton.setDirection(SwingConstants.EAST);
+ else
+ incrButton.setDirection(SwingConstants.SOUTH);
+ return incrButton;
+ }
+
+ /**
+ * This method creates a ScrollingButton that points in the appropriate
+ * direction for a decreasing button.
+ * This is package-private to avoid an accessor method.
+ *
+ * @return The decrease ScrollingButton.
+ */
+ ScrollingButton createDecreaseButton()
+ {
+ if (decrButton == null)
+ decrButton = new ScrollingButton(SwingConstants.SOUTH);
+ if (tabPane.getTabPlacement() == SwingConstants.TOP
+ || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
+ decrButton.setDirection(SwingConstants.WEST);
+ else
+ decrButton.setDirection(SwingConstants.NORTH);
+ return decrButton;
+ }
+
+ /**
+ * This method finds the point to set the view position at given the index
+ * of a tab. The tab will be the first visible tab in the run.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param index The index of the first visible tab.
+ *
+ * @return The position of the first visible tab.
+ */
+ Point findPointForIndex(int index)
+ {
+ int tabPlacement = tabPane.getTabPlacement();
+ int selectedIndex = tabPane.getSelectedIndex();
+ Insets insets = getSelectedTabPadInsets(tabPlacement);
+ int w = 0;
+ int h = 0;
+
+ if (tabPlacement == TOP || tabPlacement == BOTTOM)
+ {
+ if (index > 0)
+ {
+ w += rects[index - 1].x + rects[index - 1].width;
+ if (index > selectedIndex)
+ w -= insets.left + insets.right;
+ }
+ }
+
+ else
+ {
+ if (index > 0)
+ {
+ h += rects[index - 1].y + rects[index - 1].height;
+ if (index > selectedIndex)
+ h -= insets.top + insets.bottom;
+ }
+ }
+
+ Point p = new Point(w, h);
+ return p;
+ }
+
+ /** TabbedPanes in scrolling mode should use this method to
+ * scroll properly to the tab given by the index argument.
+ *
+ * @param index The tab to scroll to.
+ * @param placement The tab's placement.
+ */
+ final void scrollTab(int index, int placement)
+ {
+ int diff;
+ if (index >= 0 && tabPane.isEnabledAt(index))
+ {
+ // If the user clicked on the last tab and that one was
+ // only partially visible shift the scroll offset to make
+ // it completely visible.
+ switch (placement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ if ((diff = rects[index].x
+ + rects[index].width
+ - decrButton.getX() - currentScrollOffset) > 0)
+ currentScrollOffset += diff;
+ else if ((diff = rects[index].x - currentScrollOffset) < 0)
+ {
+ if (index == 0)
+ currentScrollOffset = 0;
+ else
+ currentScrollOffset += diff;
+ }
+
+ currentScrollLocation = tabForCoordinate(tabPane,
+ currentScrollOffset,
+ rects[index].y);
+ break;
+ default:
+ if ((diff = rects[index].y + rects[index].height
+ - decrButton.getY() - currentScrollOffset) > 0)
+ currentScrollOffset += diff;
+ else if ((diff = rects[index].y - currentScrollOffset) < 0)
+ {
+ if (index == 0)
+ currentScrollOffset = 0;
+ else
+ currentScrollOffset += diff;
+ }
+
+ currentScrollLocation = tabForCoordinate(tabPane,
+ rects[index].x,
+ currentScrollOffset);
+ }
+
+ updateViewPosition();
+ updateButtons();
+ }
+ }
+
+ /** Sets the enabled state of the increase and decrease button
+ * according to the current scrolling offset and tab pane width
+ * (or height in TOP/BOTTOM placement).
+ */
+ final void updateButtons()
+ {
+ int tc = tabPane.getTabCount();
+
+ // The increase button should be enabled as long as the
+ // right/bottom border of the last tab is under the left/top
+ // border of the decrease button.
+ switch (tabPane.getTabPlacement())
+ {
+ case JTabbedPane.BOTTOM:
+ case JTabbedPane.TOP:
+ incrButton.setEnabled(currentScrollLocation + 1 < tc
+ && rects[tc-1].x + rects[tc-1].width
+ - currentScrollOffset > decrButton.getX());
+ break;
+ default:
+ incrButton.setEnabled(currentScrollLocation + 1 < tc
+ && rects[tc-1].y + rects[tc-1].height
+ - currentScrollOffset > decrButton.getY());
+ }
+
+ // The decrease button is enabled when the tab pane is scrolled in any way.
+ decrButton.setEnabled(currentScrollOffset > 0);
+
+ }
+
+ /**
+ * Updates the position of the scrolling viewport's view
+ * according to the current scroll offset.
+ */
+ final void updateViewPosition()
+ {
+ Point p = viewport.getViewPosition();
+
+ // The unneeded coordinate must be set to zero
+ // in order to correctly handle placement changes.
+ switch (tabPane.getTabPlacement())
+ {
+ case JTabbedPane.LEFT:
+ case JTabbedPane.RIGHT:
+ p.x = 0;
+ p.y = currentScrollOffset;
+ break;
+ default:
+ p.x = currentScrollOffset;
+ p.y = 0;
+ }
+
+ viewport.setViewPosition(p);
+ }
+
+ /**
+ * This method creates a new BasicTabbedPaneUI.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicTabbedPaneUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicTabbedPaneUI();
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install the UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JTabbedPane)
+ {
+ tabPane = (JTabbedPane) c;
+
+ installComponents();
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+
+ layoutManager = createLayoutManager();
+ tabPane.setLayout(layoutManager);
+ }
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall the UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ layoutManager = null;
+
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallComponents();
+
+ tabPane = null;
+ }
+
+ /**
+ * This method creates the appropriate layout manager for the JTabbedPane's
+ * current tab layout policy. If the tab layout policy is
+ * SCROLL_TAB_LAYOUT, then all the associated components that need to be
+ * created will be done so now.
+ *
+ * @return A layout manager given the tab layout policy.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
+ return new TabbedPaneLayout();
+ else
+ {
+ runCount = 1;
+ tabRuns[0] = 0;
+
+ incrButton = createIncreaseButton();
+ incrButton.addMouseListener(mouseListener);
+
+ decrButton = createDecreaseButton();
+ decrButton.addMouseListener(mouseListener);
+ decrButton.setEnabled(false);
+
+ panel = new ScrollingPanel();
+ panel.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ panel.addMouseListener(mouseListener);
+ panel.addFocusListener(focusListener);
+
+ viewport = new ScrollingViewport();
+ viewport.setBackground(Color.LIGHT_GRAY);
+ viewport.setView(panel);
+ viewport.setLayout(null);
+
+ tabPane.add(incrButton);
+ tabPane.add(decrButton);
+ tabPane.add(viewport);
+
+ return new TabbedPaneScrollLayout();
+ }
+ }
+
+ /**
+ * This method installs components for this JTabbedPane.
+ */
+ protected void installComponents()
+ {
+ // Nothing to be done.
+ }
+
+ /**
+ * This method uninstalls components for this JTabbedPane.
+ */
+ protected void uninstallComponents()
+ {
+ if (incrButton != null)
+ tabPane.remove(incrButton);
+
+ if (decrButton != null)
+ tabPane.remove(decrButton);
+
+ if (viewport != null)
+ tabPane.remove(viewport);
+ }
+
+ /**
+ * This method installs defaults for the Look and Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
+ "TabbedPane.foreground",
+ "TabbedPane.font");
+ tabPane.setOpaque(false);
+
+ lightHighlight = UIManager.getColor("TabbedPane.highlight");
+ highlight = UIManager.getColor("TabbedPane.light");
+
+ shadow = UIManager.getColor("TabbedPane.shadow");
+ darkShadow = UIManager.getColor("TabbedPane.darkShadow");
+
+ focus = UIManager.getColor("TabbedPane.focus");
+
+ textIconGap = UIManager.getInt("TabbedPane.textIconGap");
+ tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
+
+ tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
+ selectedTabPadInsets
+ = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
+ tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
+ contentBorderInsets
+ = UIManager.getInsets("TabbedPane.contentBorderInsets");
+ tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
+
+ // Although 'TabbedPane.contentAreaColor' is not defined in the defaults
+ // of BasicLookAndFeel it is used by this class.
+ selectedColor = UIManager.getColor("TabbedPane.contentAreaColor");
+ if (selectedColor == null)
+ selectedColor = UIManager.getColor("control");
+
+ calcRect = new Rectangle();
+ tabRuns = new int[10];
+ tabAreaRect = new Rectangle();
+ contentRect = new Rectangle();
+ }
+
+ /**
+ * This method uninstalls defaults for the Look and Feel.
+ */
+ protected void uninstallDefaults()
+ {
+ calcRect = null;
+ tabAreaRect = null;
+ contentRect = null;
+ tabRuns = null;
+
+ tempIconRect = null;
+ tempTextRect = null;
+
+ contentBorderInsets = null;
+ tabAreaInsets = null;
+ selectedTabPadInsets = null;
+ tabInsets = null;
+
+ focus = null;
+ darkShadow = null;
+ shadow = null;
+ lightHighlight = null;
+ highlight = null;
+
+ selectedColor = null;
+ }
+
+ /**
+ * This method creates and installs the listeners for this UI.
+ */
+ protected void installListeners()
+ {
+ mouseListener = createMouseListener();
+ tabChangeListener = createChangeListener();
+ propertyChangeListener = createPropertyChangeListener();
+ focusListener = createFocusListener();
+
+ tabPane.addMouseListener(mouseListener);
+ tabPane.addChangeListener(tabChangeListener);
+ tabPane.addPropertyChangeListener(propertyChangeListener);
+ tabPane.addFocusListener(focusListener);
+ }
+
+ /**
+ * This method removes and nulls the listeners for this UI.
+ */
+ protected void uninstallListeners()
+ {
+ tabPane.removeFocusListener(focusListener);
+ tabPane.removePropertyChangeListener(propertyChangeListener);
+ tabPane.removeChangeListener(tabChangeListener);
+ tabPane.removeMouseListener(mouseListener);
+
+ if (incrButton != null)
+ incrButton.removeMouseListener(mouseListener);
+
+ if (decrButton != null)
+ decrButton.removeMouseListener(mouseListener);
+
+ if (panel != null)
+ {
+ panel.removeMouseListener(mouseListener);
+ panel.removeFocusListener(focusListener);
+ }
+
+ focusListener = null;
+ propertyChangeListener = null;
+ tabChangeListener = null;
+ mouseListener = null;
+ }
+
+ /**
+ * This method creates a new MouseListener.
+ *
+ * @return A new MouseListener.
+ */
+ protected MouseListener createMouseListener()
+ {
+ return new MouseHandler();
+ }
+
+ /**
+ * This method creates a new FocusListener.
+ *
+ * @return A new FocusListener.
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * This method creates a new ChangeListener.
+ *
+ * @return A new ChangeListener.
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new TabSelectionHandler();
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method installs keyboard actions for the JTabbedPane.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap keyMap = (InputMap) UIManager.get("TabbedPane.focusInputMap");
+ SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, keyMap);
+
+ keyMap = (InputMap) UIManager.get("TabbedPane.ancestorInputMap");
+ SwingUtilities
+ .replaceUIInputMap(tabPane,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ keyMap);
+
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(tabPane, map);
+ }
+
+ /**
+ * This method uninstalls keyboard actions for the JTabbedPane.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIActionMap(tabPane, null);
+ SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null);
+ SwingUtilities
+ .replaceUIInputMap(tabPane,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ null);
+ }
+
+ /**
+ * This method returns the minimum size of the JTabbedPane.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return layoutManager.minimumLayoutSize(tabPane);
+ }
+
+ /**
+ * This method returns the maximum size of the JTabbedPane.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
+ }
+
+ /**
+ * This method paints the JTabbedPane.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ if (!tabPane.isValid())
+ tabPane.validate();
+
+ if (tabPane.getTabCount() == 0)
+ return;
+
+ int index = tabPane.getSelectedIndex();
+ if (index < 0)
+ index = 0;
+
+ int tabPlacement = tabPane.getTabPlacement();
+
+ // Paint the tab area only in WRAP_TAB_LAYOUT Mode from this method
+ // because it is done through the ScrollingViewport.paint() method
+ // for the SCROLL_TAB_LAYOUT mode.
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
+ {
+ g.setColor(highlight);
+ g.fillRect(tabAreaRect.x, tabAreaRect.y,
+ tabAreaRect.width, tabAreaRect.height);
+ paintTabArea(g, tabPlacement, index);
+ }
+
+ paintContentBorder(g, tabPlacement, index);
+ }
+
+ /**
+ * This method paints the tab area. This includes painting the rectangles
+ * that make up the tabs.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected index.
+ */
+ protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
+ {
+ // Please note: the ordering of the painting is important.
+ // we WANT to paint the outermost run first and then work our way in.
+
+ // The following drawing code works for both tab layouts.
+ int tabCount = tabPane.getTabCount();
+
+ for (int i = runCount - 1; i >= 0; --i)
+ {
+ int start = tabRuns[i];
+ int next;
+ if (i == runCount - 1)
+ next = tabRuns[0];
+ else
+ next = tabRuns[i + 1];
+ int end = next != 0 ? next - 1 : tabCount - 1;
+ for (int j = start; j <= end; ++j)
+ {
+ if (j != selectedIndex)
+ {
+ paintTab(g, tabPlacement, rects, j,
+ tempIconRect, tempTextRect);
+ }
+ }
+ }
+
+ // Paint selected tab in front of every other tab.
+ if (selectedIndex >= 0)
+ paintTab(g, tabPlacement, rects, selectedIndex,
+ tempIconRect, tempTextRect);
+ }
+
+ /**
+ * This method paints an individual tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param rects The array of rectangles that keep the size and position of
+ * the tabs.
+ * @param tabIndex The tab index to paint.
+ * @param iconRect The rectangle to use for the icon.
+ * @param textRect The rectangle to use for the text.
+ */
+ protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
+ int tabIndex, Rectangle iconRect, Rectangle textRect)
+ {
+ Rectangle rect = rects[tabIndex];
+ boolean isSelected = tabIndex == tabPane.getSelectedIndex();
+ // Paint background if necessary.
+ if (tabsOpaque || tabPane.isOpaque())
+ {
+ paintTabBackground(g, tabPlacement, tabIndex, rect.x, rect.y,
+ rect.width, rect.height, isSelected);
+ }
+
+ // Paint border.
+ paintTabBorder(g, tabPlacement, tabIndex, rect.x, rect.y, rect.width,
+ rect.height, isSelected);
+
+ // Layout label.
+ FontMetrics fm = getFontMetrics();
+ Icon icon = getIconForTab(tabIndex);
+ String title = tabPane.getTitleAt(tabIndex);
+ layoutLabel(tabPlacement, fm, tabIndex, title, icon, rect, iconRect,
+ textRect, isSelected);
+ // Paint the text.
+ paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title,
+ textRect, isSelected);
+
+ // Paint icon if necessary.
+ paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
+
+ // Paint focus indicator.
+ paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, textRect,
+ isSelected);
+ }
+
+ /**
+ * This method lays out the tab and finds the location to paint the icon
+ * and text.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param metrics The font metrics for the font to paint with.
+ * @param tabIndex The tab index to paint.
+ * @param title The string painted.
+ * @param icon The icon painted.
+ * @param tabRect The tab bounds.
+ * @param iconRect The calculated icon bounds.
+ * @param textRect The calculated text bounds.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void layoutLabel(int tabPlacement, FontMetrics metrics,
+ int tabIndex, String title, Icon icon,
+ Rectangle tabRect, Rectangle iconRect,
+ Rectangle textRect, boolean isSelected)
+ {
+ // Reset the icon and text rectangles, as the result is not specified
+ // when the locations are not (0,0).
+ textRect.x = 0;
+ textRect.y = 0;
+ textRect.width = 0;
+ textRect.height = 0;
+ iconRect.x = 0;
+ iconRect.y = 0;
+ iconRect.width = 0;
+ iconRect.height = 0;
+ SwingUtilities.layoutCompoundLabel(tabPane, metrics, title, icon,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER,
+ SwingConstants.RIGHT, tabRect,
+ iconRect, textRect, textIconGap);
+
+ int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
+ int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
+
+ iconRect.x += shiftX;
+ iconRect.y += shiftY;
+
+ textRect.x += shiftX;
+ textRect.y += shiftY;
+ }
+
+ /**
+ * This method paints the icon.
+ *
+ * @param g The Graphics object to paint.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index to paint.
+ * @param icon The icon to paint.
+ * @param iconRect The bounds of the icon.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
+ Icon icon, Rectangle iconRect, boolean isSelected)
+ {
+ if (icon != null)
+ icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
+ }
+
+ /**
+ * This method paints the text for the given tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param font The font to paint with.
+ * @param metrics The fontmetrics of the given font.
+ * @param tabIndex The tab index.
+ * @param title The string to paint.
+ * @param textRect The bounds of the string.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void paintText(Graphics g, int tabPlacement, Font font,
+ FontMetrics metrics, int tabIndex, String title,
+ Rectangle textRect, boolean isSelected)
+ {
+ g.setFont(font);
+ View textView = getTextViewForTab(tabIndex);
+ if (textView != null)
+ {
+ textView.paint(g, textRect);
+ return;
+ }
+
+ int ascent = metrics.getAscent();
+
+ int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
+ if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex))
+ {
+ Color fg = tabPane.getForegroundAt(tabIndex);
+ if (isSelected && (fg instanceof UIResource))
+ {
+ Color selectionForeground =
+ UIManager.getColor("TabbedPane.selectionForeground");
+ if (selectionForeground != null)
+ fg = selectionForeground;
+ }
+ g.setColor(fg);
+
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
+ textRect.x,
+ textRect.y + ascent);
+ else
+ g.drawString(title, textRect.x, textRect.y + ascent);
+ }
+ else
+ {
+ Color bg = tabPane.getBackgroundAt(tabIndex);
+ g.setColor(bg.brighter());
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
+ textRect.x, textRect.y
+ + ascent);
+ else
+ g.drawString(title, textRect.x, textRect.y + ascent);
+
+ g.setColor(bg.darker());
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
+ textRect.x + 1,
+ textRect.y + 1
+ + ascent);
+ else
+ g.drawString(title, textRect.x + 1, textRect.y + 1 + ascent);
+ }
+ }
+
+ /**
+ * This method returns how much the label for the tab should shift in the X
+ * direction.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index being painted.
+ * @param isSelected Whether this tab is selected.
+ *
+ * @return The amount the label should shift by in the X direction.
+ */
+ protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
+ boolean isSelected)
+ {
+ switch (tabPlacement)
+ {
+ default:
+ case SwingUtilities.TOP:
+ case SwingUtilities.BOTTOM:
+ return 1;
+ case SwingUtilities.LEFT:
+ return (isSelected) ? -1 : 1;
+ case SwingUtilities.RIGHT:
+ return (isSelected) ? 1 : -1;
+ }
+ }
+
+ /**
+ * This method returns how much the label for the tab should shift in the Y
+ * direction.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index being painted.
+ * @param isSelected Whether this tab is selected.
+ *
+ * @return The amount the label should shift by in the Y direction.
+ */
+ protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
+ boolean isSelected)
+ {
+ switch (tabPlacement)
+ {
+ default:
+ case SwingUtilities.TOP:
+ return (isSelected) ? -1 : 1;
+ case SwingUtilities.BOTTOM:
+ return (isSelected) ? 1 : -1;
+ case SwingUtilities.LEFT:
+ case SwingUtilities.RIGHT:
+ return 0;
+ }
+ }
+
+ /**
+ * This method paints the focus rectangle around the selected tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param rects The array of rectangles keeping track of size and position.
+ * @param tabIndex The tab index.
+ * @param iconRect The icon bounds.
+ * @param textRect The text bounds.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void paintFocusIndicator(Graphics g, int tabPlacement,
+ Rectangle[] rects, int tabIndex,
+ Rectangle iconRect, Rectangle textRect,
+ boolean isSelected)
+ {
+ if (tabPane.hasFocus() && isSelected)
+ {
+ Rectangle rect = rects[tabIndex];
+ // The focus rectangle.
+ int x;
+ int y;
+ int w;
+ int h;
+
+ g.setColor(focus);
+ switch (tabPlacement)
+ {
+ case LEFT:
+ x = rect.x + 3;
+ y = rect.y + 3;
+ w = rect.width - 5;
+ h = rect.height - 6;
+ break;
+ case RIGHT:
+ x = rect.x + 2;
+ y = rect.y + 3;
+ w = rect.width - 6;
+ h = rect.height - 5;
+ break;
+ case BOTTOM:
+ x = rect.x + 3;
+ y = rect.y + 2;
+ w = rect.width - 6;
+ h = rect.height - 5;
+ break;
+ case TOP:
+ default:
+ x = rect.x + 3;
+ y = rect.y + 3;
+ w = rect.width - 6;
+ h = rect.height - 5;
+ }
+
+ BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
+ }
+ }
+
+ /**
+ * This method paints the border for an individual tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index.
+ * @param x The x position of the tab.
+ * @param y The y position of the tab.
+ * @param w The width of the tab.
+ * @param h The height of the tab.
+ * @param isSelected Whether the tab is selected.
+ */
+ protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
+ int x, int y, int w, int h, boolean isSelected)
+ {
+ Color saved = g.getColor();
+
+ switch (tabPlacement)
+ {
+ case SwingConstants.TOP:
+ g.setColor(shadow);
+ // Inner right line.
+ g.drawLine(x + w - 2, y + 2, x + w - 2, y + h);
+
+ g.setColor(darkShadow);
+ // Outer right line.
+ g.drawLine(x + w - 1, y + 2, x + w - 1, y + h);
+
+ // Upper right corner.
+ g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2);
+
+ g.setColor(lightHighlight);
+
+ // Left line.
+ g.drawLine(x, y + 3, x, y + h);
+
+ // Upper line.
+ g.drawLine(x + 3, y, x + w - 3, y);
+
+ // Upper left corner.
+ g.drawLine(x, y + 2, x + 2, y);
+
+ break;
+ case SwingConstants.LEFT:
+ g.setColor(lightHighlight);
+ // Top line.
+ g.drawLine(x + 3, y, x + w - 1, y);
+
+ // Top left border.
+ g.drawLine(x + 2, y, x, y + 2);
+
+ // Left line.
+ g.drawLine(x, y + 3, x, y + h - 4);
+
+ // Bottom left corner.
+ g.drawLine(x, y + h - 3, x + 1, y + h - 2);
+
+ g.setColor(darkShadow);
+ // Outer bottom line.
+ g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1);
+
+ g.setColor(shadow);
+ // Inner bottom line.
+ g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2);
+
+ break;
+ case SwingConstants.BOTTOM:
+ g.setColor(shadow);
+ // Inner right line.
+ g.drawLine(x + w - 2, y, x + w - 2, y + h - 2);
+
+ // Inner bottom line.
+ g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1);
+
+ g.setColor(darkShadow);
+ // Outer right line.
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 3);
+
+ // Bottom right corner.
+ g.drawLine(x + w - 1, y + h - 2, x + w - 3, y + h);
+
+ // Bottom line.
+ g.drawLine(x + 2, y + h, x + w - 4, y + h);
+
+ g.setColor(lightHighlight);
+ // Left line.
+ g.drawLine(x, y, x, y + h - 3);
+
+ // Bottom left corner.
+ g.drawLine(x, y + h - 2, x + 1, y + h - 1);
+ break;
+ case SwingConstants.RIGHT:
+ g.setColor(lightHighlight);
+ // Top line.
+ g.drawLine(x, y, x + w - 3, y);
+
+ g.setColor(darkShadow);
+ // Top right corner.
+ g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2);
+
+ // Outer right line.
+ g.drawLine(x + w - 1, y + 3, x + w - 1, y + h - 3);
+
+ // Bottom right corner.
+ g.drawLine(x + w - 2, y + h - 2, x + w - 3, y + h - 1);
+
+ // Bottom line.
+ g.drawLine(x, y + h - 1, x + w - 4, y + h - 1);
+
+ g.setColor(shadow);
+
+ // Inner right line.
+ g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3);
+
+ // Inner bottom line.
+ g.drawLine(x, y + h - 2, x + w - 3, y + h - 2);
+
+ break;
+ }
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the background for an individual tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index.
+ * @param x The x position of the tab.
+ * @param y The y position of the tab.
+ * @param w The width of the tab.
+ * @param h The height of the tab.
+ * @param isSelected Whether the tab is selected.
+ */
+ protected void paintTabBackground(Graphics g, int tabPlacement,
+ int tabIndex, int x, int y, int w, int h,
+ boolean isSelected)
+ {
+ Color saved = g.getColor();
+
+ if (isSelected)
+ g.setColor(selectedColor);
+ else
+ {
+ Color bg = tabPane.getBackgroundAt(tabIndex);
+ if (bg == null)
+ bg = Color.LIGHT_GRAY;
+ g.setColor(bg);
+ }
+
+ switch (tabPlacement)
+ {
+ case SwingConstants.TOP:
+ g.fillRect(x + 1, y + 1, w - 1, h - 1);
+ break;
+ case SwingConstants.BOTTOM:
+ g.fillRect(x, y, w - 1, h - 1);
+ break;
+ case SwingConstants.LEFT:
+ g.fillRect(x + 1, y + 1, w - 1, h - 2);
+ break;
+ case SwingConstants.RIGHT:
+ g.fillRect(x, y + 1, w - 1, h - 2);
+ break;
+ }
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the border around the content area.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The index of the selected tab.
+ */
+ protected void paintContentBorder(Graphics g, int tabPlacement,
+ int selectedIndex)
+ {
+ int width = tabPane.getWidth();
+ int height = tabPane.getHeight();
+ Insets insets = tabPane.getInsets();
+
+ // Calculate coordinates of content area.
+ int x = insets.left;
+ int y = insets.top;
+ int w = width - insets.left - insets.right;
+ int h = height - insets.top - insets.bottom;
+
+ switch (tabPlacement)
+ {
+ case LEFT:
+ x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
+ w -= x - insets.left;
+ break;
+ case RIGHT:
+ w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
+ break;
+ case BOTTOM:
+ h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
+ break;
+ case TOP:
+ default:
+ y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
+ h -= y - insets.top;
+ }
+
+ // Fill background if necessary.
+ if (tabPane.isOpaque())
+ {
+ Color bg = UIManager.getColor("TabbedPane.contentAreaColor");
+ g.setColor(bg);
+ g.fillRect(x, y, w, h);
+ }
+
+ // Paint border.
+ paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
+ paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
+ paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
+ paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
+ }
+
+ /**
+ * This method paints the top edge of the content border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected tab index.
+ * @param x The x coordinate for the content area.
+ * @param y The y coordinate for the content area.
+ * @param w The width of the content area.
+ * @param h The height of the content area.
+ */
+ protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color saved = g.getColor();
+ g.setColor(lightHighlight);
+
+ int startgap = rects[selectedIndex].x - currentScrollOffset;
+ int endgap = rects[selectedIndex].x + rects[selectedIndex].width
+ - currentScrollOffset;
+
+ // Paint the highlight line with a gap if the tabs are at the top
+ // and the selected tab is inside the visible area.
+ if (tabPlacement == SwingConstants.TOP && startgap >= 0)
+ {
+ g.drawLine(x, y, startgap, y);
+ g.drawLine(endgap, y, x + w - 1, y);
+
+ g.setColor(selectedColor);
+ g.drawLine(startgap, y, endgap - 1, y);
+ }
+ else
+ g.drawLine(x, y, x + w, y);
+
+ g.setColor(selectedColor);
+ g.drawLine(x, y + 1, x + w - 1, y + 1);
+ g.drawLine(x, y + 2, x + w - 1, y + 2);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the left edge of the content border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected tab index.
+ * @param x The x coordinate for the content area.
+ * @param y The y coordinate for the content area.
+ * @param w The width of the content area.
+ * @param h The height of the content area.
+ */
+ protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color saved = g.getColor();
+ g.setColor(lightHighlight);
+
+ int startgap = rects[selectedIndex].y - currentScrollOffset;
+ int endgap = rects[selectedIndex].y + rects[selectedIndex].height
+ - currentScrollOffset;
+
+ if (tabPlacement == SwingConstants.LEFT && startgap >= 0)
+ {
+ g.drawLine(x, y, x, startgap);
+ g.drawLine(x, endgap, x, y + h - 1);
+
+ g.setColor(selectedColor);
+ g.drawLine(x, startgap, x, endgap - 1);
+ }
+ else
+ g.drawLine(x, y, x, y + h - 1);
+
+ g.setColor(selectedColor);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 4);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the bottom edge of the content border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected tab index.
+ * @param x The x coordinate for the content area.
+ * @param y The y coordinate for the content area.
+ * @param w The width of the content area.
+ * @param h The height of the content area.
+ */
+ protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color saved = g.getColor();
+
+ int startgap = rects[selectedIndex].x - currentScrollOffset;
+ int endgap = rects[selectedIndex].x + rects[selectedIndex].width
+ - currentScrollOffset;
+
+ if (tabPlacement == SwingConstants.BOTTOM && startgap >= 0)
+ {
+ g.setColor(shadow);
+ g.drawLine(x + 1, y + h - 2, startgap, y + h - 2);
+ g.drawLine(endgap, y + h - 2, x + w - 2, y + h - 2);
+
+ g.setColor(darkShadow);
+ g.drawLine(x, y + h - 1, startgap , y + h - 1);
+ g.drawLine(endgap, y + h - 1, x + w - 1, y + h - 1);
+
+ g.setColor(selectedColor);
+ g.drawLine(startgap, y + h - 1, endgap - 1, y + h - 1);
+ g.drawLine(startgap, y + h - 2, endgap - 1, y + h - 2);
+ }
+ else
+ {
+ g.setColor(shadow);
+ g.drawLine(x + 1, y + h - 2, x + w - 1, y + h - 2);
+ g.setColor(darkShadow);
+ g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
+ }
+
+ g.setColor(selectedColor);
+ g.drawLine(x + 1, y + h - 3, x + w - 2, y + h - 3);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the right edge of the content border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected tab index.
+ * @param x The x coordinate for the content area.
+ * @param y The y coordinate for the content area.
+ * @param w The width of the content area.
+ * @param h The height of the content area.
+ */
+ protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color saved = g.getColor();
+ int startgap = rects[selectedIndex].y - currentScrollOffset;
+ int endgap = rects[selectedIndex].y + rects[selectedIndex].height
+ - currentScrollOffset;
+
+ if (tabPlacement == SwingConstants.RIGHT && startgap >= 0)
+ {
+ g.setColor(shadow);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, startgap);
+ g.drawLine(x + w - 2, endgap, x + w - 2, y + h - 2);
+
+ g.setColor(darkShadow);
+ g.drawLine(x + w - 1, y, x + w - 1, startgap);
+ g.drawLine(x + w - 1, endgap, x + w - 1, y + h - 2);
+
+ g.setColor(selectedColor);
+ g.drawLine(x + w - 2, startgap, x + w - 2, endgap - 1);
+ g.drawLine(x + w - 1, startgap, x + w - 1, endgap - 1);
+ }
+ else
+ {
+ g.setColor(shadow);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2);
+ g.setColor(darkShadow);
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 2);
+ }
+
+ g.setColor(selectedColor);
+ g.drawLine(x + w - 3, y + 1, x + w - 3, y + h - 4);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * <p>This method returns the bounds of a tab for the given index
+ * and shifts it by the current scrolling offset if the tabbed
+ * pane is in scrolling tab layout mode.</p>
+ *
+ * <p>Subclassses should retrievs a tab's bounds by this method
+ * if they want to find out whether the tab is currently visible.</p>
+ *
+ * @param pane The JTabbedPane.
+ * @param i The index to look for.
+ *
+ * @return The bounds of the tab with the given index.
+ */
+ public Rectangle getTabBounds(JTabbedPane pane, int i)
+ {
+ // Need to re-layout container if tab does not exist.
+ if (i >= rects.length)
+ layoutManager.layoutContainer(pane);
+
+ // Properly shift coordinates if scrolling has taken
+ // place.
+ if (pane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ {
+ Rectangle r = new Rectangle(rects[i]);
+
+ switch(pane.getTabPlacement())
+ {
+ case SwingConstants.TOP:
+ case SwingConstants.BOTTOM:
+ r.x -= currentScrollOffset;
+ break;
+ default:
+ r.y -= currentScrollOffset;
+ }
+
+ return r;
+ }
+
+ return rects[i];
+ }
+
+ /**
+ * This method returns the number of runs.
+ *
+ * @param pane The JTabbedPane.
+ *
+ * @return The number of runs.
+ */
+ public int getTabRunCount(JTabbedPane pane)
+ {
+ return runCount;
+ }
+
+ /**
+ * This method returns the tab index given a coordinate.
+ *
+ * @param pane The JTabbedPane.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ *
+ * @return The tab index that the coordinate lands in.
+ */
+ public int tabForCoordinate(JTabbedPane pane, int x, int y)
+ {
+ // Note: This code is tab layout mode agnostic.
+ if (! tabPane.isValid())
+ tabPane.validate();
+
+ int tabCount = tabPane.getTabCount();
+
+ // If the user clicked outside of any tab rect the
+ // selection should not change.
+ int index = tabPane.getSelectedIndex();
+ for (int i = 0; i < tabCount; ++i)
+ {
+ if (rects[i].contains(x, y))
+ {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * <p>This method returns the tab bounds in the given rectangle.</p>
+ *
+ * <p>The returned rectangle will be shifted by the current scroll
+ * offset if the tabbed pane is in scrolling tab layout mode.</p>.
+ *
+ * @param tabIndex The index to get bounds for.
+ * @param dest The rectangle to store bounds in.
+ *
+ * @return The rectangle passed in.
+ */
+ protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
+ {
+ dest.setBounds(getTabBounds(tabPane, tabIndex));
+ return dest;
+ }
+
+ /**
+ * This method returns the component that is shown in the content area.
+ *
+ * @return The component that is shown in the content area.
+ */
+ protected Component getVisibleComponent()
+ {
+ return visibleComponent;
+ }
+
+ /**
+ * This method sets the visible component.
+ *
+ * @param component The component to be set visible.
+ */
+ protected void setVisibleComponent(Component component)
+ {
+ // Make old component invisible.
+ if (visibleComponent != null && visibleComponent != component
+ && visibleComponent.getParent() == tabPane)
+ {
+ visibleComponent.setVisible(false);
+ }
+
+ // Make new component visible.
+ if (component != null && ! component.isVisible())
+ {
+ component.setVisible(true);
+ }
+ visibleComponent = component;
+ }
+
+ /**
+ * This method assures that enough rectangles are created given the
+ * tabCount. The old array is copied to the new one.
+ *
+ * @param tabCount The number of tabs.
+ */
+ protected void assureRectsCreated(int tabCount)
+ {
+ if (rects.length < tabCount)
+ {
+ Rectangle[] old = rects;
+ rects = new Rectangle[tabCount];
+ System.arraycopy(old, 0, rects, 0, old.length);
+ for (int i = old.length; i < rects.length; i++)
+ rects[i] = new Rectangle();
+ }
+ }
+
+ /**
+ * This method expands the tabRuns array to give it more room. The old array
+ * is copied to the new one.
+ */
+ protected void expandTabRunsArray()
+ {
+ // This method adds another 10 index positions to the tabRuns array.
+ if (tabRuns == null)
+ tabRuns = new int[10];
+ else
+ {
+ int[] newRuns = new int[tabRuns.length + 10];
+ System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
+ tabRuns = newRuns;
+ }
+ }
+
+ /**
+ * This method returns which run a particular tab belongs to.
+ *
+ * @param tabCount The number of tabs.
+ * @param tabIndex The tab to find.
+ *
+ * @return The tabRuns index that it belongs to.
+ */
+ protected int getRunForTab(int tabCount, int tabIndex)
+ {
+ if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
+ return 0;
+ for (int i = 0; i < runCount; i++)
+ {
+ int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
+ if (first == tabCount)
+ first = 0;
+ int last = lastTabInRun(tabCount, i);
+ if (last >= tabIndex && first <= tabIndex)
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * This method returns the index of the last tab in a run.
+ *
+ * @param tabCount The number of tabs.
+ * @param run The run to check.
+ *
+ * @return The last tab in the given run.
+ */
+ protected int lastTabInRun(int tabCount, int run)
+ {
+ int lastTab;
+ if (runCount == 1)
+ lastTab = tabCount - 1;
+ else
+ {
+ int nextRun;
+ if (run == runCount - 1)
+ nextRun = 0;
+ else
+ nextRun = run + 1;
+
+ if (tabRuns[nextRun] == 0)
+ lastTab = tabCount - 1;
+ else
+ lastTab = tabRuns[nextRun] - 1;
+ }
+ return lastTab;
+ }
+
+ /**
+ * This method returns the tab run overlay.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The tab run overlay.
+ */
+ protected int getTabRunOverlay(int tabPlacement)
+ {
+ return tabRunOverlay;
+ }
+
+ /**
+ * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
+ * makes each tab run start indented by a certain amount.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param run The run to get indent for.
+ *
+ * @return The amount a run should be indented.
+ */
+ protected int getTabRunIndent(int tabPlacement, int run)
+ {
+ return 0;
+ }
+
+ /**
+ * This method returns whether a tab run should be padded.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param run The run to check.
+ *
+ * @return Whether the given run should be padded.
+ */
+ protected boolean shouldPadTabRun(int tabPlacement, int run)
+ {
+ return true;
+ }
+
+ /**
+ * This method returns whether the tab runs should be rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return Whether runs should be rotated.
+ */
+ protected boolean shouldRotateTabRuns(int tabPlacement)
+ {
+ return true;
+ }
+
+ /**
+ * This method returns an icon for the tab. If the tab is disabled, it
+ * should return the disabledIcon. If it is enabled, then it should return
+ * the default icon.
+ *
+ * @param tabIndex The tab index to get an icon for.
+ *
+ * @return The icon for the tab index.
+ */
+ protected Icon getIconForTab(int tabIndex)
+ {
+ if (tabPane.isEnabledAt(tabIndex))
+ return tabPane.getIconAt(tabIndex);
+ else
+ return tabPane.getDisabledIconAt(tabIndex);
+ }
+
+ /**
+ * This method returns a view that can paint the text for the label.
+ *
+ * @param tabIndex The tab index to get a view for.
+ *
+ * @return The view for the tab index.
+ */
+ protected View getTextViewForTab(int tabIndex)
+ {
+ // FIXME: When the label contains HTML this should return something
+ // non-null.
+ return null;
+ }
+
+ /**
+ * This method returns the tab height, including insets, for the given index
+ * and fontheight.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The index of the tab to calculate.
+ * @param fontHeight The font height.
+ *
+ * @return This tab's height.
+ */
+ protected int calculateTabHeight(int tabPlacement, int tabIndex,
+ int fontHeight)
+ {
+ // FIXME: Handle HTML by using the view (see getTextViewForTab).
+
+ int height = fontHeight;
+ Icon icon = getIconForTab(tabIndex);
+ Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
+ if (icon != null)
+ height = Math.max(height, icon.getIconHeight());
+ height += tabInsets.top + tabInsets.bottom + 2;
+ return height;
+ }
+
+ /**
+ * This method returns the max tab height.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The maximum tab height.
+ */
+ protected int calculateMaxTabHeight(int tabPlacement)
+ {
+ maxTabHeight = 0;
+
+ FontMetrics fm = getFontMetrics();
+ int fontHeight = fm.getHeight();
+
+ for (int i = 0; i < tabPane.getTabCount(); i++)
+ maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
+ maxTabHeight);
+
+ return maxTabHeight;
+ }
+
+ /**
+ * This method calculates the tab width, including insets, for the given tab
+ * index and font metrics.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index to calculate for.
+ * @param metrics The font's metrics.
+ *
+ * @return The tab width for the given index.
+ */
+ protected int calculateTabWidth(int tabPlacement, int tabIndex,
+ FontMetrics metrics)
+ {
+ Icon icon = getIconForTab(tabIndex);
+ Insets insets = getTabInsets(tabPlacement, tabIndex);
+
+ int width = insets.bottom + insets.right + 3;
+ if (icon != null)
+ {
+ width += icon.getIconWidth() + textIconGap;
+ }
+
+ View v = getTextViewForTab(tabIndex);
+ if (v != null)
+ width += v.getPreferredSpan(View.X_AXIS);
+ else
+ {
+ String label = tabPane.getTitleAt(tabIndex);
+ width += metrics.stringWidth(label);
+ }
+ return width;
+ }
+
+ /**
+ * This method calculates the max tab width.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The maximum tab width.
+ */
+ protected int calculateMaxTabWidth(int tabPlacement)
+ {
+ maxTabWidth = 0;
+
+ FontMetrics fm = getFontMetrics();
+
+ for (int i = 0; i < tabPane.getTabCount(); i++)
+ maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
+ maxTabWidth);
+
+ return maxTabWidth;
+ }
+
+ /**
+ * This method calculates the tab area height, including insets, for the
+ * given amount of runs and tab height.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param horizRunCount The number of runs.
+ * @param maxTabHeight The max tab height.
+ *
+ * @return The tab area height.
+ */
+ protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
+ int maxTabHeight)
+ {
+ Insets insets = getTabAreaInsets(tabPlacement);
+ int tabAreaHeight = horizRunCount * maxTabHeight
+ - (horizRunCount - 1)
+ * getTabRunOverlay(tabPlacement);
+
+ tabAreaHeight += insets.top + insets.bottom;
+
+ return tabAreaHeight;
+ }
+
+ /**
+ * This method calculates the tab area width, including insets, for the
+ * given amount of runs and tab width.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param vertRunCount The number of runs.
+ * @param maxTabWidth The max tab width.
+ *
+ * @return The tab area width.
+ */
+ protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
+ int maxTabWidth)
+ {
+ Insets insets = getTabAreaInsets(tabPlacement);
+ int tabAreaWidth = vertRunCount * maxTabWidth
+ - (vertRunCount - 1)
+ * getTabRunOverlay(tabPlacement);
+
+ tabAreaWidth += insets.left + insets.right;
+
+ return tabAreaWidth;
+ }
+
+ /**
+ * This method returns the tab insets appropriately rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index.
+ *
+ * @return The tab insets for the given index.
+ */
+ protected Insets getTabInsets(int tabPlacement, int tabIndex)
+ {
+ return tabInsets;
+ }
+
+ /**
+ * This method returns the selected tab pad insets appropriately rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The selected tab pad insets.
+ */
+ protected Insets getSelectedTabPadInsets(int tabPlacement)
+ {
+ Insets target = new Insets(0, 0, 0, 0);
+ rotateInsets(selectedTabPadInsets, target, tabPlacement);
+ return target;
+ }
+
+ /**
+ * This method returns the tab area insets appropriately rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The tab area insets.
+ */
+ protected Insets getTabAreaInsets(int tabPlacement)
+ {
+ Insets target = new Insets(0, 0, 0, 0);
+ rotateInsets(tabAreaInsets, target, tabPlacement);
+ return target;
+ }
+
+ /**
+ * This method returns the content border insets appropriately rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The content border insets.
+ */
+ protected Insets getContentBorderInsets(int tabPlacement)
+ {
+ Insets target = new Insets(0, 0, 0, 0);
+ rotateInsets(contentBorderInsets, target, tabPlacement);
+ return target;
+ }
+
+ /**
+ * This method returns the fontmetrics for the font of the JTabbedPane.
+ *
+ * @return The font metrics for the JTabbedPane.
+ */
+ protected FontMetrics getFontMetrics()
+ {
+ FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont());
+ return fm;
+ }
+
+ /**
+ * This method navigates from the selected tab into the given direction. As
+ * a result, a new tab will be selected (if possible).
+ *
+ * @param direction The direction to navigate in.
+ */
+ protected void navigateSelectedTab(int direction)
+ {
+ int tabPlacement = tabPane.getTabPlacement();
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ if (direction == SwingConstants.WEST)
+ selectPreviousTabInRun(tabPane.getSelectedIndex());
+ else if (direction == SwingConstants.EAST)
+ selectNextTabInRun(tabPane.getSelectedIndex());
+
+ else
+ {
+ int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
+ tabPane.getSelectedIndex(),
+ (tabPlacement == SwingConstants.TOP)
+ ? direction == SwingConstants.NORTH
+ : direction == SwingConstants.SOUTH);
+ selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
+ offset);
+ }
+ }
+ if (tabPlacement == SwingConstants.LEFT
+ || tabPlacement == SwingConstants.RIGHT)
+ {
+ if (direction == SwingConstants.NORTH)
+ selectPreviousTabInRun(tabPane.getSelectedIndex());
+ else if (direction == SwingConstants.SOUTH)
+ selectNextTabInRun(tabPane.getSelectedIndex());
+ else
+ {
+ int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
+ tabPane.getSelectedIndex(),
+ (tabPlacement == SwingConstants.LEFT)
+ ? direction == SwingConstants.WEST
+ : direction == SwingConstants.EAST);
+ selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
+ offset);
+ }
+ }
+ }
+
+ /**
+ * This method selects the next tab in the run.
+ *
+ * @param current The current selected index.
+ */
+ protected void selectNextTabInRun(int current)
+ {
+ current = getNextTabIndexInRun(tabPane.getTabCount(),
+ current);
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(current, tabPane.getTabPlacement());
+
+ tabPane.setSelectedIndex(current);
+ }
+
+ /**
+ * This method selects the previous tab in the run.
+ *
+ * @param current The current selected index.
+ */
+ protected void selectPreviousTabInRun(int current)
+ {
+ current = getPreviousTabIndexInRun(tabPane.getTabCount(),
+ current);
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(current, tabPane.getTabPlacement());
+
+ tabPane.setSelectedIndex(current);
+ }
+
+ /**
+ * This method selects the next tab (regardless of runs).
+ *
+ * @param current The current selected index.
+ */
+ protected void selectNextTab(int current)
+ {
+ current = getNextTabIndex(current);
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(current, tabPane.getTabPlacement());
+
+ tabPane.setSelectedIndex(current);
+ }
+
+ /**
+ * This method selects the previous tab (regardless of runs).
+ *
+ * @param current The current selected index.
+ */
+ protected void selectPreviousTab(int current)
+ {
+ current = getPreviousTabIndex(current);
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(current, tabPane.getTabPlacement());
+
+ tabPane.setSelectedIndex(current);
+ }
+
+ /**
+ * This method selects the correct tab given an offset from the current tab
+ * index. If the tab placement is TOP or BOTTOM, the offset will be in the
+ * y direction, otherwise, it will be in the x direction. A new coordinate
+ * will be found by adding the offset to the current location of the tab.
+ * The tab that the new location will be selected.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab to start from.
+ * @param offset The coordinate offset.
+ */
+ protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
+ int offset)
+ {
+ int x = rects[tabIndex].x + rects[tabIndex].width / 2;
+ int y = rects[tabIndex].y + rects[tabIndex].height / 2;
+
+ switch (tabPlacement)
+ {
+ case SwingConstants.TOP:
+ case SwingConstants.BOTTOM:
+ y += offset;
+ break;
+ case SwingConstants.RIGHT:
+ case SwingConstants.LEFT:
+ x += offset;
+ break;
+ }
+
+ int index = tabForCoordinate(tabPane, x, y);
+ if (index != -1)
+ {
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(index, tabPlacement);
+ tabPane.setSelectedIndex(index);
+ }
+ }
+
+ // This method is called when you press up/down to cycle through tab runs.
+ // it returns the distance (between the two runs' x/y position.
+ // where one run is the current selected run and the other run is the run in the
+ // direction of the scroll (dictated by the forward flag)
+ // the offset is an absolute value of the difference
+
+ /**
+ * This method calculates the offset distance for use in
+ * selectAdjacentRunTab. The offset returned will be a difference in the y
+ * coordinate between the run in the desired direction and the current run
+ * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
+ * RIGHT.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabCount The number of tabs.
+ * @param tabIndex The starting index.
+ * @param forward If forward, the run in the desired direction will be the
+ * next run.
+ *
+ * @return The offset between the two runs.
+ */
+ protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
+ boolean forward)
+ {
+ int currRun = getRunForTab(tabCount, tabIndex);
+ int offset;
+ int nextRun = forward ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ offset = rects[lastTabInRun(tabCount, nextRun)].y
+ - rects[lastTabInRun(tabCount, currRun)].y;
+ else
+ offset = rects[lastTabInRun(tabCount, nextRun)].x
+ - rects[lastTabInRun(tabCount, currRun)].x;
+
+ return offset;
+ }
+
+ /**
+ * This method returns the previous tab index.
+ *
+ * @param base The index to start from.
+ *
+ * @return The previous tab index.
+ */
+ protected int getPreviousTabIndex(int base)
+ {
+ base--;
+ if (base < 0)
+ return tabPane.getTabCount() - 1;
+ return base;
+ }
+
+ /**
+ * This method returns the next tab index.
+ *
+ * @param base The index to start from.
+ *
+ * @return The next tab index.
+ */
+ protected int getNextTabIndex(int base)
+ {
+ base++;
+ if (base == tabPane.getTabCount())
+ return 0;
+ return base;
+ }
+
+ /**
+ * This method returns the next tab index in the run. If the next index is
+ * out of this run, it will return the starting tab index for the run.
+ *
+ * @param tabCount The number of tabs.
+ * @param base The index to start from.
+ *
+ * @return The next tab index in the run.
+ */
+ protected int getNextTabIndexInRun(int tabCount, int base)
+ {
+ int index = getNextTabIndex(base);
+ int run = getRunForTab(tabCount, base);
+ if (base == lastTabInRun(tabCount, run))
+ index = (run > 0)
+ ? lastTabInRun(tabCount, getPreviousTabRun(run)) + 1
+ : 0;
+
+ return index;
+ }
+
+ /**
+ * This method returns the previous tab index in the run. If the previous
+ * index is out of this run, it will return the last index for the run.
+ *
+ * @param tabCount The number of tabs.
+ * @param base The index to start from.
+ *
+ * @return The previous tab index in the run.
+ */
+ protected int getPreviousTabIndexInRun(int tabCount, int base)
+ {
+ int index = getPreviousTabIndex(base);
+ int run = getRunForTab(tabCount, base);
+ if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
+ index = lastTabInRun(tabCount, run);
+
+ return index;
+ }
+
+ /**
+ * This method returns the index of the previous run.
+ *
+ * @param baseRun The run to start from.
+ *
+ * @return The index of the previous run.
+ */
+ protected int getPreviousTabRun(int baseRun)
+ {
+ if (getTabRunCount(tabPane) == 1)
+ return 1;
+
+ int prevRun = --baseRun;
+ if (prevRun < 0)
+ prevRun = getTabRunCount(tabPane) - 1;
+ return prevRun;
+ }
+
+ /**
+ * This method returns the index of the next run.
+ *
+ * @param baseRun The run to start from.
+ *
+ * @return The index of the next run.
+ */
+ protected int getNextTabRun(int baseRun)
+ {
+ if (getTabRunCount(tabPane) == 1)
+ return 1;
+
+ int nextRun = ++baseRun;
+ if (nextRun == getTabRunCount(tabPane))
+ nextRun = 0;
+ return nextRun;
+ }
+
+ /**
+ * This method rotates the insets given a direction to rotate them in.
+ * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated
+ * insets will be stored in targetInsets. Passing in TOP as the direction
+ * does nothing. Passing in LEFT switches top and left, right and bottom.
+ * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
+ * for left, left for bottom, bottom for right, and right for top.
+ *
+ * @param topInsets The reference insets.
+ * @param targetInsets An Insets object to store the new insets.
+ * @param targetPlacement The rotation direction.
+ */
+ protected static void rotateInsets(Insets topInsets, Insets targetInsets,
+ int targetPlacement)
+ {
+ // Sun's version will happily throw an NPE if params are null,
+ // so I won't check it either.
+ switch (targetPlacement)
+ {
+ default:
+ case SwingConstants.TOP:
+ targetInsets.top = topInsets.top;
+ targetInsets.left = topInsets.left;
+ targetInsets.right = topInsets.right;
+ targetInsets.bottom = topInsets.bottom;
+ break;
+ case SwingConstants.LEFT:
+ targetInsets.left = topInsets.top;
+ targetInsets.top = topInsets.left;
+ targetInsets.right = topInsets.bottom;
+ targetInsets.bottom = topInsets.right;
+ break;
+ case SwingConstants.BOTTOM:
+ targetInsets.top = topInsets.bottom;
+ targetInsets.bottom = topInsets.top;
+ targetInsets.left = topInsets.left;
+ targetInsets.right = topInsets.right;
+ break;
+ case SwingConstants.RIGHT:
+ targetInsets.top = topInsets.left;
+ targetInsets.left = topInsets.bottom;
+ targetInsets.bottom = topInsets.right;
+ targetInsets.right = topInsets.top;
+ break;
+ }
+ }
+
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("TabbedPane.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("TabbedPane.actionMap", map);
+ }
+ return map;
+ }
+
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+
+ map.put("navigatePageDown", new NavigatePageDownAction());
+ map.put("navigatePageUp", new NavigatePageUpAction());
+ map.put("navigateDown",
+ new NavigateAction("navigateDown", SwingConstants.SOUTH));
+
+ map.put("navigateUp",
+ new NavigateAction("navigateUp", SwingConstants.NORTH));
+
+ map.put("navigateLeft",
+ new NavigateAction("navigateLeft", SwingConstants.WEST));
+
+ map.put("navigateRight",
+ new NavigateAction("navigateRight", SwingConstants.EAST));
+
+ map.put("requestFocusForVisibleComponent",
+ new RequestFocusForVisibleComponentAction());
+ map.put("requestFocus", new RequestFocusAction());
+
+ return map;
+ }
+
+ /**
+ * Sets the tab which should be highlighted when in rollover mode. And
+ * <code>index</code> of <code>-1</code> means that the rollover tab
+ * is deselected (i.e. the mouse is outside of the tabarea).
+ *
+ * @param index the index of the tab that is under the mouse, <code>-1</code>
+ * for no tab
+ *
+ * @since 1.5
+ */
+ protected void setRolloverTab(int index)
+ {
+ rolloverTab = index;
+ }
+
+ /**
+ * Retunrs the index of the tab over which the mouse is currently moving,
+ * or <code>-1</code> for no tab.
+ *
+ * @return the index of the tab over which the mouse is currently moving,
+ * or <code>-1</code> for no tab
+ *
+ * @since 1.5
+ */
+ protected int getRolloverTab()
+ {
+ return rolloverTab;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
new file mode 100644
index 000000000..7ff234e6d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
@@ -0,0 +1,566 @@
+/* BasicTableHeaderUI.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+
+import javax.swing.CellRendererPane;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TableHeaderUI;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+/**
+ * Basic pluggable look and feel interface for JTableHeader.
+ */
+public class BasicTableHeaderUI extends TableHeaderUI
+{
+ /**
+ * The width of the space (in both direction) around the column boundary,
+ * where mouse cursor changes shape into "resize"
+ */
+ static int COLUMN_BOUNDARY_TOLERANCE = 3;
+
+ public static ComponentUI createUI(JComponent h)
+ {
+ return new BasicTableHeaderUI();
+ }
+
+ /**
+ * The table header that is using this interface.
+ */
+ protected JTableHeader header;
+
+ /**
+ * The mouse input listener, responsible for mouse manipulations with
+ * the table header.
+ */
+ protected MouseInputListener mouseInputListener;
+
+ /**
+ * Paint the header cell.
+ */
+ protected CellRendererPane rendererPane;
+
+ /**
+ * The header cell border.
+ */
+ private Border cellBorder;
+
+ /**
+ * Original mouse cursor prior to resizing.
+ */
+ private Cursor originalCursor;
+
+ /**
+ * If not null, one of the columns is currently being dragged.
+ */
+ Rectangle draggingHeaderRect;
+
+ /**
+ * Handles column movement and rearrangement by mouse. The same instance works
+ * both as mouse listener and the mouse motion listner.
+ */
+ public class MouseInputHandler
+ implements MouseInputListener
+ {
+ /**
+ * If true, the cursor is being already shown in the alternative "resize"
+ * shape.
+ */
+ boolean showingResizeCursor;
+
+ /**
+ * The position, from where the cursor is dragged during resizing. Double
+ * purpose field (absolute value during resizing and relative offset during
+ * column dragging).
+ */
+ int draggingFrom = - 1;
+
+ /**
+ * The number of the column being dragged.
+ */
+ int draggingColumnNumber;
+
+ /**
+ * The previous preferred width of the column.
+ */
+ int prevPrefWidth = - 1;
+
+ /**
+ * The timer to coalesce column resizing events.
+ */
+ Timer timer;
+
+ /**
+ * Returns without action, part of the MouseInputListener interface.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * If being in the resizing mode, handle resizing.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ TableColumn resizeIt = header.getResizingColumn();
+ if (resizeIt != null && header.getResizingAllowed())
+ {
+ // The timer is intialised on demand.
+ if (timer == null)
+ {
+ // The purpose of timer is to coalesce events. If the queue
+ // is free, the repaint event is fired immediately.
+ timer = new Timer(1, new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ header.getTable().doLayout();
+ }
+ });
+ timer.setRepeats(false);
+ timer.setCoalesce(true);
+ }
+ resizeIt.setPreferredWidth(prevPrefWidth + e.getX() - draggingFrom);
+ timer.restart();
+ }
+ else if (draggingHeaderRect != null && header.getReorderingAllowed())
+ {
+ draggingHeaderRect.x = e.getX() + draggingFrom;
+ header.repaint();
+ }
+ }
+
+ /**
+ * Returns without action, part of the MouseInputListener interface.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * Reset drag information of the column resizing.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * Change the mouse cursor if the mouse if above the column boundary.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // When dragging, the functionality is handled by the mouseDragged.
+ if (e.getButton() == 0 && header.getResizingAllowed())
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n < 2)
+ // It must be at least two columns to have at least one boundary.
+ // Otherwise, nothing to do.
+ return;
+
+ boolean onBoundary = false;
+
+ int x = e.getX();
+ int a = x - COLUMN_BOUNDARY_TOLERANCE;
+ int b = x + COLUMN_BOUNDARY_TOLERANCE;
+
+ int p = 0;
+
+ Scan: for (int i = 0; i < n - 1; i++)
+ {
+ p += model.getColumn(i).getWidth();
+
+ if (p >= a && p <= b)
+ {
+ TableColumn column = model.getColumn(i);
+ onBoundary = true;
+
+ draggingFrom = x;
+ prevPrefWidth = column.getWidth();
+ header.setResizingColumn(column);
+ break Scan;
+ }
+ }
+
+ if (onBoundary != showingResizeCursor)
+ {
+ // Change the cursor shape, if needed.
+ if (onBoundary)
+ {
+
+ originalCursor = header.getCursor();
+ if (p < x)
+ header.setCursor(Cursor.getPredefinedCursor(
+ Cursor.W_RESIZE_CURSOR));
+ else
+ header.setCursor(Cursor.getPredefinedCursor(
+ Cursor.E_RESIZE_CURSOR));
+ }
+ else
+ {
+ header.setCursor(originalCursor);
+ header.setResizingColumn(null);
+ }
+
+ showingResizeCursor = onBoundary;
+ }
+ }
+ }
+
+ /**
+ * Starts the dragging/resizing procedure.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (header.getResizingAllowed())
+ {
+ TableColumn resizingColumn = header.getResizingColumn();
+ if (resizingColumn != null)
+ {
+ resizingColumn.setPreferredWidth(resizingColumn.getWidth());
+ return;
+ }
+ }
+
+ if (header.getReorderingAllowed())
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n < 2)
+ // It must be at least two columns to change the column location.
+ return;
+
+ boolean onBoundary = false;
+
+ int x = e.getX();
+ int p = 0;
+ int col = - 1;
+
+ Scan: for (int i = 0; i < n; i++)
+ {
+ p += model.getColumn(i).getWidth();
+ if (p > x)
+ {
+ col = i;
+ break Scan;
+ }
+ }
+ if (col < 0)
+ return;
+
+ TableColumn dragIt = model.getColumn(col);
+ header.setDraggedColumn(dragIt);
+
+ draggingFrom = (p - dragIt.getWidth()) - x;
+ draggingHeaderRect = new Rectangle(header.getHeaderRect(col));
+ draggingColumnNumber = col;
+ }
+ }
+
+ /**
+ * Set all column preferred width to the current width to prevend abrupt
+ * width changes during the next resize.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (header.getResizingColumn() != null && header.getResizingAllowed())
+ endResizing();
+ if (header.getDraggedColumn() != null && header.getReorderingAllowed())
+ endDragging(e);
+ }
+
+ /**
+ * Stop resizing session.
+ */
+ void endResizing()
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n > 2)
+ {
+ TableColumn c;
+ for (int i = 0; i < n; i++)
+ {
+ c = model.getColumn(i);
+ c.setPreferredWidth(c.getWidth());
+ }
+ }
+ header.setResizingColumn(null);
+ showingResizeCursor = false;
+ if (timer != null)
+ timer.stop();
+ header.setCursor(originalCursor);
+ }
+
+ /**
+ * Stop the dragging session.
+ *
+ * @param e the "mouse release" mouse event, needed to determing the final
+ * location for the dragged column.
+ */
+ void endDragging(MouseEvent e)
+ {
+ header.setDraggedColumn(null);
+ draggingHeaderRect = null;
+
+ TableColumnModel model = header.getColumnModel();
+
+ // Find where have we dragged the column.
+ int x = e.getX();
+ int p = 0;
+
+ int col = model.getColumnCount() - 1;
+ int n = model.getColumnCount();
+
+ // This loop does not find the column if the mouse if out of the
+ // right boundary of the table header. Then we make this column the
+ // rightmost column.
+ Scan: for (int i = 0; i < n; i++)
+ {
+ p += model.getColumn(i).getWidth();
+ if (p > x)
+ {
+ col = i;
+ break Scan;
+ }
+ }
+
+ header.getTable().moveColumn(draggingColumnNumber, col);
+ }
+ }
+
+ /**
+ * Create and return the mouse input listener.
+ *
+ * @return the mouse listener ({@link MouseInputHandler}, if not overridden.
+ */
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * Construct a new BasicTableHeaderUI, create mouse listeners.
+ */
+ public BasicTableHeaderUI()
+ {
+ mouseInputListener = createMouseInputListener();
+ }
+
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(header, "TableHeader.background",
+ "TableHeader.foreground",
+ "TableHeader.font");
+ cellBorder = UIManager.getBorder("TableHeader.cellBorder");
+ }
+
+ protected void installKeyboardActions()
+ {
+ // AFAICS, the RI does nothing here.
+ }
+
+ /**
+ * Add the mouse listener and the mouse motion listener to the table
+ * header. The listeners support table column resizing and rearrangement
+ * by mouse.
+ */
+ protected void installListeners()
+ {
+ header.addMouseListener(mouseInputListener);
+ header.addMouseMotionListener(mouseInputListener);
+ }
+
+ public void installUI(JComponent c)
+ {
+ header = (JTableHeader) c;
+ rendererPane = new CellRendererPane();
+ installDefaults();
+ installKeyboardActions();
+ installListeners();
+ }
+
+ protected void uninstallDefaults()
+ {
+ header.setBackground(null);
+ header.setForeground(null);
+ header.setFont(null);
+ }
+
+ protected void uninstallKeyboardActions()
+ {
+ // AFAICS, the RI does nothing here.
+ }
+
+ /**
+ * Remove the previously installed listeners.
+ */
+ protected void uninstallListeners()
+ {
+ header.removeMouseListener(mouseInputListener);
+ header.removeMouseMotionListener(mouseInputListener);
+ }
+
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallKeyboardActions();
+ uninstallDefaults();
+ }
+
+ /**
+ * Repaint the table header.
+ */
+ public void paint(Graphics gfx, JComponent c)
+ {
+ TableColumnModel cmod = header.getColumnModel();
+ int ncols = cmod.getColumnCount();
+ if (ncols == 0)
+ return;
+
+ Rectangle clip = gfx.getClipBounds();
+ TableCellRenderer defaultRend = header.getDefaultRenderer();
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ Rectangle bounds = header.getHeaderRect(i);
+ if (bounds.intersects(clip))
+ {
+ Rectangle oldClip = gfx.getClipBounds();
+ TableColumn col = cmod.getColumn(i);
+ TableCellRenderer rend = col.getHeaderRenderer();
+ if (rend == null)
+ rend = defaultRend;
+ Object val = col.getHeaderValue();
+ Component comp = rend.getTableCellRendererComponent(header.getTable(),
+ val,
+ false, // isSelected
+ false, // isFocused
+ -1, i);
+ // FIXME: The following settings should be performed in
+ // rend.getTableCellRendererComponent().
+ comp.setFont(header.getFont());
+ comp.setBackground(header.getBackground());
+ comp.setForeground(header.getForeground());
+ if (comp instanceof JComponent)
+ ((JComponent) comp).setBorder(cellBorder);
+ rendererPane.paintComponent(gfx, comp, header, bounds.x, bounds.y,
+ bounds.width, bounds.height);
+ }
+ }
+
+ // This displays a running rectangle that is much simplier than the total
+ // animation, as it is seen in Sun's application.
+ // TODO animate the collumn dragging like in Sun's jre.
+ if (draggingHeaderRect != null)
+ {
+ gfx.setColor(header.getForeground());
+ gfx.drawRect(draggingHeaderRect.x, draggingHeaderRect.y + 2,
+ draggingHeaderRect.width - 1, draggingHeaderRect.height - 6);
+ }
+ }
+
+ /**
+ * Get the preferred header size.
+ *
+ * @param ignored unused
+ *
+ * @return the preferred size of the associated header.
+ */
+ public Dimension getPreferredSize(JComponent ignored)
+ {
+ TableColumnModel cmod = header.getColumnModel();
+ TableCellRenderer defaultRend = header.getDefaultRenderer();
+ int ncols = cmod.getColumnCount();
+ Dimension ret = new Dimension(0, 0);
+ int spacing = 0;
+
+ if (header.getTable() != null
+ && header.getTable().getIntercellSpacing() != null)
+ spacing = header.getTable().getIntercellSpacing().width;
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ TableColumn col = cmod.getColumn(i);
+ TableCellRenderer rend = col.getHeaderRenderer();
+ if (rend == null)
+ rend = defaultRend;
+ Object val = col.getHeaderValue();
+ Component comp = rend.getTableCellRendererComponent(header.getTable(),
+ val,
+ false, // isSelected
+ false, // isFocused
+ -1, i);
+ comp.setFont(header.getFont());
+ comp.setBackground(header.getBackground());
+ comp.setForeground(header.getForeground());
+ if (comp instanceof JComponent)
+ ((JComponent) comp).setBorder(cellBorder);
+
+ Dimension d = comp.getPreferredSize();
+ ret.width += spacing;
+ ret.height = Math.max(d.height, ret.height);
+ }
+ ret.width = cmod.getTotalColumnWidth();
+ return ret;
+ }
+
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java
new file mode 100644
index 000000000..f5a4bcb67
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java
@@ -0,0 +1,1410 @@
+/* BasicTableUI.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.CellRendererPane;
+import javax.swing.DefaultCellEditor;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.TransferHandler;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TableUI;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+import javax.swing.table.TableModel;
+
+public class BasicTableUI extends TableUI
+{
+ public static ComponentUI createUI(JComponent comp)
+ {
+ return new BasicTableUI();
+ }
+
+ protected FocusListener focusListener;
+ protected KeyListener keyListener;
+ protected MouseInputListener mouseInputListener;
+ protected CellRendererPane rendererPane;
+ protected JTable table;
+
+ /** The normal cell border. */
+ Border cellBorder;
+
+ /** The action bound to KeyStrokes. */
+ TableAction action;
+
+ /**
+ * Listens for changes to the tables properties.
+ */
+ private PropertyChangeListener propertyChangeListener;
+
+ /**
+ * Handles key events for the JTable. Key events should be handled through
+ * the InputMap/ActionMap mechanism since JDK1.3. This class is only there
+ * for backwards compatibility.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class KeyHandler implements KeyListener
+ {
+
+ /**
+ * Receives notification that a key has been pressed and released.
+ * Activates the editing session for the focused cell by pressing the
+ * character keys.
+ *
+ * @param event the key event
+ */
+ public void keyTyped(KeyEvent event)
+ {
+ // Key events should be handled through the InputMap/ActionMap mechanism
+ // since JDK1.3. This class is only there for backwards compatibility.
+
+ // Editor activation is a specific kind of response to ''any''
+ // character key. Hence it is handled here.
+ if (!table.isEditing() && table.isEnabled())
+ {
+ int r = table.getSelectedRow();
+ int c = table.getSelectedColumn();
+ if (table.isCellEditable(r, c))
+ table.editCellAt(r, c);
+ }
+ }
+
+ /**
+ * Receives notification that a key has been pressed.
+ *
+ * @param event the key event
+ */
+ public void keyPressed(KeyEvent event)
+ {
+ // Key events should be handled through the InputMap/ActionMap mechanism
+ // since JDK1.3. This class is only there for backwards compatibility.
+ }
+
+ /**
+ * Receives notification that a key has been released.
+ *
+ * @param event the key event
+ */
+ public void keyReleased(KeyEvent event)
+ {
+ // Key events should be handled through the InputMap/ActionMap mechanism
+ // since JDK1.3. This class is only there for backwards compatibility.
+ }
+ }
+
+ public class FocusHandler implements FocusListener
+ {
+ public void focusGained(FocusEvent e)
+ {
+ // The only thing that is affected by a focus change seems to be
+ // how the lead cell is painted. So we repaint this cell.
+ repaintLeadCell();
+ }
+
+ public void focusLost(FocusEvent e)
+ {
+ // The only thing that is affected by a focus change seems to be
+ // how the lead cell is painted. So we repaint this cell.
+ repaintLeadCell();
+ }
+
+ /**
+ * Repaints the lead cell in response to a focus change, to refresh
+ * the display of the focus indicator.
+ */
+ private void repaintLeadCell()
+ {
+ int rowCount = table.getRowCount();
+ int columnCount = table.getColumnCount();
+ int rowLead = table.getSelectionModel().getLeadSelectionIndex();
+ int columnLead = table.getColumnModel().getSelectionModel().
+ getLeadSelectionIndex();
+ if (rowLead >= 0 && rowLead < rowCount && columnLead >= 0
+ && columnLead < columnCount)
+ {
+ Rectangle dirtyRect = table.getCellRect(rowLead, columnLead, false);
+ table.repaint(dirtyRect);
+ }
+ }
+ }
+
+ public class MouseInputHandler implements MouseInputListener
+ {
+ Point begin, curr;
+
+ private void updateSelection(boolean controlPressed)
+ {
+ // Update the rows
+ int lo_row = table.rowAtPoint(begin);
+ int hi_row = table.rowAtPoint(curr);
+ ListSelectionModel rowModel = table.getSelectionModel();
+ if (lo_row != -1 && hi_row != -1)
+ {
+ if (controlPressed && rowModel.getSelectionMode()
+ != ListSelectionModel.SINGLE_SELECTION)
+ rowModel.addSelectionInterval(lo_row, hi_row);
+ else
+ rowModel.setSelectionInterval(lo_row, hi_row);
+ }
+
+ // Update the columns
+ int lo_col = table.columnAtPoint(begin);
+ int hi_col = table.columnAtPoint(curr);
+ ListSelectionModel colModel = table.getColumnModel().
+ getSelectionModel();
+ if (lo_col != -1 && hi_col != -1)
+ {
+ if (controlPressed && colModel.getSelectionMode() !=
+ ListSelectionModel.SINGLE_SELECTION)
+ colModel.addSelectionInterval(lo_col, hi_col);
+ else
+ colModel.setSelectionInterval(lo_col, hi_col);
+ }
+ }
+
+ /**
+ * For the double click, start the cell editor.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ Point p = e.getPoint();
+ int row = table.rowAtPoint(p);
+ int col = table.columnAtPoint(p);
+ if (table.isCellEditable(row, col))
+ {
+ // If the cell editor is the default editor, we request the
+ // number of the required clicks from it. Otherwise,
+ // require two clicks (double click).
+ TableCellEditor editor = table.getCellEditor(row, col);
+ if (editor instanceof DefaultCellEditor)
+ {
+ DefaultCellEditor ce = (DefaultCellEditor) editor;
+ if (e.getClickCount() < ce.getClickCountToStart())
+ return;
+ }
+ table.editCellAt(row, col);
+ }
+ }
+
+ public void mouseDragged(MouseEvent e)
+ {
+ if (table.isEnabled())
+ {
+ curr = new Point(e.getX(), e.getY());
+ updateSelection(e.isControlDown());
+ }
+ }
+
+ public void mouseEntered(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseExited(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mousePressed(MouseEvent e)
+ {
+ if (table.isEnabled())
+ {
+ ListSelectionModel rowModel = table.getSelectionModel();
+ ListSelectionModel colModel = table.getColumnModel().getSelectionModel();
+ int rowLead = rowModel.getLeadSelectionIndex();
+ int colLead = colModel.getLeadSelectionIndex();
+
+ begin = new Point(e.getX(), e.getY());
+ curr = new Point(e.getX(), e.getY());
+ //if control is pressed and the cell is already selected, deselect it
+ if (e.isControlDown() && table.isCellSelected(
+ table.rowAtPoint(begin), table.columnAtPoint(begin)))
+ {
+ table.getSelectionModel().
+ removeSelectionInterval(table.rowAtPoint(begin),
+ table.rowAtPoint(begin));
+ table.getColumnModel().getSelectionModel().
+ removeSelectionInterval(table.columnAtPoint(begin),
+ table.columnAtPoint(begin));
+ }
+ else
+ updateSelection(e.isControlDown());
+
+ // If we were editing, but the moved to another cell, stop editing
+ if (rowLead != rowModel.getLeadSelectionIndex() ||
+ colLead != colModel.getLeadSelectionIndex())
+ if (table.isEditing())
+ table.editingStopped(new ChangeEvent(e));
+
+ // Must request focus explicitly.
+ table.requestFocusInWindow();
+ }
+ }
+
+ public void mouseReleased(MouseEvent e)
+ {
+ if (table.isEnabled())
+ {
+ begin = null;
+ curr = null;
+ }
+ }
+ }
+
+ /**
+ * Listens for changes to the model property of the JTable and adjusts some
+ * settings.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Receives notification if one of the JTable's properties changes.
+ *
+ * @param ev the property change event
+ */
+ public void propertyChange(PropertyChangeEvent ev)
+ {
+ String propName = ev.getPropertyName();
+ if (propName.equals("model"))
+ {
+ ListSelectionModel rowSel = table.getSelectionModel();
+ rowSel.clearSelection();
+ ListSelectionModel colSel = table.getColumnModel().getSelectionModel();
+ colSel.clearSelection();
+ TableModel model = table.getModel();
+
+ // Adjust lead and anchor selection indices of the row and column
+ // selection models.
+ if (model.getRowCount() > 0)
+ {
+ rowSel.setAnchorSelectionIndex(0);
+ rowSel.setLeadSelectionIndex(0);
+ }
+ else
+ {
+ rowSel.setAnchorSelectionIndex(-1);
+ rowSel.setLeadSelectionIndex(-1);
+ }
+ if (model.getColumnCount() > 0)
+ {
+ colSel.setAnchorSelectionIndex(0);
+ colSel.setLeadSelectionIndex(0);
+ }
+ else
+ {
+ colSel.setAnchorSelectionIndex(-1);
+ colSel.setLeadSelectionIndex(-1);
+ }
+ }
+ }
+ }
+
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+
+ /**
+ * Creates and returns a key listener for the JTable.
+ *
+ * @return a key listener for the JTable
+ */
+ protected KeyListener createKeyListener()
+ {
+ return new KeyHandler();
+ }
+
+ /**
+ * Return the maximum size of the table. The maximum height is the row
+ * height times the number of rows. The maximum width is the sum of
+ * the maximum widths of each column.
+ *
+ * @param comp the component whose maximum size is being queried,
+ * this is ignored.
+ * @return a Dimension object representing the maximum size of the table,
+ * or null if the table has no elements.
+ */
+ public Dimension getMaximumSize(JComponent comp)
+ {
+ int maxTotalColumnWidth = 0;
+ for (int i = 0; i < table.getColumnCount(); i++)
+ maxTotalColumnWidth += table.getColumnModel().getColumn(i).getMaxWidth();
+
+ return new Dimension(maxTotalColumnWidth, getHeight());
+ }
+
+ /**
+ * Return the minimum size of the table. The minimum height is the row
+ * height times the number of rows. The minimum width is the sum of
+ * the minimum widths of each column.
+ *
+ * @param comp the component whose minimum size is being queried,
+ * this is ignored.
+ * @return a Dimension object representing the minimum size of the table,
+ * or null if the table has no elements.
+ */
+ public Dimension getMinimumSize(JComponent comp)
+ {
+ int minTotalColumnWidth = 0;
+ for (int i = 0; i < table.getColumnCount(); i++)
+ minTotalColumnWidth += table.getColumnModel().getColumn(i).getMinWidth();
+
+ return new Dimension(minTotalColumnWidth, getHeight());
+ }
+
+ /**
+ * Returns the preferred size for the table of that UI.
+ *
+ * @param comp ignored, the <code>table</code> field is used instead
+ *
+ * @return the preferred size for the table of that UI
+ */
+ public Dimension getPreferredSize(JComponent comp)
+ {
+ int prefTotalColumnWidth = 0;
+ TableColumnModel tcm = table.getColumnModel();
+
+ for (int i = 0; i < tcm.getColumnCount(); i++)
+ {
+ TableColumn col = tcm.getColumn(i);
+ prefTotalColumnWidth += col.getPreferredWidth();
+ }
+
+ return new Dimension(prefTotalColumnWidth, getHeight());
+ }
+
+ /**
+ * Returns the table height. This helper method is used by
+ * {@link #getMinimumSize(JComponent)}, {@link #getPreferredSize(JComponent)}
+ * and {@link #getMaximumSize(JComponent)} to determine the table height.
+ *
+ * @return the table height
+ */
+ private int getHeight()
+ {
+ int height = 0;
+ int rowCount = table.getRowCount();
+ if (rowCount > 0 && table.getColumnCount() > 0)
+ {
+ Rectangle r = table.getCellRect(rowCount - 1, 0, true);
+ height = r.y + r.height;
+ }
+ return height;
+ }
+
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(table, "Table.background",
+ "Table.foreground", "Table.font");
+ table.setGridColor(UIManager.getColor("Table.gridColor"));
+ table.setSelectionForeground(UIManager.getColor("Table.selectionForeground"));
+ table.setSelectionBackground(UIManager.getColor("Table.selectionBackground"));
+ table.setOpaque(true);
+ }
+
+ /**
+ * Installs keyboard actions on the table.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install the input map.
+ InputMap inputMap =
+ (InputMap) SharedUIDefaults.get("Table.ancestorInputMap");
+ SwingUtilities.replaceUIInputMap(table,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ inputMap);
+
+ // FIXME: The JDK uses a LazyActionMap for parentActionMap
+ SwingUtilities.replaceUIActionMap(table, getActionMap());
+
+ }
+
+ /**
+ * Fetches the action map from the UI defaults, or create a new one
+ * if the action map hasn't been initialized.
+ *
+ * @return the action map
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("Table.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("Table.actionMap", am);
+ }
+ return am;
+ }
+
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new TableAction();
+
+ am.put("cut", TransferHandler.getCutAction());
+ am.put("copy", TransferHandler.getCopyAction());
+ am.put("paste", TransferHandler.getPasteAction());
+
+ am.put("cancel", action);
+ am.put("selectAll", action);
+ am.put("clearSelection", action);
+ am.put("startEditing", action);
+
+ am.put("selectNextRow", action);
+ am.put("selectNextRowCell", action);
+ am.put("selectNextRowExtendSelection", action);
+ am.put("selectNextRowChangeLead", action);
+
+ am.put("selectPreviousRow", action);
+ am.put("selectPreviousRowCell", action);
+ am.put("selectPreviousRowExtendSelection", action);
+ am.put("selectPreviousRowChangeLead", action);
+
+ am.put("selectNextColumn", action);
+ am.put("selectNextColumnCell", action);
+ am.put("selectNextColumnExtendSelection", action);
+ am.put("selectNextColumnChangeLead", action);
+
+ am.put("selectPreviousColumn", action);
+ am.put("selectPreviousColumnCell", action);
+ am.put("selectPreviousColumnExtendSelection", action);
+ am.put("selectPreviousColumnChangeLead", action);
+
+ am.put("scrollLeftChangeSelection", action);
+ am.put("scrollLeftExtendSelection", action);
+ am.put("scrollRightChangeSelection", action);
+ am.put("scrollRightExtendSelection", action);
+
+ am.put("scrollUpChangeSelection", action);
+ am.put("scrollUpExtendSelection", action);
+ am.put("scrollDownChangeSelection", action);
+ am.put("scrolldownExtendSelection", action);
+
+ am.put("selectFirstColumn", action);
+ am.put("selectFirstColumnExtendSelection", action);
+ am.put("selectLastColumn", action);
+ am.put("selectLastColumnExtendSelection", action);
+
+ am.put("selectFirstRow", action);
+ am.put("selectFirstRowExtendSelection", action);
+ am.put("selectLastRow", action);
+ am.put("selectLastRowExtendSelection", action);
+
+ am.put("addToSelection", action);
+ am.put("toggleAndAnchor", action);
+ am.put("extendTo", action);
+ am.put("moveSelectionTo", action);
+
+ return am;
+ }
+
+ /**
+ * This class implements the actions that we want to happen
+ * when specific keys are pressed for the JTable. The actionPerformed
+ * method is called when a key that has been registered for the JTable
+ * is received.
+ */
+ private static class TableAction
+ extends AbstractAction
+ {
+ /**
+ * What to do when this action is called.
+ *
+ * @param e the ActionEvent that caused this action.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JTable table = (JTable) e.getSource();
+
+ DefaultListSelectionModel rowModel
+ = (DefaultListSelectionModel) table.getSelectionModel();
+ DefaultListSelectionModel colModel
+ = (DefaultListSelectionModel) table.getColumnModel().getSelectionModel();
+
+ int rowLead = rowModel.getLeadSelectionIndex();
+ int rowMax = table.getModel().getRowCount() - 1;
+
+ int colLead = colModel.getLeadSelectionIndex();
+ int colMax = table.getModel().getColumnCount() - 1;
+
+ // The command with which the action has been called is stored
+ // in this undocumented action value. This allows us to have only
+ // one Action instance to serve all keyboard input for JTable.
+ String command = (String) getValue("__command__");
+ if (command.equals("selectPreviousRowExtendSelection"))
+ {
+ rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0));
+ }
+ else if (command.equals("selectLastColumn"))
+ {
+ colModel.setSelectionInterval(colMax, colMax);
+ }
+ else if (command.equals("startEditing"))
+ {
+ if (table.isCellEditable(rowLead, colLead))
+ table.editCellAt(rowLead, colLead);
+ }
+ else if (command.equals("selectFirstRowExtendSelection"))
+ {
+ rowModel.setLeadSelectionIndex(0);
+ }
+ else if (command.equals("selectFirstColumn"))
+ {
+ colModel.setSelectionInterval(0, 0);
+ }
+ else if (command.equals("selectFirstColumnExtendSelection"))
+ {
+ colModel.setLeadSelectionIndex(0);
+ }
+ else if (command.equals("selectLastRow"))
+ {
+ rowModel.setSelectionInterval(rowMax, rowMax);
+ }
+ else if (command.equals("selectNextRowExtendSelection"))
+ {
+ rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
+ }
+ else if (command.equals("selectFirstRow"))
+ {
+ rowModel.setSelectionInterval(0, 0);
+ }
+ else if (command.equals("selectNextColumnExtendSelection"))
+ {
+ colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax));
+ }
+ else if (command.equals("selectLastColumnExtendSelection"))
+ {
+ colModel.setLeadSelectionIndex(colMax);
+ }
+ else if (command.equals("selectPreviousColumnExtendSelection"))
+ {
+ colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0));
+ }
+ else if (command.equals("selectNextRow"))
+ {
+ rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
+ Math.min(rowLead + 1, rowMax));
+ }
+ else if (command.equals("scrollUpExtendSelection"))
+ {
+ int target;
+ if (rowLead == getFirstVisibleRowIndex(table))
+ target = Math.max(0, rowLead - (getLastVisibleRowIndex(table)
+ - getFirstVisibleRowIndex(table) + 1));
+ else
+ target = getFirstVisibleRowIndex(table);
+
+ rowModel.setLeadSelectionIndex(target);
+ colModel.setLeadSelectionIndex(colLead);
+ }
+ else if (command.equals("selectPreviousRow"))
+ {
+ rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
+ Math.max(rowLead - 1, 0));
+ }
+ else if (command.equals("scrollRightChangeSelection"))
+ {
+ int target;
+ if (colLead == getLastVisibleColumnIndex(table))
+ target = Math.min(colMax, colLead
+ + (getLastVisibleColumnIndex(table)
+ - getFirstVisibleColumnIndex(table) + 1));
+ else
+ target = getLastVisibleColumnIndex(table);
+
+ colModel.setSelectionInterval(target, target);
+ rowModel.setSelectionInterval(rowLead, rowLead);
+ }
+ else if (command.equals("selectPreviousColumn"))
+ {
+ colModel.setSelectionInterval(Math.max(colLead - 1, 0),
+ Math.max(colLead - 1, 0));
+ }
+ else if (command.equals("scrollLeftChangeSelection"))
+ {
+ int target;
+ if (colLead == getFirstVisibleColumnIndex(table))
+ target = Math.max(0, colLead - (getLastVisibleColumnIndex(table)
+ - getFirstVisibleColumnIndex(table) + 1));
+ else
+ target = getFirstVisibleColumnIndex(table);
+
+ colModel.setSelectionInterval(target, target);
+ rowModel.setSelectionInterval(rowLead, rowLead);
+ }
+ else if (command.equals("clearSelection"))
+ {
+ table.clearSelection();
+ }
+ else if (command.equals("cancel"))
+ {
+ // FIXME: implement other parts of "cancel" like undo-ing last
+ // selection. Right now it just calls editingCancelled if
+ // we're currently editing.
+ if (table.isEditing())
+ table.editingCanceled(new ChangeEvent("cancel"));
+ }
+ else if (command.equals("selectNextRowCell")
+ || command.equals("selectPreviousRowCell")
+ || command.equals("selectNextColumnCell")
+ || command.equals("selectPreviousColumnCell"))
+ {
+ // If nothing is selected, select the first cell in the table
+ if (table.getSelectedRowCount() == 0 &&
+ table.getSelectedColumnCount() == 0)
+ {
+ rowModel.setSelectionInterval(0, 0);
+ colModel.setSelectionInterval(0, 0);
+ return;
+ }
+
+ // If the lead selection index isn't selected (ie a remove operation
+ // happened, then set the lead to the first selected cell in the
+ // table
+ if (!table.isCellSelected(rowLead, colLead))
+ {
+ rowModel.addSelectionInterval(rowModel.getMinSelectionIndex(),
+ rowModel.getMinSelectionIndex());
+ colModel.addSelectionInterval(colModel.getMinSelectionIndex(),
+ colModel.getMinSelectionIndex());
+ return;
+ }
+
+ // multRowsSelected and multColsSelected tell us if multiple rows or
+ // columns are selected, respectively
+ boolean multRowsSelected, multColsSelected;
+ multRowsSelected = table.getSelectedRowCount() > 1 &&
+ table.getRowSelectionAllowed();
+
+ multColsSelected = table.getSelectedColumnCount() > 1 &&
+ table.getColumnSelectionAllowed();
+
+ // If there is just one selection, select the next cell, and wrap
+ // when you get to the edges of the table.
+ if (!multColsSelected && !multRowsSelected)
+ {
+ if (command.indexOf("Column") != -1)
+ advanceSingleSelection(colModel, colMax, rowModel, rowMax,
+ command.equals("selectPreviousColumnCell"));
+ else
+ advanceSingleSelection(rowModel, rowMax, colModel, colMax,
+ command.equals("selectPreviousRowCell"));
+ return;
+ }
+
+
+ // rowMinSelected and rowMaxSelected are the minimum and maximum
+ // values respectively of selected cells in the row selection model
+ // Similarly for colMinSelected and colMaxSelected.
+ int rowMaxSelected = table.getRowSelectionAllowed() ?
+ rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1;
+ int rowMinSelected = table.getRowSelectionAllowed() ?
+ rowModel.getMinSelectionIndex() : 0;
+ int colMaxSelected = table.getColumnSelectionAllowed() ?
+ colModel.getMaxSelectionIndex() :
+ table.getModel().getColumnCount() - 1;
+ int colMinSelected = table.getColumnSelectionAllowed() ?
+ colModel.getMinSelectionIndex() : 0;
+
+ // If there are multiple rows and columns selected, select the next
+ // cell and wrap at the edges of the selection.
+ if (command.indexOf("Column") != -1)
+ advanceMultipleSelection(table, colModel, colMinSelected,
+ colMaxSelected, rowModel, rowMinSelected,
+ rowMaxSelected,
+ command.equals("selectPreviousColumnCell"),
+ true);
+
+ else
+ advanceMultipleSelection(table, rowModel, rowMinSelected,
+ rowMaxSelected, colModel, colMinSelected,
+ colMaxSelected,
+ command.equals("selectPreviousRowCell"),
+ false);
+ }
+ else if (command.equals("selectNextColumn"))
+ {
+ colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
+ Math.min(colLead + 1, colMax));
+ }
+ else if (command.equals("scrollLeftExtendSelection"))
+ {
+ int target;
+ if (colLead == getFirstVisibleColumnIndex(table))
+ target = Math.max(0, colLead - (getLastVisibleColumnIndex(table)
+ - getFirstVisibleColumnIndex(table) + 1));
+ else
+ target = getFirstVisibleColumnIndex(table);
+
+ colModel.setLeadSelectionIndex(target);
+ rowModel.setLeadSelectionIndex(rowLead);
+ }
+ else if (command.equals("scrollDownChangeSelection"))
+ {
+ int target;
+ if (rowLead == getLastVisibleRowIndex(table))
+ target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table)
+ - getFirstVisibleRowIndex(table) + 1));
+ else
+ target = getLastVisibleRowIndex(table);
+
+ rowModel.setSelectionInterval(target, target);
+ colModel.setSelectionInterval(colLead, colLead);
+ }
+ else if (command.equals("scrollRightExtendSelection"))
+ {
+ int target;
+ if (colLead == getLastVisibleColumnIndex(table))
+ target = Math.min(colMax, colLead + (getLastVisibleColumnIndex(table)
+ - getFirstVisibleColumnIndex(table) + 1));
+ else
+ target = getLastVisibleColumnIndex(table);
+
+ colModel.setLeadSelectionIndex(target);
+ rowModel.setLeadSelectionIndex(rowLead);
+ }
+ else if (command.equals("selectAll"))
+ {
+ table.selectAll();
+ }
+ else if (command.equals("selectLastRowExtendSelection"))
+ {
+ rowModel.setLeadSelectionIndex(rowMax);
+ colModel.setLeadSelectionIndex(colLead);
+ }
+ else if (command.equals("scrollDownExtendSelection"))
+ {
+ int target;
+ if (rowLead == getLastVisibleRowIndex(table))
+ target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table)
+ - getFirstVisibleRowIndex(table) + 1));
+ else
+ target = getLastVisibleRowIndex(table);
+
+ rowModel.setLeadSelectionIndex(target);
+ colModel.setLeadSelectionIndex(colLead);
+ }
+ else if (command.equals("scrollUpChangeSelection"))
+ {
+ int target;
+ if (rowLead == getFirstVisibleRowIndex(table))
+ target = Math.max(0, rowLead - (getLastVisibleRowIndex(table)
+ - getFirstVisibleRowIndex(table) + 1));
+ else
+ target = getFirstVisibleRowIndex(table);
+
+ rowModel.setSelectionInterval(target, target);
+ colModel.setSelectionInterval(colLead, colLead);
+ }
+ else if (command.equals("selectNextRowChangeLead"))
+ {
+ if (rowModel.getSelectionMode()
+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ {
+ // just "selectNextRow"
+ rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
+ Math.min(rowLead + 1, rowMax));
+ colModel.setSelectionInterval(colLead, colLead);
+ }
+ else
+ rowModel.moveLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
+ }
+ else if (command.equals("selectPreviousRowChangeLead"))
+ {
+ if (rowModel.getSelectionMode()
+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ {
+ // just selectPreviousRow
+ rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
+ Math.min(rowLead - 1, 0));
+ colModel.setSelectionInterval(colLead, colLead);
+ }
+ else
+ rowModel.moveLeadSelectionIndex(Math.max(rowLead - 1, 0));
+ }
+ else if (command.equals("selectNextColumnChangeLead"))
+ {
+ if (colModel.getSelectionMode()
+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ {
+ // just selectNextColumn
+ rowModel.setSelectionInterval(rowLead, rowLead);
+ colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
+ Math.min(colLead + 1, colMax));
+ }
+ else
+ colModel.moveLeadSelectionIndex(Math.min(colLead + 1, colMax));
+ }
+ else if (command.equals("selectPreviousColumnChangeLead"))
+ {
+ if (colModel.getSelectionMode()
+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ {
+ // just selectPreviousColumn
+ rowModel.setSelectionInterval(rowLead, rowLead);
+ colModel.setSelectionInterval(Math.max(colLead - 1, 0),
+ Math.max(colLead - 1, 0));
+
+ }
+ else
+ colModel.moveLeadSelectionIndex(Math.max(colLead - 1, 0));
+ }
+ else if (command.equals("addToSelection"))
+ {
+ if (!table.isEditing())
+ {
+ int oldRowAnchor = rowModel.getAnchorSelectionIndex();
+ int oldColAnchor = colModel.getAnchorSelectionIndex();
+ rowModel.addSelectionInterval(rowLead, rowLead);
+ colModel.addSelectionInterval(colLead, colLead);
+ rowModel.setAnchorSelectionIndex(oldRowAnchor);
+ colModel.setAnchorSelectionIndex(oldColAnchor);
+ }
+ }
+ else if (command.equals("extendTo"))
+ {
+ rowModel.setSelectionInterval(rowModel.getAnchorSelectionIndex(),
+ rowLead);
+ colModel.setSelectionInterval(colModel.getAnchorSelectionIndex(),
+ colLead);
+ }
+ else if (command.equals("toggleAndAnchor"))
+ {
+ if (rowModel.isSelectedIndex(rowLead))
+ rowModel.removeSelectionInterval(rowLead, rowLead);
+ else
+ rowModel.addSelectionInterval(rowLead, rowLead);
+
+ if (colModel.isSelectedIndex(colLead))
+ colModel.removeSelectionInterval(colLead, colLead);
+ else
+ colModel.addSelectionInterval(colLead, colLead);
+
+ rowModel.setAnchorSelectionIndex(rowLead);
+ colModel.setAnchorSelectionIndex(colLead);
+ }
+ else if (command.equals("stopEditing"))
+ {
+ table.editingStopped(new ChangeEvent(command));
+ }
+ else
+ {
+ // If we're here that means we bound this TableAction class
+ // to a keyboard input but we either want to ignore that input
+ // or we just haven't implemented its action yet.
+
+ // Uncomment the following line to print the names of unused bindings
+ // when their keys are pressed
+
+ // System.out.println ("not implemented: "+e.getActionCommand());
+ }
+
+ // Any commands whose keyStrokes should be used by the Editor should not
+ // cause editing to be stopped: ie, the SPACE sends "addToSelection" but
+ // if the table is in editing mode, the space should not cause us to stop
+ // editing because it should be used by the Editor.
+ if (table.isEditing() && command != "startEditing"
+ && command != "addToSelection")
+ table.editingStopped(new ChangeEvent("update"));
+
+ table.scrollRectToVisible(table.getCellRect(
+ rowModel.getLeadSelectionIndex(), colModel.getLeadSelectionIndex(),
+ false));
+ }
+
+ /**
+ * Returns the column index of the first visible column.
+ * @return the column index of the first visible column.
+ */
+ int getFirstVisibleColumnIndex(JTable table)
+ {
+ ComponentOrientation or = table.getComponentOrientation();
+ Rectangle r = table.getVisibleRect();
+ if (!or.isLeftToRight())
+ r.translate((int) r.getWidth() - 1, 0);
+ return table.columnAtPoint(r.getLocation());
+ }
+
+ /**
+ * Returns the column index of the last visible column.
+ *
+ */
+ int getLastVisibleColumnIndex(JTable table)
+ {
+ ComponentOrientation or = table.getComponentOrientation();
+ Rectangle r = table.getVisibleRect();
+ if (or.isLeftToRight())
+ r.translate((int) r.getWidth() - 1, 0);
+ return table.columnAtPoint(r.getLocation());
+ }
+
+ /**
+ * Returns the row index of the first visible row.
+ *
+ */
+ int getFirstVisibleRowIndex(JTable table)
+ {
+ ComponentOrientation or = table.getComponentOrientation();
+ Rectangle r = table.getVisibleRect();
+ if (!or.isLeftToRight())
+ r.translate((int) r.getWidth() - 1, 0);
+ return table.rowAtPoint(r.getLocation());
+ }
+
+ /**
+ * Returns the row index of the last visible row.
+ *
+ */
+ int getLastVisibleRowIndex(JTable table)
+ {
+ ComponentOrientation or = table.getComponentOrientation();
+ Rectangle r = table.getVisibleRect();
+ r.translate(0, (int) r.getHeight() - 1);
+ if (or.isLeftToRight())
+ r.translate((int) r.getWidth() - 1, 0);
+ // The next if makes sure that we don't return -1 simply because
+ // there is white space at the bottom of the table (ie, the display
+ // area is larger than the table)
+ if (table.rowAtPoint(r.getLocation()) == -1)
+ {
+ if (getFirstVisibleRowIndex(table) == -1)
+ return -1;
+ else
+ return table.getModel().getRowCount() - 1;
+ }
+ return table.rowAtPoint(r.getLocation());
+ }
+
+ /**
+ * A helper method for the key bindings. Used because the actions
+ * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
+ *
+ * Selects the next (previous if SHIFT pressed) column for TAB, or row for
+ * ENTER from within the currently selected cells.
+ *
+ * @param firstModel the ListSelectionModel for columns (TAB) or
+ * rows (ENTER)
+ * @param firstMin the first selected index in firstModel
+ * @param firstMax the last selected index in firstModel
+ * @param secondModel the ListSelectionModel for rows (TAB) or
+ * columns (ENTER)
+ * @param secondMin the first selected index in secondModel
+ * @param secondMax the last selected index in secondModel
+ * @param reverse true if shift was held for the event
+ * @param eventIsTab true if TAB was pressed, false if ENTER pressed
+ */
+ void advanceMultipleSelection(JTable table, ListSelectionModel firstModel,
+ int firstMin,
+ int firstMax, ListSelectionModel secondModel,
+ int secondMin, int secondMax, boolean reverse,
+ boolean eventIsTab)
+ {
+ // If eventIsTab, all the "firsts" correspond to columns, otherwise, to
+ // rows "seconds" correspond to the opposite
+ int firstLead = firstModel.getLeadSelectionIndex();
+ int secondLead = secondModel.getLeadSelectionIndex();
+ int numFirsts = eventIsTab ?
+ table.getModel().getColumnCount() : table.getModel().getRowCount();
+ int numSeconds = eventIsTab ?
+ table.getModel().getRowCount() : table.getModel().getColumnCount();
+
+ // check if we have to wrap the "firsts" around, going to the other side
+ if ((firstLead == firstMax && !reverse) ||
+ (reverse && firstLead == firstMin))
+ {
+ firstModel.addSelectionInterval(reverse ? firstMax : firstMin,
+ reverse ? firstMax : firstMin);
+
+ // check if we have to wrap the "seconds"
+ if ((secondLead == secondMax && !reverse) ||
+ (reverse && secondLead == secondMin))
+ secondModel.addSelectionInterval(reverse ? secondMax : secondMin,
+ reverse ? secondMax : secondMin);
+
+ // if we're not wrapping the seconds, we have to find out where we
+ // are within the secondModel and advance to the next cell (or
+ // go back to the previous cell if reverse == true)
+ else
+ {
+ int[] secondsSelected;
+ if (eventIsTab && table.getRowSelectionAllowed() ||
+ !eventIsTab && table.getColumnSelectionAllowed())
+ secondsSelected = eventIsTab ?
+ table.getSelectedRows() : table.getSelectedColumns();
+ else
+ {
+ // if row selection is not allowed, then the entire column gets
+ // selected when you click on it, so consider ALL rows selected
+ secondsSelected = new int[numSeconds];
+ for (int i = 0; i < numSeconds; i++)
+ secondsSelected[i] = i;
+ }
+
+ // and now find the "next" index within the model
+ int secondIndex = reverse ? secondsSelected.length - 1 : 0;
+ if (!reverse)
+ while (secondsSelected[secondIndex] <= secondLead)
+ secondIndex++;
+ else
+ while (secondsSelected[secondIndex] >= secondLead)
+ secondIndex--;
+
+ // and select it - updating the lead selection index
+ secondModel.addSelectionInterval(secondsSelected[secondIndex],
+ secondsSelected[secondIndex]);
+ }
+ }
+ // We didn't have to wrap the firsts, so just find the "next" first
+ // and select it, we don't have to change "seconds"
+ else
+ {
+ int[] firstsSelected;
+ if (eventIsTab && table.getColumnSelectionAllowed() ||
+ !eventIsTab && table.getRowSelectionAllowed())
+ firstsSelected = eventIsTab ?
+ table.getSelectedColumns() : table.getSelectedRows();
+ else
+ {
+ // if selection not allowed, consider ALL firsts to be selected
+ firstsSelected = new int[numFirsts];
+ for (int i = 0; i < numFirsts; i++)
+ firstsSelected[i] = i;
+ }
+ int firstIndex = reverse ? firstsSelected.length - 1 : 0;
+ if (!reverse)
+ while (firstsSelected[firstIndex] <= firstLead)
+ firstIndex++;
+ else
+ while (firstsSelected[firstIndex] >= firstLead)
+ firstIndex--;
+ firstModel.addSelectionInterval(firstsSelected[firstIndex],
+ firstsSelected[firstIndex]);
+ secondModel.addSelectionInterval(secondLead, secondLead);
+ }
+ }
+
+ /**
+ * A helper method for the key bindings. Used because the actions
+ * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
+ *
+ * Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER)
+ * in the table, changing the current selection. All cells in the table
+ * are eligible, not just the ones that are currently selected.
+ * @param firstModel the ListSelectionModel for columns (TAB) or rows
+ * (ENTER)
+ * @param firstMax the last index in firstModel
+ * @param secondModel the ListSelectionModel for rows (TAB) or columns
+ * (ENTER)
+ * @param secondMax the last index in secondModel
+ * @param reverse true if SHIFT was pressed for the event
+ */
+
+ void advanceSingleSelection(ListSelectionModel firstModel, int firstMax,
+ ListSelectionModel secondModel, int secondMax,
+ boolean reverse)
+ {
+ // for TABs, "first" corresponds to columns and "seconds" to rows.
+ // the opposite is true for ENTERs
+ int firstLead = firstModel.getLeadSelectionIndex();
+ int secondLead = secondModel.getLeadSelectionIndex();
+
+ // if we are going backwards subtract 2 because we later add 1
+ // for a net change of -1
+ if (reverse && (firstLead == 0))
+ {
+ // check if we have to wrap around
+ if (secondLead == 0)
+ secondLead += secondMax + 1;
+ secondLead -= 2;
+ }
+
+ // do we have to wrap the "seconds"?
+ if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax))
+ secondModel.setSelectionInterval((secondLead + 1) % (secondMax + 1),
+ (secondLead + 1) % (secondMax + 1));
+ // if not, just reselect the current lead
+ else
+ secondModel.setSelectionInterval(secondLead, secondLead);
+
+ // if we are going backwards, subtract 2 because we add 1 later
+ // for net change of -1
+ if (reverse)
+ {
+ // check for wraparound
+ if (firstLead == 0)
+ firstLead += firstMax + 1;
+ firstLead -= 2;
+ }
+ // select the next "first"
+ firstModel.setSelectionInterval((firstLead + 1) % (firstMax + 1),
+ (firstLead + 1) % (firstMax + 1));
+ }
+ }
+
+ protected void installListeners()
+ {
+ if (focusListener == null)
+ focusListener = createFocusListener();
+ table.addFocusListener(focusListener);
+ if (keyListener == null)
+ keyListener = createKeyListener();
+ table.addKeyListener(keyListener);
+ if (mouseInputListener == null)
+ mouseInputListener = createMouseInputListener();
+ table.addMouseListener(mouseInputListener);
+ table.addMouseMotionListener(mouseInputListener);
+ if (propertyChangeListener == null)
+ propertyChangeListener = new PropertyChangeHandler();
+ table.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Uninstalls UI defaults that have been installed by
+ * {@link #installDefaults()}.
+ */
+ protected void uninstallDefaults()
+ {
+ // Nothing to do here for now.
+ }
+
+ /**
+ * Uninstalls the keyboard actions that have been installed by
+ * {@link #installKeyboardActions()}.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(table, JComponent.
+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ SwingUtilities.replaceUIActionMap(table, null);
+ }
+
+ protected void uninstallListeners()
+ {
+ table.removeFocusListener(focusListener);
+ table.removeKeyListener(keyListener);
+ table.removeMouseListener(mouseInputListener);
+ table.removeMouseMotionListener(mouseInputListener);
+ table.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ }
+
+ public void installUI(JComponent comp)
+ {
+ table = (JTable) comp;
+ rendererPane = new CellRendererPane();
+ table.add(rendererPane);
+
+ installDefaults();
+ installKeyboardActions();
+ installListeners();
+ }
+
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallKeyboardActions();
+ uninstallDefaults();
+
+ table.remove(rendererPane);
+ rendererPane = null;
+ table = null;
+ }
+
+ /**
+ * Paints a single cell in the table.
+ *
+ * @param g The graphics context to paint in
+ * @param row The row number to paint
+ * @param col The column number to paint
+ * @param bounds The bounds of the cell to paint, assuming a coordinate
+ * system beginning at <code>(0,0)</code> in the upper left corner of the
+ * table
+ * @param rend A cell renderer to paint with
+ */
+ void paintCell(Graphics g, int row, int col, Rectangle bounds,
+ TableCellRenderer rend)
+ {
+ Component comp = table.prepareRenderer(rend, row, col);
+ rendererPane.paintComponent(g, comp, table, bounds);
+ }
+
+ /**
+ * Paint the associated table.
+ */
+ public void paint(Graphics gfx, JComponent ignored)
+ {
+ int ncols = table.getColumnCount();
+ int nrows = table.getRowCount();
+ if (nrows == 0 || ncols == 0)
+ return;
+
+ Rectangle clip = gfx.getClipBounds();
+
+ // Determine the range of cells that are within the clip bounds.
+ Point p1 = new Point(clip.x, clip.y);
+ int c0 = table.columnAtPoint(p1);
+ if (c0 == -1)
+ c0 = 0;
+ int r0 = table.rowAtPoint(p1);
+ if (r0 == -1)
+ r0 = 0;
+ Point p2 = new Point(clip.x + clip.width, clip.y + clip.height);
+ int cn = table.columnAtPoint(p2);
+ if (cn == -1)
+ cn = table.getColumnCount() - 1;
+ int rn = table.rowAtPoint(p2);
+ if (rn == -1)
+ rn = table.getRowCount() - 1;
+
+ int columnMargin = table.getColumnModel().getColumnMargin();
+ int rowMargin = table.getRowMargin();
+
+ TableColumnModel cmodel = table.getColumnModel();
+ int[] widths = new int[cn + 1];
+ for (int i = c0; i <= cn; i++)
+ {
+ widths[i] = cmodel.getColumn(i).getWidth() - columnMargin;
+ }
+
+ Rectangle bounds = table.getCellRect(r0, c0, false);
+ // The left boundary of the area being repainted.
+ int left = bounds.x;
+
+ // The top boundary of the area being repainted.
+ int top = bounds.y;
+
+ // The bottom boundary of the area being repainted.
+ int bottom;
+
+ // paint the cell contents
+ Color grid = table.getGridColor();
+ for (int r = r0; r <= rn; ++r)
+ {
+ for (int c = c0; c <= cn; ++c)
+ {
+ bounds.width = widths[c];
+ paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c));
+ bounds.x += widths[c] + columnMargin;
+ }
+ bounds.x = left;
+ bounds.y += table.getRowHeight(r);
+ // Update row height for tables with custom heights.
+ bounds.height = table.getRowHeight(r + 1) - rowMargin;
+ }
+
+ bottom = bounds.y - rowMargin;
+
+ // paint vertical grid lines
+ if (grid != null && table.getShowVerticalLines())
+ {
+ Color save = gfx.getColor();
+ gfx.setColor(grid);
+ int x = left - columnMargin;
+ for (int c = c0; c <= cn; ++c)
+ {
+ // The vertical grid is draw right from the cells, so we
+ // add before drawing.
+ x += widths[c] + columnMargin;
+ gfx.drawLine(x, top, x, bottom);
+ }
+ gfx.setColor(save);
+ }
+
+ // paint horizontal grid lines
+ if (grid != null && table.getShowHorizontalLines())
+ {
+ Color save = gfx.getColor();
+ gfx.setColor(grid);
+ int y = top - rowMargin;
+ for (int r = r0; r <= rn; ++r)
+ {
+ // The horizontal grid is draw below the cells, so we
+ // add before drawing.
+ y += table.getRowHeight(r);
+ gfx.drawLine(left, y, p2.x, y);
+ }
+ gfx.setColor(save);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java
new file mode 100644
index 000000000..b2541b453
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java
@@ -0,0 +1,116 @@
+/* BasicTextAreaUI.java --
+ Copyright (C) 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.JTextArea;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.Element;
+import javax.swing.text.PlainView;
+import javax.swing.text.View;
+import javax.swing.text.WrappedPlainView;
+
+public class BasicTextAreaUI extends BasicTextUI
+{
+ public static ComponentUI createUI(JComponent comp)
+ {
+ return new BasicTextAreaUI();
+ }
+
+ public BasicTextAreaUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Create the view. Returns a WrappedPlainView if the text area
+ * has lineWrap set to true, otherwise returns a PlainView. If
+ * lineWrap is true has to check whether the wrap style is word
+ * or character and return an appropriate WrappedPlainView.
+ *
+ * @param elem the element to create a View for
+ * @return an appropriate View for the element
+ */
+ public View create(Element elem)
+ {
+ JTextArea comp = (JTextArea) getComponent();
+ if (comp.getLineWrap())
+ {
+ if (comp.getWrapStyleWord())
+ return new WrappedPlainView(elem, true);
+ else
+ return new WrappedPlainView(elem, false);
+ }
+ else
+ return new PlainView(elem);
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "TextArea"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "TextArea";
+ }
+
+ /**
+ * Receives notification whenever one of the text component's bound
+ * properties changes. This changes the view to WrappedPlainView
+ * if setLineWrap(true) is called, and back to PlainView if
+ * setLineWrap(false) is called.
+ *
+ * @param ev the property change event
+ */
+ protected void propertyChange(PropertyChangeEvent ev)
+ {
+ JTextArea comp = (JTextArea) getComponent();
+ if (ev.getPropertyName() == "lineWrap"
+ || ev.getPropertyName() == "wrapStyleWord")
+ {
+ // Changes the View (without modifying the document or it's listeners).
+ setView(create(textComponent.getDocument().getDefaultRootElement()));
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java
new file mode 100644
index 000000000..5f6a92757
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java
@@ -0,0 +1,118 @@
+/* BasicTextFieldUI.java
+ Copyright (C) 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.beans.PropertyChangeEvent;
+
+import javax.swing.JComponent;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.Element;
+import javax.swing.text.FieldView;
+import javax.swing.text.View;
+
+public class BasicTextFieldUI extends BasicTextUI
+{
+ public BasicTextFieldUI()
+ {
+ super();
+ }
+
+ public View create(Element elem)
+ {
+ return new FieldView(elem);
+ }
+
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicTextFieldUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "TextField"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "TextField";
+ }
+
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ }
+
+ /**
+ * Receives notification whenever one of the text component's bound
+ * properties changes. Here we check for the editable and enabled
+ * properties and adjust the background color accordingly.
+ *
+ * <p>The colors are only changed if they are not a
+ * <code>ColorUIResource</code>.</p>
+ *
+ * @param event the property change event
+ */
+ protected void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getPropertyName().equals("editable"))
+ {
+ // Changing the color only if the current background is an instance of
+ // ColorUIResource is the behavior of the RI.
+ if (textComponent.getBackground() instanceof ColorUIResource)
+ {
+ Color c = null;
+ Color old = textComponent.getBackground();
+ String prefix = getPropertyPrefix();
+ if (! textComponent.isEnabled())
+ c = SharedUIDefaults.getColor(prefix + ".disabledBackground");
+ if (c == null && ! textComponent.isEditable())
+ c = SharedUIDefaults.getColor(prefix + ".inactiveBackground");
+ if (c == null)
+ c = SharedUIDefaults.getColor(prefix + ".background");
+ if (c != null && c != old)
+ {
+ textComponent.setBackground(c);
+ }
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java
new file mode 100644
index 000000000..507e0a169
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java
@@ -0,0 +1,92 @@
+/* BasicTextPaneUI.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+
+import javax.swing.JComponent;
+import javax.swing.JTextPane;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+
+public class BasicTextPaneUI extends BasicEditorPaneUI
+{
+ public BasicTextPaneUI()
+ {
+ // Do nothing here.
+ }
+
+ public static ComponentUI createUI(JComponent comp)
+ {
+ return new BasicTextPaneUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "TextPane"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "TextPane";
+ }
+
+ /**
+ * Installs this UI on the specified <code>JTextPane</code>. This calls the
+ * super implementation and then adds a default style to the text pane.
+ *
+ * @param c the text pane to install the UI to
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ JTextPane tp = (JTextPane) c;
+ Style defaultStyle = tp.getStyle(StyleContext.DEFAULT_STYLE);
+ defaultStyle.addAttribute(StyleConstants.Foreground,
+ new ColorUIResource(Color.BLACK));
+ defaultStyle.addAttribute(StyleConstants.FontFamily, "Serif");
+ defaultStyle.addAttribute(StyleConstants.Italic, Boolean.FALSE);
+ defaultStyle.addAttribute(StyleConstants.Bold, Boolean.FALSE);
+ defaultStyle.addAttribute(StyleConstants.FontSize, new Integer(12));
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java
new file mode 100644
index 000000000..bd7cc48b9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java
@@ -0,0 +1,1538 @@
+/* BasicTextUI.java --
+ Copyright (C) 2002, 2003, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.HeadlessException;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+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.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.TransferHandler;
+import javax.swing.UIManager;
+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;
+import javax.swing.plaf.UIResource;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+import javax.swing.text.DefaultCaret;
+import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Document;
+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.Position;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * The abstract base class from which the UI classes for Swings text
+ * components are derived. This provides most of the functionality for
+ * the UI classes.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public abstract class BasicTextUI extends TextUI
+ implements ViewFactory
+{
+ /**
+ * A {@link DefaultCaret} that implements {@link UIResource}.
+ */
+ public static class BasicCaret extends DefaultCaret implements UIResource
+ {
+ public BasicCaret()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A {@link DefaultHighlighter} that implements {@link UIResource}.
+ */
+ public static class BasicHighlighter extends DefaultHighlighter
+ implements UIResource
+ {
+ public BasicHighlighter()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ private static class FocusHandler
+ implements FocusListener
+ {
+ public void focusGained(FocusEvent e)
+ {
+ // Nothing to do here.
+ }
+ public void focusLost(FocusEvent e)
+ {
+ JTextComponent textComponent = (JTextComponent) e.getComponent();
+ // Integrates Swing text components with the system clipboard:
+ // The idea is that if one wants to copy text around X11-style
+ // (select text and middle-click in the target component) the focus
+ // will move to the new component which gives the old focus owner the
+ // possibility to paste its selection into the clipboard.
+ if (!e.isTemporary()
+ && textComponent.getSelectionStart()
+ != textComponent.getSelectionEnd())
+ {
+ SecurityManager sm = System.getSecurityManager();
+ try
+ {
+ if (sm != null)
+ sm.checkSystemClipboardAccess();
+
+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
+ if (cb != null)
+ {
+ StringSelection selection = new StringSelection(
+ textComponent.getSelectedText());
+ cb.setContents(selection, selection);
+ }
+ }
+ catch (SecurityException se)
+ {
+ // Not allowed to access the clipboard: Ignore and
+ // do not access it.
+ }
+ catch (HeadlessException he)
+ {
+ // There is no AWT: Ignore and do not access the
+ // clipboard.
+ }
+ catch (IllegalStateException ise)
+ {
+ // Clipboard is currently unavaible.
+ }
+ }
+ }
+ }
+
+ /**
+ * This FocusListener triggers repaints on focus shift.
+ */
+ private static FocusListener focusListener;
+
+ /**
+ * Receives notifications when properties of the text component change.
+ */
+ private class Handler
+ implements PropertyChangeListener, DocumentListener
+ {
+ /**
+ * Notifies when a property of the text component changes.
+ *
+ * @param event the PropertyChangeEvent describing the change
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getPropertyName().equals("document"))
+ {
+ // Document changed.
+ Object oldValue = event.getOldValue();
+ if (oldValue != null)
+ {
+ Document oldDoc = (Document) oldValue;
+ oldDoc.removeDocumentListener(handler);
+ }
+ Object newValue = event.getNewValue();
+ if (newValue != null)
+ {
+ Document newDoc = (Document) newValue;
+ newDoc.addDocumentListener(handler);
+ }
+ modelChanged();
+ }
+
+ BasicTextUI.this.propertyChange(event);
+ }
+
+ /**
+ * Notification about a document change event.
+ *
+ * @param ev the DocumentEvent describing the change
+ */
+ public void changedUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.changedUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ /**
+ * Notification about a document insert event.
+ *
+ * @param ev the DocumentEvent describing the insertion
+ */
+ public void insertUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.insertUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ /**
+ * Notification about a document removal event.
+ *
+ * @param ev the DocumentEvent describing the removal
+ */
+ public void removeUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.removeUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ }
+
+ /**
+ * This view forms the root of the View hierarchy. However, it delegates
+ * most calls to another View which is the real root of the hierarchy.
+ * The purpose is to make sure that all Views in the hierarchy, including
+ * the (real) root have a well-defined parent to which they can delegate
+ * calls like {@link #preferenceChanged}, {@link #getViewFactory} and
+ * {@link #getContainer}.
+ */
+ private class RootView extends View
+ {
+ /** The real root view. */
+ private View view;
+
+ /**
+ * Creates a new RootView.
+ */
+ public RootView()
+ {
+ super(null);
+ }
+
+ /**
+ * Returns the ViewFactory for this RootView. If the current EditorKit
+ * provides a ViewFactory, this is used. Otherwise the TextUI itself
+ * is returned as a ViewFactory.
+ *
+ * @return the ViewFactory for this RootView
+ */
+ public ViewFactory getViewFactory()
+ {
+ ViewFactory factory = null;
+ EditorKit editorKit = BasicTextUI.this.getEditorKit(getComponent());
+ factory = editorKit.getViewFactory();
+ if (factory == null)
+ factory = BasicTextUI.this;
+ return factory;
+ }
+
+ /**
+ * Indicates that the preferences of one of the child view has changed.
+ * This calls revalidate on the text component.
+ *
+ * @param v the child view which's preference has changed
+ * @param width <code>true</code> if the width preference has changed
+ * @param height <code>true</code> if the height preference has changed
+ */
+ public void preferenceChanged(View v, boolean width, boolean height)
+ {
+ textComponent.revalidate();
+ }
+
+ /**
+ * Sets the real root view.
+ *
+ * @param v the root view to set
+ */
+ public void setView(View v)
+ {
+ if (view != null)
+ view.setParent(null);
+
+ if (v != null)
+ v.setParent(this);
+
+ view = v;
+ }
+
+ /**
+ * Returns the real root view, regardless of the index.
+ *
+ * @param index not used here
+ *
+ * @return the real root view, regardless of the index.
+ */
+ public View getView(int index)
+ {
+ return view;
+ }
+
+ /**
+ * Returns <code>1</code> since the RootView always contains one
+ * child, that is the real root of the View hierarchy.
+ *
+ * @return <code>1</code> since the RootView always contains one
+ * child, that is the real root of the View hierarchy
+ */
+ public int getViewCount()
+ {
+ int count = 0;
+ if (view != null)
+ count = 1;
+ return count;
+ }
+
+ /**
+ * Returns the <code>Container</code> that contains this view. This
+ * normally will be the text component that is managed by this TextUI.
+ *
+ * @return the <code>Container</code> that contains this view
+ */
+ public Container getContainer()
+ {
+ return textComponent;
+ }
+
+ /**
+ * Sets the size of the renderer. This is synchronized because that
+ * potentially triggers layout and we don't want more than one thread
+ * playing with the layout information.
+ */
+ public synchronized void setSize(float w, float h)
+ {
+ if (view != null)
+ view.setSize(w, h);
+ }
+
+ /**
+ * Paints the view. This is delegated to the real root view.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ * @param s the allocation for the View
+ */
+ public void paint(Graphics g, Shape s)
+ {
+ if (view != null)
+ {
+ Rectangle b = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
+ setSize(b.width, b.height);
+ view.paint(g, s);
+ }
+ }
+
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * This is delegated to the real root view.
+ *
+ * @param position the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param bias either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Shape modelToView(int position, Shape a, Position.Bias bias)
+ throws BadLocationException
+ {
+ return view.modelToView(position, a, bias);
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ return view.viewToModel(x, y, a, b);
+ }
+
+ /**
+ * Notification about text insertions. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ if (view != null)
+ view.insertUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Notification about text removals. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ if (view != null)
+ view.removeUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Notification about text changes. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ if (view != null)
+ view.changedUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param a the allocation for the view
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
+ int d, Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ return view.getNextVisualPositionFrom(pos, b, a, d, biasRet);
+ }
+
+ /**
+ * Returns the startOffset of this view, which is always the beginning
+ * of the document.
+ *
+ * @return the startOffset of this view
+ */
+ public int getStartOffset()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the endOffset of this view, which is always the end
+ * of the document.
+ *
+ * @return the endOffset of this view
+ */
+ public int getEndOffset()
+ {
+ return getDocument().getLength();
+ }
+
+ /**
+ * Returns the document associated with this view.
+ *
+ * @return the document associated with this view
+ */
+ public Document getDocument()
+ {
+ return textComponent.getDocument();
+ }
+
+ /**
+ * Returns the attributes, which is null for the RootView.
+ */
+ public AttributeSet getAttributes()
+ {
+ return null;
+ }
+
+ /**
+ * Overridden to forward to the view.
+ */
+ public float getPreferredSpan(int axis)
+ {
+ // The RI returns 10 in the degenerate case.
+ float span = 10;
+ if (view != null)
+ span = view.getPreferredSpan(axis);
+ return span;
+ }
+
+ /**
+ * Overridden to forward to the real view.
+ */
+ public float getMinimumSpan(int axis)
+ {
+ // The RI returns 10 in the degenerate case.
+ float span = 10;
+ if (view != null)
+ span = view.getMinimumSpan(axis);
+ return span;
+ }
+
+ /**
+ * Overridden to return Integer.MAX_VALUE.
+ */
+ public float getMaximumSpan(int axis)
+ {
+ // The RI returns Integer.MAX_VALUE here, regardless of the real view's
+ // maximum size.
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ /**
+ * The EditorKit used by this TextUI.
+ */
+ private static EditorKit kit;
+
+ /**
+ * The combined event handler for text components.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Handler handler;
+
+ /**
+ * The root view.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ RootView rootView;
+
+ /**
+ * The text component that we handle.
+ */
+ JTextComponent textComponent;
+
+ /**
+ * Creates a new <code>BasicTextUI</code> instance.
+ */
+ public BasicTextUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a {@link Caret} that should be installed into the text component.
+ *
+ * @return a caret that should be installed into the text component
+ */
+ protected Caret createCaret()
+ {
+ return new BasicCaret();
+ }
+
+ /**
+ * Creates a {@link Highlighter} that should be installed into the text
+ * component.
+ *
+ * @return a <code>Highlighter</code> for the text component
+ */
+ protected Highlighter createHighlighter()
+ {
+ return new BasicHighlighter();
+ }
+
+ /**
+ * The text component that is managed by this UI.
+ *
+ * @return the text component that is managed by this UI
+ */
+ protected final JTextComponent getComponent()
+ {
+ return textComponent;
+ }
+
+ /**
+ * Installs this UI on the text component.
+ *
+ * @param c the text component on which to install the UI
+ */
+ public void installUI(final JComponent c)
+ {
+ textComponent = (JTextComponent) c;
+
+ if (rootView == null)
+ rootView = new RootView();
+
+ installDefaults();
+ installFixedDefaults();
+
+ // These listeners must be installed outside of installListeners(),
+ // because overriding installListeners() doesn't prevent installing
+ // these in the RI, but overriding isntallUI() does.
+ if (handler == null)
+ handler = new Handler();
+ textComponent.addPropertyChangeListener(handler);
+ Document doc = textComponent.getDocument();
+ if (doc == null)
+ {
+ // The Handler takes care of installing the necessary listeners
+ // on the document here.
+ doc = getEditorKit(textComponent).createDefaultDocument();
+ textComponent.setDocument(doc);
+ }
+ else
+ {
+ // Must install the document listener.
+ doc.addDocumentListener(handler);
+ modelChanged();
+ }
+
+ installListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * Installs UI defaults on the text components.
+ */
+ protected void installDefaults()
+ {
+ String prefix = getPropertyPrefix();
+ // Install the standard properties.
+ LookAndFeel.installColorsAndFont(textComponent, prefix + ".background",
+ prefix + ".foreground", prefix + ".font");
+ LookAndFeel.installBorder(textComponent, prefix + ".border");
+
+ // Some additional text component only properties.
+ Color color = textComponent.getCaretColor();
+ if (color == null || color instanceof UIResource)
+ {
+ color = UIManager.getColor(prefix + ".caretForeground");
+ textComponent.setCaretColor(color);
+ }
+
+ // Fetch the colors for enabled/disabled text components.
+ color = textComponent.getDisabledTextColor();
+ if (color == null || color instanceof UIResource)
+ {
+ color = UIManager.getColor(prefix + ".inactiveForeground");
+ textComponent.setDisabledTextColor(color);
+ }
+ color = textComponent.getSelectedTextColor();
+ if (color == null || color instanceof UIResource)
+ {
+ color = UIManager.getColor(prefix + ".selectionForeground");
+ textComponent.setSelectedTextColor(color);
+ }
+ color = textComponent.getSelectionColor();
+ if (color == null || color instanceof UIResource)
+ {
+ color = UIManager.getColor(prefix + ".selectionBackground");
+ textComponent.setSelectionColor(color);
+ }
+
+ Insets margin = textComponent.getMargin();
+ if (margin == null || margin instanceof UIResource)
+ {
+ margin = UIManager.getInsets(prefix + ".margin");
+ textComponent.setMargin(margin);
+ }
+
+ }
+
+ /**
+ * Installs defaults that can't be overridden by overriding
+ * installDefaults().
+ */
+ private void installFixedDefaults()
+ {
+ String prefix = getPropertyPrefix();
+ Caret caret = textComponent.getCaret();
+ if (caret == null || caret instanceof UIResource)
+ {
+ caret = createCaret();
+ textComponent.setCaret(caret);
+ caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate"));
+ }
+
+ Highlighter highlighter = textComponent.getHighlighter();
+ if (highlighter == null || highlighter instanceof UIResource)
+ textComponent.setHighlighter(createHighlighter());
+
+ }
+
+ /**
+ * Install all listeners on the text component.
+ */
+ protected void installListeners()
+ {
+ //
+ if (SystemProperties.getProperty("gnu.swing.text.no-xlike-clipboard")
+ == null)
+ {
+ if (focusListener == null)
+ focusListener = new FocusHandler();
+ textComponent.addFocusListener(focusListener);
+ }
+ }
+
+ /**
+ * Returns the name of the keymap for this type of TextUI.
+ *
+ * This is implemented so that the classname of this TextUI
+ * without the package prefix is returned. This way subclasses
+ * don't have to override this method.
+ *
+ * @return the name of the keymap for this TextUI
+ */
+ protected String getKeymapName()
+ {
+ String fullClassName = getClass().getName();
+ int index = fullClassName.lastIndexOf('.');
+ String className = fullClassName.substring(index + 1);
+ return className;
+ }
+
+ /**
+ * Creates the {@link Keymap} that is installed on the text component.
+ *
+ * @return the {@link Keymap} that is installed on the text component
+ */
+ protected Keymap createKeymap()
+ {
+ String keymapName = getKeymapName();
+ Keymap keymap = JTextComponent.getKeymap(keymapName);
+ if (keymap == null)
+ {
+ Keymap parentMap =
+ JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
+ keymap = JTextComponent.addKeymap(keymapName, parentMap);
+ Object val = UIManager.get(getPropertyPrefix() + ".keyBindings");
+ if (val != null && val instanceof JTextComponent.KeyBinding[])
+ {
+ JTextComponent.KeyBinding[] bindings =
+ (JTextComponent.KeyBinding[]) val;
+ JTextComponent.loadKeymap(keymap, bindings,
+ getComponent().getActions());
+ }
+ }
+ return keymap;
+ }
+
+ /**
+ * Installs the keyboard actions on the text components.
+ */
+ protected void installKeyboardActions()
+ {
+ // This is only there for backwards compatibility.
+ textComponent.setKeymap(createKeymap());
+
+ // load any bindings for the newer InputMap / ActionMap interface
+ SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED,
+ getInputMap());
+ SwingUtilities.replaceUIActionMap(textComponent, getActionMap());
+ }
+
+ /**
+ * Creates an ActionMap to be installed on the text component.
+ *
+ * @return an ActionMap to be installed on the text component
+ */
+ private ActionMap getActionMap()
+ {
+ // Note: There are no .actionMap entries in the standard L&Fs. However,
+ // with the RI it is possible to install action maps via such keys, so
+ // we must load them too. It can be observed that when there is no
+ // .actionMap entry in the UIManager, one gets installed after a text
+ // component of that type has been loaded.
+ String prefix = getPropertyPrefix();
+ String amName = prefix + ".actionMap";
+ ActionMap am = (ActionMap) UIManager.get(amName);
+ if (am == null)
+ {
+ am = createActionMap();
+ UIManager.put(amName, am);
+ }
+
+ ActionMap map = new ActionMapUIResource();
+ map.setParent(am);
+
+ return map;
+ }
+
+ /**
+ * Creates a default ActionMap for text components that have no UI default
+ * for this (the standard for the built-in L&Fs). The ActionMap is copied
+ * from the text component's getActions() method.
+ *
+ * @returna default ActionMap
+ */
+ private ActionMap createActionMap()
+ {
+ ActionMap am = new ActionMapUIResource();
+ Action[] actions = textComponent.getActions();
+ for (int i = actions.length - 1; i >= 0; i--)
+ {
+ Action action = actions[i];
+ am.put(action.getValue(Action.NAME), action);
+ }
+ // Add TransferHandler's actions here. They don't seem to be in the
+ // JTextComponent's default actions, and I can't make up a better place
+ // to add them.
+ Action copyAction = TransferHandler.getCopyAction();
+ am.put(copyAction.getValue(Action.NAME), copyAction);
+ Action cutAction = TransferHandler.getCutAction();
+ am.put(cutAction.getValue(Action.NAME), cutAction);
+ Action pasteAction = TransferHandler.getPasteAction();
+ am.put(pasteAction.getValue(Action.NAME), pasteAction);
+
+ return am;
+ }
+
+ /**
+ * Gets the input map for the specified <code>condition</code>.
+ *
+ * @return the InputMap for the specified condition
+ */
+ private InputMap getInputMap()
+ {
+ InputMap im = new InputMapUIResource();
+ String prefix = getPropertyPrefix();
+ InputMap shared =
+ (InputMap) SharedUIDefaults.get(prefix + ".focusInputMap");
+ if (shared != null)
+ im.setParent(shared);
+ return im;
+ }
+
+ /**
+ * Uninstalls this TextUI from the text component.
+ *
+ * @param component the text component to uninstall the UI from
+ */
+ public void uninstallUI(final JComponent component)
+ {
+ textComponent.removePropertyChangeListener(handler);
+ textComponent.getDocument().removeDocumentListener(handler);
+ rootView.setView(null);
+
+ uninstallDefaults();
+ uninstallFixedDefaults();
+ uninstallListeners();
+ uninstallKeyboardActions();
+
+ textComponent = null;
+ }
+
+ /**
+ * Uninstalls all default properties that have previously been installed by
+ * this UI.
+ */
+ protected void uninstallDefaults()
+ {
+ if (textComponent.getCaretColor() instanceof UIResource)
+ textComponent.setCaretColor(null);
+ if (textComponent.getSelectionColor() instanceof UIResource)
+ textComponent.setSelectionColor(null);
+ if (textComponent.getDisabledTextColor() instanceof UIResource)
+ textComponent.setDisabledTextColor(null);
+ if (textComponent.getSelectedTextColor() instanceof UIResource)
+ textComponent.setSelectedTextColor(null);
+ LookAndFeel.uninstallBorder(textComponent);
+ if (textComponent.getMargin() instanceof UIResource)
+ textComponent.setMargin(null);
+ }
+
+ /**
+ * Uninstalls additional fixed defaults that were installed
+ * by installFixedDefaults().
+ */
+ private void uninstallFixedDefaults()
+ {
+ if (textComponent.getCaret() instanceof UIResource)
+ textComponent.setCaret(null);
+ if (textComponent.getHighlighter() instanceof UIResource)
+ textComponent.setHighlighter(null);
+ }
+
+ /**
+ * Uninstalls all listeners that have previously been installed by
+ * this UI.
+ */
+ protected void uninstallListeners()
+ {
+ // Don't nullify the focusListener field, as it is static and shared
+ // between components.
+ if (focusListener != null)
+ textComponent.removeFocusListener(focusListener);
+ }
+
+ /**
+ * Uninstalls all keyboard actions that have previously been installed by
+ * this UI.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED,
+ null);
+ SwingUtilities.replaceUIActionMap(textComponent, null);
+ }
+
+ /**
+ * Returns the property prefix by which the text component's UIDefaults
+ * are looked up.
+ *
+ * @return the property prefix by which the text component's UIDefaults
+ * are looked up
+ */
+ protected abstract String getPropertyPrefix();
+
+ /**
+ * Returns the preferred size of the text component.
+ *
+ * @param c not used here
+ *
+ * @return the preferred size of the text component
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension d = c.getSize();
+ Insets i = c.getInsets();
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ float w;
+ float h;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ if (d.width > (i.left + i.right) && d.height > (i.top + i.bottom))
+ {
+ rootView.setSize(d.width - i.left - i.right,
+ d.height - i.top - i.bottom);
+ }
+ else
+ {
+ // Not laid out yet. Force some pseudo size.
+ rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+ w = rootView.getPreferredSpan(View.X_AXIS);
+ h = rootView.getPreferredSpan(View.Y_AXIS);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ Dimension size = new Dimension((int) w + i.left + i.right,
+ (int) h + i.top + i.bottom);
+ return size;
+ }
+
+ /**
+ * Returns the maximum size for text components that use this UI.
+ *
+ * This returns (Integer.MAX_VALUE, Integer.MAX_VALUE).
+ *
+ * @param c not used here
+ *
+ * @return the maximum size for text components that use this UI
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension d = new Dimension();
+ Insets i = c.getInsets();
+ Document doc = textComponent.getDocument();
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ // Check for overflow here.
+ d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS)
+ + i.left + i.right, Integer.MAX_VALUE);
+ d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS)
+ + i.top + i.bottom, Integer.MAX_VALUE);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return d;
+ }
+
+ /**
+ * Returns the minimum size for text components. This returns the size
+ * of the component's insets.
+ *
+ * @return the minimum size for text components
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension d = new Dimension();
+ Document doc = textComponent.getDocument();
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ d.width = (int) rootView.getMinimumSpan(View.X_AXIS);
+ d.height = (int) rootView.getMinimumSpan(View.Y_AXIS);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ Insets i = c.getInsets();
+ d.width += i.left + i.right;
+ d.height += i.top + i.bottom;
+ return d;
+ }
+
+ /**
+ * Paints the text component. This acquires a read lock on the model and then
+ * calls {@link #paintSafely(Graphics)} in order to actually perform the
+ * painting.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ * @param c not used here
+ */
+ public final void paint(Graphics g, JComponent c)
+ {
+ try
+ {
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument aDoc = (AbstractDocument) doc;
+ aDoc.readLock();
+ }
+ paintSafely(g);
+ }
+ finally
+ {
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument aDoc = (AbstractDocument) doc;
+ aDoc.readUnlock();
+ }
+ }
+ }
+
+ /**
+ * This paints the text component while beeing sure that the model is not
+ * modified while painting.
+ *
+ * The following is performed in this order:
+ * <ol>
+ * <li>If the text component is opaque, the background is painted by
+ * calling {@link #paintBackground(Graphics)}.</li>
+ * <li>If there is a highlighter, the highlighter is painted.</li>
+ * <li>The view hierarchy is painted.</li>
+ * <li>The Caret is painter.</li>
+ * </ol>
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ */
+ protected void paintSafely(Graphics g)
+ {
+ Caret caret = textComponent.getCaret();
+ Highlighter highlighter = textComponent.getHighlighter();
+
+ if (textComponent.isOpaque())
+ paintBackground(g);
+
+ // Try painting with the highlighter without checking whether there
+ // is a selection because a highlighter can be used to do more than
+ // marking selected text.
+ if (highlighter != null)
+ {
+ // Handle restoring of the color here to prevent
+ // drawing problems when the Highlighter implementor
+ // forgets to restore it.
+ Color oldColor = g.getColor();
+ highlighter.paint(g);
+ g.setColor(oldColor);
+ }
+
+ rootView.paint(g, getVisibleEditorRect());
+
+ if (caret != null && textComponent.hasFocus())
+ caret.paint(g);
+ }
+
+ /**
+ * Paints the background of the text component.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ */
+ protected void paintBackground(Graphics g)
+ {
+ Color old = g.getColor();
+ g.setColor(textComponent.getBackground());
+ g.fillRect(0, 0, textComponent.getWidth(), textComponent.getHeight());
+ g.setColor(old);
+ }
+
+ /**
+ * Overridden for better control over background painting. This now simply
+ * calls {@link #paint} and this delegates the background painting to
+ * {@link #paintBackground}.
+ *
+ * @param g the graphics to use
+ * @param c the component to be painted
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ paint(g, c);
+ }
+
+ /**
+ * Marks the specified range inside the text component's model as
+ * damaged and queues a repaint request.
+ *
+ * @param t the text component
+ * @param p0 the start location inside the document model of the range that
+ * is damaged
+ * @param p1 the end location inside the document model of the range that
+ * is damaged
+ */
+ public void damageRange(JTextComponent t, int p0, int p1)
+ {
+ damageRange(t, p0, p1, Position.Bias.Forward, Position.Bias.Backward);
+ }
+
+ /**
+ * Marks the specified range inside the text component's model as
+ * damaged and queues a repaint request. This variant of this method
+ * allows a {@link Position.Bias} object to be specified for the start
+ * and end location of the range.
+ *
+ * @param t the text component
+ * @param p0 the start location inside the document model of the range that
+ * is damaged
+ * @param p1 the end location inside the document model of the range that
+ * is damaged
+ * @param firstBias the bias for the start location
+ * @param secondBias the bias for the end location
+ */
+ public void damageRange(JTextComponent t, int p0, int p1,
+ Position.Bias firstBias, Position.Bias secondBias)
+ {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc != null)
+ {
+ Document doc = t.getDocument();
+
+ // Acquire lock here to avoid structural changes in between.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ rootView.setSize(alloc.width, alloc.height);
+ Shape damage = rootView.modelToView(p0, firstBias, p1, secondBias,
+ alloc);
+ Rectangle r = damage instanceof Rectangle ? (Rectangle) damage
+ : damage.getBounds();
+ textComponent.repaint(r.x, r.y, r.width, r.height);
+ }
+ catch (BadLocationException ex)
+ {
+ // Lets ignore this as it causes no serious problems.
+ // For debugging, comment this out.
+ // ex.printStackTrace();
+ }
+ finally
+ {
+ // Release lock.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link EditorKit} used for the text component that is managed
+ * by this UI.
+ *
+ * @param t the text component
+ *
+ * @return the {@link EditorKit} used for the text component that is managed
+ * by this UI
+ */
+ public EditorKit getEditorKit(JTextComponent t)
+ {
+ if (kit == null)
+ kit = new DefaultEditorKit();
+ return kit;
+ }
+
+ /**
+ * Gets the next position inside the document model that is visible on
+ * screen, starting from <code>pos</code>.
+ *
+ * @param t the text component
+ * @param pos the start positionn
+ * @param b the bias for pos
+ * @param direction the search direction
+ * @param biasRet filled by the method to indicate the bias of the return
+ * value
+ *
+ * @return the next position inside the document model that is visible on
+ * screen
+ */
+ public int getNextVisualPositionFrom(JTextComponent t, int pos,
+ Position.Bias b, int direction,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ int offset = -1;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc != null)
+ {
+ rootView.setSize(alloc.width, alloc.height);
+ offset = rootView.getNextVisualPositionFrom(pos, b, alloc,
+ direction, biasRet);
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return offset;
+ }
+
+ /**
+ * Returns the root {@link View} of a text component.
+ *
+ * @return the root {@link View} of a text component
+ */
+ public View getRootView(JTextComponent t)
+ {
+ return rootView;
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero. A bias of {@link Position.Bias#Forward} is used in this method.
+ *
+ * @param t the text component
+ * @param pos the position of the character in the model
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Rectangle modelToView(JTextComponent t, int pos)
+ throws BadLocationException
+ {
+ return modelToView(t, pos, Position.Bias.Forward);
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * @param t the text component
+ * @param pos the position of the character in the model
+ * @param bias either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias)
+ throws BadLocationException
+ {
+ // We need to read-lock here because we depend on the document
+ // structure not beeing changed in between.
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ Rectangle rect = null;
+ try
+ {
+ Rectangle r = getVisibleEditorRect();
+ if (r != null)
+ {
+ rootView.setSize(r.width, r.height);
+ Shape s = rootView.modelToView(pos, r, bias);
+ if (s != null)
+ rect = s.getBounds();
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return rect;
+ }
+
+ /**
+ * Maps a point in the <code>View</code> coordinate space to a position
+ * inside a document model.
+ *
+ * @param t the text component
+ * @param pt the point to be mapped
+ *
+ * @return the position inside the document model that corresponds to
+ * <code>pt</code>
+ */
+ public int viewToModel(JTextComponent t, Point pt)
+ {
+ return viewToModel(t, pt, new Position.Bias[1]);
+ }
+
+ /**
+ * Maps a point in the <code>View</code> coordinate space to a position
+ * inside a document model.
+ *
+ * @param t the text component
+ * @param pt the point to be mapped
+ * @param biasReturn filled in by the method to indicate the bias of the
+ * return value
+ *
+ * @return the position inside the document model that corresponds to
+ * <code>pt</code>
+ */
+ public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn)
+ {
+ int offset = -1;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc != null)
+ {
+ rootView.setSize(alloc.width, alloc.height);
+ offset = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return offset;
+ }
+
+ /**
+ * Creates a {@link View} for the specified {@link Element}.
+ *
+ * @param elem the <code>Element</code> to create a <code>View</code> for
+ *
+ * @see ViewFactory
+ */
+ public View create(Element elem)
+ {
+ // Subclasses have to implement this to get this functionality.
+ return null;
+ }
+
+ /**
+ * Creates a {@link View} for the specified {@link Element}.
+ *
+ * @param elem the <code>Element</code> to create a <code>View</code> for
+ * @param p0 the start offset
+ * @param p1 the end offset
+ *
+ * @see ViewFactory
+ */
+ public View create(Element elem, int p0, int p1)
+ {
+ // Subclasses have to implement this to get this functionality.
+ return null;
+ }
+
+ /**
+ * A cached Insets instance to be reused below.
+ */
+ private Insets cachedInsets;
+
+ /**
+ * Returns the allocation to give the root view.
+ *
+ * @return the allocation to give the root view
+ *
+ * @specnote The allocation has nothing to do with visibility. According
+ * to the specs the naming of this method is unfortunate and
+ * has historical reasons
+ */
+ protected Rectangle getVisibleEditorRect()
+ {
+ int width = textComponent.getWidth();
+ int height = textComponent.getHeight();
+
+ // Return null if the component has no valid size.
+ if (width <= 0 || height <= 0)
+ return null;
+
+ Insets insets = textComponent.getInsets(cachedInsets);
+ return new Rectangle(insets.left, insets.top,
+ width - insets.left - insets.right,
+ height - insets.top - insets.bottom);
+ }
+
+ /**
+ * Sets the root view for the text component.
+ *
+ * @param view the <code>View</code> to be set as root view
+ */
+ protected final void setView(View view)
+ {
+ rootView.setView(view);
+ textComponent.revalidate();
+ textComponent.repaint();
+ }
+
+ /**
+ * Indicates that the model of a text component has changed. This
+ * triggers a rebuild of the view hierarchy.
+ */
+ protected void modelChanged()
+ {
+ if (textComponent == null || rootView == null)
+ return;
+ ViewFactory factory = rootView.getViewFactory();
+ if (factory == null)
+ return;
+ Document doc = textComponent.getDocument();
+ if (doc == null)
+ return;
+ Element elem = doc.getDefaultRootElement();
+ if (elem == null)
+ return;
+ View view = factory.create(elem);
+ setView(view);
+ }
+
+ /**
+ * Receives notification whenever one of the text component's bound
+ * properties changes. This default implementation does nothing.
+ * It is a hook that enables subclasses to react to property changes
+ * on the text component.
+ *
+ * @param ev the property change event
+ */
+ protected void propertyChange(PropertyChangeEvent ev)
+ {
+ // The default implementation does nothing.
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java
new file mode 100644
index 000000000..4550f08e6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java
@@ -0,0 +1,134 @@
+/* BasicToggleButtonUI.java
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Font;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.AbstractButton;
+import javax.swing.JComponent;
+import javax.swing.JToggleButton;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate for the {@link JToggleButton} component.
+ */
+public class BasicToggleButtonUI extends BasicButtonUI
+{
+
+ /**
+ * Returns a UI delegate for the specified component.
+ *
+ * @param component the component (should be an instance of
+ * {@link JToggleButton}).
+ *
+ * @return An instance of <code>BasicToggleButtonUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new BasicToggleButtonUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIManager} defaults table
+ * (<code>"ToggleButton."</code> in this case).
+ *
+ * @return <code>"ToggleButton."</code>
+ */
+ protected String getPropertyPrefix()
+ {
+ return "ToggleButton.";
+ }
+
+ /**
+ * Paint the component, which is an {@link AbstractButton}, according to
+ * its current state.
+ *
+ * @param g The graphics context to paint with
+ * @param c The component to paint the state of
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+
+ Rectangle tr = new Rectangle();
+ Rectangle ir = new Rectangle();
+ Rectangle vr = new Rectangle();
+
+ Font f = c.getFont();
+
+ g.setFont(f);
+
+ if (b.isBorderPainted())
+ SwingUtilities.calculateInnerArea(b, vr);
+ else
+ vr = SwingUtilities.getLocalBounds(b);
+ String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
+ b.getText(), currentIcon(b), b.getVerticalAlignment(),
+ b.getHorizontalAlignment(), b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(), vr, ir, tr, b.getIconTextGap()
+ + defaultTextShiftOffset);
+
+ if ((b.getModel().isArmed() && b.getModel().isPressed())
+ || b.isSelected())
+ paintButtonPressed(g, b);
+
+ paintIcon(g, b, ir);
+ if (text != null)
+ paintText(g, b, tr, text);
+ if (b.isFocusOwner() && b.isFocusPainted())
+ paintFocus(g, b, vr, tr, ir);
+ }
+
+ /**
+ * Paints the icon for the toggle button. This delegates to
+ * {@link BasicButtonUI#paintIcon(Graphics, JComponent, Rectangle)}.
+ *
+ * @param g the graphics context
+ * @param b the button to paint the icon for
+ * @param iconRect the area allocated for the icon
+ */
+ protected void paintIcon(Graphics g, AbstractButton b, Rectangle iconRect)
+ {
+ super.paintIcon(g, b, iconRect);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java
new file mode 100644
index 000000000..79cf0b0c2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java
@@ -0,0 +1,124 @@
+/* BasicToolBarSeparatorUI.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Dimension;
+import java.awt.Graphics;
+
+import javax.swing.JComponent;
+import javax.swing.JSeparator;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * The Basic Look and Feel UI delegate for Separator.
+ */
+public class BasicToolBarSeparatorUI extends BasicSeparatorUI
+{
+ private transient Dimension size;
+
+ /**
+ * Creates a new UI delegate for the given JComponent.
+ *
+ * @param c The JComponent to create a delegate for.
+ *
+ * @return A new BasicToolBarSeparatorUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicToolBarSeparatorUI();
+ }
+
+ /**
+ * This method installs the defaults that are given by the Basic L&F.
+ *
+ * @param s The Separator that is being installed.
+ */
+ protected void installDefaults(JSeparator s)
+ {
+ size = UIManager.getDimension("ToolBar.separatorSize");
+ }
+
+ /**
+ * This method does nothing as a Separator is just blank space.
+ *
+ * @param g The Graphics object to paint with
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method returns the preferred size of the JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return size;
+ }
+
+ /**
+ * This method returns the minimum size of the JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return size;
+ }
+
+ /**
+ * This method returns the maximum size of the JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return size;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java
new file mode 100644
index 000000000..f5b2b2d1e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java
@@ -0,0 +1,1610 @@
+/* BasicToolBarUI.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Hashtable;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractButton;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JToolBar;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.RootPaneContainer;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ToolBarUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicBorders.ButtonBorder;
+
+/**
+ * This is the Basic Look and Feel UI class for JToolBar.
+ */
+public class BasicToolBarUI extends ToolBarUI implements SwingConstants
+{
+
+ /**
+ * Implements the keyboard actions for JToolBar.
+ */
+ static class ToolBarAction
+ extends AbstractAction
+ {
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ Object cmd = getValue("__command__");
+ JToolBar toolBar = (JToolBar) event.getSource();
+ BasicToolBarUI ui = (BasicToolBarUI) toolBar.getUI();
+
+ if (cmd.equals("navigateRight"))
+ ui.navigateFocusedComp(EAST);
+ else if (cmd.equals("navigateLeft"))
+ ui.navigateFocusedComp(WEST);
+ else if (cmd.equals("navigateUp"))
+ ui.navigateFocusedComp(NORTH);
+ else if (cmd.equals("navigateDown"))
+ ui.navigateFocusedComp(SOUTH);
+ else
+ assert false : "Shouldn't reach here";
+ }
+ }
+
+ /** Static owner of all DragWindows.
+ * This is package-private to avoid an accessor method. */
+ static JFrame owner = new JFrame();
+
+ /** The border used when the JToolBar is in nonrollover mode. */
+ private static Border nonRolloverBorder;
+
+ /** The border used when the JToolBar is in rollover mode. */
+ private static Border rolloverBorder;
+
+ /** The last known BorderLayout constraint before floating. */
+ protected String constraintBeforeFloating;
+
+ /** The last known orientation of the JToolBar before floating.
+ * This is package-private to avoid an accessor method. */
+ int lastGoodOrientation;
+
+ /** The color of the border when it is dockable. */
+ protected Color dockingBorderColor;
+
+ /** The background color of the JToolBar when it is dockable. */
+ protected Color dockingColor;
+
+ /** The docking listener responsible for mouse events on the JToolBar. */
+ protected MouseInputListener dockingListener;
+
+ /** The window used for dragging the JToolBar. */
+ protected BasicToolBarUI.DragWindow dragWindow;
+
+ /** The color of the border when it is not dockable. */
+ protected Color floatingBorderColor;
+
+ /** The background color of the JToolBar when it is not dockable. */
+ protected Color floatingColor;
+
+ /** The index of the focused component. */
+ protected int focusedCompIndex;
+
+ /** The PropertyChangeListener for the JToolBar. */
+ protected PropertyChangeListener propertyListener;
+
+ /** The JToolBar this UI delegate is responsible for. */
+ protected JToolBar toolBar;
+
+ /** The Container listener for the JToolBar. */
+ protected ContainerListener toolBarContListener;
+
+ /** The Focus listener for the JToolBar. */
+ protected FocusListener toolBarFocusListener;
+
+ /**
+ * @deprecated since JDK1.3.
+ */
+ protected KeyStroke leftKey;
+
+ /**
+ * @deprecated since JDK1.3.
+ */
+ protected KeyStroke rightKey;
+
+ /**
+ * @deprecated since JDK1.3.
+ */
+ protected KeyStroke upKey;
+
+ /**
+ * @deprecated since JDK1.3.
+ */
+ protected KeyStroke downKey;
+
+ /**
+ * The floating window that is responsible for holding the JToolBar when it
+ * is dragged outside of its original parent.
+ */
+ private transient Window floatFrame;
+
+ /** The original parent of the JToolBar.
+ * This is package-private to avoid an accessor method. */
+ transient Container origParent;
+
+ /** A hashtable of components and their original borders.
+ * This is package-private to avoid an accessor method. */
+ transient Hashtable borders;
+
+ /** A window listener for the floatable frame. */
+ private transient WindowListener windowListener;
+
+ /** A set of cached bounds of the JToolBar.
+ * This is package-private to avoid an accessor method. */
+ transient Dimension cachedBounds;
+
+ /** The cached orientation of the JToolBar.
+ * This is package-private to avoid an accessor method. */
+ transient int cachedOrientation;
+
+ /**
+ * This method creates a new <code>BasicToolBarUI</code> object for the given JToolBar.
+ */
+ public BasicToolBarUI()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * This method returns whether the JToolBar can dock at the given position.
+ *
+ * @param c The component to try to dock in.
+ * @param p The position of the mouse cursor relative to the given
+ * component.
+ *
+ * @return Whether the JToolBar can dock.
+ */
+ public boolean canDock(Component c, Point p)
+ {
+ return areaOfClick(c, p) != -1;
+ }
+
+ /**
+ * This helper method returns the position of the JToolBar if it can dock.
+ *
+ * @param c The component to try to dock in.
+ * @param p The position of the mouse cursor relative to the given
+ * component.
+ *
+ * @return One of the SwingConstants directions or -1 if the JToolBar can't
+ * dock.
+ */
+ private int areaOfClick(Component c, Point p)
+ {
+ // Has to dock in immediate parent, not eventual root container.
+ Rectangle pBounds = c.getBounds();
+
+ // XXX: In Sun's implementation, the space the toolbar has to dock is dependent on the size it had last.
+ Dimension d = toolBar.getSize();
+ int limit = Math.min(d.width, d.height);
+
+ // The order of checking is 1. top 2. bottom 3. left 4. right
+ if (! pBounds.contains(p))
+ return -1;
+
+ if (p.y < limit)
+ return SwingConstants.NORTH;
+
+ if (p.y > (pBounds.height - limit))
+ return SwingConstants.SOUTH;
+
+ if (p.x < limit)
+ return SwingConstants.WEST;
+
+ if (p.x > (pBounds.width - limit))
+ return SwingConstants.EAST;
+
+ return -1;
+ }
+
+ /**
+ * This method creates a new DockingListener for the JToolBar.
+ *
+ * @return A new DockingListener for the JToolBar.
+ */
+ protected MouseInputListener createDockingListener()
+ {
+ return new DockingListener(toolBar);
+ }
+
+ /**
+ * This method creates a new DragWindow for the given JToolBar.
+ *
+ * @param toolbar The JToolBar to create a DragWindow for.
+ *
+ * @return A new DragWindow.
+ */
+ protected BasicToolBarUI.DragWindow createDragWindow(JToolBar toolbar)
+ {
+ return new DragWindow();
+ }
+
+ /**
+ * This method creates a new floating frame for the JToolBar. By default,
+ * this UI uses createFloatingWindow instead. This method of creating a
+ * floating frame is deprecated.
+ *
+ * @param toolbar The JToolBar to create a floating frame for.
+ *
+ * @return A new floating frame.
+ */
+ protected JFrame createFloatingFrame(JToolBar toolbar)
+ {
+ // FIXME: Though deprecated, this should still work.
+ return null;
+ }
+
+ /**
+ * This method creates a new floating window for the JToolBar. This is the
+ * method used by default to create a floating container for the JToolBar.
+ *
+ * @param toolbar The JToolBar to create a floating window for.
+ *
+ * @return A new floating window.
+ */
+ protected RootPaneContainer createFloatingWindow(JToolBar toolbar)
+ {
+ // This one is used by default though.
+ return new ToolBarDialog();
+ }
+
+ /**
+ * This method creates a new WindowListener for the JToolBar.
+ *
+ * @return A new WindowListener.
+ */
+ protected WindowListener createFrameListener()
+ {
+ return new FrameListener();
+ }
+
+ /**
+ * This method creates a new nonRolloverBorder for JButtons when the
+ * JToolBar's rollover property is set to false.
+ *
+ * @return A new NonRolloverBorder.
+ */
+ protected Border createNonRolloverBorder()
+ {
+ Border b = UIManager.getBorder("ToolBar.nonrolloverBorder");
+
+ if (b == null)
+ {
+ b = new CompoundBorder(
+ new ButtonBorder(UIManager.getColor("Button.shadow"),
+ UIManager.getColor("Button.darkShadow"),
+ UIManager.getColor("Button.light"),
+ UIManager.getColor("Button.highlight")),
+ BasicBorders.getMarginBorder());
+ }
+
+ return b; }
+
+ /**
+ * This method creates a new PropertyChangeListener for the JToolBar.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyListener()
+ {
+ return new PropertyListener();
+ }
+
+ /**
+ * This method creates a new rollover border for JButtons when the
+ * JToolBar's rollover property is set to true.
+ *
+ * @return A new rollover border.
+ */
+ protected Border createRolloverBorder()
+ {
+ Border b = UIManager.getBorder("ToolBar.rolloverBorder");
+
+ if (b == null)
+ {
+ b = new CompoundBorder(
+ new ButtonBorder(UIManager.getColor("Button.shadow"),
+ UIManager.getColor("Button.darkShadow"),
+ UIManager.getColor("Button.light"),
+ UIManager.getColor("Button.highlight")),
+ BasicBorders.getMarginBorder());
+ }
+
+ return b;
+ }
+
+ /**
+ * This method creates a new Container listener for the JToolBar.
+ *
+ * @return A new Container listener.
+ */
+ protected ContainerListener createToolBarContListener()
+ {
+ return new ToolBarContListener();
+ }
+
+ /**
+ * This method creates a new FocusListener for the JToolBar.
+ *
+ * @return A new FocusListener for the JToolBar.
+ */
+ protected FocusListener createToolBarFocusListener()
+ {
+ return new ToolBarFocusListener();
+ }
+
+ /**
+ * This method creates a new UI delegate for the given JComponent.
+ *
+ * @param c The JComponent to create a UI delegate for.
+ *
+ * @return A new UI delegate.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicToolBarUI();
+ }
+
+ /**
+ * This method is called to drag the DragWindow around when the JToolBar is
+ * being dragged around.
+ *
+ * @param position The mouse cursor coordinates relative to the JToolBar.
+ * @param origin The screen position of the JToolBar.
+ */
+ protected void dragTo(Point position, Point origin)
+ {
+ int loc = areaOfClick(origParent,
+ SwingUtilities.convertPoint(toolBar, position,
+ origParent));
+
+ if (loc != -1)
+ {
+ dragWindow.setBorderColor(dockingBorderColor);
+ dragWindow.setBackground(dockingColor);
+ }
+ else
+ {
+ dragWindow.setBorderColor(floatingBorderColor);
+ dragWindow.setBackground(floatingColor);
+ }
+
+ int w = 0;
+ int h = 0;
+
+ boolean tmp = (loc == SwingConstants.NORTH)
+ || (loc == SwingConstants.SOUTH) || (loc == -1);
+
+ cachedOrientation = toolBar.getOrientation();
+ cachedBounds = toolBar.getSize();
+ if (((cachedOrientation == SwingConstants.HORIZONTAL) && tmp)
+ || ((cachedOrientation == VERTICAL) && ! tmp))
+ {
+ w = cachedBounds.width;
+ h = cachedBounds.height;
+ }
+ else
+ {
+ w = cachedBounds.height;
+ h = cachedBounds.width;
+ }
+
+ Point p = dragWindow.getOffset();
+ Insets insets = toolBar.getInsets();
+
+ dragWindow.setBounds((origin.x + position.x) - p.x
+ - ((insets.left + insets.right) / 2),
+ (origin.y + position.y) - p.y
+ - ((insets.top + insets.bottom) / 2), w, h);
+
+ if (! dragWindow.isVisible())
+ dragWindow.show();
+ }
+
+ /**
+ * This method is used at the end of a drag session to place the frame in
+ * either its original parent as a docked JToolBar or in its floating
+ * frame.
+ *
+ * @param position The position of the mouse cursor relative to the
+ * JToolBar.
+ * @param origin The screen position of the JToolBar before the drag session
+ * started.
+ */
+ protected void floatAt(Point position, Point origin)
+ {
+ Point p = new Point(position);
+ int aoc = areaOfClick(origParent,
+ SwingUtilities.convertPoint(toolBar, p, origParent));
+
+ Container oldParent = toolBar.getParent();
+
+ oldParent.remove(toolBar);
+ oldParent.doLayout();
+ oldParent.repaint();
+
+ Container newParent;
+
+ if (aoc == -1)
+ newParent = ((RootPaneContainer) floatFrame).getContentPane();
+ else
+ {
+ floatFrame.hide();
+ newParent = origParent;
+ }
+
+ String constraint;
+ switch (aoc)
+ {
+ case SwingConstants.EAST:
+ constraint = BorderLayout.EAST;
+ break;
+ case SwingConstants.NORTH:
+ constraint = BorderLayout.NORTH;
+ break;
+ case SwingConstants.SOUTH:
+ constraint = BorderLayout.SOUTH;
+ break;
+ case SwingConstants.WEST:
+ constraint = BorderLayout.WEST;
+ break;
+ default:
+ constraint = BorderLayout.CENTER;
+ break;
+ }
+
+ int newOrientation = SwingConstants.HORIZONTAL;
+ if ((aoc != -1)
+ && ((aoc == SwingConstants.EAST) || (aoc == SwingConstants.WEST)))
+ newOrientation = SwingConstants.VERTICAL;
+
+ if (aoc != -1)
+ {
+ constraintBeforeFloating = constraint;
+ lastGoodOrientation = newOrientation;
+ }
+
+ newParent.add(toolBar, constraint);
+
+ setFloating(aoc == -1, null);
+ toolBar.setOrientation(newOrientation);
+
+ Insets insets = floatFrame.getInsets();
+ Dimension dims = toolBar.getPreferredSize();
+ p = dragWindow.getOffset();
+ setFloatingLocation((position.x + origin.x) - p.x
+ - ((insets.left + insets.right) / 2),
+ (position.y + origin.y) - p.y
+ - ((insets.top + insets.bottom) / 2));
+
+ if (aoc == -1)
+ {
+ floatFrame.pack();
+ floatFrame.setSize(dims.width + insets.left + insets.right,
+ dims.height + insets.top + insets.bottom);
+ floatFrame.show();
+ }
+
+ newParent.invalidate();
+ newParent.validate();
+ newParent.repaint();
+ }
+
+ /**
+ * This method returns the docking color.
+ *
+ * @return The docking color.
+ */
+ public Color getDockingColor()
+ {
+ return dockingColor;
+ }
+
+ /**
+ * This method returns the Color which is displayed when over a floating
+ * area.
+ *
+ * @return The color which is displayed when over a floating area.
+ */
+ public Color getFloatingColor()
+ {
+ return floatingColor;
+ }
+
+ /**
+ * This method returns the maximum size of the given JComponent for this UI.
+ *
+ * @param c The JComponent to find the maximum size for.
+ *
+ * @return The maximum size for this UI.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the minimum size of the given JComponent for this UI.
+ *
+ * @param c The JComponent to find a minimum size for.
+ *
+ * @return The minimum size for this UI.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method installs the needed components for the JToolBar.
+ */
+ protected void installComponents()
+ {
+ floatFrame = (Window) createFloatingWindow(toolBar);
+
+ dragWindow = createDragWindow(toolBar);
+
+ nonRolloverBorder = createNonRolloverBorder();
+ rolloverBorder = createRolloverBorder();
+
+ borders = new Hashtable();
+ setRolloverBorders(toolBar.isRollover());
+
+ fillHashtable();
+ }
+
+ /**
+ * This method installs the defaults as specified by the look and feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installBorder(toolBar, "ToolBar.border");
+ LookAndFeel.installColorsAndFont(toolBar, "ToolBar.background",
+ "ToolBar.foreground", "ToolBar.font");
+
+ dockingBorderColor = UIManager.getColor("ToolBar.dockingForeground");
+ dockingColor = UIManager.getColor("ToolBar.dockingBackground");
+
+ floatingBorderColor = UIManager.getColor("ToolBar.floatingForeground");
+ floatingColor = UIManager.getColor("ToolBar.floatingBackground");
+ }
+
+ /**
+ * This method installs the keyboard actions for the JToolBar as specified
+ * by the look and feel.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install the input map.
+ InputMap inputMap =
+ (InputMap) SharedUIDefaults.get("ToolBar.ancestorInputMap");
+ SwingUtilities.replaceUIInputMap(toolBar,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ inputMap);
+
+ // FIXME: The JDK uses a LazyActionMap for parentActionMap
+ SwingUtilities.replaceUIActionMap(toolBar, getActionMap());
+ }
+
+ /**
+ * Fetches the action map from the UI defaults, or create a new one
+ * if the action map hasn't been initialized.
+ *
+ * @return the action map
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("ToolBar.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("ToolBar.actionMap", am);
+ }
+ return am;
+ }
+
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new ToolBarAction();
+
+ am.put("navigateLeft", action);
+ am.put("navigateRight", action);
+ am.put("navigateUp", action);
+ am.put("navigateDown", action);
+
+ return am;
+ }
+
+ /**
+ * This method installs listeners for the JToolBar.
+ */
+ protected void installListeners()
+ {
+ dockingListener = createDockingListener();
+ toolBar.addMouseListener(dockingListener);
+ toolBar.addMouseMotionListener(dockingListener);
+
+ propertyListener = createPropertyListener();
+ toolBar.addPropertyChangeListener(propertyListener);
+
+ toolBarContListener = createToolBarContListener();
+ toolBar.addContainerListener(toolBarContListener);
+
+ windowListener = createFrameListener();
+ floatFrame.addWindowListener(windowListener);
+
+ toolBarFocusListener = createToolBarFocusListener();
+ if (toolBarFocusListener != null)
+ {
+ int count = toolBar.getComponentCount();
+ for (int i = 0; i < count; i++)
+ toolBar.getComponent(i).addFocusListener(toolBarFocusListener);
+ }
+ }
+
+ /**
+ * This method installs non rollover borders for each component inside the
+ * given JComponent.
+ *
+ * @param c The JComponent whose children need to have non rollover borders
+ * installed.
+ */
+ protected void installNonRolloverBorders(JComponent c)
+ {
+ Component[] components = toolBar.getComponents();
+
+ for (int i = 0; i < components.length; i++)
+ setBorderToNonRollover(components[i]);
+ }
+
+ /**
+ * This method installs normal (or their original) borders for each
+ * component inside the given JComponent.
+ *
+ * @param c The JComponent whose children need to have their original
+ * borders installed.
+ */
+ protected void installNormalBorders(JComponent c)
+ {
+ Component[] components = toolBar.getComponents();
+
+ for (int i = 0; i < components.length; i++)
+ setBorderToNormal(components[i]);
+ }
+
+ /**
+ * This method install rollover borders for each component inside the given
+ * JComponent.
+ *
+ * @param c The JComponent whose children need to have rollover borders
+ * installed.
+ */
+ protected void installRolloverBorders(JComponent c)
+ {
+ Component[] components = toolBar.getComponents();
+
+ for (int i = 0; i < components.length; i++)
+ setBorderToRollover(components[i]);
+ }
+
+ /**
+ * This method fills the borders hashtable with a list of components that
+ * are JButtons and their borders.
+ */
+ private void fillHashtable()
+ {
+ Component[] c = toolBar.getComponents();
+
+ for (int i = 0; i < c.length; i++)
+ {
+ if (c[i] instanceof JButton)
+ {
+ // Don't really care about anything other than JButtons
+ JButton b = (JButton) c[i];
+
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
+ }
+ }
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install a UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ if (c instanceof JToolBar)
+ {
+ toolBar = (JToolBar) c;
+ installDefaults();
+ installComponents();
+ installListeners();
+ installKeyboardActions();
+ }
+ }
+
+ /**
+ * This method returns whether the JToolBar is floating.
+ *
+ * @return Whether the JToolBar is floating.
+ */
+ public boolean isFloating()
+ {
+ return floatFrame.isVisible();
+ }
+
+ /**
+ * This method returns whether rollover borders have been set.
+ *
+ * @return Whether rollover borders have been set.
+ */
+ public boolean isRolloverBorders()
+ {
+ return toolBar.isRollover();
+ }
+
+ /**
+ * This method navigates in the given direction giving focus to the next
+ * component in the given direction.
+ *
+ * @param direction The direction to give focus to.
+ */
+ protected void navigateFocusedComp(int direction)
+ {
+ int count = toolBar.getComponentCount();
+ switch (direction)
+ {
+ case EAST:
+ case SOUTH:
+ if (focusedCompIndex >= 0 && focusedCompIndex < count)
+ {
+ int i = focusedCompIndex + 1;
+ boolean focusRequested = false;
+ // Find component to focus and request focus on it.
+ while (i != focusedCompIndex && ! focusRequested)
+ {
+ if (i >= count)
+ i = 0;
+ Component comp = toolBar.getComponentAtIndex(i++);
+ if (comp != null && comp.isFocusable()
+ && comp.isEnabled())
+ {
+ comp.requestFocus();
+ focusRequested = true;
+ }
+ }
+ }
+ break;
+ case WEST:
+ case NORTH:
+ if (focusedCompIndex >= 0 && focusedCompIndex < count)
+ {
+ int i = focusedCompIndex - 1;
+ boolean focusRequested = false;
+ // Find component to focus and request focus on it.
+ while (i != focusedCompIndex && ! focusRequested)
+ {
+ if (i < 0)
+ i = count - 1;
+ Component comp = toolBar.getComponentAtIndex(i--);
+ if (comp != null && comp.isFocusable()
+ && comp.isEnabled())
+ {
+ comp.requestFocus();
+ focusRequested = true;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * This method sets the border of the given component to a non rollover
+ * border.
+ *
+ * @param c The Component whose border needs to be set.
+ */
+ protected void setBorderToNonRollover(Component c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ b.setRolloverEnabled(false);
+
+ // Save old border in hashtable.
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
+
+ b.setBorder(nonRolloverBorder);
+ }
+ }
+
+ /**
+ * This method sets the border of the given component to its original value.
+ *
+ * @param c The Component whose border needs to be set.
+ */
+ protected void setBorderToNormal(Component c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ b.setRolloverEnabled(true);
+ b.setBorder((Border) borders.remove(b));
+ }
+ }
+
+ /**
+ * This method sets the border of the given component to a rollover border.
+ *
+ * @param c The Component whose border needs to be set.
+ */
+ protected void setBorderToRollover(Component c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ b.setRolloverEnabled(false);
+
+ // Save old border in hashtable.
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
+
+ b.setBorder(rolloverBorder);
+ }
+ }
+
+ /**
+ * This method sets the docking color.
+ *
+ * @param c The docking color.
+ */
+ public void setDockingColor(Color c)
+ {
+ dockingColor = c;
+ }
+
+ /**
+ * This method sets the floating property for the JToolBar.
+ *
+ * @param b Whether the JToolBar is floating.
+ * @param p FIXME
+ */
+ public void setFloating(boolean b, Point p)
+ {
+ // FIXME: use p for something. It's not location
+ // since we already have setFloatingLocation.
+ floatFrame.setVisible(b);
+ }
+
+ /**
+ * This method sets the color displayed when the JToolBar is not in a
+ * dockable area.
+ *
+ * @param c The floating color.
+ */
+ public void setFloatingColor(Color c)
+ {
+ floatingColor = c;
+ }
+
+ /**
+ * This method sets the floating location of the JToolBar.
+ *
+ * @param x The x coordinate for the floating frame.
+ * @param y The y coordinate for the floating frame.
+ */
+ public void setFloatingLocation(int x, int y)
+ {
+ // x,y are the coordinates of the new JFrame created to store the toolbar
+ // XXX: The floating location is bogus is not floating.
+ floatFrame.setLocation(x, y);
+ floatFrame.invalidate();
+ floatFrame.validate();
+ floatFrame.repaint();
+ }
+
+ /**
+ * This is a convenience method for changing the orientation of the
+ * JToolBar.
+ *
+ * @param orientation The new orientation.
+ */
+ public void setOrientation(int orientation)
+ {
+ toolBar.setOrientation(orientation);
+ }
+
+ /**
+ * This method changes the child components to have rollover borders if the
+ * given parameter is true. Otherwise, the components are set to have non
+ * rollover borders.
+ *
+ * @param rollover Whether the children will have rollover borders.
+ */
+ public void setRolloverBorders(boolean rollover)
+ {
+ if (rollover)
+ installRolloverBorders(toolBar);
+ else
+ installNonRolloverBorders(toolBar);
+ }
+
+ /**
+ * This method uninstall UI installed components from the JToolBar.
+ */
+ protected void uninstallComponents()
+ {
+ installNormalBorders(toolBar);
+ borders = null;
+ cachedBounds = null;
+
+ floatFrame = null;
+ dragWindow = null;
+ }
+
+ /**
+ * This method removes the defaults installed by the Look and Feel.
+ */
+ protected void uninstallDefaults()
+ {
+ toolBar.setBackground(null);
+ toolBar.setForeground(null);
+ toolBar.setFont(null);
+
+ dockingBorderColor = null;
+ dockingColor = null;
+ floatingBorderColor = null;
+ floatingColor = null;
+ }
+
+ /**
+ * This method uninstalls keyboard actions installed by the UI.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(toolBar, JComponent.
+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ SwingUtilities.replaceUIActionMap(toolBar, null);
+ }
+
+ /**
+ * This method uninstalls listeners installed by the UI.
+ */
+ protected void uninstallListeners()
+ {
+ if (toolBarFocusListener != null)
+ {
+ int count = toolBar.getComponentCount();
+ for (int i = 0; i < count; i++)
+ toolBar.getComponent(i).removeFocusListener(toolBarFocusListener);
+ toolBarFocusListener = null;
+ }
+
+ floatFrame.removeWindowListener(windowListener);
+ windowListener = null;
+
+ toolBar.removeContainerListener(toolBarContListener);
+ toolBarContListener = null;
+
+ toolBar.removeMouseMotionListener(dockingListener);
+ toolBar.removeMouseListener(dockingListener);
+ dockingListener = null;
+ }
+
+ /**
+ * This method uninstalls the UI.
+ *
+ * @param c The JComponent that is having this UI removed.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallComponents();
+ uninstallDefaults();
+ toolBar = null;
+ }
+
+ /**
+ * This is the MouseHandler class that allows the user to drag the JToolBar
+ * in and out of the parent and dock it if it can.
+ */
+ public class DockingListener implements MouseInputListener
+ {
+ /** Whether the JToolBar is being dragged. */
+ protected boolean isDragging;
+
+ /**
+ * The origin point. This point is saved from the beginning press and is
+ * used until the end of the drag session.
+ */
+ protected Point origin;
+
+ /** The JToolBar being dragged. */
+ protected JToolBar toolBar;
+
+ /**
+ * Creates a new DockingListener object.
+ *
+ * @param t The JToolBar this DockingListener is being used for.
+ */
+ public DockingListener(JToolBar t)
+ {
+ toolBar = t;
+ }
+
+ /**
+ * This method is called when the mouse is clicked.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the mouse is dragged. It delegates the drag
+ * painting to the dragTo method.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ if (isDragging)
+ dragTo(e.getPoint(), origin);
+ }
+
+ /**
+ * This method is called when the mouse enters the JToolBar.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the mouse exits the JToolBar.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the mouse is moved in the JToolBar.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the mouse is pressed in the JToolBar. If the
+ * press doesn't occur in a place where it causes the JToolBar to be
+ * dragged, it returns. Otherwise, it starts a drag session.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (! toolBar.isFloatable())
+ return;
+
+ Point ssd = e.getPoint();
+ Insets insets = toolBar.getInsets();
+
+ // Verify that this click occurs in the top inset.
+ if (toolBar.getOrientation() == SwingConstants.HORIZONTAL)
+ {
+ if (e.getX() > insets.left)
+ return;
+ }
+ else
+ {
+ if (e.getY() > insets.top)
+ return;
+ }
+
+ origin = new Point(0, 0);
+ if (toolBar.isShowing())
+ SwingUtilities.convertPointToScreen(ssd, toolBar);
+
+ if (! (SwingUtilities.getAncestorOfClass(Window.class, toolBar) instanceof UIResource))
+ // Need to know who keeps the toolBar if it gets dragged back into it.
+ origParent = toolBar.getParent();
+
+ if (toolBar.isShowing())
+ SwingUtilities.convertPointToScreen(origin, toolBar);
+
+ isDragging = true;
+
+ if (dragWindow != null)
+ dragWindow.setOffset(new Point(cachedBounds.width / 2,
+ cachedBounds.height / 2));
+
+ dragTo(e.getPoint(), origin);
+ }
+
+ /**
+ * This method is called when the mouse is released from the JToolBar.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (! isDragging || ! toolBar.isFloatable())
+ return;
+
+ isDragging = false;
+ floatAt(e.getPoint(), origin);
+ dragWindow.hide();
+ }
+ }
+
+ /**
+ * This is the window that appears when the JToolBar is being dragged
+ * around.
+ */
+ protected class DragWindow extends Window
+ {
+ /**
+ * The current border color. It changes depending on whether the JToolBar
+ * is over a place that allows it to dock.
+ */
+ private Color borderColor;
+
+ /** The between the mouse and the top left corner of the window. */
+ private Point offset;
+
+ /**
+ * Creates a new DragWindow object.
+ * This is package-private to avoid an accessor method.
+ */
+ DragWindow()
+ {
+ super(owner);
+ }
+
+ /**
+ * The color that the border should be.
+ *
+ * @return The border color.
+ */
+ public Color getBorderColor()
+ {
+ if (borderColor == null)
+ return Color.BLACK;
+
+ return borderColor;
+ }
+
+ /**
+ * This method returns the insets for the DragWindow.
+ *
+ * @return The insets for the DragWindow.
+ */
+ public Insets getInsets()
+ {
+ // This window has no decorations, so insets are empty.
+ return new Insets(0, 0, 0, 0);
+ }
+
+ /**
+ * This method returns the mouse offset from the top left corner of the
+ * DragWindow.
+ *
+ * @return The mouse offset.
+ */
+ public Point getOffset()
+ {
+ return offset;
+ }
+
+ /**
+ * This method paints the DragWindow.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ // No visiting children necessary.
+ Color saved = g.getColor();
+ Rectangle b = getBounds();
+
+ g.setColor(getBorderColor());
+ g.drawRect(0, 0, b.width - 1, b.height - 1);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method changes the border color.
+ *
+ * @param c The new border color.
+ */
+ public void setBorderColor(Color c)
+ {
+ borderColor = c;
+ }
+
+ /**
+ * This method changes the mouse offset.
+ *
+ * @param p The new mouse offset.
+ */
+ public void setOffset(Point p)
+ {
+ offset = p;
+ }
+
+ /**
+ * Sets the orientation of the toolbar and the
+ * drag window.
+ *
+ * @param o - the new orientation of the toolbar and drag
+ * window.
+ */
+ public void setOrientation(int o)
+ {
+ toolBar.setOrientation(o);
+ if (dragWindow != null)
+ dragWindow.setOrientation(o);
+ }
+ }
+
+ /**
+ * This helper class listens for Window events from the floatable window and
+ * if it is closed, returns the JToolBar to the last known good location.
+ */
+ protected class FrameListener extends WindowAdapter
+ {
+ /**
+ * This method is called when the floating window is closed.
+ *
+ * @param e The WindowEvent.
+ */
+ public void windowClosing(WindowEvent e)
+ {
+ Container parent = toolBar.getParent();
+ parent.remove(toolBar);
+
+ if (origParent != null)
+ {
+ origParent.add(toolBar,
+ (constraintBeforeFloating != null)
+ ? constraintBeforeFloating : BorderLayout.NORTH);
+ toolBar.setOrientation(lastGoodOrientation);
+ }
+
+ origParent.invalidate();
+ origParent.validate();
+ origParent.repaint();
+ }
+ }
+
+ /**
+ * This helper class listens for PropertyChangeEvents from the JToolBar.
+ */
+ protected class PropertyListener implements PropertyChangeListener
+ {
+ /**
+ * This method is called when a property from the JToolBar is changed.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // FIXME: need name properties so can change floatFrame title.
+ if (e.getPropertyName().equals("rollover") && toolBar != null)
+ setRolloverBorders(toolBar.isRollover());
+ }
+ }
+
+ /**
+ * This helper class listens for components added to and removed from the
+ * JToolBar.
+ */
+ protected class ToolBarContListener implements ContainerListener
+ {
+ /**
+ * This method is responsible for setting rollover or non rollover for new
+ * buttons added to the JToolBar.
+ *
+ * @param e The ContainerEvent.
+ */
+ public void componentAdded(ContainerEvent e)
+ {
+ if (e.getChild() instanceof JButton)
+ {
+ JButton b = (JButton) e.getChild();
+
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
+ }
+
+ if (isRolloverBorders())
+ setBorderToRollover(e.getChild());
+ else
+ setBorderToNonRollover(e.getChild());
+
+ cachedBounds = toolBar.getPreferredSize();
+ cachedOrientation = toolBar.getOrientation();
+
+ Component c = e.getChild();
+ if (toolBarFocusListener != null)
+ c.addFocusListener(toolBarFocusListener);
+ }
+
+ /**
+ * This method is responsible for giving the child components their
+ * original borders when they are removed.
+ *
+ * @param e The ContainerEvent.
+ */
+ public void componentRemoved(ContainerEvent e)
+ {
+ setBorderToNormal(e.getChild());
+ cachedBounds = toolBar.getPreferredSize();
+ cachedOrientation = toolBar.getOrientation();
+
+ Component c = e.getChild();
+ if (toolBarFocusListener != null)
+ c.removeFocusListener(toolBarFocusListener);
+ }
+ }
+
+ /**
+ * This is the floating window that is returned when getFloatingWindow is
+ * called.
+ */
+ private class ToolBarDialog extends JDialog implements UIResource
+ {
+ /**
+ * Creates a new ToolBarDialog object with the name given by the JToolBar.
+ */
+ public ToolBarDialog()
+ {
+ super();
+ setName((toolBar.getName() != null) ? toolBar.getName() : "");
+ }
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ protected class ToolBarFocusListener implements FocusListener
+ {
+ /**
+ * Creates a new ToolBarFocusListener object.
+ */
+ protected ToolBarFocusListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification when the toolbar or one of it's component
+ * receives the keyboard input focus.
+ *
+ * @param e the focus event
+ */
+ public void focusGained(FocusEvent e)
+ {
+ Component c = e.getComponent();
+ focusedCompIndex = toolBar.getComponentIndex(c);
+ }
+
+ /**
+ * Receives notification when the toolbar or one of it's component
+ * looses the keyboard input focus.
+ *
+ * @param e the focus event
+ */
+ public void focusLost(FocusEvent e)
+ {
+ // Do nothing here.
+ }
+ }
+
+ /**
+ * This helper class acts as the border for the JToolBar.
+ */
+ private static class ToolBarBorder implements Border
+ {
+ /** The size of the larger, draggable side of the border. */
+ private static final int offset = 10;
+
+ /** The other sides. */
+ private static final int regular = 2;
+
+ /**
+ * This method returns the border insets for the JToolBar.
+ *
+ * @param c The Component to find insets for.
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ if (c instanceof JToolBar)
+ {
+ JToolBar tb = (JToolBar) c;
+ int orientation = tb.getOrientation();
+
+ if (! tb.isFloatable())
+ return new Insets(regular, regular, regular, regular);
+ else if (orientation == SwingConstants.HORIZONTAL)
+ return new Insets(regular, offset, regular, regular);
+ else
+ return new Insets(offset, regular, regular, regular);
+ }
+
+ return new Insets(0, 0, 0, 0);
+ }
+
+ /**
+ * This method returns whether the border is opaque.
+ *
+ * @return Whether the border is opaque.
+ */
+ public boolean isBorderOpaque()
+ {
+ return false;
+ }
+
+ /**
+ * This method paints the ribbed area of the border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate of the area.
+ * @param y The y coordinate of the area.
+ * @param w The width of the area.
+ * @param h The height of the area.
+ * @param size The size of the bump.
+ * @param c The color of the bumps.
+ */
+ private void paintBumps(Graphics g, int x, int y, int w, int h, int size,
+ Color c)
+ {
+ Color saved = g.getColor();
+ g.setColor(c);
+
+ int hgap = 2 * size;
+ int vgap = 4 * size;
+ int count = 0;
+
+ for (int i = x; i < (w + x); i += hgap)
+ for (int j = ((count++ % 2) == 0) ? y : (y + (2 * size)); j < (h + y);
+ j += vgap)
+ g.fillRect(i, j, size, size);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the border around the given Component.
+ *
+ * @param c The Component whose border is being painted.
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate of the component.
+ * @param y The y coordinate of the component.
+ * @param width The width of the component.
+ * @param height The height of the component.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ if (c instanceof JToolBar)
+ {
+ JToolBar tb = (JToolBar) c;
+
+ int orientation = tb.getOrientation();
+
+ if (orientation == SwingConstants.HORIZONTAL)
+ {
+ paintBumps(g, x, y, offset, height, 1, Color.WHITE);
+ paintBumps(g, x + 1, y + 1, offset - 1, height - 1, 1, Color.GRAY);
+ }
+ else
+ {
+ paintBumps(g, x, y, width, offset, 1, Color.WHITE);
+ paintBumps(g, x + 1, y + 1, width - 1, offset - 1, 1, Color.GRAY);
+ }
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java
new file mode 100644
index 000000000..37c084bb8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java
@@ -0,0 +1,292 @@
+/* BasicToolTipUI.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JComponent;
+import javax.swing.JToolTip;
+import javax.swing.LookAndFeel;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ToolTipUI;
+import javax.swing.text.View;
+
+/**
+ * This is the Basic Look and Feel UI class for JToolTip.
+ */
+public class BasicToolTipUI extends ToolTipUI
+{
+
+ /**
+ * Receives notification when a property of the JToolTip changes.
+ * This updates the HTML renderer if appropriate.
+ */
+ private class PropertyChangeHandler
+ implements PropertyChangeListener
+ {
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String prop = e.getPropertyName();
+ if (prop.equals("tiptext") || prop.equals("font")
+ || prop.equals("foreground"))
+ {
+ JToolTip tip = (JToolTip) e.getSource();
+ String text = tip.getTipText();
+ BasicHTML.updateRenderer(tip, text);
+ }
+ }
+
+ }
+
+ /** The shared instance of BasicToolTipUI used for all ToolTips. */
+ private static BasicToolTipUI shared;
+
+ /** The tooltip's text */
+ private String text;
+
+ /**
+ * Handles property changes.
+ */
+ private PropertyChangeListener propertyChangeHandler;
+
+ /**
+ * Creates a new BasicToolTipUI object.
+ */
+ public BasicToolTipUI()
+ {
+ super();
+ }
+
+ /**
+ * This method creates a new BasicToolTip UI for the given
+ * JComponent.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A BasicToolTipUI that can be used by the given JComponent.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ if (shared == null)
+ shared = new BasicToolTipUI();
+ return shared;
+ }
+
+ /**
+ * This method returns the msximum size of the given JComponent.
+ *
+ * @param c The JComponent to find a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension d = getPreferredSize(c);
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ d.width += view.getMaximumSpan(View.X_AXIS)
+ - view.getPreferredSpan(View.X_AXIS);
+ return d;
+ }
+
+ /**
+ * This method returns the minimum size of the given JComponent.
+ *
+ * @param c The JComponent to find a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension d = getPreferredSize(c);
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ d.width -= view.getPreferredSpan(View.X_AXIS)
+ - view.getMinimumSpan(View.X_AXIS);
+ return d;
+ }
+
+ /**
+ * This method returns the preferred size of the given JComponent.
+ *
+ * @param c The JComponent to find a preferred size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ JToolTip tip = (JToolTip) c;
+ String str = tip.getTipText();
+ FontMetrics fm = c.getFontMetrics(c.getFont());
+ Insets i = c.getInsets();
+ Dimension d = new Dimension(i.left + i.right, i.top + i.bottom);
+ if (str != null && ! str.equals(""))
+ {
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ d.width += (int) view.getPreferredSpan(View.X_AXIS);
+ d.height += (int) view.getPreferredSpan(View.Y_AXIS);
+ }
+ else
+ {
+ d.width += fm.stringWidth(str) + 6;
+ d.height += fm.getHeight();
+ }
+ }
+ return d;
+ }
+
+ /**
+ * This method installs the defaults for the given JComponent.
+ *
+ * @param c The JComponent to install defaults for.
+ */
+ protected void installDefaults(JComponent c)
+ {
+ LookAndFeel.installColorsAndFont(c, "ToolTip.background",
+ "ToolTip.foreground", "ToolTip.font");
+ LookAndFeel.installBorder(c, "ToolTip.border");
+ }
+
+ /**
+ * This method installs the listeners for the given JComponent.
+ *
+ * @param c The JComponent to install listeners for.
+ */
+ protected void installListeners(JComponent c)
+ {
+ propertyChangeHandler = new PropertyChangeHandler();
+ c.addPropertyChangeListener(propertyChangeHandler);
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install the UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ c.setOpaque(true);
+ installDefaults(c);
+ BasicHTML.updateRenderer(c, ((JToolTip) c).getTipText());
+ installListeners(c);
+ }
+
+ /**
+ * This method paints the given JComponent with the given Graphics object.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ JToolTip tip = (JToolTip) c;
+
+ String text = tip.getTipText();
+ Font font = c.getFont();
+ FontMetrics fm = c.getFontMetrics(font);
+ int ascent = fm.getAscent();
+ Insets i = c.getInsets();
+ Dimension size = c.getSize();
+ Rectangle paintR = new Rectangle(i.left, i.top,
+ size.width - i.left - i.right,
+ size.height - i.top - i.bottom);
+ Color saved = g.getColor();
+ Font oldFont = g.getFont();
+ g.setColor(Color.BLACK);
+
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ view.paint(g, paintR);
+ else
+ g.drawString(text, paintR.x + 3, paintR.y + ascent);
+
+ g.setFont(oldFont);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method uninstalls the defaults for the given JComponent.
+ *
+ * @param c The JComponent to uninstall defaults for.
+ */
+ protected void uninstallDefaults(JComponent c)
+ {
+ c.setForeground(null);
+ c.setBackground(null);
+ c.setFont(null);
+ c.setBorder(null);
+ }
+
+ /**
+ * This method uninstalls listeners for the given JComponent.
+ *
+ * @param c The JComponent to uninstall listeners for.
+ */
+ protected void uninstallListeners(JComponent c)
+ {
+ if (propertyChangeHandler != null)
+ {
+ c.removePropertyChangeListener(propertyChangeHandler);
+ propertyChangeHandler = null;
+ }
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallDefaults(c);
+ BasicHTML.updateRenderer(c, "");
+ uninstallListeners(c);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java
new file mode 100644
index 000000000..af61a422a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java
@@ -0,0 +1,3939 @@
+/* BasicTreeUI.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 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 gnu.javax.swing.tree.GnuPath;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Label;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.CellRendererPane;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.MouseInputListener;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeExpansionListener;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TreeUI;
+import javax.swing.tree.AbstractLayoutCache;
+import javax.swing.tree.DefaultTreeCellEditor;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreeCellEditor;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+import javax.swing.tree.VariableHeightLayoutCache;
+
+/**
+ * A delegate providing the user interface for <code>JTree</code> according to
+ * the Basic look and feel.
+ *
+ * @see javax.swing.JTree
+ * @author Lillian Angel (langel@redhat.com)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+public class BasicTreeUI
+ extends TreeUI
+{
+ /**
+ * The tree cell editing may be started by the single mouse click on the
+ * selected cell. To separate it from the double mouse click, the editing
+ * session starts after this time (in ms) after that single click, and only no
+ * other clicks were performed during that time.
+ */
+ static int WAIT_TILL_EDITING = 900;
+
+ /** Collapse Icon for the tree. */
+ protected transient Icon collapsedIcon;
+
+ /** Expanded Icon for the tree. */
+ protected transient Icon expandedIcon;
+
+ /** Distance between left margin and where vertical dashes will be drawn. */
+ protected int leftChildIndent;
+
+ /**
+ * Distance between leftChildIndent and where cell contents will be drawn.
+ */
+ protected int rightChildIndent;
+
+ /**
+ * Total fistance that will be indented. The sum of leftChildIndent and
+ * rightChildIndent .
+ */
+ protected int totalChildIndent;
+
+ /** Index of the row that was last selected. */
+ protected int lastSelectedRow;
+
+ /** Component that we're going to be drawing onto. */
+ protected JTree tree;
+
+ /** Renderer that is being used to do the actual cell drawing. */
+ protected transient TreeCellRenderer currentCellRenderer;
+
+ /**
+ * Set to true if the renderer that is currently in the tree was created by
+ * this instance.
+ */
+ protected boolean createdRenderer;
+
+ /** Editor for the tree. */
+ protected transient TreeCellEditor cellEditor;
+
+ /**
+ * Set to true if editor that is currently in the tree was created by this
+ * instance.
+ */
+ protected boolean createdCellEditor;
+
+ /**
+ * Set to false when editing and shouldSelectCall() returns true meaning the
+ * node should be selected before editing, used in completeEditing.
+ * GNU Classpath editing is implemented differently, so this value is not
+ * actually read anywhere. However it is always set correctly to maintain
+ * interoperability with the derived classes that read this field.
+ */
+ protected boolean stopEditingInCompleteEditing;
+
+ /** Used to paint the TreeCellRenderer. */
+ protected CellRendererPane rendererPane;
+
+ /** Size needed to completely display all the nodes. */
+ protected Dimension preferredSize;
+
+ /** Minimum size needed to completely display all the nodes. */
+ protected Dimension preferredMinSize;
+
+ /** Is the preferredSize valid? */
+ protected boolean validCachedPreferredSize;
+
+ /** Object responsible for handling sizing and expanded issues. */
+ protected AbstractLayoutCache treeState;
+
+ /** Used for minimizing the drawing of vertical lines. */
+ protected Hashtable<TreePath, Boolean> drawingCache;
+
+ /**
+ * True if doing optimizations for a largeModel. Subclasses that don't support
+ * this may wish to override createLayoutCache to not return a
+ * FixedHeightLayoutCache instance.
+ */
+ protected boolean largeModel;
+
+ /** Responsible for telling the TreeState the size needed for a node. */
+ protected AbstractLayoutCache.NodeDimensions nodeDimensions;
+
+ /** Used to determine what to display. */
+ protected TreeModel treeModel;
+
+ /** Model maintaining the selection. */
+ protected TreeSelectionModel treeSelectionModel;
+
+ /**
+ * How much the depth should be offset to properly calculate x locations. This
+ * is based on whether or not the root is visible, and if the root handles are
+ * visible.
+ */
+ protected int depthOffset;
+
+ /**
+ * When editing, this will be the Component that is doing the actual editing.
+ */
+ protected Component editingComponent;
+
+ /** Path that is being edited. */
+ protected TreePath editingPath;
+
+ /**
+ * Row that is being edited. Should only be referenced if editingComponent is
+ * null.
+ */
+ protected int editingRow;
+
+ /** Set to true if the editor has a different size than the renderer. */
+ protected boolean editorHasDifferentSize;
+
+ /** Boolean to keep track of editing. */
+ boolean isEditing;
+
+ /** The current path of the visible nodes in the tree. */
+ TreePath currentVisiblePath;
+
+ /** The gap between the icon and text. */
+ int gap = 4;
+
+ /** The max height of the nodes in the tree. */
+ int maxHeight;
+
+ /** The hash color. */
+ Color hashColor;
+
+ /** Listeners */
+ PropertyChangeListener propertyChangeListener;
+
+ FocusListener focusListener;
+
+ TreeSelectionListener treeSelectionListener;
+
+ MouseListener mouseListener;
+
+ KeyListener keyListener;
+
+ PropertyChangeListener selectionModelPropertyChangeListener;
+
+ ComponentListener componentListener;
+
+ CellEditorListener cellEditorListener;
+
+ TreeExpansionListener treeExpansionListener;
+
+ TreeModelListener treeModelListener;
+
+ /**
+ * The zero size icon, used for expand controls, if they are not visible.
+ */
+ static Icon nullIcon;
+
+ /**
+ * Creates a new BasicTreeUI object.
+ */
+ public BasicTreeUI()
+ {
+ validCachedPreferredSize = false;
+ drawingCache = new Hashtable();
+ nodeDimensions = createNodeDimensions();
+ configureLayoutCache();
+
+ editingRow = - 1;
+ lastSelectedRow = - 1;
+ }
+
+ /**
+ * Returns an instance of the UI delegate for the specified component.
+ *
+ * @param c the <code>JComponent</code> for which we need a UI delegate for.
+ * @return the <code>ComponentUI</code> for c.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicTreeUI();
+ }
+
+ /**
+ * Returns the Hash color.
+ *
+ * @return the <code>Color</code> of the Hash.
+ */
+ protected Color getHashColor()
+ {
+ return hashColor;
+ }
+
+ /**
+ * Sets the Hash color.
+ *
+ * @param color the <code>Color</code> to set the Hash to.
+ */
+ protected void setHashColor(Color color)
+ {
+ hashColor = color;
+ }
+
+ /**
+ * Sets the left child's indent value.
+ *
+ * @param newAmount is the new indent value for the left child.
+ */
+ public void setLeftChildIndent(int newAmount)
+ {
+ leftChildIndent = newAmount;
+ }
+
+ /**
+ * Returns the indent value for the left child.
+ *
+ * @return the indent value for the left child.
+ */
+ public int getLeftChildIndent()
+ {
+ return leftChildIndent;
+ }
+
+ /**
+ * Sets the right child's indent value.
+ *
+ * @param newAmount is the new indent value for the right child.
+ */
+ public void setRightChildIndent(int newAmount)
+ {
+ rightChildIndent = newAmount;
+ }
+
+ /**
+ * Returns the indent value for the right child.
+ *
+ * @return the indent value for the right child.
+ */
+ public int getRightChildIndent()
+ {
+ return rightChildIndent;
+ }
+
+ /**
+ * Sets the expanded icon.
+ *
+ * @param newG is the new expanded icon.
+ */
+ public void setExpandedIcon(Icon newG)
+ {
+ expandedIcon = newG;
+ }
+
+ /**
+ * Returns the current expanded icon.
+ *
+ * @return the current expanded icon.
+ */
+ public Icon getExpandedIcon()
+ {
+ return expandedIcon;
+ }
+
+ /**
+ * Sets the collapsed icon.
+ *
+ * @param newG is the new collapsed icon.
+ */
+ public void setCollapsedIcon(Icon newG)
+ {
+ collapsedIcon = newG;
+ }
+
+ /**
+ * Returns the current collapsed icon.
+ *
+ * @return the current collapsed icon.
+ */
+ public Icon getCollapsedIcon()
+ {
+ return collapsedIcon;
+ }
+
+ /**
+ * Updates the componentListener, if necessary.
+ *
+ * @param largeModel sets this.largeModel to it.
+ */
+ protected void setLargeModel(boolean largeModel)
+ {
+ if (largeModel != this.largeModel)
+ {
+ completeEditing();
+ tree.removeComponentListener(componentListener);
+ this.largeModel = largeModel;
+ tree.addComponentListener(componentListener);
+ }
+ }
+
+ /**
+ * Returns true if largeModel is set
+ *
+ * @return true if largeModel is set, otherwise false.
+ */
+ protected boolean isLargeModel()
+ {
+ return largeModel;
+ }
+
+ /**
+ * Sets the row height.
+ *
+ * @param rowHeight is the height to set this.rowHeight to.
+ */
+ protected void setRowHeight(int rowHeight)
+ {
+ completeEditing();
+ if (rowHeight == 0)
+ rowHeight = getMaxHeight(tree);
+ treeState.setRowHeight(rowHeight);
+ }
+
+ /**
+ * Returns the current row height.
+ *
+ * @return current row height.
+ */
+ protected int getRowHeight()
+ {
+ return tree.getRowHeight();
+ }
+
+ /**
+ * Sets the TreeCellRenderer to <code>tcr</code>. This invokes
+ * <code>updateRenderer</code>.
+ *
+ * @param tcr is the new TreeCellRenderer.
+ */
+ protected void setCellRenderer(TreeCellRenderer tcr)
+ {
+ // Finish editing before changing the renderer.
+ completeEditing();
+
+ // The renderer is set in updateRenderer.
+ updateRenderer();
+
+ // Refresh the layout if necessary.
+ if (treeState != null)
+ {
+ treeState.invalidateSizes();
+ updateSize();
+ }
+ }
+
+ /**
+ * Return currentCellRenderer, which will either be the trees renderer, or
+ * defaultCellRenderer, which ever was not null.
+ *
+ * @return the current Cell Renderer
+ */
+ protected TreeCellRenderer getCellRenderer()
+ {
+ if (currentCellRenderer != null)
+ return currentCellRenderer;
+
+ return createDefaultCellRenderer();
+ }
+
+ /**
+ * Sets the tree's model.
+ *
+ * @param model to set the treeModel to.
+ */
+ protected void setModel(TreeModel model)
+ {
+ completeEditing();
+
+ if (treeModel != null && treeModelListener != null)
+ treeModel.removeTreeModelListener(treeModelListener);
+
+ treeModel = tree.getModel();
+
+ if (treeModel != null && treeModelListener != null)
+ treeModel.addTreeModelListener(treeModelListener);
+
+ if (treeState != null)
+ {
+ treeState.setModel(treeModel);
+ updateLayoutCacheExpandedNodes();
+ updateSize();
+ }
+ }
+
+ /**
+ * Returns the tree's model
+ *
+ * @return treeModel
+ */
+ protected TreeModel getModel()
+ {
+ return treeModel;
+ }
+
+ /**
+ * Sets the root to being visible.
+ *
+ * @param newValue sets the visibility of the root
+ */
+ protected void setRootVisible(boolean newValue)
+ {
+ completeEditing();
+ tree.setRootVisible(newValue);
+ }
+
+ /**
+ * Returns true if the root is visible.
+ *
+ * @return true if the root is visible.
+ */
+ protected boolean isRootVisible()
+ {
+ return tree.isRootVisible();
+ }
+
+ /**
+ * Determines whether the node handles are to be displayed.
+ *
+ * @param newValue sets whether or not node handles should be displayed.
+ */
+ protected void setShowsRootHandles(boolean newValue)
+ {
+ completeEditing();
+ updateDepthOffset();
+ if (treeState != null)
+ {
+ treeState.invalidateSizes();
+ updateSize();
+ }
+ }
+
+ /**
+ * Returns true if the node handles are to be displayed.
+ *
+ * @return true if the node handles are to be displayed.
+ */
+ protected boolean getShowsRootHandles()
+ {
+ return tree.getShowsRootHandles();
+ }
+
+ /**
+ * Sets the cell editor.
+ *
+ * @param editor to set the cellEditor to.
+ */
+ protected void setCellEditor(TreeCellEditor editor)
+ {
+ updateCellEditor();
+ }
+
+ /**
+ * Returns the <code>TreeCellEditor</code> for this tree.
+ *
+ * @return the cellEditor for this tree.
+ */
+ protected TreeCellEditor getCellEditor()
+ {
+ return cellEditor;
+ }
+
+ /**
+ * Configures the receiver to allow, or not allow, editing.
+ *
+ * @param newValue sets the receiver to allow editing if true.
+ */
+ protected void setEditable(boolean newValue)
+ {
+ updateCellEditor();
+ }
+
+ /**
+ * Returns true if the receiver allows editing.
+ *
+ * @return true if the receiver allows editing.
+ */
+ protected boolean isEditable()
+ {
+ return tree.isEditable();
+ }
+
+ /**
+ * Resets the selection model. The appropriate listeners are installed on the
+ * model.
+ *
+ * @param newLSM resets the selection model.
+ */
+ protected void setSelectionModel(TreeSelectionModel newLSM)
+ {
+ completeEditing();
+ if (newLSM != null)
+ {
+ treeSelectionModel = newLSM;
+ tree.setSelectionModel(treeSelectionModel);
+ }
+ }
+
+ /**
+ * Returns the current selection model.
+ *
+ * @return the current selection model.
+ */
+ protected TreeSelectionModel getSelectionModel()
+ {
+ return treeSelectionModel;
+ }
+
+ /**
+ * Returns the Rectangle enclosing the label portion that the last item in
+ * path will be drawn to. Will return null if any component in path is
+ * currently valid.
+ *
+ * @param tree is the current tree the path will be drawn to.
+ * @param path is the current path the tree to draw to.
+ * @return the Rectangle enclosing the label portion that the last item in the
+ * path will be drawn to.
+ */
+ public Rectangle getPathBounds(JTree tree, TreePath path)
+ {
+ Rectangle bounds = null;
+ if (tree != null && treeState != null)
+ {
+ bounds = treeState.getBounds(path, null);
+ Insets i = tree.getInsets();
+ if (bounds != null && i != null)
+ {
+ bounds.x += i.left;
+ bounds.y += i.top;
+ }
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the max height of all the nodes in the tree.
+ *
+ * @param tree - the current tree
+ * @return the max height.
+ */
+ int getMaxHeight(JTree tree)
+ {
+ if (maxHeight != 0)
+ return maxHeight;
+
+ Icon e = UIManager.getIcon("Tree.openIcon");
+ Icon c = UIManager.getIcon("Tree.closedIcon");
+ Icon l = UIManager.getIcon("Tree.leafIcon");
+ int rc = getRowCount(tree);
+ int iconHeight = 0;
+
+ for (int row = 0; row < rc; row++)
+ {
+ if (isLeaf(row))
+ iconHeight = l.getIconHeight();
+ else if (tree.isExpanded(row))
+ iconHeight = e.getIconHeight();
+ else
+ iconHeight = c.getIconHeight();
+
+ maxHeight = Math.max(maxHeight, iconHeight + gap);
+ }
+
+ treeState.setRowHeight(maxHeight);
+ return maxHeight;
+ }
+
+ /**
+ * Get the tree node icon.
+ */
+ Icon getNodeIcon(TreePath path)
+ {
+ Object node = path.getLastPathComponent();
+ if (treeModel.isLeaf(node))
+ return UIManager.getIcon("Tree.leafIcon");
+ else if (treeState.getExpandedState(path))
+ return UIManager.getIcon("Tree.openIcon");
+ else
+ return UIManager.getIcon("Tree.closedIcon");
+ }
+
+ /**
+ * Returns the path for passed in row. If row is not visible null is returned.
+ *
+ * @param tree is the current tree to return path for.
+ * @param row is the row number of the row to return.
+ * @return the path for passed in row. If row is not visible null is returned.
+ */
+ public TreePath getPathForRow(JTree tree, int row)
+ {
+ return treeState.getPathForRow(row);
+ }
+
+ /**
+ * Returns the row that the last item identified in path is visible at. Will
+ * return -1 if any of the elments in the path are not currently visible.
+ *
+ * @param tree is the current tree to return the row for.
+ * @param path is the path used to find the row.
+ * @return the row that the last item identified in path is visible at. Will
+ * return -1 if any of the elments in the path are not currently
+ * visible.
+ */
+ public int getRowForPath(JTree tree, TreePath path)
+ {
+ return treeState.getRowForPath(path);
+ }
+
+ /**
+ * Returns the number of rows that are being displayed.
+ *
+ * @param tree is the current tree to return the number of rows for.
+ * @return the number of rows being displayed.
+ */
+ public int getRowCount(JTree tree)
+ {
+ return treeState.getRowCount();
+ }
+
+ /**
+ * Returns the path to the node that is closest to x,y. If there is nothing
+ * currently visible this will return null, otherwise it'll always return a
+ * valid path. If you need to test if the returned object is exactly at x,y
+ * you should get the bounds for the returned path and test x,y against that.
+ *
+ * @param tree the tree to search for the closest path
+ * @param x is the x coordinate of the location to search
+ * @param y is the y coordinate of the location to search
+ * @return the tree path closes to x,y.
+ */
+ public TreePath getClosestPathForLocation(JTree tree, int x, int y)
+ {
+ return treeState.getPathClosestTo(x, y);
+ }
+
+ /**
+ * Returns true if the tree is being edited. The item that is being edited can
+ * be returned by getEditingPath().
+ *
+ * @param tree is the tree to check for editing.
+ * @return true if the tree is being edited.
+ */
+ public boolean isEditing(JTree tree)
+ {
+ return isEditing;
+ }
+
+ /**
+ * Stops the current editing session. This has no effect if the tree is not
+ * being edited. Returns true if the editor allows the editing session to
+ * stop.
+ *
+ * @param tree is the tree to stop the editing on
+ * @return true if the editor allows the editing session to stop.
+ */
+ public boolean stopEditing(JTree tree)
+ {
+ boolean ret = false;
+ if (editingComponent != null && cellEditor.stopCellEditing())
+ {
+ completeEditing(false, false, true);
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Cancels the current editing session.
+ *
+ * @param tree is the tree to cancel the editing session on.
+ */
+ public void cancelEditing(JTree tree)
+ {
+ // There is no need to send the cancel message to the editor,
+ // as the cancellation event itself arrives from it. This would
+ // only be necessary when cancelling the editing programatically.
+ if (editingComponent != null)
+ completeEditing(false, true, false);
+ }
+
+ /**
+ * Selects the last item in path and tries to edit it. Editing will fail if
+ * the CellEditor won't allow it for the selected item.
+ *
+ * @param tree is the tree to edit on.
+ * @param path is the path in tree to edit on.
+ */
+ public void startEditingAtPath(JTree tree, TreePath path)
+ {
+ tree.scrollPathToVisible(path);
+ if (path != null && tree.isVisible(path))
+ startEditing(path, null);
+ }
+
+ /**
+ * Returns the path to the element that is being editted.
+ *
+ * @param tree is the tree to get the editing path from.
+ * @return the path that is being edited.
+ */
+ public TreePath getEditingPath(JTree tree)
+ {
+ return editingPath;
+ }
+
+ /**
+ * Invoked after the tree instance variable has been set, but before any
+ * default/listeners have been installed.
+ */
+ protected void prepareForUIInstall()
+ {
+ lastSelectedRow = -1;
+ preferredSize = new Dimension();
+ largeModel = tree.isLargeModel();
+ preferredSize = new Dimension();
+ stopEditingInCompleteEditing = true;
+ setModel(tree.getModel());
+ }
+
+ /**
+ * Invoked from installUI after all the defaults/listeners have been
+ * installed.
+ */
+ protected void completeUIInstall()
+ {
+ setShowsRootHandles(tree.getShowsRootHandles());
+ updateRenderer();
+ updateDepthOffset();
+ setSelectionModel(tree.getSelectionModel());
+ configureLayoutCache();
+ treeState.setRootVisible(tree.isRootVisible());
+ treeSelectionModel.setRowMapper(treeState);
+ updateSize();
+ }
+
+ /**
+ * Invoked from uninstallUI after all the defaults/listeners have been
+ * uninstalled.
+ */
+ protected void completeUIUninstall()
+ {
+ tree = null;
+ }
+
+ /**
+ * Installs the subcomponents of the tree, which is the renderer pane.
+ */
+ protected void installComponents()
+ {
+ currentCellRenderer = createDefaultCellRenderer();
+ rendererPane = createCellRendererPane();
+ createdRenderer = true;
+ setCellRenderer(currentCellRenderer);
+ }
+
+ /**
+ * Creates an instance of NodeDimensions that is able to determine the size of
+ * a given node in the tree. The node dimensions must be created before
+ * configuring the layout cache.
+ *
+ * @return the NodeDimensions of a given node in the tree
+ */
+ protected AbstractLayoutCache.NodeDimensions createNodeDimensions()
+ {
+ return new NodeDimensionsHandler();
+ }
+
+ /**
+ * Creates a listener that is reponsible for the updates the UI based on how
+ * the tree changes.
+ *
+ * @return the PropertyChangeListener that is reposnsible for the updates
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates the listener responsible for updating the selection based on mouse
+ * events.
+ *
+ * @return the MouseListener responsible for updating.
+ */
+ protected MouseListener createMouseListener()
+ {
+ return new MouseHandler();
+ }
+
+ /**
+ * Creates the listener that is responsible for updating the display when
+ * focus is lost/grained.
+ *
+ * @return the FocusListener responsible for updating.
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * Creates the listener reponsible for getting key events from the tree.
+ *
+ * @return the KeyListener responsible for getting key events.
+ */
+ protected KeyListener createKeyListener()
+ {
+ return new KeyHandler();
+ }
+
+ /**
+ * Creates the listener responsible for getting property change events from
+ * the selection model.
+ *
+ * @returns the PropertyChangeListener reponsible for getting property change
+ * events from the selection model.
+ */
+ protected PropertyChangeListener createSelectionModelPropertyChangeListener()
+ {
+ return new SelectionModelPropertyChangeHandler();
+ }
+
+ /**
+ * Creates the listener that updates the display based on selection change
+ * methods.
+ *
+ * @return the TreeSelectionListener responsible for updating.
+ */
+ protected TreeSelectionListener createTreeSelectionListener()
+ {
+ return new TreeSelectionHandler();
+ }
+
+ /**
+ * Creates a listener to handle events from the current editor
+ *
+ * @return the CellEditorListener that handles events from the current editor
+ */
+ protected CellEditorListener createCellEditorListener()
+ {
+ return new CellEditorHandler();
+ }
+
+ /**
+ * Creates and returns a new ComponentHandler. This is used for the large
+ * model to mark the validCachedPreferredSize as invalid when the component
+ * moves.
+ *
+ * @return a new ComponentHandler.
+ */
+ protected ComponentListener createComponentListener()
+ {
+ return new ComponentHandler();
+ }
+
+ /**
+ * Creates and returns the object responsible for updating the treestate when
+ * a nodes expanded state changes.
+ *
+ * @return the TreeExpansionListener responsible for updating the treestate
+ */
+ protected TreeExpansionListener createTreeExpansionListener()
+ {
+ return new TreeExpansionHandler();
+ }
+
+ /**
+ * Creates the object responsible for managing what is expanded, as well as
+ * the size of nodes.
+ *
+ * @return the object responsible for managing what is expanded.
+ */
+ protected AbstractLayoutCache createLayoutCache()
+ {
+ return new VariableHeightLayoutCache();
+ }
+
+ /**
+ * Returns the renderer pane that renderer components are placed in.
+ *
+ * @return the rendererpane that render components are placed in.
+ */
+ protected CellRendererPane createCellRendererPane()
+ {
+ return new CellRendererPane();
+ }
+
+ /**
+ * Creates a default cell editor.
+ *
+ * @return the default cell editor.
+ */
+ protected TreeCellEditor createDefaultCellEditor()
+ {
+ DefaultTreeCellEditor ed;
+ if (currentCellRenderer != null
+ && currentCellRenderer instanceof DefaultTreeCellRenderer)
+ ed = new DefaultTreeCellEditor(tree,
+ (DefaultTreeCellRenderer) currentCellRenderer);
+ else
+ ed = new DefaultTreeCellEditor(tree, null);
+ return ed;
+ }
+
+ /**
+ * Returns the default cell renderer that is used to do the stamping of each
+ * node.
+ *
+ * @return the default cell renderer that is used to do the stamping of each
+ * node.
+ */
+ protected TreeCellRenderer createDefaultCellRenderer()
+ {
+ return new DefaultTreeCellRenderer();
+ }
+
+ /**
+ * Returns a listener that can update the tree when the model changes.
+ *
+ * @return a listener that can update the tree when the model changes.
+ */
+ protected TreeModelListener createTreeModelListener()
+ {
+ return new TreeModelHandler();
+ }
+
+ /**
+ * Uninstall all registered listeners
+ */
+ protected void uninstallListeners()
+ {
+ tree.removePropertyChangeListener(propertyChangeListener);
+ tree.removeFocusListener(focusListener);
+ tree.removeTreeSelectionListener(treeSelectionListener);
+ tree.removeMouseListener(mouseListener);
+ tree.removeKeyListener(keyListener);
+ tree.removePropertyChangeListener(selectionModelPropertyChangeListener);
+ tree.removeComponentListener(componentListener);
+ tree.removeTreeExpansionListener(treeExpansionListener);
+
+ TreeCellEditor tce = tree.getCellEditor();
+ if (tce != null)
+ tce.removeCellEditorListener(cellEditorListener);
+ if (treeModel != null)
+ treeModel.removeTreeModelListener(treeModelListener);
+ }
+
+ /**
+ * Uninstall all keyboard actions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(
+ null);
+ tree.getActionMap().setParent(null);
+ }
+
+ /**
+ * Uninstall the rendererPane.
+ */
+ protected void uninstallComponents()
+ {
+ currentCellRenderer = null;
+ rendererPane = null;
+ createdRenderer = false;
+ setCellRenderer(currentCellRenderer);
+ }
+
+ /**
+ * The vertical element of legs between nodes starts at the bottom of the
+ * parent node by default. This method makes the leg start below that.
+ *
+ * @return the vertical leg buffer
+ */
+ protected int getVerticalLegBuffer()
+ {
+ return getRowHeight() / 2;
+ }
+
+ /**
+ * The horizontal element of legs between nodes starts at the right of the
+ * left-hand side of the child node by default. This method makes the leg end
+ * before that.
+ *
+ * @return the horizontal leg buffer
+ */
+ protected int getHorizontalLegBuffer()
+ {
+ return rightChildIndent / 2;
+ }
+
+ /**
+ * Make all the nodes that are expanded in JTree expanded in LayoutCache. This
+ * invokes updateExpandedDescendants with the root path.
+ */
+ protected void updateLayoutCacheExpandedNodes()
+ {
+ if (treeModel != null && treeModel.getRoot() != null)
+ updateExpandedDescendants(new TreePath(treeModel.getRoot()));
+ }
+
+ /**
+ * Updates the expanded state of all the descendants of the <code>path</code>
+ * by getting the expanded descendants from the tree and forwarding to the
+ * tree state.
+ *
+ * @param path the path used to update the expanded states
+ */
+ protected void updateExpandedDescendants(TreePath path)
+ {
+ completeEditing();
+ Enumeration expanded = tree.getExpandedDescendants(path);
+ while (expanded.hasMoreElements())
+ treeState.setExpandedState((TreePath) expanded.nextElement(), true);
+ }
+
+ /**
+ * Returns a path to the last child of <code>parent</code>
+ *
+ * @param parent is the topmost path to specified
+ * @return a path to the last child of parent
+ */
+ protected TreePath getLastChildPath(TreePath parent)
+ {
+ return (TreePath) parent.getLastPathComponent();
+ }
+
+ /**
+ * Updates how much each depth should be offset by.
+ */
+ protected void updateDepthOffset()
+ {
+ depthOffset += getVerticalLegBuffer();
+ }
+
+ /**
+ * Updates the cellEditor based on editability of the JTree that we're
+ * contained in. If the tree is editable but doesn't have a cellEditor, a
+ * basic one will be used.
+ */
+ protected void updateCellEditor()
+ {
+ completeEditing();
+ TreeCellEditor newEd = null;
+ if (tree != null && tree.isEditable())
+ {
+ newEd = tree.getCellEditor();
+ if (newEd == null)
+ {
+ newEd = createDefaultCellEditor();
+ if (newEd != null)
+ {
+ tree.setCellEditor(newEd);
+ createdCellEditor = true;
+ }
+ }
+ }
+ // Update listeners.
+ if (newEd != cellEditor)
+ {
+ if (cellEditor != null && cellEditorListener != null)
+ cellEditor.removeCellEditorListener(cellEditorListener);
+ cellEditor = newEd;
+ if (cellEditorListener == null)
+ cellEditorListener = createCellEditorListener();
+ if (cellEditor != null && cellEditorListener != null)
+ cellEditor.addCellEditorListener(cellEditorListener);
+ createdCellEditor = false;
+ }
+ }
+
+ /**
+ * Messaged from the tree we're in when the renderer has changed.
+ */
+ protected void updateRenderer()
+ {
+ if (tree != null)
+ {
+ TreeCellRenderer rend = tree.getCellRenderer();
+ if (rend != null)
+ {
+ createdRenderer = false;
+ currentCellRenderer = rend;
+ if (createdCellEditor)
+ tree.setCellEditor(null);
+ }
+ else
+ {
+ tree.setCellRenderer(createDefaultCellRenderer());
+ createdRenderer = true;
+ }
+ }
+ else
+ {
+ currentCellRenderer = null;
+ createdRenderer = false;
+ }
+
+ updateCellEditor();
+ }
+
+ /**
+ * Resets the treeState instance based on the tree we're providing the look
+ * and feel for. The node dimensions handler is required and must be created
+ * in advance.
+ */
+ protected void configureLayoutCache()
+ {
+ treeState = createLayoutCache();
+ treeState.setNodeDimensions(nodeDimensions);
+ }
+
+ /**
+ * Marks the cached size as being invalid, and messages the tree with
+ * <code>treeDidChange</code>.
+ */
+ protected void updateSize()
+ {
+ preferredSize = null;
+ updateCachedPreferredSize();
+ tree.treeDidChange();
+ }
+
+ /**
+ * Updates the <code>preferredSize</code> instance variable, which is
+ * returned from <code>getPreferredSize()</code>.
+ */
+ protected void updateCachedPreferredSize()
+ {
+ validCachedPreferredSize = false;
+ }
+
+ /**
+ * Messaged from the VisibleTreeNode after it has been expanded.
+ *
+ * @param path is the path that has been expanded.
+ */
+ protected void pathWasExpanded(TreePath path)
+ {
+ validCachedPreferredSize = false;
+ treeState.setExpandedState(path, true);
+ tree.repaint();
+ }
+
+ /**
+ * Messaged from the VisibleTreeNode after it has collapsed
+ */
+ protected void pathWasCollapsed(TreePath path)
+ {
+ validCachedPreferredSize = false;
+ treeState.setExpandedState(path, false);
+ tree.repaint();
+ }
+
+ /**
+ * Install all defaults for the tree.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(tree, "Tree.background",
+ "Tree.foreground", "Tree.font");
+
+ hashColor = UIManager.getColor("Tree.hash");
+ if (hashColor == null)
+ hashColor = Color.black;
+
+ tree.setOpaque(true);
+
+ rightChildIndent = UIManager.getInt("Tree.rightChildIndent");
+ leftChildIndent = UIManager.getInt("Tree.leftChildIndent");
+ totalChildIndent = rightChildIndent + leftChildIndent;
+ setRowHeight(UIManager.getInt("Tree.rowHeight"));
+ tree.setRowHeight(getRowHeight());
+ tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand"));
+ setExpandedIcon(UIManager.getIcon("Tree.expandedIcon"));
+ setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon"));
+ }
+
+ /**
+ * Install all keyboard actions for this
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap focusInputMap =
+ (InputMap) SharedUIDefaults.get("Tree.focusInputMap");
+ SwingUtilities.replaceUIInputMap(tree, JComponent.WHEN_FOCUSED,
+ focusInputMap);
+ InputMap ancestorInputMap =
+ (InputMap) SharedUIDefaults.get("Tree.ancestorInputMap");
+ SwingUtilities.replaceUIInputMap(tree,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ ancestorInputMap);
+
+ SwingUtilities.replaceUIActionMap(tree, getActionMap());
+ }
+
+ /**
+ * Creates and returns the shared action map for JTrees.
+ *
+ * @return the shared action map for JTrees
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("Tree.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("Tree.actionMap", am);
+ }
+ return am;
+ }
+
+ /**
+ * Creates the default actions when there are none specified by the L&F.
+ *
+ * @return the default actions
+ */
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action;
+
+ // TreeHomeAction.
+ action = new TreeHomeAction(-1, "selectFirst");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(-1, "selectFirstChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(-1, "selectFirstExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(1, "selectLast");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(1, "selectLastChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(1, "selectLastExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+
+ // TreeIncrementAction.
+ action = new TreeIncrementAction(-1, "selectPrevious");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(-1, "selectPreviousExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(-1, "selectPreviousChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(1, "selectNext");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(1, "selectNextExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(1, "selectNextChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+
+ // TreeTraverseAction.
+ action = new TreeTraverseAction(-1, "selectParent");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeTraverseAction(1, "selectChild");
+ am.put(action.getValue(Action.NAME), action);
+
+ // TreeToggleAction.
+ action = new TreeToggleAction("toggleAndAnchor");
+ am.put(action.getValue(Action.NAME), action);
+
+ // TreePageAction.
+ action = new TreePageAction(-1, "scrollUpChangeSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(-1, "scrollUpExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(-1, "scrollUpChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(1, "scrollDownChangeSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(1, "scrollDownExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(1, "scrollDownChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+
+ // Tree editing actions
+ action = new TreeStartEditingAction("startEditing");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeCancelEditingAction("cancel");
+ am.put(action.getValue(Action.NAME), action);
+
+
+ return am;
+ }
+
+ /**
+ * Converts the modifiers.
+ *
+ * @param mod - modifier to convert
+ * @returns the new modifier
+ */
+ private int convertModifiers(int mod)
+ {
+ if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.SHIFT_MASK;
+ mod &= ~ KeyEvent.SHIFT_DOWN_MASK;
+ }
+ if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.CTRL_MASK;
+ mod &= ~ KeyEvent.CTRL_DOWN_MASK;
+ }
+ if ((mod & KeyEvent.META_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.META_MASK;
+ mod &= ~ KeyEvent.META_DOWN_MASK;
+ }
+ if ((mod & KeyEvent.ALT_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.ALT_MASK;
+ mod &= ~ KeyEvent.ALT_DOWN_MASK;
+ }
+ if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.ALT_GRAPH_MASK;
+ mod &= ~ KeyEvent.ALT_GRAPH_DOWN_MASK;
+ }
+ return mod;
+ }
+
+ /**
+ * Install all listeners for this
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+ tree.addPropertyChangeListener(propertyChangeListener);
+
+ focusListener = createFocusListener();
+ tree.addFocusListener(focusListener);
+
+ treeSelectionListener = createTreeSelectionListener();
+ tree.addTreeSelectionListener(treeSelectionListener);
+
+ mouseListener = createMouseListener();
+ tree.addMouseListener(mouseListener);
+
+ keyListener = createKeyListener();
+ tree.addKeyListener(keyListener);
+
+ selectionModelPropertyChangeListener =
+ createSelectionModelPropertyChangeListener();
+ if (treeSelectionModel != null
+ && selectionModelPropertyChangeListener != null)
+ {
+ treeSelectionModel.addPropertyChangeListener(
+ selectionModelPropertyChangeListener);
+ }
+
+ componentListener = createComponentListener();
+ tree.addComponentListener(componentListener);
+
+ treeExpansionListener = createTreeExpansionListener();
+ tree.addTreeExpansionListener(treeExpansionListener);
+
+ treeModelListener = createTreeModelListener();
+ if (treeModel != null)
+ treeModel.addTreeModelListener(treeModelListener);
+
+ cellEditorListener = createCellEditorListener();
+ }
+
+ /**
+ * Install the UI for the component
+ *
+ * @param c the component to install UI for
+ */
+ public void installUI(JComponent c)
+ {
+ tree = (JTree) c;
+
+ prepareForUIInstall();
+ installDefaults();
+ installComponents();
+ installKeyboardActions();
+ installListeners();
+ completeUIInstall();
+ }
+
+ /**
+ * Uninstall the defaults for the tree
+ */
+ protected void uninstallDefaults()
+ {
+ tree.setFont(null);
+ tree.setForeground(null);
+ tree.setBackground(null);
+ }
+
+ /**
+ * Uninstall the UI for the component
+ *
+ * @param c the component to uninstall UI for
+ */
+ public void uninstallUI(JComponent c)
+ {
+ completeEditing();
+
+ prepareForUIUninstall();
+ uninstallDefaults();
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallComponents();
+ completeUIUninstall();
+ }
+
+ /**
+ * Paints the specified component appropriate for the look and feel. This
+ * method is invoked from the ComponentUI.update method when the specified
+ * component is being painted. Subclasses should override this method and use
+ * the specified Graphics object to render the content of the component.
+ *
+ * @param g the Graphics context in which to paint
+ * @param c the component being painted; this argument is often ignored, but
+ * might be used if the UI object is stateless and shared by multiple
+ * components
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ JTree tree = (JTree) c;
+
+ int rows = treeState.getRowCount();
+
+ if (rows == 0)
+ // There is nothing to do if the tree is empty.
+ return;
+
+ Rectangle clip = g.getClipBounds();
+
+ Insets insets = tree.getInsets();
+
+ if (clip != null && treeModel != null)
+ {
+ int startIndex = tree.getClosestRowForLocation(clip.x, clip.y);
+ int endIndex = tree.getClosestRowForLocation(clip.x + clip.width,
+ clip.y + clip.height);
+ // Also paint dashes to the invisible nodes below.
+ // These should be painted first, otherwise they may cover
+ // the control icons.
+ if (endIndex < rows)
+ for (int i = endIndex + 1; i < rows; i++)
+ {
+ TreePath path = treeState.getPathForRow(i);
+ if (isLastChild(path))
+ paintVerticalPartOfLeg(g, clip, insets, path);
+ }
+
+ // The two loops are required to ensure that the lines are not
+ // painted over the other tree components.
+
+ int n = endIndex - startIndex + 1;
+ Rectangle[] bounds = new Rectangle[n];
+ boolean[] isLeaf = new boolean[n];
+ boolean[] isExpanded = new boolean[n];
+ TreePath[] path = new TreePath[n];
+ int k;
+
+ k = 0;
+ for (int i = startIndex; i <= endIndex; i++, k++)
+ {
+ path[k] = treeState.getPathForRow(i);
+ if (path[k] != null)
+ {
+ isLeaf[k] = treeModel.isLeaf(path[k].getLastPathComponent());
+ isExpanded[k] = tree.isExpanded(path[k]);
+ bounds[k] = getPathBounds(tree, path[k]);
+
+ paintHorizontalPartOfLeg(g, clip, insets, bounds[k], path[k],
+ i, isExpanded[k], false, isLeaf[k]);
+ }
+ if (isLastChild(path[k]))
+ paintVerticalPartOfLeg(g, clip, insets, path[k]);
+ }
+
+ k = 0;
+ for (int i = startIndex; i <= endIndex; i++, k++)
+ {
+ if (path[k] != null)
+ paintRow(g, clip, insets, bounds[k], path[k], i, isExpanded[k],
+ false, isLeaf[k]);
+ }
+ }
+ }
+
+ /**
+ * Check if the path is referring to the last child of some parent.
+ */
+ private boolean isLastChild(TreePath path)
+ {
+ if (path == null)
+ return false;
+ else if (path instanceof GnuPath)
+ {
+ // Except the seldom case when the layout cache is changed, this
+ // optimized code will be executed.
+ return ((GnuPath) path).isLastChild;
+ }
+ else
+ {
+ // Non optimized general case.
+ TreePath parent = path.getParentPath();
+ if (parent == null)
+ return false;
+ int childCount = treeState.getVisibleChildCount(parent);
+ int p = treeModel.getIndexOfChild(parent, path.getLastPathComponent());
+ return p == childCount - 1;
+ }
+ }
+
+ /**
+ * Ensures that the rows identified by beginRow through endRow are visible.
+ *
+ * @param beginRow is the first row
+ * @param endRow is the last row
+ */
+ protected void ensureRowsAreVisible(int beginRow, int endRow)
+ {
+ if (beginRow < endRow)
+ {
+ int temp = endRow;
+ endRow = beginRow;
+ beginRow = temp;
+ }
+
+ for (int i = beginRow; i < endRow; i++)
+ {
+ TreePath path = getPathForRow(tree, i);
+ if (! tree.isVisible(path))
+ tree.makeVisible(path);
+ }
+ }
+
+ /**
+ * Sets the preferred minimum size.
+ *
+ * @param newSize is the new preferred minimum size.
+ */
+ public void setPreferredMinSize(Dimension newSize)
+ {
+ preferredMinSize = newSize;
+ }
+
+ /**
+ * Gets the preferred minimum size.
+ *
+ * @returns the preferred minimum size.
+ */
+ public Dimension getPreferredMinSize()
+ {
+ if (preferredMinSize == null)
+ return getPreferredSize(tree);
+ else
+ return preferredMinSize;
+ }
+
+ /**
+ * Returns the preferred size to properly display the tree, this is a cover
+ * method for getPreferredSize(c, false).
+ *
+ * @param c the component whose preferred size is being queried; this argument
+ * is often ignored but might be used if the UI object is stateless
+ * and shared by multiple components
+ * @return the preferred size
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return getPreferredSize(c, false);
+ }
+
+ /**
+ * Returns the preferred size to represent the tree in c. If checkConsistancy
+ * is true, checkConsistancy is messaged first.
+ *
+ * @param c the component whose preferred size is being queried.
+ * @param checkConsistancy if true must check consistancy
+ * @return the preferred size
+ */
+ public Dimension getPreferredSize(JComponent c, boolean checkConsistancy)
+ {
+ if (! validCachedPreferredSize)
+ {
+ Rectangle size = tree.getBounds();
+ // Add the scrollbar dimensions to the preferred size.
+ preferredSize = new Dimension(treeState.getPreferredWidth(size),
+ treeState.getPreferredHeight());
+ validCachedPreferredSize = true;
+ }
+ return preferredSize;
+ }
+
+ /**
+ * Returns the minimum size for this component. Which will be the min
+ * preferred size or (0,0).
+ *
+ * @param c the component whose min size is being queried.
+ * @returns the preferred size or null
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return preferredMinSize = getPreferredSize(c);
+ }
+
+ /**
+ * Returns the maximum size for the component, which will be the preferred
+ * size if the instance is currently in JTree or (0,0).
+ *
+ * @param c the component whose preferred size is being queried
+ * @return the max size or null
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * Messages to stop the editing session. If the UI the receiver is providing
+ * the look and feel for returns true from
+ * <code>getInvokesStopCellEditing</code>, stopCellEditing will be invoked
+ * on the current editor. Then completeEditing will be messaged with false,
+ * true, false to cancel any lingering editing.
+ */
+ protected void completeEditing()
+ {
+ if (tree.getInvokesStopCellEditing() && stopEditingInCompleteEditing
+ && editingComponent != null)
+ cellEditor.stopCellEditing();
+
+ completeEditing(false, true, false);
+ }
+
+ /**
+ * Stops the editing session. If messageStop is true, the editor is messaged
+ * with stopEditing, if messageCancel is true the editor is messaged with
+ * cancelEditing. If messageTree is true, the treeModel is messaged with
+ * valueForPathChanged.
+ *
+ * @param messageStop message to stop editing
+ * @param messageCancel message to cancel editing
+ * @param messageTree message to treeModel
+ */
+ protected void completeEditing(boolean messageStop, boolean messageCancel,
+ boolean messageTree)
+ {
+ // Make no attempt to complete the non existing editing session.
+ if (stopEditingInCompleteEditing && editingComponent != null)
+ {
+ Component comp = editingComponent;
+ TreePath p = editingPath;
+ editingComponent = null;
+ editingPath = null;
+ if (messageStop)
+ cellEditor.stopCellEditing();
+ else if (messageCancel)
+ cellEditor.cancelCellEditing();
+
+ tree.remove(comp);
+
+ if (editorHasDifferentSize)
+ {
+ treeState.invalidatePathBounds(p);
+ updateSize();
+ }
+ else
+ {
+ // Need to refresh the tree.
+ Rectangle b = getPathBounds(tree, p);
+ tree.repaint(0, b.y, tree.getWidth(), b.height);
+ }
+
+ if (messageTree)
+ {
+ Object value = cellEditor.getCellEditorValue();
+ treeModel.valueForPathChanged(p, value);
+ }
+ }
+ }
+
+ /**
+ * Will start editing for node if there is a cellEditor and shouldSelectCall
+ * returns true. This assumes that path is valid and visible.
+ *
+ * @param path is the path to start editing
+ * @param event is the MouseEvent performed on the path
+ * @return true if successful
+ */
+ protected boolean startEditing(TreePath path, MouseEvent event)
+ {
+ // Maybe cancel editing.
+ if (isEditing(tree) && tree.getInvokesStopCellEditing()
+ && ! stopEditing(tree))
+ return false;
+
+ completeEditing();
+ TreeCellEditor ed = cellEditor;
+ if (ed != null && tree.isPathEditable(path))
+ {
+ if (ed.isCellEditable(event))
+ {
+ editingRow = getRowForPath(tree, path);
+ Object value = path.getLastPathComponent();
+ boolean isSelected = tree.isPathSelected(path);
+ boolean isExpanded = tree.isExpanded(editingPath);
+ boolean isLeaf = treeModel.isLeaf(value);
+ editingComponent = ed.getTreeCellEditorComponent(tree, value,
+ isSelected,
+ isExpanded,
+ isLeaf,
+ editingRow);
+
+ Rectangle bounds = getPathBounds(tree, path);
+
+ Dimension size = editingComponent.getPreferredSize();
+ int rowHeight = getRowHeight();
+ if (size.height != bounds.height && rowHeight > 0)
+ size.height = rowHeight;
+
+ if (size.width != bounds.width || size.height != bounds.height)
+ {
+ editorHasDifferentSize = true;
+ treeState.invalidatePathBounds(path);
+ updateSize();
+ }
+ else
+ editorHasDifferentSize = false;
+
+ // The editing component must be added to its container. We add the
+ // container, not the editing component itself.
+ tree.add(editingComponent);
+ editingComponent.setBounds(bounds.x, bounds.y, size.width,
+ size.height);
+ editingComponent.validate();
+ editingPath = path;
+
+ if (ed.shouldSelectCell(event))
+ {
+ stopEditingInCompleteEditing = false;
+ tree.setSelectionRow(editingRow);
+ stopEditingInCompleteEditing = true;
+ }
+
+ editorRequestFocus(editingComponent);
+ // Register MouseInputHandler to redispatch initial mouse events
+ // correctly.
+ if (event instanceof MouseEvent)
+ {
+ Point p = SwingUtilities.convertPoint(tree, event.getX(), event.getY(),
+ editingComponent);
+ Component active =
+ SwingUtilities.getDeepestComponentAt(editingComponent, p.x, p.y);
+ if (active != null)
+ {
+ MouseInputHandler ih = new MouseInputHandler(tree, active, event);
+
+ }
+ }
+
+ return true;
+ }
+ else
+ editingComponent = null;
+ }
+ return false;
+ }
+
+ /**
+ * Requests focus on the editor. The method is necessary since the
+ * DefaultTreeCellEditor returns a container that contains the
+ * actual editor, and we want to request focus on the editor, not the
+ * container.
+ */
+ private void editorRequestFocus(Component c)
+ {
+ if (c instanceof Container)
+ {
+ // TODO: Maybe do something more reasonable here, like queriying the
+ // FocusTraversalPolicy.
+ Container cont = (Container) c;
+ if (cont.getComponentCount() > 0)
+ cont.getComponent(0).requestFocus();
+ }
+ else if (c.isFocusable())
+ c.requestFocus();
+
+ }
+
+ /**
+ * If the <code>mouseX</code> and <code>mouseY</code> are in the expand or
+ * collapse region of the row, this will toggle the row.
+ *
+ * @param path the path we are concerned with
+ * @param mouseX is the cursor's x position
+ * @param mouseY is the cursor's y position
+ */
+ protected void checkForClickInExpandControl(TreePath path, int mouseX,
+ int mouseY)
+ {
+ if (isLocationInExpandControl(path, mouseX, mouseY))
+ handleExpandControlClick(path, mouseX, mouseY);
+ }
+
+ /**
+ * Returns true if the <code>mouseX</code> and <code>mouseY</code> fall in
+ * the area of row that is used to expand/collpse the node and the node at row
+ * does not represent a leaf.
+ *
+ * @param path the path we are concerned with
+ * @param mouseX is the cursor's x position
+ * @param mouseY is the cursor's y position
+ * @return true if the <code>mouseX</code> and <code>mouseY</code> fall in
+ * the area of row that is used to expand/collpse the node and the
+ * node at row does not represent a leaf.
+ */
+ protected boolean isLocationInExpandControl(TreePath path, int mouseX,
+ int mouseY)
+ {
+ boolean cntlClick = false;
+ if (! treeModel.isLeaf(path.getLastPathComponent()))
+ {
+ int width;
+ Icon expandedIcon = getExpandedIcon();
+ if (expandedIcon != null)
+ width = expandedIcon.getIconWidth();
+ else
+ // Only guessing. This is the width of
+ // the tree control icon in Metal L&F.
+ width = 18;
+
+ Insets i = tree.getInsets();
+
+ int depth;
+ if (isRootVisible())
+ depth = path.getPathCount()-1;
+ else
+ depth = path.getPathCount()-2;
+
+ int left = getRowX(tree.getRowForPath(path), depth)
+ - width + i.left;
+ cntlClick = mouseX >= left && mouseX <= left + width;
+ }
+ return cntlClick;
+ }
+
+ /**
+ * Messaged when the user clicks the particular row, this invokes
+ * toggleExpandState.
+ *
+ * @param path the path we are concerned with
+ * @param mouseX is the cursor's x position
+ * @param mouseY is the cursor's y position
+ */
+ protected void handleExpandControlClick(TreePath path, int mouseX, int mouseY)
+ {
+ toggleExpandState(path);
+ }
+
+ /**
+ * Expands path if it is not expanded, or collapses row if it is expanded. If
+ * expanding a path and JTree scroll on expand, ensureRowsAreVisible is
+ * invoked to scroll as many of the children to visible as possible (tries to
+ * scroll to last visible descendant of path).
+ *
+ * @param path the path we are concerned with
+ */
+ protected void toggleExpandState(TreePath path)
+ {
+ // tree.isExpanded(path) would do the same, but treeState knows faster.
+ if (treeState.isExpanded(path))
+ tree.collapsePath(path);
+ else
+ tree.expandPath(path);
+ }
+
+ /**
+ * Returning true signifies a mouse event on the node should toggle the
+ * selection of only the row under the mouse. The BasisTreeUI treats the
+ * event as "toggle selection event" if the CTRL button was pressed while
+ * clicking. The event is not counted as toggle event if the associated
+ * tree does not support the multiple selection.
+ *
+ * @param event is the MouseEvent performed on the row.
+ * @return true signifies a mouse event on the node should toggle the
+ * selection of only the row under the mouse.
+ */
+ protected boolean isToggleSelectionEvent(MouseEvent event)
+ {
+ return
+ (tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION) &&
+ ((event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0);
+ }
+
+ /**
+ * Returning true signifies a mouse event on the node should select from the
+ * anchor point. The BasisTreeUI treats the event as "multiple selection
+ * event" if the SHIFT button was pressed while clicking. The event is not
+ * counted as multiple selection event if the associated tree does not support
+ * the multiple selection.
+ *
+ * @param event is the MouseEvent performed on the node.
+ * @return true signifies a mouse event on the node should select from the
+ * anchor point.
+ */
+ protected boolean isMultiSelectEvent(MouseEvent event)
+ {
+ return
+ (tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION) &&
+ ((event.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0);
+ }
+
+ /**
+ * Returning true indicates the row under the mouse should be toggled based on
+ * the event. This is invoked after checkForClickInExpandControl, implying the
+ * location is not in the expand (toggle) control.
+ *
+ * @param event is the MouseEvent performed on the row.
+ * @return true indicates the row under the mouse should be toggled based on
+ * the event.
+ */
+ protected boolean isToggleEvent(MouseEvent event)
+ {
+ boolean toggle = false;
+ if (SwingUtilities.isLeftMouseButton(event))
+ {
+ int clickCount = tree.getToggleClickCount();
+ if (clickCount > 0 && event.getClickCount() == clickCount)
+ toggle = true;
+ }
+ return toggle;
+ }
+
+ /**
+ * Messaged to update the selection based on a MouseEvent over a particular
+ * row. If the even is a toggle selection event, the row is either selected,
+ * or deselected. If the event identifies a multi selection event, the
+ * selection is updated from the anchor point. Otherwise, the row is selected,
+ * and the previous selection is cleared.</p>
+ *
+ * @param path is the path selected for an event
+ * @param event is the MouseEvent performed on the path.
+ *
+ * @see #isToggleSelectionEvent(MouseEvent)
+ * @see #isMultiSelectEvent(MouseEvent)
+ */
+ protected void selectPathForEvent(TreePath path, MouseEvent event)
+ {
+ if (isToggleSelectionEvent(event))
+ {
+ // The event selects or unselects the clicked row.
+ if (tree.isPathSelected(path))
+ tree.removeSelectionPath(path);
+ else
+ {
+ tree.addSelectionPath(path);
+ tree.setAnchorSelectionPath(path);
+ }
+ }
+ else if (isMultiSelectEvent(event))
+ {
+ // The event extends selection form anchor till the clicked row.
+ TreePath anchor = tree.getAnchorSelectionPath();
+ if (anchor != null)
+ {
+ int aRow = getRowForPath(tree, anchor);
+ tree.addSelectionInterval(aRow, getRowForPath(tree, path));
+ }
+ else
+ tree.addSelectionPath(path);
+ }
+ else
+ {
+ // This is an ordinary event that just selects the clicked row.
+ tree.setSelectionPath(path);
+ if (isToggleEvent(event))
+ toggleExpandState(path);
+ }
+ }
+
+ /**
+ * Returns true if the node at <code>row</code> is a leaf.
+ *
+ * @param row is the row we are concerned with.
+ * @return true if the node at <code>row</code> is a leaf.
+ */
+ protected boolean isLeaf(int row)
+ {
+ TreePath pathForRow = getPathForRow(tree, row);
+ if (pathForRow == null)
+ return true;
+
+ Object node = pathForRow.getLastPathComponent();
+ return treeModel.isLeaf(node);
+ }
+
+ /**
+ * The action to start editing at the current lead selection path.
+ */
+ class TreeStartEditingAction
+ extends AbstractAction
+ {
+ /**
+ * Creates the new tree cancel editing action.
+ *
+ * @param name the name of the action (used in toString).
+ */
+ public TreeStartEditingAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Start editing at the current lead selection path.
+ *
+ * @param e the ActionEvent that caused this action.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ TreePath lead = tree.getLeadSelectionPath();
+ if (!tree.isEditing())
+ tree.startEditingAtPath(lead);
+ }
+ }
+
+ /**
+ * Updates the preferred size when scrolling, if necessary.
+ */
+ public class ComponentHandler
+ extends ComponentAdapter
+ implements ActionListener
+ {
+ /**
+ * Timer used when inside a scrollpane and the scrollbar is adjusting
+ */
+ protected Timer timer;
+
+ /** ScrollBar that is being adjusted */
+ protected JScrollBar scrollBar;
+
+ /**
+ * Constructor
+ */
+ public ComponentHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when the component's position changes.
+ *
+ * @param e the event that occurs when moving the component
+ */
+ public void componentMoved(ComponentEvent e)
+ {
+ if (timer == null)
+ {
+ JScrollPane scrollPane = getScrollPane();
+ if (scrollPane == null)
+ updateSize();
+ else
+ {
+ // Determine the scrollbar that is adjusting, if any, and
+ // start the timer for that. If no scrollbar is adjusting,
+ // we simply call updateSize().
+ scrollBar = scrollPane.getVerticalScrollBar();
+ if (scrollBar == null || !scrollBar.getValueIsAdjusting())
+ {
+ // It's not the vertical scrollbar, try the horizontal one.
+ scrollBar = scrollPane.getHorizontalScrollBar();
+ if (scrollBar != null && scrollBar.getValueIsAdjusting())
+ startTimer();
+ else
+ updateSize();
+ }
+ else
+ {
+ startTimer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates, if necessary, and starts a Timer to check if needed to resize
+ * the bounds
+ */
+ protected void startTimer()
+ {
+ if (timer == null)
+ {
+ timer = new Timer(200, this);
+ timer.setRepeats(true);
+ }
+ timer.start();
+ }
+
+ /**
+ * Returns the JScrollPane housing the JTree, or null if one isn't found.
+ *
+ * @return JScrollPane housing the JTree, or null if one isn't found.
+ */
+ protected JScrollPane getScrollPane()
+ {
+ JScrollPane found = null;
+ Component p = tree.getParent();
+ while (p != null && !(p instanceof JScrollPane))
+ p = p.getParent();
+ if (p instanceof JScrollPane)
+ found = (JScrollPane) p;
+ return found;
+ }
+
+ /**
+ * Public as a result of Timer. If the scrollBar is null, or not adjusting,
+ * this stops the timer and updates the sizing.
+ *
+ * @param ae is the action performed
+ */
+ public void actionPerformed(ActionEvent ae)
+ {
+ if (scrollBar == null || !scrollBar.getValueIsAdjusting())
+ {
+ if (timer != null)
+ timer.stop();
+ updateSize();
+ timer = null;
+ scrollBar = null;
+ }
+ }
+ }
+
+ /**
+ * Listener responsible for getting cell editing events and updating the tree
+ * accordingly.
+ */
+ public class CellEditorHandler
+ implements CellEditorListener
+ {
+ /**
+ * Constructor
+ */
+ public CellEditorHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Messaged when editing has stopped in the tree. Tells the listeners
+ * editing has stopped.
+ *
+ * @param e is the notification event
+ */
+ public void editingStopped(ChangeEvent e)
+ {
+ completeEditing(false, false, true);
+ }
+
+ /**
+ * Messaged when editing has been canceled in the tree. This tells the
+ * listeners the editor has canceled editing.
+ *
+ * @param e is the notification event
+ */
+ public void editingCanceled(ChangeEvent e)
+ {
+ completeEditing(false, false, false);
+ }
+ } // CellEditorHandler
+
+ /**
+ * Repaints the lead selection row when focus is lost/grained.
+ */
+ public class FocusHandler
+ implements FocusListener
+ {
+ /**
+ * Constructor
+ */
+ public FocusHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when focus is activated on the tree we're in, redraws the lead
+ * row. Invoked when a component gains the keyboard focus. The method
+ * repaints the lead row that is shown differently when the tree is in
+ * focus.
+ *
+ * @param e is the focus event that is activated
+ */
+ public void focusGained(FocusEvent e)
+ {
+ repaintLeadRow();
+ }
+
+ /**
+ * Invoked when focus is deactivated on the tree we're in, redraws the lead
+ * row. Invoked when a component loses the keyboard focus. The method
+ * repaints the lead row that is shown differently when the tree is in
+ * focus.
+ *
+ * @param e is the focus event that is deactivated
+ */
+ public void focusLost(FocusEvent e)
+ {
+ repaintLeadRow();
+ }
+
+ /**
+ * Repaint the lead row.
+ */
+ void repaintLeadRow()
+ {
+ TreePath lead = tree.getLeadSelectionPath();
+ if (lead != null)
+ tree.repaint(tree.getPathBounds(lead));
+ }
+ }
+
+ /**
+ * This is used to get multiple key down events to appropriately genereate
+ * events.
+ */
+ public class KeyHandler
+ extends KeyAdapter
+ {
+ /** Key code that is being generated for. */
+ protected Action repeatKeyAction;
+
+ /** Set to true while keyPressed is active */
+ protected boolean isKeyDown;
+
+ /**
+ * Constructor
+ */
+ public KeyHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a key has been typed. Moves the keyboard focus to the first
+ * element whose first letter matches the alphanumeric key pressed by the
+ * user. Subsequent same key presses move the keyboard focus to the next
+ * object that starts with the same letter.
+ *
+ * @param e the key typed
+ */
+ public void keyTyped(KeyEvent e)
+ {
+ char typed = Character.toLowerCase(e.getKeyChar());
+ for (int row = tree.getLeadSelectionRow() + 1;
+ row < tree.getRowCount(); row++)
+ {
+ if (checkMatch(row, typed))
+ {
+ tree.setSelectionRow(row);
+ tree.scrollRowToVisible(row);
+ return;
+ }
+ }
+
+ // Not found below, search above:
+ for (int row = 0; row < tree.getLeadSelectionRow(); row++)
+ {
+ if (checkMatch(row, typed))
+ {
+ tree.setSelectionRow(row);
+ tree.scrollRowToVisible(row);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Check if the given tree row starts with this character
+ *
+ * @param row the tree row
+ * @param typed the typed char, must be converted to lowercase
+ * @return true if the given tree row starts with this character
+ */
+ boolean checkMatch(int row, char typed)
+ {
+ TreePath path = treeState.getPathForRow(row);
+ String node = path.getLastPathComponent().toString();
+ if (node.length() > 0)
+ {
+ char x = node.charAt(0);
+ if (typed == Character.toLowerCase(x))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Invoked when a key has been pressed.
+ *
+ * @param e the key pressed
+ */
+ public void keyPressed(KeyEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a key has been released
+ *
+ * @param e the key released
+ */
+ public void keyReleased(KeyEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * MouseListener is responsible for updating the selection based on mouse
+ * events.
+ */
+ public class MouseHandler
+ extends MouseAdapter
+ implements MouseMotionListener
+ {
+
+ /**
+ * If the cell has been selected on mouse press.
+ */
+ private boolean selectedOnPress;
+
+ /**
+ * Constructor
+ */
+ public MouseHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a mouse button has been pressed on a component.
+ *
+ * @param e is the mouse event that occured
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (! e.isConsumed())
+ {
+ handleEvent(e);
+ selectedOnPress = true;
+ }
+ else
+ {
+ selectedOnPress = false;
+ }
+ }
+
+ /**
+ * Invoked when a mouse button is pressed on a component and then dragged.
+ * MOUSE_DRAGGED events will continue to be delivered to the component where
+ * the drag originated until the mouse button is released (regardless of
+ * whether the mouse position is within the bounds of the component).
+ *
+ * @param e is the mouse event that occured
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when the mouse button has been moved on a component (with no
+ * buttons no down).
+ *
+ * @param e the mouse event that occured
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a mouse button has been released on a component.
+ *
+ * @param e is the mouse event that occured
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (! e.isConsumed() && ! selectedOnPress)
+ handleEvent(e);
+ }
+
+ /**
+ * Handles press and release events.
+ *
+ * @param e the mouse event
+ */
+ private void handleEvent(MouseEvent e)
+ {
+ if (tree != null && tree.isEnabled())
+ {
+ // Maybe stop editing.
+ if (isEditing(tree) && tree.getInvokesStopCellEditing()
+ && ! stopEditing(tree))
+ return;
+
+ // Explicitly request focus.
+ tree.requestFocusInWindow();
+
+ int x = e.getX();
+ int y = e.getY();
+ TreePath path = getClosestPathForLocation(tree, x, y);
+ if (path != null)
+ {
+ Rectangle b = getPathBounds(tree, path);
+ if (y <= b.y + b.height)
+ {
+ if (SwingUtilities.isLeftMouseButton(e))
+ checkForClickInExpandControl(path, x, y);
+ if (x > b.x && x <= b.x + b.width)
+ {
+ if (! startEditing(path, e))
+ selectPathForEvent(path, e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * MouseInputHandler handles passing all mouse events, including mouse motion
+ * events, until the mouse is released to the destination it is constructed
+ * with.
+ */
+ public class MouseInputHandler
+ implements MouseInputListener
+ {
+ /** Source that events are coming from */
+ protected Component source;
+
+ /** Destination that receives all events. */
+ protected Component destination;
+
+ /**
+ * Constructor
+ *
+ * @param source that events are coming from
+ * @param destination that receives all events
+ * @param e is the event received
+ */
+ public MouseInputHandler(Component source, Component destination,
+ MouseEvent e)
+ {
+ this.source = source;
+ this.destination = destination;
+ source.addMouseListener(this);
+ source.addMouseMotionListener(this);
+ dispatch(e);
+ }
+
+ /**
+ * Invoked when the mouse button has been clicked (pressed and released) on
+ * a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ dispatch(e);
+ }
+
+ /**
+ * Invoked when a mouse button has been pressed on a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a mouse button has been released on a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ dispatch(e);
+ removeFromSource();
+ }
+
+ /**
+ * Invoked when the mouse enters a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ if (! SwingUtilities.isLeftMouseButton(e))
+ removeFromSource();
+ }
+
+ /**
+ * Invoked when the mouse exits a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ if (! SwingUtilities.isLeftMouseButton(e))
+ removeFromSource();
+ }
+
+ /**
+ * Invoked when a mouse button is pressed on a component and then dragged.
+ * MOUSE_DRAGGED events will continue to be delivered to the component where
+ * the drag originated until the mouse button is released (regardless of
+ * whether the mouse position is within the bounds of the component).
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ dispatch(e);
+ }
+
+ /**
+ * Invoked when the mouse cursor has been moved onto a component but no
+ * buttons have been pushed.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ removeFromSource();
+ }
+
+ /**
+ * Removes event from the source
+ */
+ protected void removeFromSource()
+ {
+ if (source != null)
+ {
+ source.removeMouseListener(this);
+ source.removeMouseMotionListener(this);
+ }
+ source = null;
+ destination = null;
+ }
+
+ /**
+ * Redispatches mouse events to the destination.
+ *
+ * @param e the mouse event to redispatch
+ */
+ private void dispatch(MouseEvent e)
+ {
+ if (destination != null)
+ {
+ MouseEvent e2 = SwingUtilities.convertMouseEvent(source, e,
+ destination);
+ destination.dispatchEvent(e2);
+ }
+ }
+ }
+
+ /**
+ * Class responsible for getting size of node, method is forwarded to
+ * BasicTreeUI method. X location does not include insets, that is handled in
+ * getPathBounds.
+ */
+ public class NodeDimensionsHandler
+ extends AbstractLayoutCache.NodeDimensions
+ {
+ /**
+ * Constructor
+ */
+ public NodeDimensionsHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns, by reference in bounds, the size and x origin to place value at.
+ * The calling method is responsible for determining the Y location. If
+ * bounds is null, a newly created Rectangle should be returned, otherwise
+ * the value should be placed in bounds and returned.
+ *
+ * @param cell the value to be represented
+ * @param row row being queried
+ * @param depth the depth of the row
+ * @param expanded true if row is expanded
+ * @param size a Rectangle containing the size needed to represent value
+ * @return containing the node dimensions, or null if node has no dimension
+ */
+ public Rectangle getNodeDimensions(Object cell, int row, int depth,
+ boolean expanded, Rectangle size)
+ {
+ Dimension prefSize;
+ if (editingComponent != null && editingRow == row)
+ {
+ // Editing, ask editor for preferred size.
+ prefSize = editingComponent.getPreferredSize();
+ int rowHeight = getRowHeight();
+ if (rowHeight > 0 && rowHeight != prefSize.height)
+ prefSize.height = rowHeight;
+ }
+ else
+ {
+ // Not editing, ask renderer for preferred size.
+ Component rend =
+ currentCellRenderer.getTreeCellRendererComponent(tree, cell,
+ tree.isRowSelected(row),
+ expanded,
+ treeModel.isLeaf(cell),
+ row, false);
+ // Make sure the layout is valid.
+ rendererPane.add(rend);
+ rend.validate();
+ prefSize = rend.getPreferredSize();
+ }
+ if (size != null)
+ {
+ size.x = getRowX(row, depth);
+ // FIXME: This should be handled by the layout cache.
+ size.y = prefSize.height * row;
+ size.width = prefSize.width;
+ size.height = prefSize.height;
+ }
+ else
+ // FIXME: The y should be handled by the layout cache.
+ size = new Rectangle(getRowX(row, depth), prefSize.height * row, prefSize.width,
+ prefSize.height);
+
+ return size;
+ }
+
+ /**
+ * Returns the amount to indent the given row
+ *
+ * @return amount to indent the given row.
+ */
+ protected int getRowX(int row, int depth)
+ {
+ return BasicTreeUI.this.getRowX(row, depth);
+ }
+ } // NodeDimensionsHandler
+
+ /**
+ * PropertyChangeListener for the tree. Updates the appropriate variable, or
+ * TreeState, based on what changes.
+ */
+ public class PropertyChangeHandler
+ implements PropertyChangeListener
+ {
+
+ /**
+ * Constructor
+ */
+ public PropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method gets called when a bound property is changed.
+ *
+ * @param event A PropertyChangeEvent object describing the event source and
+ * the property that has changed.
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ String property = event.getPropertyName();
+ if (property.equals(JTree.ROOT_VISIBLE_PROPERTY))
+ {
+ validCachedPreferredSize = false;
+ treeState.setRootVisible(tree.isRootVisible());
+ tree.repaint();
+ }
+ else if (property.equals(JTree.SELECTION_MODEL_PROPERTY))
+ {
+ treeSelectionModel = tree.getSelectionModel();
+ treeSelectionModel.setRowMapper(treeState);
+ }
+ else if (property.equals(JTree.TREE_MODEL_PROPERTY))
+ {
+ setModel(tree.getModel());
+ }
+ else if (property.equals(JTree.CELL_RENDERER_PROPERTY))
+ {
+ setCellRenderer(tree.getCellRenderer());
+ // Update layout.
+ if (treeState != null)
+ treeState.invalidateSizes();
+ }
+ else if (property.equals(JTree.EDITABLE_PROPERTY))
+ setEditable(((Boolean) event.getNewValue()).booleanValue());
+
+ }
+ }
+
+ /**
+ * Listener on the TreeSelectionModel, resets the row selection if any of the
+ * properties of the model change.
+ */
+ public class SelectionModelPropertyChangeHandler
+ implements PropertyChangeListener
+ {
+
+ /**
+ * Constructor
+ */
+ public SelectionModelPropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method gets called when a bound property is changed.
+ *
+ * @param event A PropertyChangeEvent object describing the event source and
+ * the property that has changed.
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ treeSelectionModel.resetRowSelection();
+ }
+ }
+
+ /**
+ * The action to cancel editing on this tree.
+ */
+ public class TreeCancelEditingAction
+ extends AbstractAction
+ {
+ /**
+ * Creates the new tree cancel editing action.
+ *
+ * @param name the name of the action (used in toString).
+ */
+ public TreeCancelEditingAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Invoked when an action occurs, cancels the cell editing (if the
+ * tree cell is being edited).
+ *
+ * @param e event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (isEnabled() && tree.isEditing())
+ tree.cancelEditing();
+ }
+ }
+
+ /**
+ * Updates the TreeState in response to nodes expanding/collapsing.
+ */
+ public class TreeExpansionHandler
+ implements TreeExpansionListener
+ {
+
+ /**
+ * Constructor
+ */
+ public TreeExpansionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Called whenever an item in the tree has been expanded.
+ *
+ * @param event is the event that occured
+ */
+ public void treeExpanded(TreeExpansionEvent event)
+ {
+ validCachedPreferredSize = false;
+ treeState.setExpandedState(event.getPath(), true);
+ // The maximal cell height may change
+ maxHeight = 0;
+ tree.revalidate();
+ tree.repaint();
+ }
+
+ /**
+ * Called whenever an item in the tree has been collapsed.
+ *
+ * @param event is the event that occured
+ */
+ public void treeCollapsed(TreeExpansionEvent event)
+ {
+ completeEditing();
+ validCachedPreferredSize = false;
+ treeState.setExpandedState(event.getPath(), false);
+ // The maximal cell height may change
+ maxHeight = 0;
+ tree.revalidate();
+ tree.repaint();
+ }
+ } // TreeExpansionHandler
+
+ /**
+ * TreeHomeAction is used to handle end/home actions. Scrolls either the first
+ * or last cell to be visible based on direction.
+ */
+ public class TreeHomeAction
+ extends AbstractAction
+ {
+
+ /** The direction, either home or end */
+ protected int direction;
+
+ /**
+ * Creates a new TreeHomeAction instance.
+ *
+ * @param dir the direction to go to, <code>-1</code> for home,
+ * <code>1</code> for end
+ * @param name the name of the action
+ */
+ public TreeHomeAction(int dir, String name)
+ {
+ direction = dir;
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e is the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (tree != null)
+ {
+ String command = (String) getValue(Action.NAME);
+ if (command.equals("selectFirst"))
+ {
+ ensureRowsAreVisible(0, 0);
+ tree.setSelectionInterval(0, 0);
+ }
+ if (command.equals("selectFirstChangeLead"))
+ {
+ ensureRowsAreVisible(0, 0);
+ tree.setLeadSelectionPath(getPathForRow(tree, 0));
+ }
+ if (command.equals("selectFirstExtendSelection"))
+ {
+ ensureRowsAreVisible(0, 0);
+ TreePath anchorPath = tree.getAnchorSelectionPath();
+ if (anchorPath == null)
+ tree.setSelectionInterval(0, 0);
+ else
+ {
+ int anchorRow = getRowForPath(tree, anchorPath);
+ tree.setSelectionInterval(0, anchorRow);
+ tree.setAnchorSelectionPath(anchorPath);
+ tree.setLeadSelectionPath(getPathForRow(tree, 0));
+ }
+ }
+ else if (command.equals("selectLast"))
+ {
+ int end = getRowCount(tree) - 1;
+ ensureRowsAreVisible(end, end);
+ tree.setSelectionInterval(end, end);
+ }
+ else if (command.equals("selectLastChangeLead"))
+ {
+ int end = getRowCount(tree) - 1;
+ ensureRowsAreVisible(end, end);
+ tree.setLeadSelectionPath(getPathForRow(tree, end));
+ }
+ else if (command.equals("selectLastExtendSelection"))
+ {
+ int end = getRowCount(tree) - 1;
+ ensureRowsAreVisible(end, end);
+ TreePath anchorPath = tree.getAnchorSelectionPath();
+ if (anchorPath == null)
+ tree.setSelectionInterval(end, end);
+ else
+ {
+ int anchorRow = getRowForPath(tree, anchorPath);
+ tree.setSelectionInterval(end, anchorRow);
+ tree.setAnchorSelectionPath(anchorPath);
+ tree.setLeadSelectionPath(getPathForRow(tree, end));
+ }
+ }
+ }
+
+ // Ensure that the lead path is visible after the increment action.
+ tree.scrollPathToVisible(tree.getLeadSelectionPath());
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ }
+
+ /**
+ * TreeIncrementAction is used to handle up/down actions. Selection is moved
+ * up or down based on direction.
+ */
+ public class TreeIncrementAction
+ extends AbstractAction
+ {
+
+ /**
+ * Specifies the direction to adjust the selection by.
+ */
+ protected int direction;
+
+ /**
+ * Creates a new TreeIncrementAction.
+ *
+ * @param dir up or down, <code>-1</code> for up, <code>1</code> for down
+ * @param name is the name of the direction
+ */
+ public TreeIncrementAction(int dir, String name)
+ {
+ direction = dir;
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e is the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ TreePath currentPath = tree.getLeadSelectionPath();
+ int currentRow;
+
+ if (currentPath != null)
+ currentRow = treeState.getRowForPath(currentPath);
+ else
+ currentRow = 0;
+
+ int rows = treeState.getRowCount();
+
+ int nextRow = currentRow + 1;
+ int prevRow = currentRow - 1;
+ boolean hasNext = nextRow < rows;
+ boolean hasPrev = prevRow >= 0 && rows > 0;
+ TreePath newPath;
+ String command = (String) getValue(Action.NAME);
+
+ if (command.equals("selectPreviousChangeLead") && hasPrev)
+ {
+ newPath = treeState.getPathForRow(prevRow);
+ tree.setSelectionPath(newPath);
+ tree.setAnchorSelectionPath(newPath);
+ tree.setLeadSelectionPath(newPath);
+ }
+ else if (command.equals("selectPreviousExtendSelection") && hasPrev)
+ {
+ newPath = treeState.getPathForRow(prevRow);
+
+ // If the new path is already selected, the selection shrinks,
+ // unselecting the previously current path.
+ if (tree.isPathSelected(newPath))
+ tree.getSelectionModel().removeSelectionPath(currentPath);
+
+ // This must be called in any case because it updates the model
+ // lead selection index.
+ tree.addSelectionPath(newPath);
+ tree.setLeadSelectionPath(newPath);
+ }
+ else if (command.equals("selectPrevious") && hasPrev)
+ {
+ newPath = treeState.getPathForRow(prevRow);
+ tree.setSelectionPath(newPath);
+ }
+ else if (command.equals("selectNext") && hasNext)
+ {
+ newPath = treeState.getPathForRow(nextRow);
+ tree.setSelectionPath(newPath);
+ }
+ else if (command.equals("selectNextExtendSelection") && hasNext)
+ {
+ newPath = treeState.getPathForRow(nextRow);
+
+ // If the new path is already selected, the selection shrinks,
+ // unselecting the previously current path.
+ if (tree.isPathSelected(newPath))
+ tree.getSelectionModel().removeSelectionPath(currentPath);
+
+ // This must be called in any case because it updates the model
+ // lead selection index.
+ tree.addSelectionPath(newPath);
+
+ tree.setLeadSelectionPath(newPath);
+ }
+ else if (command.equals("selectNextChangeLead") && hasNext)
+ {
+ newPath = treeState.getPathForRow(nextRow);
+ tree.setSelectionPath(newPath);
+ tree.setAnchorSelectionPath(newPath);
+ tree.setLeadSelectionPath(newPath);
+ }
+
+ // Ensure that the lead path is visible after the increment action.
+ tree.scrollPathToVisible(tree.getLeadSelectionPath());
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ }
+
+ /**
+ * Forwards all TreeModel events to the TreeState.
+ */
+ public class TreeModelHandler
+ implements TreeModelListener
+ {
+ /**
+ * Constructor
+ */
+ public TreeModelHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked after a node (or a set of siblings) has changed in some way. The
+ * node(s) have not changed locations in the tree or altered their children
+ * arrays, but other attributes have changed and may affect presentation.
+ * Example: the name of a file has changed, but it is in the same location
+ * in the file system. To indicate the root has changed, childIndices and
+ * children will be null. Use e.getPath() to get the parent of the changed
+ * node(s). e.getChildIndices() returns the index(es) of the changed
+ * node(s).
+ *
+ * @param e is the event that occured
+ */
+ public void treeNodesChanged(TreeModelEvent e)
+ {
+ validCachedPreferredSize = false;
+ treeState.treeNodesChanged(e);
+ tree.repaint();
+ }
+
+ /**
+ * Invoked after nodes have been inserted into the tree. Use e.getPath() to
+ * get the parent of the new node(s). e.getChildIndices() returns the
+ * index(es) of the new node(s) in ascending order.
+ *
+ * @param e is the event that occured
+ */
+ public void treeNodesInserted(TreeModelEvent e)
+ {
+ validCachedPreferredSize = false;
+ treeState.treeNodesInserted(e);
+ tree.repaint();
+ }
+
+ /**
+ * Invoked after nodes have been removed from the tree. Note that if a
+ * subtree is removed from the tree, this method may only be invoked once
+ * for the root of the removed subtree, not once for each individual set of
+ * siblings removed. Use e.getPath() to get the former parent of the deleted
+ * node(s). e.getChildIndices() returns, in ascending order, the index(es)
+ * the node(s) had before being deleted.
+ *
+ * @param e is the event that occured
+ */
+ public void treeNodesRemoved(TreeModelEvent e)
+ {
+ validCachedPreferredSize = false;
+ treeState.treeNodesRemoved(e);
+ tree.repaint();
+ }
+
+ /**
+ * Invoked after the tree has drastically changed structure from a given
+ * node down. If the path returned by e.getPath() is of length one and the
+ * first element does not identify the current root node the first element
+ * should become the new root of the tree. Use e.getPath() to get the path
+ * to the node. e.getChildIndices() returns null.
+ *
+ * @param e is the event that occured
+ */
+ public void treeStructureChanged(TreeModelEvent e)
+ {
+ if (e.getPath().length == 1
+ && ! e.getPath()[0].equals(treeModel.getRoot()))
+ tree.expandPath(new TreePath(treeModel.getRoot()));
+ validCachedPreferredSize = false;
+ treeState.treeStructureChanged(e);
+ tree.repaint();
+ }
+ } // TreeModelHandler
+
+ /**
+ * TreePageAction handles page up and page down events.
+ */
+ public class TreePageAction
+ extends AbstractAction
+ {
+ /** Specifies the direction to adjust the selection by. */
+ protected int direction;
+
+ /**
+ * Constructor
+ *
+ * @param direction up or down
+ * @param name is the name of the direction
+ */
+ public TreePageAction(int direction, String name)
+ {
+ this.direction = direction;
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e is the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ String command = (String) getValue(Action.NAME);
+ boolean extendSelection = command.equals("scrollUpExtendSelection")
+ || command.equals("scrollDownExtendSelection");
+ boolean changeSelection = command.equals("scrollUpChangeSelection")
+ || command.equals("scrollDownChangeSelection");
+
+ // Disable change lead, unless we are in discontinuous mode.
+ if (!extendSelection && !changeSelection
+ && tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
+ {
+ changeSelection = true;
+ }
+
+ int rowCount = getRowCount(tree);
+ if (rowCount > 0 && treeSelectionModel != null)
+ {
+ Dimension maxSize = tree.getSize();
+ TreePath lead = tree.getLeadSelectionPath();
+ TreePath newPath = null;
+ Rectangle visible = tree.getVisibleRect();
+ if (direction == -1) // The RI handles -1 as up.
+ {
+ newPath = getClosestPathForLocation(tree, visible.x, visible.y);
+ if (newPath.equals(lead)) // Corner case, adjust one page up.
+ {
+ visible.y = Math.max(0, visible.y - visible.height);
+ newPath = getClosestPathForLocation(tree, visible.x,
+ visible.y);
+ }
+ }
+ else // +1 is down.
+ {
+ visible.y = Math.min(maxSize.height,
+ visible.y + visible.height - 1);
+ newPath = getClosestPathForLocation(tree, visible.x, visible.y);
+ if (newPath.equals(lead)) // Corner case, adjust one page down.
+ {
+ visible.y = Math.min(maxSize.height,
+ visible.y + visible.height - 1);
+ newPath = getClosestPathForLocation(tree, visible.x,
+ visible.y);
+ }
+ }
+
+ // Determine new visible rect.
+ Rectangle newVisible = getPathBounds(tree, newPath);
+ newVisible.x = visible.x;
+ newVisible.width = visible.width;
+ if (direction == -1)
+ {
+ newVisible.height = visible.height;
+ }
+ else
+ {
+ newVisible.y -= visible.height - newVisible.height;
+ newVisible.height = visible.height;
+ }
+
+ if (extendSelection)
+ {
+ // Extend selection.
+ TreePath anchorPath = tree.getAnchorSelectionPath();
+ if (anchorPath == null)
+ {
+ tree.setSelectionPath(newPath);
+ }
+ else
+ {
+ int newIndex = getRowForPath(tree, newPath);
+ int anchorIndex = getRowForPath(tree, anchorPath);
+ tree.setSelectionInterval(Math.min(anchorIndex, newIndex),
+ Math.max(anchorIndex, newIndex));
+ tree.setAnchorSelectionPath(anchorPath);
+ tree.setLeadSelectionPath(newPath);
+ }
+ }
+ else if (changeSelection)
+ {
+ tree.setSelectionPath(newPath);
+ }
+ else // Change lead.
+ {
+ tree.setLeadSelectionPath(newPath);
+ }
+
+ tree.scrollRectToVisible(newVisible);
+ }
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ } // TreePageAction
+
+ /**
+ * Listens for changes in the selection model and updates the display
+ * accordingly.
+ */
+ public class TreeSelectionHandler
+ implements TreeSelectionListener
+ {
+ /**
+ * Constructor
+ */
+ public TreeSelectionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Messaged when the selection changes in the tree we're displaying for.
+ * Stops editing, messages super and displays the changed paths.
+ *
+ * @param event the event that characterizes the change.
+ */
+ public void valueChanged(TreeSelectionEvent event)
+ {
+ completeEditing();
+
+ TreePath op = event.getOldLeadSelectionPath();
+ TreePath np = event.getNewLeadSelectionPath();
+
+ // Repaint of the changed lead selection path.
+ if (op != np)
+ {
+ Rectangle o = treeState.getBounds(event.getOldLeadSelectionPath(),
+ new Rectangle());
+ Rectangle n = treeState.getBounds(event.getNewLeadSelectionPath(),
+ new Rectangle());
+
+ if (o != null)
+ tree.repaint(o);
+ if (n != null)
+ tree.repaint(n);
+ }
+ }
+ } // TreeSelectionHandler
+
+ /**
+ * For the first selected row expandedness will be toggled.
+ */
+ public class TreeToggleAction
+ extends AbstractAction
+ {
+ /**
+ * Creates a new TreeToggleAction.
+ *
+ * @param name is the name of <code>Action</code> field
+ */
+ public TreeToggleAction(String name)
+ {
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ int selected = tree.getLeadSelectionRow();
+ if (selected != -1 && isLeaf(selected))
+ {
+ TreePath anchorPath = tree.getAnchorSelectionPath();
+ TreePath leadPath = tree.getLeadSelectionPath();
+ toggleExpandState(getPathForRow(tree, selected));
+ // Need to do this, so that the toggling doesn't mess up the lead
+ // and anchor.
+ tree.setLeadSelectionPath(leadPath);
+ tree.setAnchorSelectionPath(anchorPath);
+
+ // Ensure that the lead path is visible after the increment action.
+ tree.scrollPathToVisible(tree.getLeadSelectionPath());
+ }
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled, false otherwise
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ } // TreeToggleAction
+
+ /**
+ * TreeTraverseAction is the action used for left/right keys. Will toggle the
+ * expandedness of a node, as well as potentially incrementing the selection.
+ */
+ public class TreeTraverseAction
+ extends AbstractAction
+ {
+ /**
+ * Determines direction to traverse, 1 means expand, -1 means collapse.
+ */
+ protected int direction;
+
+ /**
+ * Constructor
+ *
+ * @param direction to traverse
+ * @param name is the name of the direction
+ */
+ public TreeTraverseAction(int direction, String name)
+ {
+ this.direction = direction;
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ TreePath current = tree.getLeadSelectionPath();
+ if (current == null)
+ return;
+
+ String command = (String) getValue(Action.NAME);
+ if (command.equals("selectParent"))
+ {
+ if (current == null)
+ return;
+
+ if (tree.isExpanded(current))
+ {
+ tree.collapsePath(current);
+ }
+ else
+ {
+ // If the node is not expanded (also, if it is a leaf node),
+ // we just select the parent. We do not select the root if it
+ // is not visible.
+ TreePath parent = current.getParentPath();
+ if (parent != null &&
+ ! (parent.getPathCount() == 1 && ! tree.isRootVisible()))
+ tree.setSelectionPath(parent);
+ }
+ }
+ else if (command.equals("selectChild"))
+ {
+ Object node = current.getLastPathComponent();
+ int nc = treeModel.getChildCount(node);
+ if (nc == 0 || treeState.isExpanded(current))
+ {
+ // If the node is leaf or it is already expanded,
+ // we just select the next row.
+ int nextRow = tree.getLeadSelectionRow() + 1;
+ if (nextRow <= tree.getRowCount())
+ tree.setSelectionRow(nextRow);
+ }
+ else
+ {
+ tree.expandPath(current);
+ }
+ }
+
+ // Ensure that the lead path is visible after the increment action.
+ tree.scrollPathToVisible(tree.getLeadSelectionPath());
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled, false otherwise
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ }
+
+ /**
+ * Returns true if the LookAndFeel implements the control icons. Package
+ * private for use in inner classes.
+ *
+ * @returns true if there are control icons
+ */
+ boolean hasControlIcons()
+ {
+ if (expandedIcon != null || collapsedIcon != null)
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns control icon. It is null if the LookAndFeel does not implements the
+ * control icons. Package private for use in inner classes.
+ *
+ * @return control icon if it exists.
+ */
+ Icon getCurrentControlIcon(TreePath path)
+ {
+ if (hasControlIcons())
+ {
+ if (tree.isExpanded(path))
+ return expandedIcon;
+ else
+ return collapsedIcon;
+ }
+ else
+ {
+ if (nullIcon == null)
+ nullIcon = new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 0;
+ }
+
+ public int getIconWidth()
+ {
+ return 0;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // No action here.
+ }
+ };
+ return nullIcon;
+ }
+ }
+
+ /**
+ * Returns the parent of the current node
+ *
+ * @param root is the root of the tree
+ * @param node is the current node
+ * @return is the parent of the current node
+ */
+ Object getParent(Object root, Object node)
+ {
+ if (root == null || node == null || root.equals(node))
+ return null;
+
+ if (node instanceof TreeNode)
+ return ((TreeNode) node).getParent();
+ return findNode(root, node);
+ }
+
+ /**
+ * Recursively checks the tree for the specified node, starting at the root.
+ *
+ * @param root is starting node to start searching at.
+ * @param node is the node to search for
+ * @return the parent node of node
+ */
+ private Object findNode(Object root, Object node)
+ {
+ if (! treeModel.isLeaf(root) && ! root.equals(node))
+ {
+ int size = treeModel.getChildCount(root);
+ for (int j = 0; j < size; j++)
+ {
+ Object child = treeModel.getChild(root, j);
+ if (node.equals(child))
+ return root;
+
+ Object n = findNode(child, node);
+ if (n != null)
+ return n;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Selects the specified path in the tree depending on modes. Package private
+ * for use in inner classes.
+ *
+ * @param tree is the tree we are selecting the path in
+ * @param path is the path we are selecting
+ */
+ void selectPath(JTree tree, TreePath path)
+ {
+ if (path != null)
+ {
+ tree.setSelectionPath(path);
+ tree.setLeadSelectionPath(path);
+ tree.makeVisible(path);
+ tree.scrollPathToVisible(path);
+ }
+ }
+
+ /**
+ * Returns the path from node to the root. Package private for use in inner
+ * classes.
+ *
+ * @param node the node to get the path to
+ * @param depth the depth of the tree to return a path for
+ * @return an array of tree nodes that represent the path to node.
+ */
+ Object[] getPathToRoot(Object node, int depth)
+ {
+ if (node == null)
+ {
+ if (depth == 0)
+ return null;
+
+ return new Object[depth];
+ }
+
+ Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node),
+ depth + 1);
+ path[path.length - depth - 1] = node;
+ return path;
+ }
+
+ /**
+ * Draws a vertical line using the given graphic context
+ *
+ * @param g is the graphic context
+ * @param c is the component the new line will belong to
+ * @param x is the horizonal position
+ * @param top specifies the top of the line
+ * @param bottom specifies the bottom of the line
+ */
+ protected void paintVerticalLine(Graphics g, JComponent c, int x, int top,
+ int bottom)
+ {
+ // FIXME: Check if drawing a dashed line or not.
+ g.setColor(getHashColor());
+ g.drawLine(x, top, x, bottom);
+ }
+
+ /**
+ * Draws a horizontal line using the given graphic context
+ *
+ * @param g is the graphic context
+ * @param c is the component the new line will belong to
+ * @param y is the vertical position
+ * @param left specifies the left point of the line
+ * @param right specifies the right point of the line
+ */
+ protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left,
+ int right)
+ {
+ // FIXME: Check if drawing a dashed line or not.
+ g.setColor(getHashColor());
+ g.drawLine(left, y, right, y);
+ }
+
+ /**
+ * Draws an icon at around a specific position
+ *
+ * @param c is the component the new line will belong to
+ * @param g is the graphic context
+ * @param icon is the icon which will be drawn
+ * @param x is the center position in x-direction
+ * @param y is the center position in y-direction
+ */
+ protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y)
+ {
+ x -= icon.getIconWidth() / 2;
+ y -= icon.getIconHeight() / 2;
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ icon.paintIcon(c, g, x, y);
+ }
+
+ /**
+ * Draws a dashed horizontal line.
+ *
+ * @param g - the graphics configuration.
+ * @param y - the y location to start drawing at
+ * @param x1 - the x location to start drawing at
+ * @param x2 - the x location to finish drawing at
+ */
+ protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2)
+ {
+ g.setColor(getHashColor());
+ for (int i = x1; i < x2; i += 2)
+ g.drawLine(i, y, i + 1, y);
+ }
+
+ /**
+ * Draws a dashed vertical line.
+ *
+ * @param g - the graphics configuration.
+ * @param x - the x location to start drawing at
+ * @param y1 - the y location to start drawing at
+ * @param y2 - the y location to finish drawing at
+ */
+ protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2)
+ {
+ g.setColor(getHashColor());
+ for (int i = y1; i < y2; i += 2)
+ g.drawLine(x, i, x, i + 1);
+ }
+
+ /**
+ * Paints the expand (toggle) part of a row. The receiver should NOT modify
+ * clipBounds, or insets.
+ *
+ * @param g - the graphics configuration
+ * @param clipBounds -
+ * @param insets -
+ * @param bounds - bounds of expand control
+ * @param path - path to draw control for
+ * @param row - row to draw control for
+ * @param isExpanded - is the row expanded
+ * @param hasBeenExpanded - has the row already been expanded
+ * @param isLeaf - is the path a leaf
+ */
+ protected void paintExpandControl(Graphics g, Rectangle clipBounds,
+ Insets insets, Rectangle bounds,
+ TreePath path, int row, boolean isExpanded,
+ boolean hasBeenExpanded, boolean isLeaf)
+ {
+ if (shouldPaintExpandControl(path, row, isExpanded, hasBeenExpanded, isLeaf))
+ {
+ Icon icon = getCurrentControlIcon(path);
+ int iconW = icon.getIconWidth();
+ int x = bounds.x - iconW - gap;
+ icon.paintIcon(tree, g, x, bounds.y + bounds.height / 2
+ - icon.getIconHeight() / 2);
+ }
+ }
+
+ /**
+ * Paints the horizontal part of the leg. The receiver should NOT modify
+ * clipBounds, or insets. NOTE: parentRow can be -1 if the root is not
+ * visible.
+ *
+ * @param g - the graphics configuration
+ * @param clipBounds -
+ * @param insets -
+ * @param bounds - bounds of the cell
+ * @param path - path to draw leg for
+ * @param row - row to start drawing at
+ * @param isExpanded - is the row expanded
+ * @param hasBeenExpanded - has the row already been expanded
+ * @param isLeaf - is the path a leaf
+ */
+ protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
+ Insets insets, Rectangle bounds,
+ TreePath path, int row,
+ boolean isExpanded,
+ boolean hasBeenExpanded,
+ boolean isLeaf)
+ {
+ if (row != 0)
+ {
+ paintHorizontalLine(g, tree, bounds.y + bounds.height / 2,
+ bounds.x - leftChildIndent - gap, bounds.x - gap);
+ }
+ }
+
+ /**
+ * Paints the vertical part of the leg. The receiver should NOT modify
+ * clipBounds, insets.
+ *
+ * @param g - the graphics configuration.
+ * @param clipBounds -
+ * @param insets -
+ * @param path - the path to draw the vertical part for.
+ */
+ protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
+ Insets insets, TreePath path)
+ {
+ Rectangle bounds = getPathBounds(tree, path);
+ TreePath parent = path.getParentPath();
+
+ boolean paintLine;
+ if (isRootVisible())
+ paintLine = parent != null;
+ else
+ paintLine = parent != null && parent.getPathCount() > 1;
+ if (paintLine)
+ {
+ Rectangle parentBounds = getPathBounds(tree, parent);
+ paintVerticalLine(g, tree, parentBounds.x + 2 * gap,
+ parentBounds.y + parentBounds.height / 2,
+ bounds.y + bounds.height / 2);
+ }
+ }
+
+ /**
+ * Paints the renderer part of a row. The receiver should NOT modify
+ * clipBounds, or insets.
+ *
+ * @param g - the graphics configuration
+ * @param clipBounds -
+ * @param insets -
+ * @param bounds - bounds of expand control
+ * @param path - path to draw control for
+ * @param row - row to draw control for
+ * @param isExpanded - is the row expanded
+ * @param hasBeenExpanded - has the row already been expanded
+ * @param isLeaf - is the path a leaf
+ */
+ protected void paintRow(Graphics g, Rectangle clipBounds, Insets insets,
+ Rectangle bounds, TreePath path, int row,
+ boolean isExpanded, boolean hasBeenExpanded,
+ boolean isLeaf)
+ {
+ boolean selected = tree.isPathSelected(path);
+ boolean hasIcons = false;
+ Object node = path.getLastPathComponent();
+
+ paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded,
+ hasBeenExpanded, isLeaf);
+
+ TreeCellRenderer dtcr = currentCellRenderer;
+
+ boolean focused = false;
+ if (treeSelectionModel != null)
+ focused = treeSelectionModel.getLeadSelectionRow() == row
+ && tree.isFocusOwner();
+
+ Component c = dtcr.getTreeCellRendererComponent(tree, node, selected,
+ isExpanded, isLeaf, row,
+ focused);
+
+ rendererPane.paintComponent(g, c, c.getParent(), bounds);
+ }
+
+ /**
+ * Prepares for the UI to uninstall.
+ */
+ protected void prepareForUIUninstall()
+ {
+ // Nothing to do here yet.
+ }
+
+ /**
+ * Returns true if the expand (toggle) control should be drawn for the
+ * specified row.
+ *
+ * @param path - current path to check for.
+ * @param row - current row to check for.
+ * @param isExpanded - true if the path is expanded
+ * @param hasBeenExpanded - true if the path has been expanded already
+ * @param isLeaf - true if the row is a lead
+ */
+ protected boolean shouldPaintExpandControl(TreePath path, int row,
+ boolean isExpanded,
+ boolean hasBeenExpanded,
+ boolean isLeaf)
+ {
+ Object node = path.getLastPathComponent();
+ return ! isLeaf && hasControlIcons();
+ }
+
+ /**
+ * Returns the amount to indent the given row
+ *
+ * @return amount to indent the given row.
+ */
+ protected int getRowX(int row, int depth)
+ {
+ return depth * totalChildIndent;
+ }
+} // BasicTreeUI
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java
new file mode 100644
index 000000000..11c7d6390
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java
@@ -0,0 +1,75 @@
+/* BasicViewportUI.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.LookAndFeel;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ViewportUI;
+
+public class BasicViewportUI extends ViewportUI
+{
+ protected void installDefaults(JComponent c)
+ {
+ c.setOpaque(true);
+ LookAndFeel.installColorsAndFont(c, "Viewport.background",
+ "Viewport.foreground", "Viewport.font");
+ }
+ protected void uninstallDefaults(JComponent c)
+ {
+ // TODO: Implement this properly.
+ }
+
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicViewportUI();
+ }
+
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ installDefaults(c);
+ }
+
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ uninstallDefaults(c);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/ComboPopup.java b/libjava/classpath/javax/swing/plaf/basic/ComboPopup.java
new file mode 100644
index 000000000..8bdcc51b0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/ComboPopup.java
@@ -0,0 +1,103 @@
+/* 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.event.KeyListener;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+import javax.swing.JList;
+
+public interface ComboPopup
+{
+ /**
+ * This method display popup menu containing list of JComboBox's items to
+ * the screen
+ */
+ void show();
+
+ /**
+ * This method hides popup menu with list of JComboBox's item from the
+ * screen
+ */
+ void hide();
+
+ /**
+ * Retursn true if popup menu with JComboBOx's item is currently visible on
+ * the screen and false otherwise
+ *
+ * @return true if JComboBox's popup menu with list of items is currently
+ * visible on the screen and false otherwise.
+ */
+ boolean isVisible();
+
+ /**
+ * Return JList that is used to draw cells of the JComboBox.
+ *
+ * @return JList that is used to draw cells of the JcomboBox
+ */
+ JList getList();
+
+ /**
+ * This method returns MouseListener that listen's to mouse events occuring
+ * in the combo box
+ *
+ * @return MouseListenere
+ */
+ MouseListener getMouseListener();
+
+ /**
+ * This method returns MouseListener that listen's to mouse events occuring
+ * in the combo box.
+ *
+ * @return MouseMotionListener
+ */
+ MouseMotionListener getMouseMotionListener();
+
+ /**
+ * This method returns KeyListener that listen's to key events occuring in
+ * the combo box.
+ *
+ * @return KeyListener
+ */
+ KeyListener getKeyListener();
+
+ /* This method removes any listeners that were installed */
+ void uninstallingUI();
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java b/libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java
new file mode 100644
index 000000000..9760e82a6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java
@@ -0,0 +1,78 @@
+/* DefaultMenuLayout.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Container;
+import java.awt.Dimension;
+
+import javax.swing.BoxLayout;
+import javax.swing.plaf.UIResource;
+
+/**
+ * The LayoutManager that is used in PopupMenus. This is a derived from
+ * {@link BoxLayout}.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class DefaultMenuLayout
+ extends BoxLayout
+ implements UIResource
+{
+
+ /**
+ * Creates a new instance of DefaultMenuLayout.
+ *
+ * @param target the component that is laid out
+ * @param axis the axis along which the component is laid out
+ */
+ public DefaultMenuLayout(Container target, int axis)
+ {
+ super(target, axis);
+ }
+
+ /**
+ * Returns the preferred size for the layout of the menu.
+ *
+ * @param target the Container which's preferred size we calculate
+ */
+ public Dimension preferredLayoutSize(Container target)
+ {
+ return super.preferredLayoutSize(target);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java b/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java
new file mode 100644
index 000000000..34278052b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java
@@ -0,0 +1,91 @@
+/* SharedUIDefaults.java -- Manages shared instances for UIDefaults
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.awt.Color;
+import java.util.HashMap;
+
+import javax.swing.UIManager;
+
+/**
+ * Manages shared instances for UI defaults. For example, all Swing components
+ * of one type usually share one InputMap/ActionMap pair. In order to avoid
+ * duplication of such objects we store them in a Map here.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class SharedUIDefaults
+{
+
+ /**
+ * Stores the shared instances, indexed by their UI names
+ * (i.e. "TextField.InputMap").
+ */
+ private static HashMap sharedDefaults = new HashMap();
+
+ /**
+ * Returns a shared UI defaults object.
+ *
+ * @param key the key for the shared object
+ *
+ * @return a shared UI defaults object for the specified key
+ */
+ static Object get(String key)
+ {
+ Object o = sharedDefaults.get(key);
+ if (o == null)
+ {
+ o = UIManager.get(key);
+ sharedDefaults.put(key, o);
+ }
+ return o;
+ }
+
+ /**
+ * Returns a shared UI color.
+ *
+ * @param key the key
+ *
+ * @return the shared color instance
+ */
+ static Color getColor(String key)
+ {
+ return (Color) get(key);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.png
new file mode 100644
index 000000000..daeb16c78
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.png
new file mode 100644
index 000000000..60f6a8abe
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.png
new file mode 100644
index 000000000..54047dcc1
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.png
new file mode 100644
index 000000000..7c8991106
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.png
new file mode 100644
index 000000000..a3841baac
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.png
new file mode 100644
index 000000000..13a9fa4e9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.png
new file mode 100644
index 000000000..a6408ec7e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.png
new file mode 100644
index 000000000..db283c29a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.png
new file mode 100644
index 000000000..65381bd8b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.png
new file mode 100644
index 000000000..c22763a97
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.png
new file mode 100644
index 000000000..f898bee8f
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.png
new file mode 100644
index 000000000..99f8c6ec4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.png
new file mode 100644
index 000000000..59d9a6192
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.png
new file mode 100644
index 000000000..5b0971c16
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.png
new file mode 100644
index 000000000..ceba0b6e0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.png
new file mode 100644
index 000000000..fa3055f8c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.png
new file mode 100644
index 000000000..c760313e0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.png
new file mode 100644
index 000000000..6a557a044
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/package.html b/libjava/classpath/javax/swing/plaf/basic/package.html
new file mode 100644
index 000000000..700c8cdbc
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.plaf.basic package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.plaf.basic</title></head>
+
+<body>
+<p>Provides a "basic" look and feel implementation.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.dia b/libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.dia
new file mode 100644
index 000000000..02bfbbe90
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.dia
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.png b/libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.png
new file mode 100644
index 000000000..def4cbc6c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/doc-files/TreeUI-1.png b/libjava/classpath/javax/swing/plaf/doc-files/TreeUI-1.png
new file mode 100644
index 000000000..0f01ab03c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/doc-files/TreeUI-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java b/libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java
new file mode 100644
index 000000000..27c569f03
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java
@@ -0,0 +1,305 @@
+/* DefaultMetalTheme.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import gnu.classpath.SystemProperties;
+
+import java.awt.Font;
+
+import javax.swing.UIManager;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.FontUIResource;
+
+/**
+ * The default theme for the {@link MetalLookAndFeel}.
+ *
+ * @see MetalLookAndFeel#setCurrentTheme(MetalTheme)
+ */
+public class DefaultMetalTheme extends MetalTheme
+{
+ private static final ColorUIResource PRIMARY1 =
+ new ColorUIResource(102, 102, 153);
+ private static final ColorUIResource PRIMARY2 =
+ new ColorUIResource(153, 153, 204);
+ private static final ColorUIResource PRIMARY3 =
+ new ColorUIResource(204, 204, 255);
+ private static final ColorUIResource SECONDARY1 =
+ new ColorUIResource(102, 102, 102);
+ private static final ColorUIResource SECONDARY2 =
+ new ColorUIResource(153, 153, 153);
+ private static final ColorUIResource SECONDARY3 =
+ new ColorUIResource(204, 204, 204);
+
+ private static final FontUIResource SUB_TEXT_FONT =
+ new FontUIResource("Dialog", Font.PLAIN, 10);
+ private static final FontUIResource SYSTEM_TEXT_FONT =
+ new FontUIResource("Dialog", Font.PLAIN, 12);
+ private static final FontUIResource USER_TEXT_FONT =
+ new FontUIResource("Dialog", Font.PLAIN, 12);
+ private static final FontUIResource WINDOW_TITLE_FONT =
+ new FontUIResource("Dialog", Font.BOLD, 12);
+
+ /**
+ * The control text font for swing.boldMetal=false.
+ */
+ private static final FontUIResource PLAIN_CONTROL_TEXT_FONT =
+ new FontUIResource("Dialog", Font.PLAIN, 12);
+
+ /**
+ * The standard control text font.
+ */
+ private static final FontUIResource BOLD_CONTROL_TEXT_FONT =
+ new FontUIResource("Dialog", Font.BOLD, 12);
+
+ /**
+ * The menu text font for swing.boldMetal=false.
+ */
+ private static final FontUIResource PLAIN_MENU_TEXT_FONT =
+ new FontUIResource("Dialog", Font.PLAIN, 12);
+
+ /**
+ * The menu control text font.
+ */
+ private static final FontUIResource BOLD_MENU_TEXT_FONT =
+ new FontUIResource("Dialog", Font.BOLD, 12);
+
+ /**
+ * Indicates the control text font.
+ */
+ static final int CONTROL_TEXT_FONT = 1;
+
+ /**
+ * Indicates the menu text font.
+ */
+ static final int MENU_TEXT_FONT = 2;
+
+ /**
+ * Creates a new instance of this theme.
+ */
+ public DefaultMetalTheme()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Returns the name of the theme.
+ *
+ * @return <code>"Steel"</code>.
+ */
+ public String getName()
+ {
+ return "Steel";
+ }
+
+ /**
+ * Returns the first primary color for this theme.
+ *
+ * @return The first primary color.
+ */
+ protected ColorUIResource getPrimary1()
+ {
+ return PRIMARY1;
+ }
+
+ /**
+ * Returns the second primary color for this theme.
+ *
+ * @return The second primary color.
+ */
+ protected ColorUIResource getPrimary2()
+ {
+ return PRIMARY2;
+ }
+
+ /**
+ * Returns the third primary color for this theme.
+ *
+ * @return The third primary color.
+ */
+ protected ColorUIResource getPrimary3()
+ {
+ return PRIMARY3;
+ }
+
+ /**
+ * Returns the first secondary color for this theme.
+ *
+ * @return The first secondary color.
+ */
+ protected ColorUIResource getSecondary1()
+ {
+ return SECONDARY1;
+ }
+
+ /**
+ * Returns the second secondary color for this theme.
+ *
+ * @return The second secondary color.
+ */
+ protected ColorUIResource getSecondary2()
+ {
+ return SECONDARY2;
+ }
+
+ /**
+ * Returns the third secondary color for this theme.
+ *
+ * @return The third secondary color.
+ */
+ protected ColorUIResource getSecondary3()
+ {
+ return SECONDARY3;
+ }
+
+ /**
+ * Returns the font used for text on controls. In this case, the font is
+ * <code>FontUIResource("Dialog", Font.BOLD, 12)</code>, unless the
+ * <code>swing.boldMetal</code> UI default is set to {@link Boolean#FALSE}
+ * in which case it is <code>FontUIResource("Dialog", Font.PLAIN, 12)</code>.
+ *
+ * @return The font.
+ */
+ public FontUIResource getControlTextFont()
+ {
+ return getFont(CONTROL_TEXT_FONT);
+ }
+
+ /**
+ * Returns the font used for text in menus. In this case, the font is
+ * <code>FontUIResource("Dialog", Font.BOLD, 12)</code>, unless the
+ * <code>swing.boldMetal</code> UI default is set to {@link Boolean#FALSE}
+ * in which case it is <code>FontUIResource("Dialog", Font.PLAIN, 12)</code>.
+ *
+ * @return The font used for text in menus.
+ */
+ public FontUIResource getMenuTextFont()
+ {
+ return getFont(MENU_TEXT_FONT);
+ }
+
+ /**
+ * Returns the font used for sub text. In this case, the font is
+ * <code>FontUIResource("Dialog", Font.PLAIN, 10)</code>.
+ *
+ * @return The font used for sub text.
+ */
+ public FontUIResource getSubTextFont()
+ {
+ return SUB_TEXT_FONT;
+ }
+
+ /**
+ * Returns the font used for system text. In this case, the font is
+ * <code>FontUIResource("Dialog", Font.PLAIN, 12)</code>.
+ *
+ * @return The font used for system text.
+ */
+ public FontUIResource getSystemTextFont()
+ {
+ return SYSTEM_TEXT_FONT;
+ }
+
+ /**
+ * Returns the font used for user text. In this case, the font is
+ * <code>FontUIResource("Dialog", Font.PLAIN, 12)</code>.
+ *
+ * @return The font used for user text.
+ */
+ public FontUIResource getUserTextFont()
+ {
+ return USER_TEXT_FONT;
+ }
+
+ /**
+ * Returns the font used for window titles. In this case, the font is
+ * <code>FontUIResource("Dialog", Font.BOLD, 12)</code>.
+ *
+ * @return The font used for window titles.
+ */
+ public FontUIResource getWindowTitleFont()
+ {
+ return WINDOW_TITLE_FONT;
+ }
+
+ /**
+ * Returns the appropriate font. The font type to return is identified
+ * by the specified id.
+ *
+ * @param id the font type to return
+ *
+ * @return the correct font
+ */
+ private FontUIResource getFont(int id)
+ {
+ FontUIResource font = null;
+ switch (id)
+ {
+ case CONTROL_TEXT_FONT:
+ if (isBoldMetal())
+ font = BOLD_CONTROL_TEXT_FONT;
+ else
+ font = PLAIN_CONTROL_TEXT_FONT;
+ break;
+ case MENU_TEXT_FONT:
+ if (isBoldMetal())
+ font = BOLD_MENU_TEXT_FONT;
+ else
+ font = PLAIN_MENU_TEXT_FONT;
+ break;
+ // TODO: Add other font types and their mapping here.
+ }
+ return font;
+ }
+
+ /**
+ * Determines if the theme should be bold or not. The theme is bold by
+ * default, this can be turned off by setting the system property
+ * swing.boldMetal to true, or by putting the property with the same name
+ * into the current UIManager's defaults.
+ *
+ * @return <code>true</code>, when the theme is bold, <code>false</code>
+ * otherwise
+ */
+ private boolean isBoldMetal()
+ {
+ Object boldMetal = UIManager.get("swing.boldMetal");
+ return (boldMetal == null || ! Boolean.FALSE.equals(boldMetal))
+ && ! ("false".equals(SystemProperties.getProperty("swing.boldMetal")));
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java
new file mode 100644
index 000000000..253629998
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java
@@ -0,0 +1,1626 @@
+/* MetalBorders.java
+ Copyright (C) 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.JButton;
+import javax.swing.JInternalFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.border.AbstractBorder;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.plaf.BorderUIResource;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicBorders;
+import javax.swing.text.JTextComponent;
+
+
+/**
+ * A factory class that creates borders for the different Swing components.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class MetalBorders
+{
+
+ /** The shared instance for getButtonBorder(). */
+ private static Border buttonBorder;
+
+ /** The shared instance for getToggleButtonBorder(). */
+ private static Border toggleButtonBorder;
+
+ /** The shared instance for getDesktopIconBorder(). */
+ private static Border desktopIconBorder;
+
+ /** The shared instance for getRolloverButtonBorder(). */
+ private static Border toolbarButtonBorder;
+
+ /** The shared instance for getTextFieldBorder(). */
+ private static Border textFieldBorder;
+
+ /** The shared instance for getTextBorder(). */
+ private static Border textBorder;
+
+ /** The shared instance for getRolloverBorder(). */
+ private static Border rolloverBorder;
+
+ /**
+ * A MarginBorder that gets shared by multiple components.
+ * Created on demand by the private helper function {@link
+ * #getMarginBorder()}.
+ */
+ private static BasicBorders.MarginBorder marginBorder;
+
+ /**
+ * <p>A border used for {@link JButton} components.</p>
+ *
+ * <p>This {@link Border} implementation can handle only instances of
+ * {@link AbstractButton} and their subclasses.</p>
+ *
+ * <p>If the Metal Look and Feel's current theme is 'Ocean' the border
+ * will be painted with a special highlight when the mouse cursor if
+ * over the button (ie. the property <code>rollover</code> of the
+ * button's model is <code>true</code>) and is not a <b>direct</b>
+ * child of a {@link JToolBar}.</p>
+ */
+ public static class ButtonBorder extends AbstractBorder implements UIResource
+ {
+ /** The borders insets. */
+ protected static Insets borderInsets = new Insets(3, 3, 3, 3);
+
+ /**
+ * Creates a new instance of <code>ButtonBorder</code>.
+ */
+ public ButtonBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the button border.
+ *
+ * @param c the component for which we paint the border
+ * @param g the Graphics context to use
+ * @param x the X coordinate of the upper left corner of c
+ * @param y the Y coordinate of the upper left corner of c
+ * @param w the width of c
+ * @param h the height of c
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ // With the OceanTheme the button border is painted entirely different.
+ // However, I couldn't figure out how this is determined besides checking
+ // for instanceof OceanTheme. The button painting is definitely not
+ // influenced by a UI default property and it is definitely performed
+ // by the same Border class.
+ if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
+ paintOceanButtonBorder(c, g, x, y, w, h);
+ else
+ paintDefaultButtonBorder(c, g, x, y, w, h);
+ }
+
+ /**
+ * Paints the button border for the DefaultMetalTheme.
+ *
+ * @param c the component (button)
+ * @param g the graphics object to use
+ * @param x the upper left corner of the component, X coordinate
+ * @param y the upper left corner of the component, Y coordinate
+ * @param w the width of the component
+ * @param h the height of the component
+ */
+ private void paintDefaultButtonBorder(Component c, Graphics g, int x,
+ int y, int w, int h)
+ {
+ ButtonModel bmodel = null;
+
+ // The RI will fail with a ClassCastException in such a situation.
+ // This code tries to be more helpful.
+ if (c instanceof AbstractButton)
+ bmodel = ((AbstractButton) c).getModel();
+ else
+ throw new IllegalStateException("A ButtonBorder is supposed to work "
+ + "only with AbstractButton and"
+ + "subclasses.");
+
+ Color darkShadow = MetalLookAndFeel.getControlDarkShadow();
+ Color shadow = MetalLookAndFeel.getControlShadow();
+ Color light = MetalLookAndFeel.getControlHighlight();
+ Color middle = MetalLookAndFeel.getControl();
+
+ if (c.isEnabled())
+ {
+ // draw dark border
+ g.setColor(darkShadow);
+ g.drawRect(x, y, w - 2, h - 2);
+
+ // If the button is the default button, we paint a special border,
+ // regardless of the pressed state.
+ if (c instanceof JButton && ((JButton) c).isDefaultButton())
+ {
+ g.drawRect(x + 1, y + 1, w - 4, h - 4);
+ // Draw white highlight.
+ g.setColor(light);
+ g.drawLine(x + 2, y + 2, x + w - 4, y + 2);
+ g.drawLine(x + 2, y + 2, x + 2, y + h - 4);
+ g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1);
+ g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 1);
+ // Draw crossing pixels.
+ g.setColor(middle);
+ g.fillRect(x + w - 2, y + 2, 1, 1);
+ g.fillRect(x + 2, y + h - 2, 1, 1);
+ }
+ else
+ {
+ // The normal border. This is used when the button is not
+ // pressed or the button is not armed.
+ if (! (bmodel.isPressed() && bmodel.isArmed()))
+ {
+ // draw light border
+ g.setColor(light);
+ g.drawRect(x + 1, y + 1, w - 2, h - 2);
+
+ // draw crossing pixels of both borders
+ g.setColor(middle);
+ g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
+ }
+ // The pressed border. This border is painted only when
+ // the button is both pressed and armed.
+ else
+ {
+ // draw light border
+ g.setColor(light);
+ g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
+ g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
+
+ // draw shadow border
+ g.setColor(middle);
+ g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 2);
+
+ // draw crossing pixels of both borders
+ g.setColor(shadow);
+ g.drawRect(x + 1, y + h - 2, 0, 0);
+ g.drawRect(x + w - 2, y + 1, 0, 0);
+ }
+ }
+ }
+ else
+ {
+ // draw disabled border
+ g.setColor(MetalLookAndFeel.getInactiveControlTextColor());
+ g.drawRect(x, y, w - 2, h - 2);
+ }
+ }
+
+ /**
+ * Paints the button border for the OceanTheme.
+ *
+ * @param c the button
+ * @param g the graphics context
+ * @param x the X coordinate of the upper left corner of the painting rect
+ * @param y the Y coordinate of the upper left corner of the painting rect
+ * @param w the width of the painting rect
+ * @param h the height of the painting rect
+ */
+ private void paintOceanButtonBorder(Component c, Graphics g, int x,
+ int y, int w, int h)
+ {
+ ButtonModel bmodel = null;
+
+ // The RI will fail with a ClassCastException in such a situation.
+ // This code tries to be more helpful.
+ if (c instanceof AbstractButton)
+ bmodel = ((AbstractButton) c).getModel();
+ else
+ throw new IllegalStateException("A ButtonBorder is supposed to work "
+ + "only with AbstractButton and"
+ + "subclasses.");
+
+ Color darkShadow = MetalLookAndFeel.getControlDarkShadow();
+ Color shadow = MetalLookAndFeel.getControlShadow();
+ Color light = MetalLookAndFeel.getControlHighlight();
+ Color middle = MetalLookAndFeel.getControl();
+
+ if (c.isEnabled())
+ {
+ // Paint the pressed border if the button is pressed, or if
+ // the button is the default button. In the OceanTheme, the default
+ // button has the same border as a pressed button.
+ if (bmodel.isPressed() || ((c instanceof JButton)
+ && ((JButton) c).isDefaultButton()))
+ {
+ // Draw fat border.
+ g.setColor(darkShadow);
+ g.drawRect(x, y, w - 1, h - 1);
+ g.drawRect(x + 1, y + 1, w - 3, h - 3);
+ }
+ else if (bmodel.isRollover() && !(c.getParent() instanceof JToolBar))
+ {
+ // Paint a bigger border when the mouse is over the button but
+ // only if it is *not* part of a JToolBar.
+ g.setColor(shadow);
+ g.drawRect(x, y, w - 1, h - 1);
+ g.drawRect(x + 2, y + 2, w - 5, h - 5);
+ g.setColor(darkShadow);
+ g.drawRect(x + 1, y + 1, w - 3, h - 3);
+ }
+ else
+ {
+ g.setColor(darkShadow);
+ g.drawRect(x, y, w - 1, h - 1);
+ }
+ }
+ else
+ {
+ // draw disabled border
+ g.setColor(MetalLookAndFeel.getInactiveControlTextColor());
+ g.drawRect(x, y, w - 2, h - 2);
+ }
+ }
+
+ /**
+ * Returns the insets of the <code>ButtonBorder</code>.
+ *
+ * @param c the component for which the border is used (ignored).
+ *
+ * @return The insets of the <code>ButtonBorder</code>.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return borderInsets;
+ }
+
+ /**
+ * Returns the insets of the <code>ButtonBorder</code> in the specified
+ * <code>newInsets</code> object.
+ *
+ * @param c the component for which the border is used (ignored).
+ * @param newInsets the insets object where to put the values (
+ * <code>null</code> not permitted).
+ *
+ * @return The <code>newInsets</code> reference.
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ newInsets.bottom = borderInsets.bottom;
+ newInsets.left = borderInsets.left;
+ newInsets.right = borderInsets.right;
+ newInsets.top = borderInsets.top;
+ return newInsets;
+ }
+ }
+
+ /**
+ * A border used when painting {@link JInternalFrame} instances.
+ */
+ static class DesktopIconBorder extends AbstractBorder
+ implements UIResource
+ {
+ /**
+ * Creates a new border instance.
+ */
+ public DesktopIconBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ if (newInsets == null)
+ newInsets = new Insets(3, 3, 2, 3);
+ else
+ {
+ newInsets.top = 3;
+ newInsets.left = 3;
+ newInsets.bottom = 2;
+ newInsets.right = 3;
+ }
+ return newInsets;
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawRect(x, y, w - 1, h - 1);
+ }
+
+ }
+
+ /**
+ * A simple 3D border.
+ */
+ public static class Flush3DBorder extends AbstractBorder
+ implements UIResource
+ {
+ private static final Insets borderInsets = new Insets(2, 2, 2, 2);
+
+ /**
+ * Creates a new border instance.
+ */
+ public Flush3DBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return borderInsets;
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ * @param newInsets an existing insets instance, that will be populated
+ * with the border insets and returned as the result
+ * (<code>null</code> not permitted).
+ *
+ * @return The <code>newInsets</code> reference.
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ newInsets.top = borderInsets.top;
+ newInsets.left = borderInsets.left;
+ newInsets.bottom = borderInsets.bottom;
+ newInsets.right = borderInsets.right;
+ return newInsets;
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ Color savedColor = g.getColor();
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawRect(x, y, w - 2, h - 2);
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawRect(x + 1, y + 1, w - 2, h - 2);
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
+ g.setColor(savedColor);
+ }
+
+ }
+
+ /**
+ * A border used for a {@link JInternalFrame} when it is being used as a
+ * palette.
+ *
+ * @since 1.3
+ */
+ public static class PaletteBorder extends AbstractBorder
+ implements UIResource
+ {
+ private static final Insets borderInsets = new Insets(1, 1, 1, 1);
+
+ /**
+ * Creates a new <code>PaletteBorder</code>.
+ */
+ public PaletteBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return borderInsets;
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ * @param newInsets an existing insets instance, that will be populated
+ * with the border insets and returned as the result
+ * (<code>null</code> not permitted).
+ *
+ * @return The <code>newInsets</code> reference.
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ newInsets.top = borderInsets.top;
+ newInsets.left = borderInsets.left;
+ newInsets.bottom = borderInsets.bottom;
+ newInsets.right = borderInsets.right;
+ return newInsets;
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ Color savedColor = g.getColor();
+
+ // draw the outline
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ g.drawRect(x, y, w - 1, h - 1);
+
+ // put a dot in each corner
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(x, y, 1, 1);
+ g.fillRect(x + w - 1, y, 1, 1);
+ g.fillRect(x + w - 1, y + h - 1, 1, 1);
+ g.fillRect(x, y + h - 1, 1, 1);
+ g.setColor(savedColor);
+ }
+
+ }
+
+ /**
+ * A border used for the {@link JTextField} component.
+ */
+ public static class TextFieldBorder extends Flush3DBorder
+ implements UIResource
+ {
+ /**
+ * Creates a new border instance.
+ */
+ public TextFieldBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ boolean enabledTextBorder;
+ if (c instanceof JTextComponent)
+ {
+ JTextComponent tc = (JTextComponent) c;
+ enabledTextBorder = tc.isEnabled() && tc.isEditable();
+ }
+ else
+ enabledTextBorder = false;
+
+ if (enabledTextBorder)
+ super.paintBorder(c, g, x, y, w, h);
+ else
+ {
+ Color savedColor = g.getColor();
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawRect(x, y, w - 1, h - 1);
+ g.setColor(savedColor);
+ }
+ }
+
+ }
+
+ /**
+ * A border used for the {@link JInternalFrame} component.
+ */
+ public static class InternalFrameBorder extends AbstractBorder
+ implements UIResource
+ {
+ private static final Insets borderInsets = new Insets(5, 5, 5, 5);
+
+ /**
+ * Creates a new border instance.
+ */
+ public InternalFrameBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return borderInsets;
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ * @param newInsets an existing insets instance, that will be populated
+ * with the border insets and returned as the result
+ * (<code>null</code> not permitted).
+ *
+ * @return The <code>newInsets</code> reference.
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ newInsets.top = borderInsets.top;
+ newInsets.left = borderInsets.left;
+ newInsets.bottom = borderInsets.bottom;
+ newInsets.right = borderInsets.right;
+ return newInsets;
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+
+ JInternalFrame f = (JInternalFrame) c;
+ if (f.isSelected())
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+
+ // fill the border background
+ g.fillRect(x, y, w, 5);
+ g.fillRect(x, y, 5, h);
+ g.fillRect(x + w - 5, y, 5, h);
+ g.fillRect(x, y + h - 5, w, 5);
+
+ // draw a dot in each corner
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(x, y, 1, 1);
+ g.fillRect(x + w - 1, y, 1, 1);
+ g.fillRect(x + w - 1, y + h - 1, 1, 1);
+ g.fillRect(x, y + h - 1, 1, 1);
+
+ // draw the lines
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 14, y + 2, x + w - 15, y + 2);
+ g.drawLine(x + 14, y + h - 3, x + w - 15, y + h - 3);
+ g.drawLine(x + 2, y + 14, x + 2, y + h - 15);
+ g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15);
+
+ // draw the line highlights
+ if (f.isSelected())
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x + 15, y + 3, x + w - 14, y + 3);
+ g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2);
+ g.drawLine(x + 3, y + 15, x + 3, y + h - 14);
+ g.drawLine(x + w - 2, y + 15, x + w - 2, y + h - 14);
+ }
+
+ }
+
+ /**
+ * A border used for {@link JInternalFrame} components that are
+ * presented as dialogs (by the {@link JOptionPane} class).
+ */
+ public static class OptionDialogBorder extends AbstractBorder
+ implements UIResource
+ {
+
+ /**
+ * Creates a new border instance.
+ */
+ public OptionDialogBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ if (newInsets == null)
+ newInsets = new Insets(3, 3, 3, 3);
+ else
+ {
+ newInsets.top = 3;
+ newInsets.left = 3;
+ newInsets.bottom = 3;
+ newInsets.right = 3;
+ }
+ return newInsets;
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+
+ JInternalFrame f = (JInternalFrame) c;
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ if (f.getContentPane() instanceof JOptionPane)
+ {
+ JOptionPane pane = (JOptionPane) f.getContentPane();
+ int type = pane.getMessageType();
+ if (type == JOptionPane.QUESTION_MESSAGE)
+ {
+ Color bc = UIManager.getColor(
+ "OptionPane.questionDialog.border.background");
+ if (bc != null)
+ g.setColor(bc);
+ }
+ if (type == JOptionPane.WARNING_MESSAGE)
+ {
+ Color bc = UIManager.getColor(
+ "OptionPane.warningDialog.border.background");
+ if (bc != null)
+ g.setColor(bc);
+ }
+ else if (type == JOptionPane.ERROR_MESSAGE)
+ {
+ Color bc = UIManager.getColor(
+ "OptionPane.errorDialog.border.background");
+ if (bc != null)
+ g.setColor(bc);
+ }
+ }
+
+ // fill the border background
+ g.fillRect(x, y, w, 3);
+ g.fillRect(x, y, 3, h);
+ g.fillRect(x + w - 3, y, 3, h);
+ g.fillRect(x, y + h - 3, w, 3);
+
+ // draw a dot in each corner
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(x, y, 1, 1);
+ g.fillRect(x + w - 1, y, 1, 1);
+ g.fillRect(x + w - 1, y + h - 1, 1, 1);
+ g.fillRect(x, y + h - 1, 1, 1);
+
+ }
+
+ }
+
+ /**
+ * A border used for {@link JMenu} and {@link JMenuItem} components.
+ */
+ public static class MenuItemBorder extends AbstractBorder
+ implements UIResource
+ {
+ /** The border insets. */
+ protected static Insets borderInsets = new Insets(2, 2, 2, 2);
+
+ /**
+ * Creates a new border instance.
+ */
+ public MenuItemBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border for the component. A border is painted only if the
+ * component is a selected {@link JMenu} or an armed {@link JMenuItem}.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate of the border area.
+ * @param y the y-coordinate of the border area.
+ * @param w the width of the border area.
+ * @param h the height of the border area.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ Color dark = MetalLookAndFeel.getPrimaryControlDarkShadow();
+ Color light = MetalLookAndFeel.getPrimaryControlHighlight();
+ if (c instanceof JMenu)
+ {
+ JMenu menu = (JMenu) c;
+ if (menu.isSelected())
+ {
+ g.setColor(dark);
+ g.drawLine(x, y, x, y + h);
+ g.drawLine(x, y, x + w, y);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, y + h);
+ g.setColor(light);
+ g.drawLine(x + w - 1, y + 1, x + w - 1, y + h);
+ }
+ }
+ else if (c instanceof JMenuItem)
+ {
+ JMenuItem item = (JMenuItem) c;
+ if (item.isArmed())
+ {
+ g.setColor(dark);
+ g.drawLine(x, y, x + w, y);
+ g.setColor(light);
+ g.drawLine(x, y + h - 1, x + w, y + h - 1);
+ }
+ else
+ {
+ // Normally we draw a light line on the left.
+ g.setColor(light);
+ g.drawLine(x, y, x, y + h);
+ }
+ }
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return borderInsets;
+ }
+
+ /**
+ * Populates <code>insets</code> with the border insets, then returns it.
+ *
+ * @param c the component (ignored).
+ * @param insets the object to populate with the border insets.
+ *
+ * @return The border insets.
+ *
+ * @throws NullPointerException if <code>insets</code> is <code>null</code>.
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ insets.left = borderInsets.left;
+ insets.top = borderInsets.top;
+ insets.bottom = borderInsets.bottom;
+ insets.right = borderInsets.right;
+ return insets;
+ }
+ }
+
+ /**
+ * A border used for {@link JMenuBar} components.
+ */
+ public static class MenuBarBorder
+ extends AbstractBorder
+ implements UIResource
+ {
+ /** The border insets. */
+ protected static Insets borderInsets = new Insets(1, 0, 1, 0);
+
+ /**
+ * Creates a new border instance.
+ */
+ public MenuBarBorder()
+ {
+ }
+
+ /**
+ * Paints the border for the component. A border is painted only if the
+ * component is a selected {@link JMenu} or an armed {@link JMenuItem}.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate of the border area.
+ * @param y the y-coordinate of the border area.
+ * @param w the width of the border area.
+ * @param h the height of the border area.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ // Although it is not correct to decide on the static property
+ // currentTheme which color to use the RI does it like that.
+ // The trouble is that by simply changing the current theme to
+ // e.g. DefaultMetalLookAndFeel this method will use another color
+ // although a change in painting behavior should be expected only
+ // after setting a new look and feel and updating all components.
+ if(MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
+ g.setColor(UIManager.getColor("MenuBar.borderColor"));
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+
+ g.drawLine(x, y + h - 1, x + w, y + h - 1);
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return borderInsets;
+ }
+
+ /**
+ * Populates <code>insets</code> with the border insets, then returns it.
+ *
+ * @param c the component (ignored).
+ * @param insets the object to populate with the border insets.
+ *
+ * @return The border insets.
+ *
+ * @throws NullPointerException if <code>insets</code> is <code>null</code>.
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ insets.left = borderInsets.left;
+ insets.top = borderInsets.top;
+ insets.bottom = borderInsets.bottom;
+ insets.right = borderInsets.right;
+ return insets;
+ }
+ }
+
+ /**
+ * A border for {@link JScrollPane} components.
+ */
+ public static class ScrollPaneBorder
+ extends AbstractBorder
+ implements UIResource
+ {
+ /** The border insets. */
+ private static Insets insets = new Insets(1, 1, 2, 2);
+
+ /**
+ * Constructs a new ScrollPaneBorder.
+ */
+ public ScrollPaneBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the insets of the border for the Component <code>c</code>.
+ *
+ * @param c the Component for which we return the border insets
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return insets;
+ }
+
+ /**
+ * Paints the border.
+ *
+ * @param c the Component for which the border is painted
+ * @param g the Graphics context
+ * @param x the X coordinate of the upper left corner of the border
+ * @param y the Y coordinate of the upper left corner of the border
+ * @param w the width of the border
+ * @param h the height of the border
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y,
+ int w, int h)
+ {
+ Color darkShadow = MetalLookAndFeel.getControlDarkShadow();
+ Color shadow = MetalLookAndFeel.getControlShadow();
+ Color light = MetalLookAndFeel.getWhite();
+ Color middle = MetalLookAndFeel.getControl();
+
+ // paint top border line
+ g.setColor(darkShadow);
+ g.drawLine(x, y, x + w - 2, y);
+
+ // paint left border line
+ g.drawLine(x, y, x, y + h - 2);
+
+ // paint right inner border line
+ g.drawLine(x + w - 2, y, x + w - 2, y + h + 1);
+
+ // paint bottom inner border line
+ g.drawLine(x + 2, y + h - 2, x + w - 2, y + h - 2);
+
+ // draw right outer border line
+ g.setColor(light);
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
+
+ // draw bottom outer border line
+ g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
+
+ // paint the lighter points
+ g.setColor(middle);
+ g.drawLine(x + w - 1, y, x + w - 1, y);
+ g.drawLine(x + w - 2, y + 2, x + w - 2, y + 2);
+ g.drawLine(x, y + h - 1, x, y + h - 1);
+ g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
+
+ }
+
+ }
+
+ /**
+ * A button border that is only visible when the mouse pointer is within
+ * the button's bounds.
+ */
+ public static class RolloverButtonBorder
+ extends MetalBorders.ButtonBorder
+ {
+ /**
+ * Creates a new border instance.
+ */
+ public RolloverButtonBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ // TODO: What should be done here? Obviously the ButtonBorder already
+ // handles the rollover state in Sun's impl. Maybe this is only there
+ // for backwards compatibility.
+ super.paintBorder(c, g, x, y, w, h);
+ }
+ }
+
+ /**
+ * This border is used in Toolbar buttons as inner border.
+ */
+ static class RolloverMarginBorder extends AbstractBorder
+ {
+ /** The borders insets. */
+ protected static Insets borderInsets = new Insets(3, 3, 3, 3);
+
+ /**
+ * Creates a new instance of RolloverBorder.
+ */
+ public RolloverMarginBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the insets of the RolloverBorder.
+ *
+ * @param c the component for which the border is used
+ *
+ * @return the insets of the RolloverBorder
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+ /**
+ * Returns the insets of the RolloverMarginBorder in the specified
+ * Insets object.
+ *
+ * @param c the component for which the border is used
+ * @param newInsets the insets object where to put the values
+ *
+ * @return the insets of the RolloverMarginBorder
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ if (newInsets == null)
+ newInsets = new Insets(0, 0, 0, 0);
+
+ AbstractButton b = (AbstractButton) c;
+ Insets margin = b.getMargin();
+ newInsets.bottom = borderInsets.bottom;
+ newInsets.left = borderInsets.left;
+ newInsets.right = borderInsets.right;
+ newInsets.top = borderInsets.top;
+ return newInsets;
+ }
+ }
+
+ /**
+ * A border implementation for popup menus.
+ */
+ public static class PopupMenuBorder
+ extends AbstractBorder
+ implements UIResource
+ {
+
+ /** The border's insets. */
+ protected static Insets borderInsets = new Insets(3, 1, 2, 1);
+
+ /**
+ * Constructs a new PopupMenuBorder.
+ */
+ public PopupMenuBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the insets of the border, creating a new Insets instance
+ * with each call.
+ *
+ * @param c the component for which we return the border insets
+ * (not used here)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+ /**
+ * Returns the insets of the border, using the supplied Insets instance.
+ *
+ * @param c the component for which we return the border insets
+ * (not used here)
+ * @param i the Insets instance to fill with the Insets values
+ */
+ public Insets getBorderInsets(Component c, Insets i)
+ {
+ Insets insets;
+ if (i == null)
+ insets = new Insets(borderInsets.top, borderInsets.left,
+ borderInsets.bottom, borderInsets.right);
+ else
+ {
+ insets = i;
+ insets.top = borderInsets.top;
+ insets.left = borderInsets.left;
+ insets.bottom = borderInsets.bottom;
+ insets.right = borderInsets.right;
+ }
+
+ return insets;
+ }
+
+ /**
+ * Paints the border for component <code>c</code> using the
+ * Graphics context <code>g</code> with the dimension
+ * <code>x, y, w, h</code>.
+ *
+ * @param c the component for which we paint the border
+ * @param g the Graphics context to use
+ * @param x the X coordinate of the upper left corner of c
+ * @param y the Y coordinate of the upper left corner of c
+ * @param w the width of c
+ * @param h the height of c
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
+ Color light = MetalLookAndFeel.getPrimaryControlHighlight();
+
+ // draw dark outer border
+ g.setColor(darkShadow);
+ g.drawRect(x, y, w - 1, h - 1);
+
+ // draw highlighted inner border (only top and left)
+ g.setColor(light);
+ g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
+ }
+
+ }
+
+ /**
+ * A border used for the {@link JToggleButton} component.
+ *
+ * @since 1.3
+ */
+ public static class ToggleButtonBorder
+ extends ButtonBorder
+ {
+ /**
+ * Creates a new border instance.
+ */
+ public ToggleButtonBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the toggle button border.
+ *
+ * @param c the component for which we paint the border
+ * @param g the Graphics context to use
+ * @param x the X coordinate of the upper left corner of c
+ * @param y the Y coordinate of the upper left corner of c
+ * @param w the width of c
+ * @param h the height of c
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ ButtonModel bmodel = null;
+
+ if (c instanceof AbstractButton)
+ bmodel = ((AbstractButton) c).getModel();
+
+ Color darkShadow = MetalLookAndFeel.getControlDarkShadow();
+ Color shadow = MetalLookAndFeel.getControlShadow();
+ Color light = MetalLookAndFeel.getWhite();
+ Color middle = MetalLookAndFeel.getControl();
+
+ if (c.isEnabled())
+ {
+ // draw dark border
+ g.setColor(darkShadow);
+ g.drawRect(x, y, w - 2, h - 2);
+
+ if (!bmodel.isArmed())
+ {
+ // draw light border
+ g.setColor(light);
+ g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
+ g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
+ if (bmodel.isSelected())
+ g.setColor(middle);
+ g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
+
+ // draw crossing pixels of both borders
+ g.setColor(shadow);
+ g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
+ }
+ else
+ {
+ // draw light border
+ g.setColor(light);
+ g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1);
+ g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
+
+ // draw shadow border
+ g.setColor(shadow);
+ g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 2);
+
+ // draw crossing pixels of both borders
+ g.setColor(shadow);
+ g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
+
+ }
+ // draw corners
+ g.setColor(middle);
+ g.drawLine(x, y + h - 1, x, y + h - 1);
+ g.drawLine(x + w - 1, y, x + w - 1, y);
+ }
+ else
+ {
+ // draw disabled border
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawRect(x, y, w - 2, h - 2);
+ }
+ }
+ }
+
+ /**
+ * A border used for the {@link JToolBar} component.
+ */
+ public static class ToolBarBorder extends AbstractBorder
+ implements UIResource, SwingConstants
+ {
+ /**
+ * Creates a new border instance.
+ */
+ public ToolBarBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component (ignored).
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ JToolBar tb = (JToolBar) c;
+ if (tb.getOrientation() == JToolBar.HORIZONTAL)
+ {
+ if (newInsets == null)
+ newInsets = new Insets(2, 16, 2, 2);
+ else
+ {
+ newInsets.top = 2;
+ newInsets.left = 16;
+ newInsets.bottom = 2;
+ newInsets.right = 2;
+ }
+ return newInsets;
+ }
+ else // assume JToolBar.VERTICAL
+ {
+ if (newInsets == null)
+ newInsets = new Insets(16, 2, 2, 2);
+ else
+ {
+ newInsets.top = 16;
+ newInsets.left = 2;
+ newInsets.bottom = 2;
+ newInsets.right = 2;
+ }
+ return newInsets;
+ }
+
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+
+ JToolBar tb = (JToolBar) c;
+ if (tb.getOrientation() == JToolBar.HORIZONTAL)
+ {
+ MetalUtils.fillMetalPattern(tb, g, x + 2, y + 2, x + 11, y + h - 5,
+ MetalLookAndFeel.getControlHighlight(),
+ MetalLookAndFeel.getControlDarkShadow());
+ }
+ else
+ {
+ MetalUtils.fillMetalPattern(tb, g, x + 2, y + 2, x + w - 5, y + 11,
+ MetalLookAndFeel.getControlHighlight(),
+ MetalLookAndFeel.getControlDarkShadow());
+ }
+ }
+
+ }
+
+ /**
+ * A border for table header cells.
+ *
+ * @since 1.3
+ */
+ public static class TableHeaderBorder extends AbstractBorder
+ {
+ /**
+ * The insets of this border.
+ */
+ // TODO: According to tests that I have done, this is really the border
+ // that should be returned by getBorderInsets(). However, the name
+ // is very distracting. Is there any deeper meaning in it?
+ protected Insets editorBorderInsets;
+
+ /**
+ * Creates a new instance of <code>TableHeaderBorder</code>.
+ */
+ public TableHeaderBorder()
+ {
+ editorBorderInsets = new Insets(1, 1, 1, 1);
+ }
+
+ /**
+ * Return the insets of this border.
+ *
+ * @return the insets of this border
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return editorBorderInsets;
+ }
+
+ /**
+ * Paints the border.
+ *
+ * @param c the component for which to paint the border
+ * @param g the graphics context to use
+ * @param x the x cooridinate of the border rectangle
+ * @param y the y cooridinate of the border rectangle
+ * @param w the width of the border rectangle
+ * @param h the height of the border rectangle
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w, int h)
+ {
+ Color dark = MetalLookAndFeel.getControlDarkShadow();
+ Color light = MetalLookAndFeel.getWhite();
+ Color old = g.getColor();
+ g.setColor(light);
+ g.drawLine(x, y, x + w - 2, y);
+ g.drawLine(x, y, x, y + h - 2);
+ g.setColor(dark);
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
+ g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1);
+ g.setColor(old);
+ }
+ }
+
+ /**
+ * Returns a border for Swing buttons in the Metal Look &amp; Feel.
+ *
+ * @return a border for Swing buttons in the Metal Look &amp; Feel
+ */
+ public static Border getButtonBorder()
+ {
+ if (buttonBorder == null)
+ {
+ Border outer = new ButtonBorder();
+ Border inner = getMarginBorder();
+ buttonBorder = new BorderUIResource.CompoundBorderUIResource(outer,
+ inner);
+ }
+ return buttonBorder;
+ }
+
+ /**
+ * Returns a border for use with {@link JToggleButton} components.
+ *
+ * @return A border.
+ *
+ * @since 1.3
+ */
+ public static Border getToggleButtonBorder()
+ {
+ if (toggleButtonBorder == null)
+ {
+ Border outer = new ToggleButtonBorder();
+ Border inner = getMarginBorder();
+ toggleButtonBorder = new BorderUIResource.CompoundBorderUIResource(
+ outer, inner);
+ }
+ return toggleButtonBorder;
+ }
+
+ /**
+ * Returns a border instance that is used with a {@link JInternalFrame} when
+ * it is in the iconified state.
+ *
+ * @return A border.
+ *
+ * @since 1.3
+ */
+ public static Border getDesktopIconBorder()
+ {
+ if (desktopIconBorder == null)
+ desktopIconBorder = new DesktopIconBorder();
+ return desktopIconBorder;
+ }
+
+ /**
+ * Returns a border for use by the {@link JTextField} component.
+ *
+ * @return A border.
+ *
+ * @since 1.3
+ */
+ public static Border getTextFieldBorder()
+ {
+ if (textFieldBorder == null)
+ {
+ Border inner = getMarginBorder();
+ Border outer = new TextFieldBorder();
+ textFieldBorder =
+ new BorderUIResource.CompoundBorderUIResource(outer, inner);
+ }
+ return textFieldBorder;
+ }
+
+ /**
+ * Returns the border that is used for text components (except text fields,
+ * which use {@link #getTextFieldBorder}.
+ *
+ * @return the border that is used for text components
+ *
+ * @since 1.3
+ */
+ public static Border getTextBorder()
+ {
+ if (textBorder == null)
+ {
+ Border inner = getMarginBorder();
+ Border outer = new Flush3DBorder();
+ textBorder =
+ new BorderUIResource.CompoundBorderUIResource(outer, inner);
+ }
+ return textBorder;
+ }
+
+ /**
+ * Returns a border for Toolbar buttons in the Metal Look &amp; Feel.
+ *
+ * @return a border for Toolbar buttons in the Metal Look &amp; Feel
+ */
+ static Border getToolbarButtonBorder()
+ {
+ if (toolbarButtonBorder == null)
+ {
+ Border outer = new ButtonBorder();
+ Border inner = new RolloverMarginBorder();
+ toolbarButtonBorder = new CompoundBorder(outer, inner);
+ }
+ return toolbarButtonBorder;
+ }
+
+ /**
+ * Returns a shared instance of {@link BasicBorders.MarginBorder}.
+ *
+ * @return a shared instance of {@link BasicBorders.MarginBorder}
+ */
+ static Border getMarginBorder()
+ {
+ if (marginBorder == null)
+ marginBorder = new BasicBorders.MarginBorder();
+ return marginBorder;
+ }
+
+ /**
+ * Returns a shared instance of a compound border for rollover buttons.
+ *
+ * @return A shared border instance.
+ */
+ static Border getRolloverBorder()
+ {
+ if (rolloverBorder == null)
+ {
+ Border outer = new MetalBorders.RolloverButtonBorder();
+ Border inner = MetalBorders.getMarginBorder();
+ rolloverBorder = new BorderUIResource.CompoundBorderUIResource(outer,
+ inner);
+ }
+ return rolloverBorder;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java b/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java
new file mode 100644
index 000000000..7cf84e5f6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java
@@ -0,0 +1,74 @@
+/* MetalButtonListener.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.beans.PropertyChangeEvent;
+
+import javax.swing.AbstractButton;
+import javax.swing.plaf.basic.BasicButtonListener;
+
+/**
+ * A listener for buttons under the {@link MetalLookAndFeel}.
+ *
+ * @see MetalButtonUI#createButtonListener
+ */
+class MetalButtonListener extends BasicButtonListener
+{
+
+ /**
+ * Creates a new instance.
+ *
+ * @param button the button.
+ */
+ public MetalButtonListener(AbstractButton button)
+ {
+ super(button);
+ }
+
+ /**
+ * Handles property change events.
+ *
+ * @param e the event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ super.propertyChange(e);
+ // TODO: What should be done here?
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java
new file mode 100644
index 000000000..974f0a85a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java
@@ -0,0 +1,298 @@
+/* MetalButtonUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JToolBar;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicButtonUI;
+
+/**
+ * A UI delegate for the {@link JButton} component.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class MetalButtonUI
+ extends BasicButtonUI
+{
+
+ /**
+ * The shared button UI.
+ */
+ private static MetalButtonUI sharedUI;
+
+ /**
+ * The color used to draw the focus rectangle around the text and/or icon.
+ */
+ protected Color focusColor;
+
+ /**
+ * The background color for the button when it is pressed.
+ */
+ protected Color selectColor;
+
+ /**
+ * The color for disabled button labels.
+ */
+ protected Color disabledTextColor;
+
+ /**
+ * Returns a UI delegate for the specified component.
+ *
+ * @param c the component (should be a subclass of {@link AbstractButton}).
+ *
+ * @return A new instance of <code>MetalButtonUI</code>.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ if (sharedUI == null)
+ sharedUI = new MetalButtonUI();
+ return sharedUI;
+ }
+
+ /**
+ * Creates a new instance.
+ */
+ public MetalButtonUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns the color for the focus border.
+ *
+ * @return the color for the focus border
+ */
+ protected Color getFocusColor()
+ {
+ focusColor = UIManager.getColor(getPropertyPrefix() + "focus");
+ return focusColor;
+ }
+
+ /**
+ * Returns the color that indicates a selected button.
+ *
+ * @return the color that indicates a selected button
+ */
+ protected Color getSelectColor()
+ {
+ selectColor = UIManager.getColor(getPropertyPrefix() + "select");
+ return selectColor;
+ }
+
+ /**
+ * Returns the color for the text label of disabled buttons.
+ *
+ * @return the color for the text label of disabled buttons
+ */
+ protected Color getDisabledTextColor()
+ {
+ disabledTextColor = UIManager.getColor(getPropertyPrefix()
+ + "disabledText");
+ return disabledTextColor;
+ }
+
+ /**
+ * Installs the default settings for the specified button.
+ *
+ * @param button the button.
+ *
+ * @see #uninstallDefaults(AbstractButton)
+ */
+ public void installDefaults(AbstractButton button)
+ {
+ // This is overridden to be public, for whatever reason.
+ super.installDefaults(button);
+ }
+
+ /**
+ * Removes the defaults added by {@link #installDefaults(AbstractButton)}.
+ */
+ public void uninstallDefaults(AbstractButton button)
+ {
+ // This is overridden to be public, for whatever reason.
+ super.uninstallDefaults(button);
+ }
+
+ /**
+ * Paints the background of the button to indicate that it is in the
+ * "pressed" state.
+ *
+ * @param g the graphics context.
+ * @param b the button.
+ */
+ protected void paintButtonPressed(Graphics g, AbstractButton b)
+ {
+ if (b.isContentAreaFilled())
+ {
+ g.setColor(getSelectColor());
+ g.fillRect(0, 0, b.getWidth(), b.getHeight());
+ }
+ }
+
+ /**
+ * Paints the focus rectangle around the button text and/or icon.
+ *
+ * @param g the graphics context.
+ * @param b the button.
+ * @param viewRect the button bounds.
+ * @param textRect the text bounds.
+ * @param iconRect the icon bounds.
+ */
+ protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect,
+ Rectangle textRect, Rectangle iconRect)
+ {
+ if (b.isEnabled() && b.hasFocus() && b.isFocusPainted())
+ {
+ Color savedColor = g.getColor();
+ g.setColor(getFocusColor());
+ Rectangle focusRect = iconRect.union(textRect);
+ g.drawRect(focusRect.x - 1, focusRect.y,
+ focusRect.width + 1, focusRect.height);
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * Paints the button text.
+ *
+ * @param g the graphics context.
+ * @param c the button.
+ * @param textRect the text bounds.
+ * @param text the text to display.
+ */
+ protected void paintText(Graphics g, JComponent c, Rectangle textRect,
+ String text)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Font f = b.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+
+ if (b.isEnabled())
+ {
+ g.setColor(b.getForeground());
+ g.drawString(text, textRect.x, textRect.y + fm.getAscent());
+ }
+ else
+ {
+ g.setColor(getDisabledTextColor());
+ g.drawString(text, textRect.x, textRect.y + fm.getAscent());
+ }
+ }
+
+ /**
+ * If the property <code>Button.gradient</code> is set, then a gradient is
+ * painted as background, otherwise the normal superclass behaviour is
+ * called.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+ if ((b.getBackground() instanceof UIResource)
+ && b.isContentAreaFilled() && b.isEnabled())
+ {
+ ButtonModel m = b.getModel();
+ String uiKey = "Button.gradient";
+ if (! isToolbarButton(b))
+ {
+ if (! m.isArmed() && ! m.isPressed() && isDrawingGradient(uiKey))
+ {
+ MetalUtils.paintGradient(g, 0, 0, b.getWidth(), b.getHeight(),
+ SwingConstants.VERTICAL,
+ uiKey);
+ paint(g, c);
+ return;
+ }
+ }
+ else if (m.isRollover() && isDrawingGradient(uiKey))
+ {
+ MetalUtils.paintGradient(g, 0, 0, b.getWidth(), b.getHeight(),
+ SwingConstants.VERTICAL,
+ uiKey);
+ paint(g, c);
+ return;
+ }
+ }
+ // Fallback if we didn't have any of the two above cases.
+ super.update(g, c);
+ }
+
+ /**
+ * Returns <code>true</code> when the button is a toolbar button,
+ * <code>false</code> otherwise.
+ *
+ * @param b the button component to test
+ *
+ * @return <code>true</code> when the button is a toolbar button,
+ * <code>false</code> otherwise
+ */
+ private boolean isToolbarButton(Component b)
+ {
+ Component parent = b.getParent();
+ return parent instanceof JToolBar;
+ }
+
+ /**
+ * Returns <code>true</code> if we should draw the button gradient,
+ * <code>false</code> otherwise.
+ *
+ * @param uiKey the UIManager key for the gradient
+ *
+ * @return <code>true</code> if we should draw the button gradient,
+ * <code>false</code> otherwise
+ */
+ private boolean isDrawingGradient(String uiKey)
+ {
+ return (UIManager.get(uiKey) != null);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java
new file mode 100644
index 000000000..5e0ac066c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java
@@ -0,0 +1,142 @@
+/* MetalCheckBoxIcon.java -- An icon for JCheckBoxes in the Metal L&F
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.io.Serializable;
+
+import javax.swing.AbstractButton;
+import javax.swing.Icon;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.plaf.UIResource;
+
+/**
+ * An {@link Icon} used by the {@link MetalCheckBoxUI} class.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class MetalCheckBoxIcon
+ implements Icon, UIResource, Serializable
+{
+
+ /** Used to paint the border of the icon. */
+ MetalBorders.ButtonBorder border;
+
+ /**
+ * Creates a new MetalCheckBoxIcon instance.
+ */
+ public MetalCheckBoxIcon()
+ {
+ border = new MetalBorders.ButtonBorder();
+ }
+
+ /**
+ * Draws the check in the CheckBox.
+ *
+ * @param c the component to draw on
+ * @param g the Graphics context to draw with
+ * @param x the X position
+ * @param y the Y position
+ */
+ protected void drawCheck(Component c, Graphics g, int x, int y)
+ {
+ if (c.isEnabled())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(3 + x, 5 + y, 3 + x, 9 + y);
+ g.drawLine(4 + x, 5 + y, 4 + x, 9 + y);
+ g.drawLine(5 + x, 7 + y, 9 + x, 3 + y);
+ g.drawLine(5 + x, 8 + y, 9 + x, 4 + y);
+ }
+
+ /**
+ * Returns the size (both X and Y) of the checkbox icon.
+ *
+ * @return the size of the checkbox icon
+ */
+ protected int getControlSize()
+ {
+ return 13;
+ }
+
+ /**
+ * Returns the width of the icon in pixels.
+ *
+ * @return the width of the icon in pixels
+ */
+ public int getIconWidth()
+ {
+ return getControlSize();
+ }
+
+ /**
+ * Returns the height of the icon in pixels.
+ *
+ * @return the height of the icon in pixels
+ */
+ public int getIconHeight()
+ {
+ return getControlSize();
+ }
+
+ /**
+ * Paints the icon. This first paints the border of the CheckBox and
+ * if the CheckBox is selected it calls {@link #drawCheck} to draw
+ * the check.
+ *
+ * @param c the Component to draw on (gets casted to JCheckBox)
+ * @param g the Graphics context to draw with
+ * @param x the X position
+ * @param y the Y position
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ if (UIManager.get("CheckBox.gradient") != null)
+ MetalUtils.paintGradient(g, x, y, getIconWidth(), getIconHeight(),
+ SwingConstants.VERTICAL, "CheckBox.gradient");
+ border.paintBorder(c, g, x, y, getIconWidth(), getIconHeight());
+
+ AbstractButton b = (AbstractButton) c;
+ if (b.isSelected())
+ drawCheck(b, g, x, y);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java
new file mode 100644
index 000000000..11979e170
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java
@@ -0,0 +1,88 @@
+/* MetalCheckBoxUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate for the {@link JCheckBox} component.
+ */
+public class MetalCheckBoxUI
+ extends MetalRadioButtonUI
+{
+
+ // FIXME: maybe replace by a Map of instances when this becomes stateful
+ /** The shared UI instance for JCheckBoxes. */
+ private static MetalCheckBoxUI instance;
+
+ /**
+ * Constructs a new instance of MetalCheckBoxUI.
+ */
+ public MetalCheckBoxUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a shared instance of <code>MetalCheckBoxUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A shared instance of <code>MetalCheckBoxUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ if (instance == null)
+ instance = new MetalCheckBoxUI();
+ return instance;
+ }
+
+ /**
+ * Returns the prefix for properties defined in the {@link UIDefaults} table.
+ *
+ * @return The property prefix (<code>"CheckBox."</code>).
+ */
+ public String getPropertyPrefix()
+ {
+ return "CheckBox.";
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java
new file mode 100644
index 000000000..8ec8bad72
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java
@@ -0,0 +1,294 @@
+/* MetalComboBoxButton.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.CellRendererPane;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.UIManager;
+
+/**
+ * A button used by the {@link MetalComboBoxUI} class.
+ */
+public class MetalComboBoxButton
+ extends JButton
+{
+
+ /** A reference to the JComboBox that the button belongs to. */
+ protected JComboBox comboBox;
+
+ /** A reference to the JList. */
+ protected JList listBox;
+
+ /**
+ * Used for rendering the selected item.
+ */
+ protected CellRendererPane rendererPane;
+
+ /** The button icon. */
+ protected Icon comboIcon;
+
+ /** Display just the icon, or the icon plus the label. */
+ protected boolean iconOnly;
+
+ /**
+ * Creates a new button.
+ *
+ * @param cb the combo that the button is used for (<code>null</code> not
+ * permitted).
+ * @param i the icon displayed on the button.
+ * @param pane the rendering pane.
+ * @param list the list.
+ */
+ public MetalComboBoxButton(JComboBox cb, Icon i, CellRendererPane pane,
+ JList list)
+ {
+ this(cb, i, cb.isEditable(), pane, list);
+ }
+
+ /**
+ * Creates a new button.
+ *
+ * @param cb the combo that the button is used for (<code>null</code> not
+ * permitted).
+ * @param i the icon displayed on the button.
+ * @param onlyIcon a flag that specifies whether the button displays only an
+ * icon, or text as well.
+ * @param pane the rendering pane.
+ * @param list the list.
+ */
+ public MetalComboBoxButton(JComboBox cb, Icon i, boolean onlyIcon,
+ CellRendererPane pane, JList list)
+ {
+ super();
+ if (cb == null)
+ throw new NullPointerException("Null 'cb' argument");
+ comboBox = cb;
+ comboIcon = i;
+ iconOnly = onlyIcon;
+ listBox = list;
+ rendererPane = pane;
+ setRolloverEnabled(false);
+ setEnabled(comboBox.isEnabled());
+ setFocusable(comboBox.isEnabled());
+ }
+
+ /**
+ * Returns the combo box that the button is used with.
+ *
+ * @return The combo box.
+ */
+ public final JComboBox getComboBox()
+ {
+ return comboBox;
+ }
+
+ /**
+ * Sets the combo box that the button is used with.
+ *
+ * @param cb the combo box.
+ */
+ public final void setComboBox(JComboBox cb)
+ {
+ comboBox = cb;
+ }
+
+ /**
+ * Returns the icon displayed by the button. By default, this will be an
+ * instance of {@link MetalComboBoxIcon}.
+ *
+ * @return The icon displayed by the button.
+ */
+ public final Icon getComboIcon()
+ {
+ return comboIcon;
+ }
+
+ /**
+ * Sets the icon displayed by the button.
+ *
+ * @param i the icon.
+ */
+ public final void setComboIcon(Icon i)
+ {
+ comboIcon = i;
+ }
+
+ /**
+ * Returns a flag that controls whether the button displays an icon only,
+ * or text as well.
+ *
+ * @return A boolean.
+ */
+ public final boolean isIconOnly()
+ {
+ return iconOnly;
+ }
+
+ /**
+ * Sets the flag that controls whether the button displays an icon only,
+ * or text as well.
+ *
+ * @param isIconOnly the flag.
+ */
+ public final void setIconOnly(boolean isIconOnly)
+ {
+ iconOnly = isIconOnly;
+ }
+
+ /**
+ * Returns <code>false</code>, to indicate that this component is not part
+ * of the focus traversal group.
+ *
+ * @return <code>false</code>
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Enables or disables the button.
+ *
+ * @param enabled the new status.
+ */
+ public void setEnabled(boolean enabled)
+ {
+ super.setEnabled(enabled);
+ if (enabled)
+ {
+ setBackground(comboBox.getBackground());
+ setForeground(comboBox.getForeground());
+ }
+ else
+ {
+ setBackground(UIManager.getColor("ComboBox.disabledBackground"));
+ setForeground(UIManager.getColor("ComboBox.disabledForeground"));
+ }
+ }
+
+ /**
+ * Paints the component.
+ *
+ * @param g the graphics device.
+ */
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+ Insets insets = this.getInsets();
+ int w = getWidth() - (insets.left + insets.right);
+ int h = getHeight() - (insets.top + insets.bottom);
+ if (h > 0 && w > 0)
+ {
+ int x1 = insets.left;
+ int y1 = insets.top;
+ int x2 = x1 + (w - 1);
+ int y2 = y1 + (h - 1);
+ int iconWidth = 0;
+ int iconX = x2;
+ if (comboIcon != null)
+ {
+ iconWidth = comboIcon.getIconWidth();
+ int iconHeight = comboIcon.getIconHeight();
+ int iconY;
+ if (iconOnly)
+ {
+ iconX = getWidth() / 2 - iconWidth / 2;
+ iconY = getHeight() / 2 - iconHeight / 2;
+ }
+ else
+ {
+ iconX = x1 + (w - 1) - iconWidth;
+ iconY = y1 + (y2 - y1) / 2 - iconHeight / 2;
+ }
+ comboIcon.paintIcon(this, g, iconX, iconY);
+ if (this.hasFocus())
+ {
+ g.setColor(MetalLookAndFeel.getFocusColor());
+ g.drawRect(x1 - 1, y1 - 1, w + 3, h + 1);
+ }
+ }
+ if (! iconOnly && comboBox != null)
+ {
+ ListCellRenderer renderer = comboBox.getRenderer();
+ boolean pressed = this.getModel().isPressed();
+ Component comp = renderer.getListCellRendererComponent(listBox,
+ comboBox.getSelectedItem(), -1, false, false);
+ comp.setFont(rendererPane.getFont());
+
+ if ((model.isArmed() && model.isPressed())
+ || (comboBox.isFocusOwner() && !comboBox.isPopupVisible()))
+ {
+ if (isOpaque())
+ {
+ comp.setBackground(UIManager.getColor("Button.select"));
+ comp.setForeground(comboBox.getForeground());
+ }
+ }
+ else if (! comboBox.isEnabled())
+ {
+ if (this.isOpaque())
+ {
+ Color dbg =
+ UIManager.getColor("ComboBox.disabledBackground");
+ comp.setBackground(dbg);
+ Color dfg =
+ UIManager.getColor("ComboBox.disabledForeground");
+ comp.setForeground(dfg);
+ }
+ }
+ else
+ {
+ comp.setForeground(comboBox.getForeground());
+ comp.setBackground(comboBox.getBackground());
+ }
+ int wr = w - (insets.right + iconWidth);
+ rendererPane.paintComponent(g, comp, this, x1, y1, wr, h);
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java
new file mode 100644
index 000000000..8872d74e2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java
@@ -0,0 +1,190 @@
+/* MetalComboBoxEditor.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.JTextField;
+import javax.swing.border.AbstractBorder;
+import javax.swing.plaf.basic.BasicComboBoxEditor;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+
+/**
+ * An editor used by the {@link MetalComboBoxUI} class.
+ */
+public class MetalComboBoxEditor extends BasicComboBoxEditor
+{
+ /**
+ * A border used for the {@link JTextField} component.
+ */
+ static class MetalComboBoxEditorBorder extends AbstractBorder
+ {
+ /**
+ * Creates a new border instance.
+ */
+ public MetalComboBoxEditorBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ * @param w the width.
+ * @param h the height.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ g.translate(x, y);
+ if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, w - 1, 0);
+ g.drawLine(0, 0, 0, h - 1);
+ g.drawLine(0, h - 1, w - 1, h - 1);
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(1, 1, w - 2, 1);
+ g.drawLine(1, 1, 1, h - 2);
+ g.drawLine(1, h - 2, w - 1, h - 2);
+ g.drawLine(w - 1, 1, w - 1, h - 2);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, w - 1, 0);
+ g.drawLine(0, 0, 0, h - 2);
+ g.drawLine(0, h - 2, w - 1, h - 2);
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(1, 1, w - 1, 1);
+ g.drawLine(1, 1, 1, h - 1);
+ g.drawLine(1, h - 1, w - 1, h - 1);
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(1, h - 2, 1, h - 2);
+ }
+ g.translate(-x, -y);
+ }
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge, which is zero
+ * for the default implementation provided by AbstractButton.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return editorBorderInsets;
+ }
+ }
+
+ /**
+ * A subclass of {@link MetalComboBoxEditor} that implements the
+ * {@link javax.swing.plaf.UIResource} interface.
+ */
+ public static class UIResource extends MetalComboBoxEditor
+ implements javax.swing.plaf.UIResource
+ {
+ /**
+ * Creates a new instance.
+ */
+ public UIResource()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A special textfield implementation for the MetalComboBoxEditor.
+ */
+ private class EditorTextField extends JTextField
+ {
+ EditorTextField(String s, int columns)
+ {
+ super(s, columns);
+ }
+
+ /**
+ * Tests seem to show that the textfield in MetalComboBoxEditors have
+ * a height + 4.
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension size = super.getPreferredSize();
+ size.height += 4;
+ return size;
+ }
+
+ /**
+ * Tests seem to show that the textfield in MetalComboBoxEditors have
+ * a height + 4.
+ */
+ public Dimension getMinimumSize()
+ {
+ Dimension size = super.getMinimumSize();
+ size.height += 4;
+ return size;
+ }
+ }
+
+ /** The editor's border insets. */
+ protected static Insets editorBorderInsets = new Insets(2, 2, 2, 0);
+
+ /**
+ * Creates a new editor.
+ */
+ public MetalComboBoxEditor()
+ {
+ editor = new EditorTextField("", 9);
+ editor.setBorder(new MetalComboBoxEditorBorder());
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java
new file mode 100644
index 000000000..45c8eadc5
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java
@@ -0,0 +1,102 @@
+/* MetalComboBoxIcon.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.io.Serializable;
+
+import javax.swing.Icon;
+
+/**
+ * An icon used by the {@link MetalComboBoxUI} class.
+ */
+public class MetalComboBoxIcon implements Icon, Serializable
+{
+
+ /**
+ * Creates a new icon.
+ */
+ public MetalComboBoxIcon()
+ {
+ // nothing required.
+ }
+
+ /**
+ * Returns the icon width, which for this icon is 10 pixels.
+ *
+ * @return <code>10</code>.
+ */
+ public int getIconWidth()
+ {
+ return 10;
+ }
+
+ /**
+ * Returns the icon height, which for this icon is 5 pixels.
+ *
+ * @return <code>5</code>.
+ */
+ public int getIconHeight()
+ {
+ return 5;
+ }
+
+ /**
+ * Paints the icon at the location (x, y).
+ *
+ * @param c the combo box (ignored here).
+ * @param g the graphics device.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+ if (c.isEnabled())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ for (int i = 0; i < 5; i++)
+ g.drawLine(x + i, y + i, x + 9 - i, y + i);
+ g.setColor(savedColor);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java
new file mode 100644
index 000000000..b9019b70d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java
@@ -0,0 +1,372 @@
+/* MetalComboBoxUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.ComboBoxEditor;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicComboBoxUI;
+import javax.swing.plaf.basic.BasicComboPopup;
+import javax.swing.plaf.basic.ComboPopup;
+
+
+/**
+ * A UI delegate for the {@link JComboBox} component.
+ */
+public class MetalComboBoxUI extends BasicComboBoxUI
+{
+ /**
+ * A layout manager that arranges the editor component (if active) and the
+ * button that make up the combo box.
+ */
+ public class MetalComboBoxLayoutManager
+ extends BasicComboBoxUI.ComboBoxLayoutManager
+ {
+ /**
+ * Creates a new instance of the layout manager.
+ */
+ public MetalComboBoxLayoutManager()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Arranges the editor (if visible) and button that comprise the combo
+ * box.
+ *
+ * @param parent the parent.
+ */
+ public void layoutContainer(Container parent)
+ {
+ layoutComboBox(parent, this);
+ }
+
+ /**
+ * Calls the <code>layoutContainer(Container)</code> method in the super
+ * class.
+ *
+ * @param parent the container.
+ */
+ public void superLayout(Container parent)
+ {
+ super.layoutContainer(parent);
+ }
+ }
+
+ /**
+ * A listener used to handle property changes in the {@link JComboBox}
+ * component, to ensure that the UI delegate accurately reflects the current
+ * state in the rendering onscreen.
+ */
+ public class MetalPropertyChangeListener
+ extends BasicComboBoxUI.PropertyChangeHandler
+ {
+ /**
+ * Creates a new listener.
+ */
+ public MetalPropertyChangeListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Handles a property change event, updating the UI components as
+ * appropriate.
+ *
+ * @param e the event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ super.propertyChange(e);
+ String name = e.getPropertyName();
+ if (name.equals("editable"))
+ editablePropertyChanged(e);
+ else if (name.equals("enabled"))
+ {
+ if (arrowButton instanceof MetalComboBoxButton)
+ {
+ arrowButton.setFocusable(!comboBox.isEditable()
+ && comboBox.isEnabled());
+ comboBox.repaint();
+ }
+ }
+ else if (name.equals("background"))
+ {
+ Color c = (Color) e.getNewValue();
+ arrowButton.setBackground(c);
+ listBox.setBackground(c);
+ }
+ else if (name.equals("foreground"))
+ {
+ Color c = (Color) e.getNewValue();
+ arrowButton.setForeground(c);
+ listBox.setForeground(c);
+ }
+ }
+ }
+
+ /**
+ * A popup menu for the combo-box.
+ *
+ * @see #createPopup()
+ *
+ * @deprecated 1.4
+ */
+ public class MetalComboPopup extends BasicComboPopup
+ {
+ /**
+ * Creates a new popup.
+ *
+ * @param cBox the combo box.
+ */
+ public MetalComboPopup(JComboBox cBox)
+ {
+ super(cBox);
+ }
+
+ public void delegateFocus(MouseEvent e)
+ {
+ super.delegateFocus(e);
+ }
+ }
+
+ /**
+ * Constructs a new instance of MetalComboBoxUI.
+ */
+ public MetalComboBoxUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns an instance of MetalComboBoxUI.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return an instance of MetalComboBoxUI
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalComboBoxUI();
+ }
+
+ /**
+ * Creates an editor for the combo box.
+ *
+ * @return An editor.
+ */
+ protected ComboBoxEditor createEditor()
+ {
+ return new MetalComboBoxEditor.UIResource();
+ }
+
+ /**
+ * Creates a popup for the combo box.
+ *
+ * @return A popup.
+ */
+ protected ComboPopup createPopup()
+ {
+ return super.createPopup();
+ }
+
+ /**
+ * Creates a new button for use in rendering the JComboBox.
+ *
+ * @return A button.
+ */
+ protected JButton createArrowButton()
+ {
+ JButton button = new MetalComboBoxButton(comboBox, new MetalComboBoxIcon(),
+ currentValuePane, listBox);
+ button.setMargin(new Insets(0, 1, 1, 3));
+ return button;
+ }
+
+ /**
+ * Creates a new property change listener.
+ *
+ * @return A new property change listener.
+ */
+ public PropertyChangeListener createPropertyChangeListener()
+ {
+ return new MetalPropertyChangeListener();
+ }
+
+ public void paint(Graphics g, JComponent c)
+ {
+ // do nothing, the button and text field are painted elsewhere
+ }
+
+ /**
+ * Updates the button and text field to reflect a change in the 'editable'
+ * property.
+ *
+ * @param e the event.
+ *
+ * @deprecated 1.4
+ */
+ protected void editablePropertyChanged(PropertyChangeEvent e)
+ {
+ if (arrowButton instanceof MetalComboBoxButton)
+ {
+ MetalComboBoxButton b = (MetalComboBoxButton) arrowButton;
+ b.setIconOnly(comboBox.isEditable());
+ b.setFocusable(!comboBox.isEditable() && comboBox.isEnabled());
+ comboBox.repaint();
+ }
+ }
+
+ /**
+ * Creates a new layout manager for the UI delegate.
+ *
+ * @return A new layout manager.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new MetalComboBoxLayoutManager();
+ }
+
+ /**
+ * Not used in Classpath.
+ *
+ * @deprecated 1.4
+ */
+ protected void removeListeners()
+ {
+ // no longer used in JDK 1.4
+ }
+
+ /**
+ * Returns the minimum size for the combo.
+ *
+ * @param c the component
+ *
+ * @return The minimum size for the combo box.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ if (!isMinimumSizeDirty)
+ return new Dimension(cachedMinimumSize);
+
+ Dimension d;
+ if (!comboBox.isEditable() && arrowButton != null
+ && arrowButton instanceof MetalComboBoxButton)
+ {
+ MetalComboBoxButton b = (MetalComboBoxButton) arrowButton;
+ d = getDisplaySize();
+ Insets arrowInsets = b.getInsets();
+ Insets comboInsets = comboBox.getInsets();
+ Icon icon = b.getComboIcon();
+ d.width += comboInsets.left + comboInsets.right;
+ d.width += arrowInsets.left + arrowInsets.right;
+ d.width += arrowInsets.right + icon.getIconWidth();
+ d.height += comboInsets.top + comboInsets.bottom;
+ d.height += arrowInsets.top + arrowInsets.bottom;
+ }
+ else if (comboBox.isEditable() && arrowButton != null && editor != null)
+ {
+ d = super.getMinimumSize(c);
+ Insets arrowMargin = arrowButton.getMargin();
+ d.height += arrowMargin.top + arrowMargin.bottom;
+ d.width += arrowMargin.left + arrowMargin.right;
+ }
+ else
+ {
+ d = super.getMinimumSize(c);
+ }
+ cachedMinimumSize.setSize(d.width, d.height);
+ isMinimumSizeDirty = false;
+ return new Dimension(cachedMinimumSize);
+ }
+
+ /**
+ * Configures the editor for this combo box.
+ */
+ public void configureEditor()
+ {
+ super.configureEditor();
+ if (popupKeyListener != null)
+ editor.removeKeyListener(popupKeyListener);
+ if (focusListener != null)
+ editor.addFocusListener(focusListener);
+ }
+
+ /**
+ * Unconfigures the editor for this combo box.
+ */
+ public void unconfigureEditor()
+ {
+ super.unconfigureEditor();
+ if (focusListener != null)
+ editor.removeFocusListener(focusListener);
+ }
+
+ /**
+ * Lays out the ComboBox
+ */
+ public void layoutComboBox(Container parent,
+ MetalComboBoxUI.MetalComboBoxLayoutManager manager)
+ {
+ if (comboBox.isEditable())
+ manager.superLayout(parent);
+ else if (arrowButton != null)
+ {
+ Insets comboInsets = comboBox.getInsets();
+ int width = comboBox.getWidth();
+ int height = comboBox.getHeight();
+ arrowButton.setBounds(comboInsets.left, comboInsets.top,
+ width - (comboInsets.left + comboInsets.right),
+ height - (comboInsets.top + comboInsets.bottom));
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java
new file mode 100644
index 000000000..0c1163a8e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java
@@ -0,0 +1,72 @@
+/* MetalDesktopIconUI.java
+ Copyright (C) 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import javax.swing.JComponent;
+import javax.swing.JInternalFrame;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicDesktopIconUI;
+
+/**
+ * A UI delegate for the {@link JInternalFrame.JDesktopIcon} component.
+ */
+public class MetalDesktopIconUI
+ extends BasicDesktopIconUI
+{
+
+ /**
+ * Constructs a new instance of <code>MetalDesktopIconUI</code>.
+ */
+ public MetalDesktopIconUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a new <code>MetalDesktopIconUI</code> instance.
+ *
+ * @param component the component (ignored).
+ *
+ * @return A new <code>MetalDesktopIconUI</code> instance.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalDesktopIconUI();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java
new file mode 100644
index 000000000..df49edf61
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java
@@ -0,0 +1,2120 @@
+/* MetalFileChooserUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.GridLayout;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.util.Date;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractListModel;
+import javax.swing.ActionMap;
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+import javax.swing.ListModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileSystemView;
+import javax.swing.filechooser.FileView;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicFileChooserUI;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableModel;
+
+
+/**
+ * A UI delegate for the {@link JFileChooser} component. This class is only
+ * partially implemented and is not usable yet.
+ */
+public class MetalFileChooserUI
+ extends BasicFileChooserUI
+{
+
+ /**
+ * A renderer for the files and directories in the file chooser table.
+ */
+ class TableFileRenderer
+ extends DefaultTableCellRenderer
+ {
+
+ /**
+ * Creates a new renderer.
+ */
+ public TableFileRenderer()
+ {
+ super();
+ }
+
+ /**
+ * Returns a component that can render the specified value.
+ *
+ * @param table the table
+ * @param value the string value of the cell
+ * @param isSelected is the item selected?
+ * @param hasFocus does the item have the focus?
+ * @param row the row
+ * @param column the column
+ *
+ * @return The renderer.
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus, int row, int column)
+ {
+ if (column == 0)
+ {
+ FileView v = getFileView(getFileChooser());
+ ListModel lm = fileList.getModel();
+ if (row < lm.getSize())
+ setIcon(v.getIcon((File) lm.getElementAt(row)));
+ }
+ else
+ setIcon(null);
+
+ setText(value.toString());
+ setOpaque(true);
+ setEnabled(table.isEnabled());
+ setFont(fileList.getFont());
+
+ if (startEditing && column == 0 || !isSelected)
+ {
+ setBackground(table.getBackground());
+ setForeground(table.getForeground());
+ }
+ else
+ {
+ setBackground(table.getSelectionBackground());
+ setForeground(table.getSelectionForeground());
+ }
+
+ if (hasFocus)
+ setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
+ else
+ setBorder(noFocusBorder);
+
+ return this;
+ }
+ }
+
+ /**
+ * ActionListener for the list view.
+ */
+ class ListViewActionListener implements ActionListener
+ {
+
+ /**
+ * This method is invoked when an action occurs.
+ *
+ * @param e -
+ * the <code>ActionEvent</code> that occurred
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (!listView)
+ {
+ int[] index = fileTable.getSelectedRows();
+ listView = true;
+ JFileChooser fc = getFileChooser();
+ fc.remove(fileTablePanel);
+ createList(fc);
+
+ fileList.getSelectionModel().clearSelection();
+ if (index.length > 0)
+ for (int i = 0; i < index.length; i++)
+ fileList.getSelectionModel().addSelectionInterval(index[i], index[i]);
+
+ fc.add(fileListPanel, BorderLayout.CENTER);
+ fc.revalidate();
+ fc.repaint();
+ }
+ }
+ }
+
+ /**
+ * ActionListener for the details view.
+ */
+ class DetailViewActionListener implements ActionListener
+ {
+
+ /**
+ * This method is invoked when an action occurs.
+ *
+ * @param e -
+ * the <code>ActionEvent</code> that occurred
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (listView)
+ {
+ int[] index = fileList.getSelectedIndices();
+ JFileChooser fc = getFileChooser();
+ listView = false;
+ fc.remove(fileListPanel);
+
+ if (fileTable == null)
+ createDetailsView(fc);
+ else
+ updateTable();
+
+ fileTable.getSelectionModel().clearSelection();
+ if (index.length > 0)
+ {
+ for (int i = 0; i < index.length; i++)
+ fileTable.getSelectionModel().addSelectionInterval(index[i], index[i]);
+ }
+
+ fc.add(fileTablePanel, BorderLayout.CENTER);
+ fc.revalidate();
+ fc.repaint();
+ }
+ }
+ }
+
+ /**
+ * A property change listener.
+ */
+ class MetalFileChooserPropertyChangeListener
+ implements PropertyChangeListener
+ {
+ /**
+ * Default constructor.
+ */
+ public MetalFileChooserPropertyChangeListener()
+ {
+ }
+
+ /**
+ * Handles a property change event.
+ *
+ * @param e the event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ JFileChooser filechooser = getFileChooser();
+
+ String n = e.getPropertyName();
+ if (n.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY))
+ {
+ int mode = -1;
+ if (filechooser.isMultiSelectionEnabled())
+ mode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
+ else
+ mode = ListSelectionModel.SINGLE_SELECTION;
+
+ if (listView)
+ fileList.setSelectionMode(mode);
+ else
+ fileTable.setSelectionMode(mode);
+ }
+ else if (n.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY))
+ {
+ File file = filechooser.getSelectedFile();
+
+ if (file != null
+ && filechooser.getDialogType() == JFileChooser.SAVE_DIALOG)
+ {
+ if (file.isDirectory() && filechooser.isTraversable(file))
+ {
+ directoryLabel = look;
+ dirLabel.setText(directoryLabel);
+ filechooser.setApproveButtonText(openButtonText);
+ filechooser.setApproveButtonToolTipText(openButtonToolTipText);
+ }
+ else if (file.isFile())
+ {
+ directoryLabel = save;
+ dirLabel.setText(directoryLabel);
+ filechooser.setApproveButtonText(saveButtonText);
+ filechooser.setApproveButtonToolTipText(saveButtonToolTipText);
+ }
+ }
+
+ if (file == null)
+ setFileName(null);
+ else if (file.isFile() || filechooser.getFileSelectionMode()
+ != JFileChooser.FILES_ONLY)
+ setFileName(file.getName());
+ int index = -1;
+ index = getModel().indexOf(file);
+ if (index >= 0)
+ {
+ if (listView)
+ {
+ fileList.setSelectedIndex(index);
+ fileList.ensureIndexIsVisible(index);
+ fileList.revalidate();
+ fileList.repaint();
+ }
+ else
+ {
+ fileTable.getSelectionModel().addSelectionInterval(index, index);
+ fileTable.scrollRectToVisible(fileTable.getCellRect(index, 0, true));
+ fileTable.revalidate();
+ fileTable.repaint();
+ }
+ }
+ }
+
+ else if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY))
+ {
+ if (listView)
+ {
+ fileList.clearSelection();
+ fileList.revalidate();
+ fileList.repaint();
+ }
+ else
+ {
+ fileTable.clearSelection();
+ fileTable.revalidate();
+ fileTable.repaint();
+ }
+
+ setDirectorySelected(false);
+ File currentDirectory = filechooser.getCurrentDirectory();
+ setDirectory(currentDirectory);
+ boolean hasParent = currentDirectory.getParentFile() != null;
+ getChangeToParentDirectoryAction().setEnabled(hasParent);
+ }
+
+ else if (n.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY))
+ {
+ filterModel.propertyChange(e);
+ }
+ else if (n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY))
+ {
+ filterModel.propertyChange(e);
+ }
+ else if (n.equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY)
+ || n.equals(JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY))
+ {
+ Window owner = SwingUtilities.windowForComponent(filechooser);
+ if (owner instanceof JDialog)
+ ((JDialog) owner).setTitle(getDialogTitle(filechooser));
+ approveButton.setText(getApproveButtonText(filechooser));
+ approveButton.setToolTipText(
+ getApproveButtonToolTipText(filechooser));
+ approveButton.setMnemonic(getApproveButtonMnemonic(filechooser));
+ }
+
+ else if (n.equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY))
+ approveButton.setText(getApproveButtonText(filechooser));
+
+ else if (n.equals(
+ JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY))
+ approveButton.setToolTipText(getApproveButtonToolTipText(filechooser));
+
+ else if (n.equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY))
+ approveButton.setMnemonic(getApproveButtonMnemonic(filechooser));
+
+ else if (n.equals(
+ JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY))
+ {
+ if (filechooser.getControlButtonsAreShown())
+ {
+ topPanel.add(controls, BorderLayout.EAST);
+ }
+ else
+ topPanel.remove(controls);
+ topPanel.revalidate();
+ topPanel.repaint();
+ topPanel.doLayout();
+ }
+
+ else if (n.equals(
+ JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY))
+ {
+ if (filechooser.isAcceptAllFileFilterUsed())
+ filechooser.addChoosableFileFilter(
+ getAcceptAllFileFilter(filechooser));
+ else
+ filechooser.removeChoosableFileFilter(
+ getAcceptAllFileFilter(filechooser));
+ }
+
+ else if (n.equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY))
+ {
+ JComponent old = (JComponent) e.getOldValue();
+ if (old != null)
+ getAccessoryPanel().remove(old);
+ JComponent newval = (JComponent) e.getNewValue();
+ if (newval != null)
+ getAccessoryPanel().add(newval);
+ }
+
+ if (n.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)
+ || n.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)
+ || n.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY))
+ {
+ // Remove editing component
+ if (fileTable != null)
+ fileTable.removeAll();
+ if (fileList != null)
+ fileList.removeAll();
+ startEditing = false;
+
+ // Set text on button back to original.
+ if (filechooser.getDialogType() == JFileChooser.SAVE_DIALOG)
+ {
+ directoryLabel = save;
+ dirLabel.setText(directoryLabel);
+ filechooser.setApproveButtonText(saveButtonText);
+ filechooser.setApproveButtonToolTipText(saveButtonToolTipText);
+ }
+
+ rescanCurrentDirectory(filechooser);
+ }
+
+ filechooser.revalidate();
+ filechooser.repaint();
+ }
+ }
+
+ /**
+ * A combo box model containing the selected directory and all its parent
+ * directories.
+ */
+ protected class DirectoryComboBoxModel
+ extends AbstractListModel
+ implements ComboBoxModel
+ {
+ /** Storage for the items in the model. */
+ private List items;
+
+ /** The index of the selected item. */
+ private int selectedIndex;
+
+ /**
+ * Creates a new model.
+ */
+ public DirectoryComboBoxModel()
+ {
+ items = new java.util.ArrayList();
+ selectedIndex = -1;
+ }
+
+ /**
+ * Returns the number of items in the model.
+ *
+ * @return The number of items in the model.
+ */
+ public int getSize()
+ {
+ return items.size();
+ }
+
+ /**
+ * Returns the item at the specified index.
+ *
+ * @param index the item index.
+ *
+ * @return The item.
+ */
+ public Object getElementAt(int index)
+ {
+ return items.get(index);
+ }
+
+ /**
+ * Returns the depth of the item at the given <code>index</code>.
+ *
+ * @param index the item index.
+ *
+ * @return The depth.
+ */
+ public int getDepth(int index)
+ {
+ return Math.max(index, 0);
+ }
+
+ /**
+ * Returns the selected item, or <code>null</code> if no item is selected.
+ *
+ * @return The selected item, or <code>null</code>.
+ */
+ public Object getSelectedItem()
+ {
+ if (selectedIndex >= 0)
+ return items.get(selectedIndex);
+ else
+ return null;
+ }
+
+ /**
+ * Sets the selected item. This clears all the directories from the
+ * existing list, and repopulates it with the new selected directory
+ * and all its parent directories.
+ *
+ * @param selectedDirectory the selected directory.
+ */
+ public void setSelectedItem(Object selectedDirectory)
+ {
+ items.clear();
+ FileSystemView fsv = getFileChooser().getFileSystemView();
+ File parent = (File) selectedDirectory;
+ while (parent != null)
+ {
+ items.add(0, parent);
+ parent = fsv.getParentDirectory(parent);
+ }
+ selectedIndex = items.indexOf(selectedDirectory);
+ fireContentsChanged(this, 0, items.size() - 1);
+ }
+
+ }
+
+ /**
+ * Handles changes to the selection in the directory combo box.
+ */
+ protected class DirectoryComboBoxAction
+ extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ protected DirectoryComboBoxAction()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Handles the action event.
+ *
+ * @param e the event.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JFileChooser fc = getFileChooser();
+ fc.setCurrentDirectory((File) directoryModel.getSelectedItem());
+ }
+ }
+
+ /**
+ * A renderer for the items in the directory combo box.
+ */
+ class DirectoryComboBoxRenderer
+ extends DefaultListCellRenderer
+ {
+ /**
+ * This is the icon that is displayed in the combobox. This wraps
+ * the standard icon and adds indendation.
+ */
+ private IndentIcon indentIcon;
+
+ /**
+ * Creates a new renderer.
+ */
+ public DirectoryComboBoxRenderer(JFileChooser fc)
+ {
+ indentIcon = new IndentIcon();
+ }
+
+ /**
+ * Returns a component that can be used to paint the given value within
+ * the list.
+ *
+ * @param list the list.
+ * @param value the value (a {@link File}).
+ * @param index the item index.
+ * @param isSelected is the item selected?
+ * @param cellHasFocus does the list cell have focus?
+ *
+ * @return The list cell renderer.
+ */
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index,
+ boolean isSelected,
+ boolean cellHasFocus)
+ {
+ super.getListCellRendererComponent(list, value, index, isSelected,
+ cellHasFocus);
+ File file = (File) value;
+ setText(getFileChooser().getName(file));
+
+ // Install indented icon.
+ Icon icon = getFileChooser().getIcon(file);
+ indentIcon.setIcon(icon);
+ int depth = directoryModel.getDepth(index);
+ indentIcon.setDepth(depth);
+ setIcon(indentIcon);
+
+ return this;
+ }
+ }
+
+ /**
+ * An icon that wraps another icon and adds indentation.
+ */
+ class IndentIcon
+ implements Icon
+ {
+
+ /**
+ * The indentation level.
+ */
+ private static final int INDENT = 10;
+
+ /**
+ * The wrapped icon.
+ */
+ private Icon icon;
+
+ /**
+ * The current depth.
+ */
+ private int depth;
+
+ /**
+ * Sets the icon to be wrapped.
+ *
+ * @param i the icon
+ */
+ void setIcon(Icon i)
+ {
+ icon = i;
+ }
+
+ /**
+ * Sets the indentation depth.
+ *
+ * @param d the depth to set
+ */
+ void setDepth(int d)
+ {
+ depth = d;
+ }
+
+ public int getIconHeight()
+ {
+ return icon.getIconHeight();
+ }
+
+ public int getIconWidth()
+ {
+ return icon.getIconWidth() + depth * INDENT;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ icon.paintIcon(c, g, x + depth * INDENT, y);
+ }
+
+ }
+
+ /**
+ * A renderer for the files and directories in the file chooser.
+ */
+ protected class FileRenderer
+ extends DefaultListCellRenderer
+ {
+
+ /**
+ * Creates a new renderer.
+ */
+ protected FileRenderer()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a component that can render the specified value.
+ *
+ * @param list the list.
+ * @param value the value (a {@link File}).
+ * @param index the index.
+ * @param isSelected is the item selected?
+ * @param cellHasFocus does the item have the focus?
+ *
+ * @return The renderer.
+ */
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected, boolean cellHasFocus)
+ {
+ FileView v = getFileView(getFileChooser());
+ File f = (File) value;
+ if (f != null)
+ {
+ setText(v.getName(f));
+ setIcon(v.getIcon(f));
+ }
+ else
+ {
+ setText("");
+ setIcon(null);
+ }
+ setOpaque(true);
+ if (isSelected)
+ {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ }
+ else
+ {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+
+ setEnabled(list.isEnabled());
+ setFont(list.getFont());
+
+ if (cellHasFocus)
+ setBorder(UIManager.getBorder("List.focusCellHighlightBorder"));
+ else
+ setBorder(noFocusBorder);
+ return this;
+ }
+ }
+
+ /**
+ * A combo box model for the file selection filters.
+ */
+ protected class FilterComboBoxModel
+ extends AbstractListModel
+ implements ComboBoxModel, PropertyChangeListener
+ {
+
+ /** Storage for the filters in the model. */
+ protected FileFilter[] filters;
+
+ /** The index of the selected file filter. */
+ private Object selected;
+
+ /**
+ * Creates a new model.
+ */
+ protected FilterComboBoxModel()
+ {
+ filters = new FileFilter[1];
+ filters[0] = getAcceptAllFileFilter(getFileChooser());
+ selected = filters[0];
+ }
+
+ /**
+ * Handles property changes.
+ *
+ * @param e the property change event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY))
+ {
+ JFileChooser fc = getFileChooser();
+ FileFilter[] choosableFilters = fc.getChoosableFileFilters();
+ filters = choosableFilters;
+ fireContentsChanged(this, 0, filters.length);
+ selected = e.getNewValue();
+ fireContentsChanged(this, -1, -1);
+ }
+ else if (e.getPropertyName().equals(
+ JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY))
+ {
+ // repopulate list
+ JFileChooser fc = getFileChooser();
+ FileFilter[] choosableFilters = fc.getChoosableFileFilters();
+ filters = choosableFilters;
+ fireContentsChanged(this, 0, filters.length);
+ }
+ }
+
+ /**
+ * Sets the selected filter.
+ *
+ * @param filter the filter (<code>null</code> ignored).
+ */
+ public void setSelectedItem(Object filter)
+ {
+ if (filter != null)
+ {
+ selected = filter;
+ fireContentsChanged(this, -1, -1);
+ }
+ }
+
+ /**
+ * Returns the selected file filter.
+ *
+ * @return The selected file filter.
+ */
+ public Object getSelectedItem()
+ {
+ return selected;
+ }
+
+ /**
+ * Returns the number of items in the model.
+ *
+ * @return The number of items in the model.
+ */
+ public int getSize()
+ {
+ return filters.length;
+ }
+
+ /**
+ * Returns the item at the specified index.
+ *
+ * @param index the item index.
+ *
+ * @return The item at the specified index.
+ */
+ public Object getElementAt(int index)
+ {
+ return filters[index];
+ }
+
+ }
+
+ /**
+ * A renderer for the items in the file filter combo box.
+ */
+ public class FilterComboBoxRenderer
+ extends DefaultListCellRenderer
+ {
+ /**
+ * Creates a new renderer.
+ */
+ public FilterComboBoxRenderer()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a component that can be used to paint the given value within
+ * the list.
+ *
+ * @param list the list.
+ * @param value the value (a {@link FileFilter}).
+ * @param index the item index.
+ * @param isSelected is the item selected?
+ * @param cellHasFocus does the list cell have focus?
+ *
+ * @return This component as the renderer.
+ */
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected, boolean cellHasFocus)
+ {
+ super.getListCellRendererComponent(list, value, index, isSelected,
+ cellHasFocus);
+ FileFilter filter = (FileFilter) value;
+ setText(filter.getDescription());
+ return this;
+ }
+ }
+
+ /**
+ * A listener for selection events in the file list.
+ *
+ * @see #createListSelectionListener(JFileChooser)
+ */
+ class MetalFileChooserSelectionListener
+ implements ListSelectionListener
+ {
+ /**
+ * Creates a new <code>SelectionListener</code> object.
+ */
+ protected MetalFileChooserSelectionListener()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Makes changes to different properties when
+ * a value has changed in the filechooser's selection.
+ *
+ * @param e - the list selection event that occured.
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ File f = (File) fileList.getSelectedValue();
+ if (f == null)
+ return;
+ JFileChooser filechooser = getFileChooser();
+ if (! filechooser.isTraversable(f))
+ filechooser.setSelectedFile(f);
+ else
+ filechooser.setSelectedFile(null);
+ }
+ }
+
+ /**
+ * A mouse listener for the {@link JFileChooser}.
+ * This listener is used for editing filenames.
+ */
+ protected class SingleClickListener
+ extends MouseAdapter
+ {
+
+ /** Stores instance of the list */
+ JList list;
+
+ /**
+ * Stores the current file that is being edited.
+ * It is null if nothing is currently being edited.
+ */
+ File editFile;
+
+ /** The current file chooser. */
+ JFileChooser fc;
+
+ /** The last file selected. */
+ Object lastSelected;
+
+ /** The textfield used for editing. */
+ JTextField editField;
+
+ /**
+ * Creates a new listener.
+ *
+ * @param list the directory/file list.
+ */
+ public SingleClickListener(JList list)
+ {
+ this.list = list;
+ editFile = null;
+ fc = getFileChooser();
+ lastSelected = null;
+ startEditing = false;
+ }
+
+ /**
+ * Receives notification of a mouse click event.
+ *
+ * @param e the event.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1)
+ {
+ int index = list.locationToIndex(e.getPoint());
+ File[] sf = fc.getSelectedFiles();
+ if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1))
+ && index >= 0 && !startEditing && list.isSelectedIndex(index))
+ {
+ Object tmp = list.getModel().getElementAt(index);
+ if (lastSelected != null && lastSelected.equals(tmp))
+ editFile(index);
+ lastSelected = tmp;
+ }
+ else
+ completeEditing();
+ }
+ else
+ completeEditing();
+ }
+
+ /**
+ * Sets up the text editor for the current file.
+ *
+ * @param index -
+ * the current index of the item in the list to be edited.
+ */
+ void editFile(int index)
+ {
+ Rectangle bounds = list.getCellBounds(index, index);
+ list.scrollRectToVisible(bounds);
+ editFile = (File) list.getModel().getElementAt(index);
+ if (editFile.canWrite())
+ {
+ startEditing = true;
+ editField = new JTextField(editFile.getName());
+ editField.addActionListener(new EditingActionListener());
+
+ Icon icon = getFileView(fc).getIcon(editFile);
+ if (icon != null)
+ {
+ int padding = icon.getIconWidth() + 4;
+ bounds.x += padding;
+ bounds.width -= padding;
+ }
+ editField.setBounds(bounds);
+
+ list.add(editField);
+
+ editField.requestFocus();
+ editField.selectAll();
+ }
+ else
+ completeEditing();
+ list.repaint();
+ }
+
+ /**
+ * Completes the editing.
+ */
+ void completeEditing()
+ {
+ if (editField != null && editFile != null)
+ {
+ String text = editField.getText();
+ if (text != null && text != "" && !text.equals(fc.getName(editFile)))
+ {
+ File f = fc.getFileSystemView().
+ createFileObject(fc.getCurrentDirectory(), text);
+ if ( editFile.renameTo(f) )
+ rescanCurrentDirectory(fc);
+ }
+ list.remove(editField);
+ }
+ startEditing = false;
+ editFile = null;
+ lastSelected = null;
+ editField = null;
+ list.repaint();
+ }
+
+ /**
+ * ActionListener for the editing text field.
+ */
+ class EditingActionListener implements ActionListener
+ {
+
+ /**
+ * This method is invoked when an action occurs.
+ *
+ * @param e -
+ * the <code>ActionEvent</code> that occurred
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (editField != null)
+ completeEditing();
+ }
+ }
+ }
+
+ /**
+ * A mouse listener for the {@link JFileChooser}.
+ * This listener is used for the table
+ */
+ private class TableClickListener extends MouseAdapter
+ {
+
+ /** Stores instance of the table */
+ JTable table;
+
+ /** Stores instance of the file chooser */
+ JFileChooser fc;
+
+ /** The last selected file. */
+ Object lastSelected;
+
+ /**
+ * Stores the current file that is being edited.
+ * It is null if nothing is currently being edited.
+ */
+ File editFile;
+
+ /** The textfield used for editing. */
+ JTextField editField;
+
+ /**
+ * Creates a new listener.
+ *
+ * @param table the directory/file table
+ * @param fc the JFileChooser
+ */
+ public TableClickListener(JTable table, JFileChooser fc)
+ {
+ this.table = table;
+ this.fc = fc;
+ lastSelected = fileList.getSelectedValue();
+ setDirectorySelected(false);
+ startEditing = false;
+ editFile = null;
+ editField = null;
+ }
+
+ /**
+ * Receives notification of a mouse click event.
+ *
+ * @param e the event.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ int row = table.getSelectedRow();
+ Object selVal = fileList.getModel().getElementAt(row);
+ if (selVal == null)
+ return;
+ FileSystemView fsv = fc.getFileSystemView();
+ if (e.getClickCount() == 1 &&
+ selVal.equals(lastSelected) &&
+ e.getButton() == MouseEvent.BUTTON1)
+ {
+ File[] sf = fc.getSelectedFiles();
+ if ((!fc.isMultiSelectionEnabled() || (sf != null && sf.length <= 1))
+ && !startEditing)
+ {
+ editFile = (File) selVal;
+ editFile(row);
+ }
+ }
+ else if (e.getClickCount() >= 2 &&
+ selVal.equals(lastSelected))
+ {
+ if (startEditing)
+ completeEditing();
+ File f = fsv.createFileObject(lastSelected.toString());
+ if (fc.isTraversable(f))
+ {
+ fc.setCurrentDirectory(f);
+ fc.rescanCurrentDirectory();
+ }
+ else
+ {
+ fc.setSelectedFile(f);
+ fc.approveSelection();
+ closeDialog();
+ }
+ }
+ else
+ {
+ if (startEditing)
+ completeEditing();
+ String path = selVal.toString();
+ File f = fsv.createFileObject(path);
+ fc.setSelectedFile(f);
+ if (fc.isTraversable(f))
+ {
+ setDirectorySelected(true);
+ setDirectory(f);
+ }
+ else
+ {
+ setDirectorySelected(false);
+ setDirectory(null);
+ }
+ lastSelected = selVal;
+ if (f.isFile())
+ setFileName(path.substring(path.lastIndexOf("/") + 1));
+ else if (fc.getFileSelectionMode() != JFileChooser.FILES_ONLY)
+ setFileName(path);
+ }
+ fileTable.repaint();
+ }
+
+ /**
+ * Sets up the text editor for the current file.
+ *
+ * @param row -
+ * the current row of the item in the list to be edited.
+ */
+ void editFile(int row)
+ {
+ Rectangle bounds = table.getCellRect(row, 0, true);
+ table.scrollRectToVisible(bounds);
+ if (editFile.canWrite())
+ {
+ startEditing = true;
+ editField = new JTextField(editFile.getName());
+ editField.addActionListener(new EditingActionListener());
+
+ // Need to adjust y pos
+ bounds.y = row * table.getRowHeight();
+ editField.setBounds(bounds);
+
+ table.add(editField);
+
+ editField.requestFocus();
+ editField.selectAll();
+ }
+ else
+ completeEditing();
+ table.repaint();
+ }
+
+ /**
+ * Completes the editing.
+ */
+ void completeEditing()
+ {
+ if (editField != null && editFile != null)
+ {
+ String text = editField.getText();
+ if (text != null && text != "" && !text.equals(fc.getName(editFile)))
+ if (editFile.renameTo(fc.getFileSystemView().createFileObject(
+ fc.getCurrentDirectory(), text)))
+ rescanCurrentDirectory(fc);
+ table.remove(editField);
+ }
+ startEditing = false;
+ editFile = null;
+ editField = null;
+ table.repaint();
+ }
+
+ /**
+ * ActionListener for the editing text field.
+ */
+ class EditingActionListener implements ActionListener
+ {
+
+ /**
+ * This method is invoked when an action occurs.
+ *
+ * @param e -
+ * the <code>ActionEvent</code> that occurred
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (editField != null)
+ completeEditing();
+ }
+ }
+
+ /**
+ * Closes the dialog.
+ */
+ public void closeDialog()
+ {
+ Window owner = SwingUtilities.windowForComponent(fc);
+ if (owner instanceof JDialog)
+ ((JDialog) owner).dispose();
+ }
+ }
+
+ /** The text for a label describing the directory combo box. */
+ private String directoryLabel;
+
+ private JComboBox directoryComboBox;
+
+ /** The model for the directory combo box. */
+ DirectoryComboBoxModel directoryModel;
+
+ /** The text for a label describing the file text field. */
+ private String fileLabel;
+
+ /** The file name text field. */
+ private JTextField fileTextField;
+
+ /** The text for a label describing the filter combo box. */
+ private String filterLabel;
+
+ /**
+ * The top panel (contains the directory combo box and the control buttons).
+ */
+ private JPanel topPanel;
+
+ /** A panel containing the control buttons ('up', 'home' etc.). */
+ private JPanel controls;
+
+ /**
+ * The panel that contains the filename field and the filter combobox.
+ */
+ private JPanel bottomPanel;
+
+ /**
+ * The panel that contains the 'Open' (or 'Save') and 'Cancel' buttons.
+ */
+ private JPanel buttonPanel;
+
+ private JButton approveButton;
+
+ /** The file list. */
+ JList fileList;
+
+ /** The file table. */
+ JTable fileTable;
+
+ /** The panel containing the file list. */
+ JPanel fileListPanel;
+
+ /** The panel containing the file table. */
+ JPanel fileTablePanel;
+
+ /** The filter combo box model. */
+ private FilterComboBoxModel filterModel;
+
+ /** The action map. */
+ private ActionMap actionMap;
+
+ /** True if currently in list view. */
+ boolean listView;
+
+ /** True if we can or have started editing a cell. */
+ boolean startEditing;
+
+ /** The scrollpane used for the table and list. */
+ JScrollPane scrollPane;
+
+ /** The text for the label when saving. */
+ String save;
+
+ /** The text for the label when opening a directory. */
+ String look;
+
+ /** The label for the file combo box. */
+ JLabel dirLabel;
+
+ /** Listeners. */
+ ListSelectionListener listSelList;
+ MouseListener doubleClickList;
+ SingleClickListener singleClickList;
+ TableClickListener tableClickList;
+
+ /**
+ * A factory method that returns a UI delegate for the specified
+ * component.
+ *
+ * @param c the component (which should be a {@link JFileChooser}).
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ JFileChooser chooser = (JFileChooser) c;
+ return new MetalFileChooserUI(chooser);
+ }
+
+ /**
+ * Creates a new instance of this UI delegate.
+ *
+ * @param filechooser the file chooser component.
+ */
+ public MetalFileChooserUI(JFileChooser filechooser)
+ {
+ super(filechooser);
+ bottomPanel = new JPanel(new GridLayout(3, 2));
+ buttonPanel = new JPanel();
+ }
+
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ actionMap = createActionMap();
+ }
+
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ actionMap = null;
+ }
+
+ /**
+ * Installs the sub-components of the file chooser.
+ *
+ * @param fc the file chooser component.
+ */
+ public void installComponents(JFileChooser fc)
+ {
+ fc.setLayout(new BorderLayout());
+ topPanel = new JPanel(new BorderLayout());
+ dirLabel = new JLabel(directoryLabel);
+ topPanel.add(dirLabel, BorderLayout.WEST);
+ this.controls = new JPanel();
+ addControlButtons();
+
+ JPanel dirPanel = new JPanel(new VerticalMidLayout());
+ directoryModel = createDirectoryComboBoxModel(fc);
+ directoryComboBox = new JComboBox(directoryModel);
+ directoryComboBox.setRenderer(createDirectoryComboBoxRenderer(fc));
+ dirPanel.add(directoryComboBox);
+ topPanel.add(dirPanel);
+ topPanel.add(controls, BorderLayout.EAST);
+ topPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 0, 8));
+ fc.add(topPanel, BorderLayout.NORTH);
+
+ JPanel list = createList(fc);
+ list.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+ fc.add(list, BorderLayout.CENTER);
+
+ JPanel bottomPanel = getBottomPanel();
+ filterModel = createFilterComboBoxModel();
+ JComboBox fileFilterCombo = new JComboBox(filterModel);
+ fileFilterCombo.setRenderer(createFilterComboBoxRenderer());
+
+ fileTextField = new JTextField();
+ JPanel fileNamePanel = new JPanel(new VerticalMidLayout());
+ fileNamePanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 5));
+ fileNamePanel.add(fileTextField);
+ JPanel row1 = new JPanel(new BorderLayout());
+ row1.add(new JLabel(this.fileLabel), BorderLayout.WEST);
+ row1.add(fileNamePanel);
+ bottomPanel.add(row1);
+
+ JPanel row2 = new JPanel(new BorderLayout());
+ row2.add(new JLabel(this.filterLabel), BorderLayout.WEST);
+ row2.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
+ row2.add(fileFilterCombo);
+ bottomPanel.add(row2);
+ JPanel buttonPanel = new JPanel(new ButtonLayout());
+
+ approveButton = new JButton(getApproveSelectionAction());
+ approveButton.setText(getApproveButtonText(fc));
+ approveButton.setToolTipText(getApproveButtonToolTipText(fc));
+ approveButton.setMnemonic(getApproveButtonMnemonic(fc));
+ buttonPanel.add(approveButton);
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
+
+ JButton cancelButton = new JButton(getCancelSelectionAction());
+ cancelButton.setText(cancelButtonText);
+ cancelButton.setToolTipText(cancelButtonToolTipText);
+ cancelButton.setMnemonic(cancelButtonMnemonic);
+ buttonPanel.add(cancelButton);
+ bottomPanel.add(buttonPanel, BorderLayout.SOUTH);
+ bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
+ fc.add(bottomPanel, BorderLayout.SOUTH);
+
+ fc.add(getAccessoryPanel(), BorderLayout.EAST);
+ }
+
+ /**
+ * Uninstalls the components added by
+ * {@link #installComponents(JFileChooser)}.
+ *
+ * @param fc the file chooser.
+ */
+ public void uninstallComponents(JFileChooser fc)
+ {
+ fc.remove(bottomPanel);
+ bottomPanel = null;
+ fc.remove(fileListPanel);
+ fc.remove(fileTablePanel);
+ fileTablePanel = null;
+ fileListPanel = null;
+ fc.remove(topPanel);
+ topPanel = null;
+
+ directoryModel = null;
+ fileTextField = null;
+ directoryComboBox = null;
+ }
+
+ /**
+ * Returns the panel that contains the 'Open' (or 'Save') and 'Cancel'
+ * buttons.
+ *
+ * @return The panel.
+ */
+ protected JPanel getButtonPanel()
+ {
+ return buttonPanel;
+ }
+
+ /**
+ * Creates and returns a new panel that will be used for the controls at
+ * the bottom of the file chooser.
+ *
+ * @return A new panel.
+ */
+ protected JPanel getBottomPanel()
+ {
+ if (bottomPanel == null)
+ bottomPanel = new JPanel(new GridLayout(3, 2));
+ return bottomPanel;
+ }
+
+ /**
+ * Fetches localised strings for use by the labels and buttons on the
+ * file chooser.
+ *
+ * @param fc the file chooser.
+ */
+ protected void installStrings(JFileChooser fc)
+ {
+ super.installStrings(fc);
+ look = "Look In: ";
+ save = "Save In: ";
+ if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
+ directoryLabel = save;
+ else
+ directoryLabel = look;
+
+ fileLabel = "File Name: ";
+ filterLabel = "Files of Type: ";
+
+ this.cancelButtonMnemonic = 0;
+ this.cancelButtonText = "Cancel";
+ this.cancelButtonToolTipText = "Abort file chooser dialog";
+
+ this.directoryOpenButtonMnemonic = 0;
+ this.directoryOpenButtonText = "Open";
+ this.directoryOpenButtonToolTipText = "Open selected directory";
+
+ this.helpButtonMnemonic = 0;
+ this.helpButtonText = "Help";
+ this.helpButtonToolTipText = "Filechooser help";
+
+ this.openButtonMnemonic = 0;
+ this.openButtonText = "Open";
+ this.openButtonToolTipText = "Open selected file";
+
+ this.saveButtonMnemonic = 0;
+ this.saveButtonText = "Save";
+ this.saveButtonToolTipText = "Save selected file";
+
+ this.updateButtonMnemonic = 0;
+ this.updateButtonText = "Update";
+ this.updateButtonToolTipText = "Update directory listing";
+ }
+
+ /**
+ * Installs the listeners required.
+ *
+ * @param fc the file chooser.
+ */
+ protected void installListeners(JFileChooser fc)
+ {
+ directoryComboBox.setAction(new DirectoryComboBoxAction());
+ fc.addPropertyChangeListener(filterModel);
+ listSelList = createListSelectionListener(fc);
+ doubleClickList = this.createDoubleClickListener(fc, fileList);
+ singleClickList = new SingleClickListener(fileList);
+ fileList.addListSelectionListener(listSelList);
+ fileList.addMouseListener(doubleClickList);
+ fileList.addMouseListener(singleClickList);
+ super.installListeners(fc);
+ }
+
+ protected void uninstallListeners(JFileChooser fc)
+ {
+ super.uninstallListeners(fc);
+ fc.removePropertyChangeListener(filterModel);
+ directoryComboBox.setAction(null);
+ fileList.removeListSelectionListener(listSelList);
+ fileList.removeMouseListener(doubleClickList);
+ fileList.removeMouseListener(singleClickList);
+
+ if (fileTable != null)
+ fileTable.removeMouseListener(tableClickList);
+ }
+
+ protected ActionMap getActionMap()
+ {
+ if (actionMap == null)
+ actionMap = createActionMap();
+ return actionMap;
+ }
+
+ /**
+ * Creates and returns an action map.
+ *
+ * @return The action map.
+ */
+ protected ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMap();
+ map.put("approveSelection", getApproveSelectionAction());
+ map.put("cancelSelection", getCancelSelectionAction());
+ map.put("Go Up", getChangeToParentDirectoryAction());
+ return map;
+ }
+
+ /**
+ * Creates a panel containing a list of files.
+ *
+ * @param fc the file chooser.
+ *
+ * @return A panel.
+ */
+ protected JPanel createList(JFileChooser fc)
+ {
+ if (fileList == null)
+ {
+ fileListPanel = new JPanel(new BorderLayout());
+ fileList = new JList(getModel());
+ scrollPane = new JScrollPane(fileList);
+ fileList.setLayoutOrientation(JList.VERTICAL_WRAP);
+ fileList.setCellRenderer(new FileRenderer());
+ }
+ else
+ {
+ fileList.setModel(getModel());
+ fileListPanel.removeAll();
+ scrollPane.getViewport().setView(fileList);
+ }
+ fileListPanel.add(scrollPane);
+ // This size was determined using BeanShell and dumping the JFileChooser
+ // component hierarchy. Sun has an internal FilePane class in there, but
+ // that probably doesn't matter atm.
+ fileListPanel.setPreferredSize(new Dimension(405, 135));
+ return fileListPanel;
+ }
+
+ /**
+ * Creates a panel containing a table within a scroll pane.
+ *
+ * @param fc the file chooser.
+ *
+ * @return The details view.
+ */
+ protected JPanel createDetailsView(JFileChooser fc)
+ {
+ fileTablePanel = new JPanel(new BorderLayout());
+
+ Object[] cols = new Object[] {"Name", "Size", "Modified"};
+ Object[][] rows = new Object[fileList.getModel().getSize()][3];
+
+ fileTable = new JTable(new DefaultTableModel(rows, cols));
+
+ if (fc.isMultiSelectionEnabled())
+ fileTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ else
+ fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+ fileTable.setShowGrid(false);
+ fileTable.setColumnSelectionAllowed(false);
+ fileTable.setDefaultRenderer(Object.class, new TableFileRenderer());
+
+ tableClickList = new TableClickListener(fileTable, fc);
+ fileTable.addMouseListener(tableClickList);
+
+ return updateTable();
+ }
+
+ /**
+ * Sets the values in the table, and puts it in the panel.
+ *
+ * @return the panel containing the table.
+ */
+ JPanel updateTable()
+ {
+ DefaultTableModel mod = (DefaultTableModel) fileTable.getModel();
+ ListModel lm = fileList.getModel();
+ DateFormat dt = DateFormat.getDateTimeInstance(DateFormat.SHORT,
+ DateFormat.SHORT);
+ File curr = null;
+ int size = lm.getSize();
+ int rc = mod.getRowCount();
+
+ // If there are not enough rows
+ for (int x = rc; x < size; x++)
+ mod.addRow(new Object[3]);
+
+ for (int i = 0; i < size; i++)
+ {
+ curr = (File) lm.getElementAt(i);
+ fileTable.setValueAt(curr.getName(), i, 0);
+ fileTable.setValueAt(formatSize(curr.length()), i, 1);
+ fileTable.setValueAt(dt.format(new Date(curr.lastModified())), i, 2);
+ }
+
+ // If there are too many rows
+ while (rc > size)
+ mod.removeRow(--rc);
+
+ scrollPane.getViewport().setView(fileTable);
+ scrollPane.setColumnHeaderView(fileTable.getTableHeader());
+
+ fileTablePanel.removeAll();
+ fileTablePanel.add(scrollPane);
+
+ return fileTablePanel;
+ }
+
+ /**
+ * Formats bytes into the appropriate size.
+ *
+ * @param bytes the number of bytes to convert
+ * @return a string representation of the size
+ */
+ private String formatSize(long bytes)
+ {
+ NumberFormat nf = NumberFormat.getNumberInstance();
+ long mb = (long) Math.pow(2, 20);
+ long kb = (long) Math.pow(2, 10);
+ long gb = (long) Math.pow(2, 30);
+ double size = 0;
+ String id = "";
+
+ if ((bytes / gb) >= 1)
+ {
+ size = (double) bytes / (double) gb;
+ id = "GB";
+ }
+ else if ((bytes / mb) >= 1)
+ {
+ size = (double) bytes / (double) mb;
+ id = "MB";
+ }
+ else if ((bytes / kb) >= 1)
+ {
+ size = (double) bytes / (double) kb;
+ id = "KB";
+ }
+ else
+ {
+ size = bytes;
+ id = "Bytes";
+ }
+
+ return nf.format(size) + " " + id;
+ }
+ /**
+ * Creates a listener that monitors selections in the directory/file list
+ * and keeps the {@link JFileChooser} component up to date.
+ *
+ * @param fc the file chooser.
+ *
+ * @return The listener.
+ *
+ * @see #installListeners(JFileChooser)
+ */
+ public ListSelectionListener createListSelectionListener(JFileChooser fc)
+ {
+ return new MetalFileChooserSelectionListener();
+ }
+
+ /**
+ * Returns the preferred size for the file chooser component.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension tp = topPanel.getPreferredSize();
+ Dimension bp = bottomPanel.getPreferredSize();
+ Dimension fl = fileListPanel.getPreferredSize();
+ return new Dimension(fl.width, tp.height + bp.height + fl.height);
+ }
+
+ /**
+ * Returns the minimum size for the file chooser component.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension tp = topPanel.getMinimumSize();
+ Dimension bp = bottomPanel.getMinimumSize();
+ Dimension fl = fileListPanel.getMinimumSize();
+ return new Dimension(fl.width, tp.height + bp.height + fl.height);
+ }
+
+ /**
+ * Returns the maximum size for the file chooser component.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Creates a property change listener that monitors the {@link JFileChooser}
+ * for property change events and updates the component display accordingly.
+ *
+ * @param fc the file chooser.
+ *
+ * @return The property change listener.
+ *
+ * @see #installListeners(JFileChooser)
+ */
+ public PropertyChangeListener createPropertyChangeListener(JFileChooser fc)
+ {
+ return new MetalFileChooserPropertyChangeListener();
+ }
+
+ /**
+ * Creates and returns a new instance of {@link DirectoryComboBoxModel}.
+ *
+ * @return A new instance of {@link DirectoryComboBoxModel}.
+ */
+ protected MetalFileChooserUI.DirectoryComboBoxModel
+ createDirectoryComboBoxModel(JFileChooser fc)
+ {
+ return new DirectoryComboBoxModel();
+ }
+
+ /**
+ * Creates a new instance of the renderer used in the directory
+ * combo box.
+ *
+ * @param fc the file chooser.
+ *
+ * @return The renderer.
+ */
+ protected DirectoryComboBoxRenderer createDirectoryComboBoxRenderer(
+ JFileChooser fc)
+ {
+ return new DirectoryComboBoxRenderer(fc);
+ }
+
+ /**
+ * Creates and returns a new instance of {@link FilterComboBoxModel}.
+ *
+ * @return A new instance of {@link FilterComboBoxModel}.
+ */
+ protected FilterComboBoxModel createFilterComboBoxModel()
+ {
+ return new FilterComboBoxModel();
+ }
+
+ /**
+ * Creates and returns a new instance of {@link FilterComboBoxRenderer}.
+ *
+ * @return A new instance of {@link FilterComboBoxRenderer}.
+ */
+ protected MetalFileChooserUI.FilterComboBoxRenderer
+ createFilterComboBoxRenderer()
+ {
+ return new FilterComboBoxRenderer();
+ }
+
+ /**
+ * Adds the control buttons ('up', 'home' etc.) to the panel.
+ */
+ protected void addControlButtons()
+ {
+ JButton upButton = new JButton(getChangeToParentDirectoryAction());
+ upButton.setText(null);
+ upButton.setIcon(this.upFolderIcon);
+ upButton.setMargin(new Insets(0, 0, 0, 0));
+ controls.add(upButton);
+
+ JButton homeButton = new JButton(getGoHomeAction());
+ homeButton.setText(null);
+ homeButton.setIcon(this.homeFolderIcon);
+ homeButton.setMargin(new Insets(0, 0, 0, 0));
+ controls.add(homeButton);
+
+ JButton newFolderButton = new JButton(getNewFolderAction());
+ newFolderButton.setText(null);
+ newFolderButton.setIcon(this.newFolderIcon);
+ newFolderButton.setMargin(new Insets(0, 0, 0, 0));
+ controls.add(newFolderButton);
+
+ JToggleButton listButton = new JToggleButton(this.listViewIcon);
+ listButton.setMargin(new Insets(0, 0, 0, 0));
+ listButton.addActionListener(new ListViewActionListener());
+ listButton.setSelected(true);
+ listView = true;
+ controls.add(listButton);
+
+ JToggleButton detailButton = new JToggleButton(this.detailsViewIcon);
+ detailButton.setMargin(new Insets(0, 0, 0, 0));
+ detailButton.addActionListener(new DetailViewActionListener());
+ detailButton.setSelected(false);
+ controls.add(detailButton);
+
+ ButtonGroup buttonGroup = new ButtonGroup();
+ buttonGroup.add(listButton);
+ buttonGroup.add(detailButton);
+ }
+
+ /**
+ * Removes all the buttons from the control panel.
+ */
+ protected void removeControlButtons()
+ {
+ controls.removeAll();
+ controls.revalidate();
+ controls.repaint();
+ }
+
+ /**
+ * Updates the current directory.
+ *
+ * @param fc the file chooser to update.
+ */
+ public void rescanCurrentDirectory(JFileChooser fc)
+ {
+ directoryModel.setSelectedItem(fc.getCurrentDirectory());
+ getModel().validateFileCache();
+ if (!listView)
+ updateTable();
+ else
+ createList(fc);
+ }
+
+ /**
+ * Returns the file name in the text field.
+ *
+ * @return The file name.
+ */
+ public String getFileName()
+ {
+ String result = null;
+ if (fileTextField != null)
+ result = fileTextField.getText();
+ return result;
+ }
+
+ /**
+ * Sets the file name in the text field.
+ *
+ * @param filename the file name.
+ */
+ public void setFileName(String filename)
+ {
+ fileTextField.setText(filename);
+ }
+
+ /**
+ * DOCUMENT ME!!
+ *
+ * @param e - DOCUMENT ME!
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ // FIXME: Not sure what we should be doing here, if anything.
+ }
+
+ /**
+ * Returns the approve button.
+ *
+ * @return The approve button.
+ */
+ protected JButton getApproveButton(JFileChooser fc)
+ {
+ return approveButton;
+ }
+
+ /**
+ * A layout manager that is used to arrange the subcomponents of the
+ * {@link JFileChooser}.
+ */
+ class VerticalMidLayout implements LayoutManager
+ {
+ /**
+ * Performs the layout.
+ *
+ * @param parent the container.
+ */
+ public void layoutContainer(Container parent)
+ {
+ int count = parent.getComponentCount();
+ if (count > 0)
+ {
+ Insets insets = parent.getInsets();
+ Component c = parent.getComponent(0);
+ Dimension prefSize = c.getPreferredSize();
+ int h = parent.getHeight() - insets.top - insets.bottom;
+ int adj = Math.max(0, (h - prefSize.height) / 2);
+ c.setBounds(insets.left, insets.top + adj, parent.getWidth()
+ - insets.left - insets.right,
+ (int) Math.min(prefSize.getHeight(), h));
+ }
+ }
+
+ /**
+ * Returns the minimum layout size.
+ *
+ * @param parent the container.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return preferredLayoutSize(parent);
+ }
+
+ /**
+ * Returns the preferred layout size.
+ *
+ * @param parent the container.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ if (parent.getComponentCount() > 0)
+ {
+ return parent.getComponent(0).getPreferredSize();
+ }
+ else return null;
+ }
+
+ /**
+ * This layout manager does not need to track components, so this
+ * method does nothing.
+ *
+ * @param name the name the component is associated with.
+ * @param component the component.
+ */
+ public void addLayoutComponent(String name, Component component)
+ {
+ // do nothing
+ }
+
+ /**
+ * This layout manager does not need to track components, so this
+ * method does nothing.
+ *
+ * @param component the component.
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ // do nothing
+ }
+ }
+
+ /**
+ * A layout manager that is used to arrange buttons for the
+ * {@link JFileChooser}.
+ */
+ class ButtonLayout implements LayoutManager
+ {
+ static final int GAP = 4;
+
+ /**
+ * Performs the layout.
+ *
+ * @param parent the container.
+ */
+ public void layoutContainer(Container parent)
+ {
+ int count = parent.getComponentCount();
+ if (count > 0)
+ {
+ // first find the widest button
+ int maxW = 0;
+ for (int i = 0; i < count; i++)
+ {
+ Component c = parent.getComponent(i);
+ Dimension prefSize = c.getPreferredSize();
+ maxW = Math.max(prefSize.width, maxW);
+ }
+
+ // then position the buttons
+ Insets insets = parent.getInsets();
+ int availableH = parent.getHeight() - insets.top - insets.bottom;
+ int currentX = parent.getWidth() - insets.right;
+ for (int i = count - 1; i >= 0; i--)
+ {
+ Component c = parent.getComponent(i);
+ Dimension prefSize = c.getPreferredSize();
+ int adj = Math.max(0, (availableH - prefSize.height) / 2);
+ currentX = currentX - prefSize.width;
+ c.setBounds(currentX, insets.top + adj, prefSize.width,
+ (int) Math.min(prefSize.getHeight(), availableH));
+ currentX = currentX - GAP;
+ }
+ }
+ }
+
+ /**
+ * Returns the minimum layout size.
+ *
+ * @param parent the container.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return preferredLayoutSize(parent);
+ }
+
+ /**
+ * Returns the preferred layout size.
+ *
+ * @param parent the container.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ Insets insets = parent.getInsets();
+ int maxW = 0;
+ int maxH = 0;
+ int count = parent.getComponentCount();
+ if (count > 0)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ Component c = parent.getComponent(i);
+ Dimension d = c.getPreferredSize();
+ maxW = Math.max(d.width, maxW);
+ maxH = Math.max(d.height, maxH);
+ }
+ }
+ return new Dimension(maxW * count + GAP * (count - 1) + insets.left
+ + insets.right, maxH + insets.top + insets.bottom);
+ }
+
+ /**
+ * This layout manager does not need to track components, so this
+ * method does nothing.
+ *
+ * @param name the name the component is associated with.
+ * @param component the component.
+ */
+ public void addLayoutComponent(String name, Component component)
+ {
+ // do nothing
+ }
+
+ /**
+ * This layout manager does not need to track components, so this
+ * method does nothing.
+ *
+ * @param component the component.
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ // do nothing
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java
new file mode 100644
index 000000000..5382577c3
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java
@@ -0,0 +1,2626 @@
+/* MetalIconFactory.java --
+ Copyright (C) 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.io.Serializable;
+
+import javax.swing.AbstractButton;
+import javax.swing.Icon;
+import javax.swing.JCheckBox;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JFileChooser;
+import javax.swing.JInternalFrame;
+import javax.swing.JRadioButton;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.JSlider;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.plaf.UIResource;
+
+
+/**
+ * Creates icons for the {@link MetalLookAndFeel}.
+ */
+public class MetalIconFactory implements Serializable
+{
+
+ /** A constant representing "dark". */
+ public static final boolean DARK = false;
+
+ /** A constant representing "light". */
+ public static final boolean LIGHT = true;
+
+ /** A shared instance of the MenuArrowIcon. */
+ private static Icon menuArrow;
+
+ /** A shared instance of the MenuItemArrowIcon. */
+ private static Icon menuItemArrow;
+
+ /**
+ * An icon displayed for {@link JCheckBoxMenuItem} components.
+ */
+ private static class CheckBoxMenuItemIcon
+ implements Icon, UIResource, Serializable
+ {
+ /**
+ * Creates a new icon instance.
+ */
+ public CheckBoxMenuItemIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon (10 pixels).
+ */
+ public int getIconWidth()
+ {
+ return 10;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon (10 pixels).
+ */
+ public int getIconHeight()
+ {
+ return 10;
+ }
+
+ /**
+ * Paints the icon.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ JCheckBoxMenuItem item = (JCheckBoxMenuItem) c;
+
+ if (item.isArmed())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(x, y, x + 8, y);
+ g.drawLine(x, y + 1, x, y + 8);
+ g.drawLine(x + 2, y + 8, x + 8, y + 8);
+ g.drawLine(x + 8, y + 2, x + 8, y + 7);
+
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x + 1, y + 1, x + 7, y + 1);
+ g.drawLine(x + 1, y + 2, x + 1, y + 7);
+ g.drawLine(x + 1, y + 9, x + 9, y + 9);
+ g.drawLine(x + 9, y + 1, x + 9, y + 8);
+
+ // if the item is selected, we should draw a tick
+ if (item.isSelected())
+ {
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.fillRect(x + 2, y + 2, 2, 5);
+ for (int i = 0; i < 6; i++)
+ g.drawLine(x + 8 - i, y + i, x + 9 - i, y + i);
+ }
+
+ }
+ }
+
+ /**
+ * An icon used for the "detail view" button on a {@link JFileChooser} under
+ * the {@link MetalLookAndFeel}.
+ *
+ * @see MetalIconFactory#getFileChooserDetailViewIcon()
+ */
+ private static class FileChooserDetailViewIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * Creates a new icon.
+ */
+ public FileChooserDetailViewIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 18;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 18;
+ }
+
+ /**
+ * Paints the icon using colors from the {@link MetalLookAndFeel}.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+ g.setColor(MetalLookAndFeel.getBlack());
+
+ // file 1 outline
+ g.drawLine(x + 2, y + 2, x + 5, y + 2);
+ g.drawLine(x + 6, y + 3, x + 6, y + 7);
+ g.drawLine(x + 2, y + 7, x + 6, y + 7);
+ g.drawLine(x + 2, y + 2, x + 2, y + 7);
+
+ // file 2 outline
+ g.drawLine(x + 2, y + 10, x + 5, y + 10);
+ g.drawLine(x + 6, y + 11, x + 6, y + 15);
+ g.drawLine(x + 2, y + 15, x + 6, y + 15);
+ g.drawLine(x + 2, y + 10, x + 2, y + 15);
+
+ // detail lines
+ g.drawLine(x + 8, y + 5, x + 15, y + 5);
+ g.drawLine(x + 8, y + 13, x + 15, y + 13);
+
+ // fill files
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 3, y + 3, 3, 4);
+ g.fillRect(x + 3, y + 11, 3, 4);
+
+ // highlight files
+ g.setColor(MetalLookAndFeel.getPrimaryControlHighlight());
+ g.drawLine(x + 4, y + 4, x + 4, y + 5);
+ g.drawLine(x + 4, y + 12, x + 4, y + 13);
+
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An icon used for the "home folder" button on a {@link JFileChooser} under
+ * the {@link MetalLookAndFeel}.
+ *
+ * @see MetalIconFactory#getFileChooserHomeFolderIcon()
+ */
+ private static class FileChooserHomeFolderIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * Creates a new icon.
+ */
+ public FileChooserHomeFolderIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 18;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 18;
+ }
+
+ /**
+ * Paints the icon using colors from the {@link MetalLookAndFeel}.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+ g.setColor(MetalLookAndFeel.getBlack());
+
+ // roof
+ g.drawLine(x + 1, y + 8, x + 8, y + 1);
+ g.drawLine(x + 8, y + 1, x + 15, y + 8);
+
+ // base of house
+ g.drawLine(x + 3, y + 6, x + 3, y + 15);
+ g.drawLine(x + 3, y + 15, x + 13, y + 15);
+ g.drawLine(x + 13, y + 6, x + 13, y + 15);
+
+ // door frame
+ g.drawLine(x + 6, y + 9, x + 6, y + 15);
+ g.drawLine(x + 6, y + 9, x + 10, y + 9);
+ g.drawLine(x + 10, y + 9, x + 10, y + 15);
+
+ // chimney
+ g.drawLine(x + 11, y + 2, x + 11, y + 4);
+ g.drawLine(x + 12, y + 2, x + 12, y + 5);
+
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+
+ // roof paint
+ int xx = x + 8;
+ for (int i = 0; i < 4; i++)
+ g.drawLine(xx - i, y + 2 + i, xx + i, y + 2 + i);
+ g.fillRect(x + 4, y + 6, 9, 2);
+
+ // door knob
+ g.drawLine(x + 9, y + 12, x + 9, y + 12);
+
+ // house paint
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.drawLine(x + 4, y + 8, x + 12, y + 8);
+ g.fillRect(x + 4, y + 9, 2, 6);
+ g.fillRect(x + 11, y + 9, 2, 6);
+
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An icon used for the "list view" button on a {@link JFileChooser} under
+ * the {@link MetalLookAndFeel}.
+ *
+ * @see MetalIconFactory#getFileChooserListViewIcon()
+ */
+ private static class FileChooserListViewIcon
+ implements Icon, UIResource, Serializable
+ {
+ /**
+ * Creates a new icon.
+ */
+ public FileChooserListViewIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 18;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 18;
+ }
+
+ /**
+ * Paints the icon using colors from the {@link MetalLookAndFeel}.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+ g.setColor(MetalLookAndFeel.getBlack());
+
+ // file 1 outline
+ g.drawLine(x + 2, y + 2, x + 5, y + 2);
+ g.drawLine(x + 6, y + 3, x + 6, y + 7);
+ g.drawLine(x + 2, y + 7, x + 6, y + 7);
+ g.drawLine(x + 2, y + 2, x + 2, y + 7);
+
+ // file 2 outline
+ g.drawLine(x + 2, y + 10, x + 5, y + 10);
+ g.drawLine(x + 6, y + 11, x + 6, y + 15);
+ g.drawLine(x + 2, y + 15, x + 6, y + 15);
+ g.drawLine(x + 2, y + 10, x + 2, y + 15);
+
+ // file 3 outline
+ g.drawLine(x + 10, y + 2, x + 13, y + 2);
+ g.drawLine(x + 14, y + 3, x + 14, y + 7);
+ g.drawLine(x + 10, y + 7, x + 14, y + 7);
+ g.drawLine(x + 10, y + 2, x + 10, y + 7);
+
+ // file 4 outline
+ g.drawLine(x + 10, y + 10, x + 13, y + 10);
+ g.drawLine(x + 14, y + 11, x + 14, y + 15);
+ g.drawLine(x + 10, y + 15, x + 14, y + 15);
+ g.drawLine(x + 10, y + 10, x + 10, y + 15);
+
+ g.drawLine(x + 8, y + 5, x + 8, y + 5);
+ g.drawLine(x + 8, y + 13, x + 8, y + 13);
+ g.drawLine(x + 16, y + 5, x + 16, y + 5);
+ g.drawLine(x + 16, y + 13, x + 16, y + 13);
+
+ // fill files
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 3, y + 3, 3, 4);
+ g.fillRect(x + 3, y + 11, 3, 4);
+ g.fillRect(x + 11, y + 3, 3, 4);
+ g.fillRect(x + 11, y + 11, 3, 4);
+
+ // highlight files
+ g.setColor(MetalLookAndFeel.getPrimaryControlHighlight());
+ g.drawLine(x + 4, y + 4, x + 4, y + 5);
+ g.drawLine(x + 4, y + 12, x + 4, y + 13);
+ g.drawLine(x + 12, y + 4, x + 12, y + 5);
+ g.drawLine(x + 12, y + 12, x + 12, y + 13);
+
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An icon used for the "new folder" button on a {@link JFileChooser} under
+ * the {@link MetalLookAndFeel}.
+ *
+ * @see MetalIconFactory#getFileChooserNewFolderIcon()
+ */
+ private static class FileChooserNewFolderIcon
+ implements Icon, UIResource, Serializable
+ {
+ /**
+ * Creates a new icon.
+ */
+ public FileChooserNewFolderIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 18;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 18;
+ }
+
+ /**
+ * Paints the icon using colors from the {@link MetalLookAndFeel}.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+ g.setColor(MetalLookAndFeel.getBlack());
+
+ g.drawLine(x + 2, y + 5, x + 9, y + 5);
+ g.drawLine(x + 10, y + 6, x + 15, y + 6);
+ g.drawLine(x + 15, y + 5, x + 15, y + 14);
+ g.drawLine(x + 2, y + 14, x + 15, y + 14);
+ g.drawLine(x + 1, y + 6, x + 1, y + 14);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ g.drawLine(x + 11, y + 3, x + 15, y + 3);
+ g.drawLine(x + 10, y + 4, x + 15, y + 4);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 3, y + 7, 7, 7);
+ g.fillRect(x + 10, y + 8, 5, 6);
+ g.drawLine(x + 10, y + 5, x + 14, y + 5);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControlHighlight());
+ g.drawLine(x + 10, y + 7, x + 14, y + 7);
+ g.drawLine(x + 2, y + 6, x + 9, y + 6);
+ g.drawLine(x + 2, y + 6, x + 2, y + 13);
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An icon used for the "up folder" button on a {@link JFileChooser} under
+ * the {@link MetalLookAndFeel}.
+ *
+ * @see MetalIconFactory#getFileChooserNewFolderIcon()
+ */
+ private static class FileChooserUpFolderIcon extends FileChooserNewFolderIcon
+ {
+ /**
+ * Creates a new icon.
+ */
+ public FileChooserUpFolderIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the icon using colors from the {@link MetalLookAndFeel}.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+
+ // draw the folder
+ super.paintIcon(c, g, x, y);
+
+ // now draw the up arrow
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 8, y + 9, x + 8, y + 16);
+ int xx = x + 8;
+ for (int i = 0; i < 4; i++)
+ g.drawLine(xx - i, y + 9 + i, xx + i, y + 9 + i);
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An icon representing a file (drawn as a piece of paper with the top-right
+ * corner turned down).
+ */
+ public static class FileIcon16 implements Icon, Serializable
+ {
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels. The height returned is
+ * <code>16</code> plus the value returned by
+ * {@link #getAdditionalHeight()}.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 16 + getAdditionalHeight();
+ }
+
+ /**
+ * Paints the icon at the location (x, y).
+ *
+ * @param c the component.
+ * @param g the graphics context.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ y = y + getShift();
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x, y, x + 9, y);
+ g.drawLine(x, y + 1, x, y + 15);
+ g.drawLine(x, y + 15, x + 12, y + 15);
+ g.drawLine(x + 12, y + 15, x + 12, y + 6);
+ g.drawLine(x + 12, y + 6, x + 9, y);
+
+ g.drawLine(x + 7, y + 2, x + 11, y + 6);
+ g.drawLine(x + 8, y + 1, x + 9, y + 1);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.drawLine(x + 1, y + 1, x + 7, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + 14);
+ g.drawLine(x + 1, y + 14, x + 11, y + 14);
+ g.drawLine(x + 11, y + 14, x + 11, y + 7);
+ g.drawLine(x + 8, y + 2, x + 10, y + 4);
+ }
+
+ /**
+ * Returns the additional height for the icon. The
+ * {@link #getIconHeight()} method adds this value to the icon height it
+ * returns. Subclasses can override this method to adjust the icon height.
+ *
+ * @return The additional height (<code>0</code> unless overridden).
+ */
+ public int getAdditionalHeight()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the vertical shift, in pixels, applied when painting the icon.
+ * The default value is zero, but subclasses may override this (for
+ * example, see {@link TreeLeafIcon}).
+ *
+ * @return The shift.
+ */
+ public int getShift()
+ {
+ return 0;
+ }
+
+ }
+
+ /**
+ * An icon representing a folder.
+ */
+ public static class FolderIcon16 implements Icon, Serializable
+ {
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels. The height returned is
+ * <code>16</code> plus the value returned by
+ * {@link #getAdditionalHeight()}.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 16 + getAdditionalHeight();
+ }
+
+ /**
+ * Paints the icon at the location (x, y).
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ y = y + getShift();
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x, y + 6, x, y + 15);
+ g.drawLine(x, y + 15, x + 15, y + 15);
+ g.drawLine(x + 15, y + 15, x + 15, y + 5);
+ g.drawLine(x + 14, y + 6, x + 9, y + 6);
+ g.drawLine(x + 8, y + 5, x + 1, y + 5);
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 2, y + 7, 7, 8);
+ g.fillRect(x + 9, y + 8, 6, 7);
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ g.drawLine(x + 9, y + 5, x + 14, y + 5);
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ g.drawLine(x + 9, y + 4, x + 15, y + 4);
+ g.drawLine(x + 10, y + 3, x + 15, y + 3);
+ }
+
+ /**
+ * Returns the additional height for the icon. The
+ * {@link #getIconHeight()} method adds this value to the icon height it
+ * returns. Subclasses can override this method to adjust the icon height.
+ *
+ * @return The additional height (<code>0</code> unless overridden).
+ */
+ public int getAdditionalHeight()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the vertical shift, in pixels, applied when painting the icon.
+ * The default value is zero, but subclasses may override this (for
+ * example, see {@link TreeFolderIcon}).
+ *
+ * @return The shift.
+ */
+ public int getShift()
+ {
+ return 0;
+ }
+
+ }
+
+ /**
+ * An icon used by the {@link MetalInternalFrameUI} class when the frame
+ * is displayed as a palette.
+ *
+ * @since 1.3
+ */
+ public static class PaletteCloseIcon
+ implements Icon, Serializable, UIResource
+ {
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 7;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 7;
+ }
+
+ /**
+ * Paints the icon using colors from the {@link MetalLookAndFeel}.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+ AbstractButton button = (AbstractButton) c;
+ if (button.getModel().isPressed())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.fillRect(x + 2, y + 2, 3, 3);
+ g.drawLine(x + 1, y, x + 1, y + 2);
+ g.drawLine(x, y + 1, x + 2, y + 1);
+ g.drawLine(x + 5, y, x + 5, y + 2);
+ g.drawLine(x + 4, y + 1, x + 6, y + 1);
+ g.drawLine(x + 1, y + 4, x + 1, y + 6);
+ g.drawLine(x, y + 5, x + 2, y + 5);
+ g.drawLine(x + 5, y + 4, x + 5, y + 6);
+ g.drawLine(x + 4, y + 5, x + 6, y + 5);
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(x + 2, y + 6, x + 3, y + 5);
+ g.drawLine(x + 5, y + 3, x + 6, y + 2);
+ g.drawLine(x + 6, y + 6, x + 6, y + 6);
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An {@link Icon} implementation for {@link JCheckBox}es in the
+ * Metal Look &amp; Feel.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+ static class RadioButtonIcon implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * This is used as a mask when painting the gradient. See
+ * {@link MetalUtils#paintGradient(java.awt.Graphics, int, int, int, int,
+ * float, float, java.awt.Color, java.awt.Color, java.awt.Color, int,
+ * int[][])} for details.
+ */
+ private static int[][] gradientMask = new int[][] {{3, 7}, {1, 9}, {1, 9},
+ {0, 10}, {0, 10}, {0, 10},
+ {0, 10}, {1, 9}, {1, 9},
+ {3, 7}};
+
+ /**
+ * Returns the width of the icon in pixels.
+ *
+ * @return the width of the icon in pixels
+ */
+ public int getIconWidth()
+ {
+ return 13;
+ }
+
+ /**
+ * Returns the height of the icon in pixels.
+ *
+ * @return the height of the icon in pixels
+ */
+ public int getIconHeight()
+ {
+ return 13;
+ }
+
+ /**
+ * Paints the icon, taking into account whether or not the component is
+ * enabled, selected and/or armed.
+ *
+ * @param c the Component to draw on (must be an instance of
+ * {@link JRadioButton})
+ * @param g the Graphics context to draw with
+ * @param x the X position
+ * @param y the Y position
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ if (UIManager.get("RadioButton.gradient") != null)
+ MetalUtils.paintGradient(g, x + 2, y + 2, 8, 8,
+ SwingConstants.VERTICAL, "RadioButton.gradient",
+ gradientMask);
+
+ Color savedColor = g.getColor();
+ JRadioButton b = (JRadioButton) c;
+
+ // draw outer circle
+ if (b.isEnabled())
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(x + 2, y + 1, x + 3, y + 1);
+ g.drawLine(x + 4, y, x + 7, y);
+ g.drawLine(x + 8, y + 1, x + 9, y + 1);
+ g.drawLine(x + 10, y + 2, x + 10, y + 3);
+ g.drawLine(x + 11, y + 4, x + 11, y + 7);
+ g.drawLine(x + 10, y + 8, x + 10, y + 9);
+ g.drawLine(x + 8, y + 10, x + 9, y + 10);
+ g.drawLine(x + 4, y + 11, x + 7, y + 11);
+ g.drawLine(x + 2, y + 10, x + 3, y + 10);
+ g.drawLine(x + 1, y + 9, x + 1, y + 8);
+ g.drawLine(x, y + 7, x, y + 4);
+ g.drawLine(x + 1, y + 2, x + 1, y + 3);
+
+ if (b.getModel().isArmed())
+ {
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x + 4, y + 1, x + 7, y + 1);
+ g.drawLine(x + 4, y + 10, x + 7, y + 10);
+ g.drawLine(x + 1, y + 4, x + 1, y + 7);
+ g.drawLine(x + 10, y + 4, x + 10, y + 7);
+ g.fillRect(x + 2, y + 2, 8, 8);
+ }
+ else
+ {
+ // only draw inner highlight if not filled
+ if (b.isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getWhite());
+
+ g.drawLine(x + 2, y + 8, x + 2, y + 9);
+ g.drawLine(x + 1, y + 4, x + 1, y + 7);
+ g.drawLine(x + 2, y + 2, x + 2, y + 3);
+ g.drawLine(x + 3, y + 2, x + 3, y + 2);
+ g.drawLine(x + 4, y + 1, x + 7, y + 1);
+ g.drawLine(x + 8, y + 2, x + 9, y + 2);
+ }
+ }
+
+ // draw outer highlight
+ if (b.isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getWhite());
+
+ // outer
+ g.drawLine(x + 10, y + 1, x + 10, y + 1);
+ g.drawLine(x + 11, y + 2, x + 11, y + 3);
+ g.drawLine(x + 12, y + 4, x + 12, y + 7);
+ g.drawLine(x + 11, y + 8, x + 11, y + 9);
+ g.drawLine(x + 10, y + 10, x + 10, y + 10);
+ g.drawLine(x + 8, y + 11, x + 9, y + 11);
+ g.drawLine(x + 4, y + 12, x + 7, y + 12);
+ g.drawLine(x + 2, y + 11, x + 3, y + 11);
+ }
+
+ if (b.isSelected())
+ {
+ if (b.isEnabled())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(x + 4, y + 3, x + 7, y + 3);
+ g.fillRect(x + 3, y + 4, 6, 4);
+ g.drawLine(x + 4, y + 8, x + 7, y + 8);
+ }
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An icon displayed for {@link JRadioButtonMenuItem} components.
+ */
+ private static class RadioButtonMenuItemIcon
+ implements Icon, UIResource, Serializable
+ {
+ /**
+ * Creates a new icon instance.
+ */
+ public RadioButtonMenuItemIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 10;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 10;
+ }
+
+ /**
+ * Paints the icon.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+ JRadioButtonMenuItem item = (JRadioButtonMenuItem) c;
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 2, y, x + 6, y);
+ g.drawLine(x + 7, y + 1, x + 7, y + 1);
+ g.drawLine(x + 8, y + 2, x + 8, y + 6);
+ g.drawLine(x + 7, y + 7, x + 7, y + 7);
+ g.drawLine(x + 2, y + 8, x + 6, y + 8);
+ g.drawLine(x + 1, y + 7, x + 1, y + 7);
+ g.drawLine(x, y + 2, x, y + 6);
+ g.drawLine(x + 1, y + 1, x + 1, y + 1);
+
+ if (item.isSelected())
+ {
+ g.drawLine(x + 3, y + 2, x + 5, y + 2);
+ g.fillRect(x + 2, y + 3, 5, 3);
+ g.drawLine(x + 3, y + 6, x + 5, y + 6);
+ }
+
+ // highlight
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(x + 3, y + 1, x + 6, y + 1);
+ g.drawLine(x + 8, y + 1, x + 8, y + 1);
+ g.drawLine(x + 9, y + 2, x + 9, y + 7);
+ g.drawLine(x + 8, y + 8, x + 8, y + 8);
+ g.drawLine(x + 2, y + 9, x + 7, y + 9);
+ g.drawLine(x + 1, y + 8, x + 1, y + 8);
+ g.drawLine(x + 1, y + 3, x + 1, y + 6);
+ g.drawLine(x + 2, y + 2, x + 2, y + 2);
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * The icon used to display the thumb control on a horizontally oriented
+ * {@link JSlider} component.
+ */
+ private static class HorizontalSliderThumbIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * This mask is used to paint the gradient in the shape of the thumb.
+ */
+ int[][] gradientMask = new int[][] { {0, 12}, {0, 12}, {0, 12}, {0, 12},
+ {0, 12}, {0, 12}, {0, 12}, {1, 11},
+ {2, 10}, {3, 9}, {4, 8}, {5, 7},
+ {6, 6}};
+
+ /**
+ * Creates a new instance.
+ */
+ public HorizontalSliderThumbIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 15;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 16;
+ }
+
+ /**
+ * Paints the icon, taking into account whether or not the component has
+ * the focus.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ boolean enabled = false;
+ boolean focus = false;
+ if (c != null)
+ {
+ enabled = c.isEnabled();
+ focus = c.hasFocus();
+ }
+
+ // draw the outline
+ if (enabled)
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(x + 1, y, x + 13, y);
+ g.drawLine(x + 14, y + 1, x + 14, y + 7);
+ g.drawLine(x + 14, y + 8, x + 7, y + 15);
+ g.drawLine(x + 6, y + 14, x, y + 8);
+ g.drawLine(x, y + 7, x, y + 1);
+
+// The following is commented out until the masking for the gradient painting
+// is working correctly
+// // Fill the icon.
+// if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme
+// && enabled)
+// {
+// String gradient;
+// if (focus)
+// gradient = "Slider.focusGradient";
+// else
+// gradient = "Slider.gradient";
+// MetalUtils.paintGradient(g, x + 1, y + 2, 12, 13,
+// SwingConstants.VERTICAL, gradient,
+// gradientMask);
+// }
+// else
+ {
+ if (focus)
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(x + 1, y + 2, 13, 7);
+ g.drawLine(x + 2, y + 9, x + 12, y + 9);
+ g.drawLine(x + 3, y + 10, x + 11, y + 10);
+ g.drawLine(x + 4, y + 11, x + 10, y + 11);
+ g.drawLine(x + 5, y + 12, x + 9, y + 12);
+ g.drawLine(x + 6, y + 13, x + 8, y + 13);
+ g.drawLine(x + 7, y + 14, x + 7, y + 14);
+ }
+
+ // If the slider is enabled, draw dots and highlights.
+ if (c.isEnabled()
+ && !(MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme))
+ {
+ if (focus)
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 3, y + 3, x + 3, y + 3);
+ g.drawLine(x + 7, y + 3, x + 7, y + 3);
+ g.drawLine(x + 11, y + 3, x + 11, y + 3);
+
+ g.drawLine(x + 5, y + 5, x + 5, y + 5);
+ g.drawLine(x + 9, y + 5, x + 9, y + 5);
+
+ g.drawLine(x + 3, y + 7, x + 3, y + 7);
+ g.drawLine(x + 7, y + 7, x + 7, y + 7);
+ g.drawLine(x + 11, y + 7, x + 11, y + 7);
+
+ // Draw highlights
+ if (focus)
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ else
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x + 1, y + 1, x + 13, y + 1);
+ g.drawLine(x + 1, y + 2, x + 1, y + 8);
+ g.drawLine(x + 2, y + 2, x + 2, y + 2);
+ g.drawLine(x + 6, y + 2, x + 6, y + 2);
+ g.drawLine(x + 10, y + 2, x + 10, y + 2);
+
+ g.drawLine(x + 4, y + 4, x + 4, y + 4);
+ g.drawLine(x + 8, y + 4, x + 8, y + 4);
+
+ g.drawLine(x + 2, y + 6, x + 2, y + 6);
+ g.drawLine(x + 6, y + 6, x + 6, y + 6);
+ g.drawLine(x + 10, y + 6, x + 10, y + 6);
+ }
+
+ }
+ }
+
+ /**
+ * An icon used for the 'close' button in the title frame of a
+ * {@link JInternalFrame}.
+ */
+ private static class InternalFrameCloseIcon
+ implements Icon, UIResource, Serializable
+ {
+ /** The icon size in pixels. */
+ private int size;
+
+ /**
+ * Creates a new icon.
+ *
+ * @param size the icon size (width and height) in pixels.
+ */
+ public InternalFrameCloseIcon(int size)
+ {
+ this.size = size;
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return size;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return size;
+ }
+
+ /**
+ * Paints the icon.
+ *
+ * @param c the component (an {@link JInternalFrame} is expected).
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+ AbstractButton b = (AbstractButton) c;
+
+ // fill the interior
+ if (b.getModel().isPressed())
+ // FIXME: also need to take into account whether the internal frame is
+ // selected
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 2, y + 2, 10, 10);
+
+ // draw the outline box and the cross
+ if (b.getModel().isPressed())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ {
+ // FIXME: also need to take into account whether the internal frame is
+ // selected
+ boolean selected = true;
+ if (selected)
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ }
+ g.drawLine(x + 1, y + 1, x + 13, y + 1);
+ g.drawLine(x + 1, y + 2, x + 1, y + 12);
+ g.drawLine(x + 1, y + 13, x + 13, y + 13);
+ g.drawLine(x + 13, y + 2, x + 13, y + 12);
+ g.drawLine(x + 2, y + 12, x + 2, y + 12);
+ g.drawLine(x + 12, y + 2, x + 12, y + 2);
+
+ g.fillRect(x + 4, y + 4, 2, 2);
+ g.fillRect(x + 5, y + 5, 4, 4);
+ g.drawLine(x + 9, y + 4, x + 10, y + 4);
+ g.drawLine(x + 9, y + 4, x + 9, y + 5);
+ g.drawLine(x + 4, y + 9, x + 4, y + 10);
+ g.drawLine(x + 4, y + 9, x + 5, y + 9);
+ g.drawLine(x + 9, y + 8, x + 9, y + 10);
+ g.drawLine(x + 8, y + 9, x + 10, y + 9);
+
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x, y, x + 13, y);
+ g.drawLine(x, y + 1, x, y + 13);
+ g.drawLine(x + 3, y + 4, x + 4, y + 3);
+ g.drawLine(x + 3, y + 9, x + 5, y + 7);
+ g.drawLine(x + 7, y + 5, x + 9, y + 3);
+
+ g.drawLine(x + 12, y + 3, x + 12, y + 11);
+ g.drawLine(x + 3, y + 12, x + 12, y + 12);
+
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x + 1, y + 14, x + 14, y + 14);
+ g.drawLine(x + 14, y + 1, x + 14, y + 14);
+
+ if (!b.getModel().isPressed())
+ {
+ g.drawLine(x + 5, y + 10, x + 5, y + 10);
+ g.drawLine(x + 6, y + 9, x + 7, y + 9);
+ g.drawLine(x + 10, y + 5, x + 10, y + 5);
+ g.drawLine(x + 9, y + 6, x + 9, y + 7);
+ g.drawLine(x + 10, y + 10, x + 11, y + 10);
+ g.drawLine(x + 10, y + 11, x + 10, y + 11);
+ }
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * The icon displayed at the top-left corner of a {@link JInternalFrame}.
+ */
+ private static class InternalFrameDefaultMenuIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * Creates a new instance.
+ */
+ public InternalFrameDefaultMenuIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 16;
+ }
+
+ /**
+ * Paints the icon at the specified location.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ g.fillRect(x + 1, y, 14, 2);
+ g.fillRect(x, y + 1, 2, 14);
+ g.fillRect(x + 1, y + 14, 14, 2);
+ g.fillRect(x + 14, y + 1, 2, 14);
+ g.drawLine(x + 2, y + 5, x + 14, y + 5);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 2, y + 2, 12, 3);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ g.drawLine(x + 3, y + 3, x + 3, y + 3);
+ g.drawLine(x + 6, y + 3, x + 6, y + 3);
+ g.drawLine(x + 9, y + 3, x + 9, y + 3);
+ g.drawLine(x + 12, y + 3, x + 12, y + 3);
+
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.fillRect(x + 2, y + 6, 12, 8);
+ g.drawLine(x + 2, y + 2, x + 2, y + 2);
+ g.drawLine(x + 5, y + 2, x + 5, y + 2);
+ g.drawLine(x + 8, y + 2, x + 8, y + 2);
+ g.drawLine(x + 11, y + 2, x + 11, y + 2);
+ }
+ }
+
+ /**
+ * An icon used in the title frame of a {@link JInternalFrame}. When you
+ * maximise an internal frame, this icon will replace the 'maximise' icon to
+ * provide a 'restore' option.
+ */
+ private static class InternalFrameAltMaximizeIcon
+ implements Icon, UIResource, Serializable
+ {
+ /** The icon size in pixels. */
+ private int size;
+
+ /**
+ * Creates a new icon.
+ *
+ * @param size the icon size in pixels.
+ */
+ public InternalFrameAltMaximizeIcon(int size)
+ {
+ this.size = size;
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return size;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return size;
+ }
+
+ /**
+ * Paints the icon at the specified location.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+
+ AbstractButton b = (AbstractButton) c;
+
+ // fill the small box interior
+ if (b.getModel().isPressed())
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 2, y + 6, 7, 7);
+
+
+ if (b.getModel().isPressed())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+
+ g.drawLine(x + 12, y + 1, x + 13, y + 1);
+ g.drawLine(x + 11, y + 2, x + 12, y + 2);
+ g.drawLine(x + 10, y + 3, x + 11, y + 3);
+ g.drawLine(x + 8, y + 2, x + 8, y + 3);
+ g.fillRect(x + 8, y + 4, 3, 3);
+ g.drawLine(x + 11, y + 6, x + 12, y + 6);
+
+ g.drawLine(x + 1, y + 5, x + 5, y + 5);
+ g.drawLine(x + 1, y + 6, x + 1, y + 12);
+ g.drawLine(x + 9, y + 9, x + 9, y + 12);
+ g.drawLine(x + 1, y + 13, x + 9, y + 13);
+
+ g.drawLine(x + 2, y + 12, x + 2, y + 12);
+
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 12, y, x + 9, y + 3);
+ g.drawLine(x + 7, y + 1, x + 8, y + 1);
+ g.drawLine(x + 7, y + 2, x + 7, y + 6);
+ g.drawLine(x + 11, y + 5, x + 12, y + 5);
+ g.drawLine(x, y + 4, x + 5, y + 4);
+ g.drawLine(x, y + 5, x, y + 13);
+ g.drawLine(x + 3, y + 12, x + 8, y + 12);
+ g.drawLine(x + 8, y + 8, x + 8, y + 11);
+ g.drawLine(x + 9, y + 8, x + 9, y + 8);
+
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x + 9, y + 2, x + 9, y + 2);
+ g.drawLine(x + 11, y + 4, x + 13, y + 2);
+ g.drawLine(x + 13, y + 6, x + 13, y + 6);
+ g.drawLine(x + 8, y + 7, x + 13, y + 7);
+ g.drawLine(x + 6, y + 5, x + 6, y + 5);
+ g.drawLine(x + 10, y + 8, x + 10, y + 13);
+ g.drawLine(x + 1, y + 14, x + 10, y + 14);
+
+ if (!b.getModel().isPressed())
+ {
+ g.drawLine(x + 2, y + 6, x + 6, y + 6);
+ g.drawLine(x + 2, y + 6, x + 2, y + 11);
+ }
+
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An icon used for the 'maximize' button in the title frame of a
+ * {@link JInternalFrame}.
+ */
+ private static class InternalFrameMaximizeIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * Creates a new instance.
+ */
+ public InternalFrameMaximizeIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 16;
+ }
+
+ /**
+ * Paints the icon at the specified location.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+
+ AbstractButton b = (AbstractButton) c;
+
+ // fill the interior
+ if (b.getModel().isPressed())
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 2, y + 6, 7, 7);
+
+ if (b.getModel().isPressed())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+
+ g.drawLine(x + 9, y + 1, x + 10, y + 1);
+ g.fillRect(x + 11, y + 1, 3, 3);
+ g.fillRect(x + 12, y + 4, 2, 2);
+ g.drawLine(x + 10, y + 3, x + 10, y + 3);
+ g.drawLine(x + 9, y + 4, x + 10, y + 4);
+ g.drawLine(x + 1, y + 5, x + 9, y + 5);
+ g.drawLine(x + 1, y + 6, x + 1, y + 12);
+ g.drawLine(x + 9, y + 6, x + 9, y + 12);
+ g.drawLine(x + 1, y + 13, x + 9, y + 13);
+
+ // fill
+ g.drawLine(x + 7, y + 6, x + 8, y + 6);
+ g.drawLine(x + 6, y + 7, x + 8, y + 7);
+ g.drawLine(x + 5, y + 8, x + 6, y + 8);
+ g.drawLine(x + 4, y + 9, x + 5, y + 9);
+ g.drawLine(x + 3, y + 10, x + 4, y + 10);
+ g.drawLine(x + 2, y + 11, x + 3, y + 11);
+ g.drawLine(x + 2, y + 12, x + 4, y + 12);
+ g.drawLine(x + 8, y + 8, x + 8, y + 8);
+
+ // draw black
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 8, y, x + 13, y);
+ g.drawLine(x + 8, y + 1, x + 8, y + 1);
+ g.drawLine(x + 10, y + 2, x + 9, y + 3);
+ g.drawLine(x, y + 4, x + 8, y + 4);
+ g.drawLine(x, y + 5, x, y + 13);
+
+ g.drawLine(x + 2, y + 10, x + 6, y + 6);
+ g.drawLine(x + 8, y + 9, x + 8, y + 11);
+ g.drawLine(x + 5, y + 12, x + 8, y + 12);
+
+ // draw white
+ g.setColor(MetalLookAndFeel.getWhite());
+ if (!b.getModel().isPressed())
+ {
+ g.drawLine(x + 2, y + 6, x + 5, y + 6);
+ g.drawLine(x + 2, y + 7, x + 2, y + 9);
+ g.drawLine(x + 4, y + 11, x + 7, y + 8);
+ }
+
+ g.drawLine(x + 1, y + 14, x + 10, y + 14);
+ g.drawLine(x + 10, y + 5, x + 10, y + 13);
+
+ g.drawLine(x + 9, y + 2, x + 9, y + 2);
+ g.drawLine(x + 11, y + 4, x + 11, y + 5);
+ g.drawLine(x + 13, y + 6, x + 14, y + 6);
+ g.drawLine(x + 14, y + 1, x + 14, y + 5);
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * An icon used in the title frame of a {@link JInternalFrame}.
+ */
+ private static class InternalFrameMinimizeIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * Creates a new instance.
+ */
+ public InternalFrameMinimizeIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 16;
+ }
+
+ /**
+ * Paints the icon at the specified location.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color savedColor = g.getColor();
+
+ AbstractButton b = (AbstractButton) c;
+
+ if (b.getModel().isPressed())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ // FIXME: here the color depends on whether or not the internal frame
+ // is selected
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+
+ g.drawLine(x + 12, y + 1, x + 13, y + 1);
+ g.drawLine(x + 11, y + 2, x + 12, y + 2);
+ g.drawLine(x + 10, y + 3, x + 11, y + 3);
+ g.drawLine(x + 8, y + 2, x + 8, y + 3);
+ g.fillRect(x + 8, y + 4, 3, 3);
+ g.drawLine(x + 11, y + 6, x + 12, y + 6);
+
+ g.drawLine(x + 1, y + 8, x + 6, y + 8);
+ g.drawLine(x + 1, y + 9, x + 1, y + 12);
+ g.drawLine(x + 6, y + 9, x + 6, y + 12);
+ g.drawLine(x + 1, y + 13, x + 6, y + 13);
+
+ g.drawLine(x + 5, y + 9, x + 5, y + 9);
+ g.drawLine(x + 2, y + 12, x + 2, y + 12);
+
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 12, y, x + 9, y + 3);
+ g.drawLine(x + 7, y + 1, x + 8, y + 1);
+ g.drawLine(x + 7, y + 2, x + 7, y + 6);
+ g.drawLine(x, y + 7, x + 6, y + 7);
+ g.drawLine(x, y + 8, x, y + 13);
+ g.drawLine(x + 3, y + 12, x + 5, y + 12);
+ g.drawLine(x + 5, y + 10, x + 5, y + 11);
+ g.drawLine(x + 11, y + 5, x + 12, y + 5);
+
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x + 9, y + 2, x + 9, y + 2);
+ g.drawLine(x + 11, y + 4, x + 13, y + 2);
+ g.drawLine(x + 13, y + 6, x + 13, y + 6);
+ g.drawLine(x + 8, y + 7, x + 13, y + 7);
+ g.drawLine(x + 7, y + 9, x + 7, y + 13);
+ g.drawLine(x + 1, y + 14, x + 7, y + 14);
+
+ if (b.getModel().isPressed())
+ {
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ g.fillRect(x + 2, y + 9, 3, 3);
+ }
+ else
+ {
+ g.drawLine(x + 2, y + 9, x + 4, y + 9);
+ g.drawLine(x + 2, y + 10, x + 2, y + 11);
+ }
+
+ g.setColor(savedColor);
+ }
+ }
+
+ /**
+ * The icon used to display the thumb control on a horizontally oriented
+ * {@link JSlider} component.
+ */
+ private static class VerticalSliderThumbIcon
+ implements Icon, UIResource, Serializable
+ {
+ /**
+ * This mask is used to paint the gradient in the shape of the thumb.
+ */
+ int[][] gradientMask = new int[][] { {0, 12}, {0, 12}, {0, 12}, {0, 12},
+ {0, 12}, {0, 12}, {0, 12}, {1, 11},
+ {2, 10}, {3, 9}, {4, 8}, {5, 7},
+ {6, 6}};
+
+ /**
+ * Creates a new instance.
+ */
+ public VerticalSliderThumbIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 15;
+ }
+
+ /**
+ * Paints the icon taking into account whether the slider control has the
+ * focus or not.
+ *
+ * @param c the slider (must be a non-<code>null</code> instance of
+ * {@link JSlider}.
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ boolean enabled = false;
+ boolean focus = false;
+ if (c != null)
+ {
+ enabled = c.isEnabled();
+ focus = c.hasFocus();
+ }
+
+ // draw the outline
+ if (enabled)
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(x + 1, y, x + 7, y);
+ g.drawLine(x + 8, y, x + 15, y + 7);
+ g.drawLine(x + 14, y + 8, x + 8, y + 14);
+ g.drawLine(x + 8, y + 14, x + 1, y + 14);
+ g.drawLine(x, y + 13, x, y + 1);
+
+// The following is commented out until the masking for the gradient painting
+// is working correctly
+// // Fill the icon.
+// if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme
+// && enabled)
+// {
+// String gradient;
+// if (focus)
+// gradient = "Slider.focusGradient";
+// else
+// gradient = "Slider.gradient";
+// MetalUtils.paintGradient(g, x + 2, y + 1, 13, 12,
+// SwingConstants.HORIZONTAL, gradient,
+// gradientMask);
+// }
+// else
+ {
+ if (focus)
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(x + 2, y + 1, 7, 13);
+ g.drawLine(x + 9, y + 2, x + 9, y + 12);
+ g.drawLine(x + 10, y + 3, x + 10, y + 11);
+ g.drawLine(x + 11, y + 4, x + 11, y + 10);
+ g.drawLine(x + 12, y + 5, x + 12, y + 9);
+ g.drawLine(x + 13, y + 6, x + 13, y + 8);
+ g.drawLine(x + 14, y + 7, x + 14, y + 7);
+ }
+
+ // if the slider is enabled, draw dots and highlights
+ if (enabled
+ && ! (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme))
+ {
+ if (focus)
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 3, y + 3, x + 3, y + 3);
+ g.drawLine(x + 3, y + 7, x + 3, y + 7);
+ g.drawLine(x + 3, y + 11, x + 3, y + 11);
+
+ g.drawLine(x + 5, y + 5, x + 5, y + 5);
+ g.drawLine(x + 5, y + 9, x + 5, y + 9);
+
+ g.drawLine(x + 7, y + 3, x + 7, y + 3);
+ g.drawLine(x + 7, y + 7, x + 7, y + 7);
+ g.drawLine(x + 7, y + 11, x + 7, y + 11);
+
+ // draw highlights
+ if (focus)
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ else
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x + 1, y + 1, x + 8, y + 1);
+ g.drawLine(x + 1, y + 2, x + 1, y + 13);
+ g.drawLine(x + 2, y + 2, x + 2, y + 2);
+ g.drawLine(x + 2, y + 6, x + 2, y + 6);
+ g.drawLine(x + 2, y + 10, x + 2, y + 10);
+
+ g.drawLine(x + 4, y + 4, x + 4, y + 4);
+ g.drawLine(x + 4, y + 8, x + 4, y + 8);
+
+ g.drawLine(x + 6, y + 2, x + 6, y + 2);
+ g.drawLine(x + 6, y + 6, x + 6, y + 6);
+ g.drawLine(x + 6, y + 10, x + 6, y + 10);
+
+ }
+ }
+ }
+
+ /**
+ * A tree control icon. This icon can be in one of two states: expanded and
+ * collapsed.
+ */
+ public static class TreeControlIcon implements Icon, Serializable
+ {
+
+ /** ???. */
+ protected boolean isLight;
+
+ /** A flag that controls whether or not the icon is collapsed. */
+ private boolean collapsed;
+
+ /**
+ * Creates a new icon.
+ *
+ * @param isCollapsed a flag that controls whether the icon is in the
+ * collapsed state or the expanded state.
+ */
+ public TreeControlIcon(boolean isCollapsed)
+ {
+ collapsed = isCollapsed;
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return 18;
+ }
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return 18;
+ }
+
+ /**
+ * Paints the icon at the location (x, y).
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // TODO: pick up appropriate UI colors
+ Color dark = new Color(99, 130, 191);
+ Color light = new Color(163, 184, 204);
+ Color white = Color.white;
+
+ x += 8;
+ y += 6;
+
+ final int w = 6;
+ final int wHalf = (w >> 2);
+ g.setColor(light);
+ g.drawOval(x, y, w, w);
+ g.setColor(dark);
+ g.fillOval(x + 1, y + 1, w - 1, w - 1);
+
+ if (collapsed)
+ g.fillRect(x + w, y + wHalf + 1, w, 2);
+ else
+ g.fillRect(x + wHalf + 1, y + w, 2, w);
+
+ g.setColor(white);
+ g.fillRect(x + wHalf + 1, y + wHalf + 1, 2, 2);
+
+ }
+
+ /**
+ * Simply calls {@link #paintIcon(Component, Graphics, int, int)}.
+ *
+ * @param c the component.
+ * @param g the graphics device.
+ * @param x the x coordinate.
+ * @param y the y coordinate.
+ */
+ public void paintMe(Component c, Graphics g, int x, int y)
+ {
+ paintIcon(c, g, x, y);
+ }
+ }
+
+ /**
+ * A tree folder icon.
+ */
+ public static class TreeFolderIcon extends FolderIcon16
+ {
+ /**
+ * Creates a new instance.
+ */
+ public TreeFolderIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the additional height for this icon, in this case <code>2</code>
+ * pixels.
+ *
+ * @return <code>2</code>.
+ */
+ public int getAdditionalHeight()
+ {
+ return 2;
+ }
+
+ /**
+ * Returns the vertical shift, in pixels, applied when painting the icon.
+ * This overridden method returns <code>-1</code>.
+ *
+ * @return The shift.
+ */
+ public int getShift()
+ {
+ return -1;
+ }
+ }
+
+ /**
+ * A tree leaf icon.
+ */
+ public static class TreeLeafIcon extends FileIcon16
+ {
+ /**
+ * Creates a new instance.
+ */
+ public TreeLeafIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the additional height for this icon, in this case <code>4</code>
+ * pixels.
+ *
+ * @return <code>4</code>.
+ */
+ public int getAdditionalHeight()
+ {
+ return 4;
+ }
+
+ /**
+ * Returns the vertical shift, in pixels, applied when painting the icon.
+ * This overridden method returns <code>2</code>.
+ *
+ * @return The shift.
+ */
+ public int getShift()
+ {
+ return 2;
+ }
+ }
+
+ /**
+ * An icon representing a hard disk.
+ *
+ * @see MetalIconFactory#getTreeHardDriveIcon()
+ */
+ private static class TreeHardDriveIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * Creates a new icon instance.
+ */
+ public TreeHardDriveIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return <code>16</code>.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return <code>16</code>.
+ */
+ public int getIconHeight()
+ {
+ return 16;
+ }
+
+ /**
+ * Paints the icon at the specified location, using colors from the
+ * current theme.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 1, y + 4, x + 1, y + 5);
+ g.drawLine(x + 14, y + 4, x + 14, y + 5);
+ g.drawLine(x + 1, y + 7, x + 1, y + 8);
+ g.drawLine(x + 14, y + 7, x + 14, y + 8);
+ g.drawLine(x + 1, y + 10, x + 1, y + 11);
+ g.drawLine(x + 14, y + 10, x + 14, y + 11);
+
+ g.drawLine(x + 2, y + 3, x + 3, y + 3);
+ g.drawLine(x + 12, y + 3, x + 13, y + 3);
+ g.drawLine(x + 2, y + 6, x + 3, y + 6);
+ g.drawLine(x + 12, y + 6, x + 13, y + 6);
+ g.drawLine(x + 2, y + 9, x + 3, y + 9);
+ g.drawLine(x + 12, y + 9, x + 13, y + 9);
+ g.drawLine(x + 2, y + 12, x + 3, y + 12);
+ g.drawLine(x + 12, y + 12, x + 13, y + 12);
+
+ g.drawLine(x + 4, y + 2, x + 11, y + 2);
+ g.drawLine(x + 4, y + 7, x + 11, y + 7);
+ g.drawLine(x + 4, y + 10, x + 11, y + 10);
+ g.drawLine(x + 4, y + 13, x + 11, y + 13);
+
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.fillRect(x + 4, y + 3, 2, 2);
+ g.drawLine(x + 6, y + 4, x + 6, y + 4);
+ g.drawLine(x + 7, y + 3, x + 9, y + 3);
+ g.drawLine(x + 8, y + 4, x + 8, y + 4);
+ g.drawLine(x + 11, y + 3, x + 11, y + 3);
+ g.fillRect(x + 2, y + 4, 2, 2);
+ g.fillRect(x + 2, y + 7, 2, 2);
+ g.fillRect(x + 2, y + 10, 2, 2);
+ g.drawLine(x + 4, y + 6, x + 4, y + 6);
+ g.drawLine(x + 4, y + 9, x + 4, y + 9);
+ g.drawLine(x + 4, y + 12, x + 4, y + 12);
+
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x + 13, y + 4, x + 13, y + 4);
+ g.drawLine(x + 12, y + 5, x + 13, y + 5);
+ g.drawLine(x + 13, y + 7, x + 13, y + 7);
+ g.drawLine(x + 12, y + 8, x + 13, y + 8);
+ g.drawLine(x + 13, y + 10, x + 13, y + 10);
+ g.drawLine(x + 12, y + 11, x + 13, y + 11);
+
+ g.drawLine(x + 10, y + 5, x + 10, y + 5);
+ g.drawLine(x + 7, y + 6, x + 7, y + 6);
+ g.drawLine(x + 9, y + 6, x + 9, y + 6);
+ g.drawLine(x + 11, y + 6, x + 11, y + 6);
+
+ g.drawLine(x + 10, y + 8, x + 10, y + 8);
+ g.drawLine(x + 7, y + 9, x + 7, y + 9);
+ g.drawLine(x + 9, y + 9, x + 9, y + 9);
+ g.drawLine(x + 11, y + 9, x + 11, y + 9);
+
+ g.drawLine(x + 10, y + 11, x + 10, y + 11);
+ g.drawLine(x + 7, y + 12, x + 7, y + 12);
+ g.drawLine(x + 9, y + 12, x + 9, y + 12);
+ g.drawLine(x + 11, y + 12, x + 11, y + 12);
+
+ g.setColor(saved);
+ }
+ }
+
+ /**
+ * An icon representing a floppy disk.
+ *
+ * @see MetalIconFactory#getTreeFloppyDriveIcon()
+ */
+ private static class TreeFloppyDriveIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * Creates a new icon instance.
+ */
+ public TreeFloppyDriveIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return <code>16</code>.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return <code>16</code>.
+ */
+ public int getIconHeight()
+ {
+ return 16;
+ }
+
+ /**
+ * Paints the icon at the specified location, using colors from the
+ * current theme.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 1, y + 1, x + 13, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + 14);
+ g.drawLine(x + 1, y + 14, x + 14, y + 14);
+ g.drawLine(x + 14, y + 2, x + 14, y + 14);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 2, y + 2, 12, 12);
+
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.fillRect(x + 5, y + 2, 6, 5);
+ g.drawLine(x + 4, y + 8, x + 11, y + 8);
+ g.drawLine(x + 3, y + 9, x + 3, y + 13);
+ g.drawLine(x + 12, y + 9, x + 12, y + 13);
+
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.fillRect(x + 8, y + 3, 2, 3);
+ g.fillRect(x + 4, y + 9, 8, 5);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ g.drawLine(x + 5, y + 10, x + 9, y + 10);
+ g.drawLine(x + 5, y + 12, x + 8, y + 12);
+
+ g.setColor(saved);
+ }
+ }
+
+ /**
+ * An icon representing a computer.
+ *
+ * @see MetalIconFactory#getTreeComputerIcon()
+ */
+ private static class TreeComputerIcon
+ implements Icon, UIResource, Serializable
+ {
+
+ /**
+ * Creates a new icon instance.
+ */
+ public TreeComputerIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the width of the icon, in pixels.
+ *
+ * @return <code>16</code>.
+ */
+ public int getIconWidth()
+ {
+ return 16;
+ }
+
+ /**
+ * Returns the height of the icon, in pixels.
+ *
+ * @return <code>16</code>.
+ */
+ public int getIconHeight()
+ {
+ return 16;
+ }
+
+ /**
+ * Paints the icon at the specified location, using colors from the
+ * current theme.
+ *
+ * @param c the component (ignored).
+ * @param g the graphics device.
+ * @param x the x-coordinate for the top-left of the icon.
+ * @param y the y-coordinate for the top-left of the icon.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 3, y + 1, x + 12, y + 1);
+ g.drawLine(x + 2, y + 2, x + 2, y + 8);
+ g.drawLine(x + 13, y + 2, x + 13, y + 8);
+ g.drawLine(x + 3, y + 9, x + 3, y + 9);
+ g.drawLine(x + 12, y + 9, x + 12, y + 9);
+ g.drawRect(x + 1, y + 10, 13, 4);
+ g.drawLine(x + 5, y + 3, x + 10, y + 3);
+ g.drawLine(x + 5, y + 8, x + 10, y + 8);
+ g.drawLine(x + 4, y + 4, x + 4, y + 7);
+ g.drawLine(x + 11, y + 4, x + 11, y + 7);
+
+ g.setColor(MetalLookAndFeel.getPrimaryControl());
+ g.fillRect(x + 5, y + 4, 6, 4);
+
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x + 6, y + 12, x + 8, y + 12);
+ g.drawLine(x + 10, y + 12, x + 12, y + 12);
+ g.setColor(saved);
+ }
+ }
+
+ /** The icon returned by {@link #getCheckBoxIcon()}. */
+ private static Icon checkBoxIcon;
+
+ /** The icon returned by {@link #getCheckBoxMenuItemIcon()}. */
+ private static Icon checkBoxMenuItemIcon;
+
+ /** The icon returned by {@link #getFileChooserDetailViewIcon()}. */
+ private static Icon fileChooserDetailViewIcon;
+
+ /** The icon returned by {@link #getFileChooserHomeFolderIcon()}. */
+ private static Icon fileChooserHomeFolderIcon;
+
+ /** The icon returned by {@link #getFileChooserListViewIcon()}. */
+ private static Icon fileChooserListViewIcon;
+
+ /** The icon returned by {@link #getFileChooserNewFolderIcon()}. */
+ private static Icon fileChooserNewFolderIcon;
+
+ /** The icon returned by {@link #getFileChooserUpFolderIcon()}. */
+ private static Icon fileChooserUpFolderIcon;
+
+ /** The cached RadioButtonIcon instance. */
+ private static RadioButtonIcon radioButtonIcon;
+
+ /** The icon returned by {@link #getRadioButtonMenuItemIcon()}. */
+ private static Icon radioButtonMenuItemIcon;
+
+ /** The icon returned by {@link #getInternalFrameDefaultMenuIcon()}. */
+ private static Icon internalFrameDefaultMenuIcon;
+
+ /** The icon returned by {@link #getTreeComputerIcon()}. */
+ private static Icon treeComputerIcon;
+
+ /** The icon instance returned by {@link #getTreeFloppyDriveIcon()}. */
+ private static Icon treeFloppyDriveIcon;
+
+ /** The icon instance returned by {@link #getTreeHardDriveIcon()}. */
+ private static Icon treeHardDriveIcon;
+
+ /** The icon instance returned by {@link #getHorizontalSliderThumbIcon()}. */
+ private static Icon horizontalSliderThumbIcon;
+
+ /** The icon instance returned by {@link #getVerticalSliderThumbIcon()}. */
+ private static Icon verticalSliderThumbIcon;
+
+ /**
+ * Creates a new instance. All the methods are static, so creating an
+ * instance isn't necessary.
+ */
+ public MetalIconFactory()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns an icon for use when rendering the {@link JCheckBox} component.
+ *
+ * @return A check box icon.
+ *
+ * @since 1.3
+ */
+ public static Icon getCheckBoxIcon()
+ {
+ if (checkBoxIcon == null)
+ checkBoxIcon = new MetalCheckBoxIcon();
+ return checkBoxIcon;
+ }
+
+ /**
+ * Returns an icon for use when rendering the {@link JCheckBoxMenuItem}
+ * component.
+ *
+ * @return An icon.
+ */
+ public static Icon getCheckBoxMenuItemIcon()
+ {
+ if (checkBoxMenuItemIcon == null)
+ checkBoxMenuItemIcon = new CheckBoxMenuItemIcon();
+ return checkBoxMenuItemIcon;
+ }
+
+ /**
+ * Returns an icon for use by the {@link JFileChooser} component.
+ *
+ * @return An icon.
+ */
+ public static Icon getFileChooserDetailViewIcon()
+ {
+ if (fileChooserDetailViewIcon == null)
+ fileChooserDetailViewIcon = new FileChooserDetailViewIcon();
+ return fileChooserDetailViewIcon;
+ }
+
+ /**
+ * Returns an icon for use by the {@link JFileChooser} component.
+ *
+ * @return An icon.
+ */
+ public static Icon getFileChooserHomeFolderIcon()
+ {
+ if (fileChooserHomeFolderIcon == null)
+ fileChooserHomeFolderIcon = new FileChooserHomeFolderIcon();
+ return fileChooserHomeFolderIcon;
+ }
+
+ /**
+ * Returns an icon for use by the {@link JFileChooser} component.
+ *
+ * @return An icon.
+ */
+ public static Icon getFileChooserListViewIcon()
+ {
+ if (fileChooserListViewIcon == null)
+ fileChooserListViewIcon = new FileChooserListViewIcon();
+ return fileChooserListViewIcon;
+ }
+
+ /**
+ * Returns an icon for use by the {@link JFileChooser} component.
+ *
+ * @return An icon.
+ */
+ public static Icon getFileChooserNewFolderIcon()
+ {
+ if (fileChooserNewFolderIcon == null)
+ fileChooserNewFolderIcon = new FileChooserNewFolderIcon();
+ return fileChooserNewFolderIcon;
+ }
+
+ /**
+ * Returns an icon for use by the {@link JFileChooser} component.
+ *
+ * @return An icon.
+ */
+ public static Icon getFileChooserUpFolderIcon()
+ {
+ if (fileChooserUpFolderIcon == null)
+ fileChooserUpFolderIcon = new FileChooserUpFolderIcon();
+ return fileChooserUpFolderIcon;
+ }
+
+ /**
+ * Returns an icon for RadioButtons in the Metal L&amp;F.
+ *
+ * @return an icon for RadioButtons in the Metal L&amp;F
+ */
+ public static Icon getRadioButtonIcon()
+ {
+ if (radioButtonIcon == null)
+ radioButtonIcon = new RadioButtonIcon();
+ return radioButtonIcon;
+ }
+
+ /**
+ * Creates a new instance of the icon used in a {@link JRadioButtonMenuItem}.
+ *
+ * @return A new icon instance.
+ */
+ public static Icon getRadioButtonMenuItemIcon()
+ {
+ if (radioButtonMenuItemIcon == null)
+ radioButtonMenuItemIcon = new RadioButtonMenuItemIcon();
+ return radioButtonMenuItemIcon;
+ }
+
+ /**
+ * Returns the icon used to display the thumb for a horizontally oriented
+ * {@link JSlider}.
+ *
+ * @return The icon.
+ */
+ public static Icon getHorizontalSliderThumbIcon()
+ {
+ if (horizontalSliderThumbIcon == null)
+ horizontalSliderThumbIcon = new HorizontalSliderThumbIcon();
+ return horizontalSliderThumbIcon;
+ }
+
+ /**
+ * Creates a new icon used to represent the 'close' button in the title
+ * pane of a {@link JInternalFrame}.
+ *
+ * @param size the icon size.
+ *
+ * @return A close icon.
+ */
+ public static Icon getInternalFrameCloseIcon(int size)
+ {
+ return new InternalFrameCloseIcon(size);
+ }
+
+ /**
+ * Creates a new icon for the menu in a {@link JInternalFrame}. This is the
+ * icon displayed at the top left of the frame.
+ *
+ * @return A menu icon.
+ */
+ public static Icon getInternalFrameDefaultMenuIcon()
+ {
+ if (internalFrameDefaultMenuIcon == null)
+ internalFrameDefaultMenuIcon = new InternalFrameDefaultMenuIcon();
+ return internalFrameDefaultMenuIcon;
+ }
+
+ /**
+ * Creates a new icon for the 'maximize' button in a {@link JInternalFrame}.
+ *
+ * @param size the icon size in pixels.
+ *
+ * @return The icon.
+ *
+ * @see #getInternalFrameAltMaximizeIcon(int)
+ */
+ public static Icon getInternalFrameMaximizeIcon(int size)
+ {
+ return new InternalFrameMaximizeIcon();
+ }
+
+ /**
+ * Returns the icon used for the minimize button in the frame title for a
+ * {@link JInternalFrame}.
+ *
+ * @param size the icon size in pixels (ignored by this implementation).
+ *
+ * @return The icon.
+ */
+ public static Icon getInternalFrameMinimizeIcon(int size)
+ {
+ return new InternalFrameMinimizeIcon();
+ }
+
+ /**
+ * Creates a new icon for the 'restore' button in a {@link JInternalFrame}
+ * that has been maximised.
+ *
+ * @param size the icon size in pixels.
+ *
+ * @return The icon.
+ *
+ * @see #getInternalFrameMaximizeIcon(int)
+ */
+ public static Icon getInternalFrameAltMaximizeIcon(int size)
+ {
+ return new InternalFrameAltMaximizeIcon(size);
+ }
+
+ /**
+ * Returns the icon used to display the thumb for a vertically oriented
+ * {@link JSlider}.
+ *
+ * @return The icon.
+ */
+ public static Icon getVerticalSliderThumbIcon()
+ {
+ if (verticalSliderThumbIcon == null)
+ verticalSliderThumbIcon = new VerticalSliderThumbIcon();
+ return verticalSliderThumbIcon;
+ }
+
+ /**
+ * Creates and returns a new tree folder icon.
+ *
+ * @return A new tree folder icon.
+ */
+ public static Icon getTreeFolderIcon()
+ {
+ return new TreeFolderIcon();
+ }
+
+ /**
+ * Creates and returns a new tree leaf icon.
+ *
+ * @return A new tree leaf icon.
+ */
+ public static Icon getTreeLeafIcon()
+ {
+ return new TreeLeafIcon();
+ }
+
+ /**
+ * Creates and returns a tree control icon.
+ *
+ * @param isCollapsed a flag that controls whether the icon is in the
+ * collapsed or expanded state.
+ *
+ * @return A tree control icon.
+ */
+ public static Icon getTreeControlIcon(boolean isCollapsed)
+ {
+ return new TreeControlIcon(isCollapsed);
+ }
+
+ /**
+ * Returns a <code>16x16</code> icon representing a computer.
+ *
+ * @return The icon.
+ */
+ public static Icon getTreeComputerIcon()
+ {
+ if (treeComputerIcon == null)
+ treeComputerIcon = new TreeComputerIcon();
+ return treeComputerIcon;
+ }
+
+ /**
+ * Returns a <code>16x16</code> icon representing a floppy disk.
+ *
+ * @return The icon.
+ */
+ public static Icon getTreeFloppyDriveIcon()
+ {
+ if (treeFloppyDriveIcon == null)
+ treeFloppyDriveIcon = new TreeFloppyDriveIcon();
+ return treeFloppyDriveIcon;
+ }
+
+ /**
+ * Returns a <code>16x16</code> icon representing a hard disk.
+ *
+ * @return The icon.
+ */
+ public static Icon getTreeHardDriveIcon()
+ {
+ if (treeHardDriveIcon == null)
+ treeHardDriveIcon = new TreeHardDriveIcon();
+ return treeHardDriveIcon;
+ }
+
+ /**
+ * Returns a new instance of a 4 x 8 icon showing a small black triangle that
+ * points to the right. This is displayed in menu items that have a
+ * sub menu.
+ *
+ * @return The icon.
+ */
+ public static Icon getMenuArrowIcon()
+ {
+ if (menuArrow == null)
+ menuArrow = new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 8;
+ }
+
+ public int getIconWidth()
+ {
+ return 4;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+ for (int i = 0; i < 4; i++)
+ g.drawLine(x + i, y + i, x + i, y + 7 - i);
+ g.setColor(saved);
+ }
+ };
+ return menuArrow;
+ }
+
+ /**
+ * Returns a new instance of a 4 x 8 icon showing a small black triangle that
+ * points to the right. This is displayed in menu items that have a sub menu.
+ *
+ * @return The icon.
+ */
+ public static Icon getMenuItemArrowIcon()
+ {
+ if (menuItemArrow == null)
+ menuItemArrow = new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 8;
+ }
+
+ public int getIconWidth()
+ {
+ return 4;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+ for (int i = 0; i < 4; i++)
+ g.drawLine(x + i, y + i, x + i, y + 7 - i);
+ g.setColor(saved);
+ }
+ };
+ return menuItemArrow;
+ }
+
+ /**
+ * Returns a new instance of a 13 x 13 icon showing a small black check mark.
+ *
+ * @return The icon.
+ */
+ public static Icon getMenuItemCheckIcon()
+ {
+ return new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 13;
+ }
+
+ public int getIconWidth()
+ {
+ return 13;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+ g.drawLine(3 + x, 5 + y, 3 + x, 9 + y);
+ g.drawLine(4 + x, 5 + y, 4 + x, 9 + y);
+ g.drawLine(5 + x, 7 + y, 9 + x, 3 + y);
+ g.drawLine(5 + x, 8 + y, 9 + x, 4 + y);
+ g.setColor(saved);
+ }
+ };
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java
new file mode 100644
index 000000000..08de77446
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java
@@ -0,0 +1,457 @@
+/* MetalInternalFrameTitlePane.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.Icon;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
+
+
+/**
+ * The title pane for a {@link JInternalFrame} (see
+ * {@link MetalInternalFrameUI#createNorthPane(JInternalFrame)}). This can
+ * be displayed in two styles: one for regular internal frames, and the other
+ * for "palette" style internal frames.
+ */
+public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane
+{
+
+ /**
+ * A property change handler that listens for changes to the
+ * <code>JInternalFrame.isPalette</code> property and updates the title
+ * pane as appropriate.
+ */
+ class MetalInternalFrameTitlePanePropertyChangeHandler
+ extends PropertyChangeHandler
+ {
+ /**
+ * Creates a new handler.
+ */
+ public MetalInternalFrameTitlePanePropertyChangeHandler()
+ {
+ super();
+ }
+
+ /**
+ * Handles <code>JInternalFrame.isPalette</code> property changes, with all
+ * other property changes being passed to the superclass.
+ *
+ * @param e the event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String propName = e.getPropertyName();
+ if (e.getPropertyName().equals(JInternalFrame.FRAME_ICON_PROPERTY))
+ {
+ title.setIcon(frame.getFrameIcon());
+ }
+ else if (propName.equals("JInternalFrame.isPalette"))
+ {
+ if (e.getNewValue().equals(Boolean.TRUE))
+ setPalette(true);
+ else
+ setPalette(false);
+ }
+ else
+ super.propertyChange(e);
+ }
+ }
+
+ /**
+ * A layout manager for the title pane.
+ *
+ * @see #createLayout()
+ */
+ private class MetalTitlePaneLayout implements LayoutManager
+ {
+ /**
+ * Creates a new <code>TitlePaneLayout</code> object.
+ */
+ public MetalTitlePaneLayout()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Adds a Component to the Container.
+ *
+ * @param name The name to reference the added Component by.
+ * @param c The Component to add.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the children of the Title Pane.
+ *
+ * @param c The Container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+
+ Dimension size = c.getSize();
+ Insets insets = c.getInsets();
+ int width = size.width - insets.left - insets.right;
+ int height = size.height - insets.top - insets.bottom;
+
+
+ int loc = width - insets.right - 1;
+ int top = insets.top + 2;
+ int buttonHeight = height - 4;
+ if (closeButton.isVisible())
+ {
+ int buttonWidth = closeIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ closeButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ loc -= 6;
+ }
+
+ if (maxButton.isVisible())
+ {
+ int buttonWidth = maxIcon.getIconWidth();
+ loc -= buttonWidth + 4;
+ maxButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (iconButton.isVisible())
+ {
+ int buttonWidth = minIcon.getIconWidth();
+ loc -= buttonWidth + 4;
+ iconButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ loc -= 2;
+ }
+
+ Dimension titlePreferredSize = title.getPreferredSize();
+ title.setBounds(insets.left + 5, insets.top,
+ Math.min(titlePreferredSize.width, loc - insets.left - 10),
+ height);
+
+ }
+
+ /**
+ * This method returns the minimum size of the given Container given the
+ * children that it has.
+ *
+ * @param c The Container to get a minimum size for.
+ *
+ * @return The minimum size of the Container.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return preferredLayoutSize(c);
+ }
+
+ /**
+ * Returns the preferred size of the given Container taking
+ * into account the children that it has.
+ *
+ * @param c The Container to lay out.
+ *
+ * @return The preferred size of the Container.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ if (isPalette)
+ return new Dimension(paletteTitleHeight, paletteTitleHeight);
+ else
+ return new Dimension(22, 22);
+ }
+
+ /**
+ * Removes a Component from the Container.
+ *
+ * @param c The Component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /** A flag indicating whether the title pane uses the palette style. */
+ protected boolean isPalette;
+
+ /**
+ * The icon used for the close button - this is fetched from the look and
+ * feel defaults using the key <code>InternalFrame.paletteCloseIcon</code>.
+ */
+ protected Icon paletteCloseIcon;
+
+ /**
+ * The height of the title pane when <code>isPalette</code> is
+ * <code>true</code>. This value is fetched from the look and feel defaults
+ * using the key <code>InternalFrame.paletteTitleHeight</code>.
+ */
+ protected int paletteTitleHeight;
+
+ /** The label used to display the title for the internal frame. */
+ JLabel title;
+
+ /**
+ * Creates a new title pane for the specified frame.
+ *
+ * @param f the internal frame.
+ */
+ public MetalInternalFrameTitlePane(JInternalFrame f)
+ {
+ super(f);
+ isPalette = false;
+ }
+
+ /**
+ * Fetches the colors used in the title pane.
+ */
+ protected void installDefaults()
+ {
+ super.installDefaults();
+ selectedTextColor = MetalLookAndFeel.getControlTextColor();
+ selectedTitleColor = MetalLookAndFeel.getWindowTitleBackground();
+ notSelectedTextColor = MetalLookAndFeel.getInactiveControlTextColor();
+ notSelectedTitleColor = MetalLookAndFeel.getWindowTitleInactiveBackground();
+
+ paletteTitleHeight = UIManager.getInt("InternalFrame.paletteTitleHeight");
+ paletteCloseIcon = UIManager.getIcon("InternalFrame.paletteCloseIcon");
+ minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16);
+
+ title = new JLabel(frame.getTitle(),
+ MetalIconFactory.getInternalFrameDefaultMenuIcon(),
+ SwingConstants.LEFT);
+ }
+
+ /**
+ * Clears the colors used for the title pane.
+ */
+ protected void uninstallDefaults()
+ {
+ super.uninstallDefaults();
+ selectedTextColor = null;
+ selectedTitleColor = null;
+ notSelectedTextColor = null;
+ notSelectedTitleColor = null;
+ paletteCloseIcon = null;
+ minIcon = null;
+ title = null;
+ }
+
+ /**
+ * Calls the super class to create the buttons, then calls
+ * <code>setBorderPainted(false)</code> and
+ * <code>setContentAreaFilled(false)</code> for each button.
+ */
+ protected void createButtons()
+ {
+ super.createButtons();
+ closeButton.setBorderPainted(false);
+ closeButton.setContentAreaFilled(false);
+ iconButton.setBorderPainted(false);
+ iconButton.setContentAreaFilled(false);
+ maxButton.setBorderPainted(false);
+ maxButton.setContentAreaFilled(false);
+ }
+
+ /**
+ * Overridden to do nothing.
+ */
+ protected void addSystemMenuItems(JMenu systemMenu)
+ {
+ // do nothing
+ }
+
+ /**
+ * Overridden to do nothing.
+ */
+ protected void showSystemMenu()
+ {
+ // do nothing
+ }
+
+ /**
+ * Adds the sub components of the title pane.
+ */
+ protected void addSubComponents()
+ {
+ // FIXME: this method is probably overridden to only add the required
+ // buttons
+ add(title);
+ add(closeButton);
+ add(iconButton);
+ add(maxButton);
+ }
+
+ /**
+ * Creates a new instance of <code>MetalTitlePaneLayout</code> (not part of
+ * the public API).
+ *
+ * @return A new instance of <code>MetalTitlePaneLayout</code>.
+ */
+ protected LayoutManager createLayout()
+ {
+ return new MetalTitlePaneLayout();
+ }
+
+ /**
+ * Draws the title pane in the palette style.
+ *
+ * @param g the graphics device.
+ *
+ * @see #paintComponent(Graphics)
+ */
+ public void paintPalette(Graphics g)
+ {
+ Color savedColor = g.getColor();
+ Rectangle b = SwingUtilities.getLocalBounds(this);
+
+ if (UIManager.get("InternalFrame.activeTitleGradient") != null
+ && frame.isSelected())
+ {
+ MetalUtils.paintGradient(g, b.x, b.y, b.width, b.height,
+ SwingConstants.VERTICAL,
+ "InternalFrame.activeTitleGradient");
+ }
+ MetalUtils.fillMetalPattern(this, g, b.x + 4, b.y + 2, b.width
+ - paletteCloseIcon.getIconWidth() - 13, b.height - 5,
+ MetalLookAndFeel.getPrimaryControlHighlight(),
+ MetalLookAndFeel.getBlack());
+
+ // draw a line separating the title pane from the frame content
+ Dimension d = getSize();
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ g.drawLine(0, d.height - 1, d.width - 1, d.height - 1);
+
+ g.setColor(savedColor);
+ }
+
+ /**
+ * Paints a representation of the current state of the internal frame.
+ *
+ * @param g the graphics device.
+ */
+ public void paintComponent(Graphics g)
+ {
+ Color savedColor = g.getColor();
+ if (isPalette)
+ paintPalette(g);
+ else
+ {
+ paintTitleBackground(g);
+ paintChildren(g);
+ Dimension d = getSize();
+ if (frame.isSelected())
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+
+ // put a dot in each of the top corners
+ g.drawLine(0, 0, 0, 0);
+ g.drawLine(d.width - 1, 0, d.width - 1, 0);
+
+ g.drawLine(0, d.height - 1, d.width - 1, d.height - 1);
+
+ // draw the metal pattern
+ if (UIManager.get("InternalFrame.activeTitleGradient") != null
+ && frame.isSelected())
+ {
+ MetalUtils.paintGradient(g, 0, 0, getWidth(), getHeight(),
+ SwingConstants.VERTICAL,
+ "InternalFrame.activeTitleGradient");
+ }
+
+ Rectangle b = title.getBounds();
+ int startX = b.x + b.width + 5;
+ int endX = startX;
+ if (iconButton.isVisible())
+ endX = Math.max(iconButton.getX(), endX);
+ else if (maxButton.isVisible())
+ endX = Math.max(maxButton.getX(), endX);
+ else if (closeButton.isVisible())
+ endX = Math.max(closeButton.getX(), endX);
+ endX -= 7;
+ if (endX > startX)
+ MetalUtils.fillMetalPattern(this, g, startX, 3, endX - startX,
+ getHeight() - 6, Color.white, Color.gray);
+ }
+ g.setColor(savedColor);
+ }
+
+ /**
+ * Sets the flag that controls whether the title pane is drawn in the
+ * palette style or the regular style.
+ *
+ * @param b the new value of the flag.
+ */
+ public void setPalette(boolean b)
+ {
+ isPalette = b;
+ title.setVisible(!isPalette);
+ iconButton.setVisible(!isPalette && frame.isIconifiable());
+ maxButton.setVisible(!isPalette && frame.isMaximizable());
+ if (isPalette)
+ closeButton.setIcon(paletteCloseIcon);
+ else
+ closeButton.setIcon(closeIcon);
+ }
+
+ /**
+ * Creates and returns a property change handler for the title pane.
+ *
+ * @return The property change handler.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new MetalInternalFrameTitlePanePropertyChangeHandler();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java
new file mode 100644
index 000000000..5eda2ff5e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java
@@ -0,0 +1,183 @@
+/* MetalInternalFrameUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.ActionMap;
+import javax.swing.JComponent;
+import javax.swing.JInternalFrame;
+import javax.swing.SwingUtilities;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicInternalFrameUI;
+
+/**
+ * A UI delegate for the {@link JInternalFrame} component.
+ */
+public class MetalInternalFrameUI
+ extends BasicInternalFrameUI
+{
+ /**
+ * The key (<code>JInternalFrame.isPalette</code>) for the client property
+ * that controls whether the internal frame is displayed using the palette
+ * style.
+ */
+ protected static String IS_PALETTE = "JInternalFrame.isPalette";
+
+ /**
+ * Constructs a new instance of <code>MetalInternalFrameUI</code>.
+ *
+ * @param frame the frame.
+ */
+ public MetalInternalFrameUI(JInternalFrame frame)
+ {
+ super(frame);
+ }
+
+ /**
+ * Returns an instance of <code>MetalInternalFrameUI</code>.
+ *
+ * @param component the internal frame.
+ *
+ * @return an instance of <code>MetalInternalFrameUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalInternalFrameUI((JInternalFrame) component);
+ }
+
+ /**
+ * Sets the fields and properties for the component.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ JInternalFrame f = (JInternalFrame) c;
+ boolean isPalette = false;
+ Boolean p = (Boolean) f.getClientProperty(IS_PALETTE);
+ if (p != null)
+ isPalette = p.booleanValue();
+ setPalette(isPalette);
+ }
+
+ /**
+ * Creates and returns the component that will be used for the north pane
+ * of the {@link JInternalFrame}.
+ *
+ * @param w the internal frame.
+ *
+ * @return A new instance of {@link MetalInternalFrameTitlePane}.
+ */
+ protected JComponent createNorthPane(JInternalFrame w)
+ {
+ titlePane = new MetalInternalFrameTitlePane(w);
+ return titlePane;
+ }
+
+ /**
+ * Sets the state of the {@link JInternalFrame} to reflect whether or not
+ * it is using the palette style. When a frame is displayed as a palette,
+ * it uses a different border and the title pane is drawn differently.
+ *
+ * @param isPalette use the palette style?
+ */
+ public void setPalette(boolean isPalette)
+ {
+ MetalInternalFrameTitlePane title = (MetalInternalFrameTitlePane) northPane;
+ title.setPalette(isPalette);
+ if (isPalette)
+ frame.setBorder(new MetalBorders.PaletteBorder());
+ else
+ frame.setBorder(new MetalBorders.InternalFrameBorder());
+ }
+
+ /** A listener that is used to handle IS_PALETTE property changes. */
+ private PropertyChangeListener paletteListener;
+
+ /**
+ * Adds the required listeners.
+ */
+ protected void installListeners()
+ {
+ super.installListeners();
+ paletteListener = new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(IS_PALETTE))
+ {
+ if (Boolean.TRUE.equals(e.getNewValue()))
+ setPalette(true);
+ else
+ setPalette(false);
+ }
+ }
+ };
+ frame.addPropertyChangeListener(paletteListener);
+ }
+
+ /**
+ * Removes the listeners used.
+ */
+ protected void uninstallListeners()
+ {
+ super.uninstallListeners();
+ frame.removePropertyChangeListener(IS_PALETTE, paletteListener);
+ paletteListener = null;
+ }
+
+ /**
+ * Installs keyboard actions. This is overridden to remove the
+ * <code>showSystemMenu</code> Action that is installed by the
+ * <code>BasicInternalFrameUI</code>, since Metal JInternalFrames don't have
+ * a system menu.
+ */
+ protected void installKeyboardActions()
+ {
+ super.installKeyboardActions();
+ ActionMap am = SwingUtilities.getUIActionMap(frame);
+ if (am != null)
+ {
+ am.remove("showSystemMenu");
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java
new file mode 100644
index 000000000..915e5016d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java
@@ -0,0 +1,108 @@
+/* MetalLabelUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Graphics;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicGraphicsUtils;
+import javax.swing.plaf.basic.BasicLabelUI;
+
+/**
+ * A UI delegate for the {@link JLabel} component.
+ */
+public class MetalLabelUI
+ extends BasicLabelUI
+{
+
+ /** The shared instance of the UI delegate. */
+ protected static MetalLabelUI metalLabelUI;
+
+ /**
+ * Constructs a new instance of <code>MetalLabelUI</code>.
+ */
+ public MetalLabelUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a shared instance of <code>MetalLabelUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A shared instance of <code>MetalLabelUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ if (metalLabelUI == null)
+ metalLabelUI = new MetalLabelUI();
+ return metalLabelUI;
+ }
+
+ /**
+ * Draws the text for a disabled label, using the color defined in the
+ * {@link UIManager} defaults with the key
+ * <code>Label.disabledForeground</code>.
+ *
+ * @param l the label.
+ * @param g the graphics device.
+ * @param s the label text.
+ * @param textX the x-coordinate for the label.
+ * @param textY the y-coordinate for the label.
+ */
+ protected void paintDisabledText(JLabel l, Graphics g, String s, int textX,
+ int textY)
+ {
+ Color savedColor = g.getColor();
+ g.setColor(UIManager.getColor("Label.disabledForeground"));
+ int mnemIndex = l.getDisplayedMnemonicIndex();
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX,
+ textY);
+ else
+ g.drawString(s, textX, textY);
+
+ g.setColor(savedColor);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java
new file mode 100644
index 000000000..acc3fedde
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java
@@ -0,0 +1,1372 @@
+/* MetalLookAndFeel.java
+ Copyright (C) 2002, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import javax.swing.LookAndFeel;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.plaf.BorderUIResource;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.FontUIResource;
+import javax.swing.plaf.InsetsUIResource;
+import javax.swing.plaf.BorderUIResource.LineBorderUIResource;
+import javax.swing.plaf.basic.BasicLookAndFeel;
+
+
+/**
+ * A custom look and feel that is designed to look similar across different
+ * operating systems. To install this look and feel, add the following code
+ * (or something similar) near the start of your application:</p>
+ * <pre>
+ * try
+ * {
+ * &nbsp;&nbsp;UIManager.setLookAndFeel(new MetalLookAndFeel());
+ * }
+ * catch (UnsupportedLookAndFeelException e)
+ * {
+ * &nbsp;&nbsp;e.printStackTrace();
+ * }</pre>
+ */
+public class MetalLookAndFeel extends BasicLookAndFeel
+{
+ private static final long serialVersionUID = 6680646159193457980L;
+
+ /** The current theme. */
+ private static MetalTheme theme;
+
+ /**
+ * Creates a new instance of the Metal look and feel.
+ */
+ public MetalLookAndFeel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the current theme to a new instance of {@link DefaultMetalTheme}.
+ */
+ protected void createDefaultTheme()
+ {
+ getCurrentTheme();
+ }
+
+ /**
+ * Returns <code>false</code> to indicate that this look and feel does not
+ * attempt to emulate the look and feel of native applications on the host
+ * platform.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isNativeLookAndFeel()
+ {
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> to indicate that this look and feel is supported
+ * on all platforms.
+ *
+ * @return <code>true</code>.
+ */
+ public boolean isSupportedLookAndFeel()
+ {
+ return true;
+ }
+
+ /**
+ * Returns a string describing the look and feel. In this case, the method
+ * returns "Metal look and feel".
+ *
+ * @return A string describing the look and feel.
+ */
+ public String getDescription()
+ {
+ return "The Java(tm) Look and Feel";
+ }
+
+ /**
+ * Returns the look and feel identifier.
+ *
+ * @return "MetalLookAndFeel".
+ */
+ public String getID()
+ {
+ return "Metal";
+ }
+
+ /**
+ * Returns the look and feel name.
+ *
+ * @return "MetalLookAndFeel".
+ */
+ public String getName()
+ {
+ return "Metal";
+ }
+
+ public UIDefaults getDefaults()
+ {
+ createDefaultTheme();
+ UIDefaults def = super.getDefaults();
+
+ theme.addCustomEntriesToTable(def);
+ return def;
+ }
+
+ /**
+ * Returns the accelerator foreground color from the installed theme.
+ *
+ * @return The accelerator foreground color.
+ */
+ public static ColorUIResource getAcceleratorForeground()
+ {
+ if (theme != null)
+ return theme.getAcceleratorForeground();
+ return null;
+ }
+
+ /**
+ * Returns the accelerator selected foreground color from the installed
+ * theme.
+ *
+ * @return The accelerator selected foreground color.
+ */
+ public static ColorUIResource getAcceleratorSelectedForeground()
+ {
+ if (theme != null)
+ return theme.getAcceleratorSelectedForeground();
+ return null;
+ }
+
+ /**
+ * Returns the color black from the installed theme.
+ *
+ * @return The color black.
+ */
+ public static ColorUIResource getBlack()
+ {
+ if (theme != null)
+ return theme.getBlack();
+ return null;
+ }
+
+ /**
+ * Returns the control color from the installed theme.
+ *
+ * @return The control color.
+ */
+ public static ColorUIResource getControl()
+ {
+ if (theme != null)
+ return theme.getControl();
+ return null;
+ }
+
+ /**
+ * Returns the color used for dark shadows on controls, from the installed
+ * theme.
+ *
+ * @return The color used for dark shadows on controls.
+ */
+ public static ColorUIResource getControlDarkShadow()
+ {
+ if (theme != null)
+ return theme.getControlDarkShadow();
+ return null;
+ }
+
+ /**
+ * Returns the color used for disabled controls, from the installed theme.
+ *
+ * @return The color used for disabled controls.
+ */
+ public static ColorUIResource getControlDisabled()
+ {
+ if (theme != null)
+ return theme.getControlDisabled();
+ return null;
+ }
+
+ /**
+ * Returns the color used to draw highlights for controls, from the installed
+ * theme.
+ *
+ * @return The color used to draw highlights for controls.
+ */
+ public static ColorUIResource getControlHighlight()
+ {
+ if (theme != null)
+ return theme.getControlHighlight();
+ return null;
+ }
+
+ /**
+ * Returns the color used to display control info, from the installed
+ * theme.
+ *
+ * @return The color used to display control info.
+ */
+ public static ColorUIResource getControlInfo()
+ {
+ if (theme != null)
+ return theme.getControlInfo();
+ return null;
+ }
+
+ /**
+ * Returns the color used to draw shadows for controls, from the installed
+ * theme.
+ *
+ * @return The color used to draw shadows for controls.
+ */
+ public static ColorUIResource getControlShadow()
+ {
+ if (theme != null)
+ return theme.getControlShadow();
+ return null;
+ }
+
+ /**
+ * Returns the color used for text on controls, from the installed theme.
+ *
+ * @return The color used for text on controls.
+ */
+ public static ColorUIResource getControlTextColor()
+ {
+ if (theme != null)
+ return theme.getControlTextColor();
+ return null;
+ }
+
+ /**
+ * Returns the font used for text on controls, from the installed theme.
+ *
+ * @return The font used for text on controls.
+ */
+ public static FontUIResource getControlTextFont()
+ {
+ if (theme != null)
+ return theme.getControlTextFont();
+ return null;
+ }
+
+ /**
+ * Returns the color used for the desktop background, from the installed
+ * theme.
+ *
+ * @return The color used for the desktop background.
+ */
+ public static ColorUIResource getDesktopColor()
+ {
+ if (theme != null)
+ return theme.getDesktopColor();
+ return null;
+ }
+
+ /**
+ * Returns the color used to draw focus highlights, from the installed
+ * theme.
+ *
+ * @return The color used to draw focus highlights.
+ */
+ public static ColorUIResource getFocusColor()
+ {
+ if (theme != null)
+ return theme.getFocusColor();
+ return null;
+ }
+
+ /**
+ * Returns the color used to draw highlighted text, from the installed
+ * theme.
+ *
+ * @return The color used to draw highlighted text.
+ */
+ public static ColorUIResource getHighlightedTextColor()
+ {
+ if (theme != null)
+ return theme.getHighlightedTextColor();
+ return null;
+ }
+
+ /**
+ * Returns the color used to draw text on inactive controls, from the
+ * installed theme.
+ *
+ * @return The color used to draw text on inactive controls.
+ */
+ public static ColorUIResource getInactiveControlTextColor()
+ {
+ if (theme != null)
+ return theme.getInactiveControlTextColor();
+ return null;
+ }
+
+ /**
+ * Returns the color used to draw inactive system text, from the installed
+ * theme.
+ *
+ * @return The color used to draw inactive system text.
+ */
+ public static ColorUIResource getInactiveSystemTextColor()
+ {
+ if (theme != null)
+ return theme.getInactiveSystemTextColor();
+ return null;
+ }
+
+ /**
+ * Returns the background color for menu items, from the installed theme.
+ *
+ * @return The background color for menu items.
+ *
+ * @see #getMenuSelectedBackground()
+ */
+ public static ColorUIResource getMenuBackground()
+ {
+ if (theme != null)
+ return theme.getMenuBackground();
+ return null;
+ }
+
+ /**
+ * Returns the foreground color for disabled menu items, from the installed
+ * theme.
+ *
+ * @return The foreground color for disabled menu items.
+ *
+ * @see #getMenuForeground()
+ */
+ public static ColorUIResource getMenuDisabledForeground()
+ {
+ if (theme != null)
+ return theme.getMenuDisabledForeground();
+ return null;
+ }
+
+ /**
+ * Returns the foreground color for menu items, from the installed theme.
+ *
+ * @return The foreground color for menu items.
+ *
+ * @see #getMenuDisabledForeground()
+ * @see #getMenuSelectedForeground()
+ */
+ public static ColorUIResource getMenuForeground()
+ {
+ if (theme != null)
+ return theme.getMenuForeground();
+ return null;
+ }
+
+ /**
+ * Returns the background color for selected menu items, from the installed
+ * theme.
+ *
+ * @return The background color for selected menu items.
+ *
+ * @see #getMenuBackground()
+ */
+ public static ColorUIResource getMenuSelectedBackground()
+ {
+ if (theme != null)
+ return theme.getMenuSelectedBackground();
+ return null;
+ }
+
+ /**
+ * Returns the foreground color for selected menu items, from the installed
+ * theme.
+ *
+ * @return The foreground color for selected menu items.
+ *
+ * @see #getMenuForeground()
+ */
+ public static ColorUIResource getMenuSelectedForeground()
+ {
+ if (theme != null)
+ return theme.getMenuSelectedForeground();
+ return null;
+ }
+
+ /**
+ * Returns the font used for text in menus, from the installed theme.
+ *
+ * @return The font used for text in menus.
+ */
+ public static FontUIResource getMenuTextFont()
+ {
+ if (theme != null)
+ return theme.getMenuTextFont();
+ return null;
+ }
+
+ /**
+ * Returns the primary color for controls, from the installed theme.
+ *
+ * @return The primary color for controls.
+ */
+ public static ColorUIResource getPrimaryControl()
+ {
+ if (theme != null)
+ return theme.getPrimaryControl();
+ return null;
+ }
+
+ /**
+ * Returns the primary color for the dark shadow on controls, from the
+ * installed theme.
+ *
+ * @return The primary color for the dark shadow on controls.
+ */
+ public static ColorUIResource getPrimaryControlDarkShadow()
+ {
+ if (theme != null)
+ return theme.getPrimaryControlDarkShadow();
+ return null;
+ }
+
+ /**
+ * Returns the primary color for the highlight on controls, from the
+ * installed theme.
+ *
+ * @return The primary color for the highlight on controls.
+ */
+ public static ColorUIResource getPrimaryControlHighlight()
+ {
+ if (theme != null)
+ return theme.getPrimaryControlHighlight();
+ return null;
+ }
+
+ /**
+ * Returns the primary color for the information on controls, from the
+ * installed theme.
+ *
+ * @return The primary color for the information on controls.
+ */
+ public static ColorUIResource getPrimaryControlInfo()
+ {
+ if (theme != null)
+ return theme.getPrimaryControlInfo();
+ return null;
+ }
+
+ /**
+ * Returns the primary color for the shadow on controls, from the installed
+ * theme.
+ *
+ * @return The primary color for the shadow on controls.
+ */
+ public static ColorUIResource getPrimaryControlShadow()
+ {
+ if (theme != null)
+ return theme.getPrimaryControlShadow();
+ return null;
+ }
+
+ /**
+ * Returns the background color for separators, from the installed theme.
+ *
+ * @return The background color for separators.
+ */
+ public static ColorUIResource getSeparatorBackground()
+ {
+ if (theme != null)
+ return theme.getSeparatorBackground();
+ return null;
+ }
+
+ /**
+ * Returns the foreground color for separators, from the installed theme.
+ *
+ * @return The foreground color for separators.
+ */
+ public static ColorUIResource getSeparatorForeground()
+ {
+ if (theme != null)
+ return theme.getSeparatorForeground();
+ return null;
+ }
+
+ /**
+ * Returns the font used for sub text, from the installed theme.
+ *
+ * @return The font used for sub text.
+ */
+ public static FontUIResource getSubTextFont()
+ {
+ if (theme != null)
+ return theme.getSubTextFont();
+ return null;
+ }
+
+ /**
+ * Returns the color used for system text, from the installed theme.
+ *
+ * @return The color used for system text.
+ */
+ public static ColorUIResource getSystemTextColor()
+ {
+ if (theme != null)
+ return theme.getSystemTextColor();
+ return null;
+ }
+
+ /**
+ * Returns the font used for system text, from the installed theme.
+ *
+ * @return The font used for system text.
+ */
+ public static FontUIResource getSystemTextFont()
+ {
+ if (theme != null)
+ return theme.getSystemTextFont();
+ return null;
+ }
+
+ /**
+ * Returns the color used to highlight text, from the installed theme.
+ *
+ * @return The color used to highlight text.
+ */
+ public static ColorUIResource getTextHighlightColor()
+ {
+ if (theme != null)
+ return theme.getTextHighlightColor();
+ return null;
+ }
+
+ /**
+ * Returns the color used to display user text, from the installed theme.
+ *
+ * @return The color used to display user text.
+ */
+ public static ColorUIResource getUserTextColor()
+ {
+ if (theme != null)
+ return theme.getUserTextColor();
+ return null;
+ }
+
+ /**
+ * Returns the font used for user text, obtained from the current theme.
+ *
+ * @return The font used for user text.
+ */
+ public static FontUIResource getUserTextFont()
+ {
+ if (theme != null)
+ return theme.getUserTextFont();
+ return null;
+ }
+
+ /**
+ * Returns the color used for white, from the installed theme.
+ *
+ * @return The color used for white.
+ */
+ public static ColorUIResource getWhite()
+ {
+ if (theme != null)
+ return theme.getWhite();
+ return null;
+ }
+
+ /**
+ * Returns the window background color, from the installed theme.
+ *
+ * @return The window background color.
+ */
+ public static ColorUIResource getWindowBackground()
+ {
+ if (theme != null)
+ return theme.getWindowBackground();
+ return null;
+ }
+
+ /**
+ * Returns the window title background color, from the installed theme.
+ *
+ * @return The window title background color.
+ */
+ public static ColorUIResource getWindowTitleBackground()
+ {
+ if (theme != null)
+ return theme.getWindowTitleBackground();
+ return null;
+ }
+
+ /**
+ * Returns the window title font from the current theme.
+ *
+ * @return The window title font.
+ *
+ * @see MetalTheme
+ */
+ public static FontUIResource getWindowTitleFont()
+ {
+ if (theme != null)
+ return theme.getWindowTitleFont();
+ return null;
+ }
+
+ /**
+ * Returns the window title foreground color, from the installed theme.
+ *
+ * @return The window title foreground color.
+ */
+ public static ColorUIResource getWindowTitleForeground()
+ {
+ if (theme != null)
+ return theme.getWindowTitleForeground();
+ return null;
+ }
+
+ /**
+ * Returns the background color for an inactive window title, from the
+ * installed theme.
+ *
+ * @return The background color for an inactive window title.
+ */
+ public static ColorUIResource getWindowTitleInactiveBackground()
+ {
+ if (theme != null)
+ return theme.getWindowTitleInactiveBackground();
+ return null;
+ }
+
+ /**
+ * Returns the foreground color for an inactive window title, from the
+ * installed theme.
+ *
+ * @return The foreground color for an inactive window title.
+ */
+ public static ColorUIResource getWindowTitleInactiveForeground()
+ {
+ if (theme != null)
+ return theme.getWindowTitleInactiveForeground();
+ return null;
+ }
+
+ /**
+ * Sets the current theme for the look and feel. Note that the theme must be
+ * set <em>before</em> the look and feel is installed. To change the theme
+ * for an already running application that is using the
+ * {@link MetalLookAndFeel}, first set the theme with this method, then
+ * create a new instance of {@link MetalLookAndFeel} and install it in the
+ * usual way (see {@link UIManager#setLookAndFeel(LookAndFeel)}).
+ *
+ * @param theme the theme (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>theme</code> is <code>null</code>.
+ *
+ * @see #getCurrentTheme()
+ */
+ public static void setCurrentTheme(MetalTheme theme)
+ {
+ if (theme == null)
+ throw new NullPointerException("Null 'theme' not permitted.");
+ MetalLookAndFeel.theme = theme;
+ }
+
+ /**
+ * Sets the ComponentUI classes for all Swing components to the Metal
+ * implementations.
+ *
+ * In particular this sets the following keys:
+ *
+ * <table>
+ * <tr>
+ * <th>Key</th><th>Value</th>
+ * </tr><tr>
+ * <td>ButtonUI</td><td>{@link MetalButtonUI}</td>
+ * </tr><tr>
+ * <td>CheckBoxUI</td><td>{@link MetalCheckBoxUI}</td>
+ * </tr><tr>
+ * <td>ComboBoxUI</td><td>{@link MetalComboBoxUI}</td>
+ * </tr><tr>
+ * <td>DesktopIconUI</td><td>{@link MetalDesktopIconUI}</td>
+ * </tr><tr>
+ * <td>InternalFrameUI</td><td>{@link MetalInternalFrameUI}</td>
+ * </tr><tr>
+ * <td>LabelUI</td><td>{@link MetalLabelUI}</td>
+ * </tr><tr>
+ * <td>PopupMenuSeparatorUI</td><td>{@link MetalPopupMenuSeparatorUI}</td>
+ * </tr><tr>
+ * <td>ProgressBarUI</td><td>{@link MetalProgressBarUI}</td>
+ * </tr><tr>
+ * <td>RadioButtonUI</td><td>{@link MetalRadioButtonUI}</td>
+ * </tr><tr>
+ * <td>RootPaneUI</td><td>{@link MetalRootPaneUI}</td>
+ * </tr><tr>
+ * <td>ScrollBarUI</td><td>{@link MetalScrollBarUI}</td>
+ * </tr><tr>
+ * <td>ScrollPaneUI</td><td>{@link MetalScrollPaneUI}</td>
+ * </tr><tr>
+ * <td>SeparatorUI</td><td>{@link MetalSeparatorUI}</td>
+ * </tr><tr>
+ * <td>SliderUI</td><td>{@link MetalSliderUI}</td>
+ * </tr><tr>
+ * <td>SplitPaneUI</td><td>{@link MetalSplitPaneUI}</td>
+ * </tr><tr>
+ * <td>TabbedPaneUI</td><td>{@link MetalTabbedPaneUI}</td>
+ * </tr><tr>
+ * <td>TextFieldUI</td><td>{@link MetalTextFieldUI}</td>
+ * </tr><tr>
+ * <td>ToggleButtonUI</td><td>{@link MetalToggleButtonUI}</td>
+ * </tr><tr>
+ * <td>ToolBarUI</td><td>{@link MetalToolBarUI}</td>
+ * </tr><tr>
+ * <td>ToolTipUI</td><td>{@link MetalToolTipUI}</td>
+ * </tr><tr>
+ * <td>TreeUI</td><td>{@link MetalTreeUI}</td>
+ * </tr><tr>
+ * </table>
+ *
+ * @param defaults the UIDefaults where the class defaults are added
+ */
+ protected void initClassDefaults(UIDefaults defaults)
+ {
+ super.initClassDefaults(defaults);
+
+ // Variables
+ Object[] uiDefaults;
+ // Initialize Class Defaults
+ uiDefaults = new Object[] {
+ "ButtonUI", "javax.swing.plaf.metal.MetalButtonUI",
+ "CheckBoxUI", "javax.swing.plaf.metal.MetalCheckBoxUI",
+ "ComboBoxUI", "javax.swing.plaf.metal.MetalComboBoxUI",
+ "DesktopIconUI", "javax.swing.plaf.metal.MetalDesktopIconUI",
+ "FileChooserUI", "javax.swing.plaf.metal.MetalFileChooserUI",
+ "InternalFrameUI", "javax.swing.plaf.metal.MetalInternalFrameUI",
+ "LabelUI", "javax.swing.plaf.metal.MetalLabelUI",
+ "MenuBarUI", "javax.swing.plaf.metal.MetalMenuBarUI",
+ "PopupMenuSeparatorUI",
+ "javax.swing.plaf.metal.MetalPopupMenuSeparatorUI",
+ "ProgressBarUI", "javax.swing.plaf.metal.MetalProgressBarUI",
+ "RadioButtonUI", "javax.swing.plaf.metal.MetalRadioButtonUI",
+ "RootPaneUI", "javax.swing.plaf.metal.MetalRootPaneUI",
+ "ScrollBarUI", "javax.swing.plaf.metal.MetalScrollBarUI",
+ "ScrollPaneUI", "javax.swing.plaf.metal.MetalScrollPaneUI",
+ "SeparatorUI", "javax.swing.plaf.metal.MetalSeparatorUI",
+ "SliderUI", "javax.swing.plaf.metal.MetalSliderUI",
+ "SplitPaneUI", "javax.swing.plaf.metal.MetalSplitPaneUI",
+ "TabbedPaneUI", "javax.swing.plaf.metal.MetalTabbedPaneUI",
+ "TextFieldUI", "javax.swing.plaf.metal.MetalTextFieldUI",
+ "ToggleButtonUI", "javax.swing.plaf.metal.MetalToggleButtonUI",
+ "ToolBarUI", "javax.swing.plaf.metal.MetalToolBarUI",
+ "ToolTipUI", "javax.swing.plaf.metal.MetalToolTipUI",
+ "TreeUI", "javax.swing.plaf.metal.MetalTreeUI",
+ };
+ // Add Class Defaults to UI Defaults table
+ defaults.putDefaults(uiDefaults);
+ }
+
+ /**
+ * Initializes the component defaults for the Metal Look &amp; Feel.
+ *
+ * In particular this sets the following keys (the colors are given
+ * as RGB hex values):
+ *
+ * <table>
+ * <tr>
+ * <th>Key</th><th>Value</th>
+ * </tr><tr>
+ * <td>Button.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>Button.border</td><td>{@link MetalBorders#getButtonBorder()}</td>
+ * </tr><tr>
+ * <td>Button.font</td><td>{@link #getControlTextFont}</td>
+ * </tr><tr>
+ * <td>Button.margin</td><td><code>new java.awt.Insets(2, 14, 2, 14)</code>
+ * </td>
+ * </tr><tr>
+ * <td>CheckBox.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>CheckBoxMenuItem.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>ToolBar.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>Panel.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>Slider.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>OptionPane.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>ProgressBar.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>TabbedPane.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>Label.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>Label.font</td><td>{@link #getControlTextFont}</td>
+ * </tr><tr>
+ * <td>Menu.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>MenuBar.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>MenuItem.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>ScrollBar.background</td><td>0xcccccc</td>
+ * </tr><tr>
+ * <td>PopupMenu.border</td>
+ * <td><code>new javax.swing.plaf.metal.MetalBorders.PopupMenuBorder()</td>
+ * </tr><tr>
+ * </table>
+ *
+ * @param defaults the UIDefaults instance to which the values are added
+ */
+ protected void initComponentDefaults(UIDefaults defaults)
+ {
+ super.initComponentDefaults(defaults);
+ Object[] myDefaults = new Object[] {
+ "Button.background", getControl(),
+ "Button.border", MetalBorders.getButtonBorder(),
+ "Button.darkShadow", getControlDarkShadow(),
+ "Button.disabledText", getInactiveControlTextColor(),
+ "Button.focus", getFocusColor(),
+ "Button.font", getControlTextFont(),
+ "Button.foreground", getControlTextColor(),
+ "Button.highlight", getControlHighlight(),
+ "Button.light", getControlHighlight(),
+ "Button.margin", new InsetsUIResource(2, 14, 2, 14),
+ "Button.select", getControlShadow(),
+ "Button.shadow", getControlShadow(),
+
+ "CheckBox.background", getControl(),
+ "CheckBox.border", MetalBorders.getButtonBorder(),
+ "CheckBox.disabledText", getInactiveControlTextColor(),
+ "CheckBox.focus", getFocusColor(),
+ "CheckBox.font", getControlTextFont(),
+ "CheckBox.foreground", getControlTextColor(),
+ "CheckBox.icon",
+ new UIDefaults.ProxyLazyValue("javax.swing.plaf.metal.MetalCheckBoxIcon"),
+ "CheckBox.checkIcon",
+ new UIDefaults.ProxyLazyValue("javax.swing.plaf.metal.MetalCheckBoxIcon"),
+ "Checkbox.select", getControlShadow(),
+
+ "CheckBoxMenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10),
+ "CheckBoxMenuItem.acceleratorForeground", getAcceleratorForeground(),
+ "CheckBoxMenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(),
+ "CheckBoxMenuItem.background", getMenuBackground(),
+ "CheckBoxMenuItem.borderPainted", Boolean.TRUE,
+ "CheckBoxMenuItem.commandSound", "sounds/MenuItemCommand.wav",
+ "CheckBoxMenuItem.checkIcon", MetalIconFactory.getCheckBoxMenuItemIcon(),
+ "CheckBoxMenuItem.disabledForeground", getMenuDisabledForeground(),
+ "CheckBoxMenuItem.font", getMenuTextFont(),
+ "CheckBoxMenuItem.foreground", getMenuForeground(),
+ "CheckBoxMenuItem.selectionBackground", getMenuSelectedBackground(),
+ "CheckBoxMenuItem.selectionForeground", getMenuSelectedForeground(),
+
+ "ColorChooser.background", getControl(),
+ "ColorChooser.foreground", getControlTextColor(),
+ "ColorChooser.rgbBlueMnemonic", new Integer(0),
+ "ColorChooser.rgbGreenMnemonic", new Integer(0),
+ "ColorChooser.rgbRedMnemonic", new Integer(0),
+ "ColorChooser.swatchesDefaultRecentColor", getControl(),
+
+ "ComboBox.background", getControl(),
+ "ComboBox.buttonBackground", getControl(),
+ "ComboBox.buttonDarkShadow", getControlDarkShadow(),
+ "ComboBox.buttonHighlight", getControlHighlight(),
+ "ComboBox.buttonShadow", getControlShadow(),
+ "ComboBox.disabledBackground", getControl(),
+ "ComboBox.disabledForeground", getInactiveSystemTextColor(),
+ "ComboBox.font", getControlTextFont(),
+ "ComboBox.foreground", getControlTextColor(),
+ "ComboBox.selectionBackground", getPrimaryControlShadow(),
+ "ComboBox.selectionForeground", getControlTextColor(),
+
+ "Desktop.background", getDesktopColor(),
+
+ "DesktopIcon.background", getControl(),
+ "DesktopIcon.foreground", getControlTextColor(),
+ "DesktopIcon.width", new Integer(160),
+ "DesktopIcon.border", MetalBorders.getDesktopIconBorder(),
+ "DesktopIcon.font", getControlTextFont(),
+
+ "EditorPane.background", getWindowBackground(),
+ "EditorPane.caretForeground", getUserTextColor(),
+ "EditorPane.font", getControlTextFont(),
+ "EditorPane.foreground", getUserTextColor(),
+ "EditorPane.inactiveForeground", getInactiveSystemTextColor(),
+ "EditorPane.selectionBackground", getTextHighlightColor(),
+ "EditorPane.selectionForeground", getHighlightedTextColor(),
+
+ "FormattedTextField.background", getWindowBackground(),
+ "FormattedTextField.border",
+ new BorderUIResource(MetalBorders.getTextFieldBorder()),
+ "FormattedTextField.caretForeground", getUserTextColor(),
+ "FormattedTextField.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "FormattedTextField.foreground", getUserTextColor(),
+ "FormattedTextField.inactiveBackground", getControl(),
+ "FormattedTextField.inactiveForeground", getInactiveSystemTextColor(),
+ "FormattedTextField.selectionBackground", getTextHighlightColor(),
+ "FormattedTextField.selectionForeground", getHighlightedTextColor(),
+
+ "FileChooser.upFolderIcon",
+ MetalIconFactory.getFileChooserUpFolderIcon(),
+ "FileChooser.listViewIcon",
+ MetalIconFactory.getFileChooserListViewIcon(),
+ "FileChooser.newFolderIcon",
+ MetalIconFactory.getFileChooserNewFolderIcon(),
+ "FileChooser.homeFolderIcon",
+ MetalIconFactory.getFileChooserHomeFolderIcon(),
+ "FileChooser.detailsViewIcon",
+ MetalIconFactory.getFileChooserDetailViewIcon(),
+ "FileChooser.fileNameLabelMnemonic", new Integer(78),
+ "FileChooser.filesOfTypeLabelMnemonic", new Integer(84),
+ "FileChooser.lookInLabelMnemonic", new Integer(73),
+ "FileView.computerIcon", MetalIconFactory.getTreeComputerIcon(),
+ "FileView.directoryIcon", MetalIconFactory.getTreeFolderIcon(),
+ "FileView.fileIcon", MetalIconFactory.getTreeLeafIcon(),
+ "FileView.floppyDriveIcon", MetalIconFactory.getTreeFloppyDriveIcon(),
+ "FileView.hardDriveIcon", MetalIconFactory.getTreeHardDriveIcon(),
+
+ "InternalFrame.activeTitleBackground", getWindowTitleBackground(),
+ "InternalFrame.activeTitleForeground", getWindowTitleForeground(),
+ "InternalFrame.border", new MetalBorders.InternalFrameBorder(),
+ "InternalFrame.borderColor", getControl(),
+ "InternalFrame.borderDarkShadow", getControlDarkShadow(),
+ "InternalFrame.borderHighlight", getControlHighlight(),
+ "InternalFrame.borderLight", getControlHighlight(),
+ "InternalFrame.borderShadow", getControlShadow(),
+ "InternalFrame.icon", MetalIconFactory.getInternalFrameDefaultMenuIcon(),
+ "InternalFrame.closeIcon",
+ MetalIconFactory.getInternalFrameCloseIcon(16),
+ "InternalFrame.closeSound", "sounds/FrameClose.wav",
+ "InternalFrame.inactiveTitleBackground", getWindowTitleInactiveBackground(),
+ "InternalFrame.inactiveTitleForeground", getWindowTitleInactiveForeground(),
+ "InternalFrame.maximizeIcon",
+ MetalIconFactory.getInternalFrameMaximizeIcon(16),
+ "InternalFrame.maximizeSound", "sounds/FrameMaximize.wav",
+ "InternalFrame.iconifyIcon",
+ MetalIconFactory.getInternalFrameMinimizeIcon(16),
+ "InternalFrame.minimizeSound", "sounds/FrameMinimize.wav",
+ "InternalFrame.paletteBorder", new MetalBorders.PaletteBorder(),
+ "InternalFrame.paletteCloseIcon", new MetalIconFactory.PaletteCloseIcon(),
+ "InternalFrame.paletteTitleHeight", new Integer(11),
+ "InternalFrame.restoreDownSound", "sounds/FrameRestoreDown.wav",
+ "InternalFrame.restoreUpSound", "sounds/FrameRestoreUp.wav",
+
+ "Label.background", getControl(),
+ "Label.disabledForeground", getInactiveSystemTextColor(),
+ "Label.disabledShadow", getControlShadow(),
+ "Label.font", getControlTextFont(),
+ "Label.foreground", getSystemTextColor(),
+
+ "List.font", getControlTextFont(),
+ "List.background", getWindowBackground(),
+ "List.foreground", getUserTextColor(),
+ "List.selectionBackground", getTextHighlightColor(),
+ "List.selectionForeground", getHighlightedTextColor(),
+ "List.focusCellHighlightBorder",
+ new LineBorderUIResource(MetalLookAndFeel.getFocusColor()),
+
+ "Menu.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10),
+ "Menu.acceleratorForeground", getAcceleratorForeground(),
+ "Menu.acceleratorSelectionForeground", getAcceleratorSelectedForeground(),
+ "Menu.arrowIcon", MetalIconFactory.getMenuArrowIcon(),
+ "Menu.background", getMenuBackground(),
+ "Menu.border", new MetalBorders.MenuItemBorder(),
+ "Menu.borderPainted", Boolean.TRUE,
+ "MenuItem.commandSound", "sounds/MenuItemCommand.wav",
+ "Menu.disabledForeground", getMenuDisabledForeground(),
+ "Menu.font", getMenuTextFont(),
+ "Menu.foreground", getMenuForeground(),
+ "Menu.selectionBackground", getMenuSelectedBackground(),
+ "Menu.selectionForeground", getMenuSelectedForeground(),
+ "Menu.submenuPopupOffsetX", new Integer(-4),
+ "Menu.submenuPopupOffsetY", new Integer(-3),
+
+ "MenuBar.background", getMenuBackground(),
+ "MenuBar.border", new MetalBorders.MenuBarBorder(),
+ "MenuBar.font", getMenuTextFont(),
+ "MenuBar.foreground", getMenuForeground(),
+ "MenuBar.highlight", getControlHighlight(),
+ "MenuBar.shadow", getControlShadow(),
+
+ "MenuItem.acceleratorDelimiter", "-",
+ "MenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10),
+ "MenuItem.acceleratorForeground", getAcceleratorForeground(),
+ "MenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(),
+ "MenuItem.arrowIcon", MetalIconFactory.getMenuItemArrowIcon(),
+ "MenuItem.background", getMenuBackground(),
+ "MenuItem.border", new MetalBorders.MenuItemBorder(),
+ "MenuItem.borderPainted", Boolean.TRUE,
+ "MenuItem.disabledForeground", getMenuDisabledForeground(),
+ "MenuItem.font", getMenuTextFont(),
+ "MenuItem.foreground", getMenuForeground(),
+ "MenuItem.selectionBackground", getMenuSelectedBackground(),
+ "MenuItem.selectionForeground", getMenuSelectedForeground(),
+
+ "OptionPane.background", getControl(),
+ "OptionPane.errorSound", "sounds/OptionPaneError.wav",
+ "OptionPane.informationSound", "sounds/OptionPaneInformation.wav",
+ "OptionPane.questionSound", "sounds/OptionPaneQuestion.wav",
+ "OptionPane.warningSound", "sounds/OptionPaneWarning.wav",
+ "OptionPane.errorDialog.border.background", new ColorUIResource(153, 51, 51),
+ "OptionPane.errorDialog.titlePane.background", new ColorUIResource(255, 153, 153),
+ "OptionPane.errorDialog.titlePane.foreground", new ColorUIResource(51, 0, 0),
+ "OptionPane.errorDialog.titlePane.shadow", new ColorUIResource(204, 102, 102),
+ "OptionPane.foreground", getControlTextColor(),
+ "OptionPane.messageForeground", getControlTextColor(),
+ "OptionPane.questionDialog.border.background", new ColorUIResource(51, 102, 51),
+ "OptionPane.questionDialog.titlePane.background", new ColorUIResource(153, 204, 153),
+ "OptionPane.questionDialog.titlePane.foreground", new ColorUIResource(0, 51, 0),
+ "OptionPane.questionDialog.titlePane.shadow", new ColorUIResource(102, 153, 102),
+ "OptionPane.warningDialog.border.background", new ColorUIResource(153, 102, 51),
+ "OptionPane.warningDialog.titlePane.background", new ColorUIResource(255, 204, 153),
+ "OptionPane.warningDialog.titlePane.foreground", new ColorUIResource(102, 51, 0),
+ "OptionPane.warningDialog.titlePane.shadow", new ColorUIResource(204, 153, 102),
+
+ "Panel.background", getControl(),
+ "Panel.foreground", getUserTextColor(),
+
+ "PasswordField.background", getWindowBackground(),
+ "PasswordField.border",
+ new BorderUIResource(MetalBorders.getTextFieldBorder()),
+ "PasswordField.caretForeground", getUserTextColor(),
+ "PasswordField.foreground", getUserTextColor(),
+ "PasswordField.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "PasswordField.inactiveBackground", getControl(),
+ "PasswordField.inactiveForeground", getInactiveSystemTextColor(),
+ "PasswordField.selectionBackground", getTextHighlightColor(),
+ "PasswordField.selectionForeground", getHighlightedTextColor(),
+
+ "PopupMenu.background", getMenuBackground(),
+ "PopupMenu.border", new MetalBorders.PopupMenuBorder(),
+ "PopupMenu.font", getMenuTextFont(),
+ "PopupMenu.foreground", getMenuForeground(),
+ "PopupMenu.popupSound", "sounds/PopupMenuPopup.wav",
+
+ "ProgressBar.background", getControl(),
+ "ProgressBar.border", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1),
+ "ProgressBar.font", getControlTextFont(),
+ "ProgressBar.foreground", getPrimaryControlShadow(),
+ "ProgressBar.selectionBackground", getPrimaryControlDarkShadow(),
+ "ProgressBar.selectionForeground", getControl(),
+
+ "RadioButton.background", getControl(),
+ "RadioButton.darkShadow", getControlDarkShadow(),
+ "RadioButton.disabledText", getInactiveControlTextColor(),
+ "RadioButton.icon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return MetalIconFactory.getRadioButtonIcon();
+ }
+ },
+ "RadioButton.focus", MetalLookAndFeel.getFocusColor(),
+ "RadioButton.font", MetalLookAndFeel.getControlTextFont(),
+ "RadioButton.foreground", getControlTextColor(),
+ "RadioButton.highlight", getControlHighlight(),
+ "RadioButton.light", getControlHighlight(),
+ "RadioButton.select", getControlShadow(),
+ "RadioButton.shadow", getControlShadow(),
+
+ "RadioButtonMenuItem.acceleratorFont", new Font("Dialog", Font.PLAIN, 10),
+ "RadioButtonMenuItem.acceleratorForeground", getAcceleratorForeground(),
+ "RadioButtonMenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(),
+ "RadioButtonMenuItem.background", getMenuBackground(),
+ "RadioButtonMenuItem.border", new MetalBorders.MenuItemBorder(),
+ "RadioButtonMenuItem.borderPainted", Boolean.TRUE,
+ "RadioButtonMenuItem.checkIcon",
+ MetalIconFactory.getRadioButtonMenuItemIcon(),
+ "RadioButtonMenuItem.commandSound", "sounds/MenuItemCommand.wav",
+ "RadioButtonMenuItem.disabledForeground", getMenuDisabledForeground(),
+ "RadioButtonMenuItem.font", getMenuTextFont(),
+ "RadioButtonMenuItem.foreground", getMenuForeground(),
+ "RadioButtonMenuItem.margin", new InsetsUIResource(2, 2, 2, 2),
+ "RadioButtonMenuItem.selectionBackground",
+ MetalLookAndFeel.getMenuSelectedBackground(),
+ "RadioButtonMenuItem.selectionForeground",
+ MetalLookAndFeel.getMenuSelectedForeground(),
+
+ "ScrollBar.allowsAbsolutePositioning", Boolean.TRUE,
+ "ScrollBar.background", getControl(),
+ "ScrollBar.darkShadow", getControlDarkShadow(),
+ "ScrollBar.foreground", getControl(),
+ "ScrollBar.highlight", getControlHighlight(),
+ "ScrollBar.shadow", getControlShadow(),
+ "ScrollBar.thumb", getPrimaryControlShadow(),
+ "ScrollBar.thumbDarkShadow", getControlDarkShadow(),
+ "ScrollBar.thumbHighlight", getPrimaryControl(),
+ "ScrollBar.thumbShadow", getPrimaryControlDarkShadow(),
+ "ScrollBar.track", getControl(),
+ "ScrollBar.trackHighlight", getControlDarkShadow(),
+ "ScrollBar.width", new Integer(17),
+
+ "ScrollPane.background", getControl(),
+ "ScrollPane.border", new MetalBorders.ScrollPaneBorder(),
+ "ScrollPane.foreground", getControlTextColor(),
+
+ "Separator.background", getSeparatorBackground(),
+ "Separator.foreground", getSeparatorForeground(),
+ "Separator.highlight", getControlHighlight(),
+ "Separator.shadow", getControlShadow(),
+
+ "Slider.background", getControl(),
+ "Slider.focus", getFocusColor(),
+ "Slider.focusInsets", new InsetsUIResource(0, 0, 0, 0),
+ "Slider.foreground", getPrimaryControlShadow(),
+ "Slider.highlight", getControlHighlight(),
+ "Slider.horizontalThumbIcon",
+ MetalIconFactory.getHorizontalSliderThumbIcon(),
+ "Slider.majorTickLength", new Integer(6),
+ "Slider.shadow", getControlShadow(),
+ "Slider.trackWidth", new Integer(7),
+ "Slider.verticalThumbIcon",
+ MetalIconFactory.getVerticalSliderThumbIcon(),
+
+ "Spinner.arrowButtonInsets", new InsetsUIResource(0, 0, 0, 0),
+ "Spinner.background", getControl(),
+ "Spinner.border", MetalBorders.getTextFieldBorder(),
+ "Spinner.font", getControlTextFont(),
+ "Spinner.foreground", getControl(),
+
+ "SplitPane.background", getControl(),
+ "SplitPane.darkShadow", getControlDarkShadow(),
+ "SplitPane.dividerFocusColor", getPrimaryControl(),
+ "SplitPane.dividerSize", new Integer(10),
+ "SplitPane.highlight", getControlHighlight(),
+ "SplitPane.shadow", getControlShadow(),
+
+ "SplitPaneDivider.draggingColor", Color.DARK_GRAY,
+
+ "TabbedPane.background", getControlShadow(),
+ "TabbedPane.contentBorderInsets", new InsetsUIResource(2, 2, 3, 3),
+ "TabbedPane.contentOpaque", Boolean.TRUE,
+ "TabbedPane.darkShadow", getControlDarkShadow(),
+ "TabbedPane.focus", getPrimaryControlDarkShadow(),
+ "TabbedPane.font", getControlTextFont(),
+ "TabbedPane.foreground", getControlTextColor(),
+ "TabbedPane.highlight", getControlHighlight(),
+ "TabbedPane.light", getControl(),
+ "TabbedPane.selected", getControl(), // overridden in OceanTheme
+ "TabbedPane.selectHighlight", getControlHighlight(),
+ "TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1),
+ "TabbedPane.shadow", getControlShadow(),
+ "TabbedPane.tabAreaBackground", getControl(), // overridden in OceanTheme
+ "TabbedPane.tabAreaInsets", new InsetsUIResource(4, 2, 0, 6), // dito
+ "TabbedPane.tabInsets", new InsetsUIResource(0, 9, 1, 9),
+
+ // new properties in OceanTheme:
+ // TabbedPane.contentAreaColor
+ // TabbedPane.unselectedBackground
+
+ "Table.background", getWindowBackground(),
+ "Table.focusCellBackground", getWindowBackground(),
+ "Table.focusCellForeground", getControlTextColor(),
+ "Table.foreground", getControlTextColor(),
+ "Table.focusCellHighlightBorder",
+ new BorderUIResource.LineBorderUIResource(getFocusColor()),
+ "Table.focusCellBackground", getWindowBackground(),
+ "Table.gridColor", getControlDarkShadow(),
+ "Table.selectionBackground", new ColorUIResource(204, 204, 255),
+ "Table.selectionForeground", new ColorUIResource(0, 0, 0),
+
+ "TableHeader.background", getControl(),
+ "TableHeader.cellBorder", new MetalBorders.TableHeaderBorder(),
+ "TableHeader.foreground", getControlTextColor(),
+
+ "TextArea.background", getWindowBackground(),
+ "TextArea.caretForeground", getUserTextColor(),
+ "TextArea.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "TextArea.foreground", getUserTextColor(),
+ "TextArea.inactiveForeground", getInactiveSystemTextColor(),
+ "TextArea.selectionBackground", getTextHighlightColor(),
+ "TextArea.selectionForeground", getHighlightedTextColor(),
+
+ "TextField.background", getWindowBackground(),
+ "TextField.border",
+ new BorderUIResource(MetalBorders.getTextFieldBorder()),
+ "TextField.caretForeground", getUserTextColor(),
+ "TextField.darkShadow", getControlDarkShadow(),
+ "TextField.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "TextField.foreground", getUserTextColor(),
+ "TextField.highlight", getControlHighlight(),
+ "TextField.inactiveBackground", getControl(),
+ "TextField.inactiveForeground", getInactiveSystemTextColor(),
+ "TextField.light", getControlHighlight(),
+ "TextField.selectionBackground", getTextHighlightColor(),
+ "TextField.selectionForeground", getHighlightedTextColor(),
+ "TextField.shadow", getControlShadow(),
+
+ "TextPane.background", getWindowBackground(),
+ "TextPane.caretForeground", getUserTextColor(),
+ "TextPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "TextPane.foreground", getUserTextColor(),
+ "TextPane.inactiveForeground", getInactiveSystemTextColor(),
+ "TextPane.selectionBackground", getTextHighlightColor(),
+ "TextPane.selectionForeground", getHighlightedTextColor(),
+
+ "TitledBorder.border", new LineBorderUIResource(getPrimaryControl(), 1),
+ "TitledBorder.font", getControlTextFont(),
+ "TitledBorder.titleColor", getSystemTextColor(),
+
+ "ToggleButton.background", getControl(),
+ "ToggleButton.border", MetalBorders.getToggleButtonBorder(),
+ "ToggleButton.darkShadow", getControlDarkShadow(),
+ "ToggleButton.disabledText", getInactiveControlTextColor(),
+ "ToggleButton.focus", getFocusColor(),
+ "ToggleButton.font", getControlTextFont(),
+ "ToggleButton.foreground", getControlTextColor(),
+ "ToggleButton.highlight", getControlHighlight(),
+ "ToggleButton.light", getControlHighlight(),
+ "ToggleButton.margin", new InsetsUIResource(2, 14, 2, 14),
+ "ToggleButton.select", getControlShadow(),
+ "ToggleButton.shadow", getControlShadow(),
+
+ "ToolBar.background", getMenuBackground(),
+ "ToolBar.darkShadow", getControlDarkShadow(),
+ "ToolBar.dockingBackground", getMenuBackground(),
+ "ToolBar.dockingForeground", getPrimaryControlDarkShadow(),
+ "ToolBar.floatingBackground", getMenuBackground(),
+ "ToolBar.floatingForeground", getPrimaryControl(),
+ "ToolBar.font", getMenuTextFont(),
+ "ToolBar.foreground", getMenuForeground(),
+ "ToolBar.highlight", getControlHighlight(),
+ "ToolBar.light", getControlHighlight(),
+ "ToolBar.shadow", getControlShadow(),
+ "ToolBar.border", new MetalBorders.ToolBarBorder(),
+ "ToolBar.rolloverBorder", MetalBorders.getToolbarButtonBorder(),
+ "ToolBar.nonrolloverBorder", MetalBorders.getToolbarButtonBorder(),
+
+ "ToolTip.background", getPrimaryControl(),
+ "ToolTip.backgroundInactive", getControl(),
+ "ToolTip.border", new BorderUIResource.LineBorderUIResource(getPrimaryControlDarkShadow(), 1),
+ "ToolTip.borderInactive", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1),
+ "ToolTip.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ToolTip.foreground", getPrimaryControlInfo(),
+ "ToolTip.foregroundInactive", getControlDarkShadow(),
+ "ToolTip.hideAccelerator", Boolean.FALSE,
+
+ "Tree.background", getWindowBackground(),
+ "Tree.closedIcon", MetalIconFactory.getTreeFolderIcon(),
+ "Tree.collapsedIcon", MetalIconFactory.getTreeControlIcon(true),
+ "Tree.expandedIcon", MetalIconFactory.getTreeControlIcon(false),
+ "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Tree.foreground", getUserTextColor(),
+ "Tree.hash", getPrimaryControl(),
+ "Tree.leafIcon", MetalIconFactory.getTreeLeafIcon(),
+ "Tree.leftChildIndent", new Integer(7),
+ "Tree.line", getPrimaryControl(),
+ "Tree.openIcon", MetalIconFactory.getTreeFolderIcon(),
+ "Tree.rightChildIndent", new Integer(13),
+ "Tree.rowHeight", new Integer(0),
+ "Tree.scrollsOnExpand", Boolean.TRUE,
+ "Tree.selectionBackground", getTextHighlightColor(),
+ "Tree.selectionBorder", new BorderUIResource.LineBorderUIResource(new Color(102, 102, 153)),
+ "Tree.selectionBorderColor", getFocusColor(),
+ "Tree.selectionForeground", getHighlightedTextColor(),
+ "Tree.textBackground", getWindowBackground(),
+ "Tree.textForeground", getUserTextColor(),
+
+ "Viewport.background", getControl(),
+ "Viewport.foreground", getUserTextColor()
+ };
+ defaults.putDefaults(myDefaults);
+ }
+
+ /**
+ * Initializes the system color defaults.
+ *
+ * In particular this sets the following keys:
+ *
+ * <table>
+ * <tr>
+ * <th>Key</th><th>Value</th><th>Description</th>
+ * </tr><tr>
+ * <td>control</td><td>0xcccccc</td><td>The default color for components</td>
+ * </tr>
+ * </table>
+ */
+ protected void initSystemColorDefaults(UIDefaults defaults)
+ {
+ super.initSystemColorDefaults(defaults);
+ Object[] uiDefaults;
+ uiDefaults = new Object[] {
+ "control", new ColorUIResource(getControl()),
+ "desktop", new ColorUIResource(getDesktopColor())
+ };
+ defaults.putDefaults(uiDefaults);
+ }
+
+ /**
+ * Returns the current theme for the Metal look and feel. The default is
+ * an instance of {@link OceanTheme}.
+ *
+ * @return The current theme (never <code>null</code>).
+ *
+ * @see #setCurrentTheme(MetalTheme)
+ */
+ public static MetalTheme getCurrentTheme()
+ {
+ if (theme == null)
+ {
+ // swing.metalTheme property documented here:
+ // http://java.sun.com/j2se/1.5.0/docs/guide/swing/1.5/index.html
+ if ("steel".equals(SystemProperties.getProperty("swing.metalTheme")))
+ theme = new DefaultMetalTheme();
+ else
+ theme = new OceanTheme();
+ }
+ return theme;
+ }
+
+ /**
+ * Returns <code>true</code> because the Metal look
+ * and feel supports window decorations for toplevel
+ * containers.
+ *
+ * @return <code>true</code>
+ */
+ public boolean getSupportsWindowDecorations()
+ {
+ return true;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java
new file mode 100644
index 000000000..35f2b05c0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java
@@ -0,0 +1,94 @@
+/* MetalMenuBarUI.java -- MenuBar UI for the Metal L&F
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Graphics;
+
+import javax.swing.JComponent;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicMenuBarUI;
+
+/**
+ * A UI implementation for MenuBar in the Metal Look &amp; Feel.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public class MetalMenuBarUI extends BasicMenuBarUI
+{
+ /**
+ * Creates and returns a new instance of this UI for the specified component.
+ *
+ * @param c the component to create a UI for
+ *
+ * @return the UI for the component
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new MetalMenuBarUI();
+ }
+
+
+ /**
+ * If the property <code>MenuBar.gradient</code> is set, then a gradient
+ * is painted as background, otherwise the normal superclass behaviour is
+ * called.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ int height = c.getHeight();
+ if (c.isOpaque()
+ && UIManager.get("MenuBar.gradient") != null
+ && c.getBackground() instanceof UIResource
+ && height > 2)
+ {
+ MetalUtils.paintGradient(g, 0, 0, c.getWidth(), height,
+ SwingConstants.VERTICAL, "MenuBar.gradient");
+
+ paint(g, c);
+ }
+ else
+ super.update(g, c);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java
new file mode 100644
index 000000000..7c580f90f
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java
@@ -0,0 +1,77 @@
+/* MetalPopupMenuSeparatorUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import javax.swing.JComponent;
+import javax.swing.JPopupMenu;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate for the {@link JPopupMenu.Separator} component.
+ */
+public class MetalPopupMenuSeparatorUI
+ extends MetalSeparatorUI
+{
+
+ // FIXME: maybe replace by a Map of instances when this becomes stateful
+ /** The shared UI instance for MetalPopupMenuSeparatorUIs */
+ private static MetalPopupMenuSeparatorUI instance;
+
+ /**
+ * Constructs a new instance of <code>MetalPopupMenuSeparatorUI</code>.
+ */
+ public MetalPopupMenuSeparatorUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a shared instance of <code>MetalPopupMenuSeparatorUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A shared instance of <code>MetalPopupMenuSeparatorUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ if (instance == null)
+ instance = new MetalPopupMenuSeparatorUI();
+ return instance;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java
new file mode 100644
index 000000000..005c5f0d8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java
@@ -0,0 +1,147 @@
+/* MetalProgressBarUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.JComponent;
+import javax.swing.JProgressBar;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicProgressBarUI;
+
+/**
+ * A UI delegate for the {@link JProgressBar} component.
+ */
+public class MetalProgressBarUI extends BasicProgressBarUI
+{
+ /**
+ * Constructs a new instance of <code>MetalProgressBarUI</code>.
+ */
+ public MetalProgressBarUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a new instance of <code>MetalProgressBarUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A new instance of <code>MetalProgressBarUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalProgressBarUI();
+ }
+
+ /**
+ * Performs the painting for determinate progress bars. This calls the
+ * superclass behaviour and then adds some highlighting to the upper and left
+ * edge of the progress bar.
+ *
+ * @param g the graphics context
+ * @param c not used here
+ */
+ public void paintDeterminate(Graphics g, JComponent c)
+ {
+ super.paintDeterminate(g, c);
+ Color saved = g.getColor();
+ Insets i = progressBar.getInsets();
+ int w = progressBar.getWidth();
+ int h = progressBar.getHeight();
+ int orientation = progressBar.getOrientation();
+
+ Color shadow = MetalLookAndFeel.getControlShadow();
+ g.setColor(shadow);
+
+ g.drawLine(i.left, i.top, w - i.right, i.top);
+ g.drawLine(i.left, i.top, i.left, h - i.bottom);
+ int full = getAmountFull(i, w, h);
+ if (full > 0)
+ {
+ Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
+ g.setColor(darkShadow);
+ if (orientation == JProgressBar.HORIZONTAL)
+ {
+ g.drawLine(i.left, i.top, i.left, h - i.bottom);
+ g.drawLine(i.left, i.top, i.left + full - 1, i.top);
+ }
+ else
+ {
+ if (full >= (h - i.top - i.bottom))
+ g.drawLine(i.left, i.top, w - i.right, i.top);
+ g.drawLine(i.left, h - i.bottom, i.left, h - i.bottom - full);
+ }
+ }
+ g.setColor(saved);
+ }
+
+ /**
+ * Performs the painting for indeterminate progress bars. This calls the
+ * superclass behaviour and then adds some highlighting to the upper and left
+ * edge of the progress bar.
+ *
+ * @param g the graphics context
+ * @param c not used here
+ */
+ public void paintIndeterminate(Graphics g, JComponent c)
+ {
+ super.paintIndeterminate(g, c);
+ Color saved = g.getColor();
+ Insets i = progressBar.getInsets();
+ int w = progressBar.getWidth();
+ int h = progressBar.getHeight();
+ Color shadow = MetalLookAndFeel.getControlShadow();
+ g.setColor(shadow);
+ g.drawLine(i.left, i.top, w - i.right, i.top);
+ g.drawLine(i.left, i.top, i.left, h - i.bottom);
+
+ boxRect = getBox(boxRect);
+ Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
+ g.setColor(darkShadow);
+ int orientation = progressBar.getOrientation();
+ if (orientation == JProgressBar.HORIZONTAL)
+ g.drawLine(boxRect.x, i.top, boxRect.x + boxRect.width - 1, i.top);
+ else
+ g.drawLine(i.left, boxRect.y, i.left, boxRect.y + boxRect.height - 1);
+ g.setColor(saved);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java
new file mode 100644
index 000000000..0f7f3b101
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java
@@ -0,0 +1,183 @@
+/* MetalRadioButtonUI.java
+ Copyright (C) 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.AbstractButton;
+import javax.swing.JComponent;
+import javax.swing.JRadioButton;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicRadioButtonUI;
+
+
+/**
+ * A UI delegate for the {@link JRadioButton} component.
+ */
+public class MetalRadioButtonUI
+ extends BasicRadioButtonUI
+{
+
+ /** Used to draw the focus rectangle. */
+ protected Color focusColor;
+
+ /** Used to fill the icon when the button is pressed. */
+ protected Color selectColor;
+
+ /** Used to draw disabled text. */
+ protected Color disabledTextColor;
+
+ /**
+ * Constructs a new instance of <code>MetalRadioButtonUI</code>.
+ */
+ public MetalRadioButtonUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a new instance of <code>MetalRadioButtonUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A new instance of <code>MetalRadioButtonUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalRadioButtonUI();
+ }
+
+ /**
+ * Sets the default values for the specified button.
+ *
+ * @param b the button.
+ */
+ public void installDefaults(AbstractButton b)
+ {
+ super.installDefaults(b);
+ String prefix = getPropertyPrefix();
+ disabledTextColor = UIManager.getColor(prefix + "disabledText");
+ focusColor = UIManager.getColor(prefix + "focus");
+ selectColor = UIManager.getColor(prefix + "select");
+ }
+
+ /**
+ * Clears any defaults set in the installDefaults() method.
+ *
+ * @param b the {@link JRadioButton}.
+ */
+ protected void uninstallDefaults(AbstractButton b)
+ {
+ super.uninstallDefaults(b);
+ disabledTextColor = null;
+ focusColor = null;
+ selectColor = null;
+ }
+
+ /**
+ * Returns the color used to fill the {@link JRadioButton}'s icon when the
+ * button is pressed. The default color is obtained from the
+ * {@link UIManager} defaults via an entry with the key
+ * <code>RadioButton.select</code>.
+ *
+ * @return The select color.
+ */
+ protected Color getSelectColor()
+ {
+ return selectColor;
+ }
+
+ /**
+ * Returns the color for the {@link JRadioButton}'s text when the button is
+ * disabled. The default color is obtained from the {@link UIManager}
+ * defaults via an entry with the key <code>RadioButton.disabledText</code>.
+ *
+ * @return The disabled text color.
+ */
+ protected Color getDisabledTextColor()
+ {
+ return disabledTextColor;
+ }
+
+ /**
+ * Returns the color used to draw the focus rectangle when the
+ * {@link JRadioButton} has the focus. The default color is obtained from
+ * the {@link UIManager} defaults via an entry with the key
+ * <code>RadioButton.focus</code>.
+ *
+ * @return The color used to draw the focus rectangle.
+ *
+ * @see #paintFocus(Graphics, Rectangle, Dimension)
+ */
+ protected Color getFocusColor()
+ {
+ return focusColor;
+ }
+
+ /**
+ * Paints the {@link JRadioButton}.
+ *
+ * @param g the graphics device.
+ * @param c the component (an instance of {@link JRadioButton}).
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ super.paint(g, c);
+ // FIXME: disabled text isn't being drawn correctly, it's possible that
+ // it could be done here...
+ }
+
+ /**
+ * Paints the focus rectangle for the {@link JRadioButton}.
+ *
+ * @param g the graphics device.
+ * @param t the bounding rectangle for the text.
+ * @param d ???
+ */
+ protected void paintFocus(Graphics g, Rectangle t, Dimension d)
+ {
+ g.setColor(focusColor);
+ g.drawRect(t.x - 1, t.y - 1, t.width + 1, t.height + 1);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java
new file mode 100644
index 000000000..84f1303f6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java
@@ -0,0 +1,1075 @@
+/* MetalRootPaneUI.java
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.LayoutManager2;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JRootPane;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.AbstractBorder;
+import javax.swing.event.MouseInputAdapter;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicRootPaneUI;
+
+/**
+ * A UI delegate for the {@link JRootPane} component. This implementation
+ * supports the JRootPane <code>windowDecorationStyle</code> property.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.4
+ */
+public class MetalRootPaneUI
+ extends BasicRootPaneUI
+{
+
+ /**
+ * The border that is used on JRootPane when the windowDecorationStyle
+ * property of the JRootPane is set to a different value than NONE.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class MetalFrameBorder
+ extends AbstractBorder
+ {
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component
+ * @param newInsets the insets to be filled with the return value, may be
+ * <code>null</code> in which case a new object is created
+ *
+ * @return the border insets
+ */
+ public Insets getBorderInsets(Component c, Insets newInsets)
+ {
+ if (newInsets == null)
+ newInsets = new Insets(5, 5, 5, 5);
+ else
+ {
+ newInsets.top = 5;
+ newInsets.left = 5;
+ newInsets.bottom = 5;
+ newInsets.right = 5;
+ }
+ return newInsets;
+ }
+
+ /**
+ * Returns the border insets.
+ *
+ * @param c the component
+ *
+ * @return the border insets
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+ /**
+ * Paints the border for the specified component.
+ *
+ * @param c the component
+ * @param g the graphics device
+ * @param x the x-coordinate
+ * @param y the y-coordinate
+ * @param w the width
+ * @param h the height
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int w,
+ int h)
+ {
+ JRootPane f = (JRootPane) c;
+ Window frame = SwingUtilities.getWindowAncestor(f);
+ if (frame.isActive())
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+
+ // Fill the border background.
+ g.fillRect(x, y, w, 5);
+ g.fillRect(x, y, 5, h);
+ g.fillRect(x + w - 5, y, 5, h);
+ g.fillRect(x, y + h - 5, w, 5);
+
+ // Draw a dot in each corner.
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(x, y, 1, 1);
+ g.fillRect(x + w - 1, y, 1, 1);
+ g.fillRect(x + w - 1, y + h - 1, 1, 1);
+ g.fillRect(x, y + h - 1, 1, 1);
+
+ // Draw the lines.
+ g.setColor(MetalLookAndFeel.getBlack());
+ g.drawLine(x + 14, y + 2, x + w - 15, y + 2);
+ g.drawLine(x + 14, y + h - 3, x + w - 15, y + h - 3);
+ g.drawLine(x + 2, y + 14, x + 2, y + h - 15);
+ g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15);
+
+ // Draw the line highlights.
+ if (frame.isActive())
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x + 15, y + 3, x + w - 14, y + 3);
+ g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2);
+ g.drawLine(x + 3, y + 15, x + 3, y + h - 14);
+ g.drawLine(x + w - 2, y + 15, x + w - 2, y + h - 14);
+ }
+ }
+
+ /**
+ * The component that renders the title bar for frames. This duplicates
+ * most of {@link MetalInternalFrameTitlePane}. It is not reasonably possible
+ * to reuse that class because that is bound to the JInternalFrame and we
+ * need to handle JFrames/JRootPanes here.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class MetalTitlePane extends JComponent
+ {
+
+ /**
+ * Handles dragging of the title pane and moves the window accordingly.
+ */
+ private class MouseHandler
+ extends MouseInputAdapter
+ {
+ /**
+ * The point where the dragging started.
+ */
+ Point lastDragLocation;
+
+ /**
+ * Receives notification when the mouse gets pressed on the title pane.
+ * This updates the lastDragLocation.
+ *
+ * @param ev the mouse event
+ */
+ public void mousePressed(MouseEvent ev)
+ {
+ lastDragLocation = ev.getPoint();
+ }
+
+ /**
+ * Receives notification when the mouse is dragged on the title pane.
+ * This will move the nearest window accordingly.
+ *
+ * @param ev the mouse event
+ */
+ public void mouseDragged(MouseEvent ev)
+ {
+ Point dragLocation = ev.getPoint();
+ int deltaX = dragLocation.x - lastDragLocation.x;
+ int deltaY = dragLocation.y - lastDragLocation.y;
+ Window window = SwingUtilities.getWindowAncestor(rootPane);
+ Point loc = window.getLocation();
+ window.setLocation(loc.x + deltaX, loc.y + deltaY);
+ // Note that we do not update the lastDragLocation. This is because
+ // we move the underlying window while dragging the component, which
+ // results in having the same lastDragLocation under the mouse while
+ // dragging.
+ }
+ }
+
+ /**
+ * The Action responsible for closing the JInternalFrame.
+ */
+ private class CloseAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public CloseAction()
+ {
+ super("Close");
+ }
+
+ /**
+ * This method is called when something closes the frame.
+ *
+ * @param e the ActionEvent
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ Window frame = SwingUtilities.getWindowAncestor(rootPane);
+ if (frame instanceof JFrame)
+ {
+ JFrame jframe = (JFrame) frame;
+ switch (jframe.getDefaultCloseOperation())
+ {
+ case JFrame.EXIT_ON_CLOSE:
+ jframe.setVisible(false);
+ jframe.dispose();
+ System.exit(0);
+ break;
+ case JFrame.DISPOSE_ON_CLOSE:
+ jframe.setVisible(false);
+ jframe.dispose();
+ break;
+ case JFrame.HIDE_ON_CLOSE:
+ jframe.setVisible(false);
+ break;
+ case JFrame.DO_NOTHING_ON_CLOSE:
+ default:
+ break;
+ }
+ }
+ else if (frame instanceof JDialog)
+ {
+ JDialog jdialog = (JDialog) frame;
+ switch (jdialog.getDefaultCloseOperation())
+ {
+ case JFrame.DISPOSE_ON_CLOSE:
+ jdialog.setVisible(false);
+ jdialog.dispose();
+ break;
+ case JFrame.HIDE_ON_CLOSE:
+ jdialog.setVisible(false);
+ break;
+ case JFrame.DO_NOTHING_ON_CLOSE:
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This action is performed when the iconify button is pressed.
+ */
+ private class IconifyAction
+ extends AbstractAction
+ {
+
+ public void actionPerformed(ActionEvent event)
+ {
+ Window w = SwingUtilities.getWindowAncestor(rootPane);
+ if (w instanceof Frame)
+ {
+ Frame f = (Frame) w;
+ int state = f.getExtendedState();
+ f.setExtendedState(Frame.ICONIFIED);
+ }
+ }
+
+ }
+
+ /**
+ * This action is performed when the maximize button is pressed.
+ */
+ private class MaximizeAction
+ extends AbstractAction
+ {
+
+ public void actionPerformed(ActionEvent event)
+ {
+ Window w = SwingUtilities.getWindowAncestor(rootPane);
+ if (w instanceof Frame)
+ {
+ Frame f = (Frame) w;
+ int state = f.getExtendedState();
+ f.setExtendedState(Frame.MAXIMIZED_BOTH);
+ }
+ }
+ }
+
+ /**
+ * This helper class is used to create the minimize, maximize and close
+ * buttons in the top right corner of the Title Pane. These buttons are
+ * special since they cannot be given focus and have no border.
+ */
+ private class PaneButton extends JButton
+ {
+ /**
+ * Creates a new PaneButton object with the given Action.
+ *
+ * @param a The Action that the button uses.
+ */
+ public PaneButton(Action a)
+ {
+ super(a);
+ setMargin(new Insets(0, 0, 0, 0));
+ }
+
+ /**
+ * This method returns true if the Component can be focused.
+ *
+ * @return false.
+ */
+ public boolean isFocusable()
+ {
+ // These buttons cannot be given focus.
+ return false;
+ }
+
+ }
+
+ /**
+ * The layout for the JRootPane when the <code>windowDecorationStyle</code>
+ * property is set. In addition to the usual JRootPane.RootLayout behaviour
+ * this lays out the titlePane.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class MetalTitlePaneLayout implements LayoutManager
+ {
+ /**
+ * Creates a new <code>TitlePaneLayout</code> object.
+ */
+ public MetalTitlePaneLayout()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Adds a Component to the Container.
+ *
+ * @param name The name to reference the added Component by.
+ * @param c The Component to add.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the children of the Title Pane.
+ *
+ * @param c The Container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+
+ Dimension size = c.getSize();
+ Insets insets = c.getInsets();
+ int width = size.width - insets.left - insets.right;
+ int height = size.height - insets.top - insets.bottom;
+
+ int loc = width - insets.right - 1;
+ int top = insets.top + 2;
+ int buttonHeight = height - 4;
+ if (closeButton.isVisible())
+ {
+ int buttonWidth = closeIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ closeButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ loc -= 6;
+ }
+
+ if (maxButton.isVisible())
+ {
+ int buttonWidth = maxIcon.getIconWidth();
+ loc -= buttonWidth + 4;
+ maxButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (iconButton.isVisible())
+ {
+ int buttonWidth = minIcon.getIconWidth();
+ loc -= buttonWidth + 4;
+ iconButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ loc -= 2;
+ }
+
+ Dimension titlePreferredSize = title.getPreferredSize();
+ title.setBounds(insets.left + 5, insets.top,
+ Math.min(titlePreferredSize.width, loc - insets.left - 10),
+ height);
+
+ }
+
+ /**
+ * This method returns the minimum size of the given Container given the
+ * children that it has.
+ *
+ * @param c The Container to get a minimum size for.
+ *
+ * @return The minimum size of the Container.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return preferredLayoutSize(c);
+ }
+
+ /**
+ * Returns the preferred size of the given Container taking
+ * into account the children that it has.
+ *
+ * @param c The Container to lay out.
+ *
+ * @return The preferred size of the Container.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ return new Dimension(22, 22);
+ }
+
+ /**
+ * Removes a Component from the Container.
+ *
+ * @param c The Component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ JRootPane rootPane;
+
+ /** The button that closes the JInternalFrame. */
+ JButton closeButton;
+
+ /** The button that iconifies the JInternalFrame. */
+ JButton iconButton;
+
+ /** The button that maximizes the JInternalFrame. */
+ JButton maxButton;
+
+ Icon minIcon;
+
+ /** The icon displayed in the maximize button. */
+ Icon maxIcon;
+
+ /** The icon displayed in the iconify button. */
+ private Icon iconIcon;
+
+ /** The icon displayed in the close button. */
+ Icon closeIcon;
+
+ /**
+ * The background color of the TitlePane when the JInternalFrame is not
+ * selected.
+ */
+ private Color notSelectedTitleColor;
+
+ /**
+ * The background color of the TitlePane when the JInternalFrame is
+ * selected.
+ */
+ private Color selectedTitleColor;
+
+ /**
+ * The label used to display the title. This label is not added to the
+ * TitlePane.
+ */
+ JLabel title;
+
+ /** The action associated with closing the JInternalFrame. */
+ private Action closeAction;
+
+ /** The action associated with iconifying the JInternalFrame. */
+ private Action iconifyAction;
+
+ /** The action associated with maximizing the JInternalFrame. */
+ private Action maximizeAction;
+
+ /** The JMenuBar that is located at the top left of the Title Pane. */
+ private JMenuBar menuBar;
+
+ /** The JMenu inside the menuBar. */
+ protected JMenu windowMenu;
+
+ MetalTitlePane(JRootPane rp)
+ {
+ rootPane = rp;
+ setLayout(createLayout());
+ title = new JLabel();
+ title.setHorizontalAlignment(SwingConstants.LEFT);
+ title.setHorizontalTextPosition(SwingConstants.LEFT);
+ title.setOpaque(false);
+ installTitlePane();
+ }
+
+ protected LayoutManager createLayout()
+ {
+ return new MetalTitlePaneLayout();
+ }
+
+ /**
+ * This method installs the TitlePane onto the JInternalFrameTitlePane. It
+ * also creates any children components that need to be created and adds
+ * listeners to the appropriate components.
+ */
+ protected void installTitlePane()
+ {
+ installDefaults();
+ installListeners();
+ createActions();
+ assembleSystemMenu();
+ createButtons();
+ setButtonIcons();
+ addSubComponents();
+ enableActions();
+ }
+
+ private void enableActions()
+ {
+ // TODO: Implement this.
+ }
+
+ private void addSubComponents()
+ {
+ add(menuBar);
+ add(closeButton);
+ add(iconButton);
+ add(maxButton);
+ }
+
+ private void installListeners()
+ {
+ MouseInputAdapter mouseHandler = new MouseHandler();
+ addMouseListener(mouseHandler);
+ addMouseMotionListener(mouseHandler);
+ }
+
+ private void createActions()
+ {
+ closeAction = new CloseAction();
+ iconifyAction = new IconifyAction();
+ maximizeAction = new MaximizeAction();
+ }
+
+ private void assembleSystemMenu()
+ {
+ menuBar = createSystemMenuBar();
+ windowMenu = createSystemMenu();
+ menuBar.add(windowMenu);
+ addSystemMenuItems(windowMenu);
+ enableActions();
+ }
+
+ protected JMenuBar createSystemMenuBar()
+ {
+ if (menuBar == null)
+ menuBar = new JMenuBar();
+ menuBar.removeAll();
+ return menuBar;
+ }
+
+ protected JMenu createSystemMenu()
+ {
+ if (windowMenu == null)
+ windowMenu = new JMenu();
+ windowMenu.removeAll();
+ return windowMenu;
+ }
+
+ private void addSystemMenuItems(JMenu menu)
+ {
+ // TODO: Implement this.
+ }
+
+ protected void createButtons()
+ {
+ closeButton = new PaneButton(closeAction);
+ closeButton.setText(null);
+ iconButton = new PaneButton(iconifyAction);
+ iconButton.setText(null);
+ maxButton = new PaneButton(maximizeAction);
+ maxButton.setText(null);
+ closeButton.setBorderPainted(false);
+ closeButton.setContentAreaFilled(false);
+ iconButton.setBorderPainted(false);
+ iconButton.setContentAreaFilled(false);
+ maxButton.setBorderPainted(false);
+ maxButton.setContentAreaFilled(false);
+ }
+
+ protected void setButtonIcons()
+ {
+ if (closeIcon != null && closeButton != null)
+ closeButton.setIcon(closeIcon);
+ if (iconIcon != null && iconButton != null)
+ iconButton.setIcon(iconIcon);
+ if (maxIcon != null && maxButton != null)
+ maxButton.setIcon(maxIcon);
+ }
+
+ /**
+ * Paints a representation of the current state of the internal frame.
+ *
+ * @param g the graphics device.
+ */
+ public void paintComponent(Graphics g)
+ {
+ Window frame = SwingUtilities.getWindowAncestor(rootPane);
+ Color savedColor = g.getColor();
+ paintTitleBackground(g);
+ paintChildren(g);
+ Dimension d = getSize();
+ if (frame.isActive())
+ g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+
+ // put a dot in each of the top corners
+ g.drawLine(0, 0, 0, 0);
+ g.drawLine(d.width - 1, 0, d.width - 1, 0);
+
+ g.drawLine(0, d.height - 1, d.width - 1, d.height - 1);
+
+ // draw the metal pattern
+ if (UIManager.get("InternalFrame.activeTitleGradient") != null
+ && frame.isActive())
+ {
+ MetalUtils.paintGradient(g, 0, 0, getWidth(), getHeight(),
+ SwingConstants.VERTICAL,
+ "InternalFrame.activeTitleGradient");
+ }
+
+ Rectangle b = title.getBounds();
+ int startX = b.x + b.width + 5;
+ int endX = startX;
+ if (iconButton.isVisible())
+ endX = Math.max(iconButton.getX(), endX);
+ else if (maxButton.isVisible())
+ endX = Math.max(maxButton.getX(), endX);
+ else if (closeButton.isVisible())
+ endX = Math.max(closeButton.getX(), endX);
+ endX -= 7;
+ if (endX > startX)
+ MetalUtils.fillMetalPattern(this, g, startX, 3, endX - startX, getHeight() - 6, Color.white, Color.gray);
+ g.setColor(savedColor);
+ }
+
+ /**
+ * This method paints the TitlePane's background.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintTitleBackground(Graphics g)
+ {
+ Window frame = SwingUtilities.getWindowAncestor(rootPane);
+
+ if (!isOpaque())
+ return;
+
+ Color saved = g.getColor();
+ Dimension dims = getSize();
+
+ Color bg = getBackground();
+ if (frame.isActive())
+ bg = selectedTitleColor;
+ else
+ bg = notSelectedTitleColor;
+ g.setColor(bg);
+ g.fillRect(0, 0, dims.width, dims.height);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method installs the defaults determined by the look and feel.
+ */
+ private void installDefaults()
+ {
+ title.setFont(UIManager.getFont("InternalFrame.titleFont"));
+ selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground");
+ notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground");
+ closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
+ iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
+ maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
+ minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16);
+ Frame frame = (Frame) SwingUtilities.getWindowAncestor(rootPane);
+ title = new JLabel(frame.getTitle(),
+ MetalIconFactory.getInternalFrameDefaultMenuIcon(),
+ SwingConstants.LEFT);
+ }
+ }
+
+ private static class MetalRootLayout
+ implements LayoutManager2
+ {
+
+ /**
+ * The cached layout info for the glass pane.
+ */
+ private Rectangle glassPaneBounds;
+
+ /**
+ * The cached layout info for the layered pane.
+ */
+ private Rectangle layeredPaneBounds;
+
+ /**
+ * The cached layout info for the content pane.
+ */
+ private Rectangle contentPaneBounds;
+
+ /**
+ * The cached layout info for the menu bar.
+ */
+ private Rectangle menuBarBounds;
+
+ /**
+ * The cached layout info for the title pane.
+ */
+ private Rectangle titlePaneBounds;
+
+ /**
+ * The cached preferred size.
+ */
+ private Dimension prefSize;
+
+ /**
+ * The title pane for l&f decorated frames.
+ */
+ private MetalTitlePane titlePane;
+
+ /**
+ * Creates a new MetalRootLayout.
+ *
+ * @param tp the title pane
+ */
+ MetalRootLayout(MetalTitlePane tp)
+ {
+ titlePane = tp;
+ }
+
+ public void addLayoutComponent(Component component, Object constraints)
+ {
+ // Nothing to do here.
+ }
+
+ public Dimension maximumLayoutSize(Container target)
+ {
+ return preferredLayoutSize(target);
+ }
+
+ public float getLayoutAlignmentX(Container target)
+ {
+ return 0.0F;
+ }
+
+ public float getLayoutAlignmentY(Container target)
+ {
+ return 0.0F;
+ }
+
+ public void invalidateLayout(Container target)
+ {
+ synchronized (this)
+ {
+ glassPaneBounds = null;
+ layeredPaneBounds = null;
+ contentPaneBounds = null;
+ menuBarBounds = null;
+ titlePaneBounds = null;
+ prefSize = null;
+ }
+ }
+
+ public void addLayoutComponent(String name, Component component)
+ {
+ // Nothing to do here.
+ }
+
+ public void removeLayoutComponent(Component component)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ JRootPane rp = (JRootPane) parent;
+ JLayeredPane layeredPane = rp.getLayeredPane();
+ Component contentPane = rp.getContentPane();
+ Component menuBar = rp.getJMenuBar();
+
+ // We must synchronize here, otherwise we cannot guarantee that the
+ // prefSize is still non-null when returning.
+ synchronized (this)
+ {
+ if (prefSize == null)
+ {
+ Insets i = parent.getInsets();
+ prefSize = new Dimension(i.left + i.right, i.top + i.bottom);
+ Dimension contentPrefSize = contentPane.getPreferredSize();
+ prefSize.width += contentPrefSize.width;
+ prefSize.height += contentPrefSize.height
+ + titlePane.getPreferredSize().height;
+ if (menuBar != null)
+ {
+ Dimension menuBarSize = menuBar.getPreferredSize();
+ if (menuBarSize.width > contentPrefSize.width)
+ prefSize.width += menuBarSize.width - contentPrefSize.width;
+ prefSize.height += menuBarSize.height;
+ }
+ }
+ // Return a copy here so the cached value won't get trashed by some
+ // other component.
+ return new Dimension(prefSize);
+ }
+ }
+
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return preferredLayoutSize(parent);
+ }
+
+ public void layoutContainer(Container parent)
+ {
+ JRootPane rp = (JRootPane) parent;
+ JLayeredPane layeredPane = rp.getLayeredPane();
+ Component contentPane = rp.getContentPane();
+ Component menuBar = rp.getJMenuBar();
+ Component glassPane = rp.getGlassPane();
+
+ if (glassPaneBounds == null || layeredPaneBounds == null
+ || contentPaneBounds == null || menuBarBounds == null)
+ {
+ Insets i = rp.getInsets();
+ int containerWidth = parent.getBounds().width - i.left - i.right;
+ int containerHeight = parent.getBounds().height - i.top - i.bottom;
+
+ // 1. The glassPane fills entire viewable region (bounds - insets).
+ // 2. The layeredPane filles entire viewable region.
+ // 3. The titlePane is placed at the upper edge of the layeredPane.
+ // 4. The menuBar is positioned at the upper edge of layeredPane.
+ // 5. The contentPane fills viewable region minus menuBar minus
+ // titlePane, if present.
+
+ // +-------------------------------+
+ // | JLayeredPane |
+ // | +--------------------------+ |
+ // | | titlePane + |
+ // | +--------------------------+ |
+ // | +--------------------------+ |
+ // | | menuBar | |
+ // | +--------------------------+ |
+ // | +--------------------------+ |
+ // | |contentPane | |
+ // | | | |
+ // | | | |
+ // | | | |
+ // | +--------------------------+ |
+ // +-------------------------------+
+
+ // Setup titlePaneBounds.
+ if (titlePaneBounds == null)
+ titlePaneBounds = new Rectangle();
+ titlePaneBounds.width = containerWidth;
+ titlePaneBounds.height = titlePane.getPreferredSize().height;
+
+ // Setup menuBarBounds.
+ if (menuBarBounds == null)
+ menuBarBounds = new Rectangle();
+ menuBarBounds.setBounds(0,
+ titlePaneBounds.y + titlePaneBounds.height,
+ containerWidth, 0);
+ if (menuBar != null)
+ {
+ Dimension menuBarSize = menuBar.getPreferredSize();
+ if (menuBarSize.height > containerHeight)
+ menuBarBounds.height = containerHeight;
+ else
+ menuBarBounds.height = menuBarSize.height;
+ }
+
+ // Setup contentPaneBounds.
+ if (contentPaneBounds == null)
+ contentPaneBounds = new Rectangle();
+ contentPaneBounds.setBounds(0,
+ menuBarBounds.y + menuBarBounds.height,
+ containerWidth,
+ containerHeight - menuBarBounds.y
+ - menuBarBounds.height);
+ glassPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
+ layeredPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
+ }
+
+ // Layout components.
+ glassPane.setBounds(glassPaneBounds);
+ layeredPane.setBounds(layeredPaneBounds);
+ if (menuBar != null)
+ menuBar.setBounds(menuBarBounds);
+ contentPane.setBounds(contentPaneBounds);
+ titlePane.setBounds(titlePaneBounds);
+ }
+
+ }
+
+ /**
+ * The shared UI instance for MetalRootPaneUIs.
+ */
+ private static MetalRootPaneUI instance;
+
+ /**
+ * Constructs a shared instance of <code>MetalRootPaneUI</code>.
+ */
+ public MetalRootPaneUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a shared instance of <code>MetalRootPaneUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A shared instance of <code>MetalRootPaneUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ if (instance == null)
+ instance = new MetalRootPaneUI();
+ return instance;
+ }
+
+ /**
+ * Installs this UI to the root pane. If the
+ * <code>windowDecorationsStyle</code> property is set on the root pane,
+ * the Metal window decorations are installed on the root pane.
+ *
+ * @param c
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ JRootPane rp = (JRootPane) c;
+ if (rp.getWindowDecorationStyle() != JRootPane.NONE)
+ installWindowDecorations(rp);
+ }
+
+ /**
+ * Uninstalls the UI from the root pane. This performs the superclass
+ * behaviour and uninstalls the window decorations that have possibly been
+ * installed by {@link #installUI}.
+ *
+ * @param c the root pane
+ */
+ public void uninstallUI(JComponent c)
+ {
+ JRootPane rp = (JRootPane) c;
+ if (rp.getWindowDecorationStyle() != JRootPane.NONE)
+ uninstallWindowDecorations(rp);
+ super.uninstallUI(c);
+ }
+
+ /**
+ * Receives notification if any of the JRootPane's property changes. In
+ * particular this catches changes to the <code>windowDecorationStyle</code>
+ * property and installs the window decorations accordingly.
+ *
+ * @param ev the property change event
+ */
+ public void propertyChange(PropertyChangeEvent ev)
+ {
+ super.propertyChange(ev);
+ String propertyName = ev.getPropertyName();
+ if (propertyName.equals("windowDecorationStyle"))
+ {
+ JRootPane rp = (JRootPane) ev.getSource();
+ if (rp.getWindowDecorationStyle() != JRootPane.NONE)
+ installWindowDecorations(rp);
+ else
+ uninstallWindowDecorations(rp);
+ }
+ }
+
+ /**
+ * Installs the window decorations to the root pane. This sets up a border,
+ * a title pane and a layout manager that can layout the root pane with that
+ * title pane.
+ *
+ * @param rp the root pane.
+ */
+ private void installWindowDecorations(JRootPane rp)
+ {
+ rp.setBorder(new MetalFrameBorder());
+ MetalTitlePane titlePane = new MetalTitlePane(rp);
+ rp.setLayout(new MetalRootLayout(titlePane));
+ // We should have a contentPane already.
+ assert rp.getLayeredPane().getComponentCount() > 0
+ : "We should have a contentPane already";
+
+ rp.getLayeredPane().add(titlePane,
+ JLayeredPane.FRAME_CONTENT_LAYER, 1);
+ }
+
+ /**
+ * Uninstalls the window decorations from the root pane. This should rarely
+ * be necessary, but we do it anyway.
+ *
+ * @param rp the root pane
+ */
+ private void uninstallWindowDecorations(JRootPane rp)
+ {
+ rp.setBorder(null);
+ JLayeredPane lp = rp.getLayeredPane();
+ for (int i = lp.getComponentCount() - 1; i >= 0; --i)
+ {
+ if (lp.getComponent(i) instanceof MetalTitlePane)
+ {
+ lp.remove(i);
+ break;
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java
new file mode 100644
index 000000000..5f0cbe4be
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java
@@ -0,0 +1,584 @@
+/* MetalScrollBarUI.java
+ Copyright (C) 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JScrollBar;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicScrollBarUI;
+
+/**
+ * A UI delegate for the {@link JScrollBar} component.
+ */
+public class MetalScrollBarUI extends BasicScrollBarUI
+{
+
+ /**
+ * A property change handler for the UI delegate that monitors for
+ * changes to the "JScrollBar.isFreeStanding" property, and updates
+ * the buttons and track rendering as appropriate.
+ */
+ class MetalScrollBarPropertyChangeHandler
+ extends BasicScrollBarUI.PropertyChangeHandler
+ {
+ /**
+ * Creates a new handler.
+ *
+ * @see #createPropertyChangeListener()
+ */
+ public MetalScrollBarPropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Handles a property change event. If the event name is
+ * <code>JSlider.isFreeStanding</code>, this method updates the
+ * delegate, otherwise the event is passed up to the super class.
+ *
+ * @param e the property change event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(FREE_STANDING_PROP))
+ {
+ Boolean prop = (Boolean) e.getNewValue();
+ isFreeStanding = prop == null ? true : prop.booleanValue();
+ if (increaseButton != null)
+ increaseButton.setFreeStanding(isFreeStanding);
+ if (decreaseButton != null)
+ decreaseButton.setFreeStanding(isFreeStanding);
+ }
+ else
+ super.propertyChange(e);
+ }
+ }
+
+ /** The name for the 'free standing' property. */
+ public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding";
+
+ /** The minimum thumb size for a scroll bar that is not free standing. */
+ private static final Dimension MIN_THUMB_SIZE = new Dimension(15, 15);
+
+ /** The minimum thumb size for a scroll bar that is free standing. */
+ private static final Dimension MIN_THUMB_SIZE_FREE_STANDING
+ = new Dimension(17, 17);
+
+ /** The button that increases the value in the scroll bar. */
+ protected MetalScrollButton increaseButton;
+
+ /** The button that decreases the value in the scroll bar. */
+ protected MetalScrollButton decreaseButton;
+
+ /**
+ * The scroll bar width.
+ */
+ protected int scrollBarWidth;
+
+ /**
+ * A flag that indicates whether the scroll bar is "free standing", which
+ * means it has complete borders and can be used anywhere in the UI. A
+ * scroll bar which is not free standing has borders missing from one
+ * side, and relies on being part of another container with its own borders
+ * to look right visually. */
+ protected boolean isFreeStanding = true;
+
+ /**
+ * The color for the scroll bar shadow (this is read from the UIDefaults in
+ * the installDefaults() method).
+ */
+ Color scrollBarShadowColor;
+
+ /**
+ * Constructs a new instance of <code>MetalScrollBarUI</code>, with no
+ * specific initialisation.
+ */
+ public MetalScrollBarUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a new instance of <code>MetalScrollBarUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return An instance of MetalScrollBarUI
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalScrollBarUI();
+ }
+
+ /**
+ * Installs the defaults.
+ */
+ protected void installDefaults()
+ {
+ // need to initialise isFreeStanding before calling the super class,
+ // so that the value is set when createIncreaseButton() and
+ // createDecreaseButton() are called (unless there is somewhere earlier
+ // that we can do this).
+ Boolean prop = (Boolean) scrollbar.getClientProperty(FREE_STANDING_PROP);
+ isFreeStanding = prop == null ? true : prop.booleanValue();
+ scrollBarShadowColor = UIManager.getColor("ScrollBar.shadow");
+ scrollBarWidth = UIManager.getInt("ScrollBar.width");
+ super.installDefaults();
+ }
+
+ /**
+ * Creates a property change listener for the delegate to use. This
+ * overrides the method to provide a custom listener for the
+ * {@link MetalLookAndFeel} that can handle the
+ * <code>JScrollBar.isFreeStanding</code> property.
+ *
+ * @return A property change listener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new MetalScrollBarPropertyChangeHandler();
+ }
+
+ /**
+ * Creates a new button to use as the control at the lower end of the
+ * {@link JScrollBar}. This method assigns the new button (an instance of
+ * {@link MetalScrollButton} to the {@link #decreaseButton} field, and also
+ * returns the button. The button width is determined by the
+ * <code>ScrollBar.width</code> setting in the UI defaults.
+ *
+ * @param orientation the orientation of the button ({@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} or {@link #WEST}).
+ *
+ * @return The button.
+ */
+ protected JButton createDecreaseButton(int orientation)
+ {
+ decreaseButton = new MetalScrollButton(orientation, scrollBarWidth,
+ isFreeStanding);
+ return decreaseButton;
+ }
+
+ /**
+ * Creates a new button to use as the control at the upper end of the
+ * {@link JScrollBar}. This method assigns the new button (an instance of
+ * {@link MetalScrollButton} to the {@link #increaseButton} field, and also
+ * returns the button. The button width is determined by the
+ * <code>ScrollBar.width</code> setting in the UI defaults.
+ *
+ * @param orientation the orientation of the button ({@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} or {@link #WEST}).
+ *
+ * @return The button.
+ */
+ protected JButton createIncreaseButton(int orientation)
+ {
+ increaseButton = new MetalScrollButton(orientation, scrollBarWidth,
+ isFreeStanding);
+ return increaseButton;
+ }
+
+ /**
+ * Paints the track for the scrollbar.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ * @param trackBounds the track bounds.
+ */
+ protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
+ {
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width,
+ trackBounds.height);
+ if (scrollbar.getOrientation() == HORIZONTAL)
+ paintTrackHorizontal(g, c, trackBounds.x, trackBounds.y,
+ trackBounds.width, trackBounds.height);
+ else
+ paintTrackVertical(g, c, trackBounds.x, trackBounds.y,
+ trackBounds.width, trackBounds.height);
+
+ }
+
+ /**
+ * Paints the track for a horizontal scrollbar.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ * @param x the x-coordinate for the track bounds.
+ * @param y the y-coordinate for the track bounds.
+ * @param w the width for the track bounds.
+ * @param h the height for the track bounds.
+ */
+ private void paintTrackHorizontal(Graphics g, JComponent c,
+ int x, int y, int w, int h)
+ {
+ if (c.isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(x, y, x, y + h - 1);
+ g.drawLine(x, y, x + w - 1, y);
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
+
+ g.setColor(scrollBarShadowColor);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 1);
+ g.drawLine(x + 1, y + 1, x + w - 2, y + 1);
+
+ if (isFreeStanding)
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(x, y + h - 2, x + w - 1, y + h - 2);
+ g.setColor(scrollBarShadowColor);
+ g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
+ }
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ if (isFreeStanding)
+ g.drawRect(x, y, w - 1, h - 1);
+ else
+ {
+ g.drawLine(x, y, x + w - 1, y);
+ g.drawLine(x, y, x, y + h - 1);
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
+ }
+ }
+ }
+
+ /**
+ * Paints the track for a vertical scrollbar.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ * @param x the x-coordinate for the track bounds.
+ * @param y the y-coordinate for the track bounds.
+ * @param w the width for the track bounds.
+ * @param h the height for the track bounds.
+ */
+ private void paintTrackVertical(Graphics g, JComponent c,
+ int x, int y, int w, int h)
+ {
+ if (c.isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(x, y, x, y + h - 1);
+ g.drawLine(x, y, x + w - 1, y);
+ g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
+
+ g.setColor(scrollBarShadowColor);
+ g.drawLine(x + 1, y + 1, x + w - 1, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 2);
+
+ if (isFreeStanding)
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(x + w - 2, y, x + w - 2, y + h - 1);
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
+ }
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ if (isFreeStanding)
+ g.drawRect(x, y, w - 1, h - 1);
+ else
+ {
+ g.drawLine(x, y, x + w - 1, y);
+ g.drawLine(x, y, x, y + h - 1);
+ g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
+ }
+ }
+ }
+
+ /**
+ * Paints the slider button of the ScrollBar.
+ *
+ * @param g the Graphics context to use
+ * @param c the JComponent on which we paint
+ * @param thumbBounds the rectangle that is the slider button
+ */
+ protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
+ {
+ // a disabled scrollbar has no thumb in the metal look and feel
+ if (!c.isEnabled())
+ return;
+ if (scrollbar.getOrientation() == HORIZONTAL)
+ paintThumbHorizontal(g, c, thumbBounds);
+ else
+ paintThumbVertical(g, c, thumbBounds);
+
+ // Draw the pattern when the theme is not Ocean.
+ if (! (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme))
+ {
+ MetalUtils.fillMetalPattern(c, g, thumbBounds.x + 3, thumbBounds.y + 3,
+ thumbBounds.width - 6,
+ thumbBounds.height - 6,
+ thumbHighlightColor,
+ thumbLightShadowColor);
+ }
+ }
+
+ /**
+ * Paints the thumb for a horizontal scroll bar.
+ *
+ * @param g the graphics device.
+ * @param c the scroll bar component.
+ * @param thumbBounds the thumb bounds.
+ */
+ private void paintThumbHorizontal(Graphics g, JComponent c,
+ Rectangle thumbBounds)
+ {
+ int x = thumbBounds.x;
+ int y = thumbBounds.y;
+ int w = thumbBounds.width;
+ int h = thumbBounds.height;
+
+ // First we fill the background.
+ MetalTheme theme = MetalLookAndFeel.getCurrentTheme();
+ if (theme instanceof OceanTheme
+ && UIManager.get("ScrollBar.gradient") != null)
+ {
+ MetalUtils.paintGradient(g, x + 2, y + 2, w - 4, h - 2,
+ SwingConstants.VERTICAL,
+ "ScrollBar.gradient");
+ }
+ else
+ {
+ g.setColor(thumbColor);
+ if (isFreeStanding)
+ g.fillRect(x, y, w, h - 1);
+ else
+ g.fillRect(x, y, w, h);
+ }
+
+ // then draw the dark box
+ g.setColor(thumbLightShadowColor);
+ if (isFreeStanding)
+ g.drawRect(x, y, w - 1, h - 2);
+ else
+ {
+ g.drawLine(x, y, x + w - 1, y);
+ g.drawLine(x, y, x, y + h - 1);
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
+ }
+
+ // then the highlight
+ g.setColor(thumbHighlightColor);
+ if (isFreeStanding)
+ {
+ g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
+ }
+ else
+ {
+ g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 1);
+ }
+
+ // draw the shadow line
+ g.setColor(UIManager.getColor("ScrollBar.shadow"));
+ g.drawLine(x + w, y + 1, x + w, y + h - 1);
+
+ // For the OceanTheme, draw the 3 lines in the middle.
+ if (theme instanceof OceanTheme)
+ {
+ g.setColor(thumbLightShadowColor);
+ int middle = x + w / 2;
+ g.drawLine(middle - 2, y + 4, middle - 2, y + h - 5);
+ g.drawLine(middle, y + 4, middle, y + h - 5);
+ g.drawLine(middle + 2, y + 4, middle + 2, y + h - 5);
+ g.setColor(UIManager.getColor("ScrollBar.highlight"));
+ g.drawLine(middle - 1, y + 5, middle - 1, y + h - 4);
+ g.drawLine(middle + 1, y + 5, middle + 1, y + h - 4);
+ g.drawLine(middle + 3, y + 5, middle + 3, y + h - 4);
+ }
+ }
+
+ /**
+ * Paints the thumb for a vertical scroll bar.
+ *
+ * @param g the graphics device.
+ * @param c the scroll bar component.
+ * @param thumbBounds the thumb bounds.
+ */
+ private void paintThumbVertical(Graphics g, JComponent c,
+ Rectangle thumbBounds)
+ {
+ int x = thumbBounds.x;
+ int y = thumbBounds.y;
+ int w = thumbBounds.width;
+ int h = thumbBounds.height;
+
+ // First we fill the background.
+ MetalTheme theme = MetalLookAndFeel.getCurrentTheme();
+ if (theme instanceof OceanTheme
+ && UIManager.get("ScrollBar.gradient") != null)
+ {
+ MetalUtils.paintGradient(g, x + 2, y + 2, w - 2, h - 4,
+ SwingConstants.HORIZONTAL,
+ "ScrollBar.gradient");
+ }
+ else
+ {
+ g.setColor(thumbColor);
+ if (isFreeStanding)
+ g.fillRect(x, y, w - 1, h);
+ else
+ g.fillRect(x, y, w, h);
+ }
+
+ // then draw the dark box
+ g.setColor(thumbLightShadowColor);
+ if (isFreeStanding)
+ g.drawRect(x, y, w - 2, h - 1);
+ else
+ {
+ g.drawLine(x, y, x + w - 1, y);
+ g.drawLine(x, y, x, y + h - 1);
+ g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
+ }
+
+ // then the highlight
+ g.setColor(thumbHighlightColor);
+ if (isFreeStanding)
+ {
+ g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
+ }
+ else
+ {
+ g.drawLine(x + 1, y + 1, x + w - 1, y + 1);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 3);
+ }
+
+ // draw the shadow line
+ g.setColor(UIManager.getColor("ScrollBar.shadow"));
+ g.drawLine(x + 1, y + h, x + w - 2, y + h);
+
+ // For the OceanTheme, draw the 3 lines in the middle.
+ if (theme instanceof OceanTheme)
+ {
+ g.setColor(thumbLightShadowColor);
+ int middle = y + h / 2;
+ g.drawLine(x + 4, middle - 2, x + w - 5, middle - 2);
+ g.drawLine(x + 4, middle, x + w - 5, middle);
+ g.drawLine(x + 4, middle + 2, x + w - 5, middle + 2);
+ g.setColor(UIManager.getColor("ScrollBar.highlight"));
+ g.drawLine(x + 5, middle - 1, x + w - 4, middle - 1);
+ g.drawLine(x + 5, middle + 1, x + w - 4, middle + 1);
+ g.drawLine(x + 5, middle + 3, x + w - 4, middle + 3);
+ }
+ }
+
+ /**
+ * Returns the minimum thumb size. For a free standing scroll bar the
+ * minimum size is <code>17 x 17</code> pixels, whereas for a non free
+ * standing scroll bar the minimum size is <code>15 x 15</code> pixels.
+ *
+ * @return The minimum thumb size.
+ */
+ protected Dimension getMinimumThumbSize()
+ {
+ Dimension retVal;
+ if (scrollbar != null)
+ {
+ if (isFreeStanding)
+ retVal = MIN_THUMB_SIZE_FREE_STANDING;
+ else
+ retVal = MIN_THUMB_SIZE;
+ }
+ else
+ retVal = new Dimension(0, 0);
+ return retVal;
+ }
+
+ /**
+ * Returns the <code>preferredSize</code> for the specified scroll bar.
+ * For a vertical scrollbar the height is the sum of the preferred heights
+ * of the buttons plus <code>30</code>. The width is fetched from the
+ * <code>UIManager</code> property <code>ScrollBar.width</code>.
+ *
+ * For horizontal scrollbars the width is the sum of the preferred widths
+ * of the buttons plus <code>30</code>. The height is fetched from the
+ * <code>UIManager</code> property <code>ScrollBar.height</code>.
+ *
+ * @param c the scrollbar for which to calculate the preferred size
+ *
+ * @return the <code>preferredSize</code> for the specified scroll bar
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ int height;
+ int width;
+ height = width = 0;
+
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ {
+ width += incrButton.getPreferredSize().getWidth();
+ width += decrButton.getPreferredSize().getWidth();
+ width += 30;
+ height = UIManager.getInt("ScrollBar.width");
+ }
+ else
+ {
+ height += incrButton.getPreferredSize().getHeight();
+ height += decrButton.getPreferredSize().getHeight();
+ height += 30;
+ width = UIManager.getInt("ScrollBar.width");
+ }
+
+ Insets insets = scrollbar.getInsets();
+
+ height += insets.top + insets.bottom;
+ width += insets.left + insets.right;
+
+ return new Dimension(width, height);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java
new file mode 100644
index 000000000..3273908b3
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java
@@ -0,0 +1,483 @@
+/* MetalScrollButton.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.SwingUtilities;
+import javax.swing.plaf.basic.BasicArrowButton;
+
+/**
+ * A button used by the {@link MetalScrollBarUI}. The button appearance
+ * varies according to the button direction, whether or not it is part of a
+ * "free standing" scroll bar, and the current state of the button.
+ */
+public class MetalScrollButton extends BasicArrowButton
+{
+
+ /**
+ * The maximum size for buttons.
+ * @see #getMaximumSize()
+ */
+ private static Dimension maximumSize;
+
+ /** The width of the button. */
+ private int buttonWidth;
+
+ /**
+ * A flag that indicates whether the button is part of a free standing
+ * scroll bar. This affects how the border is drawn.
+ */
+ private boolean freeStanding;
+
+ /**
+ * Creates a new button.
+ *
+ * @param direction the direction (this should be one of {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} and {@link #WEST}, but
+ * this is not enforced).
+ * @param width the button width.
+ * @param freeStanding a flag indicating whether the scroll button is free
+ * standing or not.
+ */
+ public MetalScrollButton(int direction, int width, boolean freeStanding)
+ {
+ super(direction);
+ buttonWidth = width;
+ this.freeStanding = freeStanding;
+ setFocusable(false);
+ }
+
+ /**
+ * Returns the button width.
+ *
+ * @return The button width.
+ */
+ public int getButtonWidth()
+ {
+ return buttonWidth;
+ }
+
+ /**
+ * Sets the free standing flag. This controls how the button border is
+ * drawn.
+ *
+ * @param freeStanding the new value of the flag.
+ */
+ public void setFreeStanding(boolean freeStanding)
+ {
+ this.freeStanding = freeStanding;
+ }
+
+ /**
+ * Paints the button.
+ *
+ * @param g the graphics device.
+ */
+ public void paint(Graphics g)
+ {
+ Rectangle bounds = SwingUtilities.getLocalBounds(this);
+
+ // fill the background
+ if (getModel().isPressed())
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ else
+ g.setColor(MetalLookAndFeel.getControl());
+ g.fillRect(0, 0, bounds.width, bounds.height);
+
+ paintArrow(g, bounds.width, bounds.height);
+
+ // paint a border manually - I tried using a real (custom) Border
+ // but couldn't get it to stay set for the button, something was
+ // overwriting it...
+ if (freeStanding)
+ {
+ if (direction == WEST)
+ paintWestBorderFreeStanding(g, bounds.width, bounds.height);
+ else if (direction == EAST)
+ paintEastBorderFreeStanding(g, bounds.width, bounds.height);
+ else if (direction == SOUTH)
+ paintSouthBorderFreeStanding(g, bounds.width, bounds.height);
+ else // asume NORTH
+ paintNorthBorderFreeStanding(g, bounds.width, bounds.height);
+ }
+ else
+ {
+ if (direction == WEST)
+ paintWestBorder(g, bounds.width, bounds.height);
+ else if (direction == EAST)
+ paintEastBorder(g, bounds.width, bounds.height);
+ else if (direction == SOUTH)
+ paintSouthBorder(g, bounds.width, bounds.height);
+ else // asume NORTH
+ paintNorthBorder(g, bounds.width, bounds.height);
+ }
+ }
+
+ private void paintArrow(Graphics g, int w, int h)
+ {
+ if (isEnabled())
+ g.setColor(MetalLookAndFeel.getBlack());
+ else
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+
+ if (direction == SOUTH)
+ {
+ int x = w / 2;
+ int y = h / 2 + 2;
+ for (int i = 1; i < 5; i++)
+ g.drawLine(x - i, y - i, x + i - 1, y - i);
+ }
+ else if (direction == EAST)
+ {
+ int x = w / 2 + 2;
+ int y = h / 2;
+ for (int i = 1; i < 5; i++)
+ g.drawLine(x - i, y - i, x - i, y + i - 1);
+ }
+ else if (direction == WEST)
+ {
+ int x = w / 2 - 3;
+ int y = h / 2;
+ for (int i = 1; i < 5; i++)
+ g.drawLine(x + i, y - i, x + i, y + i - 1);
+ }
+ else // assume NORTH
+ {
+ int x = w / 2;
+ int y = h / 2 - 3;
+ for (int i = 1; i < 5; i++)
+ g.drawLine(x - i, y + i, x + i - 1, y + i);
+ }
+ }
+ /**
+ * Paints the border for a button with a {@link #NORTH} direction that
+ * belongs to a free standing scroll bar.
+ *
+ * @param g the graphics device.
+ * @param w the button width.
+ * @param h the button height.
+ */
+ private void paintNorthBorderFreeStanding(Graphics g, int w, int h)
+ {
+ if (isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, w - 2, 0);
+ g.drawLine(0, 0, 0, h - 1);
+ g.drawLine(2, h - 1, w - 2, h - 1);
+ g.drawLine(w - 2, 2, w - 2, h - 1);
+
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(1, 1, 1, h - 2);
+ g.drawLine(1, 1, w - 3, 1);
+ g.drawLine(w - 1, 1, w - 1, h - 1);
+
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(1, h - 1, 1, h - 1);
+ g.drawLine(w - 2, 1, w - 2, 1);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(0, 0, w - 1, 0);
+ g.drawLine(w - 1, 0, w - 1, h - 1);
+ g.drawLine(0, 0, 0, h - 1);
+ }
+ }
+
+ /**
+ * Paints the border for a button with a {@link #SOUTH} direction that
+ * belongs to a free standing scroll bar.
+ *
+ * @param g the graphics device.
+ * @param w the button width.
+ * @param h the button height.
+ */
+ private void paintSouthBorderFreeStanding(Graphics g, int w, int h)
+ {
+ if (isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, w - 2, 0);
+ g.drawLine(0, 0, 0, h - 1);
+ g.drawLine(2, h - 1, w - 2, h - 1);
+ g.drawLine(w - 2, 2, w - 2, h - 1);
+
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(1, 1, 1, h - 1);
+ g.drawLine(1, 1, w - 1, 1);
+ g.drawLine(w - 1, 1, w - 1, h - 1);
+
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(1, h - 1, 1, h - 1);
+ g.drawLine(w - 1, 1, w - 1, 1);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(0, h - 1, w - 1, h - 1);
+ g.drawLine(w - 1, 0, w - 1, h - 1);
+ g.drawLine(0, 0, 0, h - 1);
+ }
+ }
+
+ /**
+ * Paints the border for a button with an {@link #EAST} direction that
+ * belongs to a free standing scroll bar.
+ *
+ * @param g the graphics device.
+ * @param w the button width.
+ * @param h the button height.
+ */
+ private void paintEastBorderFreeStanding(Graphics g, int w, int h)
+ {
+ if (isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, w - 2, 0);
+ g.drawLine(w - 2, 0, w - 2, h - 2);
+ g.drawLine(0, h - 2, w - 2, h - 2);
+
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(0, 1, w - 1, 1);
+ g.drawLine(w - 1, 1, w - 1, h - 1);
+ g.drawLine(0, h - 1, w - 1, h - 1);
+
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(w - 2, 1, w - 2, 1);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(0, 0, w - 1, 0);
+ g.drawLine(w - 1, 0, w - 1, h - 1);
+ g.drawLine(0, h - 1, w - 1, h - 1);
+ }
+ }
+
+ /**
+ * Paints the border for a button with a {@link #WEST} direction that
+ * belongs to a free standing scroll bar.
+ *
+ * @param g the graphics device.
+ * @param w the button width.
+ * @param h the button height.
+ */
+ private void paintWestBorderFreeStanding(Graphics g, int w, int h)
+ {
+ if (isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, w - 1, 0);
+ g.drawLine(0, 0, 0, h - 2);
+ g.drawLine(0, h - 2, w - 1, h - 2);
+
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(1, 1, w - 1, 1);
+ g.drawLine(1, 1, 1, h - 1);
+ g.drawLine(1, h - 1, w - 1, h - 1);
+
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(1, h - 2, 1, h - 2);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(0, 0, w - 1, 0);
+ g.drawLine(0, 0, 0, h - 1);
+ g.drawLine(0, h - 1, w - 1, h - 1);
+ }
+ }
+
+ /**
+ * Paints the border for a button with a {@link #NORTH} direction that
+ * belongs to a scroll bar that is not free standing.
+ *
+ * @param g the graphics device.
+ * @param w the button width.
+ * @param h the button height.
+ */
+ private void paintNorthBorder(Graphics g, int w, int h)
+ {
+ if (isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, 0, h - 1);
+
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(1, 0, 1, h - 1);
+ g.drawLine(1, 0, w - 1, 0);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(0, 0, 0, h - 1);
+ }
+ }
+
+ /**
+ * Paints the border for a button with a {@link #SOUTH} direction that
+ * belongs to a scroll bar that is not free standing.
+ *
+ * @param g the graphics device.
+ * @param w the button width.
+ * @param h the button height.
+ */
+ private void paintSouthBorder(Graphics g, int w, int h)
+ {
+ if (isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, 0, h - 1);
+ g.drawLine(0, h - 1, w - 1, h - 1);
+
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(1, 0, 1, h - 1);
+ g.drawLine(1, 0, w - 1, 0);
+
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(1, h - 1, 1, h - 1);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(0, 0, 0, h - 1);
+ }
+ }
+
+ /**
+ * Paints the border for a button with an {@link #EAST} direction that
+ * belongs to a scroll bar that is not free standing.
+ *
+ * @param g the graphics device.
+ * @param w the button width.
+ * @param h the button height.
+ */
+ private void paintEastBorder(Graphics g, int w, int h)
+ {
+ if (isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, w - 1, 0);
+ g.drawLine(w - 1, 2, w - 1, h - 1);
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(0, 1, w - 2, 1);
+ g.drawLine(0, 1, 0, h - 1);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(0, 0, w - 1, 0);
+ }
+ }
+
+ /**
+ * Paints the border for a button with a {@link #WEST} direction that
+ * belongs to a scroll bar that is not free standing.
+ *
+ * @param g the graphics device.
+ * @param w the button width.
+ * @param h the button height.
+ */
+ private void paintWestBorder(Graphics g, int w, int h)
+ {
+ Rectangle bounds = SwingUtilities.getLocalBounds(this);
+ if (isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControlDarkShadow());
+ g.drawLine(0, 0, bounds.width - 1, 0);
+ g.setColor(MetalLookAndFeel.getControlHighlight());
+ g.drawLine(0, 1, bounds.width - 1, 1);
+ g.drawLine(0, 1, 0, bounds.height - 1);
+ }
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlDisabled());
+ g.drawLine(0, 0, bounds.width - 1, 0);
+ }
+ }
+
+ /**
+ * Returns the preferred size for the button, which varies depending on
+ * the direction of the button and whether or not it is free standing.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize()
+ {
+ int adj = 1;
+ if (!freeStanding)
+ adj = 2;
+
+ if (direction == EAST)
+ return new Dimension(buttonWidth - adj, buttonWidth);
+ else if (direction == WEST)
+ return new Dimension(buttonWidth - 2, buttonWidth);
+ else if (direction == SOUTH)
+ return new Dimension(buttonWidth, buttonWidth - adj);
+ else // assume NORTH
+ return new Dimension(buttonWidth, buttonWidth - 2);
+ }
+
+ /**
+ * Returns the minimum size for the button.
+ *
+ * @return The minimum size for the button.
+ */
+ public Dimension getMinimumSize()
+ {
+ return getPreferredSize();
+ }
+
+ /**
+ * Returns the maximum size for the button.
+ *
+ * @return <code>Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)</code>.
+ */
+ public Dimension getMaximumSize()
+ {
+ if (maximumSize == null)
+ maximumSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ return maximumSize;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java
new file mode 100644
index 000000000..09a47c300
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java
@@ -0,0 +1,159 @@
+/* MetalScrollPaneUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JComponent;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicScrollPaneUI;
+
+/**
+ * A UI delegate for the {@link JScrollPane} component.
+ */
+public class MetalScrollPaneUI
+ extends BasicScrollPaneUI
+{
+ /**
+ * Constructs a new instance of <code>MetalScrollPaneUI</code>.
+ */
+ public MetalScrollPaneUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a shared instance of <code>MetalScrollPaneUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A shared instance of <code>MetalScrollPaneUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalScrollPaneUI();
+ }
+
+ /**
+ * Configures the specified component appropriate for the look and feel.
+ * This method is invoked when the ComponentUI instance is being installed
+ * as the UI delegate on the specified component. This method should
+ * completely configure the component for the look and feel,
+ * including the following:
+ * 1. Install any default property values for color, fonts, borders,
+ * icons, opacity, etc. on the component. Whenever possible, property
+ * values initialized by the client program should not be overridden.
+ * 2. Install a LayoutManager on the component if necessary.
+ * 3. Create/add any required sub-components to the component.
+ * 4. Create/install event listeners on the component.
+ * 5. Create/install a PropertyChangeListener on the component in order
+ * to detect and respond to component property changes appropriately.
+ * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component.
+ * 7. Initialize any appropriate instance data.
+ *
+ * @param c - the component to install the ui on
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ JScrollBar hsb = scrollpane.getHorizontalScrollBar();
+ hsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE);
+ JScrollBar vsb = scrollpane.getVerticalScrollBar();
+ vsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, Boolean.FALSE);
+ }
+
+ /**
+ * Reverses configuration which was done on the specified component
+ * during installUI. This method is invoked when this UIComponent
+ * instance is being removed as the UI delegate for the specified
+ * component. This method should undo the configuration performed in
+ * installUI, being careful to leave the JComponent instance in a
+ * clean state (no extraneous listeners, look-and-feel-specific property
+ * objects, etc.). This should include the following:
+ * 1. Remove any UI-set borders from the component.
+ * 2. Remove any UI-set layout managers on the component.
+ * 3. Remove any UI-added sub-components from the component.
+ * 4. Remove any UI-added event/property listeners from the component.
+ * 5. Remove any UI-installed keyboard UI from the component.
+ * 6. Nullify any allocated instance data objects to allow for GC.
+ *
+ * @param c - the component to uninstall the ui on
+ */
+ public void uninstallUI(JComponent c)
+ {
+ JScrollBar hsb = scrollpane.getHorizontalScrollBar();
+ hsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, null);
+ JScrollBar vsb = scrollpane.getVerticalScrollBar();
+ vsb.putClientProperty(MetalScrollBarUI.FREE_STANDING_PROP, null);
+ super.uninstallUI(c);
+ }
+
+ /**
+ * Installs listeners on scrollPane
+ *
+ * @param scrollPane - the component to install the listeners on
+ */
+ public void installListeners(JScrollPane scrollPane)
+ {
+ super.installListeners(scrollPane);
+ }
+
+ /**
+ * Uninstalls listeners on scrollPane
+ *
+ * @param scrollPane - the component to uninstall the listeners on
+ */
+ public void uninstallListeners(JScrollPane scrollPane)
+ {
+ super.uninstallListeners(scrollPane);
+ }
+
+ /**
+ * TODO
+ *
+ * @return TODO
+ */
+ protected PropertyChangeListener createScrollBarSwapListener()
+ {
+ // FIXME: Anything else to do here?
+ return super.createPropertyChangeListener();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java
new file mode 100644
index 000000000..8afa98c97
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java
@@ -0,0 +1,131 @@
+/* MetalSeparatorUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.JComponent;
+import javax.swing.JSeparator;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicSeparatorUI;
+
+/**
+ * A UI delegate for the {@link JSeparator} component.
+ */
+public class MetalSeparatorUI
+ extends BasicSeparatorUI
+{
+
+ // FIXME: maybe replace by a Map of instances when this becomes stateful
+ /** The shared UI instance for MetalSeparatorUIs */
+ private static MetalSeparatorUI instance;
+
+ /**
+ * Constructs a new instance of <code>MetalSeparatorUI</code>.
+ */
+ public MetalSeparatorUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a shared instance of <code>MetalSeparatorUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A shared instance of <code>MetalSeparatorUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ if (instance == null)
+ instance = new MetalSeparatorUI();
+ return instance;
+ }
+
+ /**
+ * The separator is made of two lines. The top line will be
+ * the Metal theme color separatorForeground (or left line if it's vertical).
+ * The bottom or right line will be the Metal theme color
+ * separatorBackground.
+ * The two lines will
+ * be centered inside the bounds box. If the separator is horizontal,
+ * then it will be vertically centered, or if it's vertical, it will
+ * be horizontally centered.
+ *
+ * @param g The Graphics object to paint with
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Rectangle r = new Rectangle();
+ SwingUtilities.calculateInnerArea(c, r);
+ Color saved = g.getColor();
+ Color c1 = UIManager.getColor("Separator.foreground");
+ Color c2 = UIManager.getColor("Separator.background");
+ JSeparator s;
+ if (c instanceof JSeparator)
+ s = (JSeparator) c;
+ else
+ return;
+
+ if (s.getOrientation() == JSeparator.HORIZONTAL)
+ {
+ int midAB = r.height / 2;
+ g.setColor(c1);
+ g.drawLine(r.x, r.y + midAB - 1, r.x + r.width, r.y + midAB - 1);
+
+ g.setColor(c2);
+ g.fillRect(r.x, r.y + midAB, r.x + r.width, r.y + midAB);
+ }
+ else
+ {
+ int midAD = r.height / 2 + r.y;
+ g.setColor(c1);
+ g.drawLine(r.x, r.y, r.x, r.y + r.height);
+
+ g.setColor(c2);
+ g.fillRect(r.x + midAD, r.y + r.height, r.x + midAD, r.y + r.height);
+ }
+ g.setColor(saved);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java
new file mode 100644
index 000000000..e85c32462
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java
@@ -0,0 +1,475 @@
+/* MetalSliderUI.java
+ Copyright (C) 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.JSlider;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicGraphicsUtils;
+import javax.swing.plaf.basic.BasicSliderUI;
+
+/**
+ * A UI delegate for the {@link JSlider} component.
+ */
+public class MetalSliderUI extends BasicSliderUI
+{
+ /**
+ * A property change handler that updates the rendered component in response
+ * to specific property change events. This custom handler is used to
+ * intercept the "JSlider.isFilled" property, which is only recognised by
+ * the {@link MetalLookAndFeel}.
+ */
+ protected class MetalPropertyListener
+ extends BasicSliderUI.PropertyChangeHandler
+ {
+ /**
+ * Creates a new listener.
+ */
+ protected MetalPropertyListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Handles property change events. Events with the name "JSlider.isFilled"
+ * are handled here, and other events are passed to the superclass.
+ *
+ * @param e the property change event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(SLIDER_FILL))
+ {
+ Boolean b = (Boolean) e.getNewValue();
+ if (b == null)
+ filledSlider = false;
+ else
+ filledSlider = b.booleanValue();
+ }
+ else
+ super.propertyChange(e);
+ }
+ }
+
+ /** The thumb color (unused, because an icon is used to draw the thumb). */
+ protected static Color thumbColor;
+
+ /**
+ * The highlight color used for drawing the track rect when the slider is
+ * enabled.
+ */
+ protected static Color highlightColor;
+
+ /**
+ * The shadow color used for drawing the track rect when the slider is
+ * enabled.
+ */
+ protected static Color darkShadowColor;
+
+ /** The track width. */
+ protected static int trackWidth = UIManager.getInt("Slider.trackWidth");
+
+ /** The length of the major tick marks. */
+ protected static int tickLength = UIManager.getInt("Slider.majorTickLength");
+
+ /** The icon used for the thumb control of horizontally oriented sliders. */
+ protected static Icon horizThumbIcon = UIManager.getIcon(
+ "Slider.horizontalThumbIcon");
+
+ /** The icon used for the thumb control of vertically oriented sliders. */
+ protected static Icon vertThumbIcon = UIManager.getIcon(
+ "Slider.verticalThumbIcon");
+
+ /** The gap between the track and the tick marks. */
+ protected final int TICK_BUFFER = 4;
+
+ /** A key to look up the filledSlider setting in the {@link UIManager}. */
+ protected final String SLIDER_FILL = "JSlider.isFilled";
+
+ /**
+ * A flag that controls whether or not the track is filled up to the value
+ * of the slider.
+ */
+ protected boolean filledSlider;
+
+ /**
+ * Constructs a new instance.
+ */
+ public MetalSliderUI()
+ {
+ super(null);
+ filledSlider = UIManager.getBoolean(SLIDER_FILL);
+ darkShadowColor = MetalLookAndFeel.getControlDarkShadow();
+ highlightColor = MetalLookAndFeel.getControlHighlight();
+ }
+
+ /**
+ * Returns a new instance of <code>MetalSliderUI</code>.
+ *
+ * @param component the component (ignored).
+ *
+ * @return A new instance of <code>MetalSliderUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalSliderUI();
+ }
+
+ /**
+ * Installs the default for this UI delegate in the supplied component.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ Boolean b = (Boolean) c.getClientProperty(SLIDER_FILL);
+ if (b != null)
+ filledSlider = b.booleanValue();
+ }
+
+ /**
+ * Creates a property change listener for the slider.
+ *
+ * @param slider the slider.
+ *
+ * @return A new instance of {@link MetalPropertyListener}.
+ */
+ protected PropertyChangeListener createPropertyChangeListener(JSlider slider)
+ {
+ return new MetalPropertyListener();
+ }
+
+ /**
+ * Paints the thumb icon for the slider.
+ *
+ * @param g the graphics device.
+ */
+ public void paintThumb(Graphics g)
+ {
+ Color save = g.getColor();
+ g.setColor(thumbColor);
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ horizThumbIcon.paintIcon(slider, g, thumbRect.x, thumbRect.y);
+ else
+ vertThumbIcon.paintIcon(slider, g, thumbRect.x, thumbRect.y);
+ g.setColor(save);
+ }
+
+ /**
+ * Paints the track along which the thumb control moves.
+ *
+ * @param g the graphics device.
+ */
+ public void paintTrack(Graphics g)
+ {
+ Color shadowColor = MetalLookAndFeel.getControlShadow();
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ int trackX = trackRect.x;
+ int trackY = trackRect.y + (trackRect.height - getTrackWidth()) / 2;
+ int trackW = trackRect.width;
+ int trackH = getTrackWidth();
+
+ // draw border
+ if (slider.isEnabled())
+ BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH,
+ darkShadowColor, shadowColor, darkShadowColor, highlightColor);
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawRect(trackX, trackY, trackW - 2, trackH - 2);
+ }
+
+ // fill track (if required)
+ if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
+ {
+ if (slider.isEnabled())
+ {
+ int xPos = xPositionForValue(slider.getValue());
+ int x = slider.getInverted() ? xPos : trackRect.x;
+ int w = slider.getInverted() ? trackX + trackW - xPos
+ : xPos - trackRect.x;
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x + 1, trackY + 1, x + w - 3, trackY + 1);
+ g.setColor(UIManager.getColor("Slider.altTrackColor"));
+ g.drawLine(x + 1, trackY + 2, x + w - 3, trackY + 2);
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x + 1, trackY + 3, x + w - 3, trackY + 3);
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ g.drawLine(x + 1, trackY + 4, x + w - 3, trackY + 4);
+ }
+ }
+ else if (filledSlider)
+ {
+ int xPos = xPositionForValue(slider.getValue());
+ int x = slider.getInverted() ? xPos : trackRect.x;
+ int w = slider.getInverted() ? trackX + trackW - xPos
+ : xPos - trackRect.x;
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.fillRect(x + 1, trackY + 1, w - 3, getTrackWidth() - 3);
+ if (slider.isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(x + 1, trackY + 1, x + w - 3, trackY + 1);
+ g.drawLine(x + 1, trackY + 1, x + 1,
+ trackY + getTrackWidth() - 3);
+ }
+ }
+ }
+ else
+ {
+ int trackX = trackRect.x + (trackRect.width - getTrackWidth()) / 2;
+ int trackY = trackRect.y;
+ int trackW = getTrackWidth();
+ int trackH = trackRect.height;
+ if (slider.isEnabled())
+ BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH,
+ darkShadowColor, shadowColor, darkShadowColor, highlightColor);
+ else
+ {
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawRect(trackX, trackY, trackW - 2, trackH - 2);
+ }
+
+ // Fill track if necessary.
+ if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme)
+ {
+ if (slider.isEnabled())
+ {
+ int yPos = yPositionForValue(slider.getValue());
+ int y = slider.getInverted() ? trackY : yPos;
+ int h = slider.getInverted() ? yPos - trackY
+ : trackY + trackH - yPos;
+
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(trackX + 1, y + 1, trackX + 1, y + h - 3);
+ g.setColor(UIManager.getColor("Slider.altTrackColor"));
+ g.drawLine(trackX + 2, y + 1, trackX + 2, y + h - 3);
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(trackX + 3, y + 1, trackX + 3, y + h - 3);
+ g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
+ g.drawLine(trackX + 4, y + 1, trackX + 4, y + h - 3);
+ }
+ }
+ else if (filledSlider)
+ {
+ int yPos = yPositionForValue(slider.getValue());
+ int y = slider.getInverted() ? trackY : yPos;
+ int h = slider.getInverted() ? yPos - trackY
+ : trackY + trackH - yPos;
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.fillRect(trackX + 1, y + 1, getTrackWidth() - 3, h - 3);
+ if (slider.isEnabled())
+ {
+ g.setColor(MetalLookAndFeel.getControl());
+ g.drawLine(trackX + 1, y + 1, trackX + trackW - 3, y + 1);
+ g.drawLine(trackX + 1, y + 1, trackX + 1, y + h - 3);
+ }
+ }
+ }
+ }
+
+ /**
+ * Draws the focus rectangle for the slider. The Metal look and feel
+ * indicates that the {@link JSlider} has the focus by changing the color of
+ * the thumb control - this is handled elsewhere and so this method is empty
+ * (it overrides the method in the {@link BasicSliderUI} class to prevent
+ * a default focus highlight from being drawn).
+ *
+ * @param g the graphics device.
+ */
+ public void paintFocus(Graphics g)
+ {
+ thumbColor = getFocusColor();
+ paintThumb(g);
+ }
+
+ /**
+ * Returns the size of the thumb icon.
+ *
+ * @return The size of the thumb icon.
+ */
+ protected Dimension getThumbSize()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ return new Dimension(horizThumbIcon.getIconWidth(),
+ horizThumbIcon.getIconHeight());
+ else
+ return new Dimension(vertThumbIcon.getIconWidth(),
+ vertThumbIcon.getIconHeight());
+ }
+
+ /**
+ * Returns the length of the major tick marks.
+ *
+ * @return The length of the major tick marks.
+ */
+ public int getTickLength()
+ {
+ int len = tickLength + TICK_BUFFER + 1;
+ if (slider.getOrientation() == JSlider.VERTICAL)
+ len += 2;
+ return len;
+ }
+
+ /**
+ * Returns the track width.
+ *
+ * @return The track width.
+ */
+ protected int getTrackWidth()
+ {
+ return trackWidth;
+ }
+
+ /**
+ * Returns the track length.
+ *
+ * @return The track length.
+ */
+ protected int getTrackLength()
+ {
+ return slider.getOrientation() == JSlider.HORIZONTAL
+ ? tickRect.width : tickRect.height;
+ }
+
+ /**
+ * Returns the thumb overhang.
+ *
+ * @return The thumb overhang.
+ */
+ protected int getThumbOverhang()
+ {
+ // FIXME: for what might this method be used?
+ return 0;
+ }
+
+ protected void scrollDueToClickInTrack(int dir)
+ {
+ // FIXME: for what might this method be overridden?
+ super.scrollDueToClickInTrack(dir);
+ }
+
+ /**
+ * Paints the minor ticks for a slider with a horizontal orientation.
+ *
+ * @param g the graphics device.
+ * @param tickBounds the tick bounds.
+ * @param x the x value for the tick.
+ */
+ protected void paintMinorTickForHorizSlider(Graphics g, Rectangle tickBounds,
+ int x)
+ {
+ // Note the incoming 'g' has a translation in place to get us to the
+ // start of the tick rect already...
+ if (slider.isEnabled())
+ g.setColor(slider.getForeground());
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2);
+ }
+
+ /**
+ * Paints the major ticks for a slider with a horizontal orientation.
+ *
+ * @param g the graphics device.
+ * @param tickBounds the tick bounds.
+ * @param x the x value for the tick.
+ */
+ protected void paintMajorTickForHorizSlider(Graphics g, Rectangle tickBounds,
+ int x)
+ {
+ // Note the incoming 'g' has a translation in place to get us to the
+ // start of the tick rect already...
+ if (slider.isEnabled())
+ g.setColor(slider.getForeground());
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength - 1);
+ }
+
+ /**
+ * Paints the minor ticks for a slider with a vertical orientation.
+ *
+ * @param g the graphics device.
+ * @param tickBounds the tick bounds.
+ * @param y the y value for the tick.
+ */
+ protected void paintMinorTickForVertSlider(Graphics g, Rectangle tickBounds,
+ int y)
+ {
+ // Note the incoming 'g' has a translation in place to get us to the
+ // start of the tick rect already...
+ if (slider.isEnabled())
+ g.setColor(slider.getForeground());
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(TICK_BUFFER, y, TICK_BUFFER + tickLength / 2, y);
+ }
+
+ /**
+ * Paints the major ticks for a slider with a vertical orientation.
+ *
+ * @param g the graphics device.
+ * @param tickBounds the tick bounds.
+ * @param y the y value for the tick.
+ */
+ protected void paintMajorTickForVertSlider(Graphics g, Rectangle tickBounds,
+ int y)
+ {
+ // Note the incoming 'g' has a translation in place to get us to the
+ // start of the tick rect already...
+ if (slider.isEnabled())
+ g.setColor(slider.getForeground());
+ else
+ g.setColor(MetalLookAndFeel.getControlShadow());
+ g.drawLine(TICK_BUFFER, y, TICK_BUFFER + tickLength, y);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java
new file mode 100644
index 000000000..051499834
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java
@@ -0,0 +1,264 @@
+/* MetalSplitPaneDivider.java
+Copyright (C) 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.JButton;
+import javax.swing.JSplitPane;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.plaf.basic.BasicSplitPaneDivider;
+
+/**
+ * The divider that is used by the {@link MetalSplitPaneUI}.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+class MetalSplitPaneDivider extends BasicSplitPaneDivider
+{
+ /**
+ * The button pixel data, as indices into the colors array below.
+ * This is the version for 'left' buttons.
+ *
+ * This is slightly different from the icon in Sun's version, it is
+ * one pixel smaller and is more consistent with BUTTON_SPRITE_R.
+ */
+ static final byte[][] BUTTON_SPRITE_L = {{ 0, 0, 0, 2, 0, 0, 0, 0 },
+ { 0, 0, 2, 1, 1, 0, 0, 0 },
+ { 0, 2, 1, 1, 1, 1, 0, 0 },
+ { 2, 1, 1, 1, 1, 1, 1, 0 },
+ { 0, 3, 3, 3, 3, 3, 3, 3 }};
+
+ /**
+ * The button pixel data, as indices into the colors array below.
+ * This is the version for 'right' buttons.
+ */
+ static final byte[][] BUTTON_SPRITE_R = {{ 2, 2, 2, 2, 2, 2, 2, 2 },
+ { 0, 1, 1, 1, 1, 1, 1, 3 },
+ { 0, 0, 1, 1, 1, 1, 3, 0 },
+ { 0, 0, 0, 1, 1, 3, 0, 0 },
+ { 0, 0, 0, 0, 3, 0, 0, 0 }};
+
+ private class MetalOneTouchButton
+ extends JButton
+ {
+ /**
+ * Denotes a left button.
+ */
+ static final int LEFT = 0;
+
+ /**
+ * Denotes a right button.
+ */
+ static final int RIGHT = 1;
+
+ /**
+ * The colors for the button sprite.
+ */
+ private Color[] colors;
+
+ /**
+ * Either LEFT or RIGHT.
+ */
+ private int direction;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param dir either LEFT or RIGHT
+ */
+ MetalOneTouchButton(int dir)
+ {
+ direction = dir;
+ colors = new Color[4];
+ }
+
+ /**
+ * Never allow borders.
+ */
+ public void setBorder(Border b)
+ {
+ }
+
+ /**
+ * Never allow focus traversal.
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Paints the one touch button.
+ */
+ public void paint(Graphics g)
+ {
+ if (splitPane != null)
+ {
+ // Update colors here to reflect dynamic changes to the theme.
+ colors[0] = getBackground();
+ colors[1] = MetalLookAndFeel.getPrimaryControlDarkShadow();
+ colors[2] = MetalLookAndFeel.getPrimaryControlInfo();
+ colors[3] = MetalLookAndFeel.getPrimaryControlHighlight();
+
+ // Fill background.
+ g.setColor(getBackground());
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ // Pressed buttons have slightly different color mapping.
+ if (getModel().isPressed())
+ colors[1] = colors[2];
+
+ byte[][] sprite;
+ if (direction == LEFT)
+ sprite = BUTTON_SPRITE_L;
+ else
+ sprite = BUTTON_SPRITE_R;
+
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ // Draw the sprite as it is.
+ for (int y = 0; y < sprite.length; y++)
+ {
+ byte[] line = sprite[y];
+ for (int x = 0; x < line.length; x++)
+ {
+ int c = line[x];
+ if (c != 0)
+ {
+ g.setColor(colors[c]);
+ g.fillRect(x + 1, y + 1, 1, 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Draw the sprite with swapped X and Y axis.
+ for (int y = 0; y < sprite.length; y++)
+ {
+ byte[] line = sprite[y];
+ for (int x = 0; x < line.length; x++)
+ {
+ int c = line[x];
+ if (c != 0)
+ {
+ g.setColor(colors[c]);
+ g.fillRect(y + 1, x + 1, 1, 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /** The dark color in the pattern. */
+ Color dark;
+
+ /** The light color in the pattern. */
+ Color light;
+
+ /** The JSplitPane the divider is on. */
+ JSplitPane splitPane;
+
+ /** The split pane orientation. */
+ int orientation;
+
+ /**
+ * Creates a new instance of <code>MetalSplitPaneDivider</code>.
+ *
+ * @param ui the <code>MetalSplitPaneUI</code> that uses this divider
+ */
+ public MetalSplitPaneDivider(MetalSplitPaneUI ui, Color light, Color dark)
+ {
+ super(ui);
+ this.splitPane = super.splitPane;
+ this.orientation = super.orientation;
+ this.light = light;
+ this.dark = dark;
+ }
+
+ /**
+ * Paints the divider.
+ *
+ * @param g the <code>Graphics</code> context to use for painting
+ */
+ public void paint(Graphics g)
+ {
+ Dimension s = getSize();
+
+ if (splitPane.hasFocus())
+ {
+ g.setColor(UIManager.getColor("SplitPane.dividerFocusColor"));
+ g.fillRect(0, 0, s.width, s.height);
+ }
+
+ // Paint border if one exists.
+ Border border = getBorder();
+ if (border != null)
+ border.paintBorder(this, g, 0, 0, s.width, s.height);
+
+ Insets i = getInsets();
+ MetalUtils.fillMetalPattern(splitPane, g, i.left + 2, i.top + 2,
+ s.width - i.left - i.right - 4,
+ s.height - i.top - i.bottom - 4,
+ light, dark);
+ super.paint(g);
+ }
+
+ protected JButton createLeftOneTouchButton()
+ {
+ JButton b = new MetalOneTouchButton(MetalOneTouchButton.LEFT);
+ b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ b.setRequestFocusEnabled(false);
+ return b;
+ }
+
+ protected JButton createRightOneTouchButton()
+ {
+ JButton b = new MetalOneTouchButton(MetalOneTouchButton.RIGHT);
+ b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ b.setRequestFocusEnabled(false);
+ return b;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java
new file mode 100644
index 000000000..5b8f2127e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java
@@ -0,0 +1,89 @@
+/* MetalSplitPaneUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+
+import javax.swing.JComponent;
+import javax.swing.JSplitPane;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicSplitPaneDivider;
+import javax.swing.plaf.basic.BasicSplitPaneUI;
+
+/**
+ * A UI delegate for the {@link JSplitPane} component.
+ */
+public class MetalSplitPaneUI extends BasicSplitPaneUI
+{
+ /**
+ * Constructs a new instance of <code>MetalSplitPaneUI</code>.
+ */
+ public MetalSplitPaneUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a new instance of <code>MetalSplitPaneUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A new instance of <code>MetalSplitPaneUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalSplitPaneUI();
+ }
+
+ /**
+ * Returns the divider that is used by the <code>JSplitPane</code>.
+ *
+ * The divider returned by this method is a {@link BasicSplitPaneDivider}
+ * that is drawn using the Metal look.
+ *
+ * @return the default divider to use for <code>JSplitPane</code>s.
+ */
+ public BasicSplitPaneDivider createDefaultDivider()
+ {
+ Color light = UIManager.getColor("SplitPane.highlight");
+ Color dark = UIManager.getColor("SplitPane.darkShadow");
+ return new MetalSplitPaneDivider(this, light, dark);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java
new file mode 100644
index 000000000..77705ba0e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java
@@ -0,0 +1,1269 @@
+/* MetalTabbedPaneUI.java
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+
+import javax.swing.JComponent;
+import javax.swing.JTabbedPane;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicTabbedPaneUI;
+
+/**
+ * A UI delegate for the {@link JTabbedPane} component.
+ */
+public class MetalTabbedPaneUI extends BasicTabbedPaneUI
+{
+
+ /**
+ * A {@link LayoutManager} responsible for placing all the tabs and the
+ * visible component inside the {@link JTabbedPane}. This class is only used
+ * for {@link JTabbedPane#WRAP_TAB_LAYOUT}.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TabbedPaneLayout
+ extends BasicTabbedPaneUI.TabbedPaneLayout
+ {
+ /**
+ * Creates a new instance of the layout manager.
+ */
+ public TabbedPaneLayout()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Overridden to do nothing, because tab runs are not rotated in the
+ * {@link MetalLookAndFeel}.
+ *
+ * @param tabPlacement the tab placement (one of {@link #TOP},
+ * {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
+ * @param selectedRun the index of the selected run.
+ */
+ protected void rotateTabRuns(int tabPlacement, int selectedRun)
+ {
+ // do nothing, because tab runs are not rotated in the MetalLookAndFeel
+ }
+
+ /**
+ * Overridden to do nothing, because the selected tab does not have extra
+ * padding in the {@link MetalLookAndFeel}.
+ *
+ * @param tabPlacement the tab placement (one of {@link #TOP},
+ * {@link #BOTTOM}, {@link #LEFT} or {@link #RIGHT}).
+ * @param selectedIndex the index of the selected tab.
+ */
+ protected void padSelectedTab(int tabPlacement, int selectedIndex)
+ {
+ // do nothing, because the selected tab does not have extra padding in
+ // the MetalLookAndFeel
+ }
+
+ /**
+ * Overridden because tab runs are only normalized for TOP and BOTTOM
+ * tab placement in the Metal L&F.
+ */
+ protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
+ int max)
+ {
+ if (tabPlacement == TOP || tabPlacement == BOTTOM)
+ super.normalizeTabRuns(tabPlacement, tabCount, start, max);
+ }
+ }
+
+ /**
+ * The minimum tab width.
+ */
+ protected int minTabWidth;
+
+ /**
+ * The color for the selected tab.
+ */
+ protected Color selectColor;
+
+ /**
+ * The color for a highlighted selected tab.
+ */
+ protected Color selectHighlight;
+
+ /**
+ * The background color used for the tab area.
+ */
+ protected Color tabAreaBackground;
+
+ /** The graphics to draw the highlight below the tab. */
+ private Graphics hg;
+
+ /**
+ * Indicates if the tabs are having their background filled.
+ */
+ private boolean tabsOpaque;
+
+ /**
+ * Constructs a new instance of MetalTabbedPaneUI.
+ */
+ public MetalTabbedPaneUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns an instance of MetalTabbedPaneUI.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return an instance of MetalTabbedPaneUI
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalTabbedPaneUI();
+ }
+
+ /**
+ * Creates and returns an instance of {@link TabbedPaneLayout}.
+ *
+ * @return A layout manager used by this UI delegate.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
+ ? new MetalTabbedPaneUI.TabbedPaneLayout()
+ : super.createLayoutManager();
+ }
+
+ /**
+ * Paints the border for a single tab.
+ *
+ * @param g the graphics device.
+ * @param tabPlacement the tab placement ({@link #TOP}, {@link #LEFT},
+ * {@link #BOTTOM} or {@link #RIGHT}).
+ * @param tabIndex the index of the tab to draw the border for.
+ * @param x the x-coordinate for the tab's bounding rectangle.
+ * @param y the y-coordinate for the tab's bounding rectangle.
+ * @param w the width for the tab's bounding rectangle.
+ * @param h the height for the tab's bounding rectangle.
+ * @param isSelected indicates whether or not the tab is selected.
+ */
+ protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
+ int x, int y, int w, int h, boolean isSelected)
+ {
+ int bottom = y + h - 1;
+ int right = x + w - 1;
+
+ switch (tabPlacement)
+ {
+ case LEFT:
+ paintLeftTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
+ break;
+ case BOTTOM:
+ paintBottomTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
+ break;
+ case RIGHT:
+ paintRightTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
+ break;
+ case TOP:
+ default:
+ paintTopTabBorder(tabIndex, g, x, y, w, h, bottom, right, isSelected);
+ }
+ }
+
+ /**
+ * Paints the border for a tab assuming that the tab position is at the top
+ * ({@link #TOP}).
+ *
+ * @param tabIndex the tab index.
+ * @param g the graphics device.
+ * @param x the x-coordinate for the tab's bounding rectangle.
+ * @param y the y-coordinate for the tab's bounding rectangle.
+ * @param w the width for the tab's bounding rectangle.
+ * @param h the height for the tab's bounding rectangle.
+ * @param btm the y coordinate of the bottom border
+ * @param rght the x coordinate of the right border
+ * @param isSelected indicates whether the tab is selected.
+ */
+ protected void paintTopTabBorder(int tabIndex, Graphics g, int x, int y,
+ int w, int h, int btm, int rght, boolean isSelected)
+ {
+ int tabCount = tabPane.getTabCount();
+ int currentRun = getRunForTab(tabCount, tabIndex);
+ int right = w - 1;
+ int bottom = h - 1;
+
+ // Paint gap.
+ if (shouldFillGap(currentRun, tabIndex, x, y))
+ {
+ g.translate(x, y);
+ g.setColor(getColorForGap(currentRun, x, y + 1));
+ g.fillRect(1, 0, 5, 3);
+ g.fillRect(1, 3, 2, 2);
+ g.translate(-x, -y);
+ }
+
+ g.translate(x, y);
+
+ boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
+ Color oceanSelectedBorder =
+ UIManager.getColor("TabbedPane.borderHightlightColor");
+ if (isOcean && isSelected)
+ g.setColor(oceanSelectedBorder);
+ else
+ g.setColor(darkShadow);
+
+ // Slant
+ g.drawLine(1, 5, 6, 0);
+ // Top.
+ g.drawLine(6, 0, right, 0);
+ // Right.
+ int lastIndex = lastTabInRun(tabCount, currentRun);
+ if (tabIndex == lastIndex)
+ g.drawLine(right, 1, right, bottom);
+ // Left.
+ int selectedIndex = tabPane.getSelectedIndex();
+ if (isOcean && tabIndex - 1 == selectedIndex
+ && currentRun == getRunForTab(tabCount, selectedIndex))
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ if (tabIndex != tabRuns[runCount - 1])
+ {
+ if (isOcean && isSelected)
+ {
+ g.drawLine(0, 6, 0, bottom);
+ g.setColor(darkShadow);
+ g.drawLine(0, 0, 0, 5);
+ }
+ else
+ {
+ g.drawLine(0, 0, 0, bottom);
+ }
+ }
+ else
+ {
+ g.drawLine(0, 6, 0, bottom);
+ }
+
+ // Paint the highlight.
+ g.setColor(isSelected ? selectHighlight : highlight);
+ // Slant.
+ g.drawLine(1, 6, 6, 1);
+ // Top.
+ g.drawLine(6, 1, right, 1);
+ // Left.
+ g.drawLine(1, 6, 1, bottom);
+ int firstIndex = tabRuns[currentRun];
+ if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
+ {
+ if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
+ g.setColor(selectHighlight);
+ else
+ g.setColor(highlight);
+ g.drawLine(1, 0, 1, 4);
+ }
+
+ g.translate(-x, -y);
+ }
+
+ /**
+ * Paints the border for a tab assuming that the tab position is at the left
+ * ({@link #LEFT}).
+ *
+ * @param tabIndex the tab index.
+ * @param g the graphics device.
+ * @param x the x-coordinate for the tab's bounding rectangle.
+ * @param y the y-coordinate for the tab's bounding rectangle.
+ * @param w the width for the tab's bounding rectangle.
+ * @param h the height for the tab's bounding rectangle.
+ * @param btm ???
+ * @param rght ???
+ * @param isSelected indicates whether the tab is selected.
+ */
+ protected void paintLeftTabBorder(int tabIndex, Graphics g, int x, int y,
+ int w, int h, int btm, int rght, boolean isSelected)
+ {
+ g.translate(x, y);
+ int bottom = h - 1;
+ int right = w - 1;
+
+ int tabCount = tabPane.getTabCount();
+ int currentRun = getRunForTab(tabCount, tabIndex);
+ int firstIndex = tabRuns[currentRun];
+
+ // Paint the part of the above tab.
+ if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
+ {
+ Color c;
+ if (tabPane.getSelectedIndex() == tabIndex - 1)
+ c = selectColor;
+ else
+ c = getUnselectedBackground(tabIndex - 1);
+ g.setColor(c);
+ g.fillRect(2, 0, 4, 3);
+ g.drawLine(2, 3, 2, 3);
+ }
+
+ // Paint the highlight.
+ boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
+ if (isOcean)
+ {
+ g.setColor(isSelected ? selectHighlight : MetalLookAndFeel.getWhite());
+ }
+ else
+ {
+ g.setColor(isSelected ? selectHighlight : highlight);
+ }
+ // Slant.
+ g.drawLine(1, 6, 6, 1);
+ // Left.
+ g.drawLine(1, 6, 1, bottom);
+ // Top.
+ g.drawLine(6, 1, right, 1);
+ if (tabIndex != firstIndex)
+ {
+ if (isOcean)
+ {
+ g.setColor(MetalLookAndFeel.getWhite());
+ }
+ g.drawLine(1, 0, 1, 4);
+ }
+
+ // Paint border.
+ Color oceanSelectedBorder =
+ UIManager.getColor("TabbedPane.borderHightlightColor");
+ if (isOcean && isSelected)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ else
+ {
+ g.setColor(darkShadow);
+ }
+
+ // Slant.
+ g.drawLine(1, 5, 6, 0);
+ // Top.
+ g.drawLine(6, 0, right, 0);
+ // Bottom.
+ int lastIndex = lastTabInRun(tabCount, currentRun);
+ if (tabIndex == lastIndex)
+ {
+ g.drawLine(0, bottom, right, bottom);
+ }
+ // Left.
+ if (isOcean)
+ {
+ if (tabPane.getSelectedIndex() == tabIndex - 1)
+ {
+ g.drawLine(0, 6, 0, bottom);
+ if (tabIndex != firstIndex)
+ {
+ g.setColor(oceanSelectedBorder);
+ g.drawLine(0, 0, 0, 5);
+ }
+ }
+ else if (isSelected)
+ {
+ g.drawLine(0, 5, 0, bottom);
+ if (tabIndex != firstIndex)
+ {
+ g.setColor(darkShadow);
+ g.drawLine(0, 0, 0, 5);
+ }
+ }
+ else if (tabIndex != firstIndex)
+ {
+ g.drawLine(0, 0, 0, bottom);
+ }
+ else
+ {
+ g.drawLine(0, 6, 0, bottom);
+ }
+ }
+ else
+ {
+ if (tabIndex != firstIndex)
+ {
+ g.drawLine(0, 0, 0, bottom);
+ }
+ else
+ {
+ g.drawLine(0, 6, 0, bottom);
+ }
+ }
+
+ g.translate(-x, -y);
+ }
+
+ /**
+ * Paints the border for a tab assuming that the tab position is at the right
+ * ({@link #RIGHT}).
+ *
+ * @param tabIndex the tab index.
+ * @param g the graphics device.
+ * @param x the x-coordinate for the tab's bounding rectangle.
+ * @param y the y-coordinate for the tab's bounding rectangle.
+ * @param w the width for the tab's bounding rectangle.
+ * @param h the height for the tab's bounding rectangle.
+ * @param btm ???
+ * @param rght ???
+ * @param isSelected indicates whether the tab is selected.
+ */
+ protected void paintRightTabBorder(int tabIndex, Graphics g, int x, int y,
+ int w, int h, int btm, int rght, boolean isSelected)
+ {
+ g.translate(x, y);
+ int bottom = h - 1;
+ int right = w - 1;
+
+ int tabCount = tabPane.getTabCount();
+ int currentRun = getRunForTab(tabCount, tabIndex);
+ int firstIndex = tabRuns[currentRun];
+
+ // Paint part of the above tab.
+ if (tabIndex != firstIndex && tabIndex > 0 && tabsOpaque)
+ {
+ Color c;
+ if (tabPane.getSelectedIndex() == tabIndex - 1)
+ c = selectColor;
+ else
+ c = getUnselectedBackground(tabIndex - 1);
+ g.setColor(c);
+ g.fillRect(right - 5, 0, 5, 3);
+ g.fillRect(right - 2, 3, 2, 2);
+ }
+
+ // Paint highlight.
+ g.setColor(isSelected ? selectHighlight : highlight);
+
+ // Slant.
+ g.drawLine(right - 6, 1, right - 1, 6);
+ // Top.
+ g.drawLine(0, 1, right - 6, 1);
+ // Left.
+ if (! isSelected)
+ {
+ g.drawLine(0, 1, 0, bottom);
+ }
+
+ // Paint border.
+ boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
+ Color oceanSelectedBorder =
+ UIManager.getColor("TabbedPane.borderHightlightColor");
+ if (isOcean && isSelected)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ else
+ {
+ g.setColor(darkShadow);
+ }
+
+ // Bottom.
+ int lastIndex = lastTabInRun(tabCount, currentRun);
+ if (tabIndex == lastIndex)
+ {
+ g.drawLine(0, bottom, right, bottom);
+ }
+ // Slant.
+ if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ g.drawLine(right - 6, 0, right, 6);
+ // Top.
+ g.drawLine(0, 0, right - 6, 0);
+ // Right.
+ if (isOcean && isSelected)
+ {
+ g.drawLine(right, 6, right, bottom);
+ if (tabIndex != firstIndex)
+ {
+ g.setColor(darkShadow);
+ g.drawLine(right, 0, right, 5);
+ }
+ }
+ else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1)
+ {
+ if (tabIndex != firstIndex)
+ {
+ g.setColor(oceanSelectedBorder);
+ g.drawLine(right, 0, right, 6);
+ }
+ g.setColor(darkShadow);
+ g.drawLine(right, 7, right, bottom);
+ }
+ else if (tabIndex != firstIndex)
+ {
+ g.drawLine(right, 0, right, bottom);
+ }
+ else
+ {
+ g.drawLine(right, 6, right, bottom);
+ }
+ g.translate(-x, -y);
+ }
+
+ /**
+ * Paints the border for a tab assuming that the tab position is at the bottom
+ * ({@link #BOTTOM}).
+ *
+ * @param tabIndex the tab index.
+ * @param g the graphics device.
+ * @param x the x-coordinate for the tab's bounding rectangle.
+ * @param y the y-coordinate for the tab's bounding rectangle.
+ * @param w the width for the tab's bounding rectangle.
+ * @param h the height for the tab's bounding rectangle.
+ * @param btm ???
+ * @param rght ???
+ * @param isSelected indicates whether the tab is selected.
+ */
+ protected void paintBottomTabBorder(int tabIndex, Graphics g, int x, int y,
+ int w, int h, int btm, int rght, boolean isSelected)
+ {
+ int bottom = h - 1;
+ int right = w - 1;
+
+ int tabCount = tabPane.getTabCount();
+ int currentRun = getRunForTab(tabCount, tabIndex);
+ // Paint gap if necessary.
+ if (shouldFillGap(currentRun, tabIndex, x, y))
+ {
+ g.translate(x, y);
+ g.setColor(getColorForGap(currentRun, x, y));
+ g.fillRect(1, bottom - 4, 3, 5);
+ g.fillRect(4, bottom - 1, 2, 2);
+ g.translate(-x, -y);
+ }
+
+ g.translate(x, y);
+
+ // Paint border.
+ boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
+ Color oceanSelectedBorder =
+ UIManager.getColor("TabbedPane.borderHightlightColor");
+ if (isOcean && isSelected)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ else
+ {
+ g.setColor(darkShadow);
+ }
+ // Slant.
+ g.drawLine(1, bottom - 5, 6, bottom);
+ // Bottom.
+ g.drawLine(6, bottom, right, bottom);
+ // Right.
+ int lastIndex = lastTabInRun(tabCount, currentRun);
+ if (tabIndex == lastIndex)
+ {
+ g.drawLine(right, 0, right, bottom);
+ }
+ // Left.
+ if (isOcean && isSelected)
+ {
+ g.drawLine(0, 0, 0, bottom - 5);
+
+ // Paint a connecting line to the tab below for all
+ // but the first tab in the last run.
+ if (tabIndex != tabRuns[runCount-1])
+ {
+ g.setColor(darkShadow);
+ g.drawLine(0, bottom - 5, 0, bottom);
+ }
+ }
+ else
+ {
+ if (isOcean && tabIndex == tabPane.getSelectedIndex() + 1)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ if (tabIndex != tabRuns[runCount - 1])
+ {
+ g.drawLine(0, 0, 0, bottom);
+ }
+ else
+ {
+ g.drawLine(0, 0, 0, bottom - 6);
+ }
+ }
+
+ // Paint highlight.
+ g.setColor(isSelected ? selectHighlight : highlight);
+ // Slant.
+ g.drawLine(1, bottom - 6, 6, bottom - 1);
+ // Left.
+ g.drawLine(1, 0, 1, bottom - 6);
+
+ int firstIndex = tabRuns[currentRun];
+ if (tabIndex == firstIndex && tabIndex != tabRuns[runCount - 1])
+ {
+ if (tabPane.getSelectedIndex() == tabRuns[currentRun + 1])
+ {
+ g.setColor(selectHighlight);
+ }
+ else
+ {
+ g.setColor(highlight);
+ }
+ g.drawLine(1, bottom - 4, 1, bottom);
+ }
+
+ g.translate(-x, -y);
+ }
+
+ /**
+ * Paints the background for a tab.
+ *
+ * @param g the graphics device.
+ * @param tabPlacement the tab placement ({@link #TOP}, {@link #LEFT},
+ * {@link #BOTTOM} or {@link #RIGHT}).
+ * @param tabIndex the index of the tab to draw the border for.
+ * @param x the x-coordinate for the tab's bounding rectangle.
+ * @param y the y-coordinate for the tab's bounding rectangle.
+ * @param w the width for the tab's bounding rectangle.
+ * @param h the height for the tab's bounding rectangle.
+ * @param isSelected indicates whether or not the tab is selected.
+ */
+ protected void paintTabBackground(Graphics g, int tabPlacement,
+ int tabIndex, int x, int y, int w, int h, boolean isSelected)
+ {
+ if (isSelected)
+ g.setColor(selectColor);
+ else
+ g.setColor(getUnselectedBackground(tabIndex));
+
+ switch (tabPlacement)
+ {
+ case LEFT:
+ g.fillRect(x + 5, y + 1, w - 5, h - 1);
+ g.fillRect(x + 2, y + 4, 3, h - 4);
+ break;
+ case BOTTOM:
+ g.fillRect(x + 2, y, w - 2, h - 3);
+ g.fillRect(x + 5, y + h - 4, w - 5, 3);
+ break;
+ case RIGHT:
+ g.fillRect(x, y + 1, w - 4, h - 1);
+ g.fillRect(x + w - 4, y + 5, 3, h - 5);
+ break;
+ case TOP:
+ default:
+ g.fillRect(x + 4, y + 2, w - 4, h - 2);
+ g.fillRect(x + 2, y + 5, 2, h - 5);
+ }
+ }
+
+ /**
+ * This method paints the focus rectangle around the selected tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param rects The array of rectangles keeping track of size and position.
+ * @param tabIndex The tab index.
+ * @param iconRect The icon bounds.
+ * @param textRect The text bounds.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void paintFocusIndicator(Graphics g, int tabPlacement,
+ Rectangle[] rects, int tabIndex,
+ Rectangle iconRect, Rectangle textRect,
+ boolean isSelected)
+ {
+ if (tabPane.hasFocus() && isSelected)
+ {
+ Rectangle rect = rects[tabIndex];
+
+ g.setColor(focus);
+ g.translate(rect.x, rect.y);
+
+ switch (tabPlacement)
+ {
+ case LEFT:
+ // Top line
+ g.drawLine(7, 2, rect.width-2, 2);
+
+ // Right line
+ g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
+
+ // Bottom line
+ g.drawLine(rect.width-2, rect.height-2, 3, rect.height-2);
+
+ // Left line
+ g.drawLine(2, rect.height-3, 2, 7);
+
+ // Slant
+ g.drawLine(2, 6, 6, 2);
+ break;
+ case RIGHT:
+ // Top line
+ g.drawLine(1, 2, rect.width-8, 2);
+
+ // Slant
+ g.drawLine(rect.width-7, 2, rect.width-3, 6);
+
+ // Right line
+ g.drawLine(rect.width-3, 7, rect.width-3, rect.height-3);
+
+ // Bottom line
+ g.drawLine(rect.width-3, rect.height-2, 2, rect.height-2);
+
+ // Left line
+ g.drawLine(1, rect.height-2, 1, 2);
+ break;
+ case BOTTOM:
+ // Top line
+ g.drawLine(2, 1, rect.width-2, 1);
+
+ // Right line
+ g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
+
+ // Bottom line
+ g.drawLine(7, rect.height-3, rect.width-2, rect.height-3);
+
+ // Slant
+ g.drawLine(6, rect.height-3, 2, rect.height-7);
+
+ // Left line
+ g.drawLine(2, rect.height-8, 2, 2);
+
+ break;
+ case TOP:
+ default:
+ // Top line
+ g.drawLine(6, 2, rect.width-2, 2);
+
+ // Right line
+ g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3);
+
+ // Bottom line
+ g.drawLine(3, rect.height-3, rect.width-2, rect.height-3);
+
+ // Left line
+ g.drawLine(2, rect.height-3, 2, 7);
+
+ // Slant
+ g.drawLine(2, 6, 6, 2);
+
+ }
+
+ g.translate(-rect.x, -rect.y);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the tabs in the specified run should be
+ * padded to make the run fill the width/height of the {@link JTabbedPane}.
+ *
+ * @param tabPlacement the tab placement for the {@link JTabbedPane} (one of
+ * {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} and {@link #RIGHT}).
+ * @param run the run index.
+ *
+ * @return A boolean.
+ */
+ protected boolean shouldPadTabRun(int tabPlacement, int run)
+ {
+ // as far as I can tell, all runs should be padded except the last run
+ // (which is drawn at the very top for tabPlacement == TOP)
+ return run < this.runCount - 1;
+ }
+
+ /**
+ * Installs the defaults for this UI. This method calls super.installDefaults
+ * and then loads the Metal specific defaults for TabbedPane.
+ */
+ protected void installDefaults()
+ {
+ super.installDefaults();
+ selectColor = UIManager.getColor("TabbedPane.selected");
+ selectHighlight = UIManager.getColor("TabbedPane.selectHighlight");
+ tabAreaBackground = UIManager.getColor("TabbedPane.tabAreaBackground");
+ tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
+ minTabWidth = 0;
+ }
+
+ /**
+ * Returns the color for the gap.
+ *
+ * @param currentRun - The current run to return the color for
+ * @param x - The x position of the current run
+ * @param y - The y position of the current run
+ *
+ * @return the color for the gap in the current run.
+ */
+ protected Color getColorForGap(int currentRun, int x, int y)
+ {
+ int index = tabForCoordinate(tabPane, x, y);
+ int selected = tabPane.getSelectedIndex();
+ if (selected == index)
+ return selectColor;
+ return tabAreaBackground;
+ }
+
+ /**
+ * Returns true if the gap should be filled in.
+ *
+ * @param currentRun - The current run
+ * @param tabIndex - The current tab
+ * @param x - The x position of the tab
+ * @param y - The y position of the tab
+ *
+ * @return true if the gap at the current run should be filled
+ */
+ protected boolean shouldFillGap(int currentRun, int tabIndex, int x, int y)
+ {
+ // As far as I can tell, the gap is never filled in.
+ return false;
+ }
+
+ /**
+ * Paints the highlight below the tab, if there is one.
+ */
+ protected void paintHighlightBelowTab()
+ {
+ int selected = tabPane.getSelectedIndex();
+ int tabPlacement = tabPane.getTabPlacement();
+ Rectangle bounds = getTabBounds(tabPane, selected);
+
+ hg.setColor(selectColor);
+ int x = bounds.x;
+ int y = bounds.y;
+ int w = bounds.width;
+ int h = bounds.height;
+
+ if (tabPlacement == TOP)
+ hg.fillRect(x, y + h - 2, w, 30);
+ else if (tabPlacement == LEFT)
+ hg.fillRect(x + w - 1, y, 20, h);
+ else if (tabPlacement == BOTTOM)
+ hg.fillRect(x, y - h + 2, w, 30);
+ else if (tabPlacement == RIGHT)
+ hg.fillRect(x - 18, y, 20, h);
+ else
+ throw new AssertionError("Unrecognised 'tabPlacement' argument.");
+ hg = null;
+ }
+
+ /**
+ * Returns true if we should rotate the tab runs.
+ *
+ * @param tabPlacement - The current tab placement.
+ * @param selectedRun - The selected run.
+ *
+ * @return true if the tab runs should be rotated.
+ */
+ protected boolean shouldRotateTabRuns(int tabPlacement,
+ int selectedRun)
+ {
+ // false because tab runs are not rotated in the MetalLookAndFeel
+ return false;
+ }
+
+ protected int calculateMaxTabHeight(int tabPlacement)
+ {
+ // FIXME: Why is this overridden?
+ return super.calculateMaxTabHeight(tabPlacement);
+ }
+
+ /**
+ * Returns the amount of overlay among the tabs. In
+ * the Metal L&F the overlay for LEFT and RIGHT placement
+ * is half of the maxTabHeight. For TOP and BOTTOM placement
+ * the tabs do not overlay.
+ *
+ * @param tabPlacement the placement
+ *
+ * @return the amount of overlay among the tabs
+ */
+ protected int getTabRunOverlay(int tabPlacement)
+ {
+ int overlay = 0;
+ if (tabPlacement == LEFT || tabPlacement == RIGHT)
+ {
+ int maxHeight = calculateMaxTabHeight(tabPlacement);
+ overlay = maxTabHeight / 2;
+ }
+ return overlay;
+ }
+
+ /**
+ * Paints the upper edge of the content border.
+ *
+ * @param g the graphics to use for painting
+ * @param tabPlacement the tab placement
+ * @param selectedIndex the index of the selected tab
+ * @param x the upper left coordinate of the content area
+ * @param y the upper left coordinate of the content area
+ * @param w the width of the content area
+ * @param h the height of the content area
+ */
+ protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color oceanSelectedBorder =
+ UIManager.getColor("TabbedPane.borderHightlightColor");
+ boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
+ if (isOcean)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ else
+ {
+ g.setColor(selectHighlight);
+ }
+
+ Rectangle rect = selectedIndex < 0 ? null :
+ getTabBounds(selectedIndex, calcRect);
+
+ // If tabs are not placed on TOP, or if the selected tab is not in the
+ // run directly above the content or the selected tab is not visible,
+ // then we draw an unbroken line.
+ if (tabPlacement != TOP || selectedIndex < 0
+ || rect.y + rect.height + 1 < y || rect.x < x || rect.x > x + w)
+ {
+ g.drawLine(x, y, x + w - 2, y);
+ if (isOcean && tabPlacement == TOP)
+ {
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x, y + 1, x + w - 2, y + 1);
+ }
+ }
+ else
+ {
+ boolean isLast = isLastTabInRun(selectedIndex);
+ if (isLast)
+ {
+ g.drawLine(x, y, rect.x + 1, y);
+ }
+ else
+ {
+ g.drawLine(x, y, rect.x, y);
+ }
+
+ int right = x + w - 1;
+ if (rect.x + rect.width < right - 1)
+ {
+ if (isLast)
+ {
+ g.drawLine(rect.x + rect.width - 1, y, right - 1, y);
+ }
+ else
+ {
+ g.drawLine(rect.x + rect.width, y, right - 1, y);
+ }
+ }
+ else
+ {
+ g.setColor(shadow);
+ g.drawLine(x + w - 2, y, x + w - 2, y);
+ }
+
+ // When in OceanTheme, draw another white line.
+ if (isOcean)
+ {
+ g.setColor(MetalLookAndFeel.getWhite());
+ if (isLast)
+ {
+ g.drawLine(x, y + 1, rect.x + 1, y + 1);
+ }
+ else
+ {
+ g.drawLine(x, y + 1, rect.x, y + 1);
+ }
+
+ if (rect.x + rect.width < right - 1)
+ {
+ if (isLast)
+ {
+ g.drawLine(rect.x + rect.width - 1, y + 1, right - 1,
+ y + 1);
+ }
+ else
+ {
+ g.drawLine(rect.x + rect.width, y + 1, right - 1, y + 1);
+ }
+ }
+ else
+ {
+ g.setColor(shadow);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1);
+ }
+ }
+ }
+ }
+
+ /**
+ * Paints the lower edge of the content border.
+ *
+ * @param g the graphics to use for painting
+ * @param tabPlacement the tab placement
+ * @param selectedIndex the index of the selected tab
+ * @param x the upper left coordinate of the content area
+ * @param y the upper left coordinate of the content area
+ * @param w the width of the content area
+ * @param h the height of the content area
+ */
+ protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ g.setColor(darkShadow);
+
+ // If tabs are not placed on BOTTOM, or if the selected tab is not in the
+ // run directly below the content or the selected tab is not visible,
+ // then we draw an unbroken line.
+ Rectangle rect = selectedIndex < 0 ? null :
+ getTabBounds(selectedIndex, calcRect);
+ boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
+ Color oceanSelectedBorder =
+ UIManager.getColor("TabbedPane.borderHightlightColor");
+ if (tabPlacement != BOTTOM || selectedIndex < 0 || rect.y - 1 > h
+ || rect.x < x || rect.x > x + w)
+ {
+ if (isOcean && tabPlacement == BOTTOM)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
+ }
+ else
+ {
+ boolean isLast = isLastTabInRun(selectedIndex);
+ if (isOcean)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+
+ int bottom = y + h - 1;
+ int right = x + w - 1;
+ if (isLast)
+ {
+ g.drawLine(x, bottom, rect.x, bottom);
+ }
+ else
+ {
+ g.drawLine(x, bottom, rect.x - 1, bottom);
+ }
+
+ if (rect.x + rect.width < x + w - 2)
+ {
+ if (isLast)
+ {
+ g.drawLine(rect.x + rect.width - 1, bottom, right, bottom);
+ }
+ else
+ {
+ g.drawLine(rect.x + rect.width, bottom, right, bottom);
+ }
+ }
+ }
+ }
+
+ /**
+ * Paints the left edge of the content border.
+ *
+ * @param g the graphics to use for painting
+ * @param tabPlacement the tab placement
+ * @param selectedIndex the index of the selected tab
+ * @param x the upper left coordinate of the content area
+ * @param y the upper left coordinate of the content area
+ * @param w the width of the content area
+ * @param h the height of the content area
+ */
+ protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
+ Color oceanSelectedBorder =
+ UIManager.getColor("TabbedPane.borderHightlightColor");
+ Rectangle rect = selectedIndex < 0 ? null :
+ getTabBounds(selectedIndex, calcRect);
+
+ if (isOcean)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ else
+ {
+ g.setColor(selectHighlight);
+ }
+
+ // If tabs are not placed on LEFT, or if the selected tab is not in the
+ // run directly left to the content or the selected tab is not visible,
+ // then we draw an unbroken line.
+ if (tabPlacement != LEFT || selectedIndex < 0
+ || rect.x + rect.width + 1 < x || rect.y < y || rect.y > y + h)
+ {
+ g.drawLine(x, y + 1, x, y + h - 2);
+ if (isOcean && tabPlacement == LEFT)
+ {
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x, y + 1, x, y + h - 2);
+ }
+ }
+ else
+ {
+ g.drawLine(x, y, x, rect.y + 1);
+ if (rect.y + rect.height < y + h - 2)
+ {
+ g.drawLine(x, rect.y + rect.height + 1, x, y + h + 2);
+ }
+ if (isOcean)
+ {
+ g.setColor(MetalLookAndFeel.getWhite());
+ g.drawLine(x + 1, y + 1, x + 1, rect.y + 1);
+ if (rect.y + rect.height < y + h - 2)
+ {
+ g.drawLine(x + 1, rect.y + rect.height + 1, x + 1, y + h + 2);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Paints the right edge of the content border.
+ *
+ * @param g the graphics to use for painting
+ * @param tabPlacement the tab placement
+ * @param selectedIndex the index of the selected tab
+ * @param x the upper left coordinate of the content area
+ * @param y the upper left coordinate of the content area
+ * @param w the width of the content area
+ * @param h the height of the content area
+ */
+ protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ g.setColor(darkShadow);
+ Rectangle rect = selectedIndex < 0 ? null :
+ getTabBounds(selectedIndex, calcRect);
+ boolean isOcean = MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme;
+ Color oceanSelectedBorder =
+ UIManager.getColor("TabbedPane.borderHightlightColor");
+
+ // If tabs are not placed on RIGHT, or if the selected tab is not in the
+ // run directly right to the content or the selected tab is not visible,
+ // then we draw an unbroken line.
+ if (tabPlacement != RIGHT || selectedIndex < 0 || rect.x - 1 > w
+ || rect.y < y || rect.y > y + h)
+ {
+ if (isOcean && tabPlacement == RIGHT)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
+ }
+ else
+ {
+ if (isOcean)
+ {
+ g.setColor(oceanSelectedBorder);
+ }
+ g.drawLine(x + w - 1, y, x + w - 1, rect.y);
+
+ if (rect.y + rect.height < y + h - 2)
+ {
+ g.drawLine(x + w - 1, rect.y + rect.height, x + w - 1, y + h - 2);
+ }
+ }
+ }
+
+ /**
+ * Determines if the specified tab is the last tab in its tab run.
+ *
+ * @param tabIndex the index of the tab
+ *
+ * @return if the specified tab is the last tab in its tab run
+ */
+ private boolean isLastTabInRun(int tabIndex)
+ {
+ int count = tabPane.getTabCount();
+ int run = getRunForTab(count, tabIndex);
+ int lastIndex = lastTabInRun(count, run);
+ return tabIndex == lastIndex;
+ }
+
+ /**
+ * Returns the background for an unselected tab. This first asks the
+ * JTabbedPane for the background at the specified tab index, if this
+ * is an UIResource (that means, it is inherited from the JTabbedPane)
+ * and the TabbedPane.unselectedBackground UI property is not null,
+ * this returns the value of the TabbedPane.unselectedBackground property,
+ * otherwise the value returned by the JTabbedPane.
+ *
+ * @param tabIndex the index of the tab for which we query the background
+ *
+ * @return the background for an unselected tab
+ */
+ private Color getUnselectedBackground(int tabIndex)
+ {
+ Color bg = tabPane.getBackgroundAt(tabIndex);
+ Color unselectedBackground =
+ UIManager.getColor("TabbedPane.unselectedBackground");
+ if (bg instanceof UIResource && unselectedBackground != null)
+ bg = unselectedBackground;
+ return bg;
+ }
+
+ protected int getTabLabelShiftX(int tabPlacement,
+ int index,
+ boolean isSelected)
+ {
+ return 0;
+ }
+
+ protected int getTabLabelShiftY(int tabPlacement,
+ int index,
+ boolean isSelected)
+ {
+ return 0;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java
new file mode 100644
index 000000000..f7b18dbd4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java
@@ -0,0 +1,82 @@
+/* MetalTextFieldUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.beans.PropertyChangeEvent;
+
+import javax.swing.JComponent;
+import javax.swing.JTextField;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicTextFieldUI;
+
+/**
+ * A UI delegate for the {@link JTextField} component.
+ */
+public class MetalTextFieldUI extends BasicTextFieldUI
+{
+ /**
+ * Constructs a new instance of MetalTextFieldUI.
+ */
+ public MetalTextFieldUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a new instance of <code>MetalTextFieldUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A new instance of <code>MetalTextFieldUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalTextFieldUI();
+ }
+
+ /**
+ * This method gets called when a bound property is changed on the associated
+ * JTextComponent. This is a hook which UI implementations may change to
+ * reflect how the UI displays bound properties of JTextComponent subclasses.
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ super.propertyChange(evt);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTheme.java b/libjava/classpath/javax/swing/plaf/metal/MetalTheme.java
new file mode 100644
index 000000000..209cbbe5e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalTheme.java
@@ -0,0 +1,576 @@
+/* MetalTheme.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.FontUIResource;
+
+/**
+ * The base class for themes used by the {@link MetalLookAndFeel}. A default
+ * theme ({@link DefaultMetalTheme}) is provided, or you can create and use
+ * your own.
+ *
+ * @see MetalLookAndFeel#setCurrentTheme(MetalTheme)
+ */
+public abstract class MetalTheme
+{
+ private ColorUIResource BLACK = new ColorUIResource(Color.BLACK);
+ private ColorUIResource WHITE = new ColorUIResource(Color.WHITE);
+
+ /**
+ * Default constructor.
+ */
+ public MetalTheme()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Returns the name of the theme.
+ *
+ * @return The name of the theme.
+ */
+ public abstract String getName();
+
+ /**
+ * Adds custom entries to the UI defaults table. This method is empty.
+ *
+ * @param table the table.
+ */
+ public void addCustomEntriesToTable(UIDefaults table)
+ {
+ // Do nothing here.
+ // This method needs to be overridden to actually do something.
+ // It is called from MetalLookAndFeel.getDefaults().
+ }
+
+ /**
+ * Returns the accelerator foreground color. The default implementation
+ * returns the color from {@link #getPrimary1()}.
+ *
+ * @return The accelerator foreground color.
+ */
+ public ColorUIResource getAcceleratorForeground()
+ {
+ return getPrimary1();
+ }
+
+ /**
+ * Returns the accelerator selected foreground color. The default
+ * implementation returns the color from {@link #getBlack()}.
+ *
+ * @return The accelerator selected foreground color.
+ */
+ public ColorUIResource getAcceleratorSelectedForeground()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the control color. The default implementation returns the color
+ * from {@link #getSecondary3()}.
+ *
+ * @return The control color.
+ */
+ public ColorUIResource getControl()
+ {
+ return getSecondary3();
+ }
+
+ /**
+ * Returns the color used for dark shadows on controls. The default
+ * implementation returns the color from {@link #getSecondary1()}.
+ *
+ * @return The color used for dark shadows on controls.
+ */
+ public ColorUIResource getControlDarkShadow()
+ {
+ return getSecondary1();
+ }
+
+ /**
+ * Returns the color used for disabled controls. The default implementation
+ * returns the color from {@link #getSecondary1()}.
+ *
+ * @return The color used for disabled controls.
+ */
+ public ColorUIResource getControlDisabled()
+ {
+ return getSecondary2();
+ }
+
+ /**
+ * Returns the color used to draw highlights for controls. The default
+ * implementation returns the color from {@link #getWhite()}.
+ *
+ * @return The color used to draw highlights for controls.
+ */
+ public ColorUIResource getControlHighlight()
+ {
+ return getWhite();
+ }
+
+ /**
+ * Returns the color used to display control info. The default
+ * implementation returns the color from {@link #getBlack()}.
+ *
+ * @return The color used to display control info.
+ */
+ public ColorUIResource getControlInfo()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the color used to draw shadows for controls. The default
+ * implementation returns the color from {@link #getSecondary2()}.
+ *
+ * @return The color used to draw shadows for controls.
+ */
+ public ColorUIResource getControlShadow()
+ {
+ return getSecondary2();
+ }
+
+ /**
+ * Returns the color used for text on controls. The default implementation
+ * returns the color from {@link #getControlInfo()}.
+ *
+ * @return The color used for text on controls.
+ */
+ public ColorUIResource getControlTextColor()
+ {
+ return getControlInfo();
+ }
+
+ /**
+ * Returns the color used for the desktop background. The default
+ * implementation returns the color from {@link #getPrimary2()}.
+ *
+ * @return The color used for the desktop background.
+ */
+ public ColorUIResource getDesktopColor()
+ {
+ return getPrimary2();
+ }
+
+ /**
+ * Returns the color used to draw focus highlights. The default
+ * implementation returns the color from {@link #getPrimary2()}.
+ *
+ * @return The color used to draw focus highlights.
+ */
+ public ColorUIResource getFocusColor()
+ {
+ return getPrimary2();
+ }
+
+ /**
+ * Returns the color used to draw highlighted text. The default
+ * implementation returns the color from {@link #getHighlightedTextColor()}.
+ *
+ * @return The color used to draw highlighted text.
+ */
+ public ColorUIResource getHighlightedTextColor()
+ {
+ return getControlTextColor();
+ }
+
+ /**
+ * Returns the color used to draw text on inactive controls. The default
+ * implementation returns the color from {@link #getControlDisabled()}.
+ *
+ * @return The color used to draw text on inactive controls.
+ */
+ public ColorUIResource getInactiveControlTextColor()
+ {
+ return getControlDisabled();
+ }
+
+ /**
+ * Returns the color used to draw inactive system text. The default
+ * implementation returns the color from {@link #getSecondary2()}.
+ *
+ * @return The color used to draw inactive system text.
+ */
+ public ColorUIResource getInactiveSystemTextColor()
+ {
+ return getSecondary2();
+ }
+
+ /**
+ * Returns the background color for menu items. The default implementation
+ * returns the color from {@link #getSecondary3()}.
+ *
+ * @return The background color for menu items.
+ *
+ * @see #getMenuSelectedBackground()
+ */
+ public ColorUIResource getMenuBackground()
+ {
+ return getSecondary3();
+ }
+
+ /**
+ * Returns the foreground color for disabled menu items. The default
+ * implementation returns the color from {@link #getSecondary2()}.
+ *
+ * @return The foreground color for disabled menu items.
+ *
+ * @see #getMenuForeground()
+ */
+ public ColorUIResource getMenuDisabledForeground()
+ {
+ return getSecondary2();
+ }
+
+ /**
+ * Returns the foreground color for menu items. The default implementation
+ * returns the color from {@link #getBlack()}.
+ *
+ * @return The foreground color for menu items.
+ *
+ * @see #getMenuDisabledForeground()
+ * @see #getMenuSelectedForeground()
+ */
+ public ColorUIResource getMenuForeground()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the background color for selected menu items. The default
+ * implementation returns the color from {@link #getPrimary2()}.
+ *
+ * @return The background color for selected menu items.
+ *
+ * @see #getMenuBackground()
+ */
+ public ColorUIResource getMenuSelectedBackground()
+ {
+ return getPrimary2();
+ }
+
+ /**
+ * Returns the foreground color for selected menu items. The default
+ * implementation returns the value from {@link #getBlack()}.
+ *
+ * @return The foreground color for selected menu items.
+ *
+ * @see #getMenuForeground()
+ */
+ public ColorUIResource getMenuSelectedForeground()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the primary color for controls. The default implementation
+ * returns the color from {@link #getPrimary3()}.
+ *
+ * @return The primary color for controls.
+ */
+ public ColorUIResource getPrimaryControl()
+ {
+ return getPrimary3();
+ }
+
+ /**
+ * Returns the primary color for the dark shadow on controls. The default
+ * implementation returns the color from {@link #getPrimary1()}.
+ *
+ * @return The primary color for the dark shadow on controls.
+ */
+ public ColorUIResource getPrimaryControlDarkShadow()
+ {
+ return getPrimary1();
+ }
+
+ /**
+ * Returns the primary color for the highlight on controls. The default
+ * implementation returns the color from {@link #getWhite()}.
+ *
+ * @return The primary color for the highlight on controls.
+ */
+ public ColorUIResource getPrimaryControlHighlight()
+ {
+ return getWhite();
+ }
+
+ /**
+ * Returns the primary color for the information on controls. The default
+ * implementation returns the color from {@link #getBlack()}.
+ *
+ * @return The primary color for the information on controls.
+ */
+ public ColorUIResource getPrimaryControlInfo()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the primary color for the shadow on controls. The default
+ * implementation returns the color from {@link #getPrimary2()}.
+ *
+ * @return The primary color for the shadow on controls.
+ */
+ public ColorUIResource getPrimaryControlShadow()
+ {
+ return getPrimary2();
+ }
+
+ /**
+ * Returns the background color for separators. The default implementation
+ * returns the color from {@link #getWhite()}.
+ *
+ * @return The background color for separators.
+ */
+ public ColorUIResource getSeparatorBackground()
+ {
+ return getWhite();
+ }
+
+ /**
+ * Returns the foreground color for separators. The default implementation
+ * returns the value from {@link #getPrimary1()}.
+ *
+ * @return The foreground color for separators.
+ */
+ public ColorUIResource getSeparatorForeground()
+ {
+ return getPrimary1();
+ }
+
+ /**
+ * Returns the color used for system text. The default implementation
+ * returns the color from {@link #getBlack()}.
+ *
+ * @return The color used for system text.
+ */
+ public ColorUIResource getSystemTextColor()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the color used to highlight text. The default implementation
+ * returns the color from {@link #getPrimary3()}.
+ *
+ * @return The color used to highlight text.
+ */
+ public ColorUIResource getTextHighlightColor()
+ {
+ return getPrimary3();
+ }
+
+ /**
+ * Returns the color used to display user text. The default implementation
+ * returns the color from {@link #getBlack()}.
+ *
+ * @return The color used to display user text.
+ */
+ public ColorUIResource getUserTextColor()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the window background color. The default implementation returns
+ * the color from {@link #getWhite()}.
+ *
+ * @return The window background color.
+ */
+ public ColorUIResource getWindowBackground()
+ {
+ return getWhite();
+ }
+
+ /**
+ * Returns the window title background color. The default implementation
+ * returns the color from {@link #getPrimary3()}.
+ *
+ * @return The window title background color.
+ */
+ public ColorUIResource getWindowTitleBackground()
+ {
+ return getPrimary3();
+ }
+
+ /**
+ * Returns the window title foreground color. The default implementation
+ * returns the color from {@link #getBlack()}.
+ *
+ * @return The window title foreground color.
+ */
+ public ColorUIResource getWindowTitleForeground()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the background color for an inactive window title. The default
+ * implementation returns the color from {@link #getSecondary3()}.
+ *
+ * @return The background color for an inactive window title.
+ */
+ public ColorUIResource getWindowTitleInactiveBackground()
+ {
+ return getSecondary3();
+ }
+
+ /**
+ * Returns the foreground color for an inactive window title. The default
+ * implementation returns the color from {@link #getBlack()}.
+ *
+ * @return The foreground color for an inactive window title.
+ */
+ public ColorUIResource getWindowTitleInactiveForeground()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the color used for black.
+ *
+ * @return The color used for black.
+ */
+ protected ColorUIResource getBlack()
+ {
+ return BLACK;
+ }
+
+ /**
+ * Returns the color used for white.
+ *
+ * @return The color used for white.
+ */
+ protected ColorUIResource getWhite()
+ {
+ return WHITE;
+ }
+
+ /**
+ * Returns the first primary color for this theme.
+ *
+ * @return The first primary color.
+ */
+ protected abstract ColorUIResource getPrimary1();
+
+ /**
+ * Returns the second primary color for this theme.
+ *
+ * @return The second primary color.
+ */
+ protected abstract ColorUIResource getPrimary2();
+
+ /**
+ * Returns the third primary color for this theme.
+ *
+ * @return The third primary color.
+ */
+ protected abstract ColorUIResource getPrimary3();
+
+ /**
+ * Returns the first secondary color for this theme.
+ *
+ * @return The first secondary color.
+ */
+ protected abstract ColorUIResource getSecondary1();
+
+ /**
+ * Returns the second secondary color for this theme.
+ *
+ * @return The second secondary color.
+ */
+ protected abstract ColorUIResource getSecondary2();
+
+ /**
+ * Returns the third secondary color for this theme.
+ *
+ * @return The third secondary color.
+ */
+ protected abstract ColorUIResource getSecondary3();
+
+ /**
+ * Returns the font used for text on controls.
+ *
+ * @return The font used for text on controls.
+ */
+ public abstract FontUIResource getControlTextFont();
+
+ /**
+ * Returns the font used for text in menus.
+ *
+ * @return The font used for text in menus.
+ */
+ public abstract FontUIResource getMenuTextFont();
+
+ /**
+ * Returns the font used for sub text.
+ *
+ * @return The font used for sub text.
+ */
+ public abstract FontUIResource getSubTextFont();
+
+ /**
+ * Returns the font used for system text.
+ *
+ * @return The font used for system text.
+ */
+ public abstract FontUIResource getSystemTextFont();
+
+ /**
+ * Returns the font used for user text.
+ *
+ * @return The font used for user text.
+ */
+ public abstract FontUIResource getUserTextFont();
+
+ /**
+ * Returns the font used for window titles.
+ *
+ * @return The font used for window titles.
+ */
+ public abstract FontUIResource getWindowTitleFont();
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java
new file mode 100644
index 000000000..2dacd7fec
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java
@@ -0,0 +1,230 @@
+/* MetalToggleButtonUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.JComponent;
+import javax.swing.JToggleButton;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicButtonUI;
+import javax.swing.plaf.basic.BasicToggleButtonUI;
+
+/**
+ * A UI delegate for the {@link JToggleButton} component.
+ */
+public class MetalToggleButtonUI
+ extends BasicToggleButtonUI
+{
+
+ /** The color for the focus border. */
+ protected Color focusColor;
+
+ /** The color that indicates a selected button. */
+ protected Color selectColor;
+
+ /** The color for disabled button labels. */
+ protected Color disabledTextColor;
+
+ /**
+ * Returns a new instance of <code>MetalToggleButtonUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A new instance of <code>MetalToggleButtonUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalToggleButtonUI();
+ }
+
+ /**
+ * Constructs a new instance of <code>MetalToggleButtonUI</code>.
+ */
+ public MetalToggleButtonUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns the color for the focus border.
+ *
+ * @return the color for the focus border
+ */
+ protected Color getFocusColor()
+ {
+ return focusColor;
+ }
+
+ /**
+ * Returns the color that indicates a selected button.
+ *
+ * @return the color that indicates a selected button
+ */
+ protected Color getSelectColor()
+ {
+ return selectColor;
+ }
+
+ /**
+ * Returns the color for the text label of disabled buttons. The value
+ * is initialised in the {@link #installDefaults(AbstractButton)} method
+ * by reading the <code>ToggleButton.disabledText</code> item from the UI
+ * defaults.
+ *
+ * @return The color for the text label of disabled buttons.
+ */
+ protected Color getDisabledTextColor()
+ {
+ return disabledTextColor;
+ }
+
+ /**
+ * Updates the button with the defaults for this look and feel.
+ *
+ * @param b the button.
+ */
+ public void installDefaults(AbstractButton b)
+ {
+ super.installDefaults(b);
+ focusColor = UIManager.getColor(getPropertyPrefix() + "focus");
+ selectColor = UIManager.getColor(getPropertyPrefix() + "select");
+ disabledTextColor = UIManager.getColor(getPropertyPrefix() + "disabledText");
+ }
+
+ /**
+ * Paints the button background when it is pressed/selected.
+ *
+ * @param g the graphics device.
+ * @param b the button.
+ */
+ protected void paintButtonPressed(Graphics g, AbstractButton b)
+ {
+ if (b.isContentAreaFilled() && b.isOpaque())
+ {
+ Color saved = g.getColor();
+ Rectangle bounds = SwingUtilities.getLocalBounds(b);
+ g.setColor(selectColor);
+ g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
+ g.setColor(saved);
+ }
+ }
+
+ /**
+ * Paints the text for the button.
+ *
+ * As of JDK 1.4 this method is obsolete.
+ * Use {@link BasicButtonUI#paintText(java.awt.Graphics,
+ * javax.swing.AbstractButton, java.awt.Rectangle, java.lang.String)}.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ * @param textRect the bounds for the text.
+ * @param text the text.
+ *
+ */
+ protected void paintText(Graphics g, JComponent c, Rectangle textRect,
+ String text)
+ {
+ Font savedFont = g.getFont();
+ Color savedColor = g.getColor();
+ g.setFont(c.getFont());
+ if (c.isEnabled())
+ g.setColor(c.getForeground());
+ else
+ g.setColor(disabledTextColor);
+ FontMetrics fm = g.getFontMetrics(c.getFont());
+ int ascent = fm.getAscent();
+ g.drawString(text, textRect.x, textRect.y + ascent);
+ g.setFont(savedFont);
+ g.setColor(savedColor);
+ }
+
+ /**
+ * Draws the focus highlight around the text and icon.
+ *
+ * @param g the graphics device.
+ * @param b the button.
+ */
+ protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect,
+ Rectangle textRect, Rectangle iconRect)
+ {
+ if (!b.hasFocus())
+ return;
+ Color saved = g.getColor();
+ g.setColor(focusColor);
+ Rectangle fr = iconRect.union(textRect);
+ g.drawRect(fr.x - 1, fr.y - 1, fr.width + 1, fr.height + 1);
+ g.setColor(saved);
+ }
+
+ /**
+ * If the property <code>ToggleButton.gradient</code> is set, then a gradient
+ * is painted as background, otherwise the normal superclass behaviour is
+ * called.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+ ButtonModel m = b.getModel();
+ if (b.getBackground() instanceof UIResource
+ && b.isContentAreaFilled()
+ && b.isEnabled() && ! m.isArmed() && ! m.isPressed()
+ && UIManager.get(getPropertyPrefix() + "gradient") != null)
+ {
+ MetalUtils.paintGradient(g, 0, 0, c.getWidth(), c.getHeight(),
+ SwingConstants.VERTICAL,
+ getPropertyPrefix() + "gradient");
+ paint(g, c);
+ }
+ else
+ super.update(g, c);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java
new file mode 100644
index 000000000..64e679c67
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java
@@ -0,0 +1,298 @@
+/* MetalToolBarUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.event.ContainerListener;
+import java.awt.event.MouseEvent;
+
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JComponent;
+import javax.swing.JToolBar;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicToolBarUI;
+
+/**
+ * A UI delegate for the {@link JToolBar} component.
+ */
+public class MetalToolBarUI extends BasicToolBarUI
+{
+
+ /**
+ * A listener (no longer used) that responds when components are added to or
+ * removed from the {@link JToolBar}. The required behaviour is now
+ * handled in the super class.
+ *
+ * @see MetalToolBarUI#createContainerListener()
+ */
+ protected class MetalContainerListener
+ extends BasicToolBarUI.ToolBarContListener
+ {
+ /**
+ * Creates a new instance.
+ */
+ protected MetalContainerListener()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A listener (no longer used) that responds to property change events in a
+ * {@link JToolBar} component. The required behaviour is now handled in the
+ * super class.
+ *
+ * @see MetalToolBarUI#createRolloverListener()
+ */
+ protected class MetalRolloverListener
+ extends BasicToolBarUI.PropertyListener
+ {
+ /**
+ * Creates a new instance.
+ */
+ protected MetalRolloverListener()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * The container listener (an implementation specific field, according to the
+ * spec, and not used in GNU Classpath).
+ */
+ protected ContainerListener contListener;
+
+ /**
+ * The rollover listener (an implementation specific field, according to the
+ * spec, and not used in GNU Classpath).
+ */
+ protected PropertyChangeListener rolloverListener;
+
+ /**
+ * Creates a new instance of this UI delegate.
+ */
+ public MetalToolBarUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a new instance of <code>MetalToolBarUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A new instance of <code>MetalToolBarUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalToolBarUI();
+ }
+
+ /**
+ * Returns <code>null</code> as permitted by recent versions of the API
+ * specification. Originally it seems this method returned a new instance of
+ * {@link MetalRolloverListener}, but this is now redundant.
+ *
+ * @return <code>null</code>.
+ */
+ protected PropertyChangeListener createRolloverListener()
+ {
+ return null;
+ }
+
+ /**
+ * Returns <code>null</code> as permitted by recent versions of the API
+ * specification. Originally it seems this method returned a new instance of
+ * {@link MetalContainerListener}, but this is now redundant.
+ *
+ * @return <code>null</code>.
+ */
+ protected ContainerListener createContainerListener()
+ {
+ return null;
+ }
+
+ /**
+ * Returns a border with no rollover effect for buttons in the tool bar.
+ *
+ * @return A border.
+ *
+ * @see MetalBorders#getToolbarButtonBorder()
+ */
+ protected Border createNonRolloverBorder()
+ {
+ return MetalBorders.getToolbarButtonBorder();
+ }
+
+ /**
+ * Sets the offset for the window used for dragging the toolbar.
+ * It is set as long as the window is not null (it has been installed).
+ */
+ protected void setDragOffset(Point p)
+ {
+ if (dragWindow != null)
+ dragWindow.setOffset(p);
+ }
+
+ /**
+ * Creates and returns an instance of MetalDockingListener.
+ *
+ * @return an instance of MetalDockingListener.
+ */
+ protected MouseInputListener createDockingListener()
+ {
+ return new MetalDockingListener(toolBar);
+ }
+
+ /**
+ * This is the MouseHandler class that allows the user to drag the JToolBar
+ * in and out of the parent and dock it if it can.
+ */
+ protected class MetalDockingListener extends BasicToolBarUI.DockingListener
+ {
+ /**
+ * Creates a new DockingListener object.
+ *
+ * @param t The JToolBar this DockingListener is being used for.
+ */
+ public MetalDockingListener(JToolBar t)
+ {
+ super(t);
+ }
+
+ /**
+ * This method is called when the mouse is pressed in the JToolBar. If the
+ * press doesn't occur in a place where it causes the JToolBar to be
+ * dragged, it returns. Otherwise, it starts a drag session.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ super.mousePressed(e);
+ setDragOffset(new Point(e.getX(), e.getY()));
+ }
+
+ /**
+ * This method is called when the mouse is dragged. It delegates the drag
+ * painting to the dragTo method.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ // Does not do anything differently than dragging
+ // BasicToolBarUI.DockingListener
+ super.mouseDragged(e);
+ }
+ }
+
+ /**
+ * Installs the UI on the toolbar. This calls super and sets the rollover
+ * property according to the <code>UIManager</code> property
+ * &quot;ToolBar.isRollover&quot;.
+ *
+ * @param c the component to install the UI on
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JToolBar)
+ {
+ JToolBar tb = (JToolBar) c;
+ tb.setRollover(UIManager.getBoolean("ToolBar.isRollover"));
+ }
+ }
+
+ /**
+ * Uninstalls the UI from the toolbar. This calls super and resets the
+ * rollover property.
+ *
+ * @param c the component to uninstall the UI from
+ */
+ public void uninstallUI(JComponent c)
+ {
+ if (c instanceof JToolBar)
+ {
+ JToolBar tb = (JToolBar) c;
+ tb.setRollover(false);
+ }
+ super.uninstallUI(c);
+ }
+
+ /**
+ * Paints the background of the component if necessary and then calls
+ * <code>paint(g, c)</code>.
+ *
+ * This is overridden to implement the OceanTheme gradient when an OceanTheme
+ * is installed.
+ *
+ * @param g the graphics to use
+ * @param c the component to paint.
+ *
+ * @since 1.5
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ // TODO: Sun's implementation uses the MenuBar.gradient here.
+ // I would consider this a bug, but implement it like this
+ // for compatibility.
+ if (MetalLookAndFeel.getCurrentTheme() instanceof OceanTheme
+ && UIManager.get("MenuBar.gradient") != null)
+ {
+ if (c.isOpaque())
+ {
+ MetalUtils.paintGradient(g, 0, 0, c.getWidth(), c.getHeight(),
+ SwingConstants.VERTICAL,
+ "MenuBar.gradient");
+ }
+ paint(g, c);
+ }
+ else
+ {
+ super.update(g, c);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java
new file mode 100644
index 000000000..742ff2204
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java
@@ -0,0 +1,276 @@
+/* MetalToolTipUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.AbstractButton;
+import javax.swing.JComponent;
+import javax.swing.JMenuItem;
+import javax.swing.JToolTip;
+import javax.swing.KeyStroke;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicToolTipUI;
+
+/**
+ * A UI delegate for the {@link JToolTip} component.
+ */
+public class MetalToolTipUI
+ extends BasicToolTipUI
+{
+ /**
+ * The amount of space between the tool tip text and the accelerator
+ * description (if visible).
+ */
+ public static final int padSpaceBetweenStrings = 12;
+
+ /** The shared UI instance. */
+ private static MetalToolTipUI instance;
+
+ /** A flag controlling the visibility of the accelerator (if there is one). */
+ private boolean isAcceleratorHidden;
+
+ /** A string representing the accelerator key for the component. */
+ private String acceleratorString;
+
+ /**
+ * The delimiter for the accelerator string.
+ */
+ private String acceleratorDelimiter;
+
+ /** The font for the accelerator string. */
+ private Font acceleratorFont;
+
+ /** The color for the accelerator string. */
+ private Color acceleratorForeground;
+
+ /** The active border. */
+ private Border activeBorder;
+
+ /** The inactive border. */
+ private Border inactiveBorder;
+
+ /**
+ * Constructs a new instance of <code>MetalToolTipUI</code>.
+ */
+ public MetalToolTipUI()
+ {
+ super();
+ activeBorder = UIManager.getBorder("ToolTip.border");
+ inactiveBorder = UIManager.getBorder("ToolTip.borderInactive");
+ isAcceleratorHidden = UIManager.getBoolean("ToolTip.hideAccelerator");
+ acceleratorFont = UIManager.getFont("MenuItem.acceleratorFont");
+ acceleratorForeground = UIManager.getColor("MenuItem.acceleratorForeground");
+ acceleratorDelimiter = UIManager.getString("MenuItem.acceleratorDelimiter");
+ }
+
+ /**
+ * Returns a shared instance of the <code>MetalToolTipUI</code> class.
+ * Although this UI delegate does maintain state information, there is never
+ * more than one tool tip visible, so it is OK to use a shared instance.
+ *
+ * @param component the component (a {@link JToolTip}).
+ *
+ * @return A shared instance of the <code>MetalToolTipUI</code> class.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ if (instance == null)
+ instance = new MetalToolTipUI();
+ return instance;
+ }
+
+ /**
+ * Returns a string representing the accelerator key (if there is one) for
+ * the component that the tool tip belongs to.
+ *
+ * @return A string representing the accelerator key.
+ */
+ public String getAcceleratorString()
+ {
+ return acceleratorString;
+ }
+
+ /**
+ * Installs the UI for the specified component (a {@link JToolTip}).
+ *
+ * @param c the {@link JToolTip} component.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ Border existingBorder = c.getBorder();
+ if (existingBorder == null || existingBorder instanceof UIResource)
+ {
+ if (c.isEnabled())
+ c.setBorder(activeBorder);
+ else
+ c.setBorder(inactiveBorder);
+ }
+ }
+
+ /**
+ * Clears the defaults set in {@link #installUI(JComponent)}.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ if (c.getBorder() instanceof UIResource)
+ c.setBorder(null);
+ }
+
+ /**
+ * Returns <code>true</code> if the accelerator string is hidden, and
+ * <code>false</code> otherwise. This setting is controlled by the
+ * <code>ToolTip.hideAccelerator</code> entry in the UI defaults table.
+ *
+ * @return A boolean.
+ */
+ protected boolean isAcceleratorHidden()
+ {
+ return isAcceleratorHidden;
+ }
+
+ /**
+ * Returns the preferred size for the {@link JToolTip} component.
+ *
+ * @param c the component (a {@link JToolTip}).
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension d = super.getPreferredSize(c);
+ String acc = getAcceleratorString();
+ if (acc != null && ! acc.equals(""))
+ {
+ FontMetrics fm = c.getFontMetrics(c.getFont());
+ d.width += fm.stringWidth(acc);
+ }
+ return d;
+ }
+
+ /**
+ * Paints the tool tip.
+ *
+ * @param g the graphics context.
+ * @param c the {@link JToolTip} component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ super.paint(g, c);
+ // Somehow paint accelerator. Keep care for possible HTML rendering.
+ }
+
+ /**
+ * Returns a string representing the accelerator for the component, or
+ * <code>null</code> if the component has no accelerator.
+ *
+ * @param c the component.
+ *
+ * @return A string representing the accelerator (possibly
+ * <code>null</code>).
+ */
+ private String fetchAcceleratorString(JComponent c)
+ {
+ String result = null;
+ if (c instanceof JToolTip)
+ {
+ JToolTip toolTip = (JToolTip) c;
+ JComponent component = toolTip.getComponent();
+ KeyStroke ks = null;
+ int mne = 0;
+ if (component instanceof JMenuItem)
+ {
+ JMenuItem item = (JMenuItem) component;
+ ks = item.getAccelerator();
+ if (ks == null)
+ mne = item.getMnemonic();
+ }
+ else if (component instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) component;
+ mne = button.getMnemonic();
+ }
+ if (mne > 0)
+ ks = KeyStroke.getKeyStroke(Character.toUpperCase((char) mne),
+ InputEvent.ALT_MASK, false);
+ if (ks != null)
+ result = acceleratorToString(ks);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a string representing an accelerator.
+ *
+ * @param accelerator the accelerator (<code>null</code> not permitted).
+ *
+ * @return A string representing an accelerator.
+ */
+ private String acceleratorToString(KeyStroke accelerator)
+ {
+ // convert keystroke into string format
+ String modifiersText = "";
+ int modifiers = accelerator.getModifiers();
+ char keyChar = accelerator.getKeyChar();
+ int keyCode = accelerator.getKeyCode();
+
+ if (modifiers != 0)
+ modifiersText = KeyEvent.getKeyModifiersText(modifiers)
+ + acceleratorDelimiter;
+
+ if (keyCode == KeyEvent.VK_UNDEFINED)
+ return modifiersText + keyChar;
+ else
+ return modifiersText + KeyEvent.getKeyText(keyCode);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java
new file mode 100644
index 000000000..bdfa2d4f6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java
@@ -0,0 +1,317 @@
+/* MetalTreeUI.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JComponent;
+import javax.swing.JTree;
+import javax.swing.UIManager;
+import javax.swing.tree.TreePath;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicTreeUI;
+
+/**
+ * A UI delegate for the {@link JTree} component.
+ */
+public class MetalTreeUI extends BasicTreeUI
+{
+ /**
+ * Listens for property changes of the line style and updates the
+ * internal setting.
+ */
+ private class LineStyleListener
+ implements PropertyChangeListener
+ {
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(LINE_STYLE_PROPERTY))
+ decodeLineStyle(e.getNewValue());
+ }
+
+ }
+
+ /**
+ * The key to the lineStyle client property.
+ */
+ private static final String LINE_STYLE_PROPERTY = "JTree.lineStyle";
+
+ /**
+ * The property value indicating no line style.
+ */
+ private static final String LINE_STYLE_VALUE_NONE = "None";
+
+ /**
+ * The property value indicating angled line style.
+ */
+ private static final String LINE_STYLE_VALUE_ANGLED = "Angled";
+
+ /**
+ * The property value indicating horizontal line style.
+ */
+ private static final String LINE_STYLE_VALUE_HORIZONTAL = "Horizontal";
+
+ /**
+ * The line style for None.
+ */
+ private static final int LINE_STYLE_NONE = 0;
+
+ /**
+ * The line style for Angled.
+ */
+ private static final int LINE_STYLE_ANGLED = 1;
+
+ /**
+ * The line style for Horizontal.
+ */
+ private static final int LINE_STYLE_HORIZONTAL = 2;
+
+ /**
+ * The current line style.
+ */
+ private int lineStyle;
+
+ /**
+ * Listens for changes on the line style property and updates the
+ * internal settings.
+ */
+ private PropertyChangeListener lineStyleListener;
+
+ /**
+ * Constructs a new instance of <code>MetalTreeUI</code>.
+ */
+ public MetalTreeUI()
+ {
+ super();
+ }
+
+ /**
+ * Returns a new instance of <code>MetalTreeUI</code>.
+ *
+ * @param component the component for which we return an UI instance
+ *
+ * @return A new instance of <code>MetalTreeUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new MetalTreeUI();
+ }
+
+ /**
+ * The horizontal element of legs between nodes starts at the right of the
+ * left-hand side of the child node by default. This method makes the
+ * leg end before that.
+ */
+ protected int getHorizontalLegBuffer()
+ {
+ return super.getHorizontalLegBuffer();
+ }
+
+ /**
+ * Configures the specified component appropriate for the look and feel.
+ * This method is invoked when the ComponentUI instance is being installed
+ * as the UI delegate on the specified component. This method should completely
+ * configure the component for the look and feel, including the following:
+ * 1. Install any default property values for color, fonts, borders, icons,
+ * opacity, etc. on the component. Whenever possible, property values
+ * initialized by the client program should not be overridden.
+ * 2. Install a LayoutManager on the component if necessary.
+ * 3. Create/add any required sub-components to the component.
+ * 4. Create/install event listeners on the component.
+ * 5. Create/install a PropertyChangeListener on the component in order
+ * to detect and respond to component property changes appropriately.
+ * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component.
+ * 7. Initialize any appropriate instance data.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ Object lineStyleProp = c.getClientProperty(LINE_STYLE_PROPERTY);
+ decodeLineStyle(lineStyleProp);
+ if (lineStyleListener == null)
+ lineStyleListener = new LineStyleListener();
+ c.addPropertyChangeListener(lineStyleListener);
+ }
+
+ /**
+ * Reverses configuration which was done on the specified component during
+ * installUI. This method is invoked when this UIComponent instance is being
+ * removed as the UI delegate for the specified component. This method should
+ * undo the configuration performed in installUI, being careful to leave the
+ * JComponent instance in a clean state (no extraneous listeners,
+ * look-and-feel-specific property objects, etc.). This should include
+ * the following:
+ * 1. Remove any UI-set borders from the component.
+ * 2. Remove any UI-set layout managers on the component.
+ * 3. Remove any UI-added sub-components from the component.
+ * 4. Remove any UI-added event/property listeners from the component.
+ * 5. Remove any UI-installed keyboard UI from the component.
+ * 6. Nullify any allocated instance data objects to allow for GC.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ if (lineStyleListener != null)
+ c.removePropertyChangeListener(lineStyleListener);
+ lineStyleListener = null;
+ }
+
+ /**
+ * This function converts between the string passed into the client
+ * property and the internal representation (currently an int).
+ *
+ * @param lineStyleFlag - String representation
+ */
+ protected void decodeLineStyle(Object lineStyleFlag)
+ {
+ if (lineStyleFlag == null || lineStyleFlag.equals(LINE_STYLE_VALUE_ANGLED))
+ lineStyle = LINE_STYLE_ANGLED;
+ else if (lineStyleFlag.equals(LINE_STYLE_VALUE_HORIZONTAL))
+ lineStyle = LINE_STYLE_HORIZONTAL;
+ else if (lineStyleFlag.equals(LINE_STYLE_VALUE_NONE))
+ lineStyle = LINE_STYLE_NONE;
+ else
+ lineStyle = LINE_STYLE_ANGLED;
+ }
+
+ /**
+ * Checks if the location is in expand control.
+ *
+ * @param row - current row
+ * @param rowLevel - current level
+ * @param mouseX - current x location of the mouse click
+ * @param mouseY - current y location of the mouse click
+ */
+ protected boolean isLocationInExpandControl(int row, int rowLevel,
+ int mouseX, int mouseY)
+ {
+ return super.isLocationInExpandControl(tree.getPathForRow(row),
+ mouseX, mouseY);
+ }
+
+ /**
+ * Paints the specified component appropriate for the look and feel.
+ * This method is invoked from the ComponentUI.update method when the
+ * specified component is being painted. Subclasses should override this
+ * method and use the specified Graphics object to render the content of
+ * the component.
+ *
+ * @param g - the current graphics configuration.
+ * @param c - the current component to draw
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ // Calls BasicTreeUI's paint since it takes care of painting all
+ // types of icons.
+ super.paint(g, c);
+
+ if (lineStyle == LINE_STYLE_HORIZONTAL)
+ paintHorizontalSeparators(g, c);
+ }
+
+ /**
+ * Paints the horizontal separators.
+ *
+ * @param g - the current graphics configuration.
+ * @param c - the current component to draw
+ */
+ protected void paintHorizontalSeparators(Graphics g, JComponent c)
+ {
+ g.setColor(UIManager.getColor("Tree.line"));
+ Rectangle clip = g.getClipBounds();
+ int row0 = getRowForPath(tree, getClosestPathForLocation(tree, 0, clip.y));
+ int row1 =
+ getRowForPath(tree, getClosestPathForLocation(tree, 0,
+ clip.y + clip.height - 1));
+ if (row0 >= 0 && row1 >= 0)
+ {
+ for (int i = row0; i <= row1; i++)
+ {
+ TreePath p = getPathForRow(tree, i);
+ if (p != null && p.getPathCount() == 2)
+ {
+ Rectangle r = getPathBounds(tree, getPathForRow(tree, i));
+ if (r != null)
+ {
+ g.drawLine(clip.x, r.y, clip.x + clip.width, r.y);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Paints the vertical part of the leg. The receiver should NOT modify
+ * clipBounds, insets.
+ *
+ * @param g - the current graphics configuration.
+ * @param clipBounds -
+ * @param insets -
+ * @param path - the current path
+ */
+ protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
+ Insets insets, TreePath path)
+ {
+ if (lineStyle == LINE_STYLE_ANGLED)
+ super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
+ }
+
+ /**
+ * Paints the horizontal part of the leg. The receiver should NOT \
+ * modify clipBounds, or insets.
+ * NOTE: parentRow can be -1 if the root is not visible.
+ */
+ protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
+ Insets insets, Rectangle bounds,
+ TreePath path, int row,
+ boolean isExpanded, boolean hasBeenExpanded,
+ boolean isLeaf)
+ {
+ if (lineStyle == LINE_STYLE_ANGLED)
+ super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row,
+ isExpanded, hasBeenExpanded, isLeaf);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java
new file mode 100644
index 000000000..247f92265
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java
@@ -0,0 +1,597 @@
+/* MetalUtils.java
+Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GradientPaint;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.TexturePaint;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.util.List;
+
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+
+/**
+ * Some utility and helper methods for the Metal Look &amp; Feel.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+class MetalUtils
+{
+
+ /**
+ * The typical metal pattern for use with Graphics2D.
+ */
+ static BufferedImage pattern2D;
+
+ /**
+ * The light color to draw the pattern.
+ */
+ static Color lightColor;
+
+ /**
+ * The dark color to draw to draw the pattern.
+ */
+ static Color darkColor;
+
+ /**
+ * Fills a rectangle with the typical Metal pattern.
+ *
+ * @param g the <code>Graphics</code> context to use
+ * @param x the X coordinate of the upper left corner of the rectangle to
+ * fill
+ * @param y the Y coordinate of the upper left corner of the rectangle to
+ * fill
+ * @param w the width of the rectangle to fill
+ * @param h the height of the rectangle to fill
+ * @param light the light color to use
+ * @param dark the dark color to use
+ */
+ static void fillMetalPattern(Component c, Graphics g, int x, int y, int w, int h,
+ Color light, Color dark)
+ {
+ if (g instanceof Graphics2D
+ && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") == null)
+ fillMetalPattern2D((Graphics2D) g, x, y, w, h, light, dark);
+ else
+ {
+ int xOff = 0;
+ for (int mY = y; mY < (y + h); mY++)
+ {
+ // set color alternating with every line
+ if (((mY - y) % 2) == 0)
+ g.setColor(light);
+ else
+ g.setColor(dark);
+
+ for (int mX = x + xOff; mX < (x + w); mX += 4)
+ {
+ g.fillRect(mX, mY, 1, 1);
+ }
+
+ // increase x offset
+ xOff++;
+ if (xOff > 3)
+ xOff = 0;
+ }
+ }
+ }
+
+ /**
+ * Fills a rectangle with the typical Metal pattern using Java2D.
+ *
+ * @param g2d the <code>Graphics2D</code> context to use
+ * @param x the X coordinate of the upper left corner of the rectangle to
+ * fill
+ * @param y the Y coordinate of the upper left corner of the rectangle to
+ * fill
+ * @param w the width of the rectangle to fill
+ * @param h the height of the rectangle to fill
+ */
+ static void fillMetalPattern2D(Graphics2D g2d, int x, int y, int w, int h,
+ Color light, Color dark)
+ {
+ if (pattern2D == null || !darkColor.equals(dark) || !lightColor.equals(light))
+ initializePattern(light, dark);
+
+ // Prepare the texture.
+ TexturePaint texture =
+ new TexturePaint(pattern2D, new Rectangle2D.Double(0., 0., 4., 4.));
+ g2d.setPaint(texture);
+ g2d.fillRect(x, y, w, h);
+ }
+
+ /**
+ * Initializes the pattern image.
+ */
+ static void initializePattern(Color light, Color dark)
+ {
+ pattern2D = new BufferedImage(4, 4, BufferedImage.TYPE_INT_ARGB);
+ lightColor = light;
+ darkColor = dark;
+ Graphics g = pattern2D.getGraphics();
+ g.setColor(light);
+ g.fillRect(0, 0, 1, 1);
+ g.fillRect(2, 2, 1, 1);
+ g.setColor(dark);
+ g.fillRect(1, 1, 1, 1);
+ g.fillRect(3, 3, 1, 1);
+ g.dispose();
+ }
+
+ /**
+ * Paints the typical Metal gradient. See {@link #paintGradient(Graphics,
+ * int, int, int, int, float, float, Color, Color, Color, int, int[][])}
+ * for more details.
+ *
+ * This variant paints a gradient without a mask.
+ *
+ * @param g the graphics context to use
+ * @param x the X coordinate of the upper left corner of the rectangle
+ * @param y the Y coordinate of the upper left corner of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param dir the direction of the gradient, either
+ * @param uiProp the key of the UIManager property that has the parameters
+ */
+ static void paintGradient(Graphics g, int x, int y, int w, int h,
+ int dir, String uiProp)
+ {
+ paintGradient(g, x, y, w, h, dir, uiProp, null);
+ }
+
+ /**
+ * Paints the typical Metal gradient. See {@link #paintGradient(Graphics,
+ * int, int, int, int, float, float, Color, Color, Color, int, int[][])}
+ * for more details.
+ *
+ * The parameters are fetched from the UIManager using the key
+ * <code>uiProp</code>. The value is expected to be a {@link List} that
+ * contains 4 values: two {@link Double}s and 3 {@link Color} object that
+ * together make up the parameters passed to the painting method.
+ *
+ * @param g the graphics context to use
+ * @param x the X coordinate of the upper left corner of the rectangle
+ * @param y the Y coordinate of the upper left corner of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param dir the direction of the gradient, either
+ * @param uiProp the key of the UIManager property that has the parameters
+ * @param mask the mask that should be used when painting the gradient as
+ * described above
+ */
+ static void paintGradient(Graphics g, int x, int y, int w, int h,
+ int dir, String uiProp, int[][] mask)
+ {
+ List params = (List) UIManager.get(uiProp);
+ float g1 = ((Float) params.get(0)).floatValue();
+ float g2 = ((Float) params.get(1)).floatValue();
+ Color c1 = (Color) params.get(2);
+ Color c2 = (Color) params.get(3);
+ Color c3 = (Color) params.get(4);
+ paintGradient(g, x, y, w, h, g1, g2, c1, c2, c3, dir, mask);
+ }
+
+ /**
+ * Paints the typical Metal gradient. The gradient is painted as follows:
+ * <pre>
+ *
+ * +-------+--------+--------+-----------------------------+
+ * | | | | |
+ * +-------+--------+--------+-----------------------------+
+ * c1 -> c2 -- c2 -> c1 --------> c3
+ * < -g1- > < -g2- > < -g1- >
+ * </pre>
+ *
+ * There are 4 distinct areas in this gradient:
+ * <ol>
+ * <li>A gradient from color 1 to color 2 with the relative width specified
+ * by <code>g1</code></li>
+ * <li>A solid area with the color 2 and the relative width specified by
+ * <code>g2</code></li>
+ * <li>A gradient from color 2 to color 1 with the relative width specified
+ * by <code>g1</code></li>
+ *
+ * The <code>mask</code> parameter is an array if int arrays, where the first
+ * index specifies the row (in the gradient direction), and the second index
+ * is the starting and end offset of that line. This way you can specify a
+ * mask that should be laid over the gradient for paintint non-rectangular
+ * gradients. The following example should demonstrate this for painting
+ * a circular shaped gradient (note that the first and last line should not
+ * be drawn at all, they are only here to show the circular shape more
+ * clearly). Everything <em>inside</code> the surrounded area is filled by
+ * the gradient:
+ *
+ * <pre>
+ * 012345678
+ * xxx
+ * 0 x x { {4, 7},
+ * 1 x x {3, 8},
+ * 2 x x {3, 8},
+ * 3 x x {3, 8},
+ * 4 x x {4, 7} }
+ * xxx
+ * </pre>
+ *
+ * The <code>mask</code> array is expected to have <code>w</code> or
+ * <code>h</code> array elements, depending on the direction.
+ *
+ * If the <code>mask</code> parameter is null, then the gradient is painted
+ * without a mask.
+ *
+ * @param g the graphics context to use
+ * @param x the X coordinate of the upper left corner of the rectangle
+ * @param y the Y coordinate of the upper left corner of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param g1 the relative width of the c1->c2 gradients
+ * @param g2 the relative width of the c2 solid area
+ * @param c1 the color 1
+ * @param c2 the color 2
+ * @param c3 the color 3
+ * @param dir the direction of the gradient, either
+ * {@link SwingConstants#HORIZONTAL} or {@link SwingConstants#VERTICAL}
+ * @param mask the mask that should be used when painting the gradient as
+ * described above
+ */
+ static void paintGradient(Graphics g, int x, int y, int w, int h, float g1,
+ float g2, Color c1, Color c2, Color c3, int dir,
+ int[][] mask)
+ {
+ if (dir == SwingConstants.HORIZONTAL)
+ paintHorizontalGradient(g, x, y, w, h, g1, g2, c1, c2, c3, mask);
+ else
+ paintVerticalGradient(g, x, y, w, h, g1, g2, c1, c2, c3, mask);
+ }
+
+ /**
+ * Paints a horizontal gradient. See {@link #paintGradient(Graphics, int,
+ * int, int, int, float, float, Color, Color, Color, int, int[][])}
+ * for details.
+ *
+ * @param x the X coordinate of the upper left corner of the rectangle
+ * @param y the Y coordinate of the upper left corner of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param g1 the relative width of the c1->c2 gradients
+ * @param g2 the relative width of the c2 solid area
+ * @param c1 the color 1
+ * @param c2 the color 2
+ * @param c3 the color 3
+ * @param mask the mask that should be used when painting the gradient as
+ * described above
+ */
+ static void paintHorizontalGradient(Graphics g, int x, int y, int w, int h,
+ float g1, float g2, Color c1, Color c2,
+ Color c3, int[][] mask)
+ {
+
+ if (g instanceof Graphics2D
+ && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") == null)
+ {
+ paintHorizontalGradient2D((Graphics2D) g, x, y, w, h, g1, g2, c1, c2,
+ c3, mask);
+ return;
+ }
+
+ // Calculate the coordinates.
+ int y0 = y;
+ int y1 = y + h;
+ // The size of the first gradient area (c1->2).
+ int w1 = (int) (w * g1);
+ // The size of the solid c2 area.
+ int w2 = (int) (w * g2);
+ int x0 = x;
+ int x1 = x0 + w1;
+ int x2 = x1 + w2;
+ int x3 = x2 + w1;
+ int x4 = x + w;
+
+ // Paint first gradient area (c1->c2).
+ int xc; // The current y coordinate.
+ for (xc = x0; xc < x1; xc++)
+ {
+ if (xc > x + w)
+ break;
+
+ // Perform color interpolation;
+ double factor = (xc - x0) / (double) w1;
+ int rInt = (int) ((c2.getRed() - c1.getRed()) * factor + c1.getRed());
+ int gInt = (int) ((c2.getGreen() - c1.getGreen()) * factor
+ + c1.getGreen());
+ int bInt = (int) ((c2.getBlue() - c1.getBlue()) * factor
+ + c1.getBlue());
+ Color interpolated = new Color(rInt, gInt, bInt);
+ g.setColor(interpolated);
+ if (mask != null)
+ {
+ y0 = mask[xc - x0][0] + y;
+ y1 = mask[xc - x0][1] + y;
+ }
+ g.fillRect(xc, y0, 1, y1 - y0);
+ }
+ // Paint solid c2 area.
+ g.setColor(c2);
+ if (mask == null)
+ {
+ g.fillRect(x1, y, x2 - x1, h);
+ }
+ else
+ {
+ for (xc = x1; xc < x2; xc++)
+ {
+ y0 = mask[xc - x0][0] + y;
+ y1 = mask[xc - x0][1] + y;
+ g.fillRect(xc, y0, 1, y1 - y0);
+ }
+ }
+
+ // Paint second gradient area (c2->c1).
+ for (xc = x2; xc < x3; xc++)
+ {
+ if (xc > x + w)
+ break;
+
+ // Perform color interpolation;
+ double factor = (xc - x2) / (double) w1;
+ int rInt = (int) ((c1.getRed() - c2.getRed()) * factor + c2.getRed());
+ int gInt = (int) ((c1.getGreen() - c2.getGreen()) * factor
+ + c2.getGreen());
+ int bInt = (int) ((c1.getBlue() - c2.getBlue()) * factor
+ + c2.getBlue());
+ Color interpolated = new Color(rInt, gInt, bInt);
+ g.setColor(interpolated);
+ if (mask != null)
+ {
+ y0 = mask[xc - x0][0] + y;
+ y1 = mask[xc - x0][1] + y;
+ }
+ g.fillRect(xc, y0, 1, y1 - y0);
+ }
+
+ // Paint third gradient area (c1->c3).
+ for (xc = x3; xc < x4; xc++)
+ {
+ if (xc > x + w)
+ break;
+
+ // Perform color interpolation;
+ double factor = (xc - x3) / (double) (x4 - x3);
+ int rInt = (int) ((c3.getRed() - c1.getRed()) * factor + c1.getRed());
+ int gInt = (int) ((c3.getGreen() - c1.getGreen()) * factor
+ + c1.getGreen());
+ int bInt = (int) ((c3.getBlue() - c1.getBlue()) * factor
+ + c1.getBlue());
+ Color interpolated = new Color(rInt, gInt, bInt);
+ g.setColor(interpolated);
+ if (mask != null)
+ {
+ y0 = mask[xc - x0][0] + y;
+ y1 = mask[xc - x0][1] + y;
+ }
+ g.drawLine(xc, y0, xc, y1);
+ }
+ }
+
+ /**
+ * Paints a vertical gradient. See {@link #paintGradient(Graphics, int, int,
+ * int, int, float, float, Color, Color, Color, int, int[][])} for details.
+ *
+ * @param x the X coordinate of the upper left corner of the rectangle
+ * @param y the Y coordinate of the upper left corner of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param g1 the relative width of the c1->c2 gradients
+ * @param g2 the relative width of the c2 solid area
+ * @param c1 the color 1
+ * @param c2 the color 2
+ * @param c3 the color 3
+ * @param mask the mask that should be used when painting the gradient as
+ * described above
+ */
+ static void paintVerticalGradient(Graphics g, int x, int y, int w, int h,
+ float g1, float g2, Color c1, Color c2,
+ Color c3, int[][] mask)
+ {
+ if (g instanceof Graphics2D
+ && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") == null)
+ {
+ paintVerticalGradient2D((Graphics2D) g, x, y, w, h, g1, g2, c1, c2,
+ c3, mask);
+ return;
+ }
+
+ // Calculate the coordinates.
+ int x0 = x;
+ int x1 = x + w;
+ // The size of the first gradient area (c1->2).
+ int w1 = (int) (h * g1);
+ // The size of the solid c2 area.
+ int w2 = (int) (h * g2);
+ int y0 = y;
+ int y1 = y0 + w1;
+ int y2 = y1 + w2;
+ int y3 = y2 + w1;
+ int y4 = y + h;
+
+ // Paint first gradient area (c1->c2).
+ int yc; // The current y coordinate.
+ for (yc = y0; yc < y1; yc++)
+ {
+ if (yc > y + h)
+ break;
+
+ // Perform color interpolation;
+ double factor = (yc - y0) / (double) w1;
+ int rInt = (int) ((c2.getRed() - c1.getRed()) * factor + c1.getRed());
+ int gInt = (int) ((c2.getGreen() - c1.getGreen()) * factor
+ + c1.getGreen());
+ int bInt = (int) ((c2.getBlue() - c1.getBlue()) * factor
+ + c1.getBlue());
+ Color interpolated = new Color(rInt, gInt, bInt);
+ g.setColor(interpolated);
+ if (mask != null)
+ {
+ x0 = mask[yc - y0][0] + x;
+ x1 = mask[yc - y0][1] + x;
+ }
+ g.fillRect(x0, yc, x1 - x0, 1);
+ }
+ // Paint solid c2 area.
+ g.setColor(c2);
+ if (mask == null)
+ {
+ g.fillRect(x, y1, w, y2 - y1);
+ }
+ else
+ {
+ for (yc = y1; yc < y2; yc++)
+ {
+ x0 = mask[yc - y0][0] + x;
+ x1 = mask[yc - y0][1] + x;
+ g.fillRect(x0, yc, x1 - x0, 1);
+ }
+ }
+
+ // Paint second gradient area (c2->c1).
+ for (yc = y2; yc < y3; yc++)
+ {
+ if (yc > y + h)
+ break;
+
+ // Perform color interpolation;
+ double factor = (yc - y2) / (double) w1;
+ int rInt = (int) ((c1.getRed() - c2.getRed()) * factor + c2.getRed());
+ int gInt = (int) ((c1.getGreen() - c2.getGreen()) * factor
+ + c2.getGreen());
+ int bInt = (int) ((c1.getBlue() - c2.getBlue()) * factor
+ + c2.getBlue());
+ Color interpolated = new Color(rInt, gInt, bInt);
+ g.setColor(interpolated);
+ if (mask != null)
+ {
+ x0 = mask[yc - y0][0] + x;
+ x1 = mask[yc - y0][1] + x;
+ }
+ g.fillRect(x0, yc, x1 - x0, 1);
+ }
+
+ // Paint third gradient area (c1->c3).
+ for (yc = y3; yc < y4; yc++)
+ {
+ if (yc > y + h)
+ break;
+
+ // Perform color interpolation;
+ double factor = (yc - y3) / (double) (y4 - y3);
+ int rInt = (int) ((c3.getRed() - c1.getRed()) * factor + c1.getRed());
+ int gInt = (int) ((c3.getGreen() - c1.getGreen()) * factor
+ + c1.getGreen());
+ int bInt = (int) ((c3.getBlue() - c1.getBlue()) * factor
+ + c1.getBlue());
+ Color interpolated = new Color(rInt, gInt, bInt);
+ g.setColor(interpolated);
+ if (mask != null)
+ {
+ x0 = mask[yc - y0][0] + x;
+ x1 = mask[yc - y0][1] + x;
+ }
+ g.fillRect(x0, yc, x1 - x0, 1);
+ }
+ }
+
+ /**
+ * Paints a horizontal gradient using Graphics2D functionality.
+ *
+ * @param g the Graphics2D instance
+ * @param x the X coordinate of the upper left corner of the rectangle
+ * @param y the Y coordinate of the upper left corner of the rectangle
+ * @param w the width of the rectangle
+ * @param h the height of the rectangle
+ * @param g1 the relative width of the c1->c2 gradients
+ * @param g2 the relative width of the c2 solid area
+ * @param c1 the color 1
+ * @param c2 the color 2
+ * @param c3 the color 3
+ * @param mask the mask that should be used when painting the gradient as
+ * described above
+ */
+ private static void paintHorizontalGradient2D(Graphics2D g, int x, int y,
+ int w, int h, float g1,
+ float g2, Color c1,
+ Color c2, Color c3,
+ int[][] mask)
+ {
+ // FIXME: Handle the mask somehow, or do Graphics2D clipping instead.
+ GradientPaint p1 = new GradientPaint(x, y, c1, x + w * g1, y, c2);
+ g.setPaint(p1);
+ // This fills the first gradient and the solid area in one go.
+ g.fillRect(x, y, (int) (w * (g1 + g2)), h);
+
+ GradientPaint p2 = new GradientPaint(x + (w * (g1 + g2)), y, c2, x + w, y,
+ c3);
+ g.setPaint(p2);
+ g.fillRect((int) (x + (w * (g1 + g2))), y,
+ (int) (w * (1. - (g1 + g2))), h);
+ }
+
+ private static void paintVerticalGradient2D(Graphics2D g, int x, int y,
+ int w, int h, float g1,
+ float g2, Color c1,
+ Color c2, Color c3,
+ int[][] mask)
+ {
+ // FIXME: Handle the mask somehow, or do Graphics2D clipping instead.
+ GradientPaint p1 = new GradientPaint(x, y, c1, x, y + h * g1, c2);
+ g.setPaint(p1);
+ // This fills the first gradient and the solid area in one go.
+ g.fillRect(x, y, w, (int) (h * (g1 + g2)));
+
+ GradientPaint p2 = new GradientPaint(x, y + (h * (g1 + g2)), c2, x, y + h,
+ c3);
+ g.setPaint(p2);
+ g.fillRect(x, (int) (y + (h * (g1 + g2))), w,
+ (int) (h * (1. - (g1 + g2))));
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java
new file mode 100644
index 000000000..6a6a3f5aa
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java
@@ -0,0 +1,318 @@
+/* DefaultMetalTheme.java -- A modern theme for the Metal L&F
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.metal;
+
+import java.awt.Color;
+import java.awt.Insets;
+import java.util.Arrays;
+
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.BorderUIResource.LineBorderUIResource;
+
+/**
+ * A modern theme for the Metal Look &amp; Feel.
+ * @since 1.5
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class OceanTheme extends DefaultMetalTheme
+{
+ /**
+ * The OceanTheme value for black.
+ */
+ static final ColorUIResource BLACK = new ColorUIResource(51, 51, 51);
+
+ /**
+ * The OceanTheme value for primary1.
+ */
+ static final ColorUIResource PRIMARY1 = new ColorUIResource(99, 130, 191);
+
+ /**
+ * The OceanTheme value for primary1.
+ */
+ static final ColorUIResource PRIMARY2 = new ColorUIResource(163, 184, 204);
+
+ /**
+ * The OceanTheme value for primary1.
+ */
+ static final ColorUIResource PRIMARY3 = new ColorUIResource(184, 207, 229);
+
+ /**
+ * The OceanTheme value for secondary1.
+ */
+ static final ColorUIResource SECONDARY1 = new ColorUIResource(122, 138, 153);
+
+ /**
+ * The OceanTheme value for secondary2.
+ */
+ static final ColorUIResource SECONDARY2 = new ColorUIResource(184, 207, 229);
+
+ /**
+ * The OceanTheme value for secondary3.
+ */
+ static final ColorUIResource SECONDARY3 = new ColorUIResource(238, 238, 238);
+
+ /**
+ * The OceanTheme value for inactive control text.
+ */
+ static final ColorUIResource INACTIVE_CONTROL_TEXT =
+ new ColorUIResource(153, 153, 153);
+
+ /**
+ * Returns the name of this theme, &quot;Ocean&quot;
+ */
+ public String getName()
+ {
+ return "Ocean";
+ }
+
+ /**
+ * Returns the color for control text, which is the
+ * value of the theme's black value.
+ */
+ public ColorUIResource getControlTextColor()
+ {
+ return getBlack();
+ }
+
+ /**
+ * Returns the desktop color, which is the theme's white color.
+ */
+ public ColorUIResource getDesktopColor()
+ {
+ return getWhite();
+ }
+
+ /**
+ * Returns the color for inactive control text, which is the
+ * RGB value (153, 153, 153).
+ */
+ public ColorUIResource getInactiveControlTextColor()
+ {
+ return INACTIVE_CONTROL_TEXT;
+ }
+
+ /**
+ * Returns the OceanTheme's color for disabled menu foreground,
+ *
+ */
+ public ColorUIResource getMenuDisabledForeground()
+ {
+ return INACTIVE_CONTROL_TEXT;
+ }
+
+
+ /**
+ * Returns the OceanTheme's color for black, the RGB value
+ * (51, 51, 51).
+ *
+ * @return Returns the OceanTheme's value for black
+ */
+ protected ColorUIResource getBlack()
+ {
+ return BLACK;
+ }
+
+ /**
+ * Return the OceanTheme's value for primary 1, the RGB value
+ * (99, 130, 191).
+ */
+ protected ColorUIResource getPrimary1()
+ {
+ return PRIMARY1;
+ }
+
+ /**
+ * Return the OceanTheme's value for primary 2, the RGB value
+ * (163, 184, 204).
+ */
+ protected ColorUIResource getPrimary2()
+ {
+ return PRIMARY2;
+ }
+
+ /**
+ * Return the OceanTheme's value for primary 1, the RGB value
+ * (184, 207, 229).
+ */
+ protected ColorUIResource getPrimary3()
+ {
+ return PRIMARY3;
+ }
+
+ /**
+ * Return the OceanTheme's value for secondary 1, the RGB value
+ * (122, 138, 153).
+ */
+ protected ColorUIResource getSecondary1()
+ {
+ return SECONDARY1;
+ }
+
+ /**
+ * Return the OceanTheme's value for secondary 2, the RGB value
+ * (184, 207, 229).
+ */
+ protected ColorUIResource getSecondary2()
+ {
+ return SECONDARY2;
+ }
+ /**
+ * Return the OceanTheme's value for secondary 3, the RGB value
+ * (238, 238, 238).
+ */
+ protected ColorUIResource getSecondary3()
+ {
+ return SECONDARY3;
+ }
+
+ /**
+ * Adds customized entries to the UIDefaults table.
+ *
+ * @param defaults the UI defaults table
+ */
+ public void addCustomEntriesToTable(UIDefaults defaults)
+ {
+ // Gradients.
+ defaults.put("Button.gradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.0), new ColorUIResource(221, 232, 243),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("CheckBox.gradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.0), new ColorUIResource(221, 232, 243),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("CheckBoxMenuItem.gradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.0), new ColorUIResource(221, 232, 243),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("MenuBar.gradient", Arrays.asList(new Object[]
+ {new Float(1.0), new Float(0.0), new ColorUIResource(Color.WHITE),
+ new ColorUIResource(218, 218, 218), new ColorUIResource(218, 218, 218)}));
+ defaults.put("RadioButton.gradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.0), new ColorUIResource(221, 232, 243),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("RadioButtonMenuItem.gradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.0), new ColorUIResource(221, 232, 243),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("ScrollBar.gradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.0), new ColorUIResource(221, 232, 243),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("Slider.gradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.2), new ColorUIResource(200, 221, 242),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("Slider.focusGradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.2), new ColorUIResource(200, 221, 242),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("ToggleButton.gradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.0), new ColorUIResource(221, 232, 243),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+ defaults.put("InternalFrame.activeTitleGradient", Arrays.asList(new Object[]
+ {new Float(0.3), new Float(0.0), new ColorUIResource(221, 232, 243),
+ new ColorUIResource(Color.WHITE), new ColorUIResource(184, 207, 229)}));
+
+ // Colors.
+ ColorUIResource c1 = new ColorUIResource(200, 221, 242);
+ ColorUIResource c2 = new ColorUIResource(153, 153, 153);
+ ColorUIResource c3 = new ColorUIResource(204, 204, 204);
+ ColorUIResource c4 = new ColorUIResource(210, 226, 239);
+ ColorUIResource c5 = new ColorUIResource(218, 218, 218);
+ defaults.put("Button.disabledToolBarBorderBackground", c3);
+ defaults.put("Button.toolBarBorderBackground", c2);
+ defaults.put("Label.disabledForeground", c2);
+ defaults.put("MenuBar.borderColor", c3);
+ defaults.put("Slider.altTrackColor", c4);
+ defaults.put("SplitPane.dividerFocusColor", c1);
+ defaults.put("TabbedPane.contentAreaColor", c1);
+ defaults.put("TabbedPane.borderHightlightColor", PRIMARY1);
+ defaults.put("TabbedPane.selected", c1);
+ defaults.put("TabbedPane.tabAreaBackground", c5);
+ defaults.put("TabbedPane.unselectedBackground", SECONDARY3);
+ defaults.put("Table.gridColor", SECONDARY1);
+ defaults.put("ToolBar.borderColor", c3);
+ defaults.put("Tree.selectionBorderColor", PRIMARY1);
+
+ // Borders.
+ defaults.put("List.focusCellHighlightBorder",
+ new LineBorderUIResource(getPrimary1()));
+ defaults.put("Table.focusCellHighlightBorder",
+ new LineBorderUIResource(getPrimary1()));
+
+ // Insets.
+ defaults.put("TabbedPane.contentBorderInsets", new Insets(4, 2, 3, 3));
+ defaults.put("TabbedPane.tabAreaInsets", new Insets(2, 2, 0, 6));
+
+ // Flags.
+ defaults.put("SplitPane.oneTouchButtonsOpaque", Boolean.FALSE);
+ defaults.put("Menu.opaque", Boolean.FALSE);
+ defaults.put("ToolBar.isRollover", Boolean.TRUE);
+ defaults.put("RadioButton.rollover", Boolean.TRUE);
+ defaults.put("CheckBox.rollover", Boolean.TRUE);
+ defaults.put("Button.rollover", Boolean.TRUE);
+
+ // Icons.
+ // FIXME: Add OceanTheme icons.
+// defaults.put("Tree.leafIcon", XXX);
+// defaults.put("Tree.expandedIcon", XXX);
+// defaults.put("Tree.openIcon", XXX);
+// defaults.put("Tree.closedIcon", XXX);
+// defaults.put("Tree.collapsedIcon", XXX);
+// defaults.put("FileChooser.newFolderIcon", XXX);
+// defaults.put("FileChooser.homeFolderIcon", XXX);
+// defaults.put("FileChooser.upFolderIcon", XXX);
+// defaults.put("FileView.hardDriveIcon", XXX);
+// defaults.put("FileView.floppyDriveIcon", XXX);
+// defaults.put("FileView.fileIcon", XXX);
+// defaults.put("FileView.computerIcon", XXX);
+// defaults.put("FileView.directoryIcon", XXX);
+// defaults.put("OptionPane.questionIcon", XXX);
+// defaults.put("OptionPane.errorIcon", XXX);
+// defaults.put("OptionPane.warningIcon", XXX);
+// defaults.put("OptionPane.informationIcon", XXX);
+// defaults.put("InternalFrame.icon", XXX);
+// defaults.put("InternalFrame.closeIcon", XXX);
+// defaults.put("InternalFrame.iconifyIcon", XXX);
+// defaults.put("InternalFrame.minimizeIcon", XXX);
+// defaults.put("InternalFrame.maximizeIcon", XXX);
+// defaults.put("InternalFrame.paletteCloseIcon", XXX);
+
+ // UI classes.
+ defaults.put("MenuBarUI", "javax.swing.plaf.metal.MetalMenuBarUI");
+
+ // Others.
+ defaults.put("Button.rolloverIconType", "ocean");
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/metal/package.html b/libjava/classpath/javax/swing/plaf/metal/package.html
new file mode 100644
index 000000000..8675493b6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/metal/package.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.plaf.metal package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.plaf.metal</title></head>
+
+<body>
+<p>Provides a cross-platform look and feel known as "Metal". To install this
+look and feel, add the following code (or something similar)
+near the start of your application:</p>
+<pre>try
+ {
+&nbsp;&nbsp;UIManager.setLookAndFeel(new MetalLookAndFeel());
+ }
+catch (UnsupportedLookAndFeelException e)
+ {
+&nbsp;&nbsp;e.printStackTrace();
+ }</pre>
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiButtonUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiButtonUI.java
new file mode 100644
index 000000000..044f65141
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiButtonUI.java
@@ -0,0 +1,352 @@
+/* MultiButtonUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ButtonUI;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ButtonUI} instances, one
+ * from the primary look and feel, and one or more from the auxiliary look and
+ * feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiButtonUI extends ButtonUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiButtonUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiButtonUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiButtonUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiButtonUI mui = new MultiButtonUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiButtonUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiButtonUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiButtonUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiButtonUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiButtonUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiButtonUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiButtonUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiButtonUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiButtonUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiButtonUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiButtonUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiColorChooserUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiColorChooserUI.java
new file mode 100644
index 000000000..1a9684928
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiColorChooserUI.java
@@ -0,0 +1,352 @@
+/* MultiColorChooserUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ColorChooserUI;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ColorChooserUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiColorChooserUI extends ColorChooserUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiColorChooserUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiColorChooserUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiColorChooserUI</code>
+ * is returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiColorChooserUI mui = new MultiColorChooserUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiColorChooserUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiColorChooserUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiColorChooserUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiColorChooserUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiColorChooserUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiColorChooserUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiColorChooserUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiColorChooserUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiColorChooserUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiColorChooserUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiColorChooserUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiComboBoxUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiComboBoxUI.java
new file mode 100644
index 000000000..f16987399
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiComboBoxUI.java
@@ -0,0 +1,430 @@
+/* MultiComboBoxUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComboBoxUI;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ComboBoxUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiComboBoxUI extends ComboBoxUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiComboBoxUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiComboBoxUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiComboBoxUI</code>
+ * is returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiComboBoxUI mui = new MultiComboBoxUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiComboBoxUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiComboBoxUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiComboBoxUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiComboBoxUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComboBoxUI#setPopupVisible(JComboBox, boolean)} method
+ * for all the UI delegates managed by this <code>MultiComboBoxUI</code>.
+ *
+ * @param c the component.
+ * @param visible the visible state.
+ */
+ public void setPopupVisible(JComboBox c, boolean visible)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComboBoxUI ui = (ComboBoxUI) iterator.next();
+ ui.setPopupVisible(c, visible);
+ }
+ }
+
+ /**
+ * Calls the {@link ComboBoxUI#isPopupVisible(JComboBox)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The result for the UI delegate from the primary look and feel.
+ */
+ public boolean isPopupVisible(JComboBox c)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComboBoxUI ui = (ComboBoxUI) iterator.next();
+ result = ui.isPopupVisible(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComboBoxUI ui = (ComboBoxUI) iterator.next();
+ /* boolean ignored = */ ui.isPopupVisible(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComboBoxUI#isFocusTraversable(JComboBox)} method for all
+ * the UI delegates managed by this <code>MultiComboBoxUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return <code>true</code> if the combo box is traversable according to the
+ * UI delegate in the primary look and feel, and <code>false</code>
+ * otherwise.
+ */
+ public boolean isFocusTraversable(JComboBox c)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComboBoxUI ui = (ComboBoxUI) iterator.next();
+ result = ui.isFocusTraversable(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComboBoxUI ui = (ComboBoxUI) iterator.next();
+ /* boolean ignored = */ ui.isFocusTraversable(c);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiDesktopIconUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiDesktopIconUI.java
new file mode 100644
index 000000000..091566ecf
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiDesktopIconUI.java
@@ -0,0 +1,352 @@
+/* MultiDesktopIconUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.DesktopIconUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link DesktopIconUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiDesktopIconUI extends DesktopIconUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiDesktopIconUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiDesktopIconUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiDesktopIconUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiDesktopIconUI mui = new MultiDesktopIconUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopIconUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopIconUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiDesktopIconUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiDesktopIconUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopIconUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiDesktopIconUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopIconUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopIconUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopIconUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiDesktopIconUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiDesktopIconUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiDesktopPaneUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiDesktopPaneUI.java
new file mode 100644
index 000000000..fb62257b8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiDesktopPaneUI.java
@@ -0,0 +1,352 @@
+/* MultiDesktopIconUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.DesktopPaneUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link DesktopPaneUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiDesktopPaneUI extends DesktopPaneUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiDesktopPaneUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiDesktopPaneUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiDesktopPaneUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiDesktopPaneUI mui = new MultiDesktopPaneUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiDesktopPaneUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiDesktopPaneUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiDesktopPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopPaneUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopPaneUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiDesktopPaneUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiDesktopPaneUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiDesktopPaneUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiFileChooserUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiFileChooserUI.java
new file mode 100644
index 000000000..2ab8841f9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiFileChooserUI.java
@@ -0,0 +1,511 @@
+/* MultiFileChooserUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.io.File;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileView;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.FileChooserUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link FileChooserUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiFileChooserUI extends FileChooserUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiFileChooserUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiFileChooserUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiFileChooserUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiFileChooserUI mui = new MultiFileChooserUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiFileChooserUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiFileChooserUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiFileChooserUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiFileChooserUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiFileChooserUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link FileChooserUI#getAcceptAllFileFilter(JFileChooser)} method
+ * for all the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the filter for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param chooser the file chooser.
+ *
+ * @return The filter returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public FileFilter getAcceptAllFileFilter(JFileChooser chooser)
+ {
+ FileFilter result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ result = ui.getAcceptAllFileFilter(chooser);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ /* FileFilter ignored = */ ui.getAcceptAllFileFilter(chooser);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link FileChooserUI#getFileView(JFileChooser)} method
+ * for all the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the view for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param chooser the file chooser.
+ *
+ * @return The view returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public FileView getFileView(JFileChooser chooser)
+ {
+ FileView result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ result = ui.getFileView(chooser);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ /* FileView ignored = */ ui.getFileView(chooser);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link FileChooserUI#getApproveButtonText(JFileChooser)} method
+ * for all the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the text for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param chooser the file chooser.
+ *
+ * @return The text returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public String getApproveButtonText(JFileChooser chooser)
+ {
+ String result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ result = ui.getApproveButtonText(chooser);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ /* String ignored = */ ui.getApproveButtonText(chooser);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link FileChooserUI#getDialogTitle(JFileChooser)} method
+ * for all the UI delegates managed by this <code>MultiFileChooserUI</code>,
+ * returning the title for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param chooser the file chooser.
+ *
+ * @return The title returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public String getDialogTitle(JFileChooser chooser)
+ {
+ String result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ result = ui.getDialogTitle(chooser);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ /* String ignored = */ ui.getDialogTitle(chooser);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link FileChooserUI#rescanCurrentDirectory(JFileChooser)}
+ * method for all the UI delegates managed by this
+ * <code>MultiFileChooserUI</code>.
+ *
+ * @param chooser the file chooser.
+ */
+ public void rescanCurrentDirectory(JFileChooser chooser)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ ui.rescanCurrentDirectory(chooser);
+ }
+ }
+
+ /**
+ * Calls the {@link FileChooserUI#ensureFileIsVisible(JFileChooser, File)}
+ * method for all the UI delegates managed by this
+ * <code>MultiFileChooserUI</code>.
+ *
+ * @param chooser the file chooser.
+ * @param file the file.
+ */
+ public void ensureFileIsVisible(JFileChooser chooser, File file)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ FileChooserUI ui = (FileChooserUI) iterator.next();
+ ui.ensureFileIsVisible(chooser, file);
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiInternalFrameUI.java
new file mode 100644
index 000000000..37b2ee3af
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiInternalFrameUI.java
@@ -0,0 +1,353 @@
+/* MultiInternalFrameUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.InternalFrameUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link InternalFrameUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiInternalFrameUI extends InternalFrameUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiInternalFrameUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiInternalFrameUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiInternalFrameUI</code>
+ * is returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiInternalFrameUI mui = new MultiInternalFrameUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiInternalFrameUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiInternalFrameUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiInternalFrameUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiInternalFrameUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiInternalFrameUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiInternalFrameUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiInternalFrameUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiInternalFrameUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiInternalFrameUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiInternalFrameUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiInternalFrameUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiLabelUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiLabelUI.java
new file mode 100644
index 000000000..2f0e7d06e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiLabelUI.java
@@ -0,0 +1,352 @@
+/* MultiLabelUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.LabelUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link LabelUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiLabelUI extends LabelUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiLabelUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiLabelUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiLabelUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiLabelUI mui = new MultiLabelUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiLabelUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiLabelUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiLabelUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiLabelUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiLabelUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiLabelUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiLabelUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiLabelUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiLabelUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiLabelUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiLabelUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiListUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiListUI.java
new file mode 100644
index 000000000..d6b77f5b4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiListUI.java
@@ -0,0 +1,449 @@
+/* MultiListUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ListUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ListUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiListUI extends ListUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiListUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiListUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiListUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiListUI mui = new MultiListUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiListUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiListUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ListUI#locationToIndex(JList, Point)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the index for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param list the list.
+ * @param location the location.
+ *
+ * @return The index returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int locationToIndex(JList list, Point location)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ListUI ui = (ListUI) iterator.next();
+ result = ui.locationToIndex(list, location);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ListUI ui = (ListUI) iterator.next();
+ /* int ignored = */ ui.locationToIndex(list, location);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ListUI#indexToLocation(JList, int)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the location for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param list the list.
+ * @param index the index.
+ *
+ * @return The location returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Point indexToLocation(JList list, int index)
+ {
+ Point result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ListUI ui = (ListUI) iterator.next();
+ result = ui.indexToLocation(list, index);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ListUI ui = (ListUI) iterator.next();
+ /* Point ignored = */ ui.indexToLocation(list, index);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ListUI#getCellBounds(JList, int, int)} method for all
+ * the UI delegates managed by this <code>MultiListUI</code>,
+ * returning the bounds for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param list the list.
+ * @param index1 the first index.
+ * @param index2 the second index.
+ *
+ * @return The bounds returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Rectangle getCellBounds(JList list, int index1, int index2)
+ {
+ Rectangle result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ListUI ui = (ListUI) iterator.next();
+ result = ui.getCellBounds(list, index1, index2);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ListUI ui = (ListUI) iterator.next();
+ /* Rectangle ignored = */ ui.getCellBounds(list, index1, index2);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java b/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java
new file mode 100644
index 000000000..4e81fd054
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java
@@ -0,0 +1,243 @@
+/* MultiLookAndFeel.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.util.Vector;
+
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A look and feel that provides the ability to use auxiliary look and feels
+ * in addition to the primary look and feel.
+ */
+public class MultiLookAndFeel extends LookAndFeel
+{
+
+ /**
+ * Creates a new instance of the look and feel.
+ */
+ public MultiLookAndFeel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the name for the look and feel.
+ *
+ * @return "Multiplexing Look and Feel".
+ */
+ public String getName()
+ {
+ return "Multiplexing Look and Feel";
+ }
+
+ /**
+ * Returns an identifier for the look and feel.
+ *
+ * @return "Multiplex".
+ */
+ public String getID()
+ {
+ return "Multiplex";
+ }
+
+ /**
+ * Returns a description of the look and feel.
+ *
+ * @return A description of the look and feel.
+ */
+ public String getDescription()
+ {
+ return "Allows multiple UI instances per component instance";
+ }
+
+ /**
+ * Returns <code>false</code> to indicate that this look and feel is not
+ * native to any platform.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isNativeLookAndFeel()
+ {
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> always, since this look and feel is supported on
+ * all platforms.
+ *
+ * @return <code>true</code>.
+ */
+ public boolean isSupportedLookAndFeel()
+ {
+ return true;
+ }
+
+ /**
+ * Creates and returns the UI defaults for this look and feel.
+ *
+ * @return The UI defaults.
+ */
+ public UIDefaults getDefaults()
+ {
+ UIDefaults defaults = new UIDefaults();
+ defaults.put("ButtonUI", "javax.swing.plaf.multi.MultiButtonUI");
+ defaults.put("CheckBoxUI", "javax.swing.plaf.multi.MultiButtonUI");
+ defaults.put("CheckBoxMenuItemUI", "javax.swing.plaf.multi.MultiMenuItemUI");
+ defaults.put("ColorChooserUI",
+ "javax.swing.plaf.multi.MultiColorChooserUI");
+ defaults.put("ComboBoxUI", "javax.swing.plaf.multi.MultiComboBoxUI");
+ defaults.put("DesktopPaneUI", "javax.swing.plaf.multi.MultiDesktopPaneUI");
+ defaults.put("DesktopIconUI", "javax.swing.plaf.multi.MultiDesktopIconUI");
+ defaults.put("EditorPaneUI", "javax.swing.plaf.multi.MultiTextUI");
+ defaults.put("FileChooserUI", "javax.swing.plaf.multi.MultiFileChooserUI");
+ defaults.put("FormattedTextFieldUI", "javax.swing.plaf.multi.MultiTextUI");
+ defaults.put("InternalFrameUI",
+ "javax.swing.plaf.multi.MultiInternalFrameUI");
+ defaults.put("LabelUI", "javax.swing.plaf.multi.MultiLabelUI");
+ defaults.put("ListUI", "javax.swing.plaf.multi.MultiListUI");
+ defaults.put("MenuItemUI", "javax.swing.plaf.multi.MultiMenuItemUI");
+ defaults.put("MenuUI", "javax.swing.plaf.multi.MultiMenuItemUI");
+ defaults.put("MenuBarUI", "javax.swing.plaf.multi.MultiMenuBarUI");
+ defaults.put("OptionPaneUI", "javax.swing.plaf.multi.MultiOptionPaneUI");
+ defaults.put("PanelUI", "javax.swing.plaf.multi.MultiPanelUI");
+ defaults.put("PasswordFieldUI", "javax.swing.plaf.multi.MultiTextUI");
+ defaults.put("PopupMenuUI", "javax.swing.plaf.multi.MultiPopupMenuUI");
+ defaults.put("PopupMenuSeparatorUI",
+ "javax.swing.plaf.multi.MultiSeparatorUI");
+ defaults.put("ProgressBarUI", "javax.swing.plaf.multi.MultiProgressBarUI");
+ defaults.put("RadioButtonUI", "javax.swing.plaf.multi.MultiButtonUI");
+ defaults.put("RadioButtonMenuItemUI",
+ "javax.swing.plaf.multi.MultiMenuItemUI");
+ defaults.put("RootPaneUI", "javax.swing.plaf.multi.MultiRootPaneUI");
+ defaults.put("ScrollBarUI", "javax.swing.plaf.multi.MultiScrollBarUI");
+ defaults.put("ScrollPaneUI", "javax.swing.plaf.multi.MultiScrollPaneUI");
+ defaults.put("SeparatorUI", "javax.swing.plaf.multi.MultiSeparatorUI");
+ defaults.put("SliderUI", "javax.swing.plaf.multi.MultiSliderUI");
+ defaults.put("SpinnerUI", "javax.swing.plaf.multi.MultiSpinnerUI");
+ defaults.put("SplitPaneUI", "javax.swing.plaf.multi.MultiSplitPaneUI");
+ defaults.put("TabbedPaneUI", "javax.swing.plaf.multi.MultiTabbedPaneUI");
+ defaults.put("TableHeaderUI", "javax.swing.plaf.multi.MultiTableHeaderUI");
+ defaults.put("TableUI", "javax.swing.plaf.multi.MultiTableUI");
+ defaults.put("TextAreaUI", "javax.swing.plaf.multi.MultiTextUI");
+ defaults.put("TextFieldUI", "javax.swing.plaf.multi.MultiTextUI");
+ defaults.put("TextPaneUI", "javax.swing.plaf.multi.MultiTextUI");
+ defaults.put("ToggleButtonUI", "javax.swing.plaf.multi.MultiButtonUI");
+ defaults.put("ToolBarSeparatorUI",
+ "javax.swing.plaf.multi.MultiSeparatorUI");
+ defaults.put("ToolBarUI", "javax.swing.plaf.multi.MultiToolBarUI");
+ defaults.put("ToolTipUI", "javax.swing.plaf.multi.MultiToolTipUI");
+ defaults.put("ViewportUI", "javax.swing.plaf.multi.MultiViewportUI");
+ return defaults;
+ }
+
+ /**
+ * Creates the UI delegates for the <code>target</code> component and
+ * returns a multiplexing UI delegate (<code>mui</code>) if there are
+ * multiple delegates.
+ *
+ * @param mui a multiplexing UI delegate appropriate for the component.
+ * @param uis a vector into which the UI delegates will be added.
+ * @param target the target component.
+ *
+ * @return A UI delegate.
+ */
+ public static ComponentUI createUIs(ComponentUI mui, Vector uis,
+ JComponent target)
+ {
+ // get primary UI delegate for 'target', and add it to uis
+ ComponentUI ui = null;
+ LookAndFeel primary = UIManager.getLookAndFeel();
+ if (primary != null)
+ {
+ ui = UIManager.getUI(target);
+ uis.add(ui);
+ }
+ // for any auxiliary look and feels in use, get the UI delegate and add
+ // it to uis
+ LookAndFeel[] auxlafs = UIManager.getAuxiliaryLookAndFeels();
+ for (int i = 0; i < auxlafs.length; i++)
+ {
+ LookAndFeel auxlaf = auxlafs[i];
+ // FIXME: here I call getDefaults() to get the UI delegate from the
+ // auxiliary look and feel. But getDefaults() creates a new set of
+ // defaults every time it is called, which is wasteful. Unfortunately
+ // I cannot find another way to get the UI delegate, so I'm doing it
+ // anyway...
+ UIDefaults defaults = auxlaf.getDefaults();
+ ui = defaults.getUI(target);
+ if (ui != null)
+ uis.add(ui);
+ }
+ // if uis contains more than 1 delegate, return mui, otherwise return
+ // the primary delegate
+ if (uis.size() > 1)
+ return mui;
+ else
+ return ui;
+ }
+
+ /**
+ * Returns an array containing the same {@link ComponentUI} instances as
+ * <code>uis</code>. If <code>uis</code> is <code>null</code>, a zero-length
+ * array is returned.
+ *
+ * @param uis a list of {@link ComponentUI} references (<code>null</code>
+ * permitted).
+ *
+ * @return An array containing the same {@link ComponentUI} instances as
+ * <code>uis</code>, or <code>null</code> if <code>uis</code> is
+ * empty.
+ */
+ protected static ComponentUI[] uisToArray(Vector uis)
+ {
+ if (uis == null)
+ return new ComponentUI[0];
+ int size = uis.size();
+ if (size == 0)
+ return null;
+ ComponentUI[] result = new ComponentUI[size];
+ uis.copyInto(result);
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiMenuBarUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiMenuBarUI.java
new file mode 100644
index 000000000..5b43f6bd8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiMenuBarUI.java
@@ -0,0 +1,352 @@
+/* MultiMenuBarUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuBarUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link MenuBarUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiMenuBarUI extends MenuBarUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiMenuBarUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiMenuBarUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiMenuBarUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiMenuBarUI mui = new MultiMenuBarUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuBarUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuBarUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiMenuBarUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiMenuBarUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuBarUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiMenuBarUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuBarUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuBarUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuBarUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiMenuBarUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiMenuBarUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiMenuItemUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiMenuItemUI.java
new file mode 100644
index 000000000..6d361b086
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiMenuItemUI.java
@@ -0,0 +1,352 @@
+/* MultiMenuItemUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuItemUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link MenuItemUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiMenuItemUI extends MenuItemUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiMenuItemUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiMenuItemUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiItemUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiMenuItemUI mui = new MultiMenuItemUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuItemUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuItemUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiMenuItemUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiMenuItemUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuItemUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiMenuItemUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuItemUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuItemUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiMenuItemUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiMenuItemUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiMenuItemUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiOptionPaneUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiOptionPaneUI.java
new file mode 100644
index 000000000..eaa4a4257
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiOptionPaneUI.java
@@ -0,0 +1,398 @@
+/* MultiOptionPaneUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.JOptionPane;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.OptionPaneUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link OptionPaneUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiOptionPaneUI extends OptionPaneUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiOptionPaneUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiOptionPaneUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiOptionPaneUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiOptionPaneUI mui = new MultiOptionPaneUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiOptionPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiOptionPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiOptionPaneUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiOptionPaneUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiOptionPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiOptionPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiOptionPaneUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiOptionPaneUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiOptionPaneUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiOptionPaneUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiOptionPaneUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link OptionPaneUI#selectInitialValue(JOptionPane)} method for
+ * all the UI delegates managed by this <code>MultiOptionPaneUI</code>.
+ *
+ * @param pane the option pane.
+ */
+ public void selectInitialValue(JOptionPane pane)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ OptionPaneUI ui = (OptionPaneUI) iterator.next();
+ ui.selectInitialValue(pane);
+ }
+ }
+
+ /**
+ * Calls the {@link OptionPaneUI#containsCustomComponents(JOptionPane)}
+ * method for all the UI delegates managed by this
+ * <code>MultiOptionPaneUI</code>, returning the result for the UI delegate
+ * from the primary look and feel.
+ *
+ * @param pane the option pane.
+ *
+ * @return The result for the UI delegate from the primary look and feel.
+ */
+ public boolean containsCustomComponents(JOptionPane pane)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ OptionPaneUI ui = (OptionPaneUI) iterator.next();
+ result = ui.containsCustomComponents(pane);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ OptionPaneUI ui = (OptionPaneUI) iterator.next();
+ /* boolean ignored = */ ui.containsCustomComponents(pane);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiPanelUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiPanelUI.java
new file mode 100644
index 000000000..7b84599d0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiPanelUI.java
@@ -0,0 +1,352 @@
+/* MultiPanelUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.PanelUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link PanelUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiPanelUI extends PanelUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiPanelUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiPanelUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiPanelUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiPanelUI mui = new MultiPanelUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPanelUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPanelUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiPanelUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiPanelUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPanelUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiPanelUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPanelUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPanelUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPanelUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiPanelUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiPanelUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiPopupMenuUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiPopupMenuUI.java
new file mode 100644
index 000000000..0afaaf74e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiPopupMenuUI.java
@@ -0,0 +1,352 @@
+/* MultiPopupMenuUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.PopupMenuUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link PopupMenuUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiPopupMenuUI extends PopupMenuUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiPopupMenuUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiPopupMenuUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiPopupMenuUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiPopupMenuUI mui = new MultiPopupMenuUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPopupMenuUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPopupMenuUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiPopupMenuUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiPopupMenuUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPopupMenuUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiPopupMenuUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPopupMenuUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPopupMenuUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiPopupMenuUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiPopupMenuUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiPopupMenuUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiProgressBarUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiProgressBarUI.java
new file mode 100644
index 000000000..0395bdd2d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiProgressBarUI.java
@@ -0,0 +1,352 @@
+/* MultiProgressBarUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ProgressBarUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ProgressBarUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiProgressBarUI extends ProgressBarUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiProgressBarUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiProgressBarUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiProgressBarUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiProgressBarUI mui = new MultiProgressBarUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiProgressBarUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiProgressBarUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiProgressBarUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiProgressBarUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiProgressBarUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiProgressBarUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiProgressBarUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiProgressBarUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiProgressBarUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiProgressBarUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiProgressBarUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiRootPaneUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiRootPaneUI.java
new file mode 100644
index 000000000..69c7ffe1b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiRootPaneUI.java
@@ -0,0 +1,352 @@
+/* MultiRootPaneUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.RootPaneUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link RootPaneUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiRootPaneUI extends RootPaneUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiRootPanelUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiRootPaneUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiRootPaneUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiRootPaneUI mui = new MultiRootPaneUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiRootPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiRootPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiRootPaneUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiRootPaneUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiRootPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiRootPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiRootPaneUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiRootPaneUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiRootPaneUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiRootPaneUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiRootPaneUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiScrollBarUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiScrollBarUI.java
new file mode 100644
index 000000000..4ec8b3fca
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiScrollBarUI.java
@@ -0,0 +1,352 @@
+/* MultiScrollBarUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ScrollBarUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ScrollBarUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiScrollBarUI extends ScrollBarUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiScrollBarUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiScrollBarUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiScrollBarUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiScrollBarUI mui = new MultiScrollBarUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollBarUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollBarUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiScrollBarUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiScrollBarUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollBarUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiScrollBarUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollBarUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollBarUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollBarUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiScrollBarUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiScrollBarUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiScrollPaneUI.java
new file mode 100644
index 000000000..5a0bc1ebf
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiScrollPaneUI.java
@@ -0,0 +1,352 @@
+/* MultiScrollPaneUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ScrollPaneUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ScrollPaneUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiScrollPaneUI extends ScrollPaneUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiScrollPaneUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiScrollPaneUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiScrollPaneUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiScrollPaneUI mui = new MultiScrollPaneUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiScrollPaneUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiScrollPaneUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiScrollPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollPaneUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollPaneUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiScrollPaneUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiScrollPaneUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiScrollPaneUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiSeparatorUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiSeparatorUI.java
new file mode 100644
index 000000000..fbd9712fe
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiSeparatorUI.java
@@ -0,0 +1,352 @@
+/* MultiSeparatorUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SeparatorUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link MultiSeparatorUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiSeparatorUI extends SeparatorUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiSeparatorUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiSeparatorUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiSeparatorUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiSeparatorUI mui = new MultiSeparatorUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSeparatorUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSeparatorUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiSeparatorUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiSeparatorUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSeparatorUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiSeparatorUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSeparatorUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSeparatorUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSeparatorUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiSeparatorUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiSeparatorUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiSliderUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiSliderUI.java
new file mode 100644
index 000000000..cb896c547
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiSliderUI.java
@@ -0,0 +1,352 @@
+/* MultiSliderUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SliderUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link SliderUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiSliderUI extends SliderUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiSliderUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiSliderUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiSliderUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiSliderUI mui = new MultiSliderUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSliderUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSliderUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiSliderUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiSliderUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSliderUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiSliderUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSliderUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSliderUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSliderUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiSliderUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiSliderUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiSpinnerUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiSpinnerUI.java
new file mode 100644
index 000000000..fd805f9cb
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiSpinnerUI.java
@@ -0,0 +1,352 @@
+/* MultiSpinnerUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SpinnerUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link SpinnerUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiSpinnerUI extends SpinnerUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiSpinnerUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiSpinnerUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiSpinnerUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiSpinnerUI mui = new MultiSpinnerUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSpinnerUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSpinnerUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiSpinnerUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiSpinnerUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSpinnerUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiSpinnerUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSpinnerUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSpinnerUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSpinnerUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiSpinnerUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiSpinnerUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiSplitPaneUI.java
new file mode 100644
index 000000000..1eb2e41f5
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiSplitPaneUI.java
@@ -0,0 +1,494 @@
+/* MultiSplitPaneUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.JSplitPane;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SplitPaneUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link SplitPaneUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiSplitPaneUI extends SplitPaneUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiSplitPaneUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiSplitPaneUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiSplitPaneUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiSplitPaneUI mui = new MultiSplitPaneUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSplitPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSplitPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiSplitPaneUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSplitPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiSplitPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link SplitPaneUI#resetToPreferredSizes(JSplitPane)} method
+ * for all the UI delegates managed by this <code>MultiSplitPaneUI</code>.
+ *
+ * @param pane the component.
+ */
+ public void resetToPreferredSizes(JSplitPane pane)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ ui.resetToPreferredSizes(pane);
+ }
+ }
+
+ /**
+ * Calls the {@link SplitPaneUI#setDividerLocation(JSplitPane, int)} method
+ * for all the UI delegates managed by this <code>MultiSplitPaneUI</code>.
+ *
+ * @param pane the component.
+ * @param location the location.
+ */
+ public void setDividerLocation(JSplitPane pane, int location)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ ui.setDividerLocation(pane, location);
+ }
+ }
+
+ /**
+ * Calls the {@link SplitPaneUI#getDividerLocation(JSplitPane)} method for all
+ * the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the location for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param pane the component.
+ *
+ * @return The location returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getDividerLocation(JSplitPane pane)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ result = ui.getDividerLocation(pane);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ /* int ignored = */ ui.getDividerLocation(pane);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link SplitPaneUI#getMinimumDividerLocation(JSplitPane)} method
+ * for all the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the location for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param pane the component.
+ *
+ * @return The location returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getMinimumDividerLocation(JSplitPane pane)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ result = ui.getMinimumDividerLocation(pane);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ /* int ignored = */ ui.getMinimumDividerLocation(pane);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link SplitPaneUI#getMaximumDividerLocation(JSplitPane)} method
+ * for all the UI delegates managed by this <code>MultiSplitPaneUI</code>,
+ * returning the location for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param pane the component.
+ *
+ * @return The location returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getMaximumDividerLocation(JSplitPane pane)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ result = ui.getMaximumDividerLocation(pane);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ /* int ignored = */ ui.getMaximumDividerLocation(pane);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link SplitPaneUI#finishedPaintingChildren(JSplitPane,
+ * Graphics)} method for all the UI delegates managed by this
+ * <code>MultiSplitPaneUI</code>.
+ *
+ * @param pane the component.
+ * @param g the graphics device.
+ */
+ public void finishedPaintingChildren(JSplitPane pane, Graphics g)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ SplitPaneUI ui = (SplitPaneUI) iterator.next();
+ ui.finishedPaintingChildren(pane, g);
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiTabbedPaneUI.java
new file mode 100644
index 000000000..3f9d22af4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiTabbedPaneUI.java
@@ -0,0 +1,447 @@
+/* MultiTabbedPaneUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.JTabbedPane;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TabbedPaneUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link TabbedPaneUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiTabbedPaneUI extends TabbedPaneUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiTabbedPaneUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiTabbedPaneUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiTabbedPaneUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiTabbedPaneUI mui = new MultiTabbedPaneUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTabbedPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTabbedPaneUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiTabbedPaneUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiTabbedPaneUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTabbedPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiTabbedPaneUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTabbedPaneUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTabbedPaneUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTabbedPaneUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiTabbedPaneUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiTabbedPaneUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TabbedPaneUI#tabForCoordinate(JTabbedPane, int, int)}
+ * method for all the UI delegates managed by this
+ * <code>MultiTabbedPaneUI</code>, returning the tab index for the UI
+ * delegate from the primary look and feel.
+ *
+ * @param pane the tabbed pane.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return The tab index returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int tabForCoordinate(JTabbedPane pane, int x, int y)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TabbedPaneUI ui = (TabbedPaneUI) iterator.next();
+ result = ui.tabForCoordinate(pane, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TabbedPaneUI ui = (TabbedPaneUI) iterator.next();
+ /* int ignored = */ ui.tabForCoordinate(pane, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TabbedPaneUI#getTabBounds(JTabbedPane, int)}
+ * method for all the UI delegates managed by this
+ * <code>MultiTabbedPaneUI</code>, returning the bounds for the UI
+ * delegate from the primary look and feel.
+ *
+ * @param pane the tabbed pane.
+ * @param index the index.
+ *
+ * @return The bounds returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Rectangle getTabBounds(JTabbedPane pane, int index)
+ {
+ Rectangle result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TabbedPaneUI ui = (TabbedPaneUI) iterator.next();
+ result = ui.getTabBounds(pane, index);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TabbedPaneUI ui = (TabbedPaneUI) iterator.next();
+ /* int ignored = */ ui.getTabRunCount(pane);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TabbedPaneUI#getTabRunCount(JTabbedPane)}
+ * method for all the UI delegates managed by this
+ * <code>MultiTabbedPaneUI</code>, returning the nt for the UI
+ * delegate from the primary look and feel.
+ *
+ * @param pane the tabbed pane.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getTabRunCount(JTabbedPane pane)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TabbedPaneUI ui = (TabbedPaneUI) iterator.next();
+ result = ui.getTabRunCount(pane);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TabbedPaneUI ui = (TabbedPaneUI) iterator.next();
+ /* int ignored = */ ui.getTabRunCount(pane);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiTableHeaderUI.java
new file mode 100644
index 000000000..972303bec
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiTableHeaderUI.java
@@ -0,0 +1,352 @@
+/* MultiTableHeaderUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TableHeaderUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link TableHeaderUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiTableHeaderUI extends TableHeaderUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiTableHeaderUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiTableHeaderUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiTableHeaderUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiTableHeaderUI mui = new MultiTableHeaderUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableHeaderUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableHeaderUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiTableHeaderUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiTableHeaderUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableHeaderUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiTableHeaderUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableHeaderUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableHeaderUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableHeaderUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiTableHeaderUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiTableHeaderUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiTableUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiTableUI.java
new file mode 100644
index 000000000..1cd1a4c88
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiTableUI.java
@@ -0,0 +1,352 @@
+/* MultiTableUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TableUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link TableUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiTableUI extends TableUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiTableUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiTableUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiTableUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiTableUI mui = new MultiTableUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiTableUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiTableUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiTableUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTableUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiTableUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiTableUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiTextUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiTextUI.java
new file mode 100644
index 000000000..392fd9811
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiTextUI.java
@@ -0,0 +1,617 @@
+/* MultiTextUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TextUI;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.EditorKit;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.Position;
+import javax.swing.text.View;
+import javax.swing.text.Position.Bias;
+
+/**
+ * A UI delegate that that coordinates multiple {@link TextUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiTextUI extends TextUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiTextUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiTextUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiTextUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiTextUI mui = new MultiTextUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiTextUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiTextUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TextUI#modelToView(JTextComponent, int)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the bounds for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tc the text component.
+ *
+ * @return The bounds returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Rectangle modelToView(JTextComponent tc, int pos)
+ throws BadLocationException
+ {
+ Rectangle result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ result = ui.modelToView(tc, pos);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ /* Rectangle ignored = */ ui.modelToView(tc, pos);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TextUI#modelToView(JTextComponent, int, Position.Bias)}
+ * method for all the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the bounds for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tc the text component.
+ *
+ * @return The bounds returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Rectangle modelToView(JTextComponent tc, int pos, Bias bias)
+ throws BadLocationException
+ {
+ Rectangle result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ result = ui.modelToView(tc, pos, bias);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ /* Rectangle ignored = */ ui.modelToView(tc, pos, bias);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TextUI#viewToModel(JTextComponent, Point)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the position for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param t the text component.
+ * @param pt the point.
+ *
+ * @return The position returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int viewToModel(JTextComponent t, Point pt)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ result = ui.viewToModel(t, pt);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ /* int ignored = */ ui.viewToModel(t, pt);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TextUI#viewToModel(JTextComponent, Point, Bias[])} method
+ * for all the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the position for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tc the text component.
+ *
+ * @return The position returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int viewToModel(JTextComponent tc, Point loc, Bias[] outBias)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ result = ui.viewToModel(tc, loc, outBias);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ /* int ignored = */ ui.viewToModel(tc, loc, outBias);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TextUI#getNextVisualPositionFrom(JTextComponent, int,
+ * Position.Bias, int, Position.Bias[])} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the position for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tc the text component.
+ *
+ * @return The position returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getNextVisualPositionFrom(JTextComponent tc, int pos, Bias bias,
+ int direction, Bias[] outBias) throws BadLocationException
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ result = ui.getNextVisualPositionFrom(tc, pos, bias, direction,
+ outBias);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ /* int ignored = */ ui.getNextVisualPositionFrom(tc, pos, bias,
+ direction, outBias);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TextUI#damageRange(JTextComponent, int, int)} method for
+ * all the UI delegates managed by this <code>MultiTextUI</code>.
+ *
+ * @param tc the component.
+ * @param start the start position.
+ * @param end the end position.
+ */
+ public void damageRange(JTextComponent tc, int start, int end)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ ui.damageRange(tc, start, end);
+ }
+ }
+
+ /**
+ * Calls the {@link TextUI#damageRange(JTextComponent, int, int,
+ * Position.Bias, Position.Bias)} method for all the UI delegates managed by
+ * this <code>MultiTextUI</code>.
+ *
+ * @param tc the component.
+ * @param start the start position.
+ * @param end the end position.
+ * @param startBias the start bias.
+ * @param endBias the end bias.
+ */
+ public void damageRange(JTextComponent tc, int start, int end,
+ Bias startBias, Bias endBias)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ ui.damageRange(tc, start, end, startBias, endBias);
+ }
+ }
+
+ /**
+ * Calls the {@link TextUI#getEditorKit(JTextComponent)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the editor kit for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tc the text component.
+ *
+ * @return The editor kit returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public EditorKit getEditorKit(JTextComponent tc)
+ {
+ EditorKit result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ result = ui.getEditorKit(tc);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ /* EditorKit ignored = */ ui.getEditorKit(tc);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TextUI#getRootView(JTextComponent)} method for all
+ * the UI delegates managed by this <code>MultiTextUI</code>,
+ * returning the view for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tc the text component.
+ *
+ * @return The view returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public View getRootView(JTextComponent tc)
+ {
+ View result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ result = ui.getRootView(tc);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TextUI ui = (TextUI) iterator.next();
+ /* View ignored = */ ui.getRootView(tc);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiToolBarUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiToolBarUI.java
new file mode 100644
index 000000000..5f308d991
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiToolBarUI.java
@@ -0,0 +1,352 @@
+/* MultiToolBarUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ToolBarUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ToolBarUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiToolBarUI extends ToolBarUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiToolBarUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiToolBarUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiToolBarUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiToolBarUI mui = new MultiToolBarUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolBarUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolBarUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiToolBarUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiToolBarUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolBarUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiToolBarUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolBarUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolBarUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolBarUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiToolBarUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiToolBarUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiToolTipUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiToolTipUI.java
new file mode 100644
index 000000000..9db9efb69
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiToolTipUI.java
@@ -0,0 +1,352 @@
+/* MultiToolTipUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ToolTipUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ToolTipUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiToolTipUI extends ToolTipUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiToolTipUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiToolTipUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiToolTipUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiToolTipUI mui = new MultiToolTipUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolTipUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolTipUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiToolTipUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiToolTipUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolTipUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiToolTipUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolTipUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolTipUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiToolTipUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiToolTipUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiToolTipUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiTreeUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiTreeUI.java
new file mode 100644
index 000000000..f70decc69
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiTreeUI.java
@@ -0,0 +1,628 @@
+/* MultiTreeUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.JTree;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TreeUI;
+import javax.swing.tree.TreePath;
+
+/**
+ * A UI delegate that that coordinates multiple {@link TreeUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiTreeUI extends TreeUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiTreeUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiTreeUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiTreeUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiTreeUI mui = new MultiTreeUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiTreeUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiTreeUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TreeUI#getPathBounds(JTree, TreePath)} method
+ * for all the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the bounds for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tree the tree component.
+ *
+ * @return The bounds returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Rectangle getPathBounds(JTree tree, TreePath path)
+ {
+ Rectangle result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ result = ui.getPathBounds(tree, path);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ /* Rectangle ignored = */ ui.getPathBounds(tree, path);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TreeUI#getPathForRow(JTree, int)} method
+ * for all the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the path for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tree the tree component.
+ *
+ * @return The path returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public TreePath getPathForRow(JTree tree, int row)
+ {
+ TreePath result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ result = ui.getPathForRow(tree, row);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ /* TreePath ignored = */ ui.getPathForRow(tree, row);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TreeUI#getRowForPath(JTree, TreePath)} method
+ * for all the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the row index for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tree the tree component.
+ *
+ * @return The row index returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getRowForPath(JTree tree, TreePath path)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ result = ui.getRowForPath(tree, path);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ /* int ignored = */ ui.getRowForPath(tree, path);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TreeUI#getRowCount(JTree)} method
+ * for all the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tree the tree component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getRowCount(JTree tree)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ result = ui.getRowCount(tree);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ /* int ignored = */ ui.getRowCount(tree);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TreeUI#getClosestPathForLocation(JTree, int, int)} method
+ * for all the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the path for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tree the tree component.
+ *
+ * @return The path returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public TreePath getClosestPathForLocation(JTree tree, int x, int y)
+ {
+ TreePath result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ result = ui.getClosestPathForLocation(tree, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ /* TreePath ignored = */ ui.getClosestPathForLocation(tree, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TreeUI#isEditing(JTree)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tree the tree component.
+ *
+ * @return The result returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public boolean isEditing(JTree tree)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ result = ui.isEditing(tree);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ /* boolean ignored = */ ui.isEditing(tree);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TreeUI#stopEditing(JTree)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tree the tree component.
+ *
+ * @return The result returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public boolean stopEditing(JTree tree)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ result = ui.stopEditing(tree);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ /* boolean ignored = */ ui.stopEditing(tree);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link TreeUI#cancelEditing(JTree)} method for
+ * all the UI delegates managed by this <code>MultiTreeUI</code>.
+ *
+ * @param tree the tree component.
+ */
+ public void cancelEditing(JTree tree)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ ui.cancelEditing(tree);
+ }
+ }
+
+ /**
+ * Calls the {@link TreeUI#startEditingAtPath(JTree, TreePath)} method for
+ * all the UI delegates managed by this <code>MultiTreeUI</code>.
+ *
+ * @param tree the tree component.
+ * @param path the path.
+ */
+ public void startEditingAtPath(JTree tree, TreePath path)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ ui.startEditingAtPath(tree, path);
+ }
+ }
+
+ /**
+ * Calls the {@link TreeUI#getEditingPath(JTree)} method for all
+ * the UI delegates managed by this <code>MultiTreeUI</code>,
+ * returning the path for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param tree the tree component.
+ *
+ * @return The path returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public TreePath getEditingPath(JTree tree)
+ {
+ TreePath result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ result = ui.getEditingPath(tree);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ TreeUI ui = (TreeUI) iterator.next();
+ /* TreePath ignored = */ ui.getEditingPath(tree);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiViewportUI.java b/libjava/classpath/javax/swing/plaf/multi/MultiViewportUI.java
new file mode 100644
index 000000000..55f9cba70
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/MultiViewportUI.java
@@ -0,0 +1,352 @@
+/* MultiViewPortUI.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.multi;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ViewportUI;
+
+/**
+ * A UI delegate that that coordinates multiple {@link ViewportUI}
+ * instances, one from the primary look and feel, and one or more from the
+ * auxiliary look and feel(s).
+ *
+ * @see UIManager#addAuxiliaryLookAndFeel(LookAndFeel)
+ */
+public class MultiViewportUI extends ViewportUI
+{
+
+ /** A list of references to the actual component UIs. */
+ protected Vector uis;
+
+ /**
+ * Creates a new <code>MultiViewPortUI</code> instance.
+ *
+ * @see #createUI(JComponent)
+ */
+ public MultiViewportUI()
+ {
+ uis = new Vector();
+ }
+
+ /**
+ * Creates a delegate object for the specified component. If any auxiliary
+ * look and feels support this component, a <code>MultiViewportUI</code> is
+ * returned, otherwise the UI from the default look and feel is returned.
+ *
+ * @param target the component.
+ *
+ * @see MultiLookAndFeel#createUIs(ComponentUI, Vector, JComponent)
+ */
+ public static ComponentUI createUI(JComponent target)
+ {
+ MultiViewportUI mui = new MultiViewportUI();
+ return MultiLookAndFeel.createUIs(mui, mui.uis, target);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#installUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiViewportUI</code>.
+ *
+ * @param c the component.
+ */
+ public void installUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.installUI(c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#uninstallUI(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiViewportUI</code>.
+ *
+ * @param c the component.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.uninstallUI(c);
+ }
+ }
+
+ /**
+ * Returns an array containing the UI delegates managed by this
+ * <code>MultiViewportUI</code>. The first item in the array is always
+ * the UI delegate from the installed default look and feel.
+ *
+ * @return An array of UI delegates.
+ */
+ public ComponentUI[] getUIs()
+ {
+ return MultiLookAndFeel.uisToArray(uis);
+ }
+
+ /**
+ * Calls the {@link ComponentUI#contains(JComponent, int, int)} method for all
+ * the UI delegates managed by this <code>MultiViewportUI</code>,
+ * returning the result for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ *
+ * @return <code>true</code> if the specified (x, y) coordinate falls within
+ * the bounds of the component as rendered by the UI delegate in the
+ * primary look and feel, and <code>false</code> otherwise.
+ */
+ public boolean contains(JComponent c, int x, int y)
+ {
+ boolean result = false;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.contains(c, x, y);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* boolean ignored = */ ui.contains(c, x, y);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#update(Graphics, JComponent)} method for all
+ * the UI delegates managed by this <code>MultiViewportUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.update(g, c);
+ }
+ }
+
+ /**
+ * Calls the <code>paint(Graphics, JComponent)</code> method for all the UI
+ * delegates managed by this <code>MultiViewportUI</code>.
+ *
+ * @param g the graphics device.
+ * @param c the component.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Iterator iterator = uis.iterator();
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ ui.paint(g, c);
+ }
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getPreferredSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiViewportUI</code>,
+ * returning the preferred size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The preferred size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getPreferredSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getPreferredSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMinimumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiViewportUI</code>,
+ * returning the minimum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The minimum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMinimumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMinimumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getMaximumSize(JComponent)} method for all
+ * the UI delegates managed by this <code>MultiViewportUI</code>,
+ * returning the maximum size for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The maximum size returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getMaximumSize(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Dimension ignored = */ ui.getMaximumSize(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChildrenCount(JComponent)} method
+ * for all the UI delegates managed by this <code>MultiViewportUI</code>,
+ * returning the count for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component.
+ *
+ * @return The count returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int result = 0;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChildrenCount(c);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* int ignored = */ ui.getAccessibleChildrenCount(c);
+ }
+ return result;
+ }
+
+ /**
+ * Calls the {@link ComponentUI#getAccessibleChild(JComponent, int)} method
+ * for all the UI delegates managed by this <code>MultiViewportUI</code>,
+ * returning the child for the UI delegate from the primary look and
+ * feel.
+ *
+ * @param c the component
+ * @param i the child index.
+ *
+ * @return The child returned by the UI delegate from the primary
+ * look and feel.
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible result = null;
+ Iterator iterator = uis.iterator();
+ // first UI delegate provides the return value
+ if (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ result = ui.getAccessibleChild(c, i);
+ }
+ // return values from auxiliary UI delegates are ignored
+ while (iterator.hasNext())
+ {
+ ComponentUI ui = (ComponentUI) iterator.next();
+ /* Accessible ignored = */ ui.getAccessibleChild(c, i);
+ }
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/multi/package.html b/libjava/classpath/javax/swing/plaf/multi/package.html
new file mode 100644
index 000000000..568a7d0bf
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/multi/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.plaf.metal package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.plaf.multi</title></head>
+
+<body>
+<p>Provides a look and feel that can combine a primary look and feel with one or more auxiliary look and feels.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/plaf/package.html b/libjava/classpath/javax/swing/plaf/package.html
new file mode 100644
index 000000000..c266074f0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.plaf package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.plaf</title></head>
+
+<body>
+<p>A base package for the "pluggable look and feel" (plaf) mechanism used by
+the <code>javax.swing</code> classes.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/plaf/synth/ColorType.java b/libjava/classpath/javax/swing/plaf/synth/ColorType.java
new file mode 100644
index 000000000..ced1efc02
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/ColorType.java
@@ -0,0 +1,135 @@
+/* ColorType.java -- En enumeration of color types
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+/**
+ * A typesafe enumeration of color types.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public class ColorType
+{
+
+ /**
+ * A constant used to identify the foreground color of a component.
+ */
+ public static final ColorType FOREGROUND = new ColorType("Foreground");
+
+ /**
+ * A constant used to identify the background color of a component.
+ */
+ public static final ColorType BACKGROUND = new ColorType("Background");
+
+ /**
+ * A constant used to identify the foreground color of text of a component.
+ */
+ public static final ColorType TEXT_FOREGROUND
+ = new ColorType("TextForeground");
+
+ /**
+ * A constant used to identify the background color of text of a component.
+ */
+ public static final ColorType TEXT_BACKGROUND
+ = new ColorType("TextBackground");
+
+ /**
+ * A constant used to identify the focus color of a component.
+ */
+ public static final ColorType FOCUS = new ColorType("Focus");
+
+ /**
+ * The maximum number of color types.
+ */
+ public static final int MAX_COUNT;
+ static
+ {
+ // This is not a constant in the JDK.
+ MAX_COUNT = 5;
+ }
+
+ /**
+ * A counter used to assign an ID to the created color types.
+ */
+ private static int count = 0;
+
+ /**
+ * The ID of the color type.
+ */
+ private int id;
+
+ /**
+ * The description of the color type.
+ */
+ private String description;
+
+ /**
+ * Creates a new <code>Color</code> color type with the specified
+ * description.
+ *
+ * @param desc the textual description of the color type
+ */
+ protected ColorType(String desc)
+ {
+ description = desc;
+ id = count;
+ count++;
+ }
+
+ /**
+ * Returns the unique ID of the color type.
+ *
+ * @return the unique ID of the color type
+ */
+ public final int getID()
+ {
+ return id;
+ }
+
+ /**
+ * Returns the textual description of the color type.
+ *
+ * @return the textual description of the color type
+ */
+ public String toString()
+ {
+ return description;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/Region.java b/libjava/classpath/javax/swing/plaf/synth/Region.java
new file mode 100644
index 000000000..25d1a11d5
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/Region.java
@@ -0,0 +1,474 @@
+/* Region.java -- Describes a region within a component
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+/**
+ * Describes a region of a component or the complete component.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public class Region
+{
+
+ // FIXME: What should ui be for the non-component regions that have
+ // subregion==false?
+
+ /**
+ * Specifies an arrow button region.
+ */
+ public static final Region ARROW_BUTTON =
+ new Region("ArrowButton", null, false);
+
+ /**
+ * Specifies the region of a standard button.
+ */
+ public static final Region BUTTON =
+ new Region("Button", "ButtonUI", false);
+
+ /**
+ * Specifies the region of a check box.
+ */
+ public static final Region CHECK_BOX =
+ new Region("CheckBox", "CheckBoxUI", false);
+
+ /**
+ * Specifies the region of a check box menu item.
+ */
+ public static final Region CHECK_BOX_MENU_ITEM =
+ new Region("CheckBoxMenuItem", "CheckBoxMenuItemUI", false);
+
+ /**
+ * Specifies the region of a colorchooser.
+ */
+ public static final Region COLOR_CHOOSER =
+ new Region("ColorChooser", "ColorChooserUI", false);
+
+ /**
+ * Specifies the region of a combo box.
+ */
+ public static final Region COMBO_BOX =
+ new Region("ComboBox", "ComboBoxUI", false);
+
+ /**
+ * Specifies the region of a desktop pane.
+ */
+ public static final Region DESKTOP_PANE =
+ new Region("DesktopPane", "DesktopPaneUI", false);
+
+ /**
+ * Specifies the region of a desktop icon.
+ */
+ public static final Region DESKTOP_ICON =
+ new Region("DesktopIcon", "DesktopIconUI", false);
+
+ /**
+ * Specifies the region of an editor pane.
+ */
+ public static final Region EDITOR_PANE =
+ new Region("EditorPane", "EditorPaneUI", false);
+
+ /**
+ * Specifies the region of a file chooser.
+ */
+ public static final Region FILE_CHOOSER =
+ new Region("FileChooser", "FileChooserUI", false);
+
+ /**
+ * Specifies the region of a formatted text field.
+ */
+ public static final Region FORMATTED_TEXT_FIELD =
+ new Region("FormattedTextField", "FormattedTextFieldUI", false);
+
+ /**
+ * Specifies the region of an internal frame.
+ */
+ public static final Region INTERNAL_FRAME =
+ new Region("InternalFrame", "InternalFrameUI", false);
+
+ /**
+ * Specifies the region of the title pane of an internal frame.
+ */
+ public static final Region INTERNAL_FRAME_TITLE_PANE =
+ new Region("InternalFrameTitlePane", "InternalFrameTitlePaneUI", false);
+
+ /**
+ * Specifies the region of a label.
+ */
+ public static final Region LABEL =
+ new Region("Label", "LabelUI", false);
+
+ /**
+ * Specifies the region of a list.
+ */
+ public static final Region LIST =
+ new Region("List", "ListUI", false);
+
+ /**
+ * Specifies the region of a menu.
+ */
+ public static final Region MENU =
+ new Region("Menu", "MenuUI", false);
+
+ /**
+ * Specifies the region of a menu bar.
+ */
+ public static final Region MENU_BAR =
+ new Region("MenuBar", "MenuBarUI", false);
+
+ /**
+ * Specifies the region of a menu item.
+ */
+ public static final Region MENU_ITEM =
+ new Region("MenuItem", "MenuItemUI", false);
+
+ /**
+ * Specifies the region of a menu item accelerator. This is a subregion
+ * of menu item.
+ */
+ public static final Region MENU_ITEM_ACCELERATOR =
+ new Region("MenuItemAccelerator", null, true);
+
+ /**
+ * Specifies the region of an option pane.
+ */
+ public static final Region OPTION_PANE =
+ new Region("OptionPane", "OptionPaneUI", false);
+
+ /**
+ * Specifies the region of a panel.
+ */
+ public static final Region PANEL =
+ new Region("Panel", "PanelUI", false);
+
+ /**
+ * Specifies the region of a password field.
+ */
+ public static final Region PASSWORD_FIELD =
+ new Region("PasswordField", "PasswordFieldUI", false);
+
+ /**
+ * Specifies the region of a popup menu.
+ */
+ public static final Region POPUP_MENU =
+ new Region("PopupMenu", "PopupMenuUI", false);
+
+ /**
+ * Specifies the region of a popup menu separator.
+ */
+ public static final Region POPUP_MENU_SEPARATOR =
+ new Region("PopupMenuSeparator", null, false);
+
+ /**
+ * Specifies the region of a progress bar.
+ */
+ public static final Region PROGRESS_BAR =
+ new Region("ProgressBar", "ProgressBarUI", false);
+
+ /**
+ * Specifies the region of a radio button.
+ */
+ public static final Region RADIO_BUTTON =
+ new Region("RadioButton", "RadioButtonUI", false);
+
+ /**
+ * Specifies the region of a radio button menu item.
+ */
+ public static final Region RADIO_BUTTON_MENU_ITEM =
+ new Region("RadioButtonMenuItem", "RadioButtonMenuItemUI", false);
+
+ /**
+ * Specifies the region of a root pane.
+ */
+ public static final Region ROOT_PANE =
+ new Region("RootPane", "RootPaneUI", false);
+
+ /**
+ * Specifies the region of a scroll bar.
+ */
+ public static final Region SCROLL_BAR =
+ new Region("ScrollBar", "ScrollBarUI", false);
+
+ /**
+ * Specifies the region of a scroll bar track. This is a subregion of
+ * scroll bars.
+ */
+ public static final Region SCROLL_BAR_TRACK =
+ new Region("ScrollBarTrack", null, true);
+
+ /**
+ * Specifies the region of a scroll bar thumb. This is a subregion of
+ * scroll bars.
+ */
+ public static final Region SCROLL_BAR_THUMB =
+ new Region("ScrollBarThumb", null, true);
+
+ /**
+ * Specifies the region of a scroll pane.
+ */
+ public static final Region SCROLL_PANE =
+ new Region("ScrollPane", "ScrollPaneUI", false);
+
+ /**
+ * Specifies the region of a separator.
+ */
+ public static final Region SEPARATOR =
+ new Region("Separator", "SeparatorUI", false);
+
+ /**
+ * Specifies the region of a slider.
+ */
+ public static final Region SLIDER =
+ new Region("Slider", "SliderUI", false);
+
+ /**
+ * Specifies the region of a slider track. This is a subregion of a slider.
+ */
+ public static final Region SLIDER_TRACK =
+ new Region("SliderTrack", null, true);
+
+ /**
+ * Specifies the region of a slider thumb. This is a subregion of a slider.
+ */
+ public static final Region SLIDER_THUMB =
+ new Region("SliderThumb", null, true);
+
+ /**
+ * Specifies the region of a spinner.
+ */
+ public static final Region SPINNER =
+ new Region("Spinner", "SpinnerUI", false);
+
+ /**
+ * Specifies the region of a split pane.
+ */
+ public static final Region SPLIT_PANE =
+ new Region("SplitPane", "SplitPaneUI", false);
+
+ /**
+ * Specifies the region of a split pane divider. This is a subregion of
+ * a split pane.
+ */
+ public static final Region SPLIT_PANE_DIVIDER =
+ new Region("SplitPaneDivider", null, true);
+
+ /**
+ * Specifies the region of a tabbed pane.
+ */
+ public static final Region TABBED_PANE =
+ new Region("TabbedPane", "TabbedPaneUI", false);
+
+ /**
+ * This specifies the region of a tab of a tabbed pane. This is a subregion
+ * of a tabbed pane.
+ */
+ public static final Region TABBED_PANE_TAB =
+ new Region("TabbedPaneTab", null, true);
+
+ /**
+ * This specifies the region underneath the tabs of a tabbed pane. This is a
+ * subregion of a tabbed pane.
+ */
+ public static final Region TABBED_PANE_TAB_AREA =
+ new Region("TabbedPaneTabArea", null, true);
+
+ /**
+ * This specifies the region for the content of a tabbed pane. This is a
+ * subregion of a tabbed pane.
+ */
+ public static final Region TABBED_PANE_CONTENT =
+ new Region("TabbedPaneContent", null, true);
+
+ /**
+ * Specifies the region of a table.
+ */
+ public static final Region TABLE =
+ new Region("Table", "TableUI", false);
+
+ /**
+ * Specifies the region of a table header.
+ */
+ public static final Region TABLE_HEADER =
+ new Region("TableHeader", "TableHeaderUI", false);
+
+ /**
+ * Specifies the region of a text area.
+ */
+ public static final Region TEXT_AREA =
+ new Region("TextArea", "TextAreaUI", false);
+
+ /**
+ * Specifies the region of a text field.
+ */
+ public static final Region TEXT_FIELD =
+ new Region("TextField", "TextFieldUI", false);
+
+ /**
+ * Specifies the region of a text pane.
+ */
+ public static final Region TEXT_PANE =
+ new Region("TextPane", "TextPaneUI", false);
+
+ /**
+ * Specifies the region of a toggle button.
+ */
+ public static final Region TOGGLE_BUTTON =
+ new Region("ToggleButton", "ToggleButtonUI", false);
+
+ /**
+ * Specifies the region of a tool bar.
+ */
+ public static final Region TOOL_BAR =
+ new Region("ToolBar", "ToolBarUI", false);
+
+ /**
+ * Specifies the content region of a tool bar. This is a subregion of a tool
+ * bar.
+ */
+ public static final Region TOOL_BAR_CONTENT =
+ new Region("ToolBarContent", null, true);
+
+ /**
+ * Specifies the drag window region of a tool bar. This is a subregion of a
+ * tool bar.
+ */
+ public static final Region TOOL_BAR_DRAG_WINDOW =
+ new Region("ToolBarDragWindow", null, false);
+
+ /**
+ * Specifies the region of a tool tip.
+ */
+ public static final Region TOOL_TIP =
+ new Region("ToolTip", "ToolTipUI", false);
+
+ /**
+ * Specifies the region of a separator of a tool bar. This is a subregion of
+ * a tool bar.
+ */
+ public static final Region TOOL_BAR_SEPARATOR =
+ new Region("ToolBarSeparator", null, false);
+
+ /**
+ * Specifies the region of a tree.
+ */
+ public static final Region TREE =
+ new Region("Tree", "TreeUI", false);
+
+ /**
+ * Specifies the region of a tree cell. This is a subregion of a tree.
+ */
+ public static final Region TREE_CELL =
+ new Region("TreeCell", null, true);
+
+ /**
+ * Specifies the region of a viewport.
+ */
+ public static final Region VIEWPORT =
+ new Region("Viewport", "ViewportUI", false);
+
+
+ /**
+ * The UI class id for the region. This is package private because this will
+ * be used by other classes in that package.
+ */
+ String ui;
+
+ /**
+ * The name of the region.
+ */
+ private String name;
+
+ /**
+ * If this region is a subregion or not.
+ */
+ private boolean subregion;
+
+ /**
+ * Creates a new <code>Region</code> with the specified name and ui ID.
+ * The <code>ui</code> must be the same what
+ * {@link javax.swing.JComponent#getUIClassID()} returns for toplevel regions. For
+ * subregions this should be <code>null</code>.
+ *
+ * @param name the name of the region
+ * @param ui the UI class ID of the region or <code>null</code> for
+ * subregions
+ * @param subregion <code>true</code> if this region is a subregion,
+ * <code>false</code> otherwise
+ */
+ protected Region(String name, String ui, boolean subregion)
+ {
+ this.name = name;
+ this.ui = ui;
+ this.subregion = subregion;
+ }
+
+ /**
+ * Returns <code>true</code> if this region describes a subregion of a
+ * component, <code>false</code> if it describes a component region itself.
+ *
+ * @return <code>true</code> if this region describes a subregion of a
+ * component, <code>false</code> if it describes a component region
+ * itself
+ */
+ public boolean isSubregion()
+ {
+ return subregion;
+ }
+
+ /**
+ * Returns the name of the region.
+ *
+ * @return the name of the region
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Returns the name of the region.
+ *
+ * @return the name of the region
+ */
+ public String toString()
+ {
+ return name;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthConstants.java b/libjava/classpath/javax/swing/plaf/synth/SynthConstants.java
new file mode 100644
index 000000000..306024c00
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/SynthConstants.java
@@ -0,0 +1,85 @@
+/* SynthConstants.java -- A couple of constants used by Synth
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+/**
+ * A couple of constants used by the Synth Look and Feel.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public interface SynthConstants
+{
+ /**
+ * A primary state indicating that a component is enabled.
+ */
+ static final int ENABLED = 1;
+
+ /**
+ * A primary state indicating that a component is disabled.
+ */
+ static final int DISABLED = 8;
+
+ /**
+ * A primary state indicating that the mouse is over a region.
+ */
+ static final int MOUSE_OVER = 2;
+
+ /**
+ * A primary state indicating that the component is in a pressed state (which
+ * does not necessarily mean that the mouse is pressed over the component).
+ */
+ static final int PRESSED = 4;
+
+ /**
+ * Indicates that a region has focus.
+ */
+ static final int FOCUSED = 256;
+
+ /**
+ * Indicates that a region is selected.
+ */
+ static final int SELECTED = 512;
+
+ /**
+ * Indicates that a region is in its default state.
+ */
+ static final int DEFAULT = 1024;
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthContext.java b/libjava/classpath/javax/swing/plaf/synth/SynthContext.java
new file mode 100644
index 000000000..83536dae9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/SynthContext.java
@@ -0,0 +1,134 @@
+/* SynthContext.java -- Contextual information about a region
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+import javax.swing.JComponent;
+
+/**
+ * Contains some contextual information about a region. The information passed
+ * in objects of this class can only be considered valid during the method call
+ * that it was passed to.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public class SynthContext
+{
+
+ /**
+ * The component.
+ */
+ private JComponent component;
+
+ /**
+ * The region of the component.
+ */
+ private Region region;
+
+ /**
+ * The style of the component.
+ */
+ private SynthStyle style;
+
+ /**
+ * The state of the component.
+ */
+ private int state;
+
+ /**
+ * Creates a new <code>SynthContext</code> object.
+ *
+ * @param component the component for which this context is used
+ * @param region the region of the component
+ * @param style the style associated with the component
+ * @param state a or'ed bitmask of the constants from {@link SynthConstants}
+ */
+ public SynthContext(JComponent component, Region region, SynthStyle style,
+ int state)
+ {
+ this.component = component;
+ this.region = region;
+ this.style = style;
+ this.state = state;
+ }
+
+ /**
+ * Returns the component that contains the region.
+ *
+ * @return the component that contains the region
+ */
+ public JComponent getComponent()
+ {
+ return component;
+ }
+
+ /**
+ * Returns the region that identifies this state.
+ *
+ * @return the region that identifies this state
+ */
+ public Region getRegion()
+ {
+ return region;
+ }
+
+ /**
+ * Returns the style of the region.
+ *
+ * @return the style of the region
+ */
+ public SynthStyle getStyle()
+ {
+ return style;
+ }
+
+ /**
+ * Returns the state of the component. This is a or'ed bitmask of the
+ * constants defined in {@link SynthConstants}.
+ *
+ * @return the state of the component
+ *
+ * @see SynthConstants
+ */
+ public int getComponentState()
+ {
+ return state;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/synth/SynthGraphicsUtils.java
new file mode 100644
index 000000000..76a46cb21
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/SynthGraphicsUtils.java
@@ -0,0 +1,289 @@
+/* SynthGraphicsUtils.java -- Wrapper for graphics primitives used in Synth
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+import gnu.classpath.NotImplementedException;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.Icon;
+import javax.swing.SwingUtilities;
+
+/**
+ * Wrapper for graphics primitives used in Synth.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public class SynthGraphicsUtils
+
+{
+ /**
+ * Creates a new <code>SynthGraphicsUtils</code> object.
+ */
+ public SynthGraphicsUtils()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Draws a line from (x1,y1) to (x2,y2).
+ *
+ * @param ctx the synth context, identifies the region
+ * @param paintKey identifies the portion of the component to be painted, may
+ * be <code>null</code>
+ * @param g the graphics context to use for painting
+ * @param x1 the x coordinate of the start point
+ * @param y1 the y coordinate of the start point
+ * @param x2 the x coordinate of the end point
+ * @param y2 the y coordinate of the end point
+ */
+ public void drawLine(SynthContext ctx, Object paintKey, Graphics g, int x1,
+ int y1, int x2, int y2)
+ {
+ // TODO: Correct?
+ g.drawLine(x1, y1, x2, y2);
+ }
+
+ /**
+ * Lays out a label and (if non-null) an icon. The calculated coordinates are
+ * then stored in <code>viewR</code>, <code>iconR</code> and
+ * <code>textR</code>.
+ *
+ * The alignment and position parameters may be one of the alignment or
+ * position constants defined in {@link javax.swing.SwingConstants}.
+ *
+ * @param ctx the synth context, identifies the current region
+ * @param fm the font metrics to use to fetch the text measures
+ * @param text the text to lay out, may be <code>null</code>
+ * @param icon the icon to lay out, may be <code>null</code>
+ * @param hAlign the horizontal alignment of the label
+ * @param vAlign the vertical alignment of the label
+ * @param hTextPos the horizontal text position
+ * @param vTextPos the vertical text position
+ * @param viewR the view rectangle (return parameter)
+ * @param iconR the icon rectangle (return parameter)
+ * @param textR the text rectangle (return parameter)
+ * @param iconTextGap the gap between text and label
+ *
+ * @return the label text, may be shortened
+ */
+ public String layoutText(SynthContext ctx, FontMetrics fm, String text,
+ Icon icon, int hAlign, int vAlign, int hTextPos,
+ int vTextPos, Rectangle viewR, Rectangle iconR,
+ Rectangle textR, int iconTextGap)
+ {
+ return SwingUtilities.layoutCompoundLabel(fm, text, icon, vAlign, hAlign,
+ vTextPos, hTextPos, viewR, iconR,
+ textR, iconTextGap);
+ }
+
+ /**
+ * Returns the width of the string <code>text</code> for the specified font
+ * and font metrics.
+ *
+ * @param ctx identifies the current region
+ * @param font the font
+ * @param fm the font metrics to use
+ * @param text the text to be measured
+ *
+ * @return the width of the string <code>text</code> for the specified font
+ * and font metrics
+ */
+ public int computeStringWidth(SynthContext ctx, Font font, FontMetrics fm,
+ String text)
+ {
+ return fm.stringWidth(text);
+ }
+
+ /**
+ * Calculates the minimums size that is needed to render the label with
+ * <code>text</code> and <code>icon</code> correctly.
+ *
+ * @param ctx identifies the current region
+ * @param font the font to use
+ * @param text the label text
+ * @param icon the label icon
+ * @param hAlign the horizontal alignment
+ * @param vAlign the vertical alignment
+ * @param hTextPosition the horizontal text position
+ * @param vTextPosition the vertical text position
+ * @param iconTextGap the gap between icon and text
+ * @param mnemonicIndex index to the mnemonic character within
+ * <code>text</code>
+ *
+ * @return the minimums size that is needed to render the label with
+ * <code>text</code> and <code>icon</code> correctly
+ */
+ public Dimension getMinimumSize(SynthContext ctx, Font font, String text,
+ Icon icon, int hAlign, int vAlign,
+ int hTextPosition,int vTextPosition,
+ int iconTextGap,int mnemonicIndex)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return new Dimension(0, 0);
+ }
+
+ /**
+ * Calculates the preferred size that is needed to render the label with
+ * <code>text</code> and <code>icon</code> correctly.
+ *
+ * @param ctx identifies the current region
+ * @param font the font to use
+ * @param text the label text
+ * @param icon the label icon
+ * @param hAlign the horizontal alignment
+ * @param vAlign the vertical alignment
+ * @param hTextPosition the horizontal text position
+ * @param vTextPosition the vertical text position
+ * @param iconTextGap the gap between icon and text
+ * @param mnemonicIndex index to the mnemonic character within
+ * <code>text</code>
+ *
+ * @return the preferred size that is needed to render the label with
+ * <code>text</code> and <code>icon</code> correctly
+ */
+ public Dimension getPreferredSize(SynthContext ctx, Font font, String text,
+ Icon icon, int hAlign, int vAlign,
+ int hTextPosition,int vTextPosition,
+ int iconTextGap,int mnemonicIndex)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return new Dimension(0, 0);
+ }
+
+ /**
+ * Calculates the maximum size that is needed to render the label with
+ * <code>text</code> and <code>icon</code> correctly.
+ *
+ * @param ctx identifies the current region
+ * @param font the font to use
+ * @param text the label text
+ * @param icon the label icon
+ * @param hAlign the horizontal alignment
+ * @param vAlign the vertical alignment
+ * @param hTextPosition the horizontal text position
+ * @param vTextPosition the vertical text position
+ * @param iconTextGap the gap between icon and text
+ * @param mnemonicIndex index to the mnemonic character within
+ * <code>text</code>
+ *
+ * @return the maximum size that is needed to render the label with
+ * <code>text</code> and <code>icon</code> correctly
+ */
+ public Dimension getMaximumSize(SynthContext ctx, Font font, String text,
+ Icon icon, int hAlign, int vAlign,
+ int hTextPosition,int vTextPosition,
+ int iconTextGap,int mnemonicIndex)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return new Dimension(0, 0);
+ }
+
+ /**
+ * Returns the maximum character height of the font from the component of the
+ * passed in <code>context</code>.
+ *
+ * @param context identifies the current component and region
+ *
+ * @return the maximum character height of the font from the component of the
+ * passed in <code>context</code>
+ */
+ public int getMaximumCharHeight(SynthContext context)
+ {
+ Component comp = context.getComponent();
+ Font font = comp.getFont();
+ return comp.getFontMetrics(font).getHeight();
+ }
+
+ /**
+ * Renders the specified <code>text</code> within the <code>bounds</code>.
+ *
+ * @param ctx identifies the component and region
+ * @param g the graphics context for drawing the tetx
+ * @param text the text to be rendered
+ * @param bounds the bounds within which the text should be rendered
+ * @param mnemonicIndex the index of the mnemonic character within
+ * <code>text</code>
+ */
+ public void paintText(SynthContext ctx, Graphics g, String text,
+ Rectangle bounds, int mnemonicIndex)
+ {
+ // FIXME: This is very primitive and should be improved to paint the
+ // mnemonic char.
+ g.drawString(text, bounds.x, bounds.y);
+ }
+
+ /**
+ * Renders the specified <code>text</code> at the specified location.
+ *
+ * @param ctx identifies the component and region
+ * @param g the graphics context for drawing the tetx
+ * @param text the text to be rendered
+ * @param x the X location where the text should be rendered
+ * @param y the Y location where the text should be rendered
+ * @param mnemonicIndex the index of the mnemonic character within
+ * <code>text</code>
+ */
+ public void paintText(SynthContext ctx, Graphics g, String text,
+ int x, int y, int mnemonicIndex)
+ {
+ // FIXME: This is very primitive and should be improved to paint the
+ // mnemonic char.
+ g.drawString(text, x, y);
+ }
+
+ public void paintText(SynthContext ctx, Graphics g, String text, Icon icon,
+ int hAlign, int vAlign, int hTextPosition,
+ int vTextPosition, int iconTextGap, int mnemonicIndex,
+ int textOffset)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java b/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java
new file mode 100644
index 000000000..ac24dd48d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java
@@ -0,0 +1,279 @@
+/* SynthLookAndFeel.java -- A skinnable Swing look and feel
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+import gnu.classpath.NotImplementedException;
+
+import java.awt.Component;
+import java.io.InputStream;
+import java.text.ParseException;
+
+import javax.swing.JComponent;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.BasicLookAndFeel;
+
+
+/**
+ * A look and feel that can be customized either by providing a file to
+ * {@link #load} or by setting a {@link SynthStyleFactory} using
+ * {@link #setStyleFactory}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public class SynthLookAndFeel
+ extends BasicLookAndFeel
+{
+
+ /**
+ * The style factory that will be used by the UI classes to load their
+ * style sets from.
+ */
+ private static SynthStyleFactory styleFactory;
+
+ /**
+ * Creates a new instance of <code>SynthLookAndFeel</code>. In order to use
+ * the Synth look and feel you either need to call {@link #load} to load a
+ * set of styles from an XML file, or you need to call
+ * {@link #setStyleFactory} to provide your own style factory.
+ */
+ public SynthLookAndFeel()
+ {
+ // FIXME: What to do here, if anything?
+ }
+
+ /**
+ * Sets the style factory that the UI classes of Synth will use to load their
+ * sets of styles.
+ *
+ * @param sf the style factory to set
+ */
+ public static void setStyleFactory(SynthStyleFactory sf)
+ {
+ styleFactory = sf;
+ }
+
+ /**
+ * Returns the current style factory that the UI classes of Synth will use to
+ * load their sets of styles.
+ *
+ * @return the current style factory
+ */
+ public static SynthStyleFactory getStyleFactory()
+ {
+ return styleFactory;
+ }
+
+ /**
+ * Returns the style for the specified component and region.
+ *
+ * @param c the component for which to return the style
+ * @param r the region of the component for which to return the style
+ *
+ * @return the style for the specified component and region
+ */
+ public static SynthStyle getStyle(JComponent c, Region r)
+ {
+ return getStyleFactory().getStyle(c, r);
+ }
+
+ /**
+ * Updates all style information of the component and it's children.
+ *
+ * @param c the componenent for which to update the style
+ */
+ public static void updateStyles(Component c)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this properly.
+ }
+
+ /**
+ * Returns the region for a given Swing component.
+ *
+ * @param c the Swing component for which to fetch the region
+ *
+ * @return the region for a given Swing component
+ */
+ public static Region getRegion(JComponent c)
+ throws NotImplementedException
+ {
+ // FIXME: This can be implemented as soon as we have the component UI
+ // classes in place, since this region will be matched via the UI classes.
+ return null;
+ }
+
+ /**
+ * Creates the Synth look and feel component UI instance for the given
+ * component.
+ *
+ * @param c the component for which to create a UI instance
+ *
+ * @return the Synth look and feel component UI instance for the given
+ * component
+ */
+ public static ComponentUI createUI(JComponent c)
+ throws NotImplementedException
+ {
+ // FIXME: This can be implemented as soon as we have the component UI
+ // classes in place.
+ return null;
+ }
+
+ /**
+ * Initializes this look and feel.
+ */
+ public void initialize()
+ throws NotImplementedException
+ {
+ super.initialize();
+ // TODO: Implement at least the following here:
+ // if (styleFactory != null)
+ // styleFactory = new DefaultStyleFactory();
+ }
+
+ /**
+ * Uninitializes the look and feel.
+ */
+ public void uninitialize()
+ throws NotImplementedException
+ {
+ super.uninitialize();
+ // TODO: What to do here?
+ }
+
+ /**
+ * Returns the UI defaults of this look and feel.
+ *
+ * @return the UI defaults of this look and feel
+ */
+ public UIDefaults getDefaults()
+ throws NotImplementedException
+ {
+ // FIXME: This is certainly wrong. The defaults should be fetched/merged
+ // from the file from which the l&f is loaded.
+ return super.getDefaults();
+ }
+
+ /**
+ * FIXME: DOCUMENT ME!
+ *
+ * @return FIXME
+ */
+ public boolean shouldUpdateStyleOnAncestorChanged()
+ throws NotImplementedException
+ {
+ return false;
+ }
+
+ /**
+ * Loads a set of {@link SynthStyle}s that are used for the look and feel of
+ * the components. The <code>resourceBase</code> parameter is used to resolve
+ * references against, like icons and other files.
+ *
+ * @param in the input stream from where to load the styles
+ * @param resourceBase the base against which references are resolved.
+ *
+ * @throws ParseException if the input stream cannot be parsed
+ * @throws IllegalArgumentException if one of the parameters is
+ * <code>null</code>
+ */
+ public void load(InputStream in, Class<?> resourceBase)
+ throws ParseException, IllegalArgumentException, NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ }
+
+ /**
+ * Returns a textual description of the Synth look and feel. This returns
+ * &quot;Synth look and feel&quot;.
+ *
+ * @return a textual description of the Synth look and feel
+ */
+ public String getDescription()
+ {
+ return "Synth look and feel";
+ }
+
+ /**
+ * Returns the ID of the Synth look and feel. This returns &quot;Synth&quot;.
+ *
+ * @return the ID of the Synth look and feel
+ */
+ public String getID()
+ {
+ return "Synth";
+ }
+
+ /**
+ * Returns the name of the Synth look and feel. This returns
+ * &quot;Synth look and feel&quot;.
+ *
+ * @return the name of the Synth look and feel
+ */
+ public String getName()
+ {
+ return "Synth look and feel";
+ }
+
+ /**
+ * Returns <code>false</code> since the Synth look and feel is not a native
+ * look and feel.
+ *
+ * @return <code>false</code>
+ */
+ public boolean isNativeLookAndFeel()
+ {
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> since the Synth look and feel is always a
+ * supported look and feel.
+ *
+ * @return <code>true</code>
+ */
+ public boolean isSupportedLookAndFeel()
+ {
+ return true;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthPainter.java b/libjava/classpath/javax/swing/plaf/synth/SynthPainter.java
new file mode 100644
index 000000000..6a57dadca
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/SynthPainter.java
@@ -0,0 +1,1999 @@
+/* SynthPainter.java -- An abstract painter for synth components
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+import java.awt.Graphics;
+
+/**
+ * The abstract definition of a delegate that takes the responsibility of
+ * painting for the components.
+ *
+ * This class is defined to be abstract and all methods are no-ops.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public abstract class SynthPainter
+{
+
+ /**
+ * Creates a new <code>SynthPainter</code> object.
+ */
+ public SynthPainter()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the foreground of an arrow button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param dir the orientation of the arrow
+ */
+ public void paintArrowButtonForeground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h, int dir)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the foreground of a progress bar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param dir the orientation of the progress bar
+ */
+ public void paintProgressBarForeground(SynthContext ctx, Graphics g,
+ int x, int y, int w, int h,
+ int dir)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the foreground of a separator.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param dir the orientation of the separator
+ */
+ public void paintSeparatorForeground(SynthContext ctx, Graphics g,
+ int x, int y, int w, int h,
+ int dir)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the foreground of a split pane's divider.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param dir the orientation of the divider
+ */
+ public void paintSplitPaneDividerForeground(SynthContext ctx, Graphics g,
+ int x, int y, int w, int h,
+ int dir)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints a split pane's divider, when it is being dragged.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param dir the orientation of the divider
+ */
+ public void paintSplitPaneDragDivider(SynthContext ctx, Graphics g,
+ int x, int y, int w, int h,
+ int dir)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the indicator for a tree cell which has the focus.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTreeCellFocus(SynthContext ctx, Graphics g,
+ int x, int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of an arrow button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintArrowButtonBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of an arrow button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintArrowButtonBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintButtonBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintButtonBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a check box.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintCheckBoxBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a check box.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintCheckBoxBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a check box menu item.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintCheckBoxMenuItemBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a check box menu item.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintCheckBoxMenuItemBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a color chooser.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintColorChooserBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a color chooser.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintColorChooserBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a combo box.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintComboBoxBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a combo box.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintComboBoxBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a desktop icon.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintDesktopIconBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a desktop icon.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintDesktopIconBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a desktop pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintDesktopPaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a desktop pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintDesktopPaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of an editor pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintEditorPaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of an editor pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintEditorPaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a file chooser.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintFileChooserBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a file chooser.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintFileChooserBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a formatted text field.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintFormattedTextFieldBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a formatted text field.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintFormattedTextFieldBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of an internal frame.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintInternalFrameBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of an internal frame.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintInternalFrameBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of an internal frame's title pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintInternalFrameTitlePaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of an internal frame's title pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintInternalFrameTitlePaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a label.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintLabelBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a label.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintLabelBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a list.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintListBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a list.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintListBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a menu.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintMenuBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a menu.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintMenuBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a menu bar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintMenuBarBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a menu bar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintMenuBarBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a menu item.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintMenuItemBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a menu item.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintMenuItemBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of an option pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintOptionPaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of an option pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintOptionPaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a panel.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintPanelBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a panel.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintPanelBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a password field.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintPasswordFieldBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a password field.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintPasswordFieldBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a popup menu.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintPopupMenuBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a popup menu.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintPopupMenuBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a progress bar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintProgressBarBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a progress bar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintProgressBarBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a radio button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintRadioButtonBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a radio button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintRadioButtonBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a radio button menu item.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintRadioButtonMenuItemBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a radio button menu item.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintRadioButtonMenuItemBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a root pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintRootPaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a root pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintRootPaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a scrollbar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintScrollBarBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a scrollbar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintScrollBarBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a scrollbar's thumb.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param orientation orientation of the scrollbar
+ */
+ public void paintScrollBarThumbBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h, int orientation)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a scrollbar's thumb.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param orientation orientation of the scrollbar
+ */
+ public void paintScrollBarThumbBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h, int orientation)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a scrollbar's track.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintScrollBarTrackBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a scrollbar's track.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintScrollBarTrackBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a scroll pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintScrollPaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a scroll pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintScrollPaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a separator.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSeparatorBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a separator.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSeparatorBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a slider.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSliderBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a slider.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSliderBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a slider's thumb.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param orientation orientation of the slider
+ */
+ public void paintSliderThumbBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h, int orientation)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a slider's thumb.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param orientation orientation of the slider
+ */
+ public void paintSliderThumbBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h, int orientation)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a slider's track.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSliderTrackBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a slider's track.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSliderTrackBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a spinner.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSpinnerBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a spinner.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSpinnerBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a split pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSplitPaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a split pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSplitPaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a split pane's divider.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintSplitPaneDividerBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a tabbed pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTabbedPaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a tabbed pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTabbedPaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of the contents of a tabbed pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTabbedPaneContentBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of the contents of a tabbed pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTabbedPaneContentBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of the tab area of a tabbed pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTabbedPaneTabAreaBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of the tab area of a tabbed pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTabbedPaneTabAreaBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a tab of a tabbed pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param index the index of the tab to paint
+ */
+ public void paintTabbedPaneTabBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h, int index)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a tab of a tabbed pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ * @param index the index of the tab to paint
+ */
+ public void paintTabbedPaneTabBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h, int index)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a table.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTableBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a table.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTableBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a table's header.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTableHeaderBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a table's header.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTableHeaderBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a text area.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTextAreaBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a text area.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTextAreaBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a text field.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTextFieldBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a text field.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTextFieldBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a text pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTextPaneBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a text pane.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTextPaneBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a toggle button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToggleButtonBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a toggle button.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToggleButtonBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a toolbar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToolBarBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a toolbar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToolBarBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of the contents of a toolbar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToolBarContentBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of the contents of a toolbar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToolBarContentBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of the window of a detached toolbar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToolBarDragWindowBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of the window of a detached toolbar.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToolBarDragWindowBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a tool tip.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToolTipBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a tool tip.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintToolTipBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a tree.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTreeBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a tree.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTreeBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a cell in a tree.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTreeCellBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a cell in a tree.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintTreeCellBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the background of a viewport.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintViewportBackground(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border of a viewport.
+ *
+ * @param ctx the synth context identifying the component and region for
+ * painting
+ * @param g the graphics context to use for painting
+ * @param x the X coordinate of the area to paint
+ * @param y the Y coordinate of the area to paint
+ * @param w the width of the area to paint
+ * @param h the height of the area to paint
+ */
+ public void paintViewportBorder(SynthContext ctx, Graphics g, int x,
+ int y, int w, int h)
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthStyle.java b/libjava/classpath/javax/swing/plaf/synth/SynthStyle.java
new file mode 100644
index 000000000..f5ab3df3b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/SynthStyle.java
@@ -0,0 +1,203 @@
+/* SynthStyle.java -- A set of style properties
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+import gnu.classpath.NotImplementedException;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Insets;
+
+import javax.swing.Icon;
+
+/**
+ * A set of style properties that can be installed on a component.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.5
+ */
+public abstract class SynthStyle
+{
+
+ /**
+ * Creates a new <code>SynthStyle</code> object.
+ */
+ public SynthStyle()
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ }
+
+ public SynthGraphicsUtils getGraphicsUtils(SynthContext ctx)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return null;
+ }
+
+ public Color getColor(SynthContext ctx, ColorType type)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return null;
+ }
+
+ protected abstract Color getColorForState(SynthContext ctx, ColorType type);
+
+ public Font getFont(SynthContext ctx)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return null;
+ }
+
+ protected abstract Font getFontForState(SynthContext ctx);
+
+ public Insets getInsets(SynthContext ctx, Insets result)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return null;
+ }
+
+ public SynthPainter getPainter(SynthContext ctx)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return null;
+ }
+
+ public boolean isOpaque(SynthContext ctx)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return true;
+ }
+
+ public Object get(SynthContext ctx, Object key)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ return null;
+ }
+
+ public void installDefaults(SynthContext ctx)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ }
+
+ public void uninstallDefaults(SynthContext ctx)
+ throws NotImplementedException
+ {
+ // FIXME: Implement this correctly.
+ }
+
+ /**
+ * A convenience method to fetch an integer property.
+ * If the property's value is a {@link Number}, then the
+ * integer value is returned. Otherwise, the default value
+ * is returned.
+ * @param ctx the context
+ * @param key the key to fetch
+ * @param defaultValue the default value
+ * @return the integer value of the property, or the default value
+ */
+ public int getInt(SynthContext ctx, Object key, int defaultValue)
+ {
+ Object obj = get(ctx, key);
+ if (obj instanceof Number)
+ return ((Number) obj).intValue();
+ return defaultValue;
+ }
+
+ /**
+ * A convenience method to fetch an integer property.
+ * If the property's value is a {@link Boolean}, then the
+ * value is returned. Otherwise, the default value
+ * is returned.
+ * @param ctx the context
+ * @param key the key to fetch
+ * @param defaultValue the default value
+ * @return the boolean value of the property, or the default value
+ */
+ public boolean getBoolean(SynthContext ctx, Object key,
+ boolean defaultValue)
+ {
+ Object obj = get(ctx, key);
+ if (obj instanceof Boolean)
+ return ((Boolean) obj).booleanValue();
+ return defaultValue;
+ }
+
+ /**
+ * A convenience method to fetch an Icon-valued property.
+ * If the property's value is an {@link Icon}, then the
+ * value is returned. Otherwise, null is returned.
+ * @param ctx the context
+ * @param key the key to fetch
+ * @return the icon, or null
+ */
+ public Icon getIcon(SynthContext ctx, Object key)
+ {
+ Object obj = get(ctx, key);
+ if (key instanceof Icon)
+ return (Icon) obj;
+ return null;
+ }
+
+ /**
+ * A convenience method to fetch a String property.
+ * If the property's value is a {@link String}, then the
+ * value is returned. Otherwise, the default value
+ * is returned.
+ * @param ctx the context
+ * @param key the key to fetch
+ * @param defaultValue the default value
+ * @return the String value of the property, or the default value
+ */
+ public String getString(SynthContext ctx, Object key, String defaultValue)
+ {
+ Object obj = get(ctx, key);
+ if (obj instanceof String)
+ return (String) obj;
+ return defaultValue;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthStyleFactory.java b/libjava/classpath/javax/swing/plaf/synth/SynthStyleFactory.java
new file mode 100644
index 000000000..569753d8a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/SynthStyleFactory.java
@@ -0,0 +1,64 @@
+/* SynthStyleFactory.java -- A factory for SynthStyles
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.synth;
+
+import javax.swing.JComponent;
+
+public abstract class SynthStyleFactory
+{
+
+ /**
+ * Creates a new <code>SynthStyleFactory</code>.
+ */
+ public SynthStyleFactory()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a {@link SynthStyle} for the specified region of the specified
+ * component.
+ *
+ * @param c the component for which to create a style
+ * @param id the region of the component
+ *
+ * @return a style for the specified region of the specified component
+ */
+ public abstract SynthStyle getStyle(JComponent c, Region id);
+}
diff --git a/libjava/classpath/javax/swing/plaf/synth/package.html b/libjava/classpath/javax/swing/plaf/synth/package.html
new file mode 100644
index 000000000..b977e468c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/synth/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.plaf.metal package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.plaf.synth</title></head>
+
+<body>
+<p>Provides a look and feel that can be customized by and XML file or by
+ providing a custom {@link SynthStyleFactory}.
+</p>
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/table/AbstractTableModel.java b/libjava/classpath/javax/swing/table/AbstractTableModel.java
new file mode 100644
index 000000000..7e9886d30
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/AbstractTableModel.java
@@ -0,0 +1,303 @@
+/* AbstractTableModel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.EventListenerList;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+
+/**
+ * A base class that can be used to create implementations of the
+ * {@link TableModel} interface.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class AbstractTableModel implements TableModel, Serializable
+{
+ static final long serialVersionUID = -5798593159423650347L;
+
+ /**
+ * Storage for the listeners registered with this model.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Creates a default instance.
+ */
+ public AbstractTableModel()
+ {
+ // no setup required here
+ }
+
+ /**
+ * Returns the name of the specified column. This method generates default
+ * names in a sequence (starting with column 0): A, B, C, ..., Z, AA, AB,
+ * AC, ..., AZ, BA, BB, BC, and so on. Subclasses may override this method
+ * to allow column names to be specified on some other basis.
+ *
+ * @param columnIndex the column index.
+ *
+ * @return The name of the column.
+ */
+ public String getColumnName(int columnIndex)
+ {
+ CPStringBuilder buffer = new CPStringBuilder();
+ while (columnIndex >= 0)
+ {
+ buffer.insert(0, (char) ('A' + columnIndex % 26));
+ columnIndex = columnIndex / 26 - 1;
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Return the index of the specified column, or <code>-1</code> if there is
+ * no column with the specified name.
+ *
+ * @param columnName the name of the column (<code>null</code> not permitted).
+ *
+ * @return The index of the column, -1 if not found.
+ *
+ * @see #getColumnName(int)
+ * @throws NullPointerException if <code>columnName</code> is
+ * <code>null</code>.
+ */
+ public int findColumn(String columnName)
+ {
+ int count = getColumnCount();
+
+ for (int index = 0; index < count; index++)
+ {
+ String name = getColumnName(index);
+
+ if (columnName.equals(name))
+ return index;
+ }
+
+ // Unable to locate.
+ return -1;
+ }
+
+ /**
+ * Returns the <code>Class</code> for all <code>Object</code> instances
+ * in the specified column.
+ *
+ * @param columnIndex the column index.
+ *
+ * @return The class.
+ */
+ public Class<?> getColumnClass(int columnIndex)
+ {
+ return Object.class;
+ }
+
+ /**
+ * Returns <code>true</code> if the specified cell is editable, and
+ * <code>false</code> if it is not. This implementation returns
+ * <code>false</code> for all arguments, subclasses should override the
+ * method if necessary.
+ *
+ * @param rowIndex the row index of the cell.
+ * @param columnIndex the column index of the cell.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isCellEditable(int rowIndex, int columnIndex)
+ {
+ return false;
+ }
+
+ /**
+ * Sets the value of the given cell. This implementation ignores all
+ * arguments and does nothing, subclasses should override the
+ * method if necessary.
+ *
+ * @param value the new value (<code>null</code> permitted).
+ * @param rowIndex the row index of the cell.
+ * @param columnIndex the column index of the cell.
+ */
+ public void setValueAt(Object value, int rowIndex, int columnIndex)
+ {
+ // Do nothing...
+ }
+
+ /**
+ * Adds a listener to the table model. The listener will receive notification
+ * of all changes to the table model.
+ *
+ * @param listener the listener.
+ */
+ public void addTableModelListener(TableModelListener listener)
+ {
+ listenerList.add(TableModelListener.class, listener);
+ }
+
+ /**
+ * Removes a listener from the table model so that it will no longer receive
+ * notification of changes to the table model.
+ *
+ * @param listener the listener to remove.
+ */
+ public void removeTableModelListener(TableModelListener listener)
+ {
+ listenerList.remove(TableModelListener.class, listener);
+ }
+
+ /**
+ * Returns an array containing the listeners that have been added to the
+ * table model.
+ *
+ * @return Array of {@link TableModelListener} objects.
+ *
+ * @since 1.4
+ */
+ public TableModelListener[] getTableModelListeners()
+ {
+ return (TableModelListener[])
+ listenerList.getListeners(TableModelListener.class);
+ }
+
+ /**
+ * Sends a {@link TableModelEvent} to all registered listeners to inform
+ * them that the table data has changed.
+ */
+ public void fireTableDataChanged()
+ {
+ fireTableChanged(new TableModelEvent(this, 0, Integer.MAX_VALUE));
+ }
+
+ /**
+ * Sends a {@link TableModelEvent} to all registered listeners to inform
+ * them that the table structure has changed.
+ */
+ public void fireTableStructureChanged()
+ {
+ fireTableChanged(new TableModelEvent(this, TableModelEvent.HEADER_ROW));
+ }
+
+ /**
+ * Sends a {@link TableModelEvent} to all registered listeners to inform
+ * them that some rows have been inserted into the model.
+ *
+ * @param firstRow the index of the first row.
+ * @param lastRow the index of the last row.
+ */
+ public void fireTableRowsInserted(int firstRow, int lastRow)
+ {
+ fireTableChanged(new TableModelEvent(this, firstRow, lastRow,
+ TableModelEvent.ALL_COLUMNS,
+ TableModelEvent.INSERT));
+ }
+
+ /**
+ * Sends a {@link TableModelEvent} to all registered listeners to inform
+ * them that some rows have been updated.
+ *
+ * @param firstRow the index of the first row.
+ * @param lastRow the index of the last row.
+ */
+ public void fireTableRowsUpdated(int firstRow, int lastRow)
+ {
+ fireTableChanged(new TableModelEvent(this, firstRow, lastRow,
+ TableModelEvent.ALL_COLUMNS,
+ TableModelEvent.UPDATE));
+ }
+
+ /**
+ * Sends a {@link TableModelEvent} to all registered listeners to inform
+ * them that some rows have been deleted from the model.
+ *
+ * @param firstRow the index of the first row.
+ * @param lastRow the index of the last row.
+ */
+ public void fireTableRowsDeleted(int firstRow, int lastRow)
+ {
+ fireTableChanged(new TableModelEvent(this, firstRow, lastRow,
+ TableModelEvent.ALL_COLUMNS,
+ TableModelEvent.DELETE));
+ }
+
+ /**
+ * Sends a {@link TableModelEvent} to all registered listeners to inform
+ * them that a single cell has been updated.
+ *
+ * @param row the row index.
+ * @param column the column index.
+ */
+ public void fireTableCellUpdated(int row, int column)
+ {
+ fireTableChanged(new TableModelEvent(this, row, row, column));
+ }
+
+ /**
+ * Sends the specified event to all registered listeners.
+ *
+ * @param event the event to send.
+ */
+ public void fireTableChanged(TableModelEvent event)
+ {
+ int index;
+ TableModelListener listener;
+ Object[] list = listenerList.getListenerList();
+
+ for (index = 0; index < list.length; index += 2)
+ {
+ listener = (TableModelListener) list [index + 1];
+ listener.tableChanged(event);
+ }
+ }
+
+ /**
+ * Returns an array of listeners of the given type that are registered with
+ * this model.
+ *
+ * @param listenerType the listener class.
+ *
+ * @return An array of listeners (possibly empty).
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+}
diff --git a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java
new file mode 100644
index 000000000..5d4a16024
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java
@@ -0,0 +1,276 @@
+/* DefaultTableCellRenderer.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * Class to display every cells.
+ */
+public class DefaultTableCellRenderer extends JLabel
+ implements TableCellRenderer, Serializable
+{
+ static final long serialVersionUID = 7878911414715528324L;
+
+ protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+
+ public static class UIResource extends DefaultTableCellRenderer
+ implements javax.swing.plaf.UIResource
+ {
+ public UIResource()
+ {
+ super();
+ }
+ }
+
+ /**
+ * Stores the color set by setForeground().
+ */
+ Color foreground;
+
+ /**
+ * Stores the color set by setBackground().
+ */
+ Color background;
+
+ /**
+ * Creates a default table cell renderer with an empty border.
+ */
+ public DefaultTableCellRenderer()
+ {
+ super();
+ }
+
+ /**
+ * Assign the unselected-foreground.
+ *
+ * @param c the color to assign
+ */
+ public void setForeground(Color c)
+ {
+ super.setForeground(c);
+ foreground = c;
+ }
+
+ /**
+ * Assign the unselected-background.
+ *
+ * @param c the color to assign
+ */
+ public void setBackground(Color c)
+ {
+ super.setBackground(c);
+ background = c;
+ }
+
+ /**
+ * Look and feel has changed.
+ *
+ * <p>Replaces the current UI object with the latest version from
+ * the UIManager.</p>
+ */
+ public void updateUI()
+ {
+ super.updateUI();
+ background = null;
+ foreground = null;
+ }
+
+ /**
+ * Get the string value of the object and pass it to setText().
+ *
+ * @param table the JTable
+ * @param value the value of the object. For the text content,
+ * null is rendered as an empty cell.
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus,
+ int row, int column)
+ {
+ setValue(value);
+ setOpaque(true);
+
+ if (table == null)
+ return this;
+
+ if (isSelected)
+ {
+ super.setBackground(table.getSelectionBackground());
+ super.setForeground(table.getSelectionForeground());
+ }
+ else
+ {
+ if (background != null)
+ super.setBackground(background);
+ else
+ super.setBackground(table.getBackground());
+ if (foreground != null)
+ super.setForeground(foreground);
+ else
+ super.setForeground(table.getForeground());
+ }
+
+ Border b = null;
+ if (hasFocus)
+ {
+ if (isSelected)
+ b = UIManager.getBorder("Table.focusSelectedCellHighlightBorder");
+ if (b == null)
+ b = UIManager.getBorder("Table.focusCellHighlightBorder");
+ }
+ else
+ b = noFocusBorder;
+ setBorder(b);
+
+ setFont(table.getFont());
+
+ // If the current background is equal to the table's background, then we
+ // can avoid filling the background by setting the renderer opaque.
+ Color back = getBackground();
+ setOpaque(back != null && back.equals(table.getBackground()));
+
+ return this;
+ }
+
+ /**
+ * Overriden for performance.
+ *
+ * <p>This method needs to be overridden in a subclass to actually
+ * do something.</p>
+ *
+ * @return always true
+ */
+ public boolean isOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * Overriden for performance.
+ *
+ * <p>This method needs to be overridden in a subclass to actually
+ * do something.</p>
+ */
+ public void validate()
+ {
+ // Does nothing.
+ }
+
+ public void revalidate()
+ {
+ // Does nothing.
+ }
+
+ /**
+ * Overriden for performance.
+ *
+ * <p>This method needs to be overridden in a subclass to actually
+ * do something.</p>
+ */
+ public void repaint(long tm, int x, int y, int width, int height)
+ {
+ // Does nothing.
+ }
+
+ /**
+ * Overriden for performance.
+ *
+ * <p>This method needs to be overridden in a subclass to actually
+ * do something.</p>
+ */
+ public void repaint(Rectangle r)
+ {
+ // Does nothing.
+ }
+
+ /**
+ * Overriden for performance.
+ *
+ * <p>This method needs to be overridden in a subclass to actually
+ * do something.</p>
+ */
+ protected void firePropertyChange(String propertyName, Object oldValue,
+ Object newValue)
+ {
+ // Does nothing.
+ }
+
+ /**
+ * Overriden for performance.
+ *
+ * <p>This method needs to be overridden in a subclass to actually
+ * do something.</p>
+ */
+ public void firePropertyChange(String propertyName, boolean oldValue,
+ boolean newValue)
+ {
+ // Does nothing.
+ }
+
+ /**
+ * Sets the String for this cell.
+ *
+ * @param value the string value for this cell; if value is null it
+ * sets the text value to an empty string
+ */
+ protected void setValue(Object value)
+ {
+ if (value != null)
+ setText(value.toString());
+ else
+ // null is rendered as an empty cell.
+ setText("");
+ }
+}
diff --git a/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java b/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java
new file mode 100644
index 000000000..532b513a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java
@@ -0,0 +1,671 @@
+/* DefaultTableColumnModel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.Vector;
+
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.EventListenerList;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableColumnModelEvent;
+import javax.swing.event.TableColumnModelListener;
+
+/**
+ * A model that stores information about the columns used in a {@link JTable}.
+ *
+ * @see JTable#setColumnModel(TableColumnModel)
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultTableColumnModel
+ implements TableColumnModel, PropertyChangeListener, ListSelectionListener,
+ Serializable
+{
+ private static final long serialVersionUID = 6580012493508960512L;
+
+ /**
+ * Storage for the table columns.
+ */
+ protected Vector<TableColumn> tableColumns;
+
+ /**
+ * A selection model that keeps track of column selections.
+ */
+ protected ListSelectionModel selectionModel;
+
+ /**
+ * The space between the columns (the default value is <code>1</code>).
+ */
+ protected int columnMargin;
+
+ /**
+ * Storage for the listeners registered with the model.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * A change event used when notifying listeners of a change to the
+ * <code>columnMargin</code> field. This single event is reused for all
+ * notifications (it is lazily instantiated within the
+ * {@link #fireColumnMarginChanged()} method).
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * A flag that indicates whether or not columns can be selected.
+ */
+ protected boolean columnSelectionAllowed;
+
+ /**
+ * The total width of all the columns in this model.
+ */
+ protected int totalColumnWidth;
+
+ /**
+ * Creates a new table column model with zero columns. A default column
+ * selection model is created by calling {@link #createSelectionModel()}.
+ * The default value for <code>columnMargin</code> is <code>1</code> and
+ * the default value for <code>columnSelectionAllowed</code> is
+ * <code>false</code>.
+ */
+ public DefaultTableColumnModel()
+ {
+ tableColumns = new Vector();
+ selectionModel = createSelectionModel();
+ selectionModel.addListSelectionListener(this);
+ columnMargin = 1;
+ columnSelectionAllowed = false;
+ }
+
+ /**
+ * Adds a column to the model then calls
+ * {@link #fireColumnAdded(TableColumnModelEvent)} to notify the registered
+ * listeners. The model registers itself with the column as a
+ * {@link PropertyChangeListener} so that changes to the column width will
+ * invalidate the cached {@link #totalColumnWidth} value.
+ *
+ * @param column the column (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>column</code> is
+ * <code>null</code>.
+ *
+ * @see #removeColumn(TableColumn)
+ */
+ public void addColumn(TableColumn column)
+ {
+ if (column == null)
+ throw new IllegalArgumentException("Null 'col' argument.");
+ tableColumns.add(column);
+ column.addPropertyChangeListener(this);
+ invalidateWidthCache();
+ fireColumnAdded(new TableColumnModelEvent(this, 0,
+ tableColumns.size() - 1));
+ }
+
+ /**
+ * Removes a column from the model then calls
+ * {@link #fireColumnRemoved(TableColumnModelEvent)} to notify the registered
+ * listeners. If the specified column does not belong to the model, or is
+ * <code>null</code>, this method does nothing.
+ *
+ * @param column the column to be removed (<code>null</code> permitted).
+ *
+ * @see #addColumn(TableColumn)
+ */
+ public void removeColumn(TableColumn column)
+ {
+ int index = this.tableColumns.indexOf(column);
+ if (index < 0)
+ return;
+ tableColumns.remove(column);
+ fireColumnRemoved(new TableColumnModelEvent(this, index, 0));
+ column.removePropertyChangeListener(this);
+ invalidateWidthCache();
+ }
+
+ /**
+ * Moves the column at index i to the position specified by index j, then
+ * calls {@link #fireColumnMoved(TableColumnModelEvent)} to notify registered
+ * listeners.
+ *
+ * @param i index of the column that will be moved.
+ * @param j index of the column's new location.
+ *
+ * @throws IllegalArgumentException if <code>i</code> or <code>j</code> are
+ * outside the range <code>0</code> to <code>N-1</code>, where
+ * <code>N</code> is the column count.
+ */
+ public void moveColumn(int i, int j)
+ {
+ int columnCount = getColumnCount();
+ if (i < 0 || i >= columnCount)
+ throw new IllegalArgumentException("Index 'i' out of range.");
+ if (j < 0 || j >= columnCount)
+ throw new IllegalArgumentException("Index 'j' out of range.");
+ TableColumn column = tableColumns.remove(i);
+ tableColumns.add(j, column);
+ fireColumnMoved(new TableColumnModelEvent(this, i, j));
+ }
+
+ /**
+ * Sets the column margin then calls {@link #fireColumnMarginChanged()} to
+ * notify the registered listeners.
+ *
+ * @param margin the column margin.
+ *
+ * @see #getColumnMargin()
+ */
+ public void setColumnMargin(int margin)
+ {
+ columnMargin = margin;
+ fireColumnMarginChanged();
+ }
+
+ /**
+ * Returns the number of columns in the model.
+ *
+ * @return The column count.
+ */
+ public int getColumnCount()
+ {
+ return tableColumns.size();
+ }
+
+ /**
+ * Returns an enumeration of the columns in the model.
+ *
+ * @return An enumeration of the columns in the model.
+ */
+ public Enumeration<TableColumn> getColumns()
+ {
+ return tableColumns.elements();
+ }
+
+ /**
+ * Returns the index of the {@link TableColumn} with the given identifier.
+ *
+ * @param identifier the identifier (<code>null</code> not permitted).
+ *
+ * @return The index of the {@link TableColumn} with the given identifier.
+ *
+ * @throws IllegalArgumentException if <code>identifier</code> is
+ * <code>null</code> or there is no column with that identifier.
+ */
+ public int getColumnIndex(Object identifier)
+ {
+ if (identifier == null)
+ throw new IllegalArgumentException("Null identifier.");
+ int columnCount = tableColumns.size();
+ for (int i = 0; i < columnCount; i++)
+ {
+ TableColumn tc = tableColumns.get(i);
+ if (identifier.equals(tc.getIdentifier()))
+ return i;
+ }
+ throw new IllegalArgumentException("No TableColumn with that identifier.");
+ }
+
+ /**
+ * Returns the column at the specified index.
+ *
+ * @param columnIndex the column index (in the range from <code>0</code> to
+ * <code>N-1</code>, where <code>N</code> is the number of columns in
+ * the model).
+ *
+ * @return The column at the specified index.
+ *
+ * @throws ArrayIndexOutOfBoundsException if <code>i</code> is not within
+ * the specified range.
+ */
+ public TableColumn getColumn(int columnIndex)
+ {
+ return tableColumns.get(columnIndex);
+ }
+
+ /**
+ * Returns the column margin.
+ *
+ * @return The column margin.
+ *
+ * @see #setColumnMargin(int)
+ */
+ public int getColumnMargin()
+ {
+ return columnMargin;
+ }
+
+ /**
+ * Returns the index of the column that contains the specified x-coordinate.
+ * This method assumes that:
+ * <ul>
+ * <li>column zero begins at position zero;</li>
+ * <li>all columns appear in order;</li>
+ * <li>individual column widths are taken into account, but the column margin
+ * is ignored.</li>
+ * </ul>
+ * If no column contains the specified position, this method returns
+ * <code>-1</code>.
+ *
+ * @param x the x-position.
+ *
+ * @return The column index, or <code>-1</code>.
+ */
+ public int getColumnIndexAtX(int x)
+ {
+ for (int i = 0; i < tableColumns.size(); ++i)
+ {
+ int w = (tableColumns.get(i)).getWidth();
+ if (0 <= x && x < w)
+ return i;
+ else
+ x -= w;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns total width of all the columns in the model, ignoring the
+ * {@link #columnMargin}.
+ *
+ * @return The total width of all the columns.
+ */
+ public int getTotalColumnWidth()
+ {
+ if (totalColumnWidth == -1)
+ recalcWidthCache();
+ return totalColumnWidth;
+ }
+
+ /**
+ * Sets the selection model that will be used to keep track of the selected
+ * columns.
+ *
+ * @param model the selection model (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>model</code> is
+ * <code>null</code>.
+ *
+ * @see #getSelectionModel()
+ */
+ public void setSelectionModel(ListSelectionModel model)
+ {
+ if (model == null)
+ throw new IllegalArgumentException();
+
+ selectionModel.removeListSelectionListener(this);
+ selectionModel = model;
+ selectionModel.addListSelectionListener(this);
+ }
+
+ /**
+ * Returns the selection model used to track table column selections.
+ *
+ * @return The selection model.
+ *
+ * @see #setSelectionModel(ListSelectionModel)
+ */
+ public ListSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ /**
+ * Sets the flag that indicates whether or not column selection is allowed.
+ *
+ * @param flag the new flag value.
+ *
+ * @see #getColumnSelectionAllowed()
+ */
+ public void setColumnSelectionAllowed(boolean flag)
+ {
+ columnSelectionAllowed = flag;
+ }
+
+ /**
+ * Returns <code>true</code> if column selection is allowed, and
+ * <code>false</code> if column selection is not allowed.
+ *
+ * @return A boolean.
+ *
+ * @see #setColumnSelectionAllowed(boolean)
+ */
+ public boolean getColumnSelectionAllowed()
+ {
+ return columnSelectionAllowed;
+ }
+
+ /**
+ * Returns an array containing the indices of the selected columns.
+ *
+ * @return An array containing the indices of the selected columns.
+ */
+ public int[] getSelectedColumns()
+ {
+ // FIXME: Implementation of this method was taken from private method
+ // JTable.getSelections(), which is used in various places in JTable
+ // including selected row calculations and cannot be simply removed.
+ // This design should be improved to illuminate duplication of code.
+
+ ListSelectionModel lsm = this.selectionModel;
+ int sz = getSelectedColumnCount();
+ int [] ret = new int[sz];
+
+ int lo = lsm.getMinSelectionIndex();
+ int hi = lsm.getMaxSelectionIndex();
+ int j = 0;
+ java.util.ArrayList ls = new java.util.ArrayList();
+ if (lo != -1 && hi != -1)
+ {
+ switch (lsm.getSelectionMode())
+ {
+ case ListSelectionModel.SINGLE_SELECTION:
+ ret[0] = lo;
+ break;
+
+ case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ ret[j++] = i;
+ break;
+
+ case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ if (lsm.isSelectedIndex(i))
+ ret[j++] = i;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the number of selected columns in the model.
+ *
+ * @return The selected column count.
+ *
+ * @see #getSelectionModel()
+ */
+ public int getSelectedColumnCount()
+ {
+ // FIXME: Implementation of this method was taken from private method
+ // JTable.countSelections(), which is used in various places in JTable
+ // including selected row calculations and cannot be simply removed.
+ // This design should be improved to illuminate duplication of code.
+
+ ListSelectionModel lsm = this.selectionModel;
+ int lo = lsm.getMinSelectionIndex();
+ int hi = lsm.getMaxSelectionIndex();
+ int sum = 0;
+
+ if (lo != -1 && hi != -1)
+ {
+ switch (lsm.getSelectionMode())
+ {
+ case ListSelectionModel.SINGLE_SELECTION:
+ sum = 1;
+ break;
+
+ case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
+ sum = hi - lo + 1;
+ break;
+
+ case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ if (lsm.isSelectedIndex(i))
+ ++sum;
+ break;
+ }
+ }
+
+ return sum;
+ }
+
+ /**
+ * Registers a listener with the model, so that it will receive
+ * {@link TableColumnModelEvent} notifications.
+ *
+ * @param listener the listener (<code>null</code> ignored).
+ */
+ public void addColumnModelListener(TableColumnModelListener listener)
+ {
+ listenerList.add(TableColumnModelListener.class, listener);
+ }
+
+ /**
+ * Deregisters a listener so that it no longer receives notification of
+ * changes to this model.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeColumnModelListener(TableColumnModelListener listener)
+ {
+ listenerList.remove(TableColumnModelListener.class, listener);
+ }
+
+ /**
+ * Returns an array containing the listeners that are registered with the
+ * model. If there are no listeners, an empty array is returned.
+ *
+ * @return An array containing the listeners that are registered with the
+ * model.
+ *
+ * @see #addColumnModelListener(TableColumnModelListener)
+ * @since 1.4
+ */
+ public TableColumnModelListener[] getColumnModelListeners()
+ {
+ return (TableColumnModelListener[])
+ listenerList.getListeners(TableColumnModelListener.class);
+ }
+
+ /**
+ * Sends the specified {@link TableColumnModelEvent} to all registered
+ * listeners, to indicate that a column has been added to the model. The
+ * event's <code>toIndex</code> attribute should contain the index of the
+ * added column.
+ *
+ * @param e the event.
+ *
+ * @see #addColumn(TableColumn)
+ */
+ protected void fireColumnAdded(TableColumnModelEvent e)
+ {
+ TableColumnModelListener[] listeners = getColumnModelListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].columnAdded(e);
+ }
+
+ /**
+ * Sends the specified {@link TableColumnModelEvent} to all registered
+ * listeners, to indicate that a column has been removed from the model. The
+ * event's <code>fromIndex</code> attribute should contain the index of the
+ * removed column.
+ *
+ * @param e the event.
+ *
+ * @see #removeColumn(TableColumn)
+ */
+ protected void fireColumnRemoved(TableColumnModelEvent e)
+ {
+ TableColumnModelListener[] listeners = getColumnModelListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].columnRemoved(e);
+ }
+
+ /**
+ * Sends the specified {@link TableColumnModelEvent} to all registered
+ * listeners, to indicate that a column in the model has been moved. The
+ * event's <code>fromIndex</code> attribute should contain the old column
+ * index, and the <code>toIndex</code> attribute should contain the new
+ * column index.
+ *
+ * @param e the event.
+ *
+ * @see #moveColumn(int, int)
+ */
+ protected void fireColumnMoved(TableColumnModelEvent e)
+ {
+ TableColumnModelListener[] listeners = getColumnModelListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].columnMoved(e);
+ }
+
+ /**
+ * Sends the specified {@link ListSelectionEvent} to all registered listeners,
+ * to indicate that the column selections have changed.
+ *
+ * @param e the event.
+ *
+ * @see #valueChanged(ListSelectionEvent)
+ */
+ protected void fireColumnSelectionChanged(ListSelectionEvent e)
+ {
+ EventListener [] listeners = getListeners(TableColumnModelListener.class);
+ for (int i = 0; i < listeners.length; ++i)
+ ((TableColumnModelListener) listeners[i]).columnSelectionChanged(e);
+ }
+
+ /**
+ * Sends a {@link ChangeEvent} to the model's registered listeners to
+ * indicate that the column margin was changed.
+ *
+ * @see #setColumnMargin(int)
+ */
+ protected void fireColumnMarginChanged()
+ {
+ EventListener[] listeners = getListeners(TableColumnModelListener.class);
+ if (changeEvent == null && listeners.length > 0)
+ changeEvent = new ChangeEvent(this);
+ for (int i = 0; i < listeners.length; ++i)
+ ((TableColumnModelListener) listeners[i]).columnMarginChanged(changeEvent);
+ }
+
+ /**
+ * Returns an array containing the listeners (of the specified type) that
+ * are registered with this model.
+ *
+ * @param listenerType the listener type (must indicate a subclass of
+ * {@link EventListener}, <code>null</code> not permitted).
+ *
+ * @return An array containing the listeners (of the specified type) that
+ * are registered with this model.
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Receives notification of property changes for the columns in the model.
+ * If the <code>width</code> property for any column changes, we invalidate
+ * the {@link #totalColumnWidth} value here.
+ *
+ * @param event the event.
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getPropertyName().equals("width"))
+ invalidateWidthCache();
+ }
+
+ /**
+ * Receives notification of the change to the list selection model, and
+ * responds by calling
+ * {@link #fireColumnSelectionChanged(ListSelectionEvent)}.
+ *
+ * @param e the list selection event.
+ *
+ * @see #getSelectionModel()
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ fireColumnSelectionChanged(e);
+ }
+
+ /**
+ * Creates a default selection model to track the currently selected
+ * column(s). This method is called by the constructor and returns a new
+ * instance of {@link DefaultListSelectionModel}.
+ *
+ * @return A new default column selection model.
+ */
+ protected ListSelectionModel createSelectionModel()
+ {
+ return new DefaultListSelectionModel();
+ }
+
+ /**
+ * Recalculates the total width of the columns, if the cached value is
+ * <code>-1</code>. Otherwise this method does nothing.
+ *
+ * @see #getTotalColumnWidth()
+ */
+ protected void recalcWidthCache()
+ {
+ if (totalColumnWidth == -1)
+ {
+ totalColumnWidth = 0;
+ for (int i = 0; i < tableColumns.size(); ++i)
+ {
+ totalColumnWidth += tableColumns.get(i).getWidth();
+ }
+ }
+ }
+
+ /**
+ * Sets the {@link #totalColumnWidth} field to <code>-1</code>.
+ *
+ * @see #recalcWidthCache()
+ */
+ private void invalidateWidthCache()
+ {
+ totalColumnWidth = -1;
+ }
+}
diff --git a/libjava/classpath/javax/swing/table/DefaultTableModel.java b/libjava/classpath/javax/swing/table/DefaultTableModel.java
new file mode 100644
index 000000000..e1d5e6888
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/DefaultTableModel.java
@@ -0,0 +1,634 @@
+/* DefaultTableModel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import java.io.Serializable;
+import java.util.Vector;
+
+import javax.swing.event.TableModelEvent;
+
+/**
+ * A two dimensional data structure used to store <code>Object</code>
+ * instances, usually for display in a <code>JTable</code> component.
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultTableModel extends AbstractTableModel
+ implements Serializable
+{
+ static final long serialVersionUID = 6680042567037222321L;
+
+ /**
+ * Storage for the rows in the table (each row is itself
+ * a <code>Vector</code>).
+ */
+ protected Vector dataVector;
+
+ /**
+ * Storage for the column identifiers.
+ */
+ protected Vector columnIdentifiers;
+
+ /**
+ * Creates an empty table with zero rows and zero columns.
+ */
+ public DefaultTableModel()
+ {
+ this(0, 0);
+ }
+
+ /**
+ * Creates a new table with the specified number of rows and columns.
+ * All cells in the table are initially empty (set to <code>null</code>).
+ *
+ * @param numRows the number of rows.
+ * @param numColumns the number of columns.
+ */
+ public DefaultTableModel(int numRows, int numColumns)
+ {
+ Vector defaultNames = new Vector(numColumns);
+ Vector data = new Vector(numRows);
+ for (int i = 0; i < numColumns; i++)
+ {
+ defaultNames.add(super.getColumnName(i));
+ }
+ for (int r = 0; r < numRows; r++)
+ {
+ Vector tmp = new Vector(numColumns);
+ tmp.setSize(numColumns);
+ data.add(tmp);
+ }
+ setDataVector(data, defaultNames);
+ }
+
+ /**
+ * Creates a new table with the specified column names and number of
+ * rows. The number of columns is determined by the number of column
+ * names supplied.
+ *
+ * @param columnNames the column names.
+ * @param numRows the number of rows.
+ */
+ public DefaultTableModel(Vector columnNames, int numRows)
+ {
+ if (numRows < 0)
+ throw new IllegalArgumentException("numRows < 0");
+ Vector data = new Vector();
+ int numColumns = 0;
+
+ if (columnNames != null)
+ numColumns = columnNames.size();
+
+ while (0 < numRows--)
+ {
+ Vector rowData = new Vector();
+ rowData.setSize(numColumns);
+ data.add(rowData);
+ }
+ setDataVector(data, columnNames);
+ }
+
+ /**
+ * Creates a new table with the specified column names and row count.
+ *
+ * @param columnNames the column names.
+ * @param numRows the number of rows.
+ */
+ public DefaultTableModel(Object[] columnNames, int numRows)
+ {
+ this(convertToVector(columnNames), numRows);
+ }
+
+ /**
+ * Creates a new table with the specified data values and column names.
+ *
+ * @param data the data values.
+ * @param columnNames the column names.
+ */
+ public DefaultTableModel(Vector data, Vector columnNames)
+ {
+ setDataVector(data, columnNames);
+ }
+
+ /**
+ * Creates a new table with the specified data values and column names.
+ *
+ * @param data the data values.
+ * @param columnNames the column names.
+ */
+ public DefaultTableModel(Object[][] data, Object[] columnNames)
+ {
+ this(convertToVector(data), convertToVector(columnNames));
+ }
+
+ /**
+ * Returns the vector containing the row data for the table.
+ *
+ * @return The data vector.
+ */
+ public Vector getDataVector()
+ {
+ return dataVector;
+ }
+
+ /**
+ * Sets the data and column identifiers for the table. The data vector
+ * contains a <code>Vector</code> for each row in the table - if the
+ * number of objects in each row does not match the number of column
+ * names specified, the row data is truncated or expanded (by adding
+ * <code>null</code> values) as required.
+ *
+ * @param data the data for the table (a vector of row vectors).
+ * @param columnNames the column names.
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ */
+ public void setDataVector(Vector data, Vector columnNames)
+ {
+ if (data == null)
+ dataVector = new Vector();
+ else
+ dataVector = data;
+ setColumnIdentifiers(columnNames);
+ }
+
+ /**
+ * Sets the data and column identifiers for the table.
+ *
+ * @param data the data for the table.
+ * @param columnNames the column names.
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ */
+ public void setDataVector(Object[][] data, Object[] columnNames)
+ {
+ setDataVector(convertToVector(data),
+ convertToVector(columnNames));
+ }
+
+ /**
+ * Sends the specified <code>event</code> to all registered listeners.
+ * This method is equivalent to
+ * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
+ *
+ * @param event the event.
+ */
+ public void newDataAvailable(TableModelEvent event)
+ {
+ fireTableChanged(event);
+ }
+
+ /**
+ * Sends the specified <code>event</code> to all registered listeners.
+ * This method is equivalent to
+ * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
+ *
+ * @param event the event.
+ */
+ public void newRowsAdded(TableModelEvent event)
+ {
+ fireTableChanged(event);
+ }
+
+ /**
+ * Sends the specified <code>event</code> to all registered listeners.
+ * This method is equivalent to
+ * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
+ *
+ * @param event the event.
+ */
+ public void rowsRemoved(TableModelEvent event)
+ {
+ fireTableChanged(event);
+ }
+
+ /**
+ * Sets the column identifiers, updates the data rows (truncating
+ * or padding each row with <code>null</code> values) to match the
+ * number of columns, and sends a {@link TableModelEvent} to all
+ * registered listeners.
+ *
+ * @param columnIdentifiers the column identifiers.
+ */
+ public void setColumnIdentifiers(Vector columnIdentifiers)
+ {
+ this.columnIdentifiers = columnIdentifiers;
+ setColumnCount(columnIdentifiers == null ? 0 : columnIdentifiers.size());
+ }
+
+ /**
+ * Sets the column identifiers, updates the data rows (truncating
+ * or padding each row with <code>null</code> values) to match the
+ * number of columns, and sends a {@link TableModelEvent} to all
+ * registered listeners.
+ *
+ * @param columnIdentifiers the column identifiers.
+ */
+ public void setColumnIdentifiers(Object[] columnIdentifiers)
+ {
+ setColumnIdentifiers(convertToVector(columnIdentifiers));
+ }
+
+ /**
+ * This method is obsolete, use {@link #setRowCount(int)} instead.
+ *
+ * @param numRows the number of rows.
+ */
+ public void setNumRows(int numRows)
+ {
+ setRowCount(numRows);
+ }
+
+ /**
+ * Sets the number of rows in the table. If <code>rowCount</code> is less
+ * than the current number of rows in the table, rows are discarded.
+ * If <code>rowCount</code> is greater than the current number of rows in
+ * the table, new (empty) rows are added.
+ *
+ * @param rowCount the row count.
+ */
+ public void setRowCount(int rowCount)
+ {
+ int existingRowCount = dataVector.size();
+ if (rowCount < existingRowCount)
+ {
+ dataVector.setSize(rowCount);
+ fireTableRowsDeleted(rowCount, existingRowCount - 1);
+ }
+ else
+ {
+ int rowsToAdd = rowCount - existingRowCount;
+ addExtraRows(rowsToAdd, columnIdentifiers.size());
+ fireTableRowsInserted(existingRowCount, rowCount - 1);
+ }
+ }
+
+ /**
+ * Sets the number of columns in the table. Existing rows are truncated
+ * or padded with <code>null</code> values to match the new column count.
+ * A {@link TableModelEvent} is sent to all registered listeners.
+ *
+ * @param columnCount the column count.
+ */
+ public void setColumnCount(int columnCount)
+ {
+ for (int i = 0; i < dataVector.size(); ++i)
+ {
+ ((Vector) dataVector.get(i)).setSize(columnCount);
+ }
+ if (columnIdentifiers != null)
+ columnIdentifiers.setSize(columnCount);
+ fireTableStructureChanged();
+ }
+
+ /**
+ * Adds a column with the specified name to the table. All cell values
+ * for the column are initially set to <code>null</code>.
+ *
+ * @param columnName the column name (<code>null</code> permitted).
+ */
+ public void addColumn(Object columnName)
+ {
+ addColumn(columnName, (Object[]) null);
+ }
+
+ /**
+ * Adds a column with the specified name and data values to the table.
+ *
+ * @param columnName the column name (<code>null</code> permitted).
+ * @param columnData the column data.
+ */
+ public void addColumn(Object columnName, Vector columnData)
+ {
+ Object[] dataArray = null;
+ if (columnData != null)
+ {
+ int rowCount = dataVector.size();
+ if (columnData.size() < rowCount)
+ columnData.setSize(rowCount);
+ dataArray = columnData.toArray();
+ }
+ addColumn(columnName, dataArray);
+ }
+
+ /**
+ * Adds a column with the specified name and data values to the table.
+ *
+ * @param columnName the column name (<code>null</code> permitted).
+ * @param columnData the column data.
+ */
+ public void addColumn(Object columnName, Object[] columnData)
+ {
+ if (columnData != null)
+ {
+ // check columnData array for cases where the number of items
+ // doesn't match the number of rows in the existing table
+ if (columnData.length > dataVector.size())
+ {
+ int rowsToAdd = columnData.length - dataVector.size();
+ addExtraRows(rowsToAdd, columnIdentifiers.size());
+ }
+ else if (columnData.length < dataVector.size())
+ {
+ Object[] tmp = new Object[dataVector.size()];
+ System.arraycopy(columnData, 0, tmp, 0, columnData.length);
+ columnData = tmp;
+ }
+ }
+ for (int i = 0; i < dataVector.size(); ++i)
+ {
+ ((Vector) dataVector.get(i)).add(columnData == null ? null : columnData[i]);
+ }
+ columnIdentifiers.add(columnName);
+ fireTableStructureChanged();
+ }
+
+ /**
+ * Adds a new row containing the specified data to the table and sends a
+ * {@link TableModelEvent} to all registered listeners.
+ *
+ * @param rowData the row data (<code>null</code> permitted).
+ */
+ public void addRow(Vector rowData)
+ {
+ int rowIndex = dataVector.size();
+ dataVector.add(rowData);
+ newRowsAdded(new TableModelEvent(
+ this, rowIndex, rowIndex, -1, TableModelEvent.INSERT)
+ );
+ }
+
+ /**
+ * Adds a new row containing the specified data to the table and sends a
+ * {@link TableModelEvent} to all registered listeners.
+ *
+ * @param rowData the row data (<code>null</code> permitted).
+ */
+ public void addRow(Object[] rowData)
+ {
+ addRow(convertToVector(rowData));
+ }
+
+ /**
+ * Inserts a new row into the table.
+ *
+ * @param row the row index.
+ * @param rowData the row data.
+ */
+ public void insertRow(int row, Vector rowData)
+ {
+ dataVector.add(row, rowData);
+ fireTableRowsInserted(row, row);
+ }
+
+ /**
+ * Inserts a new row into the table.
+ *
+ * @param row the row index.
+ * @param rowData the row data.
+ */
+ public void insertRow(int row, Object[] rowData)
+ {
+ insertRow(row, convertToVector(rowData));
+ }
+
+ /**
+ * Moves the rows from <code>startIndex</code> to <code>endIndex</code>
+ * (inclusive) to the specified row.
+ *
+ * @param startIndex the start row.
+ * @param endIndex the end row.
+ * @param toIndex the row to move to.
+ */
+ public void moveRow(int startIndex, int endIndex, int toIndex)
+ {
+ Vector removed = new Vector();
+ for (int i = endIndex; i >= startIndex; i--)
+ {
+ removed.add(this.dataVector.remove(i));
+ }
+ for (int i = 0; i <= endIndex - startIndex; i++)
+ {
+ dataVector.insertElementAt(removed.get(i), toIndex);
+ }
+ int firstRow = Math.min(startIndex, toIndex);
+ int lastRow = Math.max(endIndex, toIndex + (endIndex - startIndex));
+ fireTableRowsUpdated(firstRow, lastRow);
+ }
+
+ /**
+ * Removes a row from the table and sends a {@link TableModelEvent} to
+ * all registered listeners.
+ *
+ * @param row the row index.
+ */
+ public void removeRow(int row)
+ {
+ dataVector.remove(row);
+ fireTableRowsDeleted(row, row);
+ }
+
+ /**
+ * Returns the number of rows in the model.
+ *
+ * @return The row count.
+ */
+ public int getRowCount()
+ {
+ return dataVector.size();
+ }
+
+ /**
+ * Returns the number of columns in the model.
+ *
+ * @return The column count.
+ */
+ public int getColumnCount()
+ {
+ return columnIdentifiers == null ? 0 : columnIdentifiers.size();
+ }
+
+ /**
+ * Get the name of the column. If the column has the column identifier set,
+ * the return value is the result of the .toString() method call on that
+ * identifier. If the identifier is not explicitly set, the returned value
+ * is calculated by {@link AbstractTableModel#getColumnName(int)}.
+ *
+ * @param column the column index.
+ *
+ * @return The column name.
+ */
+ public String getColumnName(int column)
+ {
+ String result = "";
+ if (columnIdentifiers == null)
+ result = super.getColumnName(column);
+ else
+ {
+ if (column < getColumnCount())
+ {
+ checkSize();
+ Object id = columnIdentifiers.get(column);
+ if (id != null)
+ result = id.toString();
+ else
+ result = super.getColumnName(column);
+ }
+ else
+ result = super.getColumnName(column);
+ }
+ return result;
+ }
+
+ /**
+ * Returns <code>true</code> if the specified cell can be modified, and
+ * <code>false</code> otherwise. For this implementation, the method
+ * always returns <code>true</code>.
+ *
+ * @param row the row index.
+ * @param column the column index.
+ *
+ * @return <code>true</code> in all cases.
+ */
+ public boolean isCellEditable(int row, int column)
+ {
+ return true;
+ }
+
+ /**
+ * Returns the value at the specified cell in the table.
+ *
+ * @param row the row index.
+ * @param column the column index.
+ *
+ * @return The value (<code>Object</code>, possibly <code>null</code>) at
+ * the specified cell in the table.
+ */
+ public Object getValueAt(int row, int column)
+ {
+ return ((Vector) dataVector.get(row)).get(column);
+ }
+
+ /**
+ * Sets the value for the specified cell in the table and sends a
+ * {@link TableModelEvent} to all registered listeners.
+ *
+ * @param value the value (<code>Object</code>, <code>null</code> permitted).
+ * @param row the row index.
+ * @param column the column index.
+ */
+ public void setValueAt(Object value, int row, int column)
+ {
+ ((Vector) dataVector.get(row)).set(column, value);
+ fireTableCellUpdated(row, column);
+ }
+
+ /**
+ * Converts the data array to a <code>Vector</code>.
+ *
+ * @param data the data array (<code>null</code> permitted).
+ *
+ * @return A vector (or <code>null</code> if the data array
+ * is <code>null</code>).
+ */
+ protected static Vector convertToVector(Object[] data)
+ {
+ if (data == null)
+ return null;
+ Vector vector = new Vector(data.length);
+ for (int i = 0; i < data.length; i++)
+ vector.add(data[i]);
+ return vector;
+ }
+
+ /**
+ * Converts the data array to a <code>Vector</code> of rows.
+ *
+ * @param data the data array (<code>null</code> permitted).
+ *
+ * @return A vector (or <code>null</code> if the data array
+ * is <code>null</code>.
+ */
+ protected static Vector convertToVector(Object[][] data)
+ {
+ if (data == null)
+ return null;
+ Vector vector = new Vector(data.length);
+ for (int i = 0; i < data.length; i++)
+ vector.add(convertToVector(data[i]));
+ return vector;
+ }
+
+ /**
+ * This method adds some rows to <code>dataVector</code>.
+ *
+ * @param rowsToAdd number of rows to add
+ * @param nbColumns size of the added rows
+ */
+ private void addExtraRows(int rowsToAdd, int nbColumns)
+ {
+ for (int i = 0; i < rowsToAdd; i++)
+ {
+ Vector tmp = new Vector();
+ tmp.setSize(columnIdentifiers.size());
+ dataVector.add(tmp);
+ }
+ }
+
+ /**
+ * Checks the real columns/rows sizes against the ones returned by
+ * <code>getColumnCount()</code> and <code>getRowCount()</code>.
+ * If the supposed one are bigger, then we grow <code>columIdentifiers</code>
+ * and <code>dataVector</code> to their expected size.
+ */
+ private void checkSize()
+ {
+ int columnCount = getColumnCount();
+ int rowCount = getRowCount();
+
+ if (columnCount > columnIdentifiers.size())
+ columnIdentifiers.setSize(columnCount);
+
+ if (dataVector != null && rowCount > dataVector.size())
+ {
+ int rowsToAdd = rowCount - dataVector.size();
+ addExtraRows(rowsToAdd, columnCount);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/table/JTableHeader.java b/libjava/classpath/javax/swing/table/JTableHeader.java
new file mode 100644
index 000000000..4eb91156b
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/JTableHeader.java
@@ -0,0 +1,1055 @@
+/* JTableHeader.java --
+ Copyright (C) 2003, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeListener;
+import java.util.Locale;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.accessibility.AccessibleValue;
+import javax.swing.JComponent;
+import javax.swing.JTable;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.TableColumnModelEvent;
+import javax.swing.event.TableColumnModelListener;
+import javax.swing.plaf.TableHeaderUI;
+
+/**
+ * Represents the table header. The header displays the column header values,
+ * is always visible event if the rest of the table scrolls up and down and
+ * supports column reordering and resizing with mouse.
+ */
+public class JTableHeader extends JComponent
+ implements TableColumnModelListener, Accessible
+{
+ protected class AccessibleJTableHeader extends AccessibleJComponent
+ {
+ protected class AccessibleJTableHeaderEntry extends AccessibleContext
+ implements Accessible, AccessibleComponent
+ {
+
+ private int columnIndex;
+
+ private JTableHeader parent;
+
+ private JTable table;
+
+ public AccessibleJTableHeaderEntry(int c, JTableHeader p, JTable t)
+ {
+ columnIndex = c;
+ parent = p;
+ table = t;
+ }
+
+ /**
+ * Returns the column header renderer.
+ *
+ * @return The column header renderer.
+ */
+ Component getColumnHeaderRenderer()
+ {
+ TableColumn tc = parent.getColumnModel().getColumn(columnIndex);
+ TableCellRenderer r = tc.getHeaderRenderer();
+ if (r == null)
+ r = parent.getDefaultRenderer();
+ return r.getTableCellRendererComponent(table, tc.headerValue,
+ false, false, -1, columnIndex);
+ }
+
+ /**
+ * Returns the accessible context for the column header renderer, or
+ * <code>null</code>.
+ *
+ * @return The accessible context.
+ */
+ AccessibleContext getAccessibleColumnHeaderRenderer()
+ {
+ Component c = getColumnHeaderRenderer();
+ if (c instanceof Accessible)
+ return c.getAccessibleContext();
+ return null;
+ }
+
+ /**
+ * @see #removeFocusListener(FocusListener)
+ */
+ public void addFocusListener(FocusListener l)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ c.addFocusListener(l);
+ }
+
+ /**
+ * @see #removePropertyChangeListener(PropertyChangeListener)
+ */
+ public void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ // add the listener to the accessible context for the header
+ // renderer...
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ ac.addPropertyChangeListener(l);
+ }
+
+ public boolean contains(Point p)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.contains(p);
+ else
+ return false;
+ }
+
+ public AccessibleAction getAccessibleAction()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac instanceof AccessibleAction)
+ return (AccessibleAction) ac;
+ else
+ return null;
+ }
+
+ public Accessible getAccessibleAt(Point p)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getAccessibleAt(p);
+ else
+ return null;
+ }
+
+ /**
+ * Returns <code>null</code> as the header entry has no accessible
+ * children.
+ *
+ * @return <code>null</code>.
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the number of accessible children, zero in this case.
+ *
+ * @return 0
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the accessible component for this header entry.
+ *
+ * @return <code>this</code>.
+ */
+ public AccessibleComponent getAccessibleComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible context for this header entry.
+ *
+ * @return <code>this</code>.
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible description.
+ *
+ * @return The accessible description.
+ *
+ * @see #setAccessibleDescription(String)
+ */
+ public String getAccessibleDescription()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ return ac.getAccessibleDescription();
+ return accessibleDescription;
+ }
+
+ /**
+ * Returns the index of this header entry.
+ *
+ * @return The index of this header entry.
+ */
+ public int getAccessibleIndexInParent()
+ {
+ return columnIndex;
+ }
+
+ /**
+ * Returns the accessible name.
+ *
+ * @return The accessible name.
+ *
+ * @see #setAccessibleName(String)
+ */
+ public String getAccessibleName()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ return ac.getAccessibleName();
+ return accessibleName;
+ }
+
+ /**
+ * Returns the accessible role for the header entry.
+ *
+ * @return The accessible role.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ return ac.getAccessibleRole();
+ else
+ return null;
+ }
+
+ public AccessibleSelection getAccessibleSelection()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac instanceof AccessibleValue)
+ return (AccessibleSelection) ac;
+ else
+ return null;
+ }
+
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ return ac.getAccessibleStateSet();
+ else
+ return null;
+ }
+
+ public AccessibleText getAccessibleText()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ return ac.getAccessibleText();
+ else
+ return null;
+ }
+
+ public AccessibleValue getAccessibleValue()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac instanceof AccessibleValue)
+ return (AccessibleValue) ac;
+ else
+ return null;
+ }
+
+ public Color getBackground()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getBackground();
+ else
+ return null;
+ }
+
+ public Rectangle getBounds()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getBounds();
+ else
+ return null;
+ }
+
+ public Cursor getCursor()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getCursor();
+ else
+ return null;
+ }
+
+ public Font getFont()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getFont();
+ else
+ return null;
+ }
+
+ public FontMetrics getFontMetrics(Font f)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getFontMetrics(f);
+ else
+ return null;
+ }
+
+ public Color getForeground()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getForeground();
+ else
+ return null;
+ }
+
+ public Locale getLocale()
+ {
+ Component c = getColumnHeaderRenderer();
+ if (c != null)
+ return c.getLocale();
+ return null;
+ }
+
+ public Point getLocation()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getLocation();
+ else
+ return null;
+ }
+
+ public Point getLocationOnScreen()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getLocationOnScreen();
+ else
+ return null;
+ }
+
+ public Dimension getSize()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.getSize();
+ else
+ return null;
+ }
+
+ public boolean isEnabled()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.isEnabled();
+ else
+ return false;
+ }
+
+ public boolean isFocusTraversable()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.isFocusTraversable();
+ else
+ return false;
+ }
+
+ public boolean isShowing()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.isShowing();
+ else
+ return false;
+ }
+
+ public boolean isVisible()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ return c.isVisible();
+ else
+ return false;
+ }
+
+ /**
+ * @see #addFocusListener(FocusListener)
+ */
+ public void removeFocusListener(FocusListener l)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ c.removeFocusListener(l);
+ }
+
+ /**
+ * @see #addPropertyChangeListener(PropertyChangeListener)
+ */
+ public void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ ac.removePropertyChangeListener(l);
+ }
+
+ /**
+ * @see #addFocusListener(FocusListener)
+ */
+ public void requestFocus()
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent c = ac.getAccessibleComponent();
+ if (c != null)
+ c.requestFocus();
+ }
+
+ /**
+ * @see #getAccessibleDescription()
+ */
+ public void setAccessibleDescription(String s)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ ac.setAccessibleDescription(s);
+ else
+ accessibleDescription = s;
+ }
+
+ /**
+ * @see #getAccessibleName()
+ */
+ public void setAccessibleName(String s)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ if (ac != null)
+ ac.setAccessibleName(s);
+ }
+
+ public void setBackground(Color c)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setBackground(c);
+ }
+
+ public void setBounds(Rectangle r)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setBounds(r);
+ }
+
+ public void setCursor(Cursor c)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setCursor(c);
+ }
+
+ public void setEnabled(boolean b)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setEnabled(b);
+ }
+
+ public void setFont(Font f)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setFont(f);
+ }
+
+ public void setForeground(Color c)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setForeground(c);
+ }
+
+ public void setLocation(Point p)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setLocation(p);
+ }
+
+ public void setSize(Dimension d)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setSize(d);
+ }
+
+ public void setVisible(boolean b)
+ {
+ AccessibleContext ac = getAccessibleColumnHeaderRenderer();
+ AccessibleComponent comp = ac.getAccessibleComponent();
+ if (comp != null)
+ comp.setVisible(b);
+ }
+ }
+
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PANEL;
+ }
+
+ public int getAccessibleChildrenCount()
+ {
+ return table.getColumnCount();
+ }
+
+ public Accessible getAccessibleChild(int i)
+ {
+ return new AccessibleJTableHeaderEntry(i, JTableHeader.this, table);
+ }
+
+ public Accessible getAccessibleAt(Point p)
+ {
+ return getAccessibleChild(columnAtPoint(p));
+ }
+ }
+
+ /**
+ * Use serialVersionUid for interoperability.
+ */
+ private static final long serialVersionUID = 5144633983372967710L;
+
+ /**
+ * The columnModel property.
+ */
+ protected TableColumnModel columnModel;
+
+ /**
+ * The draggedColumn property.
+ */
+ protected TableColumn draggedColumn;
+
+ /**
+ * The draggedDistance property.
+ */
+ protected int draggedDistance;
+
+ /**
+ * The opaque property.
+ */
+ boolean opaque;
+
+ /**
+ * The reorderingAllowed property.
+ */
+ protected boolean reorderingAllowed;
+
+ /**
+ * The resizingAllowed property.
+ */
+ protected boolean resizingAllowed = true;
+
+ /**
+ * The resizingColumn property.
+ */
+ protected TableColumn resizingColumn;
+
+ /**
+ * The table property.
+ */
+ protected JTable table;
+
+ /**
+ * The updateTableInRealTime property.
+ */
+ protected boolean updateTableInRealTime;
+
+ TableCellRenderer cellRenderer;
+
+ /**
+ * Creates a new default instance.
+ */
+ public JTableHeader()
+ {
+ this(null);
+ }
+
+ /**
+ * Creates a new header. If <code>cm</code> is <code>null</code>, a new
+ * table column model is created by calling
+ * {@link #createDefaultColumnModel()}.
+ *
+ * @param cm the table column model (<code>null</code> permitted).
+ */
+ public JTableHeader(TableColumnModel cm)
+ {
+ columnModel = cm == null ? createDefaultColumnModel() : cm;
+ initializeLocalVars();
+ updateUI();
+ }
+
+ /**
+ * Creates a default table column model.
+ *
+ * @return A default table column model.
+ */
+ protected TableColumnModel createDefaultColumnModel()
+ {
+ return new DefaultTableColumnModel();
+ }
+
+ /**
+ * Get the value of the {@link #accessibleContext} property.
+ *
+ * @return The current value of the property
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return accessibleContext;
+ }
+
+ /**
+ * Get the value of the {@link #columnModel} property.
+ *
+ * @return The current value of the property
+ */
+ public TableColumnModel getColumnModel()
+ {
+ return columnModel;
+ }
+
+ /**
+ * Get the column that is currently being dragged. This is used when
+ * handling the column reordering with mouse.
+ *
+ * @return the column being dragged, null if none.
+ */
+ public TableColumn getDraggedColumn()
+ {
+ return draggedColumn;
+ }
+
+ /**
+ * Get the value of the {@link #draggedDistance} property.
+ *
+ * @return The current value of the property
+ */
+ public int getDraggedDistance()
+ {
+ return draggedDistance;
+ }
+
+ /**
+ * Check if it is possible to reorder the table columns by dragging column
+ * header with mouse. The table reordering is enabled by default, but can be
+ * disabled with {@link #setReorderingAllowed(boolean)}.
+ *
+ * @return true if reordering is allowed, false otherwise.
+ */
+ public boolean getReorderingAllowed()
+ {
+ return reorderingAllowed;
+ }
+
+ /**
+ * Check if it is possible to resize the table columns by dragging the column
+ * boundary in the table header with mouse. The resizing is enabled
+ * by default, but can be disabled with {@link #setResizingAllowed(boolean)}.
+ *
+ * @return true if resizing is allowed, false otherwise.
+ */
+ public boolean getResizingAllowed()
+ {
+ return resizingAllowed;
+ }
+
+ /**
+ * Get the column that is currently being resized. This is used when
+ * handling the column resizing with mouse.
+ *
+ * @return the column being currently resized, null if none.
+ */
+ public TableColumn getResizingColumn()
+ {
+ return resizingColumn;
+ }
+
+ /**
+ * Get the table, having this header.
+ *
+ * @return the table, having this header.
+ */
+ public JTable getTable()
+ {
+ return table;
+ }
+
+ /**
+ * Get the value of the {@link #updateTableInRealTime} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getUpdateTableInRealTime()
+ {
+ return updateTableInRealTime;
+ }
+
+ /**
+ * Get the value of the {@link #opaque} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean isOpaque()
+ {
+ return opaque;
+ }
+
+ /**
+ * Set the value of the {@link #columnModel} property.
+ *
+ * @param c The new value of the property
+ */
+ public void setColumnModel(TableColumnModel c)
+ {
+ columnModel.removeColumnModelListener(this);
+ columnModel = c;
+ columnModel.addColumnModelListener(this);
+ }
+
+ /**
+ * Set the column that is currently being dragged. This is used when
+ * dragging the column with mouse. Setting to null will stop the
+ * dragging session immediately.
+ *
+ * @param draggingIt the column being currently dragged, null if none.
+ */
+ public void setDraggedColumn(TableColumn draggingIt)
+ {
+ draggedColumn = draggingIt;
+ }
+
+ /**
+ * Set the value of the {@link #draggedDistance} property.
+ *
+ * @param d The new value of the property
+ */
+ public void setDraggedDistance(int d)
+ {
+ draggedDistance = d;
+ }
+
+ /**
+ * Set the value of the {@link #opaque} property.
+ *
+ * @param o The new value of the property
+ */
+ public void setOpaque(boolean o)
+ {
+ opaque = o;
+ }
+
+ /**
+ * Set the table ability to reorder columns by dragging column header
+ * with mouse. The table reordering is enabled by default, but can be
+ * disabled with this method.
+ *
+ * @param allowed true if reordering is allowed, false otherwise.
+ */
+ public void setReorderingAllowed(boolean allowed)
+ {
+ reorderingAllowed = allowed;
+ }
+
+ /**
+ * Set the table ability to resize columns by dragging the column
+ * boundary in the table header with mouse. The resizing is enabled
+ * by default, but can be disabled using this method.
+ *
+ * @param allowed true if resizing is allowed, false otherwise.
+ */
+ public void setResizingAllowed(boolean allowed)
+ {
+ resizingAllowed = allowed;
+ }
+
+ /**
+ * The the column that is currently being resized. This property is used
+ * when handling table resizing with mouse. Setting to null would stop
+ * the resizing session immediately.
+ *
+ * @param resizingIt the column being currently resized
+ */
+ public void setResizingColumn(TableColumn resizingIt)
+ {
+ resizingColumn = resizingIt;
+ }
+
+ /**
+ * Set the value of the {@link #table} property.
+ *
+ * @param t The new value of the property
+ */
+ public void setTable(JTable t)
+ {
+ table = t;
+ }
+
+ /**
+ * Set the value of the {@link #updateTableInRealTime} property.
+ *
+ * @param u The new value of the property
+ */
+ public void setUpdateTableInRealTime(boolean u)
+ {
+ updateTableInRealTime = u;
+ }
+
+ /**
+ * Creates a default renderer.
+ *
+ * @return A default renderer.
+ */
+ protected TableCellRenderer createDefaultRenderer()
+ {
+ return new DefaultTableCellRenderer();
+ }
+
+ /**
+ * Returns the default table cell renderer.
+ *
+ * @return The default table cell renderer.
+ */
+ public TableCellRenderer getDefaultRenderer()
+ {
+ return cellRenderer;
+ }
+
+ /**
+ * Sets the default table cell renderer.
+ *
+ * @param cellRenderer the renderer.
+ */
+ public void setDefaultRenderer(TableCellRenderer cellRenderer)
+ {
+ this.cellRenderer = cellRenderer;
+ }
+
+ /**
+ * Get the rectangle, occupied by the header of the given column.
+ *
+ * @param column the column, for that the header area is requested.
+ *
+ * @return the column header area.
+ */
+ public Rectangle getHeaderRect(int column)
+ {
+ Rectangle r = getTable().getCellRect(-1, column, false);
+ r.height = getHeight();
+ return r;
+ }
+
+ protected String paramString()
+ {
+ return "JTableHeader";
+ }
+
+ // UI support
+
+ public String getUIClassID()
+ {
+ return "TableHeaderUI";
+ }
+
+ public TableHeaderUI getUI()
+ {
+ return (TableHeaderUI) ui;
+ }
+
+ public void setUI(TableHeaderUI u)
+ {
+ super.setUI(u);
+ }
+
+ public void updateUI()
+ {
+ setUI((TableHeaderUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the index of the column at the specified point.
+ *
+ * @param point the point.
+ *
+ * @return The column index, or -1.
+ */
+ public int columnAtPoint(Point point)
+ {
+ if (getBounds().contains(point))
+ return columnModel.getColumnIndexAtX(point.x);
+
+ return -1;
+ }
+
+ /**
+ * Receives notification when a column is added to the column model.
+ *
+ * @param event the table column model event
+ */
+ public void columnAdded(TableColumnModelEvent event)
+ {
+ // TODO: What else to do here (if anything)?
+ resizeAndRepaint();
+ }
+
+ /**
+ * Receives notification when a column margin changes in the column model.
+ *
+ * @param event the table column model event
+ */
+ public void columnMarginChanged(ChangeEvent event)
+ {
+ // TODO: What else to do here (if anything)?
+ resizeAndRepaint();
+ }
+
+ /**
+ * Receives notification when a column is moved within the column model.
+ *
+ * @param event the table column model event
+ */
+ public void columnMoved(TableColumnModelEvent event)
+ {
+ // TODO: What else to do here (if anything)?
+ resizeAndRepaint();
+ }
+
+ /**
+ * Receives notification when a column is removed from the column model.
+ *
+ * @param event the table column model event
+ */
+ public void columnRemoved(TableColumnModelEvent event)
+ {
+ // TODO: What else to do here (if anything)?
+ resizeAndRepaint();
+ }
+
+ /**
+ * Receives notification when the column selection has changed.
+ *
+ * @param event the table column model event
+ */
+ public void columnSelectionChanged(ListSelectionEvent event)
+ {
+ // TODO: What else to do here (if anything)?
+ resizeAndRepaint();
+ }
+
+ /**
+ * Validates the layout of this table header and repaints it. This is
+ * equivalent to <code>revalidate()</code> followed by
+ * <code>repaint()</code>.
+ */
+ public void resizeAndRepaint()
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Initializes the fields and properties of this class with default values.
+ * This is called by the constructors.
+ */
+ protected void initializeLocalVars()
+ {
+ accessibleContext = new AccessibleJTableHeader();
+ draggedColumn = null;
+ draggedDistance = 0;
+ opaque = true;
+ reorderingAllowed = true;
+ resizingAllowed = true;
+ resizingColumn = null;
+ table = null;
+ updateTableInRealTime = true;
+ cellRenderer = createDefaultRenderer();
+ }
+}
diff --git a/libjava/classpath/javax/swing/table/TableCellEditor.java b/libjava/classpath/javax/swing/table/TableCellEditor.java
new file mode 100644
index 000000000..933eb9ed7
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/TableCellEditor.java
@@ -0,0 +1,65 @@
+/* TableCellEditor.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import java.awt.Component;
+
+import javax.swing.CellEditor;
+import javax.swing.JTable;
+
+/**
+ * TableCellEditor public interface
+ * @author Andrew Selkirk
+ */
+public interface TableCellEditor extends CellEditor
+{
+
+ /**
+ * Get table cell editor component
+ * @param table JTable
+ * @param value Value of cell
+ * @param isSelected Cell selected
+ * @param row Row of cell
+ * @param column Column of cell
+ * @return Component
+ */
+ Component getTableCellEditorComponent(JTable table, Object value,
+ boolean isSelected, int row, int column);
+
+}
diff --git a/libjava/classpath/javax/swing/table/TableCellRenderer.java b/libjava/classpath/javax/swing/table/TableCellRenderer.java
new file mode 100644
index 000000000..da7296de8
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/TableCellRenderer.java
@@ -0,0 +1,66 @@
+/* TableCellRenderer.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import java.awt.Component;
+
+import javax.swing.JTable;
+
+/**
+ * TableCellRenderer public interface
+ * @author Andrew Selkirk
+ */
+public interface TableCellRenderer
+{
+
+ /**
+ * Get table cell renderer component
+ * @param table JTable
+ * @param value Value of cell
+ * @param isSelected Cell selected
+ * @param hasFocus Cell has focus
+ * @param row Row of cell
+ * @param column Column of cell
+ * @return Component
+ */
+ Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus, int row, int column);
+
+
+}
diff --git a/libjava/classpath/javax/swing/table/TableColumn.java b/libjava/classpath/javax/swing/table/TableColumn.java
new file mode 100644
index 000000000..8db4bf6d0
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/TableColumn.java
@@ -0,0 +1,693 @@
+/* TableColumn.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+
+import javax.swing.event.SwingPropertyChangeSupport;
+
+/**
+ * Represents the attributes of a column in a table, including the column index,
+ * width, minimum width, preferred width and maximum width.
+ *
+ * @author Andrew Selkirk
+ */
+public class TableColumn
+ implements Serializable
+{
+ static final long serialVersionUID = -6113660025878112608L;
+
+ /**
+ * The name for the <code>columnWidth</code> property (this field is
+ * obsolete and no longer used). Note also that the typo in the value
+ * string is deliberate, to match the specification.
+ */
+ public static final String COLUMN_WIDTH_PROPERTY = "columWidth";
+
+ /**
+ * The name for the <code>headerValue</code> property.
+ */
+ public static final String HEADER_VALUE_PROPERTY = "headerValue";
+
+ /**
+ * The name for the <code>headerRenderer</code> property.
+ */
+ public static final String HEADER_RENDERER_PROPERTY = "headerRenderer";
+
+ /**
+ * The name for the <code>cellRenderer</code> property.
+ */
+ public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
+
+ /**
+ * The index of the corresponding column in the table model.
+ */
+ protected int modelIndex;
+
+ /**
+ * The identifier for the column.
+ */
+ protected Object identifier;
+
+ /**
+ * The current width for the column.
+ */
+ protected int width;
+
+ /**
+ * The minimum width for the column.
+ */
+ protected int minWidth = 15;
+
+ /**
+ * The preferred width for the column.
+ */
+ private int preferredWidth;
+
+ /**
+ * The maximum width for the column.
+ */
+ protected int maxWidth = Integer.MAX_VALUE;
+
+ /**
+ * The renderer for the column header.
+ */
+ protected TableCellRenderer headerRenderer;
+
+ /**
+ * The value for the column header.
+ */
+ protected Object headerValue;
+
+ /**
+ * The renderer for the regular cells in this column.
+ */
+ protected TableCellRenderer cellRenderer;
+
+ /**
+ * An editor for the regular cells in this column.
+ */
+ protected TableCellEditor cellEditor;
+
+ /**
+ * A flag that determines whether or not the column is resizable (the default
+ * is <code>true</code>).
+ */
+ protected boolean isResizable = true;
+
+ /**
+ * resizedPostingDisableCount
+ *
+ * @deprecated 1.3
+ */
+ protected transient int resizedPostingDisableCount;
+
+ /**
+ * A storage and notification mechanism for property change listeners.
+ */
+ private SwingPropertyChangeSupport changeSupport =
+ new SwingPropertyChangeSupport(this);
+
+ /**
+ * Creates a new <code>TableColumn</code> that maps to column 0 in the
+ * related table model. The default width is <code>75</code> units.
+ */
+ public TableColumn()
+ {
+ this(0, 75, null, null);
+ }
+
+ /**
+ * Creates a new <code>TableColumn</code> that maps to the specified column
+ * in the related table model. The default width is <code>75</code> units.
+ *
+ * @param modelIndex the index of the column in the model
+ */
+ public TableColumn(int modelIndex)
+ {
+ this(modelIndex, 75, null, null);
+ }
+
+ /**
+ * Creates a new <code>TableColumn</code> that maps to the specified column
+ * in the related table model, and has the specified <code>width</code>.
+ *
+ * @param modelIndex the index of the column in the model
+ * @param width the width
+ */
+ public TableColumn(int modelIndex, int width)
+ {
+ this(modelIndex, width, null, null);
+ }
+
+ /**
+ * Creates a new <code>TableColumn</code> that maps to the specified column
+ * in the related table model, and has the specified <code>width</code>,
+ * <code>cellRenderer</code> and <code>cellEditor</code>.
+ *
+ * @param modelIndex the index of the column in the model
+ * @param width the width
+ * @param cellRenderer the cell renderer (<code>null</code> permitted).
+ * @param cellEditor the cell editor (<code>null</code> permitted).
+ */
+ public TableColumn(int modelIndex, int width,
+ TableCellRenderer cellRenderer, TableCellEditor cellEditor)
+ {
+ this.modelIndex = modelIndex;
+ this.width = width;
+ this.preferredWidth = width;
+ this.cellRenderer = cellRenderer;
+ this.cellEditor = cellEditor;
+ this.headerValue = null;
+ this.identifier = null;
+ }
+
+ /**
+ * Sets the index of the column in the related {@link TableModel} that this
+ * <code>TableColumn</code> maps to, and sends a {@link PropertyChangeEvent}
+ * (with the property name 'modelIndex') to all registered listeners.
+ *
+ * @param modelIndex the column index in the model.
+ *
+ * @see #getModelIndex()
+ */
+ public void setModelIndex(int modelIndex)
+ {
+ if (this.modelIndex != modelIndex)
+ {
+ int oldValue = this.modelIndex;
+ this.modelIndex = modelIndex;
+ changeSupport.firePropertyChange("modelIndex", oldValue, modelIndex);
+ }
+ }
+
+ /**
+ * Returns the index of the column in the related {@link TableModel} that
+ * this <code>TableColumn</code> maps to.
+ *
+ * @return the model index.
+ *
+ * @see #setModelIndex(int)
+ */
+ public int getModelIndex()
+ {
+ return modelIndex;
+ }
+
+ /**
+ * Sets the identifier for the column and sends a {@link PropertyChangeEvent}
+ * (with the property name 'identifier') to all registered listeners.
+ *
+ * @param identifier the identifier (<code>null</code> permitted).
+ *
+ * @see #getIdentifier()
+ */
+ public void setIdentifier(Object identifier)
+ {
+ if (this.identifier != identifier)
+ {
+ Object oldValue = this.identifier;
+ this.identifier = identifier;
+ changeSupport.firePropertyChange("identifier", oldValue, identifier);
+ }
+ }
+
+ /**
+ * Returns the identifier for the column, or {@link #getHeaderValue()} if the
+ * identifier is <code>null</code>.
+ *
+ * @return The identifier (or {@link #getHeaderValue()} if the identifier is
+ * <code>null</code>).
+ */
+ public Object getIdentifier()
+ {
+ if (identifier == null)
+ return getHeaderValue();
+ return identifier;
+ }
+
+ /**
+ * Sets the header value and sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #HEADER_VALUE_PROPERTY}) to all registered listeners.
+ *
+ * @param headerValue the value of the header (<code>null</code> permitted).
+ *
+ * @see #getHeaderValue()
+ */
+ public void setHeaderValue(Object headerValue)
+ {
+ if (this.headerValue == headerValue)
+ return;
+
+ Object oldValue = this.headerValue;
+ this.headerValue = headerValue;
+ changeSupport.firePropertyChange(HEADER_VALUE_PROPERTY, oldValue,
+ headerValue);
+ }
+
+ /**
+ * Returns the header value.
+ *
+ * @return the value of the header.
+ *
+ * @see #getHeaderValue()
+ */
+ public Object getHeaderValue()
+ {
+ return headerValue;
+ }
+
+ /**
+ * Sets the renderer for the column header and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #HEADER_RENDERER_PROPERTY}) to all registered listeners.
+ *
+ * @param renderer the header renderer (<code>null</code> permitted).
+ *
+ * @see #getHeaderRenderer()
+ */
+ public void setHeaderRenderer(TableCellRenderer renderer)
+ {
+ if (headerRenderer == renderer)
+ return;
+
+ TableCellRenderer oldRenderer = headerRenderer;
+ headerRenderer = renderer;
+ changeSupport.firePropertyChange(HEADER_RENDERER_PROPERTY, oldRenderer,
+ headerRenderer);
+ }
+
+ /**
+ * Returns the renderer for the column header.
+ *
+ * @return The renderer for the column header (possibly <code>null</code>).
+ *
+ * @see #setHeaderRenderer(TableCellRenderer)
+ */
+ public TableCellRenderer getHeaderRenderer()
+ {
+ return headerRenderer;
+ }
+
+ /**
+ * Sets the renderer for cells in this column and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #CELL_RENDERER_PROPERTY}) to all registered listeners.
+ *
+ * @param renderer the cell renderer (<code>null</code> permitted).
+ *
+ * @see #getCellRenderer()
+ */
+ public void setCellRenderer(TableCellRenderer renderer)
+ {
+ if (cellRenderer == renderer)
+ return;
+
+ TableCellRenderer oldRenderer = cellRenderer;
+ cellRenderer = renderer;
+ changeSupport.firePropertyChange(CELL_RENDERER_PROPERTY, oldRenderer,
+ cellRenderer);
+ }
+
+ /**
+ * Returns the renderer for the table cells in this column.
+ *
+ * @return The cell renderer (possibly <code>null</code>).
+ *
+ * @see #setCellRenderer(TableCellRenderer)
+ */
+ public TableCellRenderer getCellRenderer()
+ {
+ return cellRenderer;
+ }
+
+ /**
+ * Sets the cell editor for the column and sends a {@link PropertyChangeEvent}
+ * (with the property name 'cellEditor') to all registered listeners.
+ *
+ * @param cellEditor the cell editor (<code>null</code> permitted).
+ *
+ * @see #getCellEditor()
+ */
+ public void setCellEditor(TableCellEditor cellEditor)
+ {
+ if (this.cellEditor != cellEditor)
+ {
+ TableCellEditor oldValue = this.cellEditor;
+ this.cellEditor = cellEditor;
+ changeSupport.firePropertyChange("cellEditor", oldValue, cellEditor);
+ }
+ }
+
+ /**
+ * Returns the cell editor for the column (the default value is
+ * <code>null</code>).
+ *
+ * @return The cell editor (possibly <code>null</code>).
+ *
+ * @see #setCellEditor(TableCellEditor)
+ */
+ public TableCellEditor getCellEditor()
+ {
+ return cellEditor;
+ }
+
+ /**
+ * Sets the width for the column and sends a {@link PropertyChangeEvent}
+ * (with the property name 'width') to all registered listeners. If the new
+ * width falls outside the range getMinWidth() to getMaxWidth() it is
+ * adjusted to the appropriate boundary value.
+ *
+ * @param newWidth the width.
+ *
+ * @see #getWidth()
+ */
+ public void setWidth(int newWidth)
+ {
+ int oldWidth = width;
+
+ if (newWidth < minWidth)
+ width = minWidth;
+ else if (newWidth > maxWidth)
+ width = maxWidth;
+ else
+ width = newWidth;
+
+ if (width == oldWidth)
+ return;
+
+ // We do have a constant field COLUMN_WIDTH_PROPERTY,
+ // however, tests show that the actual fired property name is 'width'
+ // and even Sun's API docs say that this constant field is obsolete and
+ // not used.
+ changeSupport.firePropertyChange("width", oldWidth, width);
+ }
+
+ /**
+ * Returns the width for the column (the default value is <code>75</code>).
+ *
+ * @return The width.
+ *
+ * @see #setWidth(int)
+ */
+ public int getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * Sets the preferred width for the column and sends a
+ * {@link PropertyChangeEvent} (with the property name 'preferredWidth') to
+ * all registered listeners. If necessary, the supplied value will be
+ * adjusted to fit in the range {@link #getMinWidth()} to
+ * {@link #getMaxWidth()}.
+ *
+ * @param preferredWidth the preferred width.
+ *
+ * @see #getPreferredWidth()
+ */
+ public void setPreferredWidth(int preferredWidth)
+ {
+ int oldPrefWidth = this.preferredWidth;
+
+ if (preferredWidth < minWidth)
+ this.preferredWidth = minWidth;
+ else if (preferredWidth > maxWidth)
+ this.preferredWidth = maxWidth;
+ else
+ this.preferredWidth = preferredWidth;
+
+ changeSupport.firePropertyChange("preferredWidth", oldPrefWidth,
+ this.preferredWidth);
+ }
+
+ /**
+ * Returns the preferred width for the column (the default value is
+ * <code>75</code>).
+ *
+ * @return The preferred width.
+ *
+ * @see #setPreferredWidth(int)
+ */
+ public int getPreferredWidth()
+ {
+ return preferredWidth;
+ }
+
+ /**
+ * Sets the minimum width for the column and sends a
+ * {@link PropertyChangeEvent} (with the property name 'minWidth') to all
+ * registered listeners. If the current <code>width</code> and/or
+ * <code>preferredWidth</code> are less than the new minimum width, they are
+ * adjusted accordingly.
+ *
+ * @param minWidth the minimum width (negative values are treated as 0).
+ *
+ * @see #getMinWidth()
+ */
+ public void setMinWidth(int minWidth)
+ {
+ if (minWidth < 0)
+ minWidth = 0;
+ if (this.minWidth != minWidth)
+ {
+ if (width < minWidth)
+ setWidth(minWidth);
+ if (preferredWidth < minWidth)
+ setPreferredWidth(minWidth);
+ int oldValue = this.minWidth;
+ this.minWidth = minWidth;
+ changeSupport.firePropertyChange("minWidth", oldValue, minWidth);
+ }
+ }
+
+ /**
+ * Returns the <code>TableColumn</code>'s minimum width (the default value
+ * is <code>15</code>).
+ *
+ * @return The minimum width.
+ *
+ * @see #setMinWidth(int)
+ */
+ public int getMinWidth()
+ {
+ return minWidth;
+ }
+
+ /**
+ * Sets the maximum width for the column and sends a
+ * {@link PropertyChangeEvent} (with the property name 'maxWidth') to all
+ * registered listeners. If the current <code>width</code> and/or
+ * <code>preferredWidth</code> are greater than the new maximum width, they
+ * are adjusted accordingly.
+ *
+ * @param maxWidth the maximum width.
+ *
+ * @see #getMaxWidth()
+ */
+ public void setMaxWidth(int maxWidth)
+ {
+ if (this.maxWidth != maxWidth)
+ {
+ if (width > maxWidth)
+ setWidth(maxWidth);
+ if (preferredWidth > maxWidth)
+ setPreferredWidth(maxWidth);
+ int oldValue = this.maxWidth;
+ this.maxWidth = maxWidth;
+ changeSupport.firePropertyChange("maxWidth", oldValue, maxWidth);
+ }
+ }
+
+ /**
+ * Returns the maximum width for the column (the default value is
+ * {@link Integer#MAX_VALUE}).
+ *
+ * @return The maximum width for the column.
+ *
+ * @see #setMaxWidth(int)
+ */
+ public int getMaxWidth()
+ {
+ return maxWidth;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the column is resizable, and
+ * sends a {@link PropertyChangeEvent} (with the property name 'isResizable')
+ * to all registered listeners.
+ *
+ * @param isResizable <code>true</code> if this column is resizable,
+ * <code>false</code> otherwise.
+ *
+ * @see #getResizable()
+ */
+ public void setResizable(boolean isResizable)
+ {
+ if (this.isResizable != isResizable)
+ {
+ this.isResizable = isResizable;
+ changeSupport.firePropertyChange("isResizable", !this.isResizable,
+ isResizable);
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not the column is resizable.
+ *
+ * @return <code>true</code> if this column is resizable,
+ * <code>false</code> otherwise.
+ *
+ * @see #setResizable(boolean)
+ */
+ public boolean getResizable()
+ {
+ return isResizable;
+ }
+
+ /**
+ * Sets the minimum, maximum, preferred and current width to match the
+ * minimum, maximum and preferred width of the header renderer component.
+ * If there is no header renderer component, this method does nothing.
+ */
+ public void sizeWidthToFit()
+ {
+ if (headerRenderer == null)
+ return;
+ Component c = headerRenderer.getTableCellRendererComponent(null,
+ getHeaderValue(), false, false, 0, 0);
+ Dimension min = c.getMinimumSize();
+ Dimension max = c.getMaximumSize();
+ Dimension pref = c.getPreferredSize();
+ setMinWidth(min.width);
+ setMaxWidth(max.width);
+ setPreferredWidth(pref.width);
+ setWidth(pref.width);
+ }
+
+ /**
+ * This method is empty, unused and deprecated.
+ * @deprecated 1.3
+ */
+ public void disableResizedPosting()
+ {
+ // Does nothing
+ }
+
+ /**
+ * This method is empty, unused and deprecated.
+ * @deprecated 1.3
+ */
+ public void enableResizedPosting()
+ {
+ // Does nothing
+ }
+
+ /**
+ * Adds a listener so that it receives {@link PropertyChangeEvent}
+ * notifications from this column. The properties defined by the column are:
+ * <ul>
+ * <li><code>width</code> - see {@link #setWidth(int)};</li>
+ * <li><code>preferredWidth</code> - see {@link #setPreferredWidth(int)};</li>
+ * <li><code>minWidth</code> - see {@link #setMinWidth(int)};</li>
+ * <li><code>maxWidth</code> - see {@link #setMaxWidth(int)};</li>
+ * <li><code>modelIndex</code> - see {@link #setModelIndex(int)};</li>
+ * <li><code>isResizable</code> - see {@link #setResizable(boolean)};</li>
+ * <li><code>cellRenderer</code> - see
+ * {@link #setCellRenderer(TableCellRenderer)};</li>
+ * <li><code>cellEditor</code> - see
+ * {@link #setCellEditor(TableCellEditor)};</li>
+ * <li><code>headerRenderer</code> - see
+ * {@link #setHeaderRenderer(TableCellRenderer)};</li>
+ * <li><code>headerValue</code> - see {@link #setHeaderValue(Object)};</li>
+ * <li><code>identifier</code> - see {@link #setIdentifier(Object)}.</li>
+ * </ul>
+ *
+ * @param listener the listener to add (<code>null</code> is ignored).
+ *
+ * @see #removePropertyChangeListener(PropertyChangeListener)
+ */
+ public synchronized void addPropertyChangeListener(
+ PropertyChangeListener listener)
+ {
+ changeSupport.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Removes a listener so that it no longer receives
+ * {@link PropertyChangeEvent} notifications from this column. If
+ * <code>listener</code> is not registered with the column, or is
+ * <code>null</code>, this method does nothing.
+ *
+ * @param listener the listener to remove (<code>null</code> is ignored).
+ */
+ public synchronized void removePropertyChangeListener(
+ PropertyChangeListener listener)
+ {
+ changeSupport.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Returns the property change listeners for this <code>TableColumn</code>.
+ * An empty array is returned if there are currently no listeners registered.
+ *
+ * @return The property change listeners registered with this column.
+ *
+ * @since 1.4
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners()
+ {
+ return changeSupport.getPropertyChangeListeners();
+ }
+
+ /**
+ * Creates and returns a default renderer for the column header (in this case,
+ * a new instance of {@link DefaultTableCellRenderer}).
+ *
+ * @return A default renderer for the column header.
+ */
+ protected TableCellRenderer createDefaultHeaderRenderer()
+ {
+ return new DefaultTableCellRenderer();
+ }
+}
diff --git a/libjava/classpath/javax/swing/table/TableColumnModel.java b/libjava/classpath/javax/swing/table/TableColumnModel.java
new file mode 100644
index 000000000..9a95f92cc
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/TableColumnModel.java
@@ -0,0 +1,232 @@
+/* TableColumnModel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import java.util.Enumeration;
+
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.TableColumnModelEvent;
+import javax.swing.event.TableColumnModelListener;
+
+/**
+ * The interface used by {@link JTable} to access the columns in the table
+ * view.
+ *
+ * @author Andrew Selkirk
+ */
+public interface TableColumnModel
+{
+ /**
+ * Adds a column to the model.
+ *
+ * @param column the new column (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>column</code> is
+ * <code>null</code>.
+ */
+ void addColumn(TableColumn column);
+
+ /**
+ * Removes a column from the model. If <code>column</code> is not defined
+ * in the model, this method does nothing.
+ *
+ * @param column TableColumn
+ */
+ void removeColumn(TableColumn column);
+
+ /**
+ * Moves a column.
+ *
+ * @param columnIndex Index of column to move
+ * @param newIndex New index of column
+ */
+ void moveColumn(int columnIndex, int newIndex);
+
+ /**
+ * Sets the column margin and sends a {@link ChangeEvent} to all registered
+ * {@link TableColumnModelListener}s registered with the model.
+ *
+ * @param margin the column margin.
+ *
+ * @see #getColumnMargin()
+ */
+ void setColumnMargin(int margin);
+
+ /**
+ * Returns the number of columns in the model.
+ *
+ * @return The column count.
+ */
+ int getColumnCount();
+
+ /**
+ * Returns an enumeration of the columns in the model.
+ *
+ * @return An enumeration of the columns in the model.
+ */
+ Enumeration<TableColumn> getColumns();
+
+ /**
+ * Returns the index of the {@link TableColumn} with the given identifier.
+ *
+ * @param identifier the identifier (<code>null</code> not permitted).
+ *
+ * @return The index of the {@link TableColumn} with the given identifier.
+ *
+ * @throws IllegalArgumentException if <code>identifier</code> is
+ * <code>null</code> or there is no column with that identifier.
+ */
+ int getColumnIndex(Object identifier);
+
+ /**
+ * Returns the <code>TableColumn</code> at the specified index.
+ *
+ * @param columnIndex the column index.
+ *
+ * @return The table column.
+ */
+ TableColumn getColumn(int columnIndex);
+
+ /**
+ * Returns the column margin.
+ *
+ * @return The column margin.
+ *
+ * @see #setColumnMargin(int)
+ */
+ int getColumnMargin();
+
+ /**
+ * Returns the index of the column that contains the specified x-coordinate,
+ * assuming that:
+ * <ul>
+ * <li>column zero begins at position zero;</li>
+ * <li>all columns appear in order;</li>
+ * <li>individual column widths are taken into account, but the column margin
+ * is ignored.</li>
+ * </ul>
+ * If no column contains the specified position, this method returns
+ * <code>-1</code>.
+ *
+ * @param xPosition the x-position.
+ *
+ * @return The column index, or <code>-1</code>.
+ */
+ int getColumnIndexAtX(int xPosition);
+
+ /**
+ * Returns total width of all the columns in the model, ignoring the
+ * column margin (see {@link #getColumnMargin()}).
+ *
+ * @return The total width of all the columns.
+ */
+ int getTotalColumnWidth();
+
+ /**
+ * Sets the flag that indicates whether or not column selection is allowed.
+ *
+ * @param allowed the new flag value.
+ *
+ * @see #getColumnSelectionAllowed()
+ */
+ void setColumnSelectionAllowed(boolean allowed);
+
+ /**
+ * Returns <code>true</code> if column selection is allowed, and
+ * <code>false</code> if column selection is not allowed.
+ *
+ * @return A boolean.
+ *
+ * @see #setColumnSelectionAllowed(boolean)
+ */
+ boolean getColumnSelectionAllowed();
+
+ /**
+ * getSelectedColumns
+ * @return Selected columns
+ */
+ int[] getSelectedColumns();
+
+ /**
+ * Returns the number of selected columns in the model.
+ *
+ * @return The selected column count.
+ *
+ * @see #getSelectionModel()
+ */
+ int getSelectedColumnCount();
+
+ /**
+ * Sets the selection model that will be used to keep track of the selected
+ * columns.
+ *
+ * @param model the selection model (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>model</code> is
+ * <code>null</code>.
+ */
+ void setSelectionModel(ListSelectionModel model);
+
+ /**
+ * Returns the selection model used to track table column selections.
+ *
+ * @return The selection model.
+ *
+ * @see #setSelectionModel(ListSelectionModel)
+ */
+ ListSelectionModel getSelectionModel();
+
+ /**
+ * Registers a listener with the model, so that it will receive
+ * {@link TableColumnModelEvent} notifications.
+ *
+ * @param listener the listener (<code>null</code> ignored).
+ */
+ void addColumnModelListener(TableColumnModelListener listener);
+
+ /**
+ * Deregisters a listener, so that it will no longer receive
+ * {@link TableColumnModelEvent} notifications.
+ *
+ * @param listener the listener.
+ */
+ void removeColumnModelListener(TableColumnModelListener listener);
+}
diff --git a/libjava/classpath/javax/swing/table/TableModel.java b/libjava/classpath/javax/swing/table/TableModel.java
new file mode 100644
index 000000000..d8fb7131f
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/TableModel.java
@@ -0,0 +1,134 @@
+/* TableModel.java --
+ Copyright (C) 2002, 2005, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.table;
+
+import javax.swing.event.TableModelListener;
+
+/**
+ * A <code>TableModel</code> is a two dimensional data structure that
+ * can store arbitrary <code>Object</code> instances, usually for the
+ * purpose of display in a {@link javax.swing.JTable} component. Individual
+ * objects can be accessed by specifying the row index and column index for
+ * the object. Each column in the model has a name associated with it.
+ * <p>
+ * The {@link DefaultTableModel} class provides one implementation of
+ * this interface.
+ *
+ * @author Andrew Selkirk
+ */
+public interface TableModel
+{
+ /**
+ * Returns the number of rows in the model.
+ *
+ * @return The row count.
+ */
+ int getRowCount();
+
+ /**
+ * Returns the number of columns in the model.
+ *
+ * @return The column count
+ */
+ int getColumnCount();
+
+ /**
+ * Returns the name of a column in the model.
+ *
+ * @param columnIndex the column index.
+ *
+ * @return The column name.
+ */
+ String getColumnName(int columnIndex);
+
+ /**
+ * Returns the <code>Class</code> for all <code>Object</code> instances
+ * in the specified column.
+ *
+ * @param columnIndex the column index.
+ *
+ * @return The class.
+ */
+ Class<?> getColumnClass(int columnIndex);
+
+ /**
+ * Returns <code>true</code> if the cell is editable, and <code>false</code>
+ * otherwise.
+ *
+ * @param rowIndex the row index.
+ * @param columnIndex the column index.
+ *
+ * @return <code>true</code> if editable, <code>false</code> otherwise.
+ */
+ boolean isCellEditable(int rowIndex, int columnIndex);
+
+ /**
+ * Returns the value (<code>Object</code>) at a particular cell in the
+ * table.
+ *
+ * @param rowIndex the row index.
+ * @param columnIndex the column index.
+ *
+ * @return The value at the specified cell.
+ */
+ Object getValueAt(int rowIndex, int columnIndex);
+
+ /**
+ * Sets the value at a particular cell in the table.
+ *
+ * @param aValue the value (<code>null</code> permitted).
+ * @param rowIndex the row index.
+ * @param columnIndex the column index.
+ */
+ void setValueAt(Object aValue, int rowIndex, int columnIndex);
+
+ /**
+ * Adds a listener to the model. The listener will receive notification
+ * of updates to the model.
+ *
+ * @param listener the listener.
+ */
+ void addTableModelListener(TableModelListener listener);
+
+ /**
+ * Removes a listener from the model.
+ *
+ * @param listener the listener.
+ */
+ void removeTableModelListener(TableModelListener listener);
+}
diff --git a/libjava/classpath/javax/swing/table/package.html b/libjava/classpath/javax/swing/table/package.html
new file mode 100644
index 000000000..84e6f1aa3
--- /dev/null
+++ b/libjava/classpath/javax/swing/table/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.table package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.table</title></head>
+
+<body>
+<p>Interfaces and classes that support the {@link javax.swing.JTable}
+component.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java
new file mode 100644
index 000000000..25915bb5a
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/AbstractDocument.java
@@ -0,0 +1,2906 @@
+/* AbstractDocument.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.font.TextAttribute;
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.text.Bidi;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.EventListenerList;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.event.UndoableEditListener;
+import javax.swing.text.DocumentFilter;
+import javax.swing.tree.TreeNode;
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.CompoundEdit;
+import javax.swing.undo.UndoableEdit;
+
+/**
+ * An abstract base implementation for the {@link Document} interface.
+ * This class provides some common functionality for all <code>Element</code>s,
+ * most notably it implements a locking mechanism to make document modification
+ * thread-safe.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public abstract class AbstractDocument implements Document, Serializable
+{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 6842927725919637215L;
+
+ /**
+ * Standard error message to indicate a bad location.
+ */
+ protected static final String BAD_LOCATION = "document location failure";
+
+ /**
+ * Standard name for unidirectional <code>Element</code>s.
+ */
+ public static final String BidiElementName = "bidi level";
+
+ /**
+ * Standard name for content <code>Element</code>s. These are usually
+ * {@link LeafElement}s.
+ */
+ public static final String ContentElementName = "content";
+
+ /**
+ * Standard name for paragraph <code>Element</code>s. These are usually
+ * {@link BranchElement}s.
+ */
+ public static final String ParagraphElementName = "paragraph";
+
+ /**
+ * Standard name for section <code>Element</code>s. These are usually
+ * {@link DefaultStyledDocument.SectionElement}s.
+ */
+ public static final String SectionElementName = "section";
+
+ /**
+ * Attribute key for storing the element name.
+ */
+ public static final String ElementNameAttribute = "$ename";
+
+ /**
+ * Standard name for the bidi root element.
+ */
+ private static final String BidiRootName = "bidi root";
+
+ /**
+ * Key for storing the asynchronous load priority.
+ */
+ private static final String AsyncLoadPriority = "load priority";
+
+ /**
+ * Key for storing the I18N state.
+ */
+ private static final String I18N = "i18n";
+
+ /**
+ * The actual content model of this <code>Document</code>.
+ */
+ Content content;
+
+ /**
+ * The AttributeContext for this <code>Document</code>.
+ */
+ AttributeContext context;
+
+ /**
+ * The currently installed <code>DocumentFilter</code>.
+ */
+ DocumentFilter documentFilter;
+
+ /**
+ * The documents properties.
+ */
+ Dictionary properties;
+
+ /**
+ * Manages event listeners for this <code>Document</code>.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Stores the current writer thread. Used for locking.
+ */
+ private Thread currentWriter = null;
+
+ /**
+ * The number of readers. Used for locking.
+ */
+ private int numReaders = 0;
+
+ /**
+ * The number of current writers. If this is > 1 then the same thread entered
+ * the write lock more than once.
+ */
+ private int numWriters = 0;
+
+ /** An instance of a DocumentFilter.FilterBypass which allows calling
+ * the insert, remove and replace method without checking for an installed
+ * document filter.
+ */
+ private DocumentFilter.FilterBypass bypass;
+
+ /**
+ * The bidi root element.
+ */
+ private BidiRootElement bidiRoot;
+
+ /**
+ * True when we are currently notifying any listeners. This is used
+ * to detect illegal situations in writeLock().
+ */
+ private transient boolean notifyListeners;
+
+ /**
+ * Creates a new <code>AbstractDocument</code> with the specified
+ * {@link Content} model.
+ *
+ * @param doc the <code>Content</code> model to be used in this
+ * <code>Document<code>
+ *
+ * @see GapContent
+ * @see StringContent
+ */
+ protected AbstractDocument(Content doc)
+ {
+ this(doc, StyleContext.getDefaultStyleContext());
+ }
+
+ /**
+ * Creates a new <code>AbstractDocument</code> with the specified
+ * {@link Content} model and {@link AttributeContext}.
+ *
+ * @param doc the <code>Content</code> model to be used in this
+ * <code>Document<code>
+ * @param ctx the <code>AttributeContext</code> to use
+ *
+ * @see GapContent
+ * @see StringContent
+ */
+ protected AbstractDocument(Content doc, AttributeContext ctx)
+ {
+ content = doc;
+ context = ctx;
+
+ // FIXME: Fully implement bidi.
+ bidiRoot = new BidiRootElement();
+
+ // FIXME: This is determined using a Mauve test. Make the document
+ // actually use this.
+ putProperty(I18N, Boolean.FALSE);
+
+ // Add one child to the bidi root.
+ writeLock();
+ try
+ {
+ Element[] children = new Element[1];
+ children[0] = new BidiElement(bidiRoot, 0, 1, 0);
+ bidiRoot.replace(0, 0, children);
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ /** Returns the DocumentFilter.FilterBypass instance for this
+ * document and create it if it does not exist yet.
+ *
+ * @return This document's DocumentFilter.FilterBypass instance.
+ */
+ private DocumentFilter.FilterBypass getBypass()
+ {
+ if (bypass == null)
+ bypass = new Bypass();
+
+ return bypass;
+ }
+
+ /**
+ * Returns the paragraph {@link Element} that holds the specified position.
+ *
+ * @param pos the position for which to get the paragraph element
+ *
+ * @return the paragraph {@link Element} that holds the specified position
+ */
+ public abstract Element getParagraphElement(int pos);
+
+ /**
+ * Returns the default root {@link Element} of this <code>Document</code>.
+ * Usual <code>Document</code>s only have one root element and return this.
+ * However, there may be <code>Document</code> implementations that
+ * support multiple root elements, they have to return a default root element
+ * here.
+ *
+ * @return the default root {@link Element} of this <code>Document</code>
+ */
+ public abstract Element getDefaultRootElement();
+
+ /**
+ * Creates and returns a branch element with the specified
+ * <code>parent</code> and <code>attributes</code>. Note that the new
+ * <code>Element</code> is linked to the parent <code>Element</code>
+ * through {@link Element#getParentElement}, but it is not yet added
+ * to the parent <code>Element</code> as child.
+ *
+ * @param parent the parent <code>Element</code> for the new branch element
+ * @param attributes the text attributes to be installed in the new element
+ *
+ * @return the new branch <code>Element</code>
+ *
+ * @see BranchElement
+ */
+ protected Element createBranchElement(Element parent,
+ AttributeSet attributes)
+ {
+ return new BranchElement(parent, attributes);
+ }
+
+ /**
+ * Creates and returns a leaf element with the specified
+ * <code>parent</code> and <code>attributes</code>. Note that the new
+ * <code>Element</code> is linked to the parent <code>Element</code>
+ * through {@link Element#getParentElement}, but it is not yet added
+ * to the parent <code>Element</code> as child.
+ *
+ * @param parent the parent <code>Element</code> for the new branch element
+ * @param attributes the text attributes to be installed in the new element
+ *
+ * @return the new branch <code>Element</code>
+ *
+ * @see LeafElement
+ */
+ protected Element createLeafElement(Element parent, AttributeSet attributes,
+ int start, int end)
+ {
+ return new LeafElement(parent, attributes, start, end);
+ }
+
+ /**
+ * Creates a {@link Position} that keeps track of the location at the
+ * specified <code>offset</code>.
+ *
+ * @param offset the location in the document to keep track by the new
+ * <code>Position</code>
+ *
+ * @return the newly created <code>Position</code>
+ *
+ * @throws BadLocationException if <code>offset</code> is not a valid
+ * location in the documents content model
+ */
+ public synchronized Position createPosition(final int offset)
+ throws BadLocationException
+ {
+ return content.createPosition(offset);
+ }
+
+ /**
+ * Notifies all registered listeners when the document model changes.
+ *
+ * @param event the <code>DocumentEvent</code> to be fired
+ */
+ protected void fireChangedUpdate(DocumentEvent event)
+ {
+ notifyListeners = true;
+ try
+ {
+ DocumentListener[] listeners = getDocumentListeners();
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].changedUpdate(event);
+ }
+ finally
+ {
+ notifyListeners = false;
+ }
+ }
+
+ /**
+ * Notifies all registered listeners when content is inserted in the document
+ * model.
+ *
+ * @param event the <code>DocumentEvent</code> to be fired
+ */
+ protected void fireInsertUpdate(DocumentEvent event)
+ {
+ notifyListeners = true;
+ try
+ {
+ DocumentListener[] listeners = getDocumentListeners();
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].insertUpdate(event);
+ }
+ finally
+ {
+ notifyListeners = false;
+ }
+ }
+
+ /**
+ * Notifies all registered listeners when content is removed from the
+ * document model.
+ *
+ * @param event the <code>DocumentEvent</code> to be fired
+ */
+ protected void fireRemoveUpdate(DocumentEvent event)
+ {
+ notifyListeners = true;
+ try
+ {
+ DocumentListener[] listeners = getDocumentListeners();
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].removeUpdate(event);
+ }
+ finally
+ {
+ notifyListeners = false;
+ }
+ }
+
+ /**
+ * Notifies all registered listeners when an <code>UndoableEdit</code> has
+ * been performed on this <code>Document</code>.
+ *
+ * @param event the <code>UndoableEditEvent</code> to be fired
+ */
+ protected void fireUndoableEditUpdate(UndoableEditEvent event)
+ {
+ UndoableEditListener[] listeners = getUndoableEditListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].undoableEditHappened(event);
+ }
+
+ /**
+ * Returns the asynchronous loading priority. Returns <code>-1</code> if this
+ * document should not be loaded asynchronously.
+ *
+ * @return the asynchronous loading priority
+ */
+ public int getAsynchronousLoadPriority()
+ {
+ Object val = getProperty(AsyncLoadPriority);
+ int prio = -1;
+ if (val != null)
+ prio = ((Integer) val).intValue();
+ return prio;
+ }
+
+ /**
+ * Returns the {@link AttributeContext} used in this <code>Document</code>.
+ *
+ * @return the {@link AttributeContext} used in this <code>Document</code>
+ */
+ protected final AttributeContext getAttributeContext()
+ {
+ return context;
+ }
+
+ /**
+ * Returns the root element for bidirectional content.
+ *
+ * @return the root element for bidirectional content
+ */
+ public Element getBidiRootElement()
+ {
+ return bidiRoot;
+ }
+
+ /**
+ * Returns the {@link Content} model for this <code>Document</code>
+ *
+ * @return the {@link Content} model for this <code>Document</code>
+ *
+ * @see GapContent
+ * @see StringContent
+ */
+ protected final Content getContent()
+ {
+ return content;
+ }
+
+ /**
+ * Returns the thread that currently modifies this <code>Document</code>
+ * if there is one, otherwise <code>null</code>. This can be used to
+ * distinguish between a method call that is part of an ongoing modification
+ * or if it is a separate modification for which a new lock must be aquired.
+ *
+ * @return the thread that currently modifies this <code>Document</code>
+ * if there is one, otherwise <code>null</code>
+ */
+ protected final synchronized Thread getCurrentWriter()
+ {
+ return currentWriter;
+ }
+
+ /**
+ * Returns the properties of this <code>Document</code>.
+ *
+ * @return the properties of this <code>Document</code>
+ */
+ public Dictionary<Object, Object> getDocumentProperties()
+ {
+ // FIXME: make me thread-safe
+ if (properties == null)
+ properties = new Hashtable();
+
+ return properties;
+ }
+
+ /**
+ * Returns a {@link Position} which will always mark the end of the
+ * <code>Document</code>.
+ *
+ * @return a {@link Position} which will always mark the end of the
+ * <code>Document</code>
+ */
+ public final Position getEndPosition()
+ {
+ Position p;
+ try
+ {
+ p = createPosition(content.length());
+ }
+ catch (BadLocationException ex)
+ {
+ // Shouldn't really happen.
+ p = null;
+ }
+ return p;
+ }
+
+ /**
+ * Returns the length of this <code>Document</code>'s content.
+ *
+ * @return the length of this <code>Document</code>'s content
+ */
+ public int getLength()
+ {
+ // We return Content.getLength() -1 here because there is always an
+ // implicit \n at the end of the Content which does count in Content
+ // but not in Document.
+ return content.length() - 1;
+ }
+
+ /**
+ * Returns all registered listeners of a given listener type.
+ *
+ * @param listenerType the type of the listeners to be queried
+ *
+ * @return all registered listeners of the specified type
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Returns a property from this <code>Document</code>'s property list.
+ *
+ * @param key the key of the property to be fetched
+ *
+ * @return the property for <code>key</code> or <code>null</code> if there
+ * is no such property stored
+ */
+ public final Object getProperty(Object key)
+ {
+ // FIXME: make me thread-safe
+ Object value = null;
+ if (properties != null)
+ value = properties.get(key);
+
+ return value;
+ }
+
+ /**
+ * Returns all root elements of this <code>Document</code>. By default
+ * this just returns the single root element returned by
+ * {@link #getDefaultRootElement()}. <code>Document</code> implementations
+ * that support multiple roots must override this method and return all roots
+ * here.
+ *
+ * @return all root elements of this <code>Document</code>
+ */
+ public Element[] getRootElements()
+ {
+ Element[] elements = new Element[2];
+ elements[0] = getDefaultRootElement();
+ elements[1] = getBidiRootElement();
+ return elements;
+ }
+
+ /**
+ * Returns a {@link Position} which will always mark the beginning of the
+ * <code>Document</code>.
+ *
+ * @return a {@link Position} which will always mark the beginning of the
+ * <code>Document</code>
+ */
+ public final Position getStartPosition()
+ {
+ Position p;
+ try
+ {
+ p = createPosition(0);
+ }
+ catch (BadLocationException ex)
+ {
+ // Shouldn't really happen.
+ p = null;
+ }
+ return p;
+ }
+
+ /**
+ * Returns a piece of this <code>Document</code>'s content.
+ *
+ * @param offset the start offset of the content
+ * @param length the length of the content
+ *
+ * @return the piece of content specified by <code>offset</code> and
+ * <code>length</code>
+ *
+ * @throws BadLocationException if <code>offset</code> or <code>offset +
+ * length</code> are invalid locations with this
+ * <code>Document</code>
+ */
+ public String getText(int offset, int length) throws BadLocationException
+ {
+ return content.getString(offset, length);
+ }
+
+ /**
+ * Fetches a piece of this <code>Document</code>'s content and stores
+ * it in the given {@link Segment}.
+ *
+ * @param offset the start offset of the content
+ * @param length the length of the content
+ * @param segment the <code>Segment</code> to store the content in
+ *
+ * @throws BadLocationException if <code>offset</code> or <code>offset +
+ * length</code> are invalid locations with this
+ * <code>Document</code>
+ */
+ public void getText(int offset, int length, Segment segment)
+ throws BadLocationException
+ {
+ content.getChars(offset, length, segment);
+ }
+
+ /**
+ * Inserts a String into this <code>Document</code> at the specified
+ * position and assigning the specified attributes to it.
+ *
+ * <p>If a {@link DocumentFilter} is installed in this document, the
+ * corresponding method of the filter object is called.</p>
+ *
+ * <p>The method has no effect when <code>text</code> is <code>null</code>
+ * or has a length of zero.</p>
+ *
+ *
+ * @param offset the location at which the string should be inserted
+ * @param text the content to be inserted
+ * @param attributes the text attributes to be assigned to that string
+ *
+ * @throws BadLocationException if <code>offset</code> is not a valid
+ * location in this <code>Document</code>
+ */
+ public void insertString(int offset, String text, AttributeSet attributes)
+ throws BadLocationException
+ {
+ // Bail out if we have a bogus insertion (Behavior observed in RI).
+ if (text == null || text.length() == 0)
+ return;
+
+ writeLock();
+ try
+ {
+ if (documentFilter == null)
+ insertStringImpl(offset, text, attributes);
+ else
+ documentFilter.insertString(getBypass(), offset, text, attributes);
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ void insertStringImpl(int offset, String text, AttributeSet attributes)
+ throws BadLocationException
+ {
+ // 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);
+
+ UndoableEdit undo = content.insertString(offset, text);
+ if (undo != null)
+ event.addEdit(undo);
+
+ // Check if we need bidi layout.
+ if (getProperty(I18N).equals(Boolean.FALSE))
+ {
+ Object dir = getProperty(TextAttribute.RUN_DIRECTION);
+ if (TextAttribute.RUN_DIRECTION_RTL.equals(dir))
+ putProperty(I18N, Boolean.TRUE);
+ else
+ {
+ char[] chars = text.toCharArray();
+ if (Bidi.requiresBidi(chars, 0, chars.length))
+ putProperty(I18N, Boolean.TRUE);
+ }
+ }
+
+ insertUpdate(event, attributes);
+
+ fireInsertUpdate(event);
+
+ if (undo != null)
+ fireUndoableEditUpdate(new UndoableEditEvent(this, undo));
+ }
+
+ /**
+ * Called to indicate that text has been inserted into this
+ * <code>Document</code>. The default implementation does nothing.
+ * This method is executed within a write lock.
+ *
+ * @param chng the <code>DefaultDocumentEvent</code> describing the change
+ * @param attr the attributes of the changed content
+ */
+ protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr)
+ {
+ if (Boolean.TRUE.equals(getProperty(I18N)))
+ updateBidi(chng);
+ }
+
+ /**
+ * Called after some content has been removed from this
+ * <code>Document</code>. The default implementation does nothing.
+ * This method is executed within a write lock.
+ *
+ * @param chng the <code>DefaultDocumentEvent</code> describing the change
+ */
+ protected void postRemoveUpdate(DefaultDocumentEvent chng)
+ {
+ if (Boolean.TRUE.equals(getProperty(I18N)))
+ updateBidi(chng);
+ }
+
+ /**
+ * Stores a property in this <code>Document</code>'s property list.
+ *
+ * @param key the key of the property to be stored
+ * @param value the value of the property to be stored
+ */
+ public final void putProperty(Object key, Object value)
+ {
+ // FIXME: make me thread-safe
+ if (properties == null)
+ properties = new Hashtable();
+
+ if (value == null)
+ properties.remove(key);
+ else
+ properties.put(key, value);
+
+ // Update bidi structure if the RUN_DIRECTION is set.
+ if (TextAttribute.RUN_DIRECTION.equals(key))
+ {
+ if (TextAttribute.RUN_DIRECTION_RTL.equals(value)
+ && Boolean.FALSE.equals(getProperty(I18N)))
+ putProperty(I18N, Boolean.TRUE);
+
+ if (Boolean.TRUE.equals(getProperty(I18N)))
+ {
+ writeLock();
+ try
+ {
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(0, getLength(),
+ DocumentEvent.EventType.INSERT);
+ updateBidi(ev);
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates the bidi element structure.
+ *
+ * @param ev the document event for the change
+ */
+ private void updateBidi(DefaultDocumentEvent ev)
+ {
+ // Determine start and end offset of the paragraphs to be scanned.
+ int start = 0;
+ int end = 0;
+ DocumentEvent.EventType type = ev.getType();
+ if (type == DocumentEvent.EventType.INSERT
+ || type == DocumentEvent.EventType.CHANGE)
+ {
+ int offs = ev.getOffset();
+ int endOffs = offs + ev.getLength();
+ start = getParagraphElement(offs).getStartOffset();
+ end = getParagraphElement(endOffs).getEndOffset();
+ }
+ else if (type == DocumentEvent.EventType.REMOVE)
+ {
+ Element par = getParagraphElement(ev.getOffset());
+ start = par.getStartOffset();
+ end = par.getEndOffset();
+ }
+ else
+ assert false : "Unknown event type";
+
+ // Determine the bidi levels for the affected range.
+ Bidi[] bidis = getBidis(start, end);
+
+ int removeFrom = 0;
+ int removeTo = 0;
+
+ int offs = 0;
+ int lastRunStart = 0;
+ int lastRunEnd = 0;
+ int lastRunLevel = 0;
+ ArrayList newEls = new ArrayList();
+ for (int i = 0; i < bidis.length; i++)
+ {
+ Bidi bidi = bidis[i];
+ int numRuns = bidi.getRunCount();
+ for (int r = 0; r < numRuns; r++)
+ {
+ if (r == 0 && i == 0)
+ {
+ if (start > 0)
+ {
+ // Try to merge with the previous element if it has the
+ // same bidi level as the first run.
+ int prevElIndex = bidiRoot.getElementIndex(start - 1);
+ removeFrom = prevElIndex;
+ Element prevEl = bidiRoot.getElement(prevElIndex);
+ AttributeSet atts = prevEl.getAttributes();
+ int prevElLevel = StyleConstants.getBidiLevel(atts);
+ if (prevElLevel == bidi.getRunLevel(r))
+ {
+ // Merge previous element with current run.
+ lastRunStart = prevEl.getStartOffset() - start;
+ lastRunEnd = bidi.getRunLimit(r);
+ lastRunLevel = bidi.getRunLevel(r);
+ }
+ else if (prevEl.getEndOffset() > start)
+ {
+ // Split previous element and replace by 2 new elements.
+ lastRunStart = 0;
+ lastRunEnd = bidi.getRunLimit(r);
+ lastRunLevel = bidi.getRunLevel(r);
+ newEls.add(new BidiElement(bidiRoot,
+ prevEl.getStartOffset(),
+ start, prevElLevel));
+ }
+ else
+ {
+ // Simply start new run at start location.
+ lastRunStart = 0;
+ lastRunEnd = bidi.getRunLimit(r);
+ lastRunLevel = bidi.getRunLevel(r);
+ removeFrom++;
+ }
+ }
+ else
+ {
+ // Simply start new run at start location.
+ lastRunStart = 0;
+ lastRunEnd = bidi.getRunLimit(r);
+ lastRunLevel = bidi.getRunLevel(r);
+ removeFrom = 0;
+ }
+ }
+ if (i == bidis.length - 1 && r == numRuns - 1)
+ {
+ if (end <= getLength())
+ {
+ // Try to merge last element with next element.
+ int nextIndex = bidiRoot.getElementIndex(end);
+ Element nextEl = bidiRoot.getElement(nextIndex);
+ AttributeSet atts = nextEl.getAttributes();
+ int nextLevel = StyleConstants.getBidiLevel(atts);
+ int level = bidi.getRunLevel(r);
+ if (lastRunLevel == level && level == nextLevel)
+ {
+ // Merge runs together.
+ if (lastRunStart + start == nextEl.getStartOffset())
+ removeTo = nextIndex - 1;
+ else
+ {
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ nextEl.getEndOffset(), level));
+ removeTo = nextIndex;
+ }
+ }
+ else if (lastRunLevel == level)
+ {
+ // Merge current and last run.
+ int endOffs = offs + bidi.getRunLimit(r);
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + endOffs, level));
+ if (start + endOffs == nextEl.getStartOffset())
+ removeTo = nextIndex - 1;
+ else
+ {
+ newEls.add(new BidiElement(bidiRoot, start + endOffs,
+ nextEl.getEndOffset(),
+ nextLevel));
+ removeTo = nextIndex;
+ }
+ }
+ else if (level == nextLevel)
+ {
+ // Merge current and next run.
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + lastRunEnd,
+ lastRunLevel));
+ newEls.add(new BidiElement(bidiRoot, start + lastRunEnd,
+ nextEl.getEndOffset(), level));
+ removeTo = nextIndex;
+ }
+ else
+ {
+ // Split next element.
+ int endOffs = offs + bidi.getRunLimit(r);
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + lastRunEnd,
+ lastRunLevel));
+ newEls.add(new BidiElement(bidiRoot, start + lastRunEnd,
+ start + endOffs, level));
+ newEls.add(new BidiElement(bidiRoot, start + endOffs,
+ nextEl.getEndOffset(),
+ nextLevel));
+ removeTo = nextIndex;
+ }
+ }
+ else
+ {
+ removeTo = bidiRoot.getElementIndex(end);
+ int level = bidi.getRunLevel(r);
+ int runEnd = offs + bidi.getRunLimit(r);
+
+ if (level == lastRunLevel)
+ {
+ // Merge with previous.
+ lastRunEnd = offs + runEnd;
+ newEls.add(new BidiElement(bidiRoot,
+ start + lastRunStart,
+ start + runEnd, level));
+ }
+ else
+ {
+ // Create element for last run and current run.
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + lastRunEnd,
+ lastRunLevel));
+ newEls.add(new BidiElement(bidiRoot,
+ start + lastRunEnd,
+ start + runEnd,
+ level));
+ }
+ }
+ }
+ else
+ {
+ int level = bidi.getRunLevel(r);
+ int runEnd = bidi.getRunLimit(r);
+
+ if (level == lastRunLevel)
+ {
+ // Merge with previous.
+ lastRunEnd = offs + runEnd;
+ }
+ else
+ {
+ // Create element for last run and update values for
+ // current run.
+ newEls.add(new BidiElement(bidiRoot, start + lastRunStart,
+ start + lastRunEnd,
+ lastRunLevel));
+ lastRunStart = lastRunEnd;
+ lastRunEnd = offs + runEnd;
+ lastRunLevel = level;
+ }
+ }
+ }
+ offs += bidi.getLength();
+ }
+
+ // Determine the bidi elements which are to be removed.
+ int numRemoved = 0;
+ if (bidiRoot.getElementCount() > 0)
+ numRemoved = removeTo - removeFrom + 1;
+ Element[] removed = new Element[numRemoved];
+ for (int i = 0; i < numRemoved; i++)
+ removed[i] = bidiRoot.getElement(removeFrom + i);
+
+ Element[] added = new Element[newEls.size()];
+ added = (Element[]) newEls.toArray(added);
+
+ // Update the event.
+ ElementEdit edit = new ElementEdit(bidiRoot, removeFrom, removed, added);
+ ev.addEdit(edit);
+
+ // Update the structure.
+ bidiRoot.replace(removeFrom, numRemoved, added);
+ }
+
+ /**
+ * Determines the Bidi objects for the paragraphs in the specified range.
+ *
+ * @param start the start of the range
+ * @param end the end of the range
+ *
+ * @return the Bidi analysers for the paragraphs in the range
+ */
+ private Bidi[] getBidis(int start, int end)
+ {
+ // Determine the default run direction from the document property.
+ Boolean defaultDir = null;
+ Object o = getProperty(TextAttribute.RUN_DIRECTION);
+ if (o instanceof Boolean)
+ defaultDir = (Boolean) o;
+
+ // Scan paragraphs and add their level arrays to the overall levels array.
+ ArrayList bidis = new ArrayList();
+ Segment s = new Segment();
+ for (int i = start; i < end;)
+ {
+ Element par = getParagraphElement(i);
+ int pStart = par.getStartOffset();
+ int pEnd = par.getEndOffset();
+
+ // Determine the default run direction of the paragraph.
+ Boolean dir = defaultDir;
+ o = par.getAttributes().getAttribute(TextAttribute.RUN_DIRECTION);
+ if (o instanceof Boolean)
+ dir = (Boolean) o;
+
+ // Bidi over the paragraph.
+ try
+ {
+ getText(pStart, pEnd - pStart, s);
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "Must not happen";
+ }
+ int flag = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
+ if (dir != null)
+ {
+ if (TextAttribute.RUN_DIRECTION_LTR.equals(dir))
+ flag = Bidi.DIRECTION_LEFT_TO_RIGHT;
+ else
+ flag = Bidi.DIRECTION_RIGHT_TO_LEFT;
+ }
+ Bidi bidi = new Bidi(s.array, s.offset, null, 0, s.count, flag);
+ bidis.add(bidi);
+ i = pEnd;
+ }
+ Bidi[] ret = new Bidi[bidis.size()];
+ ret = (Bidi[]) bidis.toArray(ret);
+ return ret;
+ }
+
+ /**
+ * Blocks until a read lock can be obtained. Must block if there is
+ * currently a writer modifying the <code>Document</code>.
+ */
+ public final synchronized void readLock()
+ {
+ try
+ {
+ while (currentWriter != null)
+ {
+ if (currentWriter == Thread.currentThread())
+ return;
+ wait();
+ }
+ numReaders++;
+ }
+ catch (InterruptedException ex)
+ {
+ throw new Error("Interrupted during grab read lock");
+ }
+ }
+
+ /**
+ * Releases the read lock. If this was the only reader on this
+ * <code>Document</code>, writing may begin now.
+ */
+ public final synchronized void readUnlock()
+ {
+ // Note we could have a problem here if readUnlock was called without a
+ // prior call to readLock but the specs simply warn users to ensure that
+ // balance by using a finally block:
+ // readLock()
+ // try
+ // {
+ // doSomethingHere
+ // }
+ // finally
+ // {
+ // readUnlock();
+ // }
+
+ // All that the JDK seems to check for is that you don't call unlock
+ // more times than you've previously called lock, but it doesn't make
+ // sure that the threads calling unlock were the same ones that called lock
+
+ // If the current thread holds the write lock, and attempted to also obtain
+ // a readLock, then numReaders hasn't been incremented and we don't need
+ // to unlock it here.
+ if (currentWriter == Thread.currentThread())
+ return;
+
+ // FIXME: the reference implementation throws a
+ // javax.swing.text.StateInvariantError here
+ if (numReaders <= 0)
+ throw new IllegalStateException("document lock failure");
+
+ // If currentWriter is not null, the application code probably had a
+ // writeLock and then tried to obtain a readLock, in which case
+ // numReaders wasn't incremented
+ numReaders--;
+ notify();
+ }
+
+ /**
+ * Removes a piece of content from this <code>Document</code>.
+ *
+ * <p>If a {@link DocumentFilter} is installed in this document, the
+ * corresponding method of the filter object is called. The
+ * <code>DocumentFilter</code> is called even if <code>length</code>
+ * is zero. This is different from {@link #replace}.</p>
+ *
+ * <p>Note: When <code>length</code> is zero or below the call is not
+ * forwarded to the underlying {@link AbstractDocument.Content} instance
+ * of this document and no exception is thrown.</p>
+ *
+ * @param offset the start offset of the fragment to be removed
+ * @param length the length of the fragment to be removed
+ *
+ * @throws BadLocationException if <code>offset</code> or
+ * <code>offset + length</code> or invalid locations within this
+ * document
+ */
+ public void remove(int offset, int length) throws BadLocationException
+ {
+ writeLock();
+ try
+ {
+ DocumentFilter f = getDocumentFilter();
+ if (f == null)
+ removeImpl(offset, length);
+ else
+ f.remove(getBypass(), offset, length);
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ void removeImpl(int offset, int length) throws BadLocationException
+ {
+ // The RI silently ignores all requests that have a negative length.
+ // Don't ask my why, but that's how it is.
+ if (length > 0)
+ {
+ if (offset < 0 || offset > getLength())
+ throw new BadLocationException("Invalid remove position", offset);
+
+ if (offset + length > getLength())
+ throw new BadLocationException("Invalid remove length", offset);
+
+ DefaultDocumentEvent event =
+ new DefaultDocumentEvent(offset, length,
+ DocumentEvent.EventType.REMOVE);
+
+ // The order of the operations below is critical!
+ removeUpdate(event);
+ UndoableEdit temp = content.remove(offset, length);
+
+ postRemoveUpdate(event);
+ fireRemoveUpdate(event);
+ }
+ }
+
+ /**
+ * Replaces a piece of content in this <code>Document</code> with
+ * another piece of content.
+ *
+ * <p>If a {@link DocumentFilter} is installed in this document, the
+ * corresponding method of the filter object is called.</p>
+ *
+ * <p>The method has no effect if <code>length</code> is zero (and
+ * only zero) and, at the same time, <code>text</code> is
+ * <code>null</code> or has zero length.</p>
+ *
+ * @param offset the start offset of the fragment to be removed
+ * @param length the length of the fragment to be removed
+ * @param text the text to replace the content with
+ * @param attributes the text attributes to assign to the new content
+ *
+ * @throws BadLocationException if <code>offset</code> or
+ * <code>offset + length</code> or invalid locations within this
+ * document
+ *
+ * @since 1.4
+ */
+ public void replace(int offset, int length, String text,
+ AttributeSet attributes)
+ throws BadLocationException
+ {
+ // Bail out if we have a bogus replacement (Behavior observed in RI).
+ if (length == 0
+ && (text == null || text.length() == 0))
+ return;
+
+ writeLock();
+ try
+ {
+ if (documentFilter == null)
+ {
+ // It is important to call the methods which again do the checks
+ // of the arguments and the DocumentFilter because subclasses may
+ // have overridden these methods and provide crucial behavior
+ // which would be skipped if we call the non-checking variants.
+ // An example for this is PlainDocument where insertString can
+ // provide a filtering of newlines.
+ remove(offset, length);
+ insertString(offset, text, attributes);
+ }
+ else
+ documentFilter.replace(getBypass(), offset, length, text, attributes);
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ void replaceImpl(int offset, int length, String text,
+ AttributeSet attributes)
+ throws BadLocationException
+ {
+ removeImpl(offset, length);
+ insertStringImpl(offset, text, attributes);
+ }
+
+ /**
+ * Adds a <code>DocumentListener</code> object to this document.
+ *
+ * @param listener the listener to add
+ */
+ public void addDocumentListener(DocumentListener listener)
+ {
+ listenerList.add(DocumentListener.class, listener);
+ }
+
+ /**
+ * Removes a <code>DocumentListener</code> object from this document.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeDocumentListener(DocumentListener listener)
+ {
+ listenerList.remove(DocumentListener.class, listener);
+ }
+
+ /**
+ * Returns all registered <code>DocumentListener</code>s.
+ *
+ * @return all registered <code>DocumentListener</code>s
+ */
+ public DocumentListener[] getDocumentListeners()
+ {
+ return (DocumentListener[]) getListeners(DocumentListener.class);
+ }
+
+ /**
+ * Adds an {@link UndoableEditListener} to this <code>Document</code>.
+ *
+ * @param listener the listener to add
+ */
+ public void addUndoableEditListener(UndoableEditListener listener)
+ {
+ listenerList.add(UndoableEditListener.class, listener);
+ }
+
+ /**
+ * Removes an {@link UndoableEditListener} from this <code>Document</code>.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeUndoableEditListener(UndoableEditListener listener)
+ {
+ listenerList.remove(UndoableEditListener.class, listener);
+ }
+
+ /**
+ * Returns all registered {@link UndoableEditListener}s.
+ *
+ * @return all registered {@link UndoableEditListener}s
+ */
+ public UndoableEditListener[] getUndoableEditListeners()
+ {
+ return (UndoableEditListener[]) getListeners(UndoableEditListener.class);
+ }
+
+ /**
+ * Called before some content gets removed from this <code>Document</code>.
+ * The default implementation does nothing but may be overridden by
+ * subclasses to modify the <code>Document</code> structure in response
+ * to a remove request. The method is executed within a write lock.
+ *
+ * @param chng the <code>DefaultDocumentEvent</code> describing the change
+ */
+ protected void removeUpdate(DefaultDocumentEvent chng)
+ {
+ // Do nothing here. Subclasses may wish to override this.
+ }
+
+ /**
+ * Called to render this <code>Document</code> visually. It obtains a read
+ * lock, ensuring that no changes will be made to the <code>document</code>
+ * during the rendering process. It then calls the {@link Runnable#run()}
+ * method on <code>runnable</code>. This method <em>must not</em> attempt
+ * to modifiy the <code>Document</code>, since a deadlock will occur if it
+ * tries to obtain a write lock. When the {@link Runnable#run()} method
+ * completes (either naturally or by throwing an exception), the read lock
+ * is released. Note that there is nothing in this method related to
+ * the actual rendering. It could be used to execute arbitrary code within
+ * a read lock.
+ *
+ * @param runnable the {@link Runnable} to execute
+ */
+ public void render(Runnable runnable)
+ {
+ readLock();
+ try
+ {
+ runnable.run();
+ }
+ finally
+ {
+ readUnlock();
+ }
+ }
+
+ /**
+ * Sets the asynchronous loading priority for this <code>Document</code>.
+ * A value of <code>-1</code> indicates that this <code>Document</code>
+ * should be loaded synchronously.
+ *
+ * @param p the asynchronous loading priority to set
+ */
+ public void setAsynchronousLoadPriority(int p)
+ {
+ Integer val = p >= 0 ? new Integer(p) : null;
+ putProperty(AsyncLoadPriority, val);
+ }
+
+ /**
+ * Sets the properties of this <code>Document</code>.
+ *
+ * @param p the document properties to set
+ */
+ public void setDocumentProperties(Dictionary<Object, Object> p)
+ {
+ // FIXME: make me thread-safe
+ properties = p;
+ }
+
+ /**
+ * Blocks until a write lock can be obtained. Must wait if there are
+ * readers currently reading or another thread is currently writing.
+ */
+ protected synchronized final void writeLock()
+ {
+ try
+ {
+ while (numReaders > 0 || currentWriter != null)
+ {
+ if (Thread.currentThread() == currentWriter)
+ {
+ if (notifyListeners)
+ throw new IllegalStateException("Mutation during notify");
+ numWriters++;
+ return;
+ }
+ wait();
+ }
+ currentWriter = Thread.currentThread();
+ numWriters = 1;
+ }
+ catch (InterruptedException ex)
+ {
+ throw new Error("Interupted during grab write lock");
+ }
+ }
+
+ /**
+ * Releases the write lock. This allows waiting readers or writers to
+ * obtain the lock.
+ */
+ protected final synchronized void writeUnlock()
+ {
+ if (--numWriters <= 0)
+ {
+ numWriters = 0;
+ currentWriter = null;
+ notifyAll();
+ }
+ }
+
+ /**
+ * Returns the currently installed {@link DocumentFilter} for this
+ * <code>Document</code>.
+ *
+ * @return the currently installed {@link DocumentFilter} for this
+ * <code>Document</code>
+ *
+ * @since 1.4
+ */
+ public DocumentFilter getDocumentFilter()
+ {
+ return documentFilter;
+ }
+
+ /**
+ * Sets the {@link DocumentFilter} for this <code>Document</code>.
+ *
+ * @param filter the <code>DocumentFilter</code> to set
+ *
+ * @since 1.4
+ */
+ public void setDocumentFilter(DocumentFilter filter)
+ {
+ this.documentFilter = filter;
+ }
+
+ /**
+ * Dumps diagnostic information to the specified <code>PrintStream</code>.
+ *
+ * @param out the stream to write the diagnostic information to
+ */
+ public void dump(PrintStream out)
+ {
+ ((AbstractElement) getDefaultRootElement()).dump(out, 0);
+ ((AbstractElement) getBidiRootElement()).dump(out, 0);
+ }
+
+ /**
+ * Defines a set of methods for managing text attributes for one or more
+ * <code>Document</code>s.
+ *
+ * Replicating {@link AttributeSet}s throughout a <code>Document</code> can
+ * be very expensive. Implementations of this interface are intended to
+ * provide intelligent management of <code>AttributeSet</code>s, eliminating
+ * costly duplication.
+ *
+ * @see StyleContext
+ */
+ public interface AttributeContext
+ {
+ /**
+ * Returns an {@link AttributeSet} that contains the attributes
+ * of <code>old</code> plus the new attribute specified by
+ * <code>name</code> and <code>value</code>.
+ *
+ * @param old the attribute set to be merged with the new attribute
+ * @param name the name of the attribute to be added
+ * @param value the value of the attribute to be added
+ *
+ * @return the old attributes plus the new attribute
+ */
+ AttributeSet addAttribute(AttributeSet old, Object name, Object value);
+
+ /**
+ * Returns an {@link AttributeSet} that contains the attributes
+ * of <code>old</code> plus the new attributes in <code>attributes</code>.
+ *
+ * @param old the set of attributes where to add the new attributes
+ * @param attributes the attributes to be added
+ *
+ * @return an {@link AttributeSet} that contains the attributes
+ * of <code>old</code> plus the new attributes in
+ * <code>attributes</code>
+ */
+ AttributeSet addAttributes(AttributeSet old, AttributeSet attributes);
+
+ /**
+ * Returns an empty {@link AttributeSet}.
+ *
+ * @return an empty {@link AttributeSet}
+ */
+ AttributeSet getEmptySet();
+
+ /**
+ * Called to indicate that the attributes in <code>attributes</code> are
+ * no longer used.
+ *
+ * @param attributes the attributes are no longer used
+ */
+ void reclaim(AttributeSet attributes);
+
+ /**
+ * Returns a {@link AttributeSet} that has the attribute with the specified
+ * <code>name</code> removed from <code>old</code>.
+ *
+ * @param old the attribute set from which an attribute is removed
+ * @param name the name of the attribute to be removed
+ *
+ * @return the attributes of <code>old</code> minus the attribute
+ * specified by <code>name</code>
+ */
+ AttributeSet removeAttribute(AttributeSet old, Object name);
+
+ /**
+ * Removes all attributes in <code>attributes</code> from <code>old</code>
+ * and returns the resulting <code>AttributeSet</code>.
+ *
+ * @param old the set of attributes from which to remove attributes
+ * @param attributes the attributes to be removed from <code>old</code>
+ *
+ * @return the attributes of <code>old</code> minus the attributes in
+ * <code>attributes</code>
+ */
+ AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes);
+
+ /**
+ * Removes all attributes specified by <code>names</code> from
+ * <code>old</code> and returns the resulting <code>AttributeSet</code>.
+ *
+ * @param old the set of attributes from which to remove attributes
+ * @param names the names of the attributes to be removed from
+ * <code>old</code>
+ *
+ * @return the attributes of <code>old</code> minus the attributes in
+ * <code>attributes</code>
+ */
+ AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names);
+ }
+
+ /**
+ * A sequence of data that can be edited. This is were the actual content
+ * in <code>AbstractDocument</code>'s is stored.
+ */
+ public interface Content
+ {
+ /**
+ * Creates a {@link Position} that keeps track of the location at
+ * <code>offset</code>.
+ *
+ * @return a {@link Position} that keeps track of the location at
+ * <code>offset</code>.
+ *
+ * @throw BadLocationException if <code>offset</code> is not a valid
+ * location in this <code>Content</code> model
+ */
+ Position createPosition(int offset) throws BadLocationException;
+
+ /**
+ * Returns the length of the content.
+ *
+ * @return the length of the content
+ */
+ int length();
+
+ /**
+ * Inserts a string into the content model.
+ *
+ * @param where the offset at which to insert the string
+ * @param str the string to be inserted
+ *
+ * @return an <code>UndoableEdit</code> or <code>null</code> if undo is
+ * not supported by this <code>Content</code> model
+ *
+ * @throws BadLocationException if <code>where</code> is not a valid
+ * location in this <code>Content</code> model
+ */
+ UndoableEdit insertString(int where, String str)
+ throws BadLocationException;
+
+ /**
+ * Removes a piece of content from the content model.
+ *
+ * @param where the offset at which to remove content
+ * @param nitems the number of characters to be removed
+ *
+ * @return an <code>UndoableEdit</code> or <code>null</code> if undo is
+ * not supported by this <code>Content</code> model
+ *
+ * @throws BadLocationException if <code>where</code> is not a valid
+ * location in this <code>Content</code> model
+ */
+ UndoableEdit remove(int where, int nitems) throws BadLocationException;
+
+ /**
+ * Returns a piece of content.
+ *
+ * @param where the start offset of the requested fragment
+ * @param len the length of the requested fragment
+ *
+ * @return the requested fragment
+ * @throws BadLocationException if <code>offset</code> or
+ * <code>offset + len</code>is not a valid
+ * location in this <code>Content</code> model
+ */
+ String getString(int where, int len) throws BadLocationException;
+
+ /**
+ * Fetches a piece of content and stores it in <code>txt</code>.
+ *
+ * @param where the start offset of the requested fragment
+ * @param len the length of the requested fragment
+ * @param txt the <code>Segment</code> where to fragment is stored into
+ *
+ * @throws BadLocationException if <code>offset</code> or
+ * <code>offset + len</code>is not a valid
+ * location in this <code>Content</code> model
+ */
+ void getChars(int where, int len, Segment txt) throws BadLocationException;
+ }
+
+ /**
+ * An abstract base implementation of the {@link Element} interface.
+ */
+ public abstract class AbstractElement
+ implements Element, MutableAttributeSet, TreeNode, Serializable
+ {
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 1712240033321461704L;
+
+ /** The number of characters that this Element spans. */
+ int count;
+
+ /** The starting offset of this Element. */
+ int offset;
+
+ /** The attributes of this Element. */
+ AttributeSet attributes;
+
+ /** The parent element. */
+ Element element_parent;
+
+ /** The parent in the TreeNode interface. */
+ TreeNode tree_parent;
+
+ /** The children of this element. */
+ Vector tree_children;
+
+ /**
+ * Creates a new instance of <code>AbstractElement</code> with a
+ * specified parent <code>Element</code> and <code>AttributeSet</code>.
+ *
+ * @param p the parent of this <code>AbstractElement</code>
+ * @param s the attributes to be assigned to this
+ * <code>AbstractElement</code>
+ */
+ public AbstractElement(Element p, AttributeSet s)
+ {
+ element_parent = p;
+ AttributeContext ctx = getAttributeContext();
+ attributes = ctx.getEmptySet();
+ if (s != null)
+ addAttributes(s);
+ }
+
+ /**
+ * Returns the child nodes of this <code>Element</code> as an
+ * <code>Enumeration</code> of {@link TreeNode}s.
+ *
+ * @return the child nodes of this <code>Element</code> as an
+ * <code>Enumeration</code> of {@link TreeNode}s
+ */
+ public abstract Enumeration children();
+
+ /**
+ * Returns <code>true</code> if this <code>AbstractElement</code>
+ * allows children.
+ *
+ * @return <code>true</code> if this <code>AbstractElement</code>
+ * allows children
+ */
+ public abstract boolean getAllowsChildren();
+
+ /**
+ * Returns the child of this <code>AbstractElement</code> at
+ * <code>index</code>.
+ *
+ * @param index the position in the child list of the child element to
+ * be returned
+ *
+ * @return the child of this <code>AbstractElement</code> at
+ * <code>index</code>
+ */
+ public TreeNode getChildAt(int index)
+ {
+ return (TreeNode) tree_children.get(index);
+ }
+
+ /**
+ * Returns the number of children of this <code>AbstractElement</code>.
+ *
+ * @return the number of children of this <code>AbstractElement</code>
+ */
+ public int getChildCount()
+ {
+ return tree_children.size();
+ }
+
+ /**
+ * Returns the index of a given child <code>TreeNode</code> or
+ * <code>-1</code> if <code>node</code> is not a child of this
+ * <code>AbstractElement</code>.
+ *
+ * @param node the node for which the index is requested
+ *
+ * @return the index of a given child <code>TreeNode</code> or
+ * <code>-1</code> if <code>node</code> is not a child of this
+ * <code>AbstractElement</code>
+ */
+ public int getIndex(TreeNode node)
+ {
+ return tree_children.indexOf(node);
+ }
+
+ /**
+ * Returns the parent <code>TreeNode</code> of this
+ * <code>AbstractElement</code> or <code>null</code> if this element
+ * has no parent.
+ *
+ * @return the parent <code>TreeNode</code> of this
+ * <code>AbstractElement</code> or <code>null</code> if this
+ * element has no parent
+ */
+ public TreeNode getParent()
+ {
+ return tree_parent;
+ }
+
+ /**
+ * Returns <code>true</code> if this <code>AbstractElement</code> is a
+ * leaf element, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if this <code>AbstractElement</code> is a
+ * leaf element, <code>false</code> otherwise
+ */
+ public abstract boolean isLeaf();
+
+ /**
+ * Adds an attribute to this element.
+ *
+ * @param name the name of the attribute to be added
+ * @param value the value of the attribute to be added
+ */
+ public void addAttribute(Object name, Object value)
+ {
+ attributes = getAttributeContext().addAttribute(attributes, name, value);
+ }
+
+ /**
+ * Adds a set of attributes to this element.
+ *
+ * @param attrs the attributes to be added to this element
+ */
+ public void addAttributes(AttributeSet attrs)
+ {
+ attributes = getAttributeContext().addAttributes(attributes, attrs);
+ }
+
+ /**
+ * Removes an attribute from this element.
+ *
+ * @param name the name of the attribute to be removed
+ */
+ public void removeAttribute(Object name)
+ {
+ attributes = getAttributeContext().removeAttribute(attributes, name);
+ }
+
+ /**
+ * Removes a set of attributes from this element.
+ *
+ * @param attrs the attributes to be removed
+ */
+ public void removeAttributes(AttributeSet attrs)
+ {
+ attributes = getAttributeContext().removeAttributes(attributes, attrs);
+ }
+
+ /**
+ * Removes a set of attribute from this element.
+ *
+ * @param names the names of the attributes to be removed
+ */
+ public void removeAttributes(Enumeration<?> names)
+ {
+ attributes = getAttributeContext().removeAttributes(attributes, names);
+ }
+
+ /**
+ * Sets the parent attribute set against which the element can resolve
+ * attributes that are not defined in itself.
+ *
+ * @param parent the resolve parent to set
+ */
+ public void setResolveParent(AttributeSet parent)
+ {
+ attributes = getAttributeContext().addAttribute(attributes,
+ ResolveAttribute,
+ parent);
+ }
+
+ /**
+ * Returns <code>true</code> if this element contains the specified
+ * attribute.
+ *
+ * @param name the name of the attribute to check
+ * @param value the value of the attribute to check
+ *
+ * @return <code>true</code> if this element contains the specified
+ * attribute
+ */
+ public boolean containsAttribute(Object name, Object value)
+ {
+ return attributes.containsAttribute(name, value);
+ }
+
+ /**
+ * Returns <code>true</code> if this element contains all of the
+ * specified attributes.
+ *
+ * @param attrs the attributes to check
+ *
+ * @return <code>true</code> if this element contains all of the
+ * specified attributes
+ */
+ public boolean containsAttributes(AttributeSet attrs)
+ {
+ return attributes.containsAttributes(attrs);
+ }
+
+ /**
+ * Returns a copy of the attributes of this element.
+ *
+ * @return a copy of the attributes of this element
+ */
+ public AttributeSet copyAttributes()
+ {
+ return attributes.copyAttributes();
+ }
+
+ /**
+ * Returns the attribute value with the specified key. If this attribute
+ * is not defined in this element and this element has a resolving
+ * parent, the search goes upward to the resolve parent chain.
+ *
+ * @param key the key of the requested attribute
+ *
+ * @return the attribute value for <code>key</code> of <code>null</code>
+ * if <code>key</code> is not found locally and cannot be resolved
+ * in this element's resolve parents
+ */
+ public Object getAttribute(Object key)
+ {
+ Object result = attributes.getAttribute(key);
+ if (result == null)
+ {
+ AttributeSet resParent = getResolveParent();
+ if (resParent != null)
+ result = resParent.getAttribute(key);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the number of defined attributes in this element.
+ *
+ * @return the number of defined attributes in this element
+ */
+ public int getAttributeCount()
+ {
+ return attributes.getAttributeCount();
+ }
+
+ /**
+ * Returns the names of the attributes of this element.
+ *
+ * @return the names of the attributes of this element
+ */
+ public Enumeration<?> getAttributeNames()
+ {
+ return attributes.getAttributeNames();
+ }
+
+ /**
+ * Returns the resolve parent of this element.
+ * This is taken from the AttributeSet, but if this is null,
+ * this method instead returns the Element's parent's
+ * AttributeSet
+ *
+ * @return the resolve parent of this element
+ *
+ * @see #setResolveParent(AttributeSet)
+ */
+ public AttributeSet getResolveParent()
+ {
+ return attributes.getResolveParent();
+ }
+
+ /**
+ * Returns <code>true</code> if an attribute with the specified name
+ * is defined in this element, <code>false</code> otherwise.
+ *
+ * @param attrName the name of the requested attributes
+ *
+ * @return <code>true</code> if an attribute with the specified name
+ * is defined in this element, <code>false</code> otherwise
+ */
+ public boolean isDefined(Object attrName)
+ {
+ return attributes.isDefined(attrName);
+ }
+
+ /**
+ * Returns <code>true</code> if the specified <code>AttributeSet</code>
+ * is equal to this element's <code>AttributeSet</code>, <code>false</code>
+ * otherwise.
+ *
+ * @param attrs the attributes to compare this element to
+ *
+ * @return <code>true</code> if the specified <code>AttributeSet</code>
+ * is equal to this element's <code>AttributeSet</code>,
+ * <code>false</code> otherwise
+ */
+ public boolean isEqual(AttributeSet attrs)
+ {
+ return attributes.isEqual(attrs);
+ }
+
+ /**
+ * Returns the attributes of this element.
+ *
+ * @return the attributes of this element
+ */
+ public AttributeSet getAttributes()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the {@link Document} to which this element belongs.
+ *
+ * @return the {@link Document} to which this element belongs
+ */
+ public Document getDocument()
+ {
+ return AbstractDocument.this;
+ }
+
+ /**
+ * Returns the child element at the specified <code>index</code>.
+ *
+ * @param index the index of the requested child element
+ *
+ * @return the requested element
+ */
+ public abstract Element getElement(int index);
+
+ /**
+ * Returns the name of this element.
+ *
+ * @return the name of this element
+ */
+ public String getName()
+ {
+ return (String) attributes.getAttribute(ElementNameAttribute);
+ }
+
+ /**
+ * Returns the parent element of this element.
+ *
+ * @return the parent element of this element
+ */
+ public Element getParentElement()
+ {
+ return element_parent;
+ }
+
+ /**
+ * Returns the offset inside the document model that is after the last
+ * character of this element.
+ *
+ * @return the offset inside the document model that is after the last
+ * character of this element
+ */
+ public abstract int getEndOffset();
+
+ /**
+ * Returns the number of child elements of this element.
+ *
+ * @return the number of child elements of this element
+ */
+ public abstract int getElementCount();
+
+ /**
+ * Returns the index of the child element that spans the specified
+ * offset in the document model.
+ *
+ * @param offset the offset for which the responsible element is searched
+ *
+ * @return the index of the child element that spans the specified
+ * offset in the document model
+ */
+ public abstract int getElementIndex(int offset);
+
+ /**
+ * Returns the start offset if this element inside the document model.
+ *
+ * @return the start offset if this element inside the document model
+ */
+ public abstract int getStartOffset();
+
+ /**
+ * Prints diagnostic output to the specified stream.
+ *
+ * @param stream the stream to write to
+ * @param indent the indentation level
+ */
+ public void dump(PrintStream stream, int indent)
+ {
+ CPStringBuilder b = new CPStringBuilder();
+ for (int i = 0; i < indent; ++i)
+ b.append(' ');
+ b.append('<');
+ b.append(getName());
+ // Dump attributes if there are any.
+ if (getAttributeCount() > 0)
+ {
+ b.append('\n');
+ Enumeration attNames = getAttributeNames();
+ while (attNames.hasMoreElements())
+ {
+ for (int i = 0; i < indent + 2; ++i)
+ b.append(' ');
+ Object attName = attNames.nextElement();
+ b.append(attName);
+ b.append('=');
+ Object attribute = getAttribute(attName);
+ b.append(attribute);
+ b.append('\n');
+ }
+ }
+ if (getAttributeCount() > 0)
+ {
+ for (int i = 0; i < indent; ++i)
+ b.append(' ');
+ }
+ b.append(">\n");
+
+ // Dump element content for leaf elements.
+ if (isLeaf())
+ {
+ for (int i = 0; i < indent + 2; ++i)
+ b.append(' ');
+ int start = getStartOffset();
+ int end = getEndOffset();
+ b.append('[');
+ b.append(start);
+ b.append(',');
+ b.append(end);
+ b.append("][");
+ try
+ {
+ b.append(getDocument().getText(start, end - start));
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError("BadLocationException "
+ + "must not be thrown "
+ + "here.");
+ err.initCause(ex);
+ throw err;
+ }
+ b.append("]\n");
+ }
+ stream.print(b.toString());
+
+ // Dump child elements if any.
+ int count = getElementCount();
+ for (int i = 0; i < count; ++i)
+ {
+ Element el = getElement(i);
+ if (el instanceof AbstractElement)
+ ((AbstractElement) el).dump(stream, indent + 2);
+ }
+ }
+ }
+
+ /**
+ * An implementation of {@link Element} to represent composite
+ * <code>Element</code>s that contain other <code>Element</code>s.
+ */
+ public class BranchElement extends AbstractElement
+ {
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -6037216547466333183L;
+
+ /**
+ * The child elements of this BranchElement.
+ */
+ private Element[] children;
+
+ /**
+ * The number of children in the branch element.
+ */
+ private int numChildren;
+
+ /**
+ * The last found index in getElementIndex(). Used for faster searching.
+ */
+ private int lastIndex;
+
+ /**
+ * Creates a new <code>BranchElement</code> with the specified
+ * parent and attributes.
+ *
+ * @param parent the parent element of this <code>BranchElement</code>
+ * @param attributes the attributes to set on this
+ * <code>BranchElement</code>
+ */
+ public BranchElement(Element parent, AttributeSet attributes)
+ {
+ super(parent, attributes);
+ children = new Element[1];
+ numChildren = 0;
+ lastIndex = -1;
+ }
+
+ /**
+ * Returns the children of this <code>BranchElement</code>.
+ *
+ * @return the children of this <code>BranchElement</code>
+ */
+ public Enumeration children()
+ {
+ if (numChildren == 0)
+ return null;
+
+ Vector tmp = new Vector();
+
+ for (int index = 0; index < numChildren; ++index)
+ tmp.add(children[index]);
+
+ return tmp.elements();
+ }
+
+ /**
+ * Returns <code>true</code> since <code>BranchElements</code> allow
+ * child elements.
+ *
+ * @return <code>true</code> since <code>BranchElements</code> allow
+ * child elements
+ */
+ public boolean getAllowsChildren()
+ {
+ return true;
+ }
+
+ /**
+ * Returns the child element at the specified <code>index</code>.
+ *
+ * @param index the index of the requested child element
+ *
+ * @return the requested element
+ */
+ public Element getElement(int index)
+ {
+ if (index < 0 || index >= numChildren)
+ return null;
+
+ return children[index];
+ }
+
+ /**
+ * Returns the number of child elements of this element.
+ *
+ * @return the number of child elements of this element
+ */
+ public int getElementCount()
+ {
+ return numChildren;
+ }
+
+ /**
+ * Returns the index of the child element that spans the specified
+ * offset in the document model.
+ *
+ * @param offset the offset for which the responsible element is searched
+ *
+ * @return the index of the child element that spans the specified
+ * offset in the document model
+ */
+ public int getElementIndex(int offset)
+ {
+ // Implemented using an improved linear search.
+ // This makes use of the fact that searches are not random but often
+ // close to the previous search. So we try to start the binary
+ // search at the last found index.
+
+ int i0 = 0; // The lower bounds.
+ int i1 = numChildren - 1; // The upper bounds.
+ int index = -1; // The found index.
+
+ int p0 = getStartOffset();
+ int p1; // Start and end offset local variables.
+
+ if (numChildren == 0)
+ index = 0;
+ else if (offset >= getEndOffset())
+ index = numChildren - 1;
+ else
+ {
+ // Try lastIndex.
+ if (lastIndex >= i0 && lastIndex <= i1)
+ {
+ Element last = getElement(lastIndex);
+ p0 = last.getStartOffset();
+ p1 = last.getEndOffset();
+ if (offset >= p0 && offset < p1)
+ index = lastIndex;
+ else
+ {
+ // Narrow the search bounds using the lastIndex, even
+ // if it hasn't been a hit.
+ if (offset < p0)
+ i1 = lastIndex;
+ else
+ i0 = lastIndex;
+ }
+ }
+ // The actual search.
+ int i = 0;
+ while (i0 <= i1 && index == -1)
+ {
+ i = i0 + (i1 - i0) / 2;
+ Element el = getElement(i);
+ p0 = el.getStartOffset();
+ p1 = el.getEndOffset();
+ if (offset >= p0 && offset < p1)
+ {
+ // Found it!
+ index = i;
+ }
+ else if (offset < p0)
+ i1 = i - 1;
+ else
+ i0 = i + 1;
+ }
+
+ if (index == -1)
+ {
+ // Didn't find it. Return the boundary index.
+ if (offset < p0)
+ index = i;
+ else
+ index = i + 1;
+ }
+
+ lastIndex = index;
+ }
+ return index;
+ }
+
+ /**
+ * Returns the offset inside the document model that is after the last
+ * character of this element.
+ * This is the end offset of the last child element. If this element
+ * has no children, this method throws a <code>NullPointerException</code>.
+ *
+ * @return the offset inside the document model that is after the last
+ * character of this element
+ *
+ * @throws NullPointerException if this branch element has no children
+ */
+ public int getEndOffset()
+ {
+ // This might accss one cached element or trigger an NPE for
+ // numChildren == 0. This is checked by a Mauve test.
+ Element child = numChildren > 0 ? children[numChildren - 1]
+ : children[0];
+ return child.getEndOffset();
+ }
+
+ /**
+ * Returns the name of this element. This is {@link #ParagraphElementName}
+ * in this case.
+ *
+ * @return the name of this element
+ */
+ public String getName()
+ {
+ return ParagraphElementName;
+ }
+
+ /**
+ * Returns the start offset of this element inside the document model.
+ * This is the start offset of the first child element. If this element
+ * has no children, this method throws a <code>NullPointerException</code>.
+ *
+ * @return the start offset of this element inside the document model
+ *
+ * @throws NullPointerException if this branch element has no children and
+ * no startOffset value has been cached
+ */
+ public int getStartOffset()
+ {
+ // Do not explicitly throw an NPE here. If the first element is null
+ // then the NPE gets thrown anyway. If it isn't, then it either
+ // holds a real value (for numChildren > 0) or a cached value
+ // (for numChildren == 0) as we don't fully remove elements in replace()
+ // when removing single elements.
+ // This is checked by a Mauve test.
+ return children[0].getStartOffset();
+ }
+
+ /**
+ * Returns <code>false</code> since <code>BranchElement</code> are no
+ * leafes.
+ *
+ * @return <code>false</code> since <code>BranchElement</code> are no
+ * leafes
+ */
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ /**
+ * Returns the <code>Element</code> at the specified <code>Document</code>
+ * offset.
+ *
+ * @return the <code>Element</code> at the specified <code>Document</code>
+ * offset
+ *
+ * @see #getElementIndex(int)
+ */
+ public Element positionToElement(int position)
+ {
+ // XXX: There is surely a better algorithm
+ // as beginning from first element each time.
+ for (int index = 0; index < numChildren; ++index)
+ {
+ Element elem = children[index];
+
+ if ((elem.getStartOffset() <= position)
+ && (position < elem.getEndOffset()))
+ return elem;
+ }
+
+ return null;
+ }
+
+ /**
+ * Replaces a set of child elements with a new set of child elemens.
+ *
+ * @param offset the start index of the elements to be removed
+ * @param length the number of elements to be removed
+ * @param elements the new elements to be inserted
+ */
+ public void replace(int offset, int length, Element[] elements)
+ {
+ int delta = elements.length - length;
+ int copyFrom = offset + length; // From where to copy.
+ int copyTo = copyFrom + delta; // Where to copy to.
+ int numMove = numChildren - copyFrom; // How many elements are moved.
+ if (numChildren + delta > children.length)
+ {
+ // Gotta grow the array.
+ int newSize = Math.max(2 * children.length, numChildren + delta);
+ Element[] target = new Element[newSize];
+ System.arraycopy(children, 0, target, 0, offset);
+ System.arraycopy(elements, 0, target, offset, elements.length);
+ System.arraycopy(children, copyFrom, target, copyTo, numMove);
+ children = target;
+ }
+ else
+ {
+ System.arraycopy(children, copyFrom, children, copyTo, numMove);
+ System.arraycopy(elements, 0, children, offset, elements.length);
+ }
+ numChildren += delta;
+ }
+
+ /**
+ * Returns a string representation of this element.
+ *
+ * @return a string representation of this element
+ */
+ public String toString()
+ {
+ return ("BranchElement(" + getName() + ") "
+ + getStartOffset() + "," + getEndOffset() + "\n");
+ }
+ }
+
+ /**
+ * Stores the changes when a <code>Document</code> is beeing modified.
+ */
+ public class DefaultDocumentEvent extends CompoundEdit
+ implements DocumentEvent
+ {
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 5230037221564563284L;
+
+ /**
+ * The threshold that indicates when we switch to using a Hashtable.
+ */
+ private static final int THRESHOLD = 10;
+
+ /** The starting offset of the change. */
+ private int offset;
+
+ /** The length of the change. */
+ private int length;
+
+ /** The type of change. */
+ private DocumentEvent.EventType type;
+
+ /**
+ * Maps <code>Element</code> to their change records. This is only
+ * used when the changes array gets too big. We can use an
+ * (unsync'ed) HashMap here, since changes to this are (should) always
+ * be performed inside a write lock.
+ */
+ private HashMap changes;
+
+ /**
+ * Indicates if this event has been modified or not. This is used to
+ * determine if this event is thrown.
+ */
+ private boolean modified;
+
+ /**
+ * Creates a new <code>DefaultDocumentEvent</code>.
+ *
+ * @param offset the starting offset of the change
+ * @param length the length of the change
+ * @param type the type of change
+ */
+ public DefaultDocumentEvent(int offset, int length,
+ DocumentEvent.EventType type)
+ {
+ this.offset = offset;
+ this.length = length;
+ this.type = type;
+ modified = false;
+ }
+
+ /**
+ * Adds an UndoableEdit to this <code>DocumentEvent</code>. If this
+ * edit is an instance of {@link ElementEdit}, then this record can
+ * later be fetched by calling {@link #getChange}.
+ *
+ * @param edit the undoable edit to add
+ */
+ public boolean addEdit(UndoableEdit edit)
+ {
+ // Start using Hashtable when we pass a certain threshold. This
+ // gives a good memory/performance compromise.
+ if (changes == null && edits.size() > THRESHOLD)
+ {
+ changes = new HashMap();
+ int count = edits.size();
+ for (int i = 0; i < count; i++)
+ {
+ Object o = edits.elementAt(i);
+ if (o instanceof ElementChange)
+ {
+ ElementChange ec = (ElementChange) o;
+ changes.put(ec.getElement(), ec);
+ }
+ }
+ }
+
+ if (changes != null && edit instanceof ElementChange)
+ {
+ ElementChange elEdit = (ElementChange) edit;
+ changes.put(elEdit.getElement(), elEdit);
+ }
+ return super.addEdit(edit);
+ }
+
+ /**
+ * Returns the document that has been modified.
+ *
+ * @return the document that has been modified
+ */
+ public Document getDocument()
+ {
+ return AbstractDocument.this;
+ }
+
+ /**
+ * Returns the length of the modification.
+ *
+ * @return the length of the modification
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * Returns the start offset of the modification.
+ *
+ * @return the start offset of the modification
+ */
+ public int getOffset()
+ {
+ return offset;
+ }
+
+ /**
+ * Returns the type of the modification.
+ *
+ * @return the type of the modification
+ */
+ public DocumentEvent.EventType getType()
+ {
+ return type;
+ }
+
+ /**
+ * Returns the changes for an element.
+ *
+ * @param elem the element for which the changes are requested
+ *
+ * @return the changes for <code>elem</code> or <code>null</code> if
+ * <code>elem</code> has not been changed
+ */
+ public ElementChange getChange(Element elem)
+ {
+ ElementChange change = null;
+ if (changes != null)
+ {
+ change = (ElementChange) changes.get(elem);
+ }
+ else
+ {
+ int count = edits.size();
+ for (int i = 0; i < count && change == null; i++)
+ {
+ Object o = edits.get(i);
+ if (o instanceof ElementChange)
+ {
+ ElementChange ec = (ElementChange) o;
+ if (elem.equals(ec.getElement()))
+ change = ec;
+ }
+ }
+ }
+ return change;
+ }
+
+ /**
+ * Returns a String description of the change event. This returns the
+ * toString method of the Vector of edits.
+ */
+ public String toString()
+ {
+ return edits.toString();
+ }
+ }
+
+ /**
+ * An implementation of {@link DocumentEvent.ElementChange} to be added
+ * to {@link DefaultDocumentEvent}s.
+ */
+ public static class ElementEdit extends AbstractUndoableEdit
+ implements DocumentEvent.ElementChange
+ {
+ /** The serial version UID of ElementEdit. */
+ private static final long serialVersionUID = -1216620962142928304L;
+
+ /**
+ * The changed element.
+ */
+ private Element elem;
+
+ /**
+ * The index of the change.
+ */
+ private int index;
+
+ /**
+ * The removed elements.
+ */
+ private Element[] removed;
+
+ /**
+ * The added elements.
+ */
+ private Element[] added;
+
+ /**
+ * Creates a new <code>ElementEdit</code>.
+ *
+ * @param elem the changed element
+ * @param index the index of the change
+ * @param removed the removed elements
+ * @param added the added elements
+ */
+ public ElementEdit(Element elem, int index,
+ Element[] removed, Element[] added)
+ {
+ this.elem = elem;
+ this.index = index;
+ this.removed = removed;
+ this.added = added;
+ }
+
+ /**
+ * Returns the added elements.
+ *
+ * @return the added elements
+ */
+ public Element[] getChildrenAdded()
+ {
+ return added;
+ }
+
+ /**
+ * Returns the removed elements.
+ *
+ * @return the removed elements
+ */
+ public Element[] getChildrenRemoved()
+ {
+ return removed;
+ }
+
+ /**
+ * Returns the changed element.
+ *
+ * @return the changed element
+ */
+ public Element getElement()
+ {
+ return elem;
+ }
+
+ /**
+ * Returns the index of the change.
+ *
+ * @return the index of the change
+ */
+ public int getIndex()
+ {
+ return index;
+ }
+ }
+
+ /**
+ * An implementation of {@link Element} that represents a leaf in the
+ * document structure. This is used to actually store content.
+ */
+ public class LeafElement extends AbstractElement
+ {
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -8906306331347768017L;
+
+ /**
+ * Manages the start offset of this element.
+ */
+ private Position startPos;
+
+ /**
+ * Manages the end offset of this element.
+ */
+ private Position endPos;
+
+ /**
+ * Creates a new <code>LeafElement</code>.
+ *
+ * @param parent the parent of this <code>LeafElement</code>
+ * @param attributes the attributes to be set
+ * @param start the start index of this element inside the document model
+ * @param end the end index of this element inside the document model
+ */
+ public LeafElement(Element parent, AttributeSet attributes, int start,
+ int end)
+ {
+ super(parent, attributes);
+ try
+ {
+ startPos = createPosition(start);
+ endPos = createPosition(end);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError as;
+ as = new AssertionError("BadLocationException thrown "
+ + "here. start=" + start
+ + ", end=" + end
+ + ", length=" + getLength());
+ as.initCause(ex);
+ throw as;
+ }
+ }
+
+ /**
+ * Returns <code>null</code> since <code>LeafElement</code>s cannot have
+ * children.
+ *
+ * @return <code>null</code> since <code>LeafElement</code>s cannot have
+ * children
+ */
+ public Enumeration children()
+ {
+ return null;
+ }
+
+ /**
+ * Returns <code>false</code> since <code>LeafElement</code>s cannot have
+ * children.
+ *
+ * @return <code>false</code> since <code>LeafElement</code>s cannot have
+ * children
+ */
+ public boolean getAllowsChildren()
+ {
+ return false;
+ }
+
+ /**
+ * Returns <code>null</code> since <code>LeafElement</code>s cannot have
+ * children.
+ *
+ * @return <code>null</code> since <code>LeafElement</code>s cannot have
+ * children
+ */
+ public Element getElement(int index)
+ {
+ return null;
+ }
+
+ /**
+ * Returns <code>0</code> since <code>LeafElement</code>s cannot have
+ * children.
+ *
+ * @return <code>0</code> since <code>LeafElement</code>s cannot have
+ * children
+ */
+ public int getElementCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns <code>-1</code> since <code>LeafElement</code>s cannot have
+ * children.
+ *
+ * @return <code>-1</code> since <code>LeafElement</code>s cannot have
+ * children
+ */
+ public int getElementIndex(int offset)
+ {
+ return -1;
+ }
+
+ /**
+ * Returns the end offset of this <code>Element</code> inside the
+ * document.
+ *
+ * @return the end offset of this <code>Element</code> inside the
+ * document
+ */
+ public int getEndOffset()
+ {
+ return endPos.getOffset();
+ }
+
+ /**
+ * Returns the name of this <code>Element</code>. This is
+ * {@link #ContentElementName} in this case.
+ *
+ * @return the name of this <code>Element</code>
+ */
+ public String getName()
+ {
+ String name = super.getName();
+ if (name == null)
+ name = ContentElementName;
+ return name;
+ }
+
+ /**
+ * Returns the start offset of this <code>Element</code> inside the
+ * document.
+ *
+ * @return the start offset of this <code>Element</code> inside the
+ * document
+ */
+ public int getStartOffset()
+ {
+ return startPos.getOffset();
+ }
+
+ /**
+ * Returns <code>true</code>.
+ *
+ * @return <code>true</code>
+ */
+ public boolean isLeaf()
+ {
+ return true;
+ }
+
+ /**
+ * Returns a string representation of this <code>Element</code>.
+ *
+ * @return a string representation of this <code>Element</code>
+ */
+ public String toString()
+ {
+ return ("LeafElement(" + getName() + ") "
+ + getStartOffset() + "," + getEndOffset() + "\n");
+ }
+ }
+
+ /**
+ * The root element for bidirectional text.
+ */
+ private class BidiRootElement
+ extends BranchElement
+ {
+ /**
+ * Creates a new bidi root element.
+ */
+ BidiRootElement()
+ {
+ super(null, null);
+ }
+
+ /**
+ * Returns the name of the element.
+ *
+ * @return the name of the element
+ */
+ public String getName()
+ {
+ return BidiRootName;
+ }
+ }
+
+ /**
+ * A leaf element for the bidi structure.
+ */
+ private class BidiElement
+ extends LeafElement
+ {
+ /**
+ * Creates a new BidiElement.
+ *
+ * @param parent the parent element
+ * @param start the start offset
+ * @param end the end offset
+ * @param level the bidi level
+ */
+ BidiElement(Element parent, int start, int end, int level)
+ {
+ super(parent, new SimpleAttributeSet(), start, end);
+ addAttribute(StyleConstants.BidiLevel, new Integer(level));
+ }
+
+ /**
+ * Returns the name of the element.
+ *
+ * @return the name of the element
+ */
+ public String getName()
+ {
+ return BidiElementName;
+ }
+ }
+
+ /** A class whose methods delegate to the insert, remove and replace methods
+ * of this document which do not check for an installed DocumentFilter.
+ */
+ class Bypass extends DocumentFilter.FilterBypass
+ {
+
+ public Document getDocument()
+ {
+ return AbstractDocument.this;
+ }
+
+ public void insertString(int offset, String string, AttributeSet attr)
+ throws BadLocationException
+ {
+ AbstractDocument.this.insertStringImpl(offset, string, attr);
+ }
+
+ public void remove(int offset, int length)
+ throws BadLocationException
+ {
+ AbstractDocument.this.removeImpl(offset, length);
+ }
+
+ public void replace(int offset, int length, String string,
+ AttributeSet attrs)
+ throws BadLocationException
+ {
+ AbstractDocument.this.replaceImpl(offset, length, string, attrs);
+ }
+
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/AbstractWriter.java b/libjava/classpath/javax/swing/text/AbstractWriter.java
new file mode 100644
index 000000000..e7df26e9e
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/AbstractWriter.java
@@ -0,0 +1,481 @@
+/* AbstractWriter.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.IOException;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Enumeration;
+
+/**
+ * This is an abstract base class for writing Document instances to a
+ * Writer. A concrete subclass must implement a method to iterate
+ * over the Elements of the Document and correctly format them.
+ */
+public abstract class AbstractWriter
+{
+ /**
+ * The default line separator character.
+ * @specnote although this is a constant, it is not static in the JDK
+ */
+ protected static final char NEWLINE = '\n';
+
+ // Where we write.
+ private Writer writer;
+ // How we iterate over the document.
+ private ElementIterator iter;
+ // The document over which we iterate.
+ private Document document;
+ // Maximum number of characters per line.
+ private int maxLineLength = 100;
+ // Number of characters we have currently written.
+ private int lineLength;
+ // True if we can apply line wrapping.
+ private boolean canWrapLines; // FIXME default?
+ // The number of spaces per indentation level.
+ private int indentSpace = 2;
+ // The current indentation level.
+ private int indentLevel;
+ // True if we have indented this line.
+ private boolean indented;
+ // Starting offset in document.
+ private int startOffset;
+ // Ending offset in document.
+ private int endOffset;
+ // The line separator string.
+ private String lineSeparator = "" + NEWLINE;
+ // The characters making up the line separator.
+ private char[] lineSeparatorChars = lineSeparator.toCharArray();
+
+ /**
+ * Create a new AbstractWriter with the indicated Writer and
+ * Document. The full range of the Document will be used. The
+ * internal ElementIterator will be initialized with the Document's
+ * root node.
+ */
+ protected AbstractWriter(Writer writer, Document doc)
+ {
+ this.writer = writer;
+ this.iter = new ElementIterator(doc);
+ this.document = doc;
+ this.startOffset = 0;
+ this.endOffset = doc.getLength();
+ }
+
+ /**
+ * Create a new AbstractWriter with the indicated Writer and
+ * Document. The full range of the Document will be used. The
+ * internal ElementIterator will be initialized with the Document's
+ * root node.
+ */
+ protected AbstractWriter(Writer writer, Document doc, int pos, int len)
+ {
+ this.writer = writer;
+ this.iter = new ElementIterator(doc);
+ this.document = doc;
+ this.startOffset = pos;
+ this.endOffset = pos + len;
+ }
+
+ /**
+ * Create a new AbstractWriter with the indicated Writer and
+ * Element. The full range of the Element will be used.
+ */
+ protected AbstractWriter(Writer writer, Element elt)
+ {
+ this.writer = writer;
+ this.iter = new ElementIterator(elt);
+ this.document = elt.getDocument();
+ this.startOffset = elt.getStartOffset();
+ this.endOffset = elt.getEndOffset();
+ }
+
+ /**
+ * Create a new AbstractWriter with the indicated Writer and
+ * Element. The full range of the Element will be used. The range
+ * will be limited to the indicated range of the Document.
+ */
+ protected AbstractWriter(Writer writer, Element elt, int pos, int len)
+ {
+ this.writer = writer;
+ this.iter = new ElementIterator(elt);
+ this.document = elt.getDocument();
+ this.startOffset = pos;
+ this.endOffset = pos + len;
+ }
+
+ /**
+ * Return the ElementIterator for this writer.
+ */
+ protected ElementIterator getElementIterator()
+ {
+ return iter;
+ }
+
+ /**
+ * Return the Writer to which we are writing.
+ * @since 1.3
+ */
+ protected Writer getWriter()
+ {
+ return writer;
+ }
+
+ /**
+ * Return this writer's Document.
+ */
+ protected Document getDocument()
+ {
+ return document;
+ }
+
+ /**
+ * This method must be overridden by a concrete subclass. It is
+ * responsible for iterating over the Elements of the Document and
+ * writing them out.
+ */
+ protected abstract void write() throws IOException, BadLocationException;
+
+ /**
+ * Return the text of the Document that is associated with the given
+ * Element. If the Element is not a leaf Element, this will throw
+ * BadLocationException.
+ *
+ * @throws BadLocationException if the element is not a leaf
+ */
+ protected String getText(Element elt) throws BadLocationException
+ {
+ if (! elt.isLeaf())
+ throw new BadLocationException("Element is not a leaf",
+ elt.getStartOffset());
+ return document.getText(elt.getStartOffset(),
+ elt.getEndOffset() - elt.getStartOffset());
+ }
+
+ /**
+ * This method calls Writer.write on the indicated data, and updates
+ * the current line length. This method does not look for newlines
+ * in the written data; the caller is responsible for that.
+ *
+ * @since 1.3
+ */
+ protected void output(char[] data, int start, int len) throws IOException
+ {
+ writer.write(data, start, len);
+ lineLength += len;
+ }
+
+ /**
+ * Write a line separator using the output method, and then reset
+ * the current line length.
+ *
+ * @since 1.3
+ */
+ protected void writeLineSeparator() throws IOException
+ {
+ output(lineSeparatorChars, 0, lineSeparatorChars.length);
+ lineLength = 0;
+ indented = false;
+ }
+
+ /**
+ * Write a single character.
+ */
+ protected void write(char ch) throws IOException
+ {
+ write(new char[] { ch }, 0, 1);
+ }
+
+ /**
+ * Write a String.
+ */
+ protected void write(String s) throws IOException
+ {
+ char[] v = s.toCharArray();
+ write(v, 0, v.length);
+ }
+
+ /**
+ * Write a character array to the output Writer, properly handling
+ * newlines and, if needed, wrapping lines as they are output.
+ * @since 1.3
+ */
+ protected void write(char[] data, int start, int len) throws IOException
+ {
+ if (getCanWrapLines())
+ {
+ // FIXME: should we be handling newlines specially here?
+ for (int i = 0; i < len; )
+ {
+ int start_i = i;
+ // Find next space.
+ while (i < len && data[start + i] != ' ')
+ ++i;
+ if (i < len && lineLength + i - start_i >= maxLineLength)
+ writeLineSeparator();
+ else if (i < len)
+ {
+ // Write the trailing space.
+ ++i;
+ }
+ // Write out the text.
+ output(data, start + start_i, start + i - start_i);
+ }
+ }
+ else
+ {
+ int saved_i = start;
+ for (int i = start; i < start + len; ++i)
+ {
+ if (data[i] == NEWLINE)
+ {
+ output(data, saved_i, i - saved_i);
+ writeLineSeparator();
+ }
+ }
+ if (saved_i < start + len - 1)
+ output(data, saved_i, start + len - saved_i);
+ }
+ }
+
+ /**
+ * Indent this line by emitting spaces, according to the current
+ * indent level and the current number of spaces per indent. After
+ * this method is called, the current line is no longer considered
+ * to be empty, even if no spaces are actually written.
+ */
+ protected void indent() throws IOException
+ {
+ int spaces = indentLevel * indentSpace;
+ if (spaces > 0)
+ {
+ char[] v = new char[spaces];
+ Arrays.fill(v, ' ');
+ write(v, 0, v.length);
+ }
+ indented = true;
+ }
+
+ /**
+ * Return the index of the Document at which output starts.
+ * @since 1.3
+ */
+ public int getStartOffset()
+ {
+ return startOffset;
+ }
+
+ /**
+ * Return the index of the Document at which output ends.
+ * @since 1.3
+ */
+ public int getEndOffset()
+ {
+ return endOffset;
+ }
+
+ /**
+ * Return true if the Element's range overlaps our desired output
+ * range; false otherwise.
+ */
+ protected boolean inRange(Element elt)
+ {
+ int eltStart = elt.getStartOffset();
+ int eltEnd = elt.getEndOffset();
+ return ((eltStart >= startOffset && eltStart < endOffset)
+ || (eltEnd >= startOffset && eltEnd < endOffset));
+ }
+
+ /**
+ * Output the text of the indicated Element, properly clipping it to
+ * the range of the Document specified when the AbstractWriter was
+ * created.
+ */
+ protected void text(Element elt) throws BadLocationException, IOException
+ {
+ int eltStart = elt.getStartOffset();
+ int eltEnd = elt.getEndOffset();
+
+ eltStart = Math.max(eltStart, startOffset);
+ eltEnd = Math.min(eltEnd, endOffset);
+ write(document.getText(eltStart, eltEnd));
+ }
+
+ /**
+ * Set the maximum line length.
+ */
+ protected void setLineLength(int maxLineLength)
+ {
+ this.maxLineLength = maxLineLength;
+ }
+
+ /**
+ * Return the maximum line length.
+ * @since 1.3
+ */
+ protected int getLineLength()
+ {
+ return maxLineLength;
+ }
+
+ /**
+ * Set the current line length.
+ * @since 1.3
+ */
+ protected void setCurrentLineLength(int lineLength)
+ {
+ this.lineLength = lineLength;
+ }
+
+ /**
+ * Return the current line length.
+ * @since 1.3
+ */
+ protected int getCurrentLineLength()
+ {
+ return lineLength;
+ }
+
+ /**
+ * Return true if the line is empty, false otherwise. The line is
+ * empty if nothing has been written since the last newline, and
+ * indent has not been invoked.
+ */
+ protected boolean isLineEmpty()
+ {
+ return lineLength == 0 && ! indented;
+ }
+
+ /**
+ * Set the flag indicating whether lines will wrap. This affects
+ * the behavior of write().
+ * @since 1.3
+ */
+ protected void setCanWrapLines(boolean canWrapLines)
+ {
+ this.canWrapLines = canWrapLines;
+ }
+
+ /**
+ * Return true if lines printed via write() will wrap, false
+ * otherwise.
+ * @since 1.3
+ */
+ protected boolean getCanWrapLines()
+ {
+ return canWrapLines;
+ }
+
+ /**
+ * Set the number of spaces per indent level.
+ * @since 1.3
+ */
+ protected void setIndentSpace(int indentSpace)
+ {
+ this.indentSpace = indentSpace;
+ }
+
+ /**
+ * Return the number of spaces per indent level.
+ * @since 1.3
+ */
+ protected int getIndentSpace()
+ {
+ return indentSpace;
+ }
+
+ /**
+ * Set the current line separator.
+ * @since 1.3
+ */
+ public void setLineSeparator(String lineSeparator)
+ {
+ this.lineSeparator = lineSeparator;
+ this.lineSeparatorChars = lineSeparator.toCharArray();
+ }
+
+ /**
+ * Return the current line separator.
+ * @since 1.3
+ */
+ public String getLineSeparator()
+ {
+ return lineSeparator;
+ }
+
+ /**
+ * Increment the indent level.
+ */
+ protected void incrIndent()
+ {
+ ++indentLevel;
+ }
+
+ /**
+ * Decrement the indent level.
+ */
+ protected void decrIndent()
+ {
+ --indentLevel;
+ }
+
+ /**
+ * Return the current indent level.
+ * @since 1.3
+ */
+ protected int getIndentLevel()
+ {
+ return indentLevel;
+ }
+
+ /**
+ * Print the given AttributeSet as a sequence of assignment-like
+ * strings, e.g. "key=value".
+ */
+ protected void writeAttributes(AttributeSet attrs) throws IOException
+ {
+ Enumeration e = attrs.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ Object name = e.nextElement();
+ Object val = attrs.getAttribute(name);
+ write(name + "=" + val);
+ writeLineSeparator();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/AsyncBoxView.java b/libjava/classpath/javax/swing/text/AsyncBoxView.java
new file mode 100644
index 000000000..aca77aa3b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/AsyncBoxView.java
@@ -0,0 +1,1480 @@
+/* AsyncBoxView.java -- A box view that performs layout asynchronously
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Component;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.util.ArrayList;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.Position.Bias;
+
+/**
+ * A {@link View} implementation that lays out its child views in a box, either
+ * vertically or horizontally. The difference to {@link BoxView} is that the
+ * layout is performed in an asynchronous manner. This helps to keep the
+ * eventqueue free from non-GUI related tasks.
+ *
+ * This view is currently not used in standard text components. In order to
+ * use it you would have to implement a special {@link EditorKit} with a
+ * {@link ViewFactory} that returns this view. For example:
+ *
+ * <pre>
+ * static class AsyncEditorKit extends StyledEditorKit implements ViewFactory
+ * {
+ * public View create(Element el)
+ * {
+ * if (el.getName().equals(AbstractDocument.SectionElementName))
+ * return new AsyncBoxView(el, View.Y_AXIS);
+ * return super.getViewFactory().create(el);
+ * }
+ * public ViewFactory getViewFactory() {
+ * return this;
+ * }
+ * }
+ * </pre>
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.3
+ */
+public class AsyncBoxView
+ extends View
+{
+
+ /**
+ * Manages the effective position of child views. That keeps the visible
+ * layout stable while the AsyncBoxView might be changing until the layout
+ * thread decides to publish the new layout.
+ */
+ public class ChildLocator
+ {
+
+ /**
+ * The last valid location.
+ */
+ protected ChildState lastValidOffset;
+
+ /**
+ * The last allocation.
+ */
+ protected Rectangle lastAlloc;
+
+ /**
+ * A Rectangle used for child allocation calculation to avoid creation
+ * of lots of garbage Rectangle objects.
+ */
+ protected Rectangle childAlloc;
+
+ /**
+ * Creates a new ChildLocator.
+ */
+ public ChildLocator()
+ {
+ lastAlloc = new Rectangle();
+ childAlloc = new Rectangle();
+ }
+
+ /**
+ * Receives notification that a child has changed. This is called by
+ * child state objects that have changed it's major span.
+ *
+ * This sets the {@link #lastValidOffset} field to <code>cs</code> if
+ * the new child state's view start offset is smaller than the start offset
+ * of the current child state's view or when <code>lastValidOffset</code>
+ * is <code>null</code>.
+ *
+ * @param cs the child state object that has changed
+ */
+ public synchronized void childChanged(ChildState cs)
+ {
+ if (lastValidOffset == null
+ || cs.getChildView().getStartOffset()
+ < lastValidOffset.getChildView().getStartOffset())
+ {
+ lastValidOffset = cs;
+ }
+ }
+
+ /**
+ * Returns the view index of the view that occupies the specified area, or
+ * <code>-1</code> if there is no such child view.
+ *
+ * @param x the x coordinate (relative to <code>a</code>)
+ * @param y the y coordinate (relative to <code>a</code>)
+ * @param a the current allocation of this view
+ *
+ * @return the view index of the view that occupies the specified area, or
+ * <code>-1</code> if there is no such child view
+ */
+ public int getViewIndexAtPoint(float x, float y, Shape a)
+ {
+ setAllocation(a);
+ float targetOffset = (getMajorAxis() == X_AXIS) ? x - lastAlloc.x
+ : y - lastAlloc.y;
+ int index = getViewIndexAtVisualOffset(targetOffset);
+ return index;
+ }
+
+ /**
+ * Returns the current allocation for a child view. This updates the
+ * offsets for all children <em>before</em> the requested child view.
+ *
+ * @param index the index of the child view
+ * @param a the current allocation of this view
+ *
+ * @return the current allocation for a child view
+ */
+ public synchronized Shape getChildAllocation(int index, Shape a)
+ {
+ if (a == null)
+ return null;
+ setAllocation(a);
+ ChildState cs = getChildState(index);
+ if (cs.getChildView().getStartOffset()
+ > lastValidOffset.getChildView().getStartOffset())
+ {
+ updateChildOffsetsToIndex(index);
+ }
+ Shape ca = getChildAllocation(index);
+ return ca;
+ }
+
+ /**
+ * Paints all child views.
+ *
+ * @param g the graphics context to use
+ */
+ public synchronized void paintChildren(Graphics g)
+ {
+ Rectangle clip = g.getClipBounds();
+ float targetOffset = (getMajorAxis() == X_AXIS) ? clip.x - lastAlloc.x
+ : clip.y - lastAlloc.y;
+ int index = getViewIndexAtVisualOffset(targetOffset);
+ int n = getViewCount();
+ float offs = getChildState(index).getMajorOffset();
+ for (int i = index; i < n; i++)
+ {
+ ChildState cs = getChildState(i);
+ cs.setMajorOffset(offs);
+ Shape ca = getChildAllocation(i);
+ if (ca.intersects(clip))
+ {
+ synchronized (cs)
+ {
+ View v = cs.getChildView();
+ v.paint(g, ca);
+ }
+ }
+ else
+ {
+ // done painting intersection
+ break;
+ }
+ offs += cs.getMajorSpan();
+ }
+ }
+
+ /**
+ * Returns the current allocation of the child view with the specified
+ * index. Note that this will <em>not</em> update any location information.
+ *
+ * @param index the index of the requested child view
+ *
+ * @return the current allocation of the child view with the specified
+ * index
+ */
+ protected Shape getChildAllocation(int index)
+ {
+ ChildState cs = getChildState(index);
+ if (! cs.isLayoutValid())
+ cs.run();
+
+ if (getMajorAxis() == X_AXIS)
+ {
+ childAlloc.x = lastAlloc.x + (int) cs.getMajorOffset();
+ childAlloc.y = lastAlloc.y + (int) cs.getMinorOffset();
+ childAlloc.width = (int) cs.getMajorSpan();
+ childAlloc.height = (int) cs.getMinorSpan();
+ }
+ else
+ {
+ childAlloc.y = lastAlloc.y + (int) cs.getMajorOffset();
+ childAlloc.x = lastAlloc.x + (int) cs.getMinorOffset();
+ childAlloc.height = (int) cs.getMajorSpan();
+ childAlloc.width = (int) cs.getMinorSpan();
+ }
+ return childAlloc;
+ }
+
+ /**
+ * Sets the current allocation for this view.
+ *
+ * @param a the allocation to set
+ */
+ protected void setAllocation(Shape a)
+ {
+ if (a instanceof Rectangle)
+ lastAlloc.setBounds((Rectangle) a);
+ else
+ lastAlloc.setBounds(a.getBounds());
+
+ setSize(lastAlloc.width, lastAlloc.height);
+ }
+
+ /**
+ * Returns the index of the view at the specified offset along the major
+ * layout axis.
+ *
+ * @param targetOffset the requested offset
+ *
+ * @return the index of the view at the specified offset along the major
+ * layout axis
+ */
+ protected int getViewIndexAtVisualOffset(float targetOffset)
+ {
+ int n = getViewCount();
+ if (n > 0)
+ {
+ if (lastValidOffset == null)
+ lastValidOffset = getChildState(0);
+ if (targetOffset > majorSpan)
+ return 0;
+ else if (targetOffset > lastValidOffset.getMajorOffset())
+ return updateChildOffsets(targetOffset);
+ else
+ {
+ float offs = 0f;
+ for (int i = 0; i < n; i++)
+ {
+ ChildState cs = getChildState(i);
+ float nextOffs = offs + cs.getMajorSpan();
+ if (targetOffset < nextOffs)
+ return i;
+ offs = nextOffs;
+ }
+ }
+ }
+ return n - 1;
+ }
+
+ /**
+ * Updates all the child view offsets up to the specified targetOffset.
+ *
+ * @param targetOffset the offset up to which the child view offsets are
+ * updated
+ *
+ * @return the index of the view at the specified offset
+ */
+ private int updateChildOffsets(float targetOffset)
+ {
+ int n = getViewCount();
+ int targetIndex = n - 1;
+ int pos = lastValidOffset.getChildView().getStartOffset();
+ int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
+ float start = lastValidOffset.getMajorOffset();
+ float lastOffset = start;
+ for (int i = startIndex; i < n; i++)
+ {
+ ChildState cs = getChildState(i);
+ cs.setMajorOffset(lastOffset);
+ lastOffset += cs.getMajorSpan();
+ if (targetOffset < lastOffset)
+ {
+ targetIndex = i;
+ lastValidOffset = cs;
+ break;
+ }
+ }
+ return targetIndex;
+ }
+
+ /**
+ * Updates the offsets of the child views up to the specified index.
+ *
+ * @param index the index up to which the offsets are updated
+ */
+ private void updateChildOffsetsToIndex(int index)
+ {
+ int pos = lastValidOffset.getChildView().getStartOffset();
+ int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
+ float lastOffset = lastValidOffset.getMajorOffset();
+ for (int i = startIndex; i <= index; i++)
+ {
+ ChildState cs = getChildState(i);
+ cs.setMajorOffset(lastOffset);
+ lastOffset += cs.getMajorSpan();
+ }
+ }
+ }
+
+ /**
+ * Represents the layout state of a child view.
+ */
+ public class ChildState
+ implements Runnable
+ {
+
+ /**
+ * The child view for this state record.
+ */
+ private View childView;
+
+ /**
+ * Indicates if the minor axis requirements of this child view are valid
+ * or not.
+ */
+ private boolean minorValid;
+
+ /**
+ * Indicates if the major axis requirements of this child view are valid
+ * or not.
+ */
+ private boolean majorValid;
+
+ /**
+ * Indicates if the current child size is valid. This is package private
+ * to avoid synthetic accessor method.
+ */
+ boolean childSizeValid;
+
+ /**
+ * The child views minimumSpan. This is package private to avoid accessor
+ * method.
+ */
+ float minimum;
+
+ /**
+ * The child views preferredSpan. This is package private to avoid accessor
+ * method.
+ */
+ float preferred;
+
+ /**
+ * The current span of the child view along the major axis.
+ */
+ private float majorSpan;
+
+ /**
+ * The current offset of the child view along the major axis.
+ */
+ private float majorOffset;
+
+ /**
+ * The current span of the child view along the minor axis.
+ */
+ private float minorSpan;
+
+ /**
+ * The current offset of the child view along the major axis.
+ */
+ private float minorOffset;
+
+ /**
+ * The child views maximumSpan.
+ */
+ private float maximum;
+
+ /**
+ * Creates a new <code>ChildState</code> object for the specified child
+ * view.
+ *
+ * @param view the child view for which to create the state record
+ */
+ public ChildState(View view)
+ {
+ childView = view;
+ }
+
+ /**
+ * Returns the child view for which this <code>ChildState</code> represents
+ * the layout state.
+ *
+ * @return the child view for this child state object
+ */
+ public View getChildView()
+ {
+ return childView;
+ }
+
+ /**
+ * Returns <code>true</code> if the current layout information is valid,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the current layout information is valid,
+ * <code>false</code> otherwise
+ */
+ public boolean isLayoutValid()
+ {
+ return minorValid && majorValid && childSizeValid;
+ }
+
+ /**
+ * Performs the layout update for the child view managed by this
+ * <code>ChildState</code>.
+ */
+ public void run()
+ {
+ Document doc = getDocument();
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument abstractDoc = (AbstractDocument) doc;
+ abstractDoc.readLock();
+ }
+
+ try
+ {
+
+ if (!(minorValid && majorValid && childSizeValid)
+ && childView.getParent() == AsyncBoxView.this)
+ {
+ synchronized(AsyncBoxView.this)
+ {
+ changing = this;
+ }
+ update();
+ synchronized(AsyncBoxView.this)
+ {
+ changing = null;
+ }
+ // Changing the major axis may cause the minor axis
+ // requirements to have changed, so we need to do this again.
+ update();
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument abstractDoc = (AbstractDocument) doc;
+ abstractDoc.readUnlock();
+ }
+ }
+ }
+
+ /**
+ * Performs the actual update after the run methods has made its checks
+ * and locked the document.
+ */
+ private void update()
+ {
+ int majorAxis = getMajorAxis();
+ boolean minorUpdated = false;
+ synchronized (this)
+ {
+ if (! minorValid)
+ {
+ int minorAxis = getMinorAxis();
+ minimum = childView.getMinimumSpan(minorAxis);
+ preferred = childView.getPreferredSpan(minorAxis);
+ maximum = childView.getMaximumSpan(minorAxis);
+ minorValid = true;
+ minorUpdated = true;
+ }
+ }
+ if (minorUpdated)
+ minorRequirementChange(this);
+
+ boolean majorUpdated = false;
+ float delta = 0.0F;
+ synchronized (this)
+ {
+ if (! majorValid)
+ {
+ float oldSpan = majorSpan;
+ majorSpan = childView.getPreferredSpan(majorAxis);
+ delta = majorSpan - oldSpan;
+ majorValid = true;
+ majorUpdated = true;
+ }
+ }
+ if (majorUpdated)
+ {
+ majorRequirementChange(this, delta);
+ locator.childChanged(this);
+ }
+
+ synchronized (this)
+ {
+ if (! childSizeValid)
+ {
+ float w;
+ float h;
+ if (majorAxis == X_AXIS)
+ {
+ w = majorSpan;
+ h = getMinorSpan();
+ }
+ else
+ {
+ w = getMinorSpan();
+ h = majorSpan;
+ }
+ childSizeValid = true;
+ childView.setSize(w, h);
+ }
+ }
+ }
+
+ /**
+ * Returns the span of the child view along the minor layout axis.
+ *
+ * @return the span of the child view along the minor layout axis
+ */
+ public float getMinorSpan()
+ {
+ float retVal;
+ if (maximum < minorSpan)
+ retVal = maximum;
+ else
+ retVal = Math.max(minimum, minorSpan);
+ return retVal;
+ }
+
+ /**
+ * Returns the offset of the child view along the minor layout axis.
+ *
+ * @return the offset of the child view along the minor layout axis
+ */
+ public float getMinorOffset()
+ {
+ float retVal;
+ if (maximum < minorSpan)
+ {
+ float align = childView.getAlignment(getMinorAxis());
+ retVal = ((minorSpan - maximum) * align);
+ }
+ else
+ retVal = 0f;
+
+ return retVal;
+ }
+
+ /**
+ * Returns the span of the child view along the major layout axis.
+ *
+ * @return the span of the child view along the major layout axis
+ */
+
+ public float getMajorSpan()
+ {
+ return majorSpan;
+ }
+
+ /**
+ * Returns the offset of the child view along the major layout axis.
+ *
+ * @return the offset of the child view along the major layout axis
+ */
+ public float getMajorOffset()
+ {
+ return majorOffset;
+ }
+
+ /**
+ * Sets the offset of the child view along the major layout axis. This
+ * should only be called by the ChildLocator of that child view.
+ *
+ * @param offset the offset to set
+ */
+ public void setMajorOffset(float offset)
+ {
+ majorOffset = offset;
+ }
+
+ /**
+ * Mark the preferences changed for that child. This forwards to
+ * {@link AsyncBoxView#preferenceChanged}.
+ *
+ * @param width <code>true</code> if the width preference has changed
+ * @param height <code>true</code> if the height preference has changed
+ */
+ public void preferenceChanged(boolean width, boolean height)
+ {
+ if (getMajorAxis() == X_AXIS)
+ {
+ if (width)
+ majorValid = false;
+ if (height)
+ minorValid = false;
+ }
+ else
+ {
+ if (width)
+ minorValid = false;
+ if (height)
+ majorValid = false;
+ }
+ childSizeValid = false;
+ }
+ }
+
+ /**
+ * Flushes the requirements changes upwards asynchronously.
+ */
+ private class FlushTask implements Runnable
+ {
+ /**
+ * Starts the flush task. This obtains a readLock on the document
+ * and then flushes all the updates using
+ * {@link AsyncBoxView#flushRequirementChanges()} after updating the
+ * requirements.
+ */
+ public void run()
+ {
+ try
+ {
+ // Acquire a lock on the document.
+ Document doc = getDocument();
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument abstractDoc = (AbstractDocument) doc;
+ abstractDoc.readLock();
+ }
+
+ int n = getViewCount();
+ if (minorChanged && (n > 0))
+ {
+ LayoutQueue q = getLayoutQueue();
+ ChildState min = getChildState(0);
+ ChildState pref = getChildState(0);
+ for (int i = 1; i < n; i++)
+ {
+ ChildState cs = getChildState(i);
+ if (cs.minimum > min.minimum)
+ min = cs;
+ if (cs.preferred > pref.preferred)
+ pref = cs;
+ }
+ synchronized (AsyncBoxView.this)
+ {
+ minReq = min;
+ prefReq = pref;
+ }
+ }
+
+ flushRequirementChanges();
+ }
+ finally
+ {
+ // Release the lock on the document.
+ Document doc = getDocument();
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument abstractDoc = (AbstractDocument) doc;
+ abstractDoc.readUnlock();
+ }
+ }
+ }
+
+ }
+
+ /**
+ * The major layout axis.
+ */
+ private int majorAxis;
+
+ /**
+ * The top inset.
+ */
+ private float topInset;
+
+ /**
+ * The bottom inset.
+ */
+ private float bottomInset;
+
+ /**
+ * The left inset.
+ */
+ private float leftInset;
+
+ /**
+ * Indicates if the major span should be treated as beeing estimated or not.
+ */
+ private boolean estimatedMajorSpan;
+
+ /**
+ * The right inset.
+ */
+ private float rightInset;
+
+ /**
+ * The children and their layout statistics.
+ */
+ private ArrayList childStates;
+
+ /**
+ * The currently changing child state. May be null if there is no child state
+ * updating at the moment. This is package private to avoid a synthetic
+ * accessor method inside ChildState.
+ */
+ ChildState changing;
+
+ /**
+ * Represents the minimum requirements. This is used in
+ * {@link #getMinimumSpan(int)}.
+ */
+ ChildState minReq;
+
+ /**
+ * Represents the minimum requirements. This is used in
+ * {@link #getPreferredSpan(int)}.
+ */
+ ChildState prefReq;
+
+ /**
+ * Indicates that the major axis requirements have changed.
+ */
+ private boolean majorChanged;
+
+ /**
+ * Indicates that the minor axis requirements have changed. This is package
+ * private to avoid synthetic accessor method.
+ */
+ boolean minorChanged;
+
+ /**
+ * The current span along the major layout axis. This is package private to
+ * avoid synthetic accessor method.
+ */
+ float majorSpan;
+
+ /**
+ * The current span along the minor layout axis. This is package private to
+ * avoid synthetic accessor method.
+ */
+ float minorSpan;
+
+ /**
+ * This tasked is placed on the layout queue to flush updates up to the
+ * parent view.
+ */
+ private Runnable flushTask;
+
+ /**
+ * The child locator for this view.
+ */
+ protected ChildLocator locator;
+
+ /**
+ * Creates a new <code>AsyncBoxView</code> that represents the specified
+ * element and layouts its children along the specified axis.
+ *
+ * @param elem the element
+ * @param axis the layout axis
+ */
+ public AsyncBoxView(Element elem, int axis)
+ {
+ super(elem);
+ majorAxis = axis;
+ childStates = new ArrayList();
+ flushTask = new FlushTask();
+ locator = new ChildLocator();
+ minorSpan = Short.MAX_VALUE;
+ }
+
+ /**
+ * Returns the major layout axis.
+ *
+ * @return the major layout axis
+ */
+ public int getMajorAxis()
+ {
+ return majorAxis;
+ }
+
+ /**
+ * Returns the minor layout axis, that is the axis orthogonal to the major
+ * layout axis.
+ *
+ * @return the minor layout axis
+ */
+ public int getMinorAxis()
+ {
+ return majorAxis == X_AXIS ? Y_AXIS : X_AXIS;
+ }
+
+ /**
+ * Returns the view at the specified <code>index</code>.
+ *
+ * @param index the index of the requested child view
+ *
+ * @return the view at the specified <code>index</code>
+ */
+ public View getView(int index)
+ {
+ View view = null;
+ synchronized(childStates)
+ {
+ if ((index >= 0) && (index < childStates.size()))
+ {
+ ChildState cs = (ChildState) childStates.get(index);
+ view = cs.getChildView();
+ }
+ }
+ return view;
+ }
+
+ /**
+ * Returns the number of child views.
+ *
+ * @return the number of child views
+ */
+ public int getViewCount()
+ {
+ synchronized(childStates)
+ {
+ return childStates.size();
+ }
+ }
+
+ /**
+ * Returns the view index of the child view that represents the specified
+ * model position.
+ *
+ * @param pos the model position for which we search the view index
+ * @param bias the bias
+ *
+ * @return the view index of the child view that represents the specified
+ * model position
+ */
+ public int getViewIndex(int pos, Position.Bias bias)
+ {
+ int retVal = -1;
+
+ if (bias == Position.Bias.Backward)
+ pos = Math.max(0, pos - 1);
+
+ // TODO: A possible optimization would be to implement a binary search
+ // here.
+ int numChildren = childStates.size();
+ if (numChildren > 0)
+ {
+ for (int i = 0; i < numChildren; ++i)
+ {
+ View child = ((ChildState) childStates.get(i)).getChildView();
+ if (child.getStartOffset() <= pos && child.getEndOffset() > pos)
+ {
+ retVal = i;
+ break;
+ }
+ }
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the top inset.
+ *
+ * @return the top inset
+ */
+ public float getTopInset()
+ {
+ return topInset;
+ }
+
+ /**
+ * Sets the top inset.
+ *
+ * @param top the top inset
+ */
+ public void setTopInset(float top)
+ {
+ topInset = top;
+ }
+
+ /**
+ * Returns the bottom inset.
+ *
+ * @return the bottom inset
+ */
+ public float getBottomInset()
+ {
+ return bottomInset;
+ }
+
+ /**
+ * Sets the bottom inset.
+ *
+ * @param bottom the bottom inset
+ */
+ public void setBottomInset(float bottom)
+ {
+ bottomInset = bottom;
+ }
+
+ /**
+ * Returns the left inset.
+ *
+ * @return the left inset
+ */
+ public float getLeftInset()
+ {
+ return leftInset;
+ }
+
+ /**
+ * Sets the left inset.
+ *
+ * @param left the left inset
+ */
+ public void setLeftInset(float left)
+ {
+ leftInset = left;
+ }
+
+ /**
+ * Returns the right inset.
+ *
+ * @return the right inset
+ */
+ public float getRightInset()
+ {
+ return rightInset;
+ }
+
+ /**
+ * Sets the right inset.
+ *
+ * @param right the right inset
+ */
+ public void setRightInset(float right)
+ {
+ rightInset = right;
+ }
+
+ /**
+ * Loads the child views of this view. This is triggered by
+ * {@link #setParent(View)}.
+ *
+ * @param f the view factory to build child views with
+ */
+ protected void loadChildren(ViewFactory f)
+ {
+ Element e = getElement();
+ int n = e.getElementCount();
+ if (n > 0)
+ {
+ View[] added = new View[n];
+ for (int i = 0; i < n; i++)
+ {
+ added[i] = f.create(e.getElement(i));
+ }
+ replace(0, 0, added);
+ }
+ }
+
+ /**
+ * Returns the span along an axis that is taken up by the insets.
+ *
+ * @param axis the axis
+ *
+ * @return the span along an axis that is taken up by the insets
+ *
+ * @since 1.4
+ */
+ protected float getInsetSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = leftInset + rightInset;
+ else
+ span = topInset + bottomInset;
+ return span;
+ }
+
+ /**
+ * Sets the <code>estimatedMajorSpan</code> property that determines if
+ * the major span should be treated as beeing estimated.
+ *
+ * @param estimated if the major span should be treated as estimated or not
+ *
+ * @since 1.4
+ */
+ protected void setEstimatedMajorSpan(boolean estimated)
+ {
+ estimatedMajorSpan = estimated;
+ }
+
+ /**
+ * Determines whether the major span should be treated as estimated or as
+ * beeing accurate.
+ *
+ * @return <code>true</code> if the major span should be treated as
+ * estimated, <code>false</code> if the major span should be treated
+ * as accurate
+ *
+ * @since 1.4
+ */
+ protected boolean getEstimatedMajorSpan()
+ {
+ return estimatedMajorSpan;
+ }
+
+ /**
+ * Receives notification from the child states that the requirements along
+ * the minor axis have changed.
+ *
+ * @param cs the child state from which this notification is messaged
+ */
+ protected synchronized void minorRequirementChange(ChildState cs)
+ {
+ minorChanged = true;
+ }
+
+ /**
+ * Receives notification from the child states that the requirements along
+ * the major axis have changed.
+ *
+ * @param cs the child state from which this notification is messaged
+ */
+ protected void majorRequirementChange(ChildState cs, float delta)
+ {
+ if (! estimatedMajorSpan)
+ majorSpan += delta;
+ majorChanged = true;
+ }
+
+ /**
+ * Sets the parent for this view. This calls loadChildren if
+ * <code>parent</code> is not <code>null</code> and there have not been any
+ * child views initializes.
+ *
+ * @param parent the new parent view; <code>null</code> if this view is
+ * removed from the view hierarchy
+ *
+ * @see View#setParent(View)
+ */
+ public void setParent(View parent)
+ {
+ super.setParent(parent);
+ if ((parent != null) && (getViewCount() == 0))
+ {
+ ViewFactory f = getViewFactory();
+ loadChildren(f);
+ }
+ }
+
+ /**
+ * Sets the size of this view. This is ususally called before {@link #paint}
+ * is called to make sure the view has a valid layout.
+ *
+ * This implementation queues layout requests for every child view if the
+ * minor axis span has changed. (The major axis span is requested to never
+ * change for this view).
+ *
+ * @param width the width of the view
+ * @param height the height of the view
+ */
+ public void setSize(float width, float height)
+ {
+ float targetSpan;
+ if (majorAxis == X_AXIS)
+ targetSpan = height - getTopInset() - getBottomInset();
+ else
+ targetSpan = width - getLeftInset() - getRightInset();
+
+ if (targetSpan != minorSpan)
+ {
+ minorSpan = targetSpan;
+
+ int n = getViewCount();
+ LayoutQueue q = getLayoutQueue();
+ for (int i = 0; i < n; i++)
+ {
+ ChildState cs = getChildState(i);
+ cs.childSizeValid = false;
+ q.addTask(cs);
+ }
+ q.addTask(flushTask);
+ }
+ }
+
+ /**
+ * Replaces child views with new child views.
+ *
+ * This creates ChildState objects for all the new views and adds layout
+ * requests for them to the layout queue.
+ *
+ * @param offset the offset at which to remove/insert
+ * @param length the number of child views to remove
+ * @param views the new child views to insert
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ synchronized(childStates)
+ {
+ LayoutQueue q = getLayoutQueue();
+ for (int i = 0; i < length; i++)
+ childStates.remove(offset);
+
+ for (int i = views.length - 1; i >= 0; i--)
+ childStates.add(offset, createChildState(views[i]));
+
+ // We need to go through the new child states _after_ they have been
+ // added to the childStates list, otherwise the layout tasks may find
+ // an incomplete child list. That means we have to loop through
+ // them again, but what else can we do?
+ if (views.length != 0)
+ {
+ for (int i = 0; i < views.length; i++)
+ {
+ ChildState cs = (ChildState) childStates.get(i + offset);
+ cs.getChildView().setParent(this);
+ q.addTask(cs);
+ }
+ q.addTask(flushTask);
+ }
+ }
+ }
+
+ /**
+ * Paints the view. This requests the {@link ChildLocator} to paint the views
+ * after setting the allocation on it.
+ *
+ * @param g the graphics context to use
+ * @param s the allocation for this view
+ */
+ public void paint(Graphics g, Shape s)
+ {
+ synchronized (locator)
+ {
+ locator.setAllocation(s);
+ locator.paintChildren(g);
+ }
+ }
+
+ /**
+ * Returns the preferred span of this view along the specified layout axis.
+ *
+ * @return the preferred span of this view along the specified layout axis
+ */
+ public float getPreferredSpan(int axis)
+ {
+ float retVal;
+ if (majorAxis == axis)
+ retVal = majorSpan;
+
+ else if (prefReq != null)
+ {
+ View child = prefReq.getChildView();
+ retVal = child.getPreferredSpan(axis);
+ }
+
+ // If we have no layout information yet, then return insets + 30 as
+ // an estimation.
+ else
+ {
+ if (axis == X_AXIS)
+ retVal = getLeftInset() + getRightInset() + 30;
+ else
+ retVal = getTopInset() + getBottomInset() + 30;
+ }
+ return retVal;
+ }
+
+ /**
+ * Maps a model location to view coordinates.
+ *
+ * @param pos the model location
+ * @param a the current allocation of this view
+ * @param b the bias
+ *
+ * @return the view allocation for the specified model location
+ */
+ public Shape modelToView(int pos, Shape a, Bias b)
+ throws BadLocationException
+ {
+ int index = getViewIndexAtPosition(pos, b);
+ Shape ca = locator.getChildAllocation(index, a);
+
+ ChildState cs = getChildState(index);
+ synchronized (cs)
+ {
+ View cv = cs.getChildView();
+ Shape v = cv.modelToView(pos, ca, b);
+ return v;
+ }
+ }
+
+ /**
+ * Maps view coordinates to a model location.
+ *
+ * @param x the x coordinate (relative to <code>a</code>)
+ * @param y the y coordinate (relative to <code>a</code>)
+ * @param b holds the bias of the model location on method exit
+ *
+ * @return the model location for the specified view location
+ */
+ public int viewToModel(float x, float y, Shape a, Bias[] b)
+ {
+ int pos;
+ int index;
+ Shape ca;
+
+ synchronized (locator)
+ {
+ index = locator.getViewIndexAtPoint(x, y, a);
+ ca = locator.getChildAllocation(index, a);
+ }
+
+ ChildState cs = getChildState(index);
+ synchronized (cs)
+ {
+ View v = cs.getChildView();
+ pos = v.viewToModel(x, y, ca, b);
+ }
+ return pos;
+ }
+
+ /**
+ * Returns the child allocation for the child view with the specified
+ * <code>index</code>.
+ *
+ * @param index the index of the child view
+ * @param a the current allocation of this view
+ *
+ * @return the allocation of the child view
+ */
+ public Shape getChildAllocation(int index, Shape a)
+ {
+ Shape ca = locator.getChildAllocation(index, a);
+ return ca;
+ }
+
+ /**
+ * Returns the maximum span of this view along the specified axis.
+ * This is implemented to return the <code>preferredSpan</code> for the
+ * major axis (that means the box can't be resized along the major axis) and
+ * {@link Short#MAX_VALUE} for the minor axis.
+ *
+ * @param axis the axis
+ *
+ * @return the maximum span of this view along the specified axis
+ */
+ public float getMaximumSpan(int axis)
+ {
+ float max;
+ if (axis == majorAxis)
+ max = getPreferredSpan(axis);
+ else
+ max = Short.MAX_VALUE;
+ return max;
+ }
+
+ /**
+ * Returns the minimum span along the specified axis.
+ */
+ public float getMinimumSpan(int axis)
+ {
+ float min;
+ if (axis == majorAxis)
+ min = getPreferredSpan(axis);
+ else
+ {
+ if (minReq != null)
+ {
+ View child = minReq.getChildView();
+ min = child.getMinimumSpan(axis);
+ }
+ else
+ {
+ // No layout information yet. Return insets + 5 as some kind of
+ // estimation.
+ if (axis == X_AXIS)
+ min = getLeftInset() + getRightInset() + 5;
+ else
+ min = getTopInset() + getBottomInset() + 5;
+ }
+ }
+ return min;
+ }
+
+ /**
+ * Receives notification that one of the child views has changed its
+ * layout preferences along one or both axis.
+ *
+ * This queues a layout request for that child view if necessary.
+ *
+ * @param view the view that has changed its preferences
+ * @param width <code>true</code> if the width preference has changed
+ * @param height <code>true</code> if the height preference has changed
+ */
+ public synchronized void preferenceChanged(View view, boolean width,
+ boolean height)
+ {
+ if (view == null)
+ getParent().preferenceChanged(this, width, height);
+ else
+ {
+ if (changing != null)
+ {
+ View cv = changing.getChildView();
+ if (cv == view)
+ {
+ changing.preferenceChanged(width, height);
+ return;
+ }
+ }
+ int index = getViewIndexAtPosition(view.getStartOffset(),
+ Position.Bias.Forward);
+ ChildState cs = getChildState(index);
+ cs.preferenceChanged(width, height);
+ LayoutQueue q = getLayoutQueue();
+ q.addTask(cs);
+ q.addTask(flushTask);
+ }
+ }
+
+ /**
+ * Updates the layout for this view. This is implemented to trigger
+ * {@link ChildLocator#childChanged} for the changed view, if there is
+ * any.
+ *
+ * @param ec the element change, may be <code>null</code> if there were
+ * no changes to the element of this view
+ * @param e the document event
+ * @param a the current allocation of this view
+ */
+ protected void updateLayout(DocumentEvent.ElementChange ec,
+ DocumentEvent e, Shape a)
+ {
+ if (ec != null)
+ {
+ int index = Math.max(ec.getIndex() - 1, 0);
+ ChildState cs = getChildState(index);
+ locator.childChanged(cs);
+ }
+ }
+
+
+ /**
+ * Returns the <code>ChildState</code> object associated with the child view
+ * at the specified <code>index</code>.
+ *
+ * @param index the index of the child view for which to query the state
+ *
+ * @return the child state for the specified child view
+ */
+ protected ChildState getChildState(int index) {
+ synchronized (childStates)
+ {
+ return (ChildState) childStates.get(index);
+ }
+ }
+
+ /**
+ * Returns the <code>LayoutQueue</code> used for layouting the box view.
+ * This simply returns {@link LayoutQueue#getDefaultQueue()}.
+ *
+ * @return the <code>LayoutQueue</code> used for layouting the box view
+ */
+ protected LayoutQueue getLayoutQueue()
+ {
+ return LayoutQueue.getDefaultQueue();
+ }
+
+ /**
+ * Returns the child view index of the view that represents the specified
+ * position in the document model.
+ *
+ * @param pos the position in the model
+ * @param b the bias
+ *
+ * @return the child view index of the view that represents the specified
+ * position in the document model
+ */
+ protected synchronized int getViewIndexAtPosition(int pos, Position.Bias b)
+ {
+ if (b == Position.Bias.Backward)
+ pos = Math.max(0, pos - 1);
+ Element elem = getElement();
+ return elem.getElementIndex(pos);
+ }
+
+ /**
+ * Creates a <code>ChildState</code> object for the specified view.
+ *
+ * @param v the view for which to create a child state object
+ *
+ * @return the created child state
+ */
+ protected ChildState createChildState(View v)
+ {
+ return new ChildState(v);
+ }
+
+ /**
+ * Flushes the requirements changes upwards to the parent view. This is
+ * called from the layout thread.
+ */
+ protected synchronized void flushRequirementChanges()
+ {
+ if (majorChanged || minorChanged)
+ {
+ View p = getParent();
+ if (p != null)
+ {
+ boolean horizontal;
+ boolean vertical;
+ if (majorAxis == X_AXIS)
+ {
+ horizontal = majorChanged;
+ vertical = minorChanged;
+ }
+ else
+ {
+ vertical = majorChanged;
+ horizontal = minorChanged;
+ }
+
+ p.preferenceChanged(this, horizontal, vertical);
+ majorChanged = false;
+ minorChanged = false;
+
+ Component c = getContainer();
+ if (c != null)
+ c.repaint();
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/AttributeSet.java b/libjava/classpath/javax/swing/text/AttributeSet.java
new file mode 100644
index 000000000..a596cd4c3
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/AttributeSet.java
@@ -0,0 +1,195 @@
+/* AttributeSet.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.Enumeration;
+
+/**
+ * A set of attributes. An attribute has a key and a value. They typically
+ * describe features of a piece of text that make up its graphical
+ * representation.
+ *
+ * An <code>AttributeSet</code> may have a resolving parent,
+ * that is another <code>AttributeSet</code> that is searched for attribute
+ * keys that are not stored locally in this <code>AttributeSet</code>.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public interface AttributeSet
+{
+ /**
+ * Used as keys to identify character-run attributes.
+ */
+ static interface CharacterAttribute
+ {
+ // This interface is a marker interface and has no methods.
+ }
+
+ /**
+ * Used as keys to identify color attributes.
+ */
+ static interface ColorAttribute
+ {
+ // This interface is a marker interface and has no methods.
+ }
+
+ /**
+ * Used as keys to identify font attributes.
+ */
+ static interface FontAttribute
+ {
+ // This interface is a marker interface and has no methods.
+ }
+
+ /**
+ * Used as keys to identify paragraph level attributes.
+ */
+ static interface ParagraphAttribute
+ {
+ // This interface is a marker interface and has no methods.
+ }
+
+ /**
+ * Key of the attribute that is used to describe the name of an
+ * <code>AttributeSet</code>.
+ */
+ Object NameAttribute = StyleConstants.NameAttribute;
+
+ /**
+ * Key of the attribute that is used to identify the resolving parent of
+ * an <code>AttributeSet</code>.
+ */
+ Object ResolveAttribute = StyleConstants.ResolveAttribute;
+
+ /**
+ * Returns <code>true</code> if this <code>AttributeSet</code> contains
+ * an attribute with the specified <code>name</code> and <code>value</code>,
+ * <code>false</code> otherwise.
+ *
+ * @param name the name of the requested attribute
+ * @param value the value of the requested attribute
+ *
+ * @return <code>true</code> if this <code>AttributeSet</code> contains
+ * an attribute with the specified <code>name</code> and
+ * <code>value</code>, <code>false</code> otherwise
+ */
+ boolean containsAttribute(Object name, Object value);
+
+ /**
+ * Returns <code>true</code> of this <code>AttributeSet</code> contains all
+ * of the specified <code>attributes</code>.
+ *
+ * @param attributes the requested attributes
+ *
+ * @return <code>true</code> of this <code>AttributeSet</code> contains all
+ * of the specified <code>attributes</code>
+ */
+ boolean containsAttributes(AttributeSet attributes);
+
+ /**
+ * Creates and returns a copy of this <code>AttributeSet</code>.
+ *
+ * @return a copy of this <code>AttributeSet</code>
+ */
+ AttributeSet copyAttributes();
+
+ /**
+ * Returns the attribute with the specified <code>key</code> or
+ * <code>null</code> if no such attribute is defined in this
+ * <code>AttributeSet</code> and its resolving parents.
+ *
+ * @param key the key of the attribute that is looked up
+ *
+ * @return the attribute with the specified <code>key</code> or
+ * <code>null</code> if no such attribute is defined in this
+ * <code>AttributeSet</code> and its resolving parents
+ */
+ Object getAttribute(Object key);
+
+ /**
+ * Returns the number of attributes that are stored locally in this
+ * <code>AttributeSet</code>.
+ *
+ * @return the number of attributes that are stored locally in this
+ * <code>AttributeSet</code>
+ */
+ int getAttributeCount();
+
+ /**
+ * Returns the names of the attributes that are stored in this
+ * <code>AttributeSet</code>.
+ *
+ * @return the names of the attributes that are stored in this
+ * <code>AttributeSet</code>
+ */
+ Enumeration<?> getAttributeNames();
+
+ /**
+ * Returns the resolving parent of this <code>AttributeSet</code>.
+ * If a key is not stored locally, then a {@link #getAttribute(Object)}
+ * request is resolved up in the resolving parent of this
+ * <code>AttributeSet</code>.
+ *
+ * @return the resolving parent of this <code>AttributeSet</code>
+ */
+ AttributeSet getResolveParent();
+
+ /**
+ * Returns <code>true</code> if an attribute with the specified name is
+ * defined locally in this <code>AttributeSet</code>, without resolving
+ * through the resolving parents.
+ *
+ * @return <code>true</code> if an attribute with the specified name is
+ * defined locally in this <code>AttributeSet</code>
+ */
+ boolean isDefined(Object attrName);
+
+ /**
+ * Returns <code>true</code> if all of the attributes in <code>attr</code>
+ * are equal to the attributes in this <code>AttributeSet</code>,
+ * <code>false</code> otherwise.
+ *
+ * @param attr the attributes to be compared to <code>this</code>
+ *
+ * @return <code>true</code> if all of the attributes in <code>attr</code>
+ * are equal to the attributes in this <code>AttributeSet</code>,
+ * <code>false</code> otherwise
+ */
+ boolean isEqual(AttributeSet attr);
+}
diff --git a/libjava/classpath/javax/swing/text/BadLocationException.java b/libjava/classpath/javax/swing/text/BadLocationException.java
new file mode 100644
index 000000000..70591402c
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/BadLocationException.java
@@ -0,0 +1,78 @@
+/* BadLocationException.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Indicates that an invalid location within a <code>Document</code> has been
+ * accessed.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class BadLocationException extends Exception
+{
+ /** The serial version UID for BadLocationException. */
+ private static final long serialVersionUID = -7712259886815656766L;
+
+ /**
+ * The invalid location.
+ */
+ int offset;
+
+ /**
+ * Constructs a <code>BadLocationException</code>
+ *
+ * @param str a string indicating what was wrong with the arguments
+ * @param offset offset within the document that was requested &gt;= 0
+ */
+ public BadLocationException(String str, int offset)
+ {
+ super(str);
+ this.offset = offset;
+ }
+
+ /**
+ * Returns the offset into the document that was not legal.
+ *
+ * @return the offset into the document that was not legal
+ */
+ public int offsetRequested()
+ {
+ return offset;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/BoxView.java b/libjava/classpath/javax/swing/text/BoxView.java
new file mode 100644
index 000000000..325364d2b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/BoxView.java
@@ -0,0 +1,1112 @@
+/* BoxView.java -- An composite view
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Container;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+
+/**
+ * An implementation of {@link CompositeView} that arranges its children in
+ * a box along one axis. This is comparable to how the <code>BoxLayout</code>
+ * works, but for <code>View</code> children.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class BoxView
+ extends CompositeView
+{
+
+ /**
+ * The axis along which this <code>BoxView</code> is laid out.
+ */
+ private int myAxis;
+
+ /**
+ * Indicates if the layout is valid along X_AXIS or Y_AXIS.
+ */
+ private boolean[] layoutValid = new boolean[2];
+
+ /**
+ * Indicates if the requirements for an axis are valid.
+ */
+ private boolean[] requirementsValid = new boolean[2];
+
+ /**
+ * The spans along the X_AXIS and Y_AXIS.
+ */
+ private int[][] spans = new int[2][];
+
+ /**
+ * The offsets of the children along the X_AXIS and Y_AXIS.
+ */
+ private int[][] offsets = new int[2][];
+
+ /**
+ * The size requirements along the X_AXIS and Y_AXIS.
+ */
+ private SizeRequirements[] requirements = new SizeRequirements[2];
+
+ /**
+ * The current span along X_AXIS or Y_AXIS.
+ */
+ private int[] span = new int[2];
+
+ /**
+ * Creates a new <code>BoxView</code> for the given
+ * <code>Element</code> and axis. Valid values for the axis are
+ * {@link View#X_AXIS} and {@link View#Y_AXIS}.
+ *
+ * @param element the element that is rendered by this BoxView
+ * @param axis the axis along which the box is laid out
+ */
+ public BoxView(Element element, int axis)
+ {
+ super(element);
+ myAxis = axis;
+ layoutValid[0] = false;
+ layoutValid[1] = false;
+ requirementsValid[X_AXIS] = false;
+ requirementsValid[Y_AXIS] = false;
+ span[0] = 0;
+ span[1] = 0;
+ requirements[0] = new SizeRequirements();
+ requirements[1] = new SizeRequirements();
+
+ // Initialize the cache arrays.
+ spans[0] = new int[0];
+ spans[1] = new int[0];
+ offsets[0] = new int[0];
+ offsets[1] = new int[0];
+ }
+
+ /**
+ * Returns the axis along which this <code>BoxView</code> is laid out.
+ *
+ * @return the axis along which this <code>BoxView</code> is laid out
+ *
+ * @since 1.3
+ */
+ public int getAxis()
+ {
+ return myAxis;
+ }
+
+ /**
+ * Sets the axis along which this <code>BoxView</code> is laid out.
+ *
+ * Valid values for the axis are {@link View#X_AXIS} and
+ * {@link View#Y_AXIS}.
+ *
+ * @param axis the axis along which this <code>BoxView</code> is laid out
+ *
+ * @since 1.3
+ */
+ public void setAxis(int axis)
+ {
+ boolean changed = axis != myAxis;
+ myAxis = axis;
+ if (changed)
+ preferenceChanged(null, true, true);
+ }
+
+ /**
+ * Marks the layout along the specified axis as invalid. This is triggered
+ * automatically when any of the child view changes its preferences
+ * via {@link #preferenceChanged(View, boolean, boolean)}.
+ *
+ * The layout will be updated the next time when
+ * {@link #setSize(float, float)} is called, typically from within the
+ * {@link #paint(Graphics, Shape)} method.
+ *
+ * Valid values for the axis are {@link View#X_AXIS} and
+ * {@link View#Y_AXIS}.
+ *
+ * @param axis an <code>int</code> value
+ *
+ * @since 1.3
+ */
+ public void layoutChanged(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Invalid axis parameter.");
+ layoutValid[axis] = false;
+ }
+
+ /**
+ * Returns <code>true</code> if the layout along the specified
+ * <code>axis</code> is valid, <code>false</code> otherwise.
+ *
+ * Valid values for the axis are {@link View#X_AXIS} and
+ * {@link View#Y_AXIS}.
+ *
+ * @param axis the axis
+ *
+ * @return <code>true</code> if the layout along the specified
+ * <code>axis</code> is valid, <code>false</code> otherwise
+ *
+ * @since 1.4
+ */
+ protected boolean isLayoutValid(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Invalid axis parameter.");
+ return layoutValid[axis];
+ }
+
+ /**
+ * Paints the child <code>View</code> at the specified <code>index</code>.
+ * This method modifies the actual values in <code>alloc</code> so make
+ * sure you have a copy of the original values if you need them.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ * @param alloc the allocated region for the child to paint into
+ * @param index the index of the child to be painted
+ *
+ * @see #childAllocation(int, Rectangle)
+ */
+ protected void paintChild(Graphics g, Rectangle alloc, int index)
+ {
+ View child = getView(index);
+ child.paint(g, alloc);
+ }
+
+ /**
+ * Replaces child views by some other child views. If there are no views to
+ * remove (<code>length == 0</code>), the result is a simple insert, if
+ * there are no children to add (<code>view == null</code>) the result
+ * is a simple removal.
+ *
+ * In addition this invalidates the layout and resizes the internal cache
+ * for the child allocations. The old children's cached allocations can
+ * still be accessed (although they are not guaranteed to be valid), and
+ * the new children will have an initial offset and span of 0.
+ *
+ * @param offset the start offset from where to remove children
+ * @param length the number of children to remove
+ * @param views the views that replace the removed children
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ // Actually perform the replace.
+ super.replace(offset, length, views);
+
+ // Resize and copy data for cache arrays.
+ int newItems = views != null ? views.length : 0;
+ int minor = 1 - myAxis;
+ offsets[myAxis] = replaceLayoutArray(offsets[myAxis], offset, newItems);
+ spans[myAxis] = replaceLayoutArray(spans[myAxis], offset, newItems);
+ layoutValid[myAxis] = false;
+ requirementsValid[myAxis] = false;
+ offsets[minor] = replaceLayoutArray(offsets[minor], offset, newItems);
+ spans[minor] = replaceLayoutArray(spans[minor], offset, newItems);
+ layoutValid[minor] = false;
+ requirementsValid[minor] = false;
+ }
+
+ /**
+ * Helper method. This updates the layout cache arrays in response
+ * to a call to {@link #replace(int, int, View[])}.
+ *
+ * @param oldArray the old array
+ *
+ * @return the replaced array
+ */
+ private int[] replaceLayoutArray(int[] oldArray, int offset, int newItems)
+
+ {
+ int num = getViewCount();
+ int[] newArray = new int[num];
+ System.arraycopy(oldArray, 0, newArray, 0, offset);
+ System.arraycopy(oldArray, offset, newArray, offset + newItems,
+ num - newItems - offset);
+ return newArray;
+ }
+
+ /**
+ * A Rectangle instance to be reused in the paint() method below.
+ */
+ private final Rectangle tmpRect = new Rectangle();
+
+ private Rectangle clipRect = new Rectangle();
+
+ /**
+ * Renders the <code>Element</code> that is associated with this
+ * <code>View</code>.
+ *
+ * @param g the <code>Graphics</code> context to render to
+ * @param a the allocated region for the <code>Element</code>
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ // Try to avoid allocation if possible (almost all cases).
+ Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
+ // This returns a cached instance.
+ alloc = getInsideAllocation(alloc);
+
+ int count = getViewCount();
+ for (int i = 0; i < count; i++)
+ {
+ View child = getView(i);
+ tmpRect.setBounds(alloc);
+ childAllocation(i, tmpRect);
+ if (g.hitClip(tmpRect.x, tmpRect.y, tmpRect.width, tmpRect.height))
+ paintChild(g, tmpRect, i);
+ }
+ }
+
+ /**
+ * Returns the preferred span of the content managed by this
+ * <code>View</code> along the specified <code>axis</code>.
+ *
+ * @param axis the axis
+ *
+ * @return the preferred span of this <code>View</code>.
+ */
+ public float getPreferredSpan(int axis)
+ {
+ updateRequirements(axis);
+ // Add margin.
+ float margin;
+ if (axis == X_AXIS)
+ margin = getLeftInset() + getRightInset();
+ else
+ margin = getTopInset() + getBottomInset();
+ return requirements[axis].preferred + margin;
+ }
+
+ /**
+ * Returns the maximum span of this view along the specified axis.
+ * This returns <code>Integer.MAX_VALUE</code> for the minor axis
+ * and the preferred span for the major axis.
+ *
+ * @param axis the axis
+ *
+ * @return the maximum span of this view along the specified axis
+ */
+ public float getMaximumSpan(int axis)
+ {
+ updateRequirements(axis);
+ // Add margin.
+ float margin;
+ if (axis == X_AXIS)
+ margin = getLeftInset() + getRightInset();
+ else
+ margin = getTopInset() + getBottomInset();
+ return requirements[axis].maximum + margin;
+ }
+
+ /**
+ * Returns the minimum span of this view along the specified axis.
+ * This calculates the minimum span using
+ * {@link #calculateMajorAxisRequirements} or
+ * {@link #calculateMinorAxisRequirements} (depending on the axis) and
+ * returns the resulting minimum span.
+ *
+ * @param axis the axis
+ *
+ * @return the minimum span of this view along the specified axis
+ */
+ public float getMinimumSpan(int axis)
+ {
+ updateRequirements(axis);
+ // Add margin.
+ float margin;
+ if (axis == X_AXIS)
+ margin = getLeftInset() + getRightInset();
+ else
+ margin = getTopInset() + getBottomInset();
+ return requirements[axis].minimum + margin;
+ }
+
+ /**
+ * Calculates size requirements for a baseline layout. This is not
+ * used by the BoxView itself, but by subclasses that wish to perform
+ * a baseline layout, like the FlowView's rows.
+ *
+ * @param axis the axis that is examined
+ * @param sr the <code>SizeRequirements</code> object to hold the result,
+ * if <code>null</code>, a new one is created
+ *
+ * @return the size requirements for this <code>BoxView</code> along
+ * the specified axis
+ */
+ protected SizeRequirements baselineRequirements(int axis,
+ SizeRequirements sr)
+ {
+ // Create new instance if sr == null.
+ if (sr == null)
+ sr = new SizeRequirements();
+ sr.alignment = 0.5F;
+
+ // Calculate overall ascent and descent.
+ int totalAscentMin = 0;
+ int totalAscentPref = 0;
+ int totalAscentMax = 0;
+ int totalDescentMin = 0;
+ int totalDescentPref = 0;
+ int totalDescentMax = 0;
+
+ int count = getViewCount();
+ for (int i = 0; i < count; i++)
+ {
+ View v = getView(i);
+ float align = v.getAlignment(axis);
+ int span = (int) v.getPreferredSpan(axis);
+ int ascent = (int) (align * span);
+ int descent = span - ascent;
+
+ totalAscentPref = Math.max(ascent, totalAscentPref);
+ totalDescentPref = Math.max(descent, totalDescentPref);
+ if (v.getResizeWeight(axis) > 0)
+ {
+ // If the view is resizable, then use the min and max size
+ // of the view.
+ span = (int) v.getMinimumSpan(axis);
+ ascent = (int) (align * span);
+ descent = span - ascent;
+ totalAscentMin = Math.max(ascent, totalAscentMin);
+ totalDescentMin = Math.max(descent, totalDescentMin);
+
+ span = (int) v.getMaximumSpan(axis);
+ ascent = (int) (align * span);
+ descent = span - ascent;
+ totalAscentMax = Math.max(ascent, totalAscentMax);
+ totalDescentMax = Math.max(descent, totalDescentMax);
+ }
+ else
+ {
+ // If the view is not resizable, use the preferred span.
+ totalAscentMin = Math.max(ascent, totalAscentMin);
+ totalDescentMin = Math.max(descent, totalDescentMin);
+ totalAscentMax = Math.max(ascent, totalAscentMax);
+ totalDescentMax = Math.max(descent, totalDescentMax);
+ }
+ }
+
+ // Preferred overall span is the sum of the preferred ascent and descent.
+ // With overflow check.
+ sr.preferred = (int) Math.min((long) totalAscentPref
+ + (long) totalDescentPref,
+ Integer.MAX_VALUE);
+
+ // Align along the baseline.
+ if (sr.preferred > 0)
+ sr.alignment = (float) totalAscentPref / sr.preferred;
+
+ if (sr.alignment == 0)
+ {
+ // Nothing above the baseline, use the descent.
+ sr.minimum = totalDescentMin;
+ sr.maximum = totalDescentMax;
+ }
+ else if (sr.alignment == 1.0F)
+ {
+ // Nothing below the baseline, use the descent.
+ sr.minimum = totalAscentMin;
+ sr.maximum = totalAscentMax;
+ }
+ else
+ {
+ sr.minimum = Math.max((int) (totalAscentMin / sr.alignment),
+ (int) (totalDescentMin / (1.0F - sr.alignment)));
+ sr.maximum = Math.min((int) (totalAscentMax / sr.alignment),
+ (int) (totalDescentMax / (1.0F - sr.alignment)));
+ }
+ return sr;
+ }
+
+ /**
+ * Calculates the baseline layout of the children of this
+ * <code>BoxView</code> along the specified axis.
+ *
+ * This is not used by the BoxView itself, but by subclasses that wish to
+ * perform a baseline layout, like the FlowView's rows.
+ *
+ * @param span the target span
+ * @param axis the axis that is examined
+ * @param offsets an empty array, filled with the offsets of the children
+ * @param spans an empty array, filled with the spans of the children
+ */
+ protected void baselineLayout(int span, int axis, int[] offsets,
+ int[] spans)
+ {
+ int totalAscent = (int) (span * getAlignment(axis));
+ int totalDescent = span - totalAscent;
+
+ int count = getViewCount();
+ for (int i = 0; i < count; i++)
+ {
+ View v = getView(i);
+ float align = v.getAlignment(axis);
+ int viewSpan;
+ if (v.getResizeWeight(axis) > 0)
+ {
+ // If possible, then resize for best fit.
+ int min = (int) v.getMinimumSpan(axis);
+ int max = (int) v.getMaximumSpan(axis);
+ if (align == 0.0F)
+ viewSpan = Math.max(Math.min(max, totalDescent), min);
+ else if (align == 1.0F)
+ viewSpan = Math.max(Math.min(max, totalAscent), min);
+ else
+ {
+ int fit = (int) Math.min(totalAscent / align,
+ totalDescent / (1.0F - align));
+ viewSpan = Math.max(Math.min(max, fit), min);
+ }
+ }
+ else
+ viewSpan = (int) v.getPreferredSpan(axis);
+ offsets[i] = totalAscent - (int) (viewSpan * align);
+ spans[i] = viewSpan;
+ }
+ }
+
+ /**
+ * Calculates the size requirements of this <code>BoxView</code> along
+ * its major axis, that is the axis specified in the constructor.
+ *
+ * @param axis the axis that is examined
+ * @param sr the <code>SizeRequirements</code> object to hold the result,
+ * if <code>null</code>, a new one is created
+ *
+ * @return the size requirements for this <code>BoxView</code> along
+ * the specified axis
+ */
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements sr)
+ {
+ SizeRequirements res = sr;
+ if (res == null)
+ res = new SizeRequirements();
+
+ float min = 0;
+ float pref = 0;
+ float max = 0;
+
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View child = getView(i);
+ min += child.getMinimumSpan(axis);
+ pref += child.getPreferredSpan(axis);
+ max += child.getMaximumSpan(axis);
+ }
+
+ res.minimum = (int) min;
+ res.preferred = (int) pref;
+ res.maximum = (int) max;
+ res.alignment = 0.5F;
+
+ return res;
+ }
+
+ /**
+ * Calculates the size requirements of this <code>BoxView</code> along
+ * its minor axis, that is the axis opposite to the axis specified in the
+ * constructor.
+ *
+ * @param axis the axis that is examined
+ * @param sr the <code>SizeRequirements</code> object to hold the result,
+ * if <code>null</code>, a new one is created
+ *
+ * @return the size requirements for this <code>BoxView</code> along
+ * the specified axis
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements sr)
+ {
+ SizeRequirements res = sr;
+ if (res == null)
+ res = new SizeRequirements();
+
+ res.minimum = 0;
+ res.preferred = 0;
+ res.maximum = Integer.MAX_VALUE;
+ res.alignment = 0.5F;
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View child = getView(i);
+ res.minimum = Math.max((int) child.getMinimumSpan(axis), res.minimum);
+ res.preferred = Math.max((int) child.getPreferredSpan(axis),
+ res.preferred);
+ res.maximum = Math.max((int) child.getMaximumSpan(axis), res.maximum);
+ }
+
+ return res;
+ }
+
+
+ /**
+ * Returns <code>true</code> if the specified point lies before the
+ * given <code>Rectangle</code>, <code>false</code> otherwise.
+ *
+ * &quot;Before&quot; is typically defined as being to the left or above.
+ *
+ * @param x the X coordinate of the point
+ * @param y the Y coordinate of the point
+ * @param r the rectangle to test the point against
+ *
+ * @return <code>true</code> if the specified point lies before the
+ * given <code>Rectangle</code>, <code>false</code> otherwise
+ */
+ protected boolean isBefore(int x, int y, Rectangle r)
+ {
+ boolean result = false;
+
+ if (myAxis == X_AXIS)
+ result = x < r.x;
+ else
+ result = y < r.y;
+
+ return result;
+ }
+
+ /**
+ * Returns <code>true</code> if the specified point lies after the
+ * given <code>Rectangle</code>, <code>false</code> otherwise.
+ *
+ * &quot;After&quot; is typically defined as being to the right or below.
+ *
+ * @param x the X coordinate of the point
+ * @param y the Y coordinate of the point
+ * @param r the rectangle to test the point against
+ *
+ * @return <code>true</code> if the specified point lies after the
+ * given <code>Rectangle</code>, <code>false</code> otherwise
+ */
+ protected boolean isAfter(int x, int y, Rectangle r)
+ {
+ boolean result = false;
+
+ if (myAxis == X_AXIS)
+ result = x > r.x + r.width;
+ else
+ result = y > r.y + r.height;
+
+ return result;
+ }
+
+ /**
+ * Returns the child <code>View</code> at the specified location.
+ *
+ * @param x the X coordinate
+ * @param y the Y coordinate
+ * @param r the inner allocation of this <code>BoxView</code> on entry,
+ * the allocation of the found child on exit
+ *
+ * @return the child <code>View</code> at the specified location
+ */
+ protected View getViewAtPoint(int x, int y, Rectangle r)
+ {
+ View result = null;
+ int count = getViewCount();
+ if (myAxis == X_AXIS)
+ {
+ // Border case. Requested point is left from the box.
+ if (x < r.x + offsets[X_AXIS][0])
+ {
+ childAllocation(0, r);
+ result = getView(0);
+ }
+ else
+ {
+ // Search views inside box.
+ for (int i = 0; i < count && result == null; i++)
+ {
+ if (x < r.x + offsets[X_AXIS][i])
+ {
+ childAllocation(i - 1, r);
+ result = getView(i - 1);
+ }
+ }
+ }
+ }
+ else // Same algorithm for Y_AXIS.
+ {
+ // Border case. Requested point is above the box.
+ if (y < r.y + offsets[Y_AXIS][0])
+ {
+ childAllocation(0, r);
+ result = getView(0);
+ }
+ else
+ {
+ // Search views inside box.
+ for (int i = 0; i < count && result == null; i++)
+ {
+ if (y < r.y + offsets[Y_AXIS][i])
+ {
+ childAllocation(i - 1, r);
+ result = getView(i - 1);
+ }
+ }
+ }
+ }
+ // Not found, other border case: point is right from or below the box.
+ if (result == null)
+ {
+ childAllocation(count - 1, r);
+ result = getView(count - 1);
+ }
+ return result;
+ }
+
+ /**
+ * Computes the allocation for a child <code>View</code>. The parameter
+ * <code>a</code> stores the allocation of this <code>CompositeView</code>
+ * and is then adjusted to hold the allocation of the child view.
+ *
+ * @param index
+ * the index of the child <code>View</code>
+ * @param a
+ * the allocation of this <code>CompositeView</code> before the
+ * call, the allocation of the child on exit
+ */
+ protected void childAllocation(int index, Rectangle a)
+ {
+ a.x += offsets[X_AXIS][index];
+ a.y += offsets[Y_AXIS][index];
+ a.width = spans[X_AXIS][index];
+ a.height = spans[Y_AXIS][index];
+ }
+
+ /**
+ * Lays out the children of this <code>BoxView</code> with the specified
+ * bounds.
+ *
+ * @param width the width of the allocated region for the children (that
+ * is the inner allocation of this <code>BoxView</code>
+ * @param height the height of the allocated region for the children (that
+ * is the inner allocation of this <code>BoxView</code>
+ */
+ protected void layout(int width, int height)
+ {
+ layoutAxis(X_AXIS, width);
+ layoutAxis(Y_AXIS, height);
+ }
+
+ private void layoutAxis(int axis, int s)
+ {
+ if (span[axis] != s)
+ layoutValid[axis] = false;
+ if (! layoutValid[axis])
+ {
+ span[axis] = s;
+ updateRequirements(axis);
+ if (axis == myAxis)
+ layoutMajorAxis(span[axis], axis, offsets[axis], spans[axis]);
+ else
+ layoutMinorAxis(span[axis], axis, offsets[axis], spans[axis]);
+ layoutValid[axis] = true;
+
+ // Push out child layout.
+ int viewCount = getViewCount();
+ for (int i = 0; i < viewCount; i++)
+ {
+ View v = getView(i);
+ v.setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+ }
+ }
+ }
+
+ /**
+ * Performs the layout along the major axis of a <code>BoxView</code>.
+ *
+ * @param targetSpan the (inner) span of the <code>BoxView</code> in which
+ * to layout the children
+ * @param axis the axis along which the layout is performed
+ * @param offsets the array that holds the offsets of the children on exit
+ * @param spans the array that holds the spans of the children on exit
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ // Set the spans to the preferred sizes. Determine the space
+ // that we have to adjust the sizes afterwards.
+ long sumPref = 0;
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View child = getView(i);
+ spans[i] = (int) child.getPreferredSpan(axis);
+ sumPref += spans[i];
+ }
+
+ // Try to adjust the spans so that we fill the targetSpan.
+ long diff = targetSpan - sumPref;
+ float factor = 0.0F;
+ int[] diffs = null;
+ if (diff != 0)
+ {
+ long total = 0;
+ diffs = new int[n];
+ for (int i = 0; i < n; i++)
+ {
+ View child = getView(i);
+ int span;
+ if (diff < 0)
+ {
+ span = (int) child.getMinimumSpan(axis);
+ diffs[i] = spans[i] - span;
+ }
+ else
+ {
+ span = (int) child.getMaximumSpan(axis);
+ diffs[i] = span - spans[i];
+ }
+ total += span;
+ }
+
+ float maxAdjust = Math.abs(total - sumPref);
+ factor = diff / maxAdjust;
+ factor = Math.min(factor, 1.0F);
+ factor = Math.max(factor, -1.0F);
+ }
+
+ // Actually perform adjustments.
+ int totalOffs = 0;
+ for (int i = 0; i < n; i++)
+ {
+ offsets[i] = totalOffs;
+ if (diff != 0)
+ {
+ float adjust = factor * diffs[i];
+ spans[i] += Math.round(adjust);
+ }
+ // Avoid overflow here.
+ totalOffs = (int) Math.min((long) totalOffs + (long) spans[i],
+ Integer.MAX_VALUE);
+ }
+ }
+
+ /**
+ * Performs the layout along the minor axis of a <code>BoxView</code>.
+ *
+ * @param targetSpan the (inner) span of the <code>BoxView</code> in which
+ * to layout the children
+ * @param axis the axis along which the layout is performed
+ * @param offsets the array that holds the offsets of the children on exit
+ * @param spans the array that holds the spans of the children on exit
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ int count = getViewCount();
+ for (int i = 0; i < count; i++)
+ {
+ View child = getView(i);
+ int max = (int) child.getMaximumSpan(axis);
+ if (max < targetSpan)
+ {
+ // Align child when it can't be made as wide as the target span.
+ float align = child.getAlignment(axis);
+ offsets[i] = (int) ((targetSpan - max) * align);
+ spans[i] = max;
+ }
+ else
+ {
+ // Expand child to target width if possible.
+ int min = (int) child.getMinimumSpan(axis);
+ offsets[i] = 0;
+ spans[i] = Math.max(min, targetSpan);
+ }
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the cached allocations for the children
+ * are still valid, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the cached allocations for the children
+ * are still valid, <code>false</code> otherwise
+ */
+ protected boolean isAllocationValid()
+ {
+ return isLayoutValid(X_AXIS) && isLayoutValid(Y_AXIS);
+ }
+
+ /**
+ * Return the current width of the box. This is the last allocated width.
+ *
+ * @return the current width of the box
+ */
+ public int getWidth()
+ {
+ // The RI returns the following here, however, I'd think that is a bug.
+ // return span[X_AXIS] + getLeftInset() - getRightInset();
+ return span[X_AXIS] + getLeftInset() + getRightInset();
+ }
+
+ /**
+ * Return the current height of the box. This is the last allocated height.
+ *
+ * @return the current height of the box
+ */
+ public int getHeight()
+ {
+ // The RI returns the following here, however, I'd think that is a bug.
+ // return span[Y_AXIS] + getTopInset() - getBottomInset();
+ return span[Y_AXIS] + getTopInset() + getBottomInset();
+ }
+
+ /**
+ * Sets the size of the view. If the actual size has changed, the layout
+ * is updated accordingly.
+ *
+ * @param width the new width
+ * @param height the new height
+ */
+ public void setSize(float width, float height)
+ {
+ layout((int) (width - getLeftInset() - getRightInset()),
+ (int) (height - getTopInset() - getBottomInset()));
+ }
+
+ /**
+ * Returns the span for the child view with the given index for the specified
+ * axis.
+ *
+ * @param axis the axis to examine, either <code>X_AXIS</code> or
+ * <code>Y_AXIS</code>
+ * @param childIndex the index of the child for for which to return the span
+ *
+ * @return the span for the child view with the given index for the specified
+ * axis
+ */
+ protected int getSpan(int axis, int childIndex)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis argument");
+ return spans[axis][childIndex];
+ }
+
+ /**
+ * Returns the offset for the child view with the given index for the
+ * specified axis.
+ *
+ * @param axis the axis to examine, either <code>X_AXIS</code> or
+ * <code>Y_AXIS</code>
+ * @param childIndex the index of the child for for which to return the span
+ *
+ * @return the offset for the child view with the given index for the
+ * specified axis
+ */
+ protected int getOffset(int axis, int childIndex)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis argument");
+ return offsets[axis][childIndex];
+ }
+
+ /**
+ * Returns the alignment for this box view for the specified axis. The
+ * axis that is tiled (the major axis) will be requested to be aligned
+ * centered (0.5F). The minor axis alignment depends on the child view's
+ * total alignment.
+ *
+ * @param axis the axis which is examined
+ *
+ * @return the alignment for this box view for the specified axis
+ */
+ public float getAlignment(int axis)
+ {
+ updateRequirements(axis);
+ return requirements[axis].alignment;
+ }
+
+ /**
+ * Called by a child View when its preferred span has changed.
+ *
+ * @param width indicates that the preferred width of the child changed.
+ * @param height indicates that the preferred height of the child changed.
+ * @param child the child View.
+ */
+ public void preferenceChanged(View child, boolean width, boolean height)
+ {
+ if (width)
+ {
+ layoutValid[X_AXIS] = false;
+ requirementsValid[X_AXIS] = false;
+ }
+ if (height)
+ {
+ layoutValid[Y_AXIS] = false;
+ requirementsValid[Y_AXIS] = false;
+ }
+ super.preferenceChanged(child, width, height);
+ }
+
+ /**
+ * Maps the document model position <code>pos</code> to a Shape
+ * in the view coordinate space. This method overrides CompositeView's
+ * method to make sure the children are allocated properly before
+ * calling the super's behaviour.
+ */
+ public Shape modelToView(int pos, Shape a, Position.Bias bias)
+ throws BadLocationException
+ {
+ // Make sure everything is allocated properly and then call super
+ if (! isAllocationValid())
+ {
+ Rectangle bounds = a.getBounds();
+ setSize(bounds.width, bounds.height);
+ }
+ return super.modelToView(pos, a, bias);
+ }
+
+ /**
+ * Returns the resize weight of this view. A value of <code>0</code> or less
+ * means this view is not resizeable. Positive values make the view
+ * resizeable. This implementation returns <code>0</code> for the major
+ * axis and <code>1</code> for the minor axis of this box view.
+ *
+ * @param axis the axis
+ *
+ * @return the resizability of this view along the specified axis
+ *
+ * @throws IllegalArgumentException if <code>axis</code> is invalid
+ */
+ public int getResizeWeight(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis argument");
+ updateRequirements(axis);
+ int weight = 0;
+ if ((requirements[axis].preferred != requirements[axis].minimum)
+ || (requirements[axis].preferred != requirements[axis].maximum))
+ weight = 1;
+ return weight;
+ }
+
+ /**
+ * Returns the child allocation for the child view with the specified
+ * <code>index</code>. If the layout is invalid, this returns
+ * <code>null</code>.
+ *
+ * @param index the child view index
+ * @param a the allocation to this view
+ *
+ * @return the child allocation for the child view with the specified
+ * <code>index</code> or <code>null</code> if the layout is invalid
+ * or <code>a</code> is null
+ */
+ public Shape getChildAllocation(int index, Shape a)
+ {
+ Shape ret = null;
+ if (isAllocationValid() && a != null)
+ ret = super.getChildAllocation(index, a);
+ return ret;
+ }
+
+ protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e,
+ Shape a, ViewFactory vf)
+ {
+ boolean wasValid = isLayoutValid(myAxis);
+ super.forwardUpdate(ec, e, a, vf);
+ // Trigger repaint when one of the children changed the major axis.
+ if (wasValid && ! isLayoutValid(myAxis))
+ {
+ Container c = getContainer();
+ if (a != null && c != null)
+ {
+ int pos = e.getOffset();
+ int index = getViewIndexAtPosition(pos);
+ Rectangle r = getInsideAllocation(a);
+ if (myAxis == X_AXIS)
+ {
+ r.x += offsets[myAxis][index];
+ r.width -= offsets[myAxis][index];
+ }
+ else
+ {
+ r.y += offsets[myAxis][index];
+ r.height -= offsets[myAxis][index];
+ }
+ c.repaint(r.x, r.y, r.width, r.height);
+ }
+ }
+ }
+
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
+ {
+ if (! isAllocationValid())
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ setSize(r.width, r.height);
+ }
+ return super.viewToModel(x, y, a, bias);
+ }
+
+ protected boolean flipEastAndWestAtEnds(int position, Position.Bias bias)
+ {
+ // FIXME: What to do here?
+ return super.flipEastAndWestAtEnds(position, bias);
+ }
+
+ /**
+ * Updates the view's cached requirements along the specified axis if
+ * necessary. The requirements are only updated if the layout for the
+ * specified axis is marked as invalid.
+ *
+ * @param axis the axis
+ */
+ private void updateRequirements(int axis)
+ {
+ if (axis != Y_AXIS && axis != X_AXIS)
+ throw new IllegalArgumentException("Illegal axis: " + axis);
+ if (! requirementsValid[axis])
+ {
+ if (axis == myAxis)
+ requirements[axis] = calculateMajorAxisRequirements(axis,
+ requirements[axis]);
+ else
+ requirements[axis] = calculateMinorAxisRequirements(axis,
+ requirements[axis]);
+ requirementsValid[axis] = true;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/Caret.java b/libjava/classpath/javax/swing/text/Caret.java
new file mode 100644
index 000000000..06972f904
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Caret.java
@@ -0,0 +1,207 @@
+/* Caret.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Graphics;
+import java.awt.Point;
+
+import javax.swing.event.ChangeListener;
+
+/**
+ * Defines the method to be implemented by a caret that can be used in Swing
+ * text components.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public interface Caret
+{
+ /**
+ * Registers a {@link ChangeListener} that is notified whenever that state
+ * of this <code>Caret</code> changes.
+ *
+ * @param l the listener to register to this caret
+ */
+ void addChangeListener(ChangeListener l);
+
+ /**
+ * Removes a {@link ChangeListener} from the list of registered listeners.
+ *
+ * @param l the listener to remove
+ */
+ void removeChangeListener(ChangeListener l);
+
+ /**
+ * Installs this <code>Caret</code> on the specified text component. This
+ * usually involves setting up listeners.
+ *
+ * This method is called by {@link JTextComponent#setCaret(Caret)} after
+ * this caret has been set on the text component.
+ *
+ * @param c the text component to install this caret to
+ */
+ void install(JTextComponent c);
+
+ /**
+ * Deinstalls this <code>Caret</code> from the specified text component.
+ * This usually involves removing listeners from the text component.
+ *
+ * This method is called by {@link JTextComponent#setCaret(Caret)} before
+ * this caret is removed from the text component.
+ *
+ * @param c the text component to deinstall this caret from
+ */
+ void deinstall(JTextComponent c);
+
+ /**
+ * Returns the blink rate of this <code>Caret</code> in milliseconds.
+ * A value of <code>0</code> means that the caret does not blink.
+ *
+ * @return the blink rate of this <code>Caret</code> or <code>0</code> if
+ * this caret does not blink
+ */
+ int getBlinkRate();
+
+ /**
+ * Sets the blink rate of this <code>Caret</code> in milliseconds.
+ * A value of <code>0</code> means that the caret does not blink.
+ *
+ * @param rate the new blink rate to set
+ */
+ void setBlinkRate(int rate);
+
+ /**
+ * Returns the current position of this <code>Caret</code> within the
+ * <code>Document</code>.
+ *
+ * @return the current position of this <code>Caret</code> within the
+ * <code>Document</code>
+ */
+ int getDot();
+
+ /**
+ * Sets the current position of this <code>Caret</code> within the
+ * <code>Document</code>. This also sets the <code>mark</code> to the
+ * new location.
+ *
+ * @param dot the new position to be set
+ *
+ * @see #moveDot(int)
+ */
+ void setDot(int dot);
+
+ /**
+ * Moves the <code>dot</code> location without touching the
+ * <code>mark</code>. This is used when making a selection.
+ *
+ * @param dot the location where to move the dot
+ *
+ * @see #setDot(int)
+ */
+ void moveDot(int dot);
+
+ /**
+ * Returns the current position of the <code>mark</code>. The
+ * <code>mark</code> marks the location in the <code>Document</code> that
+ * is the end of a selection. If there is no selection, the <code>mark</code>
+ * is the same as the <code>dot</code>.
+ *
+ * @return the current position of the mark
+ */
+ int getMark();
+
+ /**
+ * Returns the current visual position of this <code>Caret</code>.
+ *
+ * @return the current visual position of this <code>Caret</code>
+ *
+ * @see #setMagicCaretPosition
+ */
+ Point getMagicCaretPosition();
+
+ /**
+ * Sets the current visual position of this <code>Caret</code>.
+ *
+ * @param p the Point to use for the saved location. May be <code>null</code>
+ * to indicate that there is no visual location
+ */
+ void setMagicCaretPosition(Point p);
+
+ /**
+ * Returns <code>true</code> if the selection is currently visible,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the selection is currently visible,
+ * <code>false</code> otherwise
+ */
+ boolean isSelectionVisible();
+
+ /**
+ * Sets the visiblity state of the selection.
+ *
+ * @param v <code>true</code> if the selection should be visible,
+ * <code>false</code> otherwise
+ */
+ void setSelectionVisible(boolean v);
+
+ /**
+ * Returns <code>true</code> if this <code>Caret</code> is currently visible,
+ * and <code>false</code> if it is not.
+ *
+ * @return <code>true</code> if this <code>Caret</code> is currently visible,
+ * and <code>false</code> if it is not
+ */
+ boolean isVisible();
+
+ /**
+ * Sets the visibility state of the caret. <code>true</code> shows the
+ * <code>Caret</code>, <code>false</code> hides it.
+ *
+ * @param v the visibility to set
+ */
+ void setVisible(boolean v);
+
+ /**
+ * Paints this <code>Caret</code> to the specified <code>Graphics</code>
+ * context.
+ *
+ * @param g the graphics context to render to
+ */
+ void paint(Graphics g);
+}
diff --git a/libjava/classpath/javax/swing/text/ChangedCharSetException.java b/libjava/classpath/javax/swing/text/ChangedCharSetException.java
new file mode 100644
index 000000000..7fba29a30
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/ChangedCharSetException.java
@@ -0,0 +1,100 @@
+/* ChangedCharSetException.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.IOException;
+import java.io.Serializable;
+
+/**
+ * The exception is thrown when the document charset is changed.
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+public class ChangedCharSetException
+ extends IOException
+ implements Serializable
+{
+ /**
+ * Use serialVersionUID for interoperability.
+ * This value corresponds the version 1.4.
+ */
+ private static final long serialVersionUID = 9119851554465432389L;
+
+ /**
+ * The char set specification.
+ */
+ private final String m_charSetSpec;
+
+ /**
+ * The char set key.
+ */
+ private final boolean m_charSetKey;
+
+ /**
+ * Constructs a new char set exception with two additional parameters,
+ * defining the circumstances under that the exception was raised.
+ */
+ public ChangedCharSetException(String charSetSpec, boolean charSetKey)
+ {
+ m_charSetSpec = charSetSpec;
+ m_charSetKey = charSetKey;
+ }
+
+ /**
+ * Get the value of the first parameter, previously passed to the
+ * constructor.
+ *
+ * @return the value of the first parameter
+ */
+ public String getCharSetSpec()
+ {
+ return m_charSetSpec;
+ }
+
+ /**
+ * Get the value of the second parameter, previously passed to the
+ * constructor.
+ *
+ * @return the value of the second parameter
+ */
+ public boolean keyEqualsCharSet()
+ {
+ return m_charSetKey;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/ComponentView.java b/libjava/classpath/javax/swing/text/ComponentView.java
new file mode 100644
index 000000000..3680b4245
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/ComponentView.java
@@ -0,0 +1,494 @@
+/* ComponentView.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SwingUtilities;
+
+/**
+ * A {@link View} implementation that is able to render arbitrary
+ * {@link Component}s. This uses the attribute
+ * {@link StyleConstants#ComponentAttribute} to determine the
+ * <code>Component</code> that should be rendered. This <code>Component</code>
+ * becomes a direct child of the <code>JTextComponent</code> that contains
+ * this <code>ComponentView</code>, so this view must not be shared between
+ * multiple <code>JTextComponent</code>s.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author original author unknown
+ */
+public class ComponentView extends View
+{
+
+ /**
+ * A special container that sits between the component and the hosting
+ * container. This is used to propagate invalidate requests and cache
+ * the component's layout sizes.
+ */
+ private class Interceptor
+ extends Container
+ {
+ Dimension min;
+ Dimension pref;
+ Dimension max;
+ float alignX;
+ float alignY;
+
+ /**
+ * Creates a new instance that hosts the specified component.
+ */
+ Interceptor(Component c)
+ {
+ setLayout(null);
+ add(c);
+ cacheComponentSizes();
+ }
+
+ /**
+ * Intercepts the normal invalidate call and propagates the invalidate
+ * request up using the View's preferenceChanged().
+ */
+ public void invalidate()
+ {
+ super.invalidate();
+ if (getParent() != null)
+ preferenceChanged(null, true, true);
+ }
+
+ /**
+ * This is overridden to simply cache the layout sizes.
+ */
+ public void doLayout()
+ {
+ cacheComponentSizes();
+ }
+
+ /**
+ * Overridden to also reshape the component itself.
+ */
+ public void reshape(int x, int y, int w, int h)
+ {
+ super.reshape(x, y, w, h);
+ if (getComponentCount() > 0)
+ getComponent(0).setSize(w, h);
+ cacheComponentSizes();
+ }
+
+ /**
+ * Overridden to also show the component.
+ */
+ public void show()
+ {
+ super.show();
+ if (getComponentCount() > 0)
+ getComponent(0).setVisible(true);
+ }
+
+ /**
+ * Overridden to also hide the component.
+ */
+ public void hide()
+ {
+ super.hide();
+ if (getComponentCount() > 0)
+ getComponent(0).setVisible(false);
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public Dimension getMinimumSize()
+ {
+ maybeValidate();
+ return min;
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public Dimension getPreferredSize()
+ {
+ maybeValidate();
+ return pref;
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public Dimension getMaximumSize()
+ {
+ maybeValidate();
+ return max;
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public float getAlignmentX()
+ {
+ maybeValidate();
+ return alignX;
+ }
+
+ /**
+ * Overridden to return the cached value.
+ */
+ public float getAlignmentY()
+ {
+ maybeValidate();
+ return alignY;
+ }
+
+ /**
+ * Validates the container only when necessary.
+ */
+ private void maybeValidate()
+ {
+ if (! isValid())
+ validate();
+ }
+
+ /**
+ * Fetches the component layout sizes into the cache.
+ */
+ private void cacheComponentSizes()
+ {
+ if (getComponentCount() > 0)
+ {
+ Component c = getComponent(0);
+ min = c.getMinimumSize();
+ pref = c.getPreferredSize();
+ max = c.getMaximumSize();
+ alignX = c.getAlignmentX();
+ alignY = c.getAlignmentY();
+ }
+ }
+ }
+
+ /**
+ * The component that is displayed by this view.
+ */
+ private Component comp;
+
+ /**
+ * The intercepting container.
+ */
+ private Interceptor interceptor;
+
+ /**
+ * Creates a new instance of <code>ComponentView</code> for the specified
+ * <code>Element</code>.
+ *
+ * @param elem the element that this <code>View</code> is rendering
+ */
+ public ComponentView(Element elem)
+ {
+ super(elem);
+ }
+
+ /**
+ * Creates the <code>Component</code> that this <code>View</code> is
+ * rendering. The <code>Component</code> is determined using
+ * the {@link StyleConstants#ComponentAttribute} of the associated
+ * <code>Element</code>.
+ *
+ * @return the component that is rendered
+ */
+ protected Component createComponent()
+ {
+ return StyleConstants.getComponent(getElement().getAttributes());
+ }
+
+ /**
+ * Returns the alignment of this <code>View</code> along the specified axis.
+ *
+ * @param axis either {@link View#X_AXIS} or {@link View#Y_AXIS}
+ *
+ * @return the alignment of this <code>View</code> along the specified axis
+ */
+ public float getAlignment(int axis)
+ {
+ float align = 0.0F;
+ // I'd rather throw an IllegalArgumentException for illegal axis,
+ // but the Harmony testsuite indicates fallback to super behaviour.
+ if (interceptor != null && (axis == X_AXIS || axis == Y_AXIS))
+ {
+ if (axis == X_AXIS)
+ align = interceptor.getAlignmentX();
+ else if (axis == Y_AXIS)
+ align = interceptor.getAlignmentY();
+ else
+ assert false : "Must not reach here";
+ }
+ else
+ align = super.getAlignment(axis);
+ return align;
+ }
+
+ /**
+ * Returns the <code>Component</code> that is rendered by this
+ * <code>ComponentView</code>.
+ *
+ * @return the <code>Component</code> that is rendered by this
+ * <code>ComponentView</code>
+ */
+ public final Component getComponent()
+ {
+ return comp;
+ }
+
+ /**
+ * Returns the maximum span of this <code>View</code> along the specified
+ * axis.
+ *
+ * This will return {@link Component#getMaximumSize()} for the specified
+ * axis.
+ *
+ * @return the maximum span of this <code>View</code> along the specified
+ * axis
+ */
+ public float getMaximumSpan(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis");
+ float span = 0;
+ if (interceptor != null)
+ {
+ if (axis == X_AXIS)
+ span = interceptor.getMaximumSize().width;
+ else if (axis == Y_AXIS)
+ span = interceptor.getMaximumSize().height;
+ else
+ assert false : "Must not reach here";
+ }
+ return span;
+ }
+
+ public float getMinimumSpan(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis");
+ float span = 0;
+ if (interceptor != null)
+ {
+ if (axis == X_AXIS)
+ span = interceptor.getMinimumSize().width;
+ else if (axis == Y_AXIS)
+ span = interceptor.getMinimumSize().height;
+ else
+ assert false : "Must not reach here";
+ }
+ return span;
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis");
+ float span = 0;
+ if (interceptor != null)
+ {
+ if (axis == X_AXIS)
+ span = interceptor.getPreferredSize().width;
+ else if (axis == Y_AXIS)
+ span = interceptor.getPreferredSize().height;
+ else
+ assert false : "Must not reach here";
+ }
+ return span;
+ }
+
+ public Shape modelToView(int pos, Shape a, Position.Bias b)
+ throws BadLocationException
+ {
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+ if (pos >= p0 && pos <= p1)
+ {
+ Rectangle viewRect = a.getBounds();
+ if (pos == p1)
+ viewRect.x += viewRect.width;
+ viewRect.width = 0;
+ return viewRect;
+ }
+ else
+ throw new BadLocationException("Illegal position", pos);
+ }
+
+ /**
+ * The real painting behavour is performed by normal component painting,
+ * triggered by the text component that hosts this view. This method does
+ * not paint by itself. However, it sets the size of the component according
+ * to the allocation that is passed here.
+ *
+ * @param g the graphics context
+ * @param a the allocation of the child
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ if (interceptor != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ interceptor.setBounds(r.x, r.y, r.width, r.height);
+ }
+ }
+
+ /**
+ * This sets up the component when the view is added to its parent, or
+ * cleans up the view when it is removed from its parent.
+ *
+ * When this view is added to a parent view, the component of this view
+ * is added to the container that hosts this view. When <code>p</code> is
+ * <code>null</code>, then the view is removed from it's parent and we have
+ * to also remove the component from it's parent container.
+ *
+ * @param p the parent view or <code>null</code> if this view is removed
+ * from it's parent
+ */
+ public void setParent(final View p)
+ {
+ super.setParent(p);
+ if (SwingUtilities.isEventDispatchThread())
+ setParentImpl();
+ else
+ SwingUtilities.invokeLater
+ (new Runnable()
+ {
+ public void run()
+ {
+ Document doc = getDocument();
+ try
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ setParentImpl();
+ Container host = getContainer();
+ if (host != null)
+ {
+ preferenceChanged(null, true, true);
+ host.repaint();
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ }
+ });
+ }
+
+ /**
+ * The implementation of {@link #setParent}. This is package private to
+ * avoid a synthetic accessor method.
+ */
+ void setParentImpl()
+ {
+ View p = getParent();
+ if (p != null)
+ {
+ Container c = getContainer();
+ if (c != null)
+ {
+ if (interceptor == null)
+ {
+ // Create component and put it inside the interceptor.
+ Component created = createComponent();
+ if (created != null)
+ {
+ comp = created;
+ interceptor = new Interceptor(comp);
+ }
+ }
+ if (interceptor != null)
+ {
+ // Add the interceptor to the hosting container.
+ if (interceptor.getParent() == null)
+ c.add(interceptor, this);
+ }
+ }
+ }
+ else
+ {
+ if (interceptor != null)
+ {
+ Container parent = interceptor.getParent();
+ if (parent != null)
+ parent.remove(interceptor);
+ }
+ }
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ int pos;
+ // I'd rather do the following. The harmony testsuite indicates
+ // that a simple cast is performed.
+ //Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ Rectangle r = (Rectangle) a;
+ if (x < r.x + r.width / 2)
+ {
+ b[0] = Position.Bias.Forward;
+ pos = getStartOffset();
+ }
+ else
+ {
+ b[0] = Position.Bias.Backward;
+ pos = getEndOffset();
+ }
+ return pos;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java
new file mode 100644
index 000000000..ae3c79d2f
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/CompositeView.java
@@ -0,0 +1,792 @@
+/* CompositeView.java -- An abstract view that manages child views
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SwingConstants;
+
+/**
+ * An abstract base implementation of {@link View} that manages child
+ * <code>View</code>s.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public abstract class CompositeView
+ extends View
+{
+
+ /**
+ * The child views of this <code>CompositeView</code>.
+ */
+ private View[] children;
+
+ /**
+ * The number of child views.
+ */
+ private int numChildren;
+
+ /**
+ * The allocation of this <code>View</code> minus its insets. This is
+ * initialized in {@link #getInsideAllocation} and reused and modified in
+ * {@link #childAllocation(int, Rectangle)}.
+ */
+ private final Rectangle insideAllocation = new Rectangle();
+
+ /**
+ * The insets of this <code>CompositeView</code>. This is initialized
+ * in {@link #setInsets}.
+ */
+ private short top;
+ private short bottom;
+ private short left;
+ private short right;
+
+ /**
+ * Creates a new <code>CompositeView</code> for the given
+ * <code>Element</code>.
+ *
+ * @param element the element that is rendered by this CompositeView
+ */
+ public CompositeView(Element element)
+ {
+ super(element);
+ children = new View[0];
+ top = 0;
+ bottom = 0;
+ left = 0;
+ right = 0;
+ }
+
+ /**
+ * Loads the child views of this <code>CompositeView</code>. This method
+ * is called from {@link #setParent} to initialize the child views of
+ * this composite view.
+ *
+ * @param f the view factory to use for creating new child views
+ *
+ * @see #setParent
+ */
+ protected void loadChildren(ViewFactory f)
+ {
+ if (f != null)
+ {
+ Element el = getElement();
+ int count = el.getElementCount();
+ View[] newChildren = new View[count];
+ for (int i = 0; i < count; ++i)
+ {
+ Element child = el.getElement(i);
+ View view = f.create(child);
+ newChildren[i] = view;
+ }
+ // I'd have called replace(0, getViewCount(), newChildren) here
+ // in order to replace all existing views. However according to
+ // Harmony's tests this is not what the RI does.
+ replace(0, 0, newChildren);
+ }
+ }
+
+ /**
+ * Sets the parent of this <code>View</code>.
+ * In addition to setting the parent, this calls {@link #loadChildren}, if
+ * this <code>View</code> does not already have its children initialized.
+ *
+ * @param parent the parent to set
+ */
+ public void setParent(View parent)
+ {
+ super.setParent(parent);
+ if (parent != null && numChildren == 0)
+ loadChildren(getViewFactory());
+ }
+
+ /**
+ * Returns the number of child views.
+ *
+ * @return the number of child views
+ */
+ public int getViewCount()
+ {
+ return numChildren;
+ }
+
+ /**
+ * Returns the child view at index <code>n</code>.
+ *
+ * @param n the index of the requested child view
+ *
+ * @return the child view at index <code>n</code>
+ */
+ public View getView(int n)
+ {
+ return children[n];
+ }
+
+ /**
+ * Replaces child views by some other child views. If there are no views to
+ * remove (<code>length == 0</code>), the result is a simple insert, if
+ * there are no children to add (<code>view == null</code>) the result
+ * is a simple removal.
+ *
+ * @param offset the start offset from where to remove children
+ * @param length the number of children to remove
+ * @param views the views that replace the removed children
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ // Make sure we have an array. The Harmony testsuite indicates that we
+ // have to do something like this.
+ if (views == null)
+ views = new View[0];
+
+ // First we set the parent of the removed children to null.
+ int endOffset = offset + length;
+ for (int i = offset; i < endOffset; ++i)
+ {
+ if (children[i].getParent() == this)
+ children[i].setParent(null);
+ children[i] = null;
+ }
+
+ // Update the children array.
+ int delta = views.length - length;
+ int src = offset + length;
+ int numMove = numChildren - src;
+ int dst = src + delta;
+ if (numChildren + delta > children.length)
+ {
+ // Grow array.
+ int newLength = Math.max(2 * children.length, numChildren + delta);
+ View[] newChildren = new View[newLength];
+ System.arraycopy(children, 0, newChildren, 0, offset);
+ System.arraycopy(views, 0, newChildren, offset, views.length);
+ System.arraycopy(children, src, newChildren, dst, numMove);
+ children = newChildren;
+ }
+ else
+ {
+ // Patch existing array.
+ System.arraycopy(children, src, children, dst, numMove);
+ System.arraycopy(views, 0, children, offset, views.length);
+ }
+ numChildren += delta;
+
+ // Finally we set the parent of the added children to this.
+ for (int i = 0; i < views.length; ++i)
+ views[i].setParent(this);
+ }
+
+ /**
+ * Returns the allocation for the specified child <code>View</code>.
+ *
+ * @param index the index of the child view
+ * @param a the allocation for this view
+ *
+ * @return the allocation for the specified child <code>View</code>
+ */
+ public Shape getChildAllocation(int index, Shape a)
+ {
+ Rectangle r = getInsideAllocation(a);
+ childAllocation(index, r);
+ return r;
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * @param pos the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param bias either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Shape modelToView(int pos, Shape a, Position.Bias bias)
+ throws BadLocationException
+ {
+ boolean backward = bias == Position.Bias.Backward;
+ int testpos = backward ? Math.max(0, pos - 1) : pos;
+
+ Shape ret = null;
+ if (! backward || testpos >= getStartOffset())
+ {
+ int childIndex = getViewIndexAtPosition(testpos);
+ if (childIndex != -1 && childIndex < getViewCount())
+ {
+ View child = getView(childIndex);
+ if (child != null && testpos >= child.getStartOffset()
+ && testpos < child.getEndOffset())
+ {
+ Shape childAlloc = getChildAllocation(childIndex, a);
+ if (childAlloc != null)
+ {
+ ret = child.modelToView(pos, childAlloc, bias);
+ // Handle corner case.
+ if (ret == null && child.getEndOffset() == pos)
+ {
+ childIndex++;
+ if (childIndex < getViewCount())
+ {
+ child = getView(childIndex);
+ childAlloc = getChildAllocation(childIndex, a);
+ ret = child.modelToView(pos, childAlloc, bias);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (ret == null)
+ throw new BadLocationException("Position " + pos
+ + " is not represented by view.", pos);
+
+ return ret;
+ }
+
+ /**
+ * Maps a region in the document into the coordinate space of the View.
+ *
+ * @param p1 the beginning position inside the document
+ * @param b1 the direction bias for the beginning position
+ * @param p2 the end position inside the document
+ * @param b2 the direction bias for the end position
+ * @param a the area that is occupied by the view
+ *
+ * @return a rectangle that gives the span of the document region
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>p1</code> or <code>p2</code> are
+ * invalid
+ * @throws IllegalArgumentException if b1 or b2 is not one of the above
+ * listed valid values
+ */
+ public Shape modelToView(int p1, Position.Bias b1,
+ int p2, Position.Bias b2, Shape a)
+ throws BadLocationException
+ {
+ // TODO: This is most likely not 100% ok, figure out what else is to
+ // do here.
+ return super.modelToView(p1, b1, p2, b2, a);
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space, x >= 0
+ * @param y the y coordinate in the view space, y >= 0
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code> >= 0
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ if (x >= 0 && y >= 0)
+ {
+ Rectangle r = getInsideAllocation(a);
+ View view = getViewAtPoint((int) x, (int) y, r);
+ return view.viewToModel(x, y, r, b);
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the next model location that is visible in eiter north / south
+ * direction or east / west direction. This is used to determine the placement
+ * of the caret when navigating around the document with the arrow keys. This
+ * is a convenience method for {@link #getNextNorthSouthVisualPositionFrom}
+ * and {@link #getNextEastWestVisualPositionFrom}.
+ *
+ * @param pos
+ * the model position to start search from
+ * @param b
+ * the bias for <code>pos</code>
+ * @param a
+ * the allocated region for this view
+ * @param direction
+ * the direction from the current position, can be one of the
+ * following:
+ * <ul>
+ * <li>{@link SwingConstants#WEST}</li>
+ * <li>{@link SwingConstants#EAST}</li>
+ * <li>{@link SwingConstants#NORTH}</li>
+ * <li>{@link SwingConstants#SOUTH}</li>
+ * </ul>
+ * @param biasRet
+ * the bias of the return value gets stored here
+ * @return the position inside the model that represents the next visual
+ * location
+ * @throws BadLocationException
+ * if <code>pos</code> is not a valid location inside the document
+ * model
+ * @throws IllegalArgumentException
+ * if <code>direction</code> is invalid
+ */
+ public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
+ int direction, Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ int retVal = -1;
+ switch (direction)
+ {
+ case SwingConstants.WEST:
+ case SwingConstants.EAST:
+ retVal = getNextEastWestVisualPositionFrom(pos, b, a, direction,
+ biasRet);
+ break;
+ case SwingConstants.NORTH:
+ case SwingConstants.SOUTH:
+ retVal = getNextNorthSouthVisualPositionFrom(pos, b, a, direction,
+ biasRet);
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal value for direction.");
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the index of the child view that represents the specified
+ * model location.
+ *
+ * @param pos the model location for which to determine the child view index
+ * @param b the bias to be applied to <code>pos</code>
+ *
+ * @return the index of the child view that represents the specified
+ * model location
+ */
+ public int getViewIndex(int pos, Position.Bias b)
+ {
+ if (b == Position.Bias.Backward)
+ pos -= 1;
+ int i = -1;
+ if (pos >= getStartOffset() && pos < getEndOffset())
+ i = getViewIndexAtPosition(pos);
+ return i;
+ }
+
+ /**
+ * Returns <code>true</code> if the specified point lies before the
+ * given <code>Rectangle</code>, <code>false</code> otherwise.
+ *
+ * &quot;Before&quot; is typically defined as being to the left or above.
+ *
+ * @param x the X coordinate of the point
+ * @param y the Y coordinate of the point
+ * @param r the rectangle to test the point against
+ *
+ * @return <code>true</code> if the specified point lies before the
+ * given <code>Rectangle</code>, <code>false</code> otherwise
+ */
+ protected abstract boolean isBefore(int x, int y, Rectangle r);
+
+ /**
+ * Returns <code>true</code> if the specified point lies after the
+ * given <code>Rectangle</code>, <code>false</code> otherwise.
+ *
+ * &quot;After&quot; is typically defined as being to the right or below.
+ *
+ * @param x the X coordinate of the point
+ * @param y the Y coordinate of the point
+ * @param r the rectangle to test the point against
+ *
+ * @return <code>true</code> if the specified point lies after the
+ * given <code>Rectangle</code>, <code>false</code> otherwise
+ */
+ protected abstract boolean isAfter(int x, int y, Rectangle r);
+
+ /**
+ * Returns the child <code>View</code> at the specified location.
+ *
+ * @param x the X coordinate
+ * @param y the Y coordinate
+ * @param r the inner allocation of this <code>BoxView</code> on entry,
+ * the allocation of the found child on exit
+ *
+ * @return the child <code>View</code> at the specified location
+ */
+ protected abstract View getViewAtPoint(int x, int y, Rectangle r);
+
+ /**
+ * Computes the allocation for a child <code>View</code>. The parameter
+ * <code>a</code> stores the allocation of this <code>CompositeView</code>
+ * and is then adjusted to hold the allocation of the child view.
+ *
+ * @param index the index of the child <code>View</code>
+ * @param a the allocation of this <code>CompositeView</code> before the
+ * call, the allocation of the child on exit
+ */
+ protected abstract void childAllocation(int index, Rectangle a);
+
+ /**
+ * Returns the child <code>View</code> that contains the given model
+ * position. The given <code>Rectangle</code> gives the parent's allocation
+ * and is changed to the child's allocation on exit.
+ *
+ * @param pos the model position to query the child <code>View</code> for
+ * @param a the parent allocation on entry and the child allocation on exit
+ *
+ * @return the child view at the given model position
+ */
+ protected View getViewAtPosition(int pos, Rectangle a)
+ {
+ View view = null;
+ int i = getViewIndexAtPosition(pos);
+ if (i >= 0 && i < getViewCount() && a != null)
+ {
+ view = getView(i);
+ childAllocation(i, a);
+ }
+ return view;
+ }
+
+ /**
+ * Returns the index of the child <code>View</code> for the given model
+ * position.
+ *
+ * @param pos the model position for whicht the child <code>View</code> is
+ * queried
+ *
+ * @return the index of the child <code>View</code> for the given model
+ * position
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ // We have a 1:1 mapping of elements to views here, so we forward
+ // this to the element.
+ Element el = getElement();
+ return el.getElementIndex(pos);
+ }
+
+ /**
+ * Returns the allocation that is given to this <code>CompositeView</code>
+ * minus this <code>CompositeView</code>'s insets.
+ *
+ * Also this translates from an immutable allocation to a mutable allocation
+ * that is typically reused and further narrowed, like in
+ * {@link #childAllocation}.
+ *
+ * @param a the allocation given to this <code>CompositeView</code>
+ *
+ * @return the allocation that is given to this <code>CompositeView</code>
+ * minus this <code>CompositeView</code>'s insets or
+ * <code>null</code> if a was <code>null</code>
+ */
+ protected Rectangle getInsideAllocation(Shape a)
+ {
+ if (a == null)
+ return null;
+
+ // Try to avoid allocation of Rectangle here.
+ Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
+ // Initialize the inside allocation rectangle. This is done inside
+ // a synchronized block in order to avoid multiple threads creating
+ // this instance simultanously.
+ Rectangle inside = insideAllocation;
+ inside.x = alloc.x + getLeftInset();
+ inside.y = alloc.y + getTopInset();
+ inside.width = alloc.width - getLeftInset() - getRightInset();
+ inside.height = alloc.height - getTopInset() - getBottomInset();
+ return inside;
+ }
+
+ /**
+ * Sets the insets defined by attributes in <code>attributes</code>. This
+ * queries the attribute keys {@link StyleConstants#SpaceAbove},
+ * {@link StyleConstants#SpaceBelow}, {@link StyleConstants#LeftIndent} and
+ * {@link StyleConstants#RightIndent} and calls {@link #setInsets} to
+ * actually set the insets on this <code>CompositeView</code>.
+ *
+ * @param attributes the attributes from which to query the insets
+ */
+ protected void setParagraphInsets(AttributeSet attributes)
+ {
+ top = (short) StyleConstants.getSpaceAbove(attributes);
+ bottom = (short) StyleConstants.getSpaceBelow(attributes);
+ left = (short) StyleConstants.getLeftIndent(attributes);
+ right = (short) StyleConstants.getRightIndent(attributes);
+ }
+
+ /**
+ * Sets the insets of this <code>CompositeView</code>.
+ *
+ * @param t the top inset
+ * @param l the left inset
+ * @param b the bottom inset
+ * @param r the right inset
+ */
+ protected void setInsets(short t, short l, short b, short r)
+ {
+ top = t;
+ left = l;
+ bottom = b;
+ right = r;
+ }
+
+ /**
+ * Returns the left inset of this <code>CompositeView</code>.
+ *
+ * @return the left inset of this <code>CompositeView</code>
+ */
+ protected short getLeftInset()
+ {
+ return left;
+ }
+
+ /**
+ * Returns the right inset of this <code>CompositeView</code>.
+ *
+ * @return the right inset of this <code>CompositeView</code>
+ */
+ protected short getRightInset()
+ {
+ return right;
+ }
+
+ /**
+ * Returns the top inset of this <code>CompositeView</code>.
+ *
+ * @return the top inset of this <code>CompositeView</code>
+ */
+ protected short getTopInset()
+ {
+ return top;
+ }
+
+ /**
+ * Returns the bottom inset of this <code>CompositeView</code>.
+ *
+ * @return the bottom inset of this <code>CompositeView</code>
+ */
+ protected short getBottomInset()
+ {
+ return bottom;
+ }
+
+ /**
+ * Returns the next model location that is visible in north or south
+ * direction.
+ * This is used to determine the
+ * placement of the caret when navigating around the document with
+ * the arrow keys.
+ *
+ * @param pos the model position to start search from
+ * @param b the bias for <code>pos</code>
+ * @param a the allocated region for this view
+ * @param direction the direction from the current position, can be one of
+ * the following:
+ * <ul>
+ * <li>{@link SwingConstants#NORTH}</li>
+ * <li>{@link SwingConstants#SOUTH}</li>
+ * </ul>
+ * @param biasRet the bias of the return value gets stored here
+ *
+ * @return the position inside the model that represents the next visual
+ * location
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid location
+ * inside the document model
+ * @throws IllegalArgumentException if <code>direction</code> is invalid
+ */
+ protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b,
+ Shape a, int direction,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ // TODO: It is unknown to me how this method has to be implemented and
+ // there is no specification telling me how to do it properly. Therefore
+ // the implementation was done for cases that are known.
+ //
+ // If this method ever happens to act silly for your particular case then
+ // it is likely that it is a cause of not knowing about your case when it
+ // was implemented first. You are free to fix the behavior.
+ //
+ // Here are the assumptions that lead to the implementation:
+ // If direction is NORTH chose the View preceding the one that contains the
+ // offset 'pos' (imagine the views are stacked on top of each other where
+ // the top is 0 and the bottom is getViewCount()-1.
+ // Consecutively when the direction is SOUTH the View following the one
+ // the offset 'pos' lies in is questioned.
+ //
+ // This limitation is described as PR 27345.
+ int index = getViewIndex(pos, b);
+ View v = null;
+
+ if (index == -1)
+ return pos;
+
+ switch (direction)
+ {
+ case NORTH:
+ // If we cannot calculate a proper offset return the one that was
+ // provided.
+ if (index <= 0)
+ return pos;
+
+ v = getView(index - 1);
+ break;
+ case SOUTH:
+ // If we cannot calculate a proper offset return the one that was
+ // provided.
+ if (index >= getViewCount() - 1)
+ return pos;
+
+ v = getView(index + 1);
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ return v.getNextVisualPositionFrom(pos, b, a, direction, biasRet);
+ }
+
+ /**
+ * Returns the next model location that is visible in east or west
+ * direction.
+ * This is used to determine the
+ * placement of the caret when navigating around the document with
+ * the arrow keys.
+ *
+ * @param pos the model position to start search from
+ * @param b the bias for <code>pos</code>
+ * @param a the allocated region for this view
+ * @param direction the direction from the current position, can be one of
+ * the following:
+ * <ul>
+ * <li>{@link SwingConstants#EAST}</li>
+ * <li>{@link SwingConstants#WEST}</li>
+ * </ul>
+ * @param biasRet the bias of the return value gets stored here
+ *
+ * @return the position inside the model that represents the next visual
+ * location
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid location
+ * inside the document model
+ * @throws IllegalArgumentException if <code>direction</code> is invalid
+ */
+ protected int getNextEastWestVisualPositionFrom(int pos, Position.Bias b,
+ Shape a, int direction,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ // TODO: It is unknown to me how this method has to be implemented and
+ // there is no specification telling me how to do it properly. Therefore
+ // the implementation was done for cases that are known.
+ //
+ // If this method ever happens to act silly for your particular case then
+ // it is likely that it is a cause of not knowing about your case when it
+ // was implemented first. You are free to fix the behavior.
+ //
+ // Here are the assumptions that lead to the implementation:
+ // If direction is EAST increase the offset by one and ask the View to
+ // which that index belong to calculate the 'next visual position'.
+ // If the direction is WEST do the same with offset 'pos' being decreased
+ // by one.
+ // This behavior will fail in a right-to-left or bidi environment!
+ //
+ // This limitation is described as PR 27346.
+ int index;
+
+ View v = null;
+
+ switch (direction)
+ {
+ case EAST:
+ index = getViewIndex(pos + 1, b);
+ // If we cannot calculate a proper offset return the one that was
+ // provided.
+ if (index == -1)
+ return pos;
+
+ v = getView(index);
+ break;
+ case WEST:
+ index = getViewIndex(pos - 1, b);
+ // If we cannot calculate a proper offset return the one that was
+ // provided.
+ if (index == -1)
+ return pos;
+
+ v = getView(index);
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ return v.getNextVisualPositionFrom(pos,
+ b,
+ a,
+ direction,
+ biasRet);
+ }
+
+ /**
+ * Determines if the next view in horinzontal direction is located to
+ * the east or west of the view at position <code>pos</code>. Usually
+ * the <code>View</code>s are laid out from the east to the west, so
+ * we unconditionally return <code>false</code> here. Subclasses that
+ * support bidirectional text may wish to override this method.
+ *
+ * @param pos the position in the document
+ * @param bias the bias to be applied to <code>pos</code>
+ *
+ * @return <code>true</code> if the next <code>View</code> is located
+ * to the EAST, <code>false</code> otherwise
+ */
+ protected boolean flipEastAndWestAtEnds(int pos, Position.Bias bias)
+ {
+ return false;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/DateFormatter.java b/libjava/classpath/javax/swing/text/DateFormatter.java
new file mode 100644
index 000000000..869f9a090
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DateFormatter.java
@@ -0,0 +1,85 @@
+/* DateFormatter.java --
+Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.text.DateFormat;
+
+/**
+ * <code>DateFormatter</code> is an {@link InternationalFormatter}
+ * that implements value to string and string to value conversion via
+ * an instance of {@link DateFormat}.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class DateFormatter extends InternationalFormatter
+{
+
+ /** The serialVersoinUID. */
+ private static final long serialVersionUID = 5423279572591848797L;
+
+ /**
+ * Creates a new instance using the default {@link DateFormat} object
+ * returned by {@link DateFormat#getDateInstance()}.
+ */
+ public DateFormatter()
+ {
+ this(DateFormat.getDateInstance());
+ }
+
+ /**
+ * Creates a new instance of <code>DateFormatter</code> using the
+ * specified <code>DateFormat</code>
+ *
+ * @param format the <code>DateFormat</code> to use
+ */
+ public DateFormatter(DateFormat format)
+ {
+ super();
+ setFormat(format);
+ }
+
+ /**
+ * Sets the format that is used by this <code>DateFormatter</code>.
+ *
+ * @param format the <code>DateFormat</code> to use
+ */
+ public void setFormat(DateFormat format)
+ {
+ super.setFormat(format);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java
new file mode 100644
index 000000000..acfcdb368
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultCaret.java
@@ -0,0 +1,1287 @@
+/* DefaultCaret.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.EventListener;
+
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.EventListenerList;
+import javax.swing.text.Position.Bias;
+
+/**
+ * The default implementation of the {@link Caret} interface.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class DefaultCaret extends Rectangle
+ implements Caret, FocusListener, MouseListener, MouseMotionListener
+{
+
+ /** A text component in the current VM which currently has a
+ * text selection or <code>null</code>.
+ */
+ static JTextComponent componentWithSelection;
+
+ /** An implementation of NavigationFilter.FilterBypass which delegates
+ * to the corresponding methods of the <code>DefaultCaret</code>.
+ *
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+ class Bypass extends NavigationFilter.FilterBypass
+ {
+
+ public Caret getCaret()
+ {
+ return DefaultCaret.this;
+ }
+
+ public void moveDot(int dot, Bias bias)
+ {
+ DefaultCaret.this.moveDotImpl(dot);
+ }
+
+ public void setDot(int dot, Bias bias)
+ {
+ DefaultCaret.this.setDotImpl(dot);
+ }
+
+ }
+
+ /**
+ * Controls the blinking of the caret.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+ private class BlinkTimerListener implements ActionListener
+ {
+ /**
+ * Forces the next event to be ignored. The next event should be ignored
+ * if we force the caret to appear. We do not know how long will it take
+ * to fire the comming event; this may be near immediately. Better to leave
+ * the caret visible one iteration longer.
+ */
+ boolean ignoreNextEvent;
+
+ /**
+ * Receives notification when the blink timer fires and updates the visible
+ * state of the caret.
+ *
+ * @param event the action event
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ if (ignoreNextEvent)
+ ignoreNextEvent = false;
+ else
+ {
+ visible = !visible;
+ repaint();
+ }
+ }
+ }
+
+ /**
+ * Listens for changes in the text component's document and updates the
+ * caret accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class DocumentHandler implements DocumentListener
+ {
+ /**
+ * Receives notification that some text attributes have changed. No action
+ * is taken here.
+ *
+ * @param event the document event
+ */
+ public void changedUpdate(DocumentEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification that some text has been inserted from the text
+ * component. The caret is moved forward accordingly.
+ *
+ * @param event the document event
+ */
+ public void insertUpdate(DocumentEvent event)
+ {
+ if (policy == ALWAYS_UPDATE ||
+ (SwingUtilities.isEventDispatchThread() &&
+ policy == UPDATE_WHEN_ON_EDT))
+ {
+ int dot = getDot();
+ setDot(dot + event.getLength());
+ }
+ }
+
+ /**
+ * Receives notification that some text has been removed into the text
+ * component. The caret is moved backwards accordingly.
+ *
+ * @param event the document event
+ */
+ public void removeUpdate(DocumentEvent event)
+ {
+ if (policy == ALWAYS_UPDATE
+ || (SwingUtilities.isEventDispatchThread()
+ && policy == UPDATE_WHEN_ON_EDT))
+ {
+ int dot = getDot();
+ setDot(dot - event.getLength());
+ }
+ else if (policy == NEVER_UPDATE
+ || (! SwingUtilities.isEventDispatchThread()
+ && policy == UPDATE_WHEN_ON_EDT))
+ {
+ int docLength = event.getDocument().getLength();
+ if (getDot() > docLength)
+ setDot(docLength);
+ }
+ }
+ }
+
+ /**
+ * Listens for property changes on the text document. This is used to add and
+ * remove our document listener, if the document of the text component has
+ * changed.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class PropertyChangeHandler implements PropertyChangeListener
+ {
+
+ /**
+ * Receives notification when a property has changed on the text component.
+ * This adds/removes our document listener from the text component's
+ * document when the document changes.
+ *
+ * @param e the property change event
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String name = e.getPropertyName();
+
+ if (name.equals("document"))
+ {
+ Document oldDoc = (Document) e.getOldValue();
+ if (oldDoc != null)
+ oldDoc.removeDocumentListener(documentListener);
+
+ Document newDoc = (Document) e.getNewValue();
+ if (newDoc != null)
+ newDoc.addDocumentListener(documentListener);
+ }
+ else if (name.equals("editable"))
+ {
+ active = (((Boolean) e.getNewValue()).booleanValue()
+ && textComponent.isEnabled());
+ }
+ else if (name.equals("enabled"))
+ {
+ active = (((Boolean) e.getNewValue()).booleanValue()
+ && textComponent.isEditable());
+ }
+
+ }
+
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 4325555698756477346L;
+
+ /**
+ * Indicates the Caret position should always be updated after Document
+ * changes even if the updates are not performed on the Event Dispatching
+ * thread.
+ *
+ * @since 1.5
+ */
+ public static final int ALWAYS_UPDATE = 2;
+
+ /**
+ * Indicates the Caret position should not be changed unless the Document
+ * length becomes less than the Caret position, in which case the Caret
+ * is moved to the end of the Document.
+ *
+ * @since 1.5
+ */
+ public static final int NEVER_UPDATE = 1;
+
+ /**
+ * Indicates the Caret position should be updated only if Document changes
+ * are made on the Event Dispatcher thread.
+ *
+ * @since 1.5
+ */
+ public static final int UPDATE_WHEN_ON_EDT = 0;
+
+ /** Keeps track of the current update policy **/
+ int policy = UPDATE_WHEN_ON_EDT;
+
+ /**
+ * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}.
+ */
+ protected ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /**
+ * Stores all registered event listeners.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Our document listener.
+ */
+ DocumentListener documentListener;
+
+ /**
+ * Our property listener.
+ */
+ PropertyChangeListener propertyChangeListener;
+
+ /**
+ * The text component in which this caret is installed.
+ *
+ * (Package private to avoid synthetic accessor method.)
+ */
+ JTextComponent textComponent;
+
+ /**
+ * Indicates if the selection should be visible or not.
+ */
+ private boolean selectionVisible = true;
+
+ /**
+ * The blink rate of this <code>Caret</code>.
+ */
+ private int blinkRate = 500;
+
+ /**
+ * The current dot position.
+ */
+ private int dot = 0;
+
+ /**
+ * The current mark position.
+ */
+ private int mark = 0;
+
+ /**
+ * The current visual caret position.
+ */
+ private Point magicCaretPosition = null;
+
+ /**
+ * Indicates if this <code>Caret</code> is currently visible or not. This is
+ * package private to avoid an accessor method.
+ */
+ boolean visible = false;
+
+ /** Indicates whether the text component where the caret is installed is
+ * editable and enabled. If either of these properties is <code>false</code>
+ * the caret is not drawn.
+ */
+ boolean active = true;
+
+ /**
+ * The current highlight entry.
+ */
+ private Object highlightEntry;
+
+ private Timer blinkTimer;
+
+ private BlinkTimerListener blinkListener;
+
+ /**
+ * A <code>NavigationFilter.FilterBypass</code> instance which
+ * is provided to the a <code>NavigationFilter</code> to
+ * unconditionally set or move the caret.
+ */
+ NavigationFilter.FilterBypass bypass;
+
+ /**
+ * Creates a new <code>DefaultCaret</code> instance.
+ */
+ public DefaultCaret()
+ {
+ // Nothing to do here.
+ }
+
+ /** Returns the caret's <code>NavigationFilter.FilterBypass</code> instance
+ * and creates it if it does not yet exist.
+ *
+ * @return The caret's <code>NavigationFilter.FilterBypass</code> instance.
+ */
+ private NavigationFilter.FilterBypass getBypass()
+ {
+ return (bypass == null) ? bypass = new Bypass() : bypass;
+ }
+
+ /**
+ * Sets the Caret update policy.
+ *
+ * @param policy the new policy. Valid values are:
+ * ALWAYS_UPDATE: always update the Caret position, even when Document
+ * updates don't occur on the Event Dispatcher thread.
+ * NEVER_UPDATE: don't update the Caret position unless the Document
+ * length becomes less than the Caret position (then update the
+ * Caret to the end of the Document).
+ * UPDATE_WHEN_ON_EDT: update the Caret position when the
+ * Document updates occur on the Event Dispatcher thread. This is the
+ * default.
+ *
+ * @since 1.5
+ * @throws IllegalArgumentException if policy is not one of the above.
+ */
+ public void setUpdatePolicy (int policy)
+ {
+ if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE
+ && policy != UPDATE_WHEN_ON_EDT)
+ throw new
+ IllegalArgumentException
+ ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT");
+ this.policy = policy;
+ }
+
+ /**
+ * Gets the caret update policy.
+ *
+ * @return the caret update policy.
+ * @since 1.5
+ */
+ public int getUpdatePolicy ()
+ {
+ return policy;
+ }
+
+ /**
+ * Moves the caret position when the mouse is dragged over the text
+ * component, modifying the selectiony.
+ *
+ * <p>When the text component where the caret is installed is disabled,
+ * the selection is not change but you can still scroll the text and
+ * update the caret's location.</p>
+ *
+ * @param event the <code>MouseEvent</code> describing the drag operation
+ */
+ public void mouseDragged(MouseEvent event)
+ {
+ if (event.getButton() == MouseEvent.BUTTON1)
+ {
+ if (textComponent.isEnabled())
+ moveCaret(event);
+ else
+ positionCaret(event);
+ }
+ }
+
+ /**
+ * Indicates a mouse movement over the text component. Does nothing here.
+ *
+ * @param event the <code>MouseEvent</code> describing the mouse operation
+ */
+ public void mouseMoved(MouseEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * When the click is received from Button 1 then the following actions
+ * are performed here:
+ *
+ * <ul>
+ * <li>If we receive a double click, the caret position (dot) is set
+ * to the position associated to the mouse click and the word at
+ * this location is selected. If there is no word at the pointer
+ * the gap is selected instead.</li>
+ * <li>If we receive a triple click, the caret position (dot) is set
+ * to the position associated to the mouse click and the line at
+ * this location is selected.</li>
+ * </ul>
+ *
+ * @param event the <code>MouseEvent</code> describing the click operation
+ */
+ public void mouseClicked(MouseEvent event)
+ {
+ // Do not modify selection if component is disabled.
+ if (!textComponent.isEnabled())
+ return;
+
+ int count = event.getClickCount();
+
+ if (event.getButton() == MouseEvent.BUTTON1 && count >= 2)
+ {
+ int newDot = getComponent().viewToModel(event.getPoint());
+ JTextComponent t = getComponent();
+
+ try
+ {
+ if (count == 3)
+ {
+ setDot(Utilities.getRowStart(t, newDot));
+ moveDot( Utilities.getRowEnd(t, newDot));
+ }
+ else
+ {
+ int wordStart = Utilities.getWordStart(t, newDot);
+
+ // When the mouse points at the offset of the first character
+ // in a word Utilities().getPreviousWord will not return that
+ // word but we want to select that. We have to use
+ // Utilities.getWordStart() to get it.
+ if (newDot == wordStart)
+ {
+ setDot(wordStart);
+ moveDot(Utilities.getWordEnd(t, wordStart));
+ }
+ else
+ {
+ int nextWord = Utilities.getNextWord(t, newDot);
+ int previousWord = Utilities.getPreviousWord(t, newDot);
+ int previousWordEnd = Utilities.getWordEnd(t, previousWord);
+
+ // If the user clicked in the space between two words,
+ // then select the space.
+ if (newDot >= previousWordEnd && newDot <= nextWord)
+ {
+ setDot(previousWordEnd);
+ moveDot(nextWord);
+ }
+ // Otherwise select the word under the mouse pointer.
+ else
+ {
+ setDot(previousWord);
+ moveDot(previousWordEnd);
+ }
+ }
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // TODO: Swallowing ok here?
+ }
+ }
+
+ }
+
+ /**
+ * Indicates that the mouse has entered the text component. Nothing is done
+ * here.
+ *
+ * @param event the <code>MouseEvent</code> describing the mouse operation
+ */
+ public void mouseEntered(MouseEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Indicates that the mouse has exited the text component. Nothing is done
+ * here.
+ *
+ * @param event the <code>MouseEvent</code> describing the mouse operation
+ */
+ public void mouseExited(MouseEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * If the button 1 is pressed, the caret position is updated to the
+ * position of the mouse click and the text component requests the input
+ * focus if it is enabled. If the SHIFT key is held down, the caret will
+ * be moved, which might select the text between the old and new location.
+ *
+ * @param event the <code>MouseEvent</code> describing the press operation
+ */
+ public void mousePressed(MouseEvent event)
+ {
+
+ // The implementation assumes that consuming the event makes the AWT event
+ // mechanism forget about this event instance and not transfer focus.
+ // By observing how the RI reacts the following behavior has been
+ // implemented (in regard to text components):
+ // - a left-click moves the caret
+ // - a left-click when shift is held down expands the selection
+ // - a right-click or click with any additional mouse button
+ // on a text component is ignored
+ // - a middle-click positions the caret and pastes the clipboard
+ // contents.
+ // - a middle-click when shift is held down is ignored
+
+ if (SwingUtilities.isLeftMouseButton(event))
+ {
+ // Handle the caret.
+ if (event.isShiftDown() && getDot() != -1)
+ {
+ moveCaret(event);
+ }
+ else
+ {
+ positionCaret(event);
+ }
+
+ // Handle the focus.
+ if (textComponent != null && textComponent.isEnabled()
+ && textComponent.isRequestFocusEnabled())
+ {
+ textComponent.requestFocus();
+ }
+
+ // TODO: Handle double click for selecting words.
+ }
+ else if(event.getButton() == MouseEvent.BUTTON2)
+ {
+ // Special handling for X11-style pasting.
+ if (! event.isShiftDown())
+ {
+ positionCaret(event);
+ textComponent.paste();
+ }
+ }
+ }
+
+ /**
+ * Indicates that a mouse button has been released on the text component.
+ * Nothing is done here.
+ *
+ * @param event the <code>MouseEvent</code> describing the mouse operation
+ */
+ public void mouseReleased(MouseEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the caret to <code>visible</code> if the text component is editable.
+ *
+ * @param event the <code>FocusEvent</code>
+ */
+ public void focusGained(FocusEvent event)
+ {
+ if (textComponent.isEditable())
+ {
+ setVisible(true);
+ updateTimerStatus();
+ }
+ }
+
+ /**
+ * Sets the caret to <code>invisible</code>.
+ *
+ * @param event the <code>FocusEvent</code>
+ */
+ public void focusLost(FocusEvent event)
+ {
+ if (textComponent.isEditable() && event.isTemporary() == false)
+ {
+ setVisible(false);
+
+ // Stop the blinker, if running.
+ if (blinkTimer != null && blinkTimer.isRunning())
+ blinkTimer.stop();
+ }
+ }
+
+ /**
+ * Install (if not present) and start the timer, if the caret must blink. The
+ * caret does not blink if it is invisible, or the component is disabled or
+ * not editable.
+ */
+ private void updateTimerStatus()
+ {
+ if (textComponent.isEnabled() && textComponent.isEditable())
+ {
+ if (blinkTimer == null)
+ initBlinkTimer();
+ if (!blinkTimer.isRunning())
+ blinkTimer.start();
+ }
+ else
+ {
+ if (blinkTimer != null)
+ blinkTimer.stop();
+ }
+ }
+
+ /**
+ * Moves the caret to the position specified in the <code>MouseEvent</code>.
+ * This will cause a selection if the dot and mark are different.
+ *
+ * @param event the <code>MouseEvent</code> from which to fetch the position
+ */
+ protected void moveCaret(MouseEvent event)
+ {
+ int newDot = getComponent().viewToModel(event.getPoint());
+ moveDot(newDot);
+ }
+
+ /**
+ * Repositions the caret to the position specified in the
+ * <code>MouseEvent</code>.
+ *
+ * @param event the <code>MouseEvent</code> from which to fetch the position
+ */
+ protected void positionCaret(MouseEvent event)
+ {
+ int newDot = getComponent().viewToModel(event.getPoint());
+ setDot(newDot);
+ }
+
+ /**
+ * Deinstalls this <code>Caret</code> from the specified
+ * <code>JTextComponent</code>. This removes any listeners that have been
+ * registered by this <code>Caret</code>.
+ *
+ * @param c the text component from which to install this caret
+ */
+ public void deinstall(JTextComponent c)
+ {
+ textComponent.removeFocusListener(this);
+ textComponent.removeMouseListener(this);
+ textComponent.removeMouseMotionListener(this);
+ textComponent.getDocument().removeDocumentListener(documentListener);
+ documentListener = null;
+ textComponent.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ textComponent = null;
+
+ // Deinstall blink timer if present.
+ if (blinkTimer != null)
+ blinkTimer.stop();
+ blinkTimer = null;
+ }
+
+ /**
+ * Installs this <code>Caret</code> on the specified
+ * <code>JTextComponent</code>. This registers a couple of listeners
+ * on the text component.
+ *
+ * @param c the text component on which to install this caret
+ */
+ public void install(JTextComponent c)
+ {
+ textComponent = c;
+ textComponent.addFocusListener(this);
+ textComponent.addMouseListener(this);
+ textComponent.addMouseMotionListener(this);
+ propertyChangeListener = new PropertyChangeHandler();
+ textComponent.addPropertyChangeListener(propertyChangeListener);
+ documentListener = new DocumentHandler();
+
+ Document doc = textComponent.getDocument();
+ if (doc != null)
+ doc.addDocumentListener(documentListener);
+
+ active = textComponent.isEditable() && textComponent.isEnabled();
+
+ repaint();
+ }
+
+ /**
+ * Sets the current visual position of this <code>Caret</code>.
+ *
+ * @param p the Point to use for the saved location. May be <code>null</code>
+ * to indicate that there is no visual location
+ */
+ public void setMagicCaretPosition(Point p)
+ {
+ magicCaretPosition = p;
+ }
+
+ /**
+ * Returns the current visual position of this <code>Caret</code>.
+ *
+ * @return the current visual position of this <code>Caret</code>
+ *
+ * @see #setMagicCaretPosition
+ */
+ public Point getMagicCaretPosition()
+ {
+ return magicCaretPosition;
+ }
+
+ /**
+ * Returns the current position of the <code>mark</code>. The
+ * <code>mark</code> marks the location in the <code>Document</code> that
+ * is the end of a selection. If there is no selection, the <code>mark</code>
+ * is the same as the <code>dot</code>.
+ *
+ * @return the current position of the mark
+ */
+ public int getMark()
+ {
+ return mark;
+ }
+
+ private void clearHighlight()
+ {
+ Highlighter highlighter = textComponent.getHighlighter();
+
+ if (highlighter == null)
+ return;
+
+ if (selectionVisible)
+ {
+ try
+ {
+ if (highlightEntry != null)
+ highlighter.changeHighlight(highlightEntry, 0, 0);
+
+ // Free the global variable which stores the text component with an active
+ // selection.
+ if (componentWithSelection == textComponent)
+ componentWithSelection = null;
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ throw new InternalError();
+ }
+ }
+ else
+ {
+ if (highlightEntry != null)
+ {
+ highlighter.removeHighlight(highlightEntry);
+ highlightEntry = null;
+ }
+ }
+ }
+
+ 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)
+ {
+ try
+ {
+ if (highlightEntry == null)
+ highlightEntry = highlighter.addHighlight(p0, p1, getSelectionPainter());
+ else
+ highlighter.changeHighlight(highlightEntry, p0, p1);
+
+ // If another component currently has a text selection clear that selection
+ // first.
+ if (componentWithSelection != null)
+ if (componentWithSelection != textComponent)
+ {
+ Caret c = componentWithSelection.getCaret();
+ c.setDot(c.getDot());
+ }
+ componentWithSelection = textComponent;
+
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ throw new InternalError();
+ }
+ }
+ else
+ {
+ if (highlightEntry != null)
+ {
+ highlighter.removeHighlight(highlightEntry);
+ highlightEntry = null;
+ }
+ }
+ }
+
+ /**
+ * Sets the visiblity state of the selection.
+ *
+ * @param v <code>true</code> if the selection should be visible,
+ * <code>false</code> otherwise
+ */
+ public void setSelectionVisible(boolean v)
+ {
+ if (selectionVisible == v)
+ return;
+
+ selectionVisible = v;
+ handleHighlight();
+ repaint();
+ }
+
+ /**
+ * Returns <code>true</code> if the selection is currently visible,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the selection is currently visible,
+ * <code>false</code> otherwise
+ */
+ public boolean isSelectionVisible()
+ {
+ return selectionVisible;
+ }
+
+ /**
+ * Causes the <code>Caret</code> to repaint itself.
+ */
+ protected final void repaint()
+ {
+ getComponent().repaint(x, y, width, height);
+ }
+
+ /**
+ * Paints this <code>Caret</code> using the specified <code>Graphics</code>
+ * context.
+ *
+ * @param g the graphics context to use
+ */
+ public void paint(Graphics g)
+ {
+ JTextComponent comp = getComponent();
+ if (comp == null)
+ return;
+
+ // Make sure the dot has a sane position.
+ dot = Math.min(dot, textComponent.getDocument().getLength());
+ dot = Math.max(dot, 0);
+
+ Rectangle rect = null;
+
+ try
+ {
+ rect = textComponent.modelToView(dot);
+ }
+ catch (BadLocationException e)
+ {
+ // Let's ignore that. This shouldn't really occur. But if it
+ // does (it seems that this happens when the model is mutating),
+ // it causes no real damage. Uncomment this for debugging.
+ // e.printStackTrace();
+ }
+
+ if (rect == null)
+ return;
+
+ // Check if paint has possibly been called directly, without a previous
+ // call to damage(). In this case we need to do some cleanup first.
+ if ((x != rect.x) || (y != rect.y))
+ {
+ repaint(); // Erase previous location of caret.
+ x = rect.x;
+ y = rect.y;
+ width = 1;
+ height = rect.height;
+ }
+
+ // Now draw the caret on the new position if visible.
+ if (visible && active)
+ {
+ g.setColor(textComponent.getCaretColor());
+ g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height - 1);
+ }
+ }
+
+ /**
+ * Returns all registered event listeners of the specified type.
+ *
+ * @param listenerType the type of listener to return
+ *
+ * @return all registered event listeners of the specified type
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Registers a {@link ChangeListener} that is notified whenever that state
+ * of this <code>Caret</code> changes.
+ *
+ * @param listener the listener to register to this caret
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Removes a {@link ChangeListener} from the list of registered listeners.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Returns all registered {@link ChangeListener}s of this <code>Caret</code>.
+ *
+ * @return all registered {@link ChangeListener}s of this <code>Caret</code>
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Notifies all registered {@link ChangeListener}s that the state
+ * of this <code>Caret</code> has changed.
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].stateChanged(changeEvent);
+ }
+
+ /**
+ * Returns the <code>JTextComponent</code> on which this <code>Caret</code>
+ * is installed.
+ *
+ * @return the <code>JTextComponent</code> on which this <code>Caret</code>
+ * is installed
+ */
+ protected final JTextComponent getComponent()
+ {
+ return textComponent;
+ }
+
+ /**
+ * Returns the blink rate of this <code>Caret</code> in milliseconds.
+ * A value of <code>0</code> means that the caret does not blink.
+ *
+ * @return the blink rate of this <code>Caret</code> or <code>0</code> if
+ * this caret does not blink
+ */
+ public int getBlinkRate()
+ {
+ return blinkRate;
+ }
+
+ /**
+ * Sets the blink rate of this <code>Caret</code> in milliseconds.
+ * A value of <code>0</code> means that the caret does not blink.
+ *
+ * @param rate the new blink rate to set
+ */
+ public void setBlinkRate(int rate)
+ {
+ if (blinkTimer != null)
+ blinkTimer.setDelay(rate);
+ blinkRate = rate;
+ }
+
+ /**
+ * Returns the current position of this <code>Caret</code> within the
+ * <code>Document</code>.
+ *
+ * @return the current position of this <code>Caret</code> within the
+ * <code>Document</code>
+ */
+ public int getDot()
+ {
+ return dot;
+ }
+
+ /**
+ * Moves the <code>dot</code> location without touching the
+ * <code>mark</code>. This is used when making a selection.
+ *
+ * <p>If the underlying text component has a {@link NavigationFilter}
+ * installed the caret will call the corresponding method of that object.</p>
+ *
+ * @param dot the location where to move the dot
+ *
+ * @see #setDot(int)
+ */
+ public void moveDot(int dot)
+ {
+ NavigationFilter filter = textComponent.getNavigationFilter();
+ if (filter != null)
+ filter.moveDot(getBypass(), dot, Bias.Forward);
+ else
+ moveDotImpl(dot);
+ }
+
+ void moveDotImpl(int dot)
+ {
+ if (dot >= 0)
+ {
+ Document doc = textComponent.getDocument();
+ if (doc != null)
+ this.dot = Math.min(dot, doc.getLength());
+ this.dot = Math.max(this.dot, 0);
+
+ handleHighlight();
+
+ appear();
+ }
+ }
+
+ /**
+ * Sets the current position of this <code>Caret</code> within the
+ * <code>Document</code>. This also sets the <code>mark</code> to the new
+ * location.
+ *
+ * <p>If the underlying text component has a {@link NavigationFilter}
+ * installed the caret will call the corresponding method of that object.</p>
+ *
+ * @param dot
+ * the new position to be set
+ * @see #moveDot(int)
+ */
+ public void setDot(int dot)
+ {
+ NavigationFilter filter = textComponent.getNavigationFilter();
+ if (filter != null)
+ filter.setDot(getBypass(), dot, Bias.Forward);
+ else
+ setDotImpl(dot);
+ }
+
+ void setDotImpl(int dot)
+ {
+ if (dot >= 0)
+ {
+ Document doc = textComponent.getDocument();
+ if (doc != null)
+ this.dot = Math.min(dot, doc.getLength());
+ this.dot = Math.max(this.dot, 0);
+ this.mark = this.dot;
+
+ clearHighlight();
+
+ appear();
+ }
+ }
+
+ /**
+ * Show the caret (may be hidden due blinking) and adjust the timer not to
+ * hide it (possibly immediately).
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+ void appear()
+ {
+ // All machinery is only required if the carret is blinking.
+ if (blinkListener != null)
+ {
+ blinkListener.ignoreNextEvent = true;
+
+ // If the caret is visible, erase the current position by repainting
+ // over.
+ if (visible)
+ repaint();
+
+ // Draw the caret in the new position.
+ visible = true;
+
+ Rectangle area = null;
+ int dot = getDot();
+ try
+ {
+ area = getComponent().modelToView(dot);
+ }
+ catch (BadLocationException e)
+ {
+ // Let's ignore that. This shouldn't really occur. But if it
+ // does (it seems that this happens when the model is mutating),
+ // it causes no real damage. Uncomment this for debugging.
+ // e.printStackTrace();
+ }
+ if (area != null)
+ {
+ adjustVisibility(area);
+ if (getMagicCaretPosition() == null)
+ setMagicCaretPosition(new Point(area.x, area.y));
+ damage(area);
+ }
+ }
+ repaint();
+ }
+
+ /**
+ * Returns <code>true</code> if this <code>Caret</code> is blinking,
+ * and <code>false</code> if not. The returned value is independent of
+ * the visiblity of this <code>Caret</code> as returned by {@link #isVisible()}.
+ *
+ * @return <code>true</code> if this <code>Caret</code> is blinking,
+ * and <code>false</code> if not.
+ * @see #isVisible()
+ * @since 1.5
+ */
+ public boolean isActive()
+ {
+ if (blinkTimer != null)
+ return blinkTimer.isRunning();
+
+ return false;
+ }
+
+ /**
+ * Returns <code>true</code> if this <code>Caret</code> is currently visible,
+ * and <code>false</code> if it is not.
+ *
+ * @return <code>true</code> if this <code>Caret</code> is currently visible,
+ * and <code>false</code> if it is not
+ */
+ public boolean isVisible()
+ {
+ return visible && active;
+ }
+
+ /**
+ * Sets the visibility state of the caret. <code>true</code> shows the
+ * <code>Caret</code>, <code>false</code> hides it.
+ *
+ * @param v the visibility to set
+ */
+ public void setVisible(boolean v)
+ {
+ if (v != visible)
+ {
+ visible = v;
+ updateTimerStatus();
+ Rectangle area = null;
+ int dot = getDot();
+ try
+ {
+ area = getComponent().modelToView(dot);
+ }
+ catch (BadLocationException e)
+ {
+ AssertionError ae;
+ ae = new AssertionError("Unexpected bad caret location: " + dot);
+ ae.initCause(e);
+ throw ae;
+ }
+ if (area != null)
+ damage(area);
+ }
+ }
+
+ /**
+ * Returns the {@link Highlighter.HighlightPainter} that should be used
+ * to paint the selection.
+ *
+ * @return the {@link Highlighter.HighlightPainter} that should be used
+ * to paint the selection
+ */
+ protected Highlighter.HighlightPainter getSelectionPainter()
+ {
+ return DefaultHighlighter.DefaultPainter;
+ }
+
+ /**
+ * Updates the carets rectangle properties to the specified rectangle and
+ * repaints the caret.
+ *
+ * @param r the rectangle to set as the caret rectangle
+ */
+ protected void damage(Rectangle r)
+ {
+ if (r == null)
+ return;
+ x = r.x;
+ y = r.y;
+ width = 1;
+ // height is normally set in paint and we leave it untouched. However, we
+ // must set a valid value here, since otherwise the painting mechanism
+ // sets a zero clip and never calls paint.
+ if (height <= 0)
+ try
+ {
+ height = textComponent.modelToView(dot).height;
+ }
+ catch (BadLocationException ble)
+ {
+ // Should not happen.
+ throw new InternalError("Caret location not within document range.");
+ }
+
+ repaint();
+ }
+
+ /**
+ * Adjusts the text component so that the caret is visible. This default
+ * implementation simply calls
+ * {@link JComponent#scrollRectToVisible(Rectangle)} on the text component.
+ * Subclasses may wish to change this.
+ */
+ protected void adjustVisibility(Rectangle rect)
+ {
+ getComponent().scrollRectToVisible(rect);
+ }
+
+ /**
+ * Initializes the blink timer.
+ */
+ private void initBlinkTimer()
+ {
+ // Setup the blink timer.
+ blinkListener = new BlinkTimerListener();
+ blinkTimer = new Timer(getBlinkRate(), blinkListener);
+ blinkTimer.setRepeats(true);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/DefaultEditorKit.java b/libjava/classpath/javax/swing/text/DefaultEditorKit.java
new file mode 100644
index 000000000..b3b7e6077
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultEditorKit.java
@@ -0,0 +1,1702 @@
+/* DefaultEditorKit.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+
+import javax.swing.Action;
+import javax.swing.SwingConstants;
+
+/**
+ * The default implementation of {@link EditorKit}. This <code>EditorKit</code>
+ * a plain text <code>Document</code> and several commands that together
+ * make up a basic editor, like cut / copy + paste.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+public class DefaultEditorKit extends EditorKit
+{
+ static class SelectionPreviousWordAction
+ extends TextAction
+ {
+ SelectionPreviousWordAction()
+ {
+ super(selectionPreviousWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ try
+ {
+ JTextComponent t = getTextComponent(event);
+
+ if (t != null)
+ {
+ int offs = Utilities.getPreviousWord(t, t.getCaretPosition());
+
+ Caret c = t.getCaret();
+ c.moveDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+
+ static class SelectionNextWordAction
+ extends TextAction
+ {
+ SelectionNextWordAction()
+ {
+ super(selectionNextWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ try
+ {
+ JTextComponent t = getTextComponent(event);
+
+ if (t != null)
+ {
+ int offs = Utilities.getNextWord(t, t.getCaretPosition());
+
+ Caret c = t.getCaret();
+ c.moveDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+
+ static class SelectionBeginWordAction extends TextAction
+ {
+ SelectionBeginWordAction()
+ {
+ super(selectionBeginWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ try
+ {
+ JTextComponent t = getTextComponent(event);
+
+ if (t != null)
+ {
+ int offs = Utilities.getWordStart(t, t.getCaretPosition());
+
+ Caret c = t.getCaret();
+ c.moveDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+
+ static class SelectionEndWordAction extends TextAction
+ {
+ SelectionEndWordAction()
+ {
+ super(selectionEndWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ try
+ {
+ JTextComponent t = getTextComponent(event);
+
+ if (t != null)
+ {
+ int offs = Utilities.getWordEnd(t, t.getCaretPosition());
+
+ Caret c = t.getCaret();
+ c.moveDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+
+ static class BeginWordAction extends TextAction
+ {
+ BeginWordAction()
+ {
+ super(beginWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ try
+ {
+ JTextComponent t = getTextComponent(event);
+
+ if (t != null)
+ {
+ int offs = Utilities.getWordStart(t, t.getCaretPosition());
+
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+
+ static class EndWordAction extends TextAction
+ {
+ EndWordAction()
+ {
+ super(endWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ try
+ {
+ JTextComponent t = getTextComponent(event);
+
+ if (t != null)
+ {
+ int offs = Utilities.getWordEnd(t, t.getCaretPosition());
+
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+
+ static class PreviousWordAction
+ extends TextAction
+ {
+ PreviousWordAction()
+ {
+ super(previousWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ try
+ {
+ JTextComponent t = getTextComponent(event);
+
+ if (t != null)
+ {
+ int offs = Utilities.getPreviousWord(t, t.getCaretPosition());
+
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+
+ static class NextWordAction
+ extends TextAction
+ {
+ NextWordAction()
+ {
+ super(nextWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ try
+ {
+ JTextComponent t = getTextComponent(event);
+
+ if (t != null)
+ {
+ int offs = Utilities.getNextWord(t, t.getCaretPosition());
+
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+
+ static class SelectAllAction
+ extends TextAction
+ {
+ SelectAllAction()
+ {
+ super(selectAllAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.setDot(0);
+ c.moveDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ static class SelectionBeginAction
+ extends TextAction
+ {
+ SelectionBeginAction()
+ {
+ super(selectionBeginAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ c.moveDot(0);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(0).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ static class SelectionEndAction
+ extends TextAction
+ {
+ SelectionEndAction()
+ {
+ super(selectionEndAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.moveDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ static class SelectionBeginLineAction
+ extends TextAction
+ {
+
+ SelectionBeginLineAction()
+ {
+ super(selectionBeginLineAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ try
+ {
+ int offs = Utilities.getRowStart(t, c.getDot());
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ static class SelectionEndLineAction
+ extends TextAction
+ {
+ SelectionEndLineAction()
+ {
+ super(selectionEndLineAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ try
+ {
+ int offs = Utilities.getRowEnd(t, c.getDot());
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ static class SelectLineAction extends TextAction
+ {
+ SelectLineAction()
+ {
+ super(selectLineAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ try
+ {
+ int offs1 = Utilities.getRowStart(t, c.getDot());
+ int offs2 = Utilities.getRowEnd(t, c.getDot());
+ c.setDot(offs2);
+ c.moveDot(offs1);
+ c.setMagicCaretPosition(t.modelToView(offs2).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ static class SelectWordAction extends TextAction
+ {
+ SelectWordAction()
+ {
+ super(selectWordAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ int dot = c.getDot();
+ try
+ {
+ int wordStart = Utilities.getWordStart(t, dot);
+
+ if (dot == wordStart)
+ {
+ // Current cursor position is on the first character in a word.
+ c.setDot(wordStart);
+ c.moveDot(Utilities.getWordEnd(t, wordStart));
+ }
+ else
+ {
+ // Current cursor position is not on the first character
+ // in a word.
+ int nextWord = Utilities.getNextWord(t, dot);
+ int previousWord = Utilities.getPreviousWord(t, dot);
+ int previousWordEnd = Utilities.getWordEnd(t, previousWord);
+
+ // Cursor position is in the space between two words. In such a
+ // situation just select the space.
+ if (dot >= previousWordEnd && dot <= nextWord)
+ {
+ c.setDot(previousWordEnd);
+ c.moveDot(nextWord);
+ }
+ else
+ {
+ // Cursor position is inside a word. Just select it then.
+ c.setDot(previousWord);
+ c.moveDot(previousWordEnd);
+ }
+ }
+
+ // If the position was updated change the magic caret position
+ // as well.
+ if (c.getDot() != dot)
+ c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ static class SelectionDownAction
+ extends TextAction.VerticalMovementAction
+ {
+ SelectionDownAction()
+ {
+ super(selectionDownAction, SwingConstants.SOUTH);
+ }
+
+ protected void actionPerformedImpl(Caret c, int offs)
+ {
+ c.moveDot(offs);
+ }
+
+ }
+
+ static class SelectionUpAction
+ extends TextAction.VerticalMovementAction
+ {
+ SelectionUpAction()
+ {
+ super(selectionUpAction, SwingConstants.NORTH);
+ }
+
+ protected void actionPerformedImpl(Caret c, int offs)
+ {
+ c.moveDot(offs);
+ }
+
+ }
+
+ static class SelectionForwardAction
+ extends TextAction.HorizontalMovementAction
+ {
+ SelectionForwardAction()
+ {
+ super(selectionForwardAction, SwingConstants.EAST);
+ }
+
+ protected void actionPerformedImpl(Caret c, int offs)
+ {
+ c.moveDot(offs);
+ }
+ }
+
+ static class SelectionBackwardAction
+ extends TextAction.HorizontalMovementAction
+ {
+ SelectionBackwardAction()
+ {
+ super(selectionBackwardAction, SwingConstants.WEST);
+ }
+
+ protected void actionPerformedImpl(Caret c, int offs)
+ {
+ c.moveDot(offs);
+ }
+ }
+
+ static class DownAction
+ extends TextAction.VerticalMovementAction
+ {
+ DownAction()
+ {
+ super(downAction, SwingConstants.SOUTH);
+ }
+
+ protected void actionPerformedImpl(Caret c, int offs)
+ {
+ c.setDot(offs);
+ }
+ }
+
+ static class UpAction
+ extends TextAction.VerticalMovementAction
+ {
+ UpAction()
+ {
+ super(upAction, SwingConstants.NORTH);
+ }
+
+ protected void actionPerformedImpl(Caret c, int offs)
+ {
+ c.setDot(offs);
+ }
+
+ }
+
+ static class ForwardAction
+ extends TextAction.HorizontalMovementAction
+ {
+ ForwardAction()
+ {
+ super(forwardAction, SwingConstants.EAST);
+ }
+
+ protected void actionPerformedImpl(Caret c, int offs)
+ {
+ c.setDot(offs);
+ }
+
+ }
+
+ static class BackwardAction
+ extends TextAction.HorizontalMovementAction
+ {
+ BackwardAction()
+ {
+ super(backwardAction, SwingConstants.WEST);
+ }
+
+ protected void actionPerformedImpl(Caret c, int offs)
+ {
+ c.setDot(offs);
+ }
+
+ }
+
+ static class DeletePrevCharAction
+ extends TextAction
+ {
+ DeletePrevCharAction()
+ {
+ super(deletePrevCharAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ try
+ {
+ int pos = t.getSelectionStart();
+ int len = t.getSelectionEnd() - pos;
+
+ if (len > 0)
+ t.getDocument().remove(pos, len);
+ else if (pos > 0)
+ {
+ pos--;
+ t.getDocument().remove(pos, 1);
+ Caret c = t.getCaret();
+ c.setDot(pos);
+ c.setMagicCaretPosition(t.modelToView(pos).getLocation());
+ }
+ }
+ catch (BadLocationException e)
+ {
+ // FIXME: we're not authorized to throw this.. swallow it?
+ }
+ }
+ }
+ }
+
+ static class DeleteNextCharAction
+ extends TextAction
+ {
+ DeleteNextCharAction()
+ {
+ super(deleteNextCharAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ try
+ {
+ int pos = t.getSelectionStart();
+ int len = t.getSelectionEnd() - pos;
+
+ if (len > 0)
+ t.getDocument().remove(pos, len);
+ else if (pos < t.getDocument().getLength())
+ t.getDocument().remove(pos, 1);
+
+ Caret c = t.getCaret();
+ c.setDot(pos);
+ c.setMagicCaretPosition(t.modelToView(pos).getLocation());
+ }
+ catch (BadLocationException e)
+ {
+ // FIXME: we're not authorized to throw this.. swallow it?
+ }
+ }
+ }
+ }
+
+ static class EndLineAction
+ extends TextAction
+ {
+ EndLineAction()
+ {
+ super(endLineAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ try
+ {
+ int offs = Utilities.getRowEnd(t, t.getCaretPosition());
+ if (offs > -1)
+ {
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // Nothing to do here
+ }
+ }
+ }
+ }
+
+ static class BeginLineAction
+ extends TextAction
+ {
+ BeginLineAction()
+ {
+ super(beginLineAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ try
+ {
+ int offs = Utilities.getRowStart(t, t.getCaretPosition());
+ if (offs > -1)
+ {
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // Do nothing here.
+ }
+ }
+ }
+ }
+
+ static class BeginAction extends TextAction
+ {
+
+ BeginAction()
+ {
+ super(beginAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ c.setDot(0);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(0).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ static class EndAction extends TextAction
+ {
+
+ EndAction()
+ {
+ super(endAction);
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ {
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a beep on the PC speaker.
+ *
+ * @see Toolkit#beep()
+ */
+ public static class BeepAction extends TextAction
+ {
+ /**
+ * Creates a new <code>BeepAction</code>.
+ */
+ public BeepAction()
+ {
+ super(beepAction);
+ }
+
+ /**
+ * Performs the <code>Action</code>.
+ *
+ * @param event the action event describing the user action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ Toolkit.getDefaultToolkit().beep();
+ }
+ }
+
+ /**
+ * Copies the selected content into the system clipboard.
+ *
+ * @see Toolkit#getSystemClipboard()
+ * @see CutAction
+ * @see PasteAction
+ */
+ public static class CopyAction extends TextAction
+ {
+
+ /**
+ * Create a new <code>CopyAction</code>.
+ */
+ public CopyAction()
+ {
+ super(copyAction);
+ }
+
+ /**
+ * Performs the <code>Action</code>.
+ *
+ * @param event the action event describing the user action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.copy();
+ }
+ }
+
+
+ /**
+ * Copies the selected content into the system clipboard and deletes the
+ * selection.
+ *
+ * @see Toolkit#getSystemClipboard()
+ * @see CopyAction
+ * @see PasteAction
+ */
+ public static class CutAction extends TextAction
+ {
+
+ /**
+ * Create a new <code>CutAction</code>.
+ */
+ public CutAction()
+ {
+ super(cutAction);
+ }
+
+ /**
+ * Performs the <code>Action</code>.
+ *
+ * @param event the action event describing the user action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.cut();
+ }
+ }
+
+ /**
+ * Copies content from the system clipboard into the editor.
+ *
+ * @see Toolkit#getSystemClipboard()
+ * @see CopyAction
+ * @see CutAction
+ */
+ public static class PasteAction extends TextAction
+ {
+
+ /**
+ * Create a new <code>PasteAction</code>.
+ */
+ public PasteAction()
+ {
+ super(pasteAction);
+ }
+
+ /**
+ * Performs the <code>Action</code>.
+ *
+ * @param event the action event describing the user action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.paste();
+ }
+ }
+
+ /**
+ * This action is executed as default action when a KEY_TYPED
+ * event is received and no keymap entry exists for that. The purpose
+ * of this action is to filter out a couple of characters. This includes
+ * the control characters and characters with the ALT-modifier.
+ *
+ * If an event does not get filtered, it is inserted into the document
+ * of the text component. If there is some text selected in the text
+ * component, this text will be replaced.
+ */
+ public static class DefaultKeyTypedAction
+ extends TextAction
+ {
+
+ /**
+ * Creates a new <code>DefaultKeyTypedAction</code>.
+ */
+ public DefaultKeyTypedAction()
+ {
+ super(defaultKeyTypedAction);
+ }
+
+ /**
+ * Performs the <code>Action</code>.
+ *
+ * @param event the action event describing the user action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // first we filter the following events:
+ // - control characters
+ // - key events with the ALT modifier
+ JTextComponent target = getTextComponent(event);
+ if ((target != null) && (event != null))
+ {
+ if ((target.isEditable()) && (target.isEnabled()))
+ {
+ String content = event.getActionCommand();
+ int mod = event.getModifiers();
+ if ((content != null) && (content.length() > 0)
+ && (mod & ActionEvent.ALT_MASK) == 0
+ && (mod & ActionEvent.CTRL_MASK) == 0)
+ {
+ char c = content.charAt(0);
+ if ((c >= 0x20) && (c != 0x7F))
+ {
+ target.replaceSelection(content);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This action inserts a newline character into the document
+ * of the text component. This is typically triggered by hitting
+ * ENTER on the keyboard.
+ */
+ public static class InsertBreakAction extends TextAction
+ {
+
+ /**
+ * Creates a new <code>InsertBreakAction</code>.
+ */
+ public InsertBreakAction()
+ {
+ super(insertBreakAction);
+ }
+
+ /**
+ * Performs the <code>Action</code>.
+ *
+ * @param event the action event describing the user action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ t.replaceSelection("\n");
+ }
+ }
+
+ /**
+ * Places content into the associated editor. If there currently is a
+ * selection, this selection is replaced.
+ */
+ // FIXME: Figure out what this Action is supposed to do. Obviously text
+ // that is entered by the user is inserted through DefaultKeyTypedAction.
+ public static class InsertContentAction extends TextAction
+ {
+
+ /**
+ * Creates a new <code>InsertContentAction</code>.
+ */
+ public InsertContentAction()
+ {
+ super(insertContentAction);
+ }
+
+ /**
+ * Performs the <code>Action</code>.
+ *
+ * @param event the action event describing the user action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // FIXME: Figure out what this Action is supposed to do. Obviously text
+ // that is entered by the user is inserted through DefaultKeyTypedAction.
+ }
+ }
+
+ /**
+ * Inserts a TAB character into the text editor.
+ */
+ public static class InsertTabAction extends TextAction
+ {
+
+ /**
+ * Creates a new <code>TabAction</code>.
+ */
+ public InsertTabAction()
+ {
+ super(insertTabAction);
+ }
+
+ /**
+ * Performs the <code>Action</code>.
+ *
+ * @param event the action event describing the user action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ if (t != null)
+ t.replaceSelection("\t");
+ }
+ }
+
+ /**
+ * The serial version of DefaultEditorKit.
+ */
+ private static final long serialVersionUID = 9017245433028523428L;
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one character
+ * backwards.
+ *
+ * @see #getActions()
+ */
+ public static final String backwardAction = "caret-backward";
+
+ /**
+ * The name of the <code>Action</code> that creates a beep in the speaker.
+ *
+ * @see #getActions()
+ */
+ public static final String beepAction = "beep";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the <code>Document</code>.
+ *
+ * @see #getActions()
+ */
+ public static final String beginAction = "caret-begin";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the current line.
+ *
+ * @see #getActions()
+ */
+ public static final String beginLineAction = "caret-begin-line";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the current paragraph.
+ *
+ * @see #getActions()
+ */
+ public static final String beginParagraphAction = "caret-begin-paragraph";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the current word.
+ *
+ * @see #getActions()
+ */
+ public static final String beginWordAction = "caret-begin-word";
+
+ /**
+ * The name of the <code>Action</code> that copies the selected content
+ * into the system clipboard.
+ *
+ * @see #getActions()
+ */
+ public static final String copyAction = "copy-to-clipboard";
+
+ /**
+ * The name of the <code>Action</code> that copies the selected content
+ * into the system clipboard and removes the selection.
+ *
+ * @see #getActions()
+ */
+ public static final String cutAction = "cut-to-clipboard";
+
+ /**
+ * The name of the <code>Action</code> that is performed by default if
+ * a key is typed and there is no keymap entry.
+ *
+ * @see #getActions()
+ */
+ public static final String defaultKeyTypedAction = "default-typed";
+
+ /**
+ * The name of the <code>Action</code> that deletes the character that
+ * follows the current caret position.
+ *
+ * @see #getActions()
+ */
+ public static final String deleteNextCharAction = "delete-next";
+
+ /**
+ * The name of the <code>Action</code> that deletes the character that
+ * precedes the current caret position.
+ *
+ * @see #getActions()
+ */
+ public static final String deletePrevCharAction = "delete-previous";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one line down.
+ *
+ * @see #getActions()
+ */
+ public static final String downAction = "caret-down";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the end
+ * of the <code>Document</code>.
+ *
+ * @see #getActions()
+ */
+ public static final String endAction = "caret-end";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the end
+ * of the current line.
+ *
+ * @see #getActions()
+ */
+ public static final String endLineAction = "caret-end-line";
+
+ /**
+ * When a document is read and an CRLF is encountered, then we add a property
+ * with this name and a value of &quot;\r\n&quot;.
+ */
+ public static final String EndOfLineStringProperty = "__EndOfLine__";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the end
+ * of the current paragraph.
+ *
+ * @see #getActions()
+ */
+ public static final String endParagraphAction = "caret-end-paragraph";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the end
+ * of the current word.
+ *
+ * @see #getActions()
+ */
+ public static final String endWordAction = "caret-end-word";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one character
+ * forward.
+ *
+ * @see #getActions()
+ */
+ public static final String forwardAction = "caret-forward";
+
+ /**
+ * The name of the <code>Action</code> that inserts a line break.
+ *
+ * @see #getActions()
+ */
+ public static final String insertBreakAction = "insert-break";
+
+ /**
+ * The name of the <code>Action</code> that inserts some content.
+ *
+ * @see #getActions()
+ */
+ public static final String insertContentAction = "insert-content";
+
+ /**
+ * The name of the <code>Action</code> that inserts a TAB.
+ *
+ * @see #getActions()
+ */
+ public static final String insertTabAction = "insert-tab";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the next word.
+ *
+ * @see #getActions()
+ */
+ public static final String nextWordAction = "caret-next-word";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one page down.
+ *
+ * @see #getActions()
+ */
+ public static final String pageDownAction = "page-down";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one page up.
+ *
+ * @see #getActions()
+ */
+ public static final String pageUpAction = "page-up";
+
+ /**
+ * The name of the <code>Action</code> that copies content from the system
+ * clipboard into the document.
+ *
+ * @see #getActions()
+ */
+ public static final String pasteAction = "paste-from-clipboard";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the previous word.
+ *
+ * @see #getActions()
+ */
+ public static final String previousWordAction = "caret-previous-word";
+
+ /**
+ * The name of the <code>Action</code> that sets the editor in read only
+ * mode.
+ *
+ * @see #getActions()
+ */
+ public static final String readOnlyAction = "set-read-only";
+
+ /**
+ * The name of the <code>Action</code> that selects the whole document.
+ *
+ * @see #getActions()
+ */
+ public static final String selectAllAction = "select-all";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one character
+ * backwards, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionBackwardAction = "selection-backward";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the document, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionBeginAction = "selection-begin";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the current line, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionBeginLineAction = "selection-begin-line";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the current paragraph, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionBeginParagraphAction =
+ "selection-begin-paragraph";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the current word, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionBeginWordAction = "selection-begin-word";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one line down,
+ * possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionDownAction = "selection-down";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the end
+ * of the document, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionEndAction = "selection-end";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the end
+ * of the current line, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionEndLineAction = "selection-end-line";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the end
+ * of the current paragraph, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionEndParagraphAction =
+ "selection-end-paragraph";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the end
+ * of the current word, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionEndWordAction = "selection-end-word";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one character
+ * forwards, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionForwardAction = "selection-forward";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the next word, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionNextWordAction = "selection-next-word";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret to the beginning
+ * of the previous word, possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionPreviousWordAction =
+ "selection-previous-word";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one line up,
+ * possibly extending the current selection.
+ *
+ * @see #getActions()
+ */
+ public static final String selectionUpAction = "selection-up";
+
+ /**
+ * The name of the <code>Action</code> that selects the line around the
+ * caret.
+ *
+ * @see #getActions()
+ */
+ public static final String selectLineAction = "select-line";
+
+ /**
+ * The name of the <code>Action</code> that selects the paragraph around the
+ * caret.
+ *
+ * @see #getActions()
+ */
+ public static final String selectParagraphAction = "select-paragraph";
+
+ /**
+ * The name of the <code>Action</code> that selects the word around the
+ * caret.
+ *
+ * @see #getActions()
+ */
+ public static final String selectWordAction = "select-word";
+
+ /**
+ * The name of the <code>Action</code> that moves the caret one line up.
+ *
+ * @see #getActions()
+ */
+ public static final String upAction = "caret-up";
+
+ /**
+ * The name of the <code>Action</code> that sets the editor in read-write
+ * mode.
+ *
+ * @see #getActions()
+ */
+ public static final String writableAction = "set-writable";
+
+ /**
+ * Creates a new <code>DefaultEditorKit</code>.
+ */
+ public DefaultEditorKit()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * The <code>Action</code>s that are supported by the
+ * <code>DefaultEditorKit</code>.
+ */
+ private static Action[] defaultActions =
+ new Action[] {
+ // These classes are public because they are so in the RI.
+ new BeepAction(),
+ new CopyAction(),
+ new CutAction(),
+ new DefaultKeyTypedAction(),
+ new InsertBreakAction(),
+ new InsertContentAction(),
+ new InsertTabAction(),
+ new PasteAction(),
+
+ // These are (package-)private inner classes.
+ new DeleteNextCharAction(),
+ new DeletePrevCharAction(),
+
+ new BeginLineAction(),
+ new SelectionBeginLineAction(),
+
+ new EndLineAction(),
+ new SelectionEndLineAction(),
+
+ new BackwardAction(),
+ new SelectionBackwardAction(),
+
+ new ForwardAction(),
+ new SelectionForwardAction(),
+
+ new UpAction(),
+ new SelectionUpAction(),
+
+ new DownAction(),
+ new SelectionDownAction(),
+
+ new NextWordAction(),
+ new SelectionNextWordAction(),
+
+ new PreviousWordAction(),
+ new SelectionPreviousWordAction(),
+
+ new BeginAction(),
+ new SelectionBeginAction(),
+
+ new EndAction(),
+ new SelectionEndAction(),
+
+ new BeginWordAction(),
+ new SelectionBeginWordAction(),
+
+ new EndWordAction(),
+ new SelectionEndWordAction(),
+
+ new SelectAllAction(),
+ new SelectLineAction(),
+ new SelectWordAction()
+ };
+
+ /**
+ * Creates the <code>Caret</code> for this <code>EditorKit</code>. This
+ * returns a {@link DefaultCaret} in this case.
+ *
+ * @return the <code>Caret</code> for this <code>EditorKit</code>
+ */
+ public Caret createCaret()
+ {
+ return new DefaultCaret();
+ }
+
+ /**
+ * Creates the default {@link Document} that this <code>EditorKit</code>
+ * supports. This is a {@link PlainDocument} in this case.
+ *
+ * @return the default {@link Document} that this <code>EditorKit</code>
+ * supports
+ */
+ public Document createDefaultDocument()
+ {
+ return new PlainDocument();
+ }
+
+ /**
+ * Returns the <code>Action</code>s supported by this <code>EditorKit</code>.
+ *
+ * @return the <code>Action</code>s supported by this <code>EditorKit</code>
+ */
+ public Action[] getActions()
+ {
+ return defaultActions;
+ }
+
+ /**
+ * Returns the content type that this <code>EditorKit</code> supports.
+ * The <code>DefaultEditorKit</code> supports the content type
+ * <code>text/plain</code>.
+ *
+ * @return the content type that this <code>EditorKit</code> supports
+ */
+ public String getContentType()
+ {
+ return "text/plain";
+ }
+
+ /**
+ * Returns a {@link ViewFactory} that is able to create {@link View}s for
+ * the <code>Element</code>s that are used in this <code>EditorKit</code>'s
+ * model. This returns null which lets the UI of the text component supply
+ * <code>View</code>s.
+ *
+ * @return a {@link ViewFactory} that is able to create {@link View}s for
+ * the <code>Element</code>s that are used in this
+ * <code>EditorKit</code>'s model
+ */
+ public ViewFactory getViewFactory()
+ {
+ return null;
+ }
+
+ /**
+ * Reads a document of the supported content type from an {@link InputStream}
+ * into the actual {@link Document} object.
+ *
+ * @param in the stream from which to read the document
+ * @param document the document model into which the content is read
+ * @param offset the offset inside to document where the content is inserted
+ *
+ * @throws BadLocationException if <code>offset</code> is an invalid location
+ * inside <code>document</code>
+ * @throws IOException if something goes wrong while reading from
+ * <code>in</code>
+ */
+ public void read(InputStream in, Document document, int offset)
+ throws BadLocationException, IOException
+ {
+ read(new InputStreamReader(in), document, offset);
+ }
+
+ /**
+ * Reads a document of the supported content type from a {@link Reader}
+ * into the actual {@link Document} object.
+ *
+ * @param in the reader from which to read the document
+ * @param document the document model into which the content is read
+ * @param offset the offset inside to document where the content is inserted
+ *
+ * @throws BadLocationException if <code>offset</code> is an invalid location
+ * inside <code>document</code>
+ * @throws IOException if something goes wrong while reading from
+ * <code>in</code>
+ */
+ public void read(Reader in, Document document, int offset)
+ throws BadLocationException, IOException
+ {
+ BufferedReader reader = new BufferedReader(in);
+
+ String line;
+ CPStringBuilder content = new CPStringBuilder();
+
+ while ((line = reader.readLine()) != null)
+ {
+ content.append(line);
+ content.append("\n");
+ }
+
+ document.insertString(offset, content.substring(0, content.length() - 1),
+ SimpleAttributeSet.EMPTY);
+ }
+
+ /**
+ * Writes the <code>Document</code> (or a fragment of the
+ * <code>Document</code>) to an {@link OutputStream} in the
+ * supported content type format.
+ *
+ * @param out the stream to write to
+ * @param document the document that should be written out
+ * @param offset the beginning offset from where to write
+ * @param len the length of the fragment to write
+ *
+ * @throws BadLocationException if <code>offset</code> or
+ * <code>offset + len</code>is an invalid location inside
+ * <code>document</code>
+ * @throws IOException if something goes wrong while writing to
+ * <code>out</code>
+ */
+ public void write(OutputStream out, Document document, int offset, int len)
+ throws BadLocationException, IOException
+ {
+ write(new OutputStreamWriter(out), document, offset, len);
+ }
+
+ /**
+ * Writes the <code>Document</code> (or a fragment of the
+ * <code>Document</code>) to a {@link Writer} in the
+ * supported content type format.
+ *
+ * @param out the writer to write to
+ * @param document the document that should be written out
+ * @param offset the beginning offset from where to write
+ * @param len the length of the fragment to write
+ *
+ * @throws BadLocationException if <code>offset</code> is an
+ * invalid location inside <code>document</code>.
+ * @throws IOException if something goes wrong while writing to
+ * <code>out</code>
+ */
+ public void write(Writer out, Document document, int offset, int len)
+ throws BadLocationException, IOException
+ {
+ // Throw a BLE if offset is invalid
+ if (offset < 0 || offset > document.getLength())
+ throw new BadLocationException("Tried to write to invalid location",
+ offset);
+
+ // If they gave an overly large len, just adjust it
+ if (offset + len > document.getLength())
+ len = document.getLength() - offset;
+
+ out.write(document.getText(offset, len));
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/DefaultFormatter.java b/libjava/classpath/javax/swing/text/DefaultFormatter.java
new file mode 100644
index 000000000..cd9829d11
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultFormatter.java
@@ -0,0 +1,433 @@
+/* DefaultFormatter.java --
+Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.lang.reflect.Constructor;
+import java.text.ParseException;
+
+import javax.swing.JFormattedTextField;
+
+/**
+ * The <code>DefaultFormatter</code> is a concrete formatter for use in
+ * {@link JFormattedTextField}s.
+ *
+ * It can format arbitrary values by invoking
+ * their {@link Object#toString} method.
+ *
+ * In order to convert a String back to
+ * a value, the value class must provide a single argument constructor that
+ * takes a String object as argument value. If no such constructor is found,
+ * the String itself is passed back by #stringToValue.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
+ implements Cloneable, Serializable
+{
+
+ /**
+ * A {@link DocumentFilter} that intercepts modification of the
+ * JFormattedTextField's Document and commits the value depending
+ * on the value of the <code>commitsOnValidEdit</code> property.
+ *
+ */
+ // FIXME: Handle allowsInvalid and overwriteMode properties
+ private class FormatterDocumentFilter
+ extends DocumentFilter
+ {
+ /**
+ * Invoked when text is removed from a text component.
+ *
+ * @param bypass the FilterBypass to use to mutate the document
+ * @param offset the start position of the modification
+ * @param length the length of the removed text
+ *
+ * @throws BadLocationException if offset or lenght are invalid in
+ * the Document
+ */
+ public void remove(DocumentFilter.FilterBypass bypass, int offset,
+ int length)
+ throws BadLocationException
+ {
+ super.remove(bypass, offset, length);
+ checkValidInput();
+ commitIfAllowed();
+ }
+
+ /**
+ * Invoked when text is inserted into a text component.
+ *
+ * @param bypass the FilterBypass to use to mutate the document
+ * @param offset the start position of the modification
+ * @param text the inserted text
+ * @param attributes the attributes of the inserted text
+ *
+ * @throws BadLocationException if offset or lenght are invalid in
+ * the Document
+ */
+ public void insertString(DocumentFilter.FilterBypass bypass, int offset,
+ String text, AttributeSet attributes)
+ throws BadLocationException
+ {
+ if (overwriteMode == true)
+ replace(bypass, offset, text.length(), text, attributes);
+ else
+ super.insertString(bypass, offset, text, attributes);
+ checkValidInput();
+ commitIfAllowed();
+ }
+
+ /**
+ * Invoked when text is replaced in a text component.
+ *
+ * @param bypass the FilterBypass to use to mutate the document
+ * @param offset the start position of the modification
+ * @param length the length of the removed text
+ * @param text the inserted text
+ * @param attributes the attributes of the inserted text
+ *
+ * @throws BadLocationException if offset or lenght are invalid in
+ * the Document
+ */
+ public void replace(DocumentFilter.FilterBypass bypass, int offset,
+ int length, String text, AttributeSet attributes)
+ throws BadLocationException
+ {
+ super.replace(bypass, offset, length, text, attributes);
+ checkValidInput();
+ commitIfAllowed();
+ }
+
+ /**
+ * Commits the value to the JTextTextField if the property
+ * <code>commitsOnValidEdit</code> is set to <code>true</code>.
+ */
+ private void commitIfAllowed()
+ {
+ if (commitsOnValidEdit == true)
+ try
+ {
+ getFormattedTextField().commitEdit();
+ }
+ catch (ParseException ex)
+ {
+ // ignore invalid edits
+ }
+ }
+
+ /**
+ * Checks if the value in the input field is valid. If the
+ * property allowsInvalid is set to <code>false</code>, then
+ * the string in the input field is not allowed to be entered.
+ */
+ private void checkValidInput()
+ {
+ JFormattedTextField ftf = getFormattedTextField();
+ try
+ {
+ Object newval = stringToValue(ftf.getText());
+ }
+ catch (ParseException ex)
+ {
+ if (!allowsInvalid)
+ {
+ // roll back the input if invalid edits are not allowed
+ try
+ {
+ ftf.setText(valueToString(ftf.getValue()));
+ }
+ catch (ParseException pe)
+ {
+ // if that happens, something serious must be wrong
+ AssertionError ae;
+ ae = new AssertionError("values must be parseable");
+ ae.initCause(pe);
+ throw ae;
+ }
+ }
+ }
+ }
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -355018354457785329L;
+
+ /**
+ * Indicates if the value should be committed after every
+ * valid modification of the Document.
+ */
+ boolean commitsOnValidEdit;
+
+ /**
+ * If <code>true</code> newly inserted characters overwrite existing
+ * values, otherwise insertion is done the normal way.
+ */
+ boolean overwriteMode;
+
+ /**
+ * If <code>true</code> invalid edits are allowed for a limited
+ * time.
+ */
+ boolean allowsInvalid;
+
+ /**
+ * The class that is used for values.
+ */
+ Class valueClass;
+
+ /**
+ * Creates a new instance of <code>DefaultFormatter</code>.
+ */
+ public DefaultFormatter()
+ {
+ commitsOnValidEdit = false;
+ overwriteMode = true;
+ allowsInvalid = true;
+ }
+
+ /**
+ * Installs the formatter on the specified {@link JFormattedTextField}.
+ *
+ * This method does the following things:
+ * <ul>
+ * <li>Display the value of #valueToString in the
+ * <code>JFormattedTextField</code></li>
+ * <li>Install the Actions from #getActions on the <code>JTextField</code>
+ * </li>
+ * <li>Install the DocumentFilter returned by #getDocumentFilter</li>
+ * <li>Install the NavigationFilter returned by #getNavigationFilter</li>
+ * </ul>
+ *
+ * This method is typically not overridden by subclasses. Instead override
+ * one of the mentioned methods in order to customize behaviour.
+ *
+ * @param ftf the {@link JFormattedTextField} in which this formatter
+ * is installed
+ */
+ public void install(JFormattedTextField ftf)
+ {
+ super.install(ftf);
+ }
+
+ /**
+ * Returns <code>true</code> if the value should be committed after
+ * each valid modification of the input field, <code>false</code> if
+ * it should never be committed by this formatter.
+ *
+ * @return the state of the <code>commitsOnValidEdit</code> property
+ *
+ * @see #setCommitsOnValidEdit
+ */
+ public boolean getCommitsOnValidEdit()
+ {
+ return commitsOnValidEdit;
+ }
+
+ /**
+ * Sets the value of the <code>commitsOnValidEdit</code> property.
+ *
+ * @param commitsOnValidEdit the new state of the
+ * <code>commitsOnValidEdit</code> property
+ *
+ * @see #getCommitsOnValidEdit
+ */
+ public void setCommitsOnValidEdit(boolean commitsOnValidEdit)
+ {
+ this.commitsOnValidEdit = commitsOnValidEdit;
+ }
+
+ /**
+ * Returns the value of the <code>overwriteMode</code> property.
+ * If that is set to <code>true</code> then newly inserted characters
+ * overwrite existing values, otherwise the characters are inserted like
+ * normal. The default is <code>true</code>.
+ *
+ * @return the value of the <code>overwriteMode</code> property
+ */
+ public boolean getOverwriteMode()
+ {
+ return overwriteMode;
+ }
+
+ /**
+ * Sets the value of the <code>overwriteMode</code> property.
+ *
+ * If that is set to <code>true</code> then newly inserted characters
+ * overwrite existing values, otherwise the characters are inserted like
+ * normal. The default is <code>true</code>.
+ *
+ * @param overwriteMode the new value for the <code>overwriteMode</code>
+ * property
+ */
+ public void setOverwriteMode(boolean overwriteMode)
+ {
+ this.overwriteMode = overwriteMode;
+ }
+
+ /**
+ * Returns whether or not invalid edits are allowed or not. If invalid
+ * edits are allowed, the JFormattedTextField may temporarily contain invalid
+ * characters.
+ *
+ * @return the value of the allowsInvalid property
+ */
+ public boolean getAllowsInvalid()
+ {
+ return allowsInvalid;
+ }
+
+ /**
+ * Sets the value of the <code>allowsInvalid</code> property.
+ *
+ * @param allowsInvalid the new value for the property
+ *
+ * @see #getAllowsInvalid()
+ */
+ public void setAllowsInvalid(boolean allowsInvalid)
+ {
+ this.allowsInvalid = allowsInvalid;
+ }
+
+ /**
+ * Returns the class that is used for values. When Strings are converted
+ * back to values, this class is used to create new value objects.
+ *
+ * @return the class that is used for values
+ */
+ public Class<?> getValueClass()
+ {
+ return valueClass;
+ }
+
+ /**
+ * Sets the class that is used for values.
+ *
+ * @param valueClass the class that is used for values
+ *
+ * @see #getValueClass()
+ */
+ public void setValueClass(Class<?> valueClass)
+ {
+ this.valueClass = valueClass;
+ }
+
+ /**
+ * Converts a String (from the JFormattedTextField input) to a value.
+ * In order to achieve this, the formatter tries to instantiate an object
+ * of the class returned by #getValueClass() using a single argument
+ * constructor that takes a String argument. If such a constructor cannot
+ * be found, the String itself is returned.
+ *
+ * @param string the string to convert
+ *
+ * @return the value for the string
+ *
+ * @throws ParseException if the string cannot be converted into
+ * a value object (e.g. invalid input)
+ */
+ public Object stringToValue(String string)
+ throws ParseException
+ {
+ Object value = string;
+ Class valueClass = getValueClass();
+ if (valueClass == null)
+ {
+ JFormattedTextField jft = getFormattedTextField();
+ if (jft != null)
+ valueClass = jft.getValue().getClass();
+ }
+ if (valueClass != null)
+ try
+ {
+ Constructor constr = valueClass.getConstructor
+ (new Class[]{String.class});
+ value = constr.newInstance(new Object[]{ string });
+ }
+ catch (NoSuchMethodException ex)
+ {
+ // leave value as string
+ }
+ catch (Exception ex)
+ {
+ throw new ParseException(string, 0);
+ }
+ return value;
+ }
+
+ /**
+ * Converts a value object into a String. This is done by invoking the
+ * {@link Object#toString()} method on the value.
+ *
+ * @param value the value to be converted
+ *
+ * @return the string representation of the value
+ *
+ * @throws ParseException if the value cannot be converted
+ */
+ public String valueToString(Object value)
+ throws ParseException
+ {
+ if (value == null)
+ return "";
+ return value.toString();
+ }
+
+ /**
+ * Creates and returns a clone of this DefaultFormatter.
+ *
+ * @return a clone of this object
+ *
+ * @throws CloneNotSupportedException not thrown here
+ */
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ return super.clone();
+ }
+
+ /**
+ * Returns the DocumentFilter that is used to restrict input.
+ *
+ * @return the DocumentFilter that is used to restrict input
+ */
+ protected DocumentFilter getDocumentFilter()
+ {
+ return new FormatterDocumentFilter();
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/DefaultFormatterFactory.java b/libjava/classpath/javax/swing/text/DefaultFormatterFactory.java
new file mode 100644
index 000000000..a6a9defaf
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultFormatterFactory.java
@@ -0,0 +1,280 @@
+/* DefaultFormatterFactory.java -- FIXME: briefly describe file purpose
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.JFormattedTextField;
+import javax.swing.JFormattedTextField.AbstractFormatter;
+import javax.swing.JFormattedTextField.AbstractFormatterFactory;
+
+/**
+ * This class is Swing's only concrete implementation of
+ * JFormattedTextField.AbstractFormatterFactory. It holds several
+ * formatters and determines the best one to be used based on the
+ * passed-in value from the text field.
+ *
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ * @since 1.4
+ */
+public class DefaultFormatterFactory extends AbstractFormatterFactory implements
+ Serializable
+{
+ /**
+ * The default formatter.
+ **/
+ AbstractFormatter defaultFormatter;
+
+ /**
+ * The formatter to use when the JFormattedTextField has focus and either the
+ * value isn't null or the value is null but no <code>nullFormatter</code>
+ * has been specified.
+ */
+ AbstractFormatter editFormatter;
+
+ /**
+ * The formatter to use when the JFormattedTextField doesn't havefocus and
+ * either the value isn't null or the value is null but no
+ * <code>nullFormatter</code> has been specified.
+ */
+ AbstractFormatter displayFormatter;
+
+ /**
+ * The formatter to use when the value of the JFormattedTextField is null.
+ */
+ AbstractFormatter nullFormatter;
+
+ /**
+ * Creates a DefaultFormatterFactory with no formatters
+ */
+ public DefaultFormatterFactory()
+ {
+ // Nothing to be done here.
+ }
+
+ /**
+ * Creates a new DefaultFormatterFactory with the specified formatters.
+ * @param defaultFormat the formatter to use if no other appropriate non-null
+ * formatted can be found.
+ */
+ public DefaultFormatterFactory(AbstractFormatter defaultFormat)
+ {
+ defaultFormatter = defaultFormat;
+ }
+
+ /**
+ * Creates a new DefaultFormatterFactory with the specified formatters.
+ * @param defaultFormat the formatter to use if no other appropriate non-null
+ * formatted can be found.
+ * @param displayFormat the formatter to use if the JFormattedTextField
+ * doesn't have focus and either the value is not null or the value is null
+ * but no <code>nullFormatter</code> has been specified.
+ */
+ public DefaultFormatterFactory(AbstractFormatter defaultFormat,
+ AbstractFormatter displayFormat)
+ {
+ defaultFormatter = defaultFormat;
+ displayFormatter = displayFormat;
+ }
+
+ /**
+ * Creates a new DefaultFormatterFactory with the specified formatters.
+ * @param defaultFormat the formatter to use if no other appropriate non-null
+ * formatted can be found.
+ * @param displayFormat the formatter to use if the JFormattedTextField
+ * doesn't have focus and either the value is not null or the value is null
+ * but no <code>nullFormatter</code> has been specified.
+ * @param editFormat the formatter to use if the JFormattedTextField has
+ * focus and either the value is not null or the value is null but not
+ * <code>nullFormatter</code> has been specified.
+ */
+ public DefaultFormatterFactory(AbstractFormatter defaultFormat,
+ AbstractFormatter displayFormat,
+ AbstractFormatter editFormat)
+ {
+ defaultFormatter = defaultFormat;
+ displayFormatter = displayFormat;
+ editFormatter = editFormat;
+ }
+
+ /**
+ * Creates a new DefaultFormatterFactory with the specified formatters.
+ * @param defaultFormat the formatter to use if no other appropriate non-null
+ * formatted can be found.
+ * @param displayFormat the formatter to use if the JFormattedTextField
+ * doesn't have focus and either the value is not null or the value is null
+ * but no <code>nullFormatter</code> has been specified.
+ * @param editFormat the formatter to use if the JFormattedTextField has
+ * focus and either the value is not null or the value is null but not
+ * <code>nullFormatter</code> has been specified.
+ * @param nullFormat the formatter to use when the value of the
+ * JFormattedTextField is null.
+ */
+ public DefaultFormatterFactory(AbstractFormatter defaultFormat,
+ AbstractFormatter displayFormat,
+ AbstractFormatter editFormat,
+ AbstractFormatter nullFormat)
+ {
+ defaultFormatter = defaultFormat;
+ displayFormatter = displayFormat;
+ editFormatter = editFormat;
+ nullFormatter = nullFormat;
+ }
+
+ /**
+ * Returns the formatted to be used if no other appropriate non-null
+ * formatter can be found.
+ * @return the formatted to be used if no other appropriate non-null
+ * formatter can be found.
+ */
+ public AbstractFormatter getDefaultFormatter()
+ {
+ return defaultFormatter;
+ }
+
+ /**
+ * Sets the formatted to be used if no other appropriate non-null formatter
+ * can be found.
+ * @param defaultFormatter the formatted to be used if no other appropriate
+ * non-null formatter can be found.
+ */
+ public void setDefaultFormatter(AbstractFormatter defaultFormatter)
+ {
+ this.defaultFormatter = defaultFormatter;
+ }
+
+ /**
+ * Gets the <code>displayFormatter</code>. This is the formatter to use if
+ * the JFormattedTextField is not being edited and either the value is not
+ * null or the value is null and no <code>nullFormatter<code> has been
+ * specified.
+ * @return the formatter to use if
+ * the JFormattedTextField is not being edited and either the value is not
+ * null or the value is null and no <code>nullFormatter<code> has been
+ * specified.
+ */
+ public AbstractFormatter getDisplayFormatter()
+ {
+ return displayFormatter;
+ }
+
+ /**
+ * Sets the <code>displayFormatter</code>. This is the formatter to use if
+ * the JFormattedTextField is not being edited and either the value is not
+ * null or the value is null and no <code>nullFormatter<code> has been
+ * specified.
+ * @param displayFormatter the formatter to use.
+ */
+ public void setDisplayFormatter(AbstractFormatter displayFormatter)
+ {
+ this.displayFormatter = displayFormatter;
+ }
+
+ /**
+ * Gets the <code>editFormatter</code>. This is the formatter to use if the
+ * JFormattedTextField is being edited and either the value is not null or
+ * the value is null and no <code>nullFormatter<code> has been specified.
+ * @return the formatter to use if the JFormattedTextField is being edited
+ * and the value is not null or the value is null but no nullFormatted has
+ * been specified.
+ */
+ public AbstractFormatter getEditFormatter()
+ {
+ return editFormatter;
+ }
+
+ /**
+ * Sets the <code>editFormatter</code>. This is the formatter to use if the
+ * JFormattedTextField is being edited and either the value is not null or
+ * the value is null and no <code>nullFormatter<code> has been specified.
+ * @param editFormatter the formatter to use.
+ */
+ public void setEditFormatter(AbstractFormatter editFormatter)
+ {
+ this.editFormatter = editFormatter;
+ }
+
+ /**
+ * Gets the formatter to use if the value of the JFormattedTextField is null.
+ * @return the formatter to use for null values.
+ */
+ public AbstractFormatter getNullFormatter()
+ {
+ return nullFormatter;
+ }
+
+ /**
+ * Sets the <code>nullFormatter</code>. This is the formatter to use if the
+ * value of the JFormattedTextField is null.
+ * @param nullFormatter the formatter to use for null values.
+ */
+ public void setNullFormatter(AbstractFormatter nullFormatter)
+ {
+ this.nullFormatter = nullFormatter;
+ }
+
+ /**
+ * Returns the appropriate formatter based on the state of
+ * <code>tf</code>. If <code>tf<code> is null we return null, otherwise
+ * we return one of the following:
+ * 1. Returns <code>nullFormatter</code> if <code>tf.getValue()</code> is
+ * null and <code>nullFormatter</code> is not.
+ * 2. Returns <code>editFormatter</code> if <code>tf.hasFocus()</code> is
+ * true and <code>editFormatter</code> is not null.
+ * 3. Returns <code>displayFormatter</code> if <code>tf.hasFocus()</code> is
+ * false and <code>displayFormatter</code> is not null.
+ * 4. Otherwise returns <code>defaultFormatter</code>.
+ */
+ public AbstractFormatter getFormatter(JFormattedTextField tf)
+ {
+ if (tf == null)
+ return null;
+
+ if (tf.getValue() == null && nullFormatter != null)
+ return nullFormatter;
+
+ if (tf.hasFocus() && editFormatter != null)
+ return editFormatter;
+
+ if (!tf.hasFocus() && displayFormatter != null)
+ return displayFormatter;
+
+ return defaultFormatter;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/DefaultHighlighter.java b/libjava/classpath/javax/swing/text/DefaultHighlighter.java
new file mode 100644
index 000000000..a4264d31a
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultHighlighter.java
@@ -0,0 +1,478 @@
+/* DefaultHighlighter.java -- The default highlight for Swing
+ Copyright (C) 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.swing.SwingUtilities;
+import javax.swing.plaf.TextUI;
+
+/**
+ * The default highlight for Swing text components. It highlights text
+ * by filling the background with a rectangle.
+ */
+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;
+ }
+
+ public void paint(Graphics g, int p0, int p1, Shape bounds,
+ JTextComponent t)
+ {
+ if (p0 == p1)
+ return;
+
+ Rectangle rect = bounds.getBounds();
+
+ Color col = getColor();
+ if (col == null)
+ col = t.getSelectionColor();
+ g.setColor(col);
+
+ TextUI ui = t.getUI();
+
+ try
+ {
+
+ Rectangle l0 = ui.modelToView(t, p0, null);
+ Rectangle l1 = ui.modelToView(t, p1, null);
+
+ // Note: The computed locations may lie outside of the allocation
+ // area if the text is scrolled.
+
+ if (l0.y == l1.y)
+ {
+ SwingUtilities.computeUnion(l0.x, l0.y, l0.width, l0.height, l1);
+
+ // Paint only inside the allocation area.
+ SwingUtilities.computeIntersection(rect.x, rect.y, rect.width,
+ rect.height, l1);
+
+ g.fillRect(l1.x, l1.y, l1.width, l1.height);
+ }
+ else
+ {
+ // 1. The line of p0 is painted from the position of p0
+ // to the right border.
+ // 2. All lines between the ones where p0 and p1 lie on
+ // are completely highlighted. The allocation area is used to find
+ // out the bounds.
+ // 3. The final line is painted from the left border to the
+ // position of p1.
+
+ int firstLineWidth = rect.x + rect.width - l0.x;
+ g.fillRect(l0.x, l0.y, firstLineWidth, l0.height);
+ if (l0.y + l0.height != l1.y)
+ {
+ g.fillRect(rect.x, l0.y + l0.height, rect.width,
+ l1.y - l0.y - l0.height);
+ }
+ g.fillRect(rect.x, l1.y, l1.x - rect.x, l1.height);
+ }
+ }
+ catch (BadLocationException ex)
+ {
+ // Can't render. Comment out for debugging.
+ // ex.printStackTrace();
+ }
+ }
+
+ public Shape paintLayer(Graphics g, int p0, int p1, Shape bounds,
+ JTextComponent c, View view)
+ {
+ Color col = getColor();
+ if (col == null)
+ col = c.getSelectionColor();
+ g.setColor(col);
+
+ Rectangle rect = null;
+ if (p0 == view.getStartOffset() && p1 == view.getEndOffset())
+ {
+ // Paint complete bounds region.
+ rect = bounds instanceof Rectangle ? (Rectangle) bounds
+ : bounds.getBounds();
+ }
+ else
+ {
+ // Only partly inside the view.
+ try
+ {
+ Shape s = view.modelToView(p0, Position.Bias.Forward,
+ p1, Position.Bias.Backward,
+ bounds);
+ rect = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
+ }
+ catch (BadLocationException ex)
+ {
+ // Can't render the highlight.
+ }
+ }
+
+ if (rect != null)
+ {
+ g.fillRect(rect.x, rect.y, rect.width, rect.height);
+ }
+ return rect;
+ }
+ }
+
+ private class HighlightEntry implements Highlighter.Highlight
+ {
+ Position p0;
+ Position p1;
+ Highlighter.HighlightPainter painter;
+
+ public HighlightEntry(Position p0, Position p1,
+ Highlighter.HighlightPainter painter)
+ {
+ this.p0 = p0;
+ this.p1 = p1;
+ this.painter = painter;
+ }
+
+ public int getStartOffset()
+ {
+ return p0.getOffset();
+ }
+
+ public int getEndOffset()
+ {
+ return p1.getOffset();
+ }
+
+ public Highlighter.HighlightPainter getPainter()
+ {
+ return painter;
+ }
+ }
+
+ /**
+ * A HighlightEntry that is used for LayerPainter painters. In addition
+ * to the info maintained by the HighlightEntry, this class maintains
+ * a painting rectangle. This is used as repaint region when the
+ * highlight changes and the text component needs repainting.
+ */
+ private class LayerHighlightEntry
+ extends HighlightEntry
+ {
+
+ /**
+ * The paint rectangle.
+ */
+ Rectangle paintRect = new Rectangle();
+
+ LayerHighlightEntry(Position p0, Position p1,
+ Highlighter.HighlightPainter p)
+ {
+ super(p0, p1, p);
+ }
+
+ /**
+ * Paints the highlight by calling the LayerPainter. This
+ * restricts the area to be painted by startOffset and endOffset
+ * and manages the paint rectangle.
+ */
+ void paintLayeredHighlight(Graphics g, int p0, int p1, Shape bounds,
+ JTextComponent tc, View view)
+ {
+ p0 = Math.max(getStartOffset(), p0);
+ p1 = Math.min(getEndOffset(), p1);
+
+ Highlighter.HighlightPainter painter = getPainter();
+ if (painter instanceof LayerPainter)
+ {
+ LayerPainter layerPainter = (LayerPainter) painter;
+ Shape area = layerPainter.paintLayer(g, p0, p1, bounds, tc, view);
+ Rectangle rect;
+ if (area instanceof Rectangle && paintRect != null)
+ rect = (Rectangle) area;
+ else
+ rect = area.getBounds();
+
+ if (paintRect.width == 0 || paintRect.height == 0)
+ paintRect = rect.getBounds();
+ else
+ paintRect = SwingUtilities.computeUnion(rect.x, rect.y, rect.width,
+ rect.height, paintRect);
+ }
+ }
+ }
+
+ /**
+ * @specnote final as of 1.4
+ */
+ public static final LayeredHighlighter.LayerPainter DefaultPainter =
+ new DefaultHighlightPainter(null);
+
+ private JTextComponent textComponent;
+ private ArrayList highlights = new ArrayList();
+ private boolean drawsLayeredHighlights = true;
+
+ public DefaultHighlighter()
+ {
+ // Nothing to do here.
+ }
+
+ public boolean getDrawsLayeredHighlights()
+ {
+ return drawsLayeredHighlights;
+ }
+
+ public void setDrawsLayeredHighlights(boolean newValue)
+ {
+ drawsLayeredHighlights = newValue;
+ }
+
+ private void checkPositions(int p0, int p1)
+ throws BadLocationException
+ {
+ if (p0 < 0)
+ throw new BadLocationException("DefaultHighlighter", p0);
+
+ if (p1 < p0)
+ throw new BadLocationException("DefaultHighlighter", p1);
+ }
+
+ public void install(JTextComponent c)
+ {
+ textComponent = c;
+ removeAllHighlights();
+ }
+
+ public void deinstall(JTextComponent c)
+ {
+ textComponent = null;
+ }
+
+ public Object addHighlight(int p0, int p1,
+ Highlighter.HighlightPainter painter)
+ throws BadLocationException
+ {
+ checkPositions(p0, p1);
+ HighlightEntry entry;
+ Document doc = textComponent.getDocument();
+ Position pos0 = doc.createPosition(p0);
+ Position pos1 = doc.createPosition(p1);
+ if (getDrawsLayeredHighlights() && painter instanceof LayerPainter)
+ entry = new LayerHighlightEntry(pos0, pos1, painter);
+ else
+ entry = new HighlightEntry(pos0, pos1, painter);
+ highlights.add(entry);
+
+ textComponent.getUI().damageRange(textComponent, p0, p1);
+
+ return entry;
+ }
+
+ public void removeHighlight(Object tag)
+ {
+ HighlightEntry entry = (HighlightEntry) tag;
+ if (entry instanceof LayerHighlightEntry)
+ {
+ LayerHighlightEntry lEntry = (LayerHighlightEntry) entry;
+ Rectangle paintRect = lEntry.paintRect;
+ textComponent.repaint(paintRect.x, paintRect.y, paintRect.width,
+ paintRect.height);
+ }
+ else
+ {
+ textComponent.getUI().damageRange(textComponent,
+ entry.getStartOffset(),
+ entry.getEndOffset());
+ }
+ highlights.remove(tag);
+
+ }
+
+ public void removeAllHighlights()
+ {
+ // Repaint damaged region.
+ int minX = 0;
+ int maxX = 0;
+ int minY = 0;
+ int maxY = 0;
+ int p0 = -1;
+ int p1 = -1;
+ for (Iterator i = highlights.iterator(); i.hasNext();)
+ {
+ HighlightEntry e = (HighlightEntry) i.next();
+ if (e instanceof LayerHighlightEntry)
+ {
+ LayerHighlightEntry le = (LayerHighlightEntry) e;
+ Rectangle r = le.paintRect;
+ minX = Math.min(r.x, minX);
+ maxX = Math.max(r.x + r.width, maxX);
+ minY = Math.min(r.y, minY);
+ maxY = Math.max(r.y + r.height, maxY);
+ }
+ else
+ {
+ if (p0 == -1 || p1 == -1)
+ {
+ p0 = e.getStartOffset();
+ p1 = e.getEndOffset();
+ }
+ else
+ {
+ p0 = Math.min(p0, e.getStartOffset());
+ p1 = Math.max(p1, e.getEndOffset());
+ }
+ }
+ if (minX != maxX && minY != maxY)
+ textComponent.repaint(minX, minY, maxX - minX, maxY - minY);
+ if (p0 != -1 && p1 != -1)
+ {
+ TextUI ui = textComponent.getUI();
+ ui.damageRange(textComponent, p0, p1);
+ }
+
+ }
+ highlights.clear();
+ }
+
+ public Highlighter.Highlight[] getHighlights()
+ {
+ return (Highlighter.Highlight[])
+ highlights.toArray(new Highlighter.Highlight[highlights.size()]);
+ }
+
+ public void changeHighlight(Object tag, int n0, int n1)
+ throws BadLocationException
+ {
+ Document doc = textComponent.getDocument();
+ TextUI ui = textComponent.getUI();
+ if (tag instanceof LayerHighlightEntry)
+ {
+ LayerHighlightEntry le = (LayerHighlightEntry) tag;
+ Rectangle r = le.paintRect;
+ if (r.width > 0 && r.height > 0)
+ textComponent.repaint(r.x, r.y, r.width, r.height);
+ r.width = 0;
+ r.height = 0;
+ le.p0 = doc.createPosition(n0);
+ le.p1 = doc.createPosition(n1);
+ ui.damageRange(textComponent, Math.min(n0, n1), Math.max(n0, n1));
+ }
+ else if (tag instanceof HighlightEntry)
+ {
+ HighlightEntry e = (HighlightEntry) tag;
+ int p0 = e.getStartOffset();
+ int p1 = e.getEndOffset();
+ if (p0 == n0)
+ {
+ ui.damageRange(textComponent, Math.min(p1, n1),
+ Math.max(p1, n1));
+ }
+ else if (n1 == p1)
+ {
+ ui.damageRange(textComponent, Math.min(p0, n0),
+ Math.max(p0, n0));
+ }
+ else
+ {
+ ui.damageRange(textComponent, p0, p1);
+ ui.damageRange(textComponent, n0, n1);
+ }
+ e.p0 = doc.createPosition(n0);
+ e.p1 = doc.createPosition(n1);
+ }
+ }
+
+ public void paintLayeredHighlights(Graphics g, int p0, int p1,
+ Shape viewBounds, JTextComponent editor,
+ View view)
+ {
+ for (Iterator i = highlights.iterator(); i.hasNext();)
+ {
+ Object o = i.next();
+ if (o instanceof LayerHighlightEntry)
+ {
+ LayerHighlightEntry entry = (LayerHighlightEntry) o;
+ int start = entry.getStartOffset();
+ int end = entry.getEndOffset();
+ if ((p0 < start && p1 > start) || (p0 >= start && p0 < end))
+ entry.paintLayeredHighlight(g, p0, p1, viewBounds, editor, view);
+ }
+ }
+ }
+
+ public void paint(Graphics g)
+ {
+ int size = highlights.size();
+
+ // Check if there are any highlights.
+ if (size == 0)
+ return;
+
+ // Prepares the rectangle of the inner drawing area.
+ Insets insets = textComponent.getInsets();
+ Shape bounds =
+ new Rectangle(insets.left,
+ insets.top,
+ textComponent.getWidth() - insets.left - insets.right,
+ textComponent.getHeight() - insets.top - insets.bottom);
+
+ for (int index = 0; index < size; ++index)
+ {
+ HighlightEntry entry = (HighlightEntry) highlights.get(index);
+ if (! (entry instanceof LayerHighlightEntry))
+ entry.painter.paint(g, entry.getStartOffset(), entry.getEndOffset(),
+ bounds, textComponent);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
new file mode 100644
index 000000000..9021a1990
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
@@ -0,0 +1,2526 @@
+/* DefaultStyledDocument.java --
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Stack;
+import java.util.Vector;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.UndoableEdit;
+
+/**
+ * The default implementation of {@link StyledDocument}. The document is
+ * modeled as an {@link Element} tree, which has a {@link SectionElement} as
+ * single root, which has one or more {@link AbstractDocument.BranchElement}s
+ * as paragraph nodes and each paragraph node having one or more
+ * {@link AbstractDocument.LeafElement}s as content nodes.
+ *
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class DefaultStyledDocument extends AbstractDocument implements
+ StyledDocument
+{
+
+ /**
+ * An {@link UndoableEdit} that can undo attribute changes to an element.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public static class AttributeUndoableEdit extends AbstractUndoableEdit
+ {
+ /**
+ * A copy of the old attributes.
+ */
+ protected AttributeSet copy;
+
+ /**
+ * The new attributes.
+ */
+ protected AttributeSet newAttributes;
+
+ /**
+ * If the new attributes replaced the old attributes or if they only were
+ * added to them.
+ */
+ protected boolean isReplacing;
+
+ /**
+ * The element that has changed.
+ */
+ protected Element element;
+
+ /**
+ * Creates a new <code>AttributeUndoableEdit</code>.
+ *
+ * @param el
+ * the element that changes attributes
+ * @param newAtts
+ * the new attributes
+ * @param replacing
+ * if the new attributes replace the old or only append to them
+ */
+ public AttributeUndoableEdit(Element el, AttributeSet newAtts,
+ boolean replacing)
+ {
+ element = el;
+ newAttributes = newAtts;
+ isReplacing = replacing;
+ copy = el.getAttributes().copyAttributes();
+ }
+
+ /**
+ * Undos the attribute change. The <code>copy</code> field is set as
+ * attributes on <code>element</code>.
+ */
+ public void undo()
+ {
+ super.undo();
+ AttributeSet atts = element.getAttributes();
+ if (atts instanceof MutableAttributeSet)
+ {
+ MutableAttributeSet mutable = (MutableAttributeSet) atts;
+ mutable.removeAttributes(atts);
+ mutable.addAttributes(copy);
+ }
+ }
+
+ /**
+ * Redos an attribute change. This adds <code>newAttributes</code> to the
+ * <code>element</code>'s attribute set, possibly clearing all attributes
+ * if <code>isReplacing</code> is true.
+ */
+ public void redo()
+ {
+ super.undo();
+ AttributeSet atts = element.getAttributes();
+ if (atts instanceof MutableAttributeSet)
+ {
+ MutableAttributeSet mutable = (MutableAttributeSet) atts;
+ if (isReplacing)
+ mutable.removeAttributes(atts);
+ mutable.addAttributes(newAttributes);
+ }
+ }
+ }
+
+ /**
+ * Carries specification information for new {@link Element}s that should be
+ * created in {@link ElementBuffer}. This allows the parsing process to be
+ * decoupled from the <code>Element</code> creation process.
+ */
+ public static class ElementSpec
+ {
+ /**
+ * This indicates a start tag. This is a possible value for {@link #getType}.
+ */
+ public static final short StartTagType = 1;
+
+ /**
+ * This indicates an end tag. This is a possible value for {@link #getType}.
+ */
+ public static final short EndTagType = 2;
+
+ /**
+ * This indicates a content element. This is a possible value for
+ * {@link #getType}.
+ */
+ public static final short ContentType = 3;
+
+ /**
+ * This indicates that the data associated with this spec should be joined
+ * with what precedes it. This is a possible value for {@link #getDirection}.
+ */
+ public static final short JoinPreviousDirection = 4;
+
+ /**
+ * This indicates that the data associated with this spec should be joined
+ * with what follows it. This is a possible value for {@link #getDirection}.
+ */
+ public static final short JoinNextDirection = 5;
+
+ /**
+ * This indicates that the data associated with this spec should be used to
+ * create a new element. This is a possible value for {@link #getDirection}.
+ */
+ public static final short OriginateDirection = 6;
+
+ /**
+ * This indicates that the data associated with this spec should be joined
+ * to the fractured element. This is a possible value for
+ * {@link #getDirection}.
+ */
+ public static final short JoinFractureDirection = 7;
+
+ /**
+ * The type of the tag.
+ */
+ short type;
+
+ /**
+ * The direction of the tag.
+ */
+ short direction;
+
+ /**
+ * The offset of the content.
+ */
+ int offset;
+
+ /**
+ * The length of the content.
+ */
+ int length;
+
+ /**
+ * The actual content.
+ */
+ char[] content;
+
+ /**
+ * The attributes for the tag.
+ */
+ AttributeSet attributes;
+
+ /**
+ * Creates a new <code>ElementSpec</code> with no content, length or
+ * offset. This is most useful for start and end tags.
+ *
+ * @param a
+ * the attributes for the element to be created
+ * @param type
+ * the type of the tag
+ */
+ public ElementSpec(AttributeSet a, short type)
+ {
+ this(a, type, 0);
+ }
+
+ /**
+ * Creates a new <code>ElementSpec</code> that specifies the length but
+ * not the offset of an element. Such <code>ElementSpec</code>s are
+ * processed sequentially from a known starting point.
+ *
+ * @param a
+ * the attributes for the element to be created
+ * @param type
+ * the type of the tag
+ * @param len
+ * the length of the element
+ */
+ public ElementSpec(AttributeSet a, short type, int len)
+ {
+ this(a, type, null, 0, len);
+ }
+
+ /**
+ * Creates a new <code>ElementSpec</code> with document content.
+ *
+ * @param a
+ * the attributes for the element to be created
+ * @param type
+ * the type of the tag
+ * @param txt
+ * the actual content
+ * @param offs
+ * the offset into the <code>txt</code> array
+ * @param len
+ * the length of the element
+ */
+ public ElementSpec(AttributeSet a, short type, char[] txt, int offs, int len)
+ {
+ attributes = a;
+ this.type = type;
+ offset = offs;
+ length = len;
+ content = txt;
+ direction = OriginateDirection;
+ }
+
+ /**
+ * Sets the type of the element.
+ *
+ * @param type
+ * the type of the element to be set
+ */
+ public void setType(short type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns the type of the element.
+ *
+ * @return the type of the element
+ */
+ public short getType()
+ {
+ return type;
+ }
+
+ /**
+ * Sets the direction of the element.
+ *
+ * @param dir
+ * the direction of the element to be set
+ */
+ public void setDirection(short dir)
+ {
+ direction = dir;
+ }
+
+ /**
+ * Returns the direction of the element.
+ *
+ * @return the direction of the element
+ */
+ public short getDirection()
+ {
+ return direction;
+ }
+
+ /**
+ * Returns the attributes of the element.
+ *
+ * @return the attributes of the element
+ */
+ public AttributeSet getAttributes()
+ {
+ return attributes;
+ }
+
+ /**
+ * Returns the actual content of the element.
+ *
+ * @return the actual content of the element
+ */
+ public char[] getArray()
+ {
+ return content;
+ }
+
+ /**
+ * Returns the offset of the content.
+ *
+ * @return the offset of the content
+ */
+ public int getOffset()
+ {
+ return offset;
+ }
+
+ /**
+ * Returns the length of the content.
+ *
+ * @return the length of the content
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * Returns a String representation of this <code>ElementSpec</code>
+ * describing the type, direction and length of this
+ * <code>ElementSpec</code>.
+ *
+ * @return a String representation of this <code>ElementSpec</code>
+ */
+ public String toString()
+ {
+ CPStringBuilder b = new CPStringBuilder();
+ switch (type)
+ {
+ case StartTagType:
+ b.append("StartTag");
+ break;
+ case EndTagType:
+ b.append("EndTag");
+ break;
+ case ContentType:
+ b.append("Content");
+ break;
+ default:
+ b.append("??");
+ break;
+ }
+
+ b.append(':');
+
+ switch (direction)
+ {
+ case JoinPreviousDirection:
+ b.append("JoinPrevious");
+ break;
+ case JoinNextDirection:
+ b.append("JoinNext");
+ break;
+ case OriginateDirection:
+ b.append("Originate");
+ break;
+ case JoinFractureDirection:
+ b.append("Fracture");
+ break;
+ default:
+ b.append("??");
+ break;
+ }
+
+ b.append(':');
+ b.append(length);
+
+ return b.toString();
+ }
+ }
+
+ /**
+ * Performs all <em>structural</code> changes to the <code>Element</code>
+ * hierarchy. This class was implemented with much help from the document:
+ * http://java.sun.com/products/jfc/tsc/articles/text/element_buffer/index.html.
+ */
+ public class ElementBuffer implements Serializable
+ {
+ /**
+ * Instance of all editing information for an object in the Vector. This class
+ * is used to add information to the DocumentEvent associated with an
+ * insertion/removal/change as well as to store the changes that need to be
+ * made so they can be made all at the same (appropriate) time.
+ */
+ class Edit
+ {
+ /** The element to edit . */
+ Element e;
+
+ /** The index of the change. */
+ int index;
+
+ /** The removed elements. */
+ ArrayList removed = new ArrayList();
+
+ /** The added elements. */
+ ArrayList added = new ArrayList();
+
+ /**
+ * Indicates if this edit contains a fracture.
+ */
+ boolean isFracture;
+
+ /**
+ * Creates a new Edit for the specified element at index i.
+ *
+ * @param el the element
+ * @param i the index
+ */
+ Edit(Element el, int i)
+ {
+ this(el, i, false);
+ }
+
+ /**
+ * Creates a new Edit for the specified element at index i.
+ *
+ * @param el the element
+ * @param i the index
+ * @param frac if this is a fracture edit or not
+ */
+ Edit(Element el, int i, boolean frac)
+ {
+ e = el;
+ index = i;
+ isFracture = frac;
+ }
+
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 1688745877691146623L;
+
+ /** The root element of the hierarchy. */
+ private Element root;
+
+ /** Holds the offset for structural changes. */
+ private int offset;
+
+ /** Holds the end offset for structural changes. */
+ private int endOffset;
+
+ /** Holds the length of structural changes. */
+ private int length;
+
+ /** Holds the position of the change. */
+ private int pos;
+
+ /**
+ * The parent of the fracture.
+ */
+ private Element fracturedParent;
+
+ /**
+ * The fractured child.
+ */
+ private Element fracturedChild;
+
+ /**
+ * Indicates if a fracture has been created.
+ */
+ private boolean createdFracture;
+
+ /**
+ * The current position in the element tree. This is used for bulk inserts
+ * using ElementSpecs.
+ */
+ private Stack elementStack;
+
+ private Edit[] insertPath;
+
+ private boolean recreateLeafs;
+
+ /**
+ * Vector that contains all the edits. Maybe replace by a HashMap.
+ */
+ private ArrayList edits;
+
+ private boolean offsetLastIndex;
+ private boolean offsetLastIndexReplace;
+
+ /**
+ * Creates a new <code>ElementBuffer</code> for the specified
+ * <code>root</code> element.
+ *
+ * @param root
+ * the root element for this <code>ElementBuffer</code>
+ */
+ public ElementBuffer(Element root)
+ {
+ this.root = root;
+ }
+
+ /**
+ * Returns the root element of this <code>ElementBuffer</code>.
+ *
+ * @return the root element of this <code>ElementBuffer</code>
+ */
+ public Element getRootElement()
+ {
+ return root;
+ }
+
+ /**
+ * Removes the content. This method sets some internal parameters and
+ * delegates the work to {@link #removeUpdate}.
+ *
+ * @param offs
+ * the offset from which content is remove
+ * @param len
+ * the length of the removed content
+ * @param ev
+ * the document event that records the changes
+ */
+ public void remove(int offs, int len, DefaultDocumentEvent ev)
+ {
+ prepareEdit(offs, len);
+ removeUpdate();
+ finishEdit(ev);
+ }
+
+ /**
+ * Updates the element structure of the document in response to removal of
+ * content. It removes the affected {@link Element}s from the document
+ * structure.
+ */
+ protected void removeUpdate()
+ {
+ removeElements(root, offset, endOffset);
+ }
+
+ private boolean removeElements(Element elem, int rmOffs0, int rmOffs1)
+ {
+ boolean ret = false;
+ if (! elem.isLeaf())
+ {
+ // Update stack for changes.
+ int index0 = elem.getElementIndex(rmOffs0);
+ int index1 = elem.getElementIndex(rmOffs1);
+ elementStack.push(new Edit(elem, index0));
+ Edit ec = (Edit) elementStack.peek();
+
+ // If the range is contained by one element,
+ // we just forward the request
+ if (index0 == index1)
+ {
+ Element child0 = elem.getElement(index0);
+ if(rmOffs0 <= child0.getStartOffset()
+ && rmOffs1 >= child0.getEndOffset())
+ {
+ // Element totally removed.
+ ec.removed.add(child0);
+ }
+ else if (removeElements(child0, rmOffs0, rmOffs1))
+ {
+ ec.removed.add(child0);
+ }
+ }
+ else
+ {
+ // The removal range spans elements. If we can join
+ // the two endpoints, do it. Otherwise we remove the
+ // interior and forward to the endpoints.
+ Element child0 = elem.getElement(index0);
+ Element child1 = elem.getElement(index1);
+ boolean containsOffs1 = (rmOffs1 < elem.getEndOffset());
+ if (containsOffs1 && canJoin(child0, child1))
+ {
+ // Remove and join.
+ for (int i = index0; i <= index1; i++)
+ {
+ ec.removed.add(elem.getElement(i));
+ }
+ Element e = join(elem, child0, child1, rmOffs0, rmOffs1);
+ ec.added.add(e);
+ }
+ else
+ {
+ // Remove interior and forward.
+ int rmIndex0 = index0 + 1;
+ int rmIndex1 = index1 - 1;
+ if (child0.getStartOffset() == rmOffs0
+ || (index0 == 0 && child0.getStartOffset() > rmOffs0
+ && child0.getEndOffset() <= rmOffs1))
+ {
+ // Start element completely consumed.
+ child0 = null;
+ rmIndex0 = index0;
+ }
+ if (! containsOffs1)
+ {
+ child1 = null;
+ rmIndex1++;
+ }
+ else if (child1.getStartOffset() == rmOffs1)
+ {
+ // End element not touched.
+ child1 = null;
+ }
+ if (rmIndex0 <= rmIndex1)
+ {
+ ec.index = rmIndex0;
+ }
+ for (int i = rmIndex0; i <= rmIndex1; i++)
+ {
+ ec.removed.add(elem.getElement(i));
+ }
+ if (child0 != null)
+ {
+ if(removeElements(child0, rmOffs0, rmOffs1))
+ {
+ ec.removed.add(0, child0);
+ ec.index = index0;
+ }
+ }
+ if (child1 != null)
+ {
+ if(removeElements(child1, rmOffs0, rmOffs1))
+ {
+ ec.removed.add(child1);
+ }
+ }
+ }
+ }
+
+ // Perform changes.
+ pop();
+
+ // Return true if we no longer have any children.
+ if(elem.getElementCount() == (ec.removed.size() - ec.added.size()))
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a document in response to a call to
+ * {@link DefaultStyledDocument#create(ElementSpec[])}.
+ *
+ * @param len the length of the inserted text
+ * @param data the specs for the elements
+ * @param ev the document event
+ */
+ void create(int len, ElementSpec[] data, DefaultDocumentEvent ev)
+ {
+ prepareEdit(offset, len);
+ Element el = root;
+ int index = el.getElementIndex(0);
+ while (! el.isLeaf())
+ {
+ Element child = el.getElement(index);
+ Edit edit = new Edit(el, index, false);
+ elementStack.push(edit);
+ el = child;
+ index = el.getElementIndex(0);
+ }
+ Edit ed = (Edit) elementStack.peek();
+ Element child = ed.e.getElement(ed.index);
+ ed.added.add(createLeafElement(ed.e, child.getAttributes(), getLength(),
+ child.getEndOffset()));
+ ed.removed.add(child);
+ while (elementStack.size() > 1)
+ pop();
+ int n = data.length;
+
+ // Reset root element's attributes.
+ AttributeSet newAtts = null;
+ if (n > 0 && data[0].getType() == ElementSpec.StartTagType)
+ newAtts = data[0].getAttributes();
+ if (newAtts == null)
+ newAtts = SimpleAttributeSet.EMPTY;
+ MutableAttributeSet mAtts = (MutableAttributeSet) root.getAttributes();
+ ev.addEdit(new AttributeUndoableEdit(root, newAtts, true));
+ mAtts.removeAttributes(mAtts);
+ mAtts.addAttributes(newAtts);
+
+ // Insert the specified elements.
+ for (int i = 1; i < n; i++)
+ insertElement(data[i]);
+
+ // Pop remaining stack.
+ while (elementStack.size() > 0)
+ pop();
+
+ finishEdit(ev);
+ }
+
+ private boolean canJoin(Element e0, Element e1)
+ {
+ boolean ret = false;
+ if ((e0 != null) && (e1 != null))
+ {
+ // Don't join a leaf to a branch.
+ boolean isLeaf0 = e0.isLeaf();
+ boolean isLeaf1 = e1.isLeaf();
+ if(isLeaf0 == isLeaf1)
+ {
+ if (isLeaf0)
+ {
+ // Only join leaves if the attributes match, otherwise
+ // style information will be lost.
+ ret = e0.getAttributes().isEqual(e1.getAttributes());
+ }
+ else
+ {
+ // Only join non-leafs if the names are equal. This may result
+ // in loss of style information, but this is typically
+ // acceptable for non-leafs.
+ String name0 = e0.getName();
+ String name1 = e1.getName();
+ if (name0 != null)
+ ret = name0.equals(name1);
+ else if (name1 != null)
+ ret = name1.equals(name0);
+ else // Both names null.
+ ret = true;
+ }
+ }
+ }
+ return ret;
+ }
+
+ private Element join(Element p, Element left, Element right, int rmOffs0,
+ int rmOffs1)
+ {
+ Element joined = null;
+ if (left.isLeaf() && right.isLeaf())
+ {
+ joined = createLeafElement(p, left.getAttributes(),
+ left.getStartOffset(),
+ right.getEndOffset());
+ }
+ else if ((! left.isLeaf()) && (! right.isLeaf()))
+ {
+ // Join two branch elements. This copies the children before
+ // the removal range on the left element, and after the removal
+ // range on the right element. The two elements on the edge
+ // are joined if possible and needed.
+ joined = createBranchElement(p, left.getAttributes());
+ int ljIndex = left.getElementIndex(rmOffs0);
+ int rjIndex = right.getElementIndex(rmOffs1);
+ Element lj = left.getElement(ljIndex);
+ if (lj.getStartOffset() >= rmOffs0)
+ {
+ lj = null;
+ }
+ Element rj = right.getElement(rjIndex);
+ if (rj.getStartOffset() == rmOffs1)
+ {
+ rj = null;
+ }
+ ArrayList children = new ArrayList();
+ // Transfer the left.
+ for (int i = 0; i < ljIndex; i++)
+ {
+ children.add(clone(joined, left.getElement(i)));
+ }
+
+ // Transfer the join/middle.
+ if (canJoin(lj, rj))
+ {
+ Element e = join(joined, lj, rj, rmOffs0, rmOffs1);
+ children.add(e);
+ }
+ else
+ {
+ if (lj != null)
+ {
+ children.add(cloneAsNecessary(joined, lj, rmOffs0, rmOffs1));
+ }
+ if (rj != null)
+ {
+ children.add(cloneAsNecessary(joined, rj, rmOffs0, rmOffs1));
+ }
+ }
+
+ // Transfer the right.
+ int n = right.getElementCount();
+ for (int i = (rj == null) ? rjIndex : rjIndex + 1; i < n; i++)
+ {
+ children.add(clone(joined, right.getElement(i)));
+ }
+
+ // Install the children.
+ Element[] c = new Element[children.size()];
+ c = (Element[]) children.toArray(c);
+ ((BranchElement) joined).replace(0, 0, c);
+ }
+ else
+ {
+ assert false : "Must not happen";
+ }
+ return joined;
+ }
+
+ /**
+ * Performs the actual work for {@link #change}. The elements at the
+ * interval boundaries are split up (if necessary) so that the interval
+ * boundaries are located at element boundaries.
+ */
+ protected void changeUpdate()
+ {
+ boolean didEnd = split(offset, length);
+ if (! didEnd)
+ {
+ // need to do the other end
+ while (elementStack.size() != 0)
+ {
+ pop();
+ }
+ split(offset + length, 0);
+ }
+ while (elementStack.size() != 0)
+ {
+ pop();
+ }
+ }
+
+ /**
+ * Modifies the element structure so that the specified interval starts and
+ * ends at an element boundary. Content and paragraph elements are split and
+ * created as necessary. This also updates the
+ * <code>DefaultDocumentEvent</code> to reflect the structural changes.
+ * The bulk work is delegated to {@link #changeUpdate()}.
+ *
+ * @param offset
+ * the start index of the interval to be changed
+ * @param length
+ * the length of the interval to be changed
+ * @param ev
+ * the <code>DefaultDocumentEvent</code> describing the change
+ */
+ public void change(int offset, int length, DefaultDocumentEvent ev)
+ {
+ prepareEdit(offset, length);
+ changeUpdate();
+ finishEdit(ev);
+ }
+
+ /**
+ * Creates and returns a deep clone of the specified <code>clonee</code>
+ * with the specified parent as new parent.
+ *
+ * This method can only clone direct instances of {@link BranchElement}
+ * or {@link LeafElement}.
+ *
+ * @param parent the new parent
+ * @param clonee the element to be cloned
+ *
+ * @return the cloned element with the new parent
+ */
+ public Element clone(Element parent, Element clonee)
+ {
+ Element clone = clonee;
+ // We can only handle AbstractElements here.
+ if (clonee instanceof BranchElement)
+ {
+ BranchElement branchEl = (BranchElement) clonee;
+ BranchElement branchClone =
+ new BranchElement(parent, branchEl.getAttributes());
+ // Also clone all of the children.
+ int numChildren = branchClone.getElementCount();
+ Element[] cloneChildren = new Element[numChildren];
+ for (int i = 0; i < numChildren; ++i)
+ {
+ cloneChildren[i] = clone(branchClone,
+ branchClone.getElement(i));
+ }
+ branchClone.replace(0, 0, cloneChildren);
+ clone = branchClone;
+ }
+ else if (clonee instanceof LeafElement)
+ {
+ clone = new LeafElement(parent, clonee.getAttributes(),
+ clonee.getStartOffset(),
+ clonee.getEndOffset());
+ }
+ return clone;
+ }
+
+ private Element cloneAsNecessary(Element parent, Element clonee,
+ int rmOffs0, int rmOffs1)
+ {
+ Element cloned;
+ if (clonee.isLeaf())
+ {
+ cloned = createLeafElement(parent, clonee.getAttributes(),
+ clonee.getStartOffset(),
+ clonee.getEndOffset());
+ }
+ else
+ {
+ Element e = createBranchElement(parent, clonee.getAttributes());
+ int n = clonee.getElementCount();
+ ArrayList childrenList = new ArrayList(n);
+ for (int i = 0; i < n; i++)
+ {
+ Element elem = clonee.getElement(i);
+ if (elem.getStartOffset() < rmOffs0
+ || elem.getEndOffset() > rmOffs1)
+ {
+ childrenList.add(cloneAsNecessary(e, elem, rmOffs0,
+ rmOffs1));
+ }
+ }
+ Element[] children = new Element[childrenList.size()];
+ children = (Element[]) childrenList.toArray(children);
+ ((BranchElement) e).replace(0, 0, children);
+ cloned = e;
+ }
+ return cloned;
+ }
+
+ /**
+ * Inserts new <code>Element</code> in the document at the specified
+ * position. Most of the work is done by {@link #insertUpdate}, after some
+ * fields have been prepared for it.
+ *
+ * @param offset
+ * the location in the document at which the content is inserted
+ * @param length
+ * the length of the inserted content
+ * @param data
+ * the element specifications for the content to be inserted
+ * @param ev
+ * the document event that is updated to reflect the structural
+ * changes
+ */
+ public void insert(int offset, int length, ElementSpec[] data,
+ DefaultDocumentEvent ev)
+ {
+ if (length > 0)
+ {
+ prepareEdit(offset, length);
+ insertUpdate(data);
+ finishEdit(ev);
+ }
+ }
+
+ /**
+ * Prepares the state of this object for performing an insert.
+ *
+ * @param offset the offset at which is inserted
+ * @param length the length of the inserted region
+ */
+ private void prepareEdit(int offset, int length)
+ {
+ this.offset = offset;
+ this.pos = offset;
+ this.endOffset = offset + length;
+ this.length = length;
+
+ if (edits == null)
+ edits = new ArrayList();
+ else
+ edits.clear();
+
+ if (elementStack == null)
+ elementStack = new Stack();
+ else
+ elementStack.clear();
+
+ fracturedParent = null;
+ fracturedChild = null;
+ offsetLastIndex = false;
+ offsetLastIndexReplace = false;
+ }
+
+ /**
+ * Finishes an insert. This applies all changes and updates
+ * the DocumentEvent.
+ *
+ * @param ev the document event
+ */
+ private void finishEdit(DefaultDocumentEvent ev)
+ {
+ // This for loop applies all the changes that were made and updates the
+ // DocumentEvent.
+ for (Iterator i = edits.iterator(); i.hasNext();)
+ {
+ Edit edits = (Edit) i.next();
+ Element[] removed = new Element[edits.removed.size()];
+ removed = (Element[]) edits.removed.toArray(removed);
+ Element[] added = new Element[edits.added.size()];
+ added = (Element[]) edits.added.toArray(added);
+ int index = edits.index;
+ BranchElement parent = (BranchElement) edits.e;
+ parent.replace(index, removed.length, added);
+ ElementEdit ee = new ElementEdit(parent, index, removed, added);
+ ev.addEdit(ee);
+ }
+ edits.clear();
+ elementStack.clear();
+ }
+
+ /**
+ * Inserts new content.
+ *
+ * @param data the element specifications for the elements to be inserted
+ */
+ protected void insertUpdate(ElementSpec[] data)
+ {
+ // Push the current path to the stack.
+ Element current = root;
+ int index = current.getElementIndex(offset);
+ while (! current.isLeaf())
+ {
+ Element child = current.getElement(index);
+ int editIndex = child.isLeaf() ? index : index + 1;
+ Edit edit = new Edit(current, editIndex);
+ elementStack.push(edit);
+ current = child;
+ index = current.getElementIndex(offset);
+ }
+
+ // Create a copy of the original path.
+ insertPath = new Edit[elementStack.size()];
+ insertPath = (Edit[]) elementStack.toArray(insertPath);
+
+ // No fracture yet.
+ createdFracture = false;
+
+ // Insert first content tag.
+ int i = 0;
+ recreateLeafs = false;
+ int type = data[0].getType();
+ if (type == ElementSpec.ContentType)
+ {
+ // If the first tag is content we must treat it separately to allow
+ // for joining properly to previous Elements and to ensure that
+ // no extra LeafElements are erroneously inserted.
+ insertFirstContentTag(data);
+ pos += data[0].length;
+ i = 1;
+ }
+ else
+ {
+ createFracture(data);
+ i = 0;
+ }
+
+ // Handle each ElementSpec individually.
+ for (; i < data.length; i++)
+ {
+ insertElement(data[i]);
+ }
+
+ // Fracture if we haven't done yet.
+ if (! createdFracture)
+ fracture(-1);
+
+ // Pop the remaining stack.
+ while (elementStack.size() != 0)
+ pop();
+
+ // Offset last index if necessary.
+ if (offsetLastIndex && offsetLastIndexReplace)
+ insertPath[insertPath.length - 1].index++;
+
+ // Make sure we havea an Edit for each path item that has a change.
+ for (int p = insertPath.length - 1; p >= 0; p--)
+ {
+ Edit edit = insertPath[p];
+ if (edit.e == fracturedParent)
+ edit.added.add(fracturedChild);
+ if ((edit.added.size() > 0 || edit.removed.size() > 0)
+ && ! edits.contains(edit))
+ edits.add(edit);
+ }
+
+ // Remove element that would be created by an insert at 0 with
+ // an initial end tag.
+ if (offset == 0 && fracturedParent != null
+ && data[0].getType() == ElementSpec.EndTagType)
+ {
+ int p;
+ for (p = 0;
+ p < data.length && data[p].getType() == ElementSpec.EndTagType;
+ p++)
+ ;
+
+ Edit edit = insertPath[insertPath.length - p - 1];
+ edit.index--;
+ edit.removed.add(0, edit.e.getElement(edit.index));
+ }
+ }
+
+ private void pop()
+ {
+ Edit edit = (Edit) elementStack.peek();
+ elementStack.pop();
+ if ((edit.added.size() > 0) || (edit.removed.size() > 0))
+ {
+ edits.add(edit);
+ }
+ else if (! elementStack.isEmpty())
+ {
+ Element e = edit.e;
+ if (e.getElementCount() == 0)
+ {
+ // If we pushed a branch element that didn't get
+ // used, make sure its not marked as having been added.
+ edit = (Edit) elementStack.peek();
+ edit.added.remove(e);
+ }
+ }
+ }
+
+ private void insertElement(ElementSpec spec)
+ {
+ if (elementStack.isEmpty())
+ return;
+
+ Edit edit = (Edit) elementStack.peek();
+ switch (spec.getType())
+ {
+ case ElementSpec.StartTagType:
+ switch (spec.getDirection())
+ {
+ case ElementSpec.JoinFractureDirection:
+ // Fracture the tree and ensure the appropriate element
+ // is on top of the stack.
+ if (! createdFracture)
+ {
+ fracture(elementStack.size() - 1);
+ }
+ if (! edit.isFracture)
+ {
+ // If the parent isn't a fracture, then the fracture is
+ // in fracturedChild.
+ Edit newEdit = new Edit(fracturedChild, 0, true);
+ elementStack.push(newEdit);
+ }
+ else
+ {
+ // Otherwise use the parent's first child.
+ Element el = edit.e.getElement(0);
+ Edit newEdit = new Edit(el, 0, true);
+ elementStack.push(newEdit);
+ }
+ break;
+ case ElementSpec.JoinNextDirection:
+ // Push the next paragraph element onto the stack so
+ // future insertions are added to it.
+ Element parent = edit.e.getElement(edit.index);
+ if (parent.isLeaf())
+ {
+ if (edit.index + 1 < edit.e.getElementCount())
+ parent = edit.e.getElement(edit.index + 1);
+ else
+ assert false; // Must not happen.
+ }
+ elementStack.push(new Edit(parent, 0, true));
+ break;
+ default:
+ Element branch = createBranchElement(edit.e,
+ spec.getAttributes());
+ edit.added.add(branch);
+ elementStack.push(new Edit(branch, 0));
+ break;
+ }
+ break;
+ case ElementSpec.EndTagType:
+ pop();
+ break;
+ case ElementSpec.ContentType:
+ insertContentTag(spec, edit);
+ break;
+ }
+ }
+
+ /**
+ * Inserts the first tag into the document.
+ *
+ * @param data -
+ * the data to be inserted.
+ */
+ private void insertFirstContentTag(ElementSpec[] data)
+ {
+ ElementSpec first = data[0];
+ Edit edit = (Edit) elementStack.peek();
+ Element current = edit.e.getElement(edit.index);
+ int firstEndOffset = offset + first.length;
+ boolean onlyContent = data.length == 1;
+ switch (first.getDirection())
+ {
+ case ElementSpec.JoinPreviousDirection:
+ if (current.getEndOffset() != firstEndOffset && ! onlyContent)
+ {
+ Element newEl1 = createLeafElement(edit.e,
+ current.getAttributes(),
+ current.getStartOffset(),
+ firstEndOffset);
+ edit.added.add(newEl1);
+ edit.removed.add(current);
+ if (current.getEndOffset() != endOffset)
+ recreateLeafs = true;
+ else
+ offsetLastIndex = true;
+ }
+ else
+ {
+ offsetLastIndex = true;
+ offsetLastIndexReplace = true;
+ }
+ break;
+ case ElementSpec.JoinNextDirection:
+ if (offset != 0)
+ {
+ Element newEl1 = createLeafElement(edit.e,
+ current.getAttributes(),
+ current.getStartOffset(),
+ offset);
+ edit.added.add(newEl1);
+ Element next = edit.e.getElement(edit.index + 1);
+ if (onlyContent)
+ newEl1 = createLeafElement(edit.e, next.getAttributes(),
+ offset, next.getEndOffset());
+ else
+ {
+ newEl1 = createLeafElement(edit.e, next.getAttributes(),
+ offset, firstEndOffset);
+ }
+ edit.added.add(newEl1);
+ edit.removed.add(current);
+ edit.removed.add(next);
+ }
+ break;
+ default: // OriginateDirection.
+ if (current.getStartOffset() != offset)
+ {
+ Element newEl = createLeafElement(edit.e,
+ current.getAttributes(),
+ current.getStartOffset(),
+ offset);
+ edit.added.add(newEl);
+ }
+ edit.removed.add(current);
+ Element newEl1 = createLeafElement(edit.e, first.getAttributes(),
+ offset, firstEndOffset);
+ edit.added.add(newEl1);
+ if (current.getEndOffset() != endOffset)
+ recreateLeafs = true;
+ else
+ offsetLastIndex = true;
+ break;
+ }
+ }
+
+ /**
+ * Inserts a content element into the document structure.
+ *
+ * @param tag -
+ * the element spec
+ */
+ private void insertContentTag(ElementSpec tag, Edit edit)
+ {
+ int len = tag.getLength();
+ int dir = tag.getDirection();
+ if (dir == ElementSpec.JoinNextDirection)
+ {
+ if (! edit.isFracture)
+ {
+ Element first = null;
+ if (insertPath != null)
+ {
+ for (int p = insertPath.length - 1; p >= 0; p--)
+ {
+ if (insertPath[p] == edit)
+ {
+ if (p != insertPath.length - 1)
+ first = edit.e.getElement(edit.index);
+ break;
+ }
+ }
+ }
+ if (first == null)
+ first = edit.e.getElement(edit.index + 1);
+ Element leaf = createLeafElement(edit.e, first.getAttributes(),
+ pos, first.getEndOffset());
+ edit.added.add(leaf);
+ edit.removed.add(first);
+ }
+ else
+ {
+ Element first = edit.e.getElement(0);
+ Element leaf = createLeafElement(edit.e, first.getAttributes(),
+ pos, first.getEndOffset());
+ edit.added.add(leaf);
+ edit.removed.add(first);
+ }
+ }
+ else
+ {
+ Element leaf = createLeafElement(edit.e, tag.getAttributes(), pos,
+ pos + len);
+ edit.added.add(leaf);
+ }
+
+ pos += len;
+
+ }
+
+ /**
+ * This method fractures bottomost leaf in the elementStack. This
+ * happens when the first inserted tag is not content.
+ *
+ * @param data
+ * the ElementSpecs used for the entire insertion
+ */
+ private void createFracture(ElementSpec[] data)
+ {
+ Edit edit = (Edit) elementStack.peek();
+ Element child = edit.e.getElement(edit.index);
+ if (offset != 0)
+ {
+ Element newChild = createLeafElement(edit.e, child.getAttributes(),
+ child.getStartOffset(), offset);
+ edit.added.add(newChild);
+ }
+ edit.removed.add(child);
+ if (child.getEndOffset() != endOffset)
+ recreateLeafs = true;
+ else
+ offsetLastIndex = true;
+ }
+
+ private void fracture(int depth)
+ {
+ int len = insertPath.length;
+ int lastIndex = -1;
+ boolean recreate = recreateLeafs;
+ Edit lastEdit = insertPath[len - 1];
+ boolean childChanged = lastEdit.index + 1 < lastEdit.e.getElementCount();
+ int deepestChangedIndex = recreate ? len : - 1;
+ int lastChangedIndex = len - 1;
+ createdFracture = true;
+ for (int i = len - 2; i >= 0; i--)
+ {
+ Edit edit = insertPath[i];
+ if (edit.added.size() > 0 || i == depth)
+ {
+ lastIndex = i;
+ if (! recreate && childChanged)
+ {
+ recreate = true;
+ if (deepestChangedIndex == -1)
+ deepestChangedIndex = lastChangedIndex + 1;
+ }
+ }
+ if (! childChanged && edit.index < edit.e.getElementCount())
+ {
+ childChanged = true;
+ lastChangedIndex = i;
+ }
+ }
+ if (recreate)
+ {
+ if (lastIndex == -1)
+ lastIndex = len - 1;
+ recreate(lastIndex, deepestChangedIndex);
+ }
+ }
+
+ private void recreate(int startIndex, int endIndex)
+ {
+ // Recreate the element representing the inserted index.
+ Edit edit = insertPath[startIndex];
+ Element child;
+ Element newChild;
+ int changeLength = insertPath.length;
+
+ if (startIndex + 1 == changeLength)
+ child = edit.e.getElement(edit.index);
+ else
+ child = edit.e.getElement(edit.index - 1);
+
+ if(child.isLeaf())
+ {
+ newChild = createLeafElement(edit.e, child.getAttributes(),
+ Math.max(endOffset, child.getStartOffset()),
+ child.getEndOffset());
+ }
+ else
+ {
+ newChild = createBranchElement(edit.e, child.getAttributes());
+ }
+ fracturedParent = edit.e;
+ fracturedChild = newChild;
+
+ // Recreate all the elements to the right of the insertion point.
+ Element parent = newChild;
+ while (++startIndex < endIndex)
+ {
+ boolean isEnd = (startIndex + 1) == endIndex;
+ boolean isEndLeaf = (startIndex + 1) == changeLength;
+
+ // Create the newChild, a duplicate of the elment at
+ // index. This isn't done if isEnd and offsetLastIndex are true
+ // indicating a join previous was done.
+ edit = insertPath[startIndex];
+
+ // Determine the child to duplicate, won't have to duplicate
+ // if at end of fracture, or offseting index.
+ if(isEnd)
+ {
+ if(offsetLastIndex || ! isEndLeaf)
+ child = null;
+ else
+ child = edit.e.getElement(edit.index);
+ }
+ else
+ {
+ child = edit.e.getElement(edit.index - 1);
+ }
+
+ // Duplicate it.
+ if(child != null)
+ {
+ if(child.isLeaf())
+ {
+ newChild = createLeafElement(parent, child.getAttributes(),
+ Math.max(endOffset, child.getStartOffset()),
+ child.getEndOffset());
+ }
+ else
+ {
+ newChild = createBranchElement(parent,
+ child.getAttributes());
+ }
+ }
+ else
+ newChild = null;
+
+ // Recreate the remaining children (there may be none).
+ int childrenToMove = edit.e.getElementCount() - edit.index;
+ Element[] children;
+ int moveStartIndex;
+ int childStartIndex = 1;
+
+ if (newChild == null)
+ {
+ // Last part of fracture.
+ if (isEndLeaf)
+ {
+ childrenToMove--;
+ moveStartIndex = edit.index + 1;
+ }
+ else
+ {
+ moveStartIndex = edit.index;
+ }
+ childStartIndex = 0;
+ children = new Element[childrenToMove];
+ }
+ else
+ {
+ if (! isEnd)
+ {
+ // Branch.
+ childrenToMove++;
+ moveStartIndex = edit.index;
+ }
+ else
+ {
+ // Last leaf, need to recreate part of it.
+ moveStartIndex = edit.index + 1;
+ }
+ children = new Element[childrenToMove];
+ children[0] = newChild;
+ }
+
+ for (int c = childStartIndex; c < childrenToMove; c++)
+ {
+ Element toMove = edit.e.getElement(moveStartIndex++);
+ children[c] = recreateFracturedElement(parent, toMove);
+ edit.removed.add(toMove);
+ }
+ ((BranchElement) parent).replace(0, 0, children);
+ parent = newChild;
+ }
+
+ }
+
+ private Element recreateFracturedElement(Element parent, Element toCopy)
+ {
+ Element recreated;
+ if(toCopy.isLeaf())
+ {
+ recreated = createLeafElement(parent, toCopy.getAttributes(),
+ Math.max(toCopy.getStartOffset(), endOffset),
+ toCopy.getEndOffset());
+ }
+ else
+ {
+ Element newParent = createBranchElement(parent,
+ toCopy.getAttributes());
+ int childCount = toCopy.getElementCount();
+ Element[] newChildren = new Element[childCount];
+ for (int i = 0; i < childCount; i++)
+ {
+ newChildren[i] = recreateFracturedElement(newParent,
+ toCopy.getElement(i));
+ }
+ ((BranchElement) newParent).replace(0, 0, newChildren);
+ recreated = newParent;
+ }
+ return recreated;
+ }
+
+ private boolean split(int offs, int len)
+ {
+ boolean splitEnd = false;
+ // Push the path to the stack.
+ Element e = root;
+ int index = e.getElementIndex(offs);
+ while (! e.isLeaf())
+ {
+ elementStack.push(new Edit(e, index));
+ e = e.getElement(index);
+ index = e.getElementIndex(offs);
+ }
+
+ Edit ec = (Edit) elementStack.peek();
+ Element child = ec.e.getElement(ec.index);
+ // Make sure there is something to do. If the
+ // offset is already at a boundary then there is
+ // nothing to do.
+ if (child.getStartOffset() < offs && offs < child.getEndOffset())
+ {
+ // We need to split, now see if the other end is within
+ // the same parent.
+ int index0 = ec.index;
+ int index1 = index0;
+ if (((offs + len) < ec.e.getEndOffset()) && (len != 0))
+ {
+ // It's a range split in the same parent.
+ index1 = ec.e.getElementIndex(offs+len);
+ if (index1 == index0)
+ {
+ // It's a three-way split.
+ ec.removed.add(child);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ child.getStartOffset(), offs);
+ ec.added.add(e);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ offs, offs + len);
+ ec.added.add(e);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ offs + len, child.getEndOffset());
+ ec.added.add(e);
+ return true;
+ }
+ else
+ {
+ child = ec.e.getElement(index1);
+ if ((offs + len) == child.getStartOffset())
+ {
+ // End is already on a boundary.
+ index1 = index0;
+ }
+ }
+ splitEnd = true;
+ }
+
+ // Split the first location.
+ pos = offs;
+ child = ec.e.getElement(index0);
+ ec.removed.add(child);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ child.getStartOffset(), pos);
+ ec.added.add(e);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ pos, child.getEndOffset());
+ ec.added.add(e);
+
+ // Pick up things in the middle.
+ for (int i = index0 + 1; i < index1; i++)
+ {
+ child = ec.e.getElement(i);
+ ec.removed.add(child);
+ ec.added.add(child);
+ }
+
+ if (index1 != index0)
+ {
+ child = ec.e.getElement(index1);
+ pos = offs + len;
+ ec.removed.add(child);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ child.getStartOffset(), pos);
+ ec.added.add(e);
+ e = createLeafElement(ec.e, child.getAttributes(),
+ pos, child.getEndOffset());
+
+ ec.added.add(e);
+ }
+ }
+ return splitEnd;
+
+ }
+
+ }
+
+
+ /**
+ * An element type for sections. This is a simple BranchElement with a unique
+ * name.
+ */
+ protected class SectionElement extends BranchElement
+ {
+ /**
+ * Creates a new SectionElement.
+ */
+ public SectionElement()
+ {
+ super(null, null);
+ }
+
+ /**
+ * Returns the name of the element. This method always returns
+ * &quot;section&quot;.
+ *
+ * @return the name of the element
+ */
+ public String getName()
+ {
+ return SectionElementName;
+ }
+ }
+
+ /**
+ * Receives notification when any of the document's style changes and calls
+ * {@link DefaultStyledDocument#styleChanged(Style)}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class StyleChangeListener implements ChangeListener
+ {
+
+ /**
+ * Receives notification when any of the document's style changes and calls
+ * {@link DefaultStyledDocument#styleChanged(Style)}.
+ *
+ * @param event
+ * the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ Style style = (Style) event.getSource();
+ styleChanged(style);
+ }
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 940485415728614849L;
+
+ /**
+ * The default size to use for new content buffers.
+ */
+ public static final int BUFFER_SIZE_DEFAULT = 4096;
+
+ /**
+ * The <code>EditorBuffer</code> that is used to manage to
+ * <code>Element</code> hierarchy.
+ */
+ protected DefaultStyledDocument.ElementBuffer buffer;
+
+ /**
+ * Listens for changes on this document's styles and notifies styleChanged().
+ */
+ private StyleChangeListener styleChangeListener;
+
+ /**
+ * Creates a new <code>DefaultStyledDocument</code>.
+ */
+ public DefaultStyledDocument()
+ {
+ this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext());
+ }
+
+ /**
+ * Creates a new <code>DefaultStyledDocument</code> that uses the specified
+ * {@link StyleContext}.
+ *
+ * @param context
+ * the <code>StyleContext</code> to use
+ */
+ public DefaultStyledDocument(StyleContext context)
+ {
+ this(new GapContent(BUFFER_SIZE_DEFAULT), context);
+ }
+
+ /**
+ * Creates a new <code>DefaultStyledDocument</code> that uses the specified
+ * {@link StyleContext} and {@link Content} buffer.
+ *
+ * @param content
+ * the <code>Content</code> buffer to use
+ * @param context
+ * the <code>StyleContext</code> to use
+ */
+ public DefaultStyledDocument(AbstractDocument.Content content,
+ StyleContext context)
+ {
+ super(content, context);
+ buffer = new ElementBuffer(createDefaultRoot());
+ setLogicalStyle(0, context.getStyle(StyleContext.DEFAULT_STYLE));
+ }
+
+ /**
+ * Adds a style into the style hierarchy. Unspecified style attributes can be
+ * resolved in the <code>parent</code> style, if one is specified. While it
+ * is legal to add nameless styles (<code>nm == null</code),
+ * you must be aware that the client application is then responsible
+ * for managing the style hierarchy, since unnamed styles cannot be
+ * looked up by their name.
+ *
+ * @param nm the name of the style or <code>null</code> if the style should
+ * be unnamed
+ * @param parent the parent in which unspecified style attributes are
+ * resolved, or <code>null</code> if that is not necessary
+ *
+ * @return the newly created <code>Style</code>
+ */
+ public Style addStyle(String nm, Style parent)
+ {
+ StyleContext context = (StyleContext) getAttributeContext();
+ Style newStyle = context.addStyle(nm, parent);
+
+ // Register change listener.
+ if (styleChangeListener == null)
+ styleChangeListener = new StyleChangeListener();
+ newStyle.addChangeListener(styleChangeListener);
+
+ return newStyle;
+ }
+
+ /**
+ * Create the default root element for this kind of <code>Document</code>.
+ *
+ * @return the default root element for this kind of <code>Document</code>
+ */
+ protected AbstractDocument.AbstractElement createDefaultRoot()
+ {
+ Element[] tmp;
+ SectionElement section = new SectionElement();
+
+ BranchElement paragraph = new BranchElement(section, null);
+ tmp = new Element[1];
+ tmp[0] = paragraph;
+ section.replace(0, 0, tmp);
+
+ Element leaf = new LeafElement(paragraph, null, 0, 1);
+ tmp = new Element[1];
+ tmp[0] = leaf;
+ paragraph.replace(0, 0, tmp);
+
+ return section;
+ }
+
+ /**
+ * Returns the <code>Element</code> that corresponds to the character at the
+ * specified position.
+ *
+ * @param position
+ * the position of which we query the corresponding
+ * <code>Element</code>
+ * @return the <code>Element</code> that corresponds to the character at the
+ * specified position
+ */
+ public Element getCharacterElement(int position)
+ {
+ Element element = getDefaultRootElement();
+
+ while (!element.isLeaf())
+ {
+ int index = element.getElementIndex(position);
+ element = element.getElement(index);
+ }
+
+ return element;
+ }
+
+ /**
+ * Extracts a background color from a set of attributes.
+ *
+ * @param attributes
+ * the attributes from which to get a background color
+ * @return the background color that correspond to the attributes
+ */
+ public Color getBackground(AttributeSet attributes)
+ {
+ StyleContext context = (StyleContext) getAttributeContext();
+ return context.getBackground(attributes);
+ }
+
+ /**
+ * Returns the default root element.
+ *
+ * @return the default root element
+ */
+ public Element getDefaultRootElement()
+ {
+ return buffer.getRootElement();
+ }
+
+ /**
+ * Extracts a font from a set of attributes.
+ *
+ * @param attributes
+ * the attributes from which to get a font
+ * @return the font that correspond to the attributes
+ */
+ public Font getFont(AttributeSet attributes)
+ {
+ StyleContext context = (StyleContext) getAttributeContext();
+ return context.getFont(attributes);
+ }
+
+ /**
+ * Extracts a foreground color from a set of attributes.
+ *
+ * @param attributes
+ * the attributes from which to get a foreground color
+ * @return the foreground color that correspond to the attributes
+ */
+ public Color getForeground(AttributeSet attributes)
+ {
+ StyleContext context = (StyleContext) getAttributeContext();
+ return context.getForeground(attributes);
+ }
+
+ /**
+ * Returns the logical <code>Style</code> for the specified position.
+ *
+ * @param position
+ * the position from which to query to logical style
+ * @return the logical <code>Style</code> for the specified position
+ */
+ public Style getLogicalStyle(int position)
+ {
+ Element paragraph = getParagraphElement(position);
+ AttributeSet attributes = paragraph.getAttributes();
+ AttributeSet a = attributes.getResolveParent();
+ // If the resolve parent is not of type Style, we return null.
+ if (a instanceof Style)
+ return (Style) a;
+ return null;
+ }
+
+ /**
+ * Returns the paragraph element for the specified position. If the position
+ * is outside the bounds of the document's root element, then the closest
+ * element is returned. That is the last paragraph if
+ * <code>position >= endIndex</code> or the first paragraph if
+ * <code>position < startIndex</code>.
+ *
+ * @param position
+ * the position for which to query the paragraph element
+ * @return the paragraph element for the specified position
+ */
+ public Element getParagraphElement(int position)
+ {
+ Element e = getDefaultRootElement();
+ while (!e.isLeaf())
+ e = e.getElement(e.getElementIndex(position));
+
+ if (e != null)
+ return e.getParentElement();
+ return e;
+ }
+
+ /**
+ * Looks up and returns a named <code>Style</code>.
+ *
+ * @param nm
+ * the name of the <code>Style</code>
+ * @return the found <code>Style</code> of <code>null</code> if no such
+ * <code>Style</code> exists
+ */
+ public Style getStyle(String nm)
+ {
+ StyleContext context = (StyleContext) getAttributeContext();
+ return context.getStyle(nm);
+ }
+
+ /**
+ * Removes a named <code>Style</code> from the style hierarchy.
+ *
+ * @param nm
+ * the name of the <code>Style</code> to be removed
+ */
+ public void removeStyle(String nm)
+ {
+ StyleContext context = (StyleContext) getAttributeContext();
+ context.removeStyle(nm);
+ }
+
+ /**
+ * Sets text attributes for the fragment specified by <code>offset</code>
+ * and <code>length</code>.
+ *
+ * @param offset
+ * the start offset of the fragment
+ * @param length
+ * the length of the fragment
+ * @param attributes
+ * the text attributes to set
+ * @param replace
+ * if <code>true</code>, the attributes of the current selection
+ * are overridden, otherwise they are merged
+ */
+ public void setCharacterAttributes(int offset, int length,
+ AttributeSet attributes, boolean replace)
+ {
+ // Exit early if length is 0, so no DocumentEvent is created or fired.
+ if (length == 0)
+ return;
+ try
+ {
+ // Must obtain a write lock for this method. writeLock() and
+ // writeUnlock() should always be in try/finally block to make
+ // sure that locking happens in a balanced manner.
+ writeLock();
+ DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
+ length,
+ DocumentEvent.EventType.CHANGE);
+
+ // Modify the element structure so that the interval begins at an
+ // element
+ // start and ends at an element end.
+ buffer.change(offset, length, ev);
+
+ // Visit all paragraph elements within the specified interval
+ int end = offset + length;
+ Element curr;
+ for (int pos = offset; pos < end;)
+ {
+ // Get the CharacterElement at offset pos.
+ curr = getCharacterElement(pos);
+ if (pos == curr.getEndOffset())
+ break;
+
+ MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes();
+ ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace));
+ // If replace is true, remove all the old attributes.
+ if (replace)
+ a.removeAttributes(a);
+ // Add all the new attributes.
+ a.addAttributes(attributes);
+ // Increment pos so we can check the next CharacterElement.
+ pos = curr.getEndOffset();
+ }
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Sets the logical style for the paragraph at the specified position.
+ *
+ * @param position
+ * the position at which the logical style is added
+ * @param style
+ * the style to set for the current paragraph
+ */
+ public void setLogicalStyle(int position, Style style)
+ {
+ Element el = getParagraphElement(position);
+ // getParagraphElement doesn't return null but subclasses might so
+ // we check for null here.
+ if (el == null)
+ return;
+ try
+ {
+ writeLock();
+ if (el instanceof AbstractElement)
+ {
+ AbstractElement ael = (AbstractElement) el;
+ ael.setResolveParent(style);
+ int start = el.getStartOffset();
+ int end = el.getEndOffset();
+ DefaultDocumentEvent ev = new DefaultDocumentEvent(start,
+ end - start,
+ DocumentEvent.EventType.CHANGE);
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ else
+ throw new AssertionError(
+ "paragraph elements are expected to be"
+ + "instances of AbstractDocument.AbstractElement");
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Sets text attributes for the paragraph at the specified fragment.
+ *
+ * @param offset
+ * the beginning of the fragment
+ * @param length
+ * the length of the fragment
+ * @param attributes
+ * the text attributes to set
+ * @param replace
+ * if <code>true</code>, the attributes of the current selection
+ * are overridden, otherwise they are merged
+ */
+ public void setParagraphAttributes(int offset, int length,
+ AttributeSet attributes, boolean replace)
+ {
+ try
+ {
+ // Must obtain a write lock for this method. writeLock() and
+ // writeUnlock() should always be in try/finally blocks to make
+ // sure that locking occurs in a balanced manner.
+ writeLock();
+
+ // Create a DocumentEvent to use for changedUpdate().
+ DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
+ length,
+ DocumentEvent.EventType.CHANGE);
+
+ // Have to iterate through all the _paragraph_ elements that are
+ // contained or partially contained in the interval
+ // (offset, offset + length).
+ Element rootElement = getDefaultRootElement();
+ int startElement = rootElement.getElementIndex(offset);
+ int endElement = rootElement.getElementIndex(offset + length - 1);
+ if (endElement < startElement)
+ endElement = startElement;
+
+ for (int i = startElement; i <= endElement; i++)
+ {
+ Element par = rootElement.getElement(i);
+ MutableAttributeSet a = (MutableAttributeSet) par.getAttributes();
+ // Add the change to the DocumentEvent.
+ ev.addEdit(new AttributeUndoableEdit(par, attributes, replace));
+ // If replace is true remove the old attributes.
+ if (replace)
+ a.removeAttributes(a);
+ // Add the new attributes.
+ a.addAttributes(attributes);
+ }
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Called in response to content insert actions. This is used to update the
+ * element structure.
+ *
+ * @param ev
+ * the <code>DocumentEvent</code> describing the change
+ * @param attr
+ * the attributes for the change
+ */
+ protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
+ {
+ int offs = ev.getOffset();
+ int len = ev.getLength();
+ int endOffs = offs + len;
+ if (attr == null)
+ attr = SimpleAttributeSet.EMPTY;
+
+ // Paragraph attributes are fetched from the point _after_ the insertion.
+ Element paragraph = getParagraphElement(endOffs);
+ AttributeSet pAttr = paragraph.getAttributes();
+ // Character attributes are fetched from the actual insertion point.
+ Element paragraph2 = getParagraphElement(offs);
+ int contIndex = paragraph2.getElementIndex(offs);
+ Element content = paragraph2.getElement(contIndex);
+ AttributeSet cAttr = content.getAttributes();
+
+ boolean insertAtBoundary = content.getEndOffset() == endOffs;
+ try
+ {
+ Segment s = new Segment();
+ ArrayList buf = new ArrayList();
+ ElementSpec lastStartTag = null;
+ boolean insertAfterNewline = false;
+ short lastStartDir = ElementSpec.OriginateDirection;
+
+ // Special handle if we are inserting after a newline.
+ if (offs > 0)
+ {
+ getText(offs - 1, 1, s);
+ if (s.array[s.offset] == '\n')
+ {
+ insertAfterNewline = true;
+ lastStartDir = insertAfterNewline(paragraph, paragraph2,
+ pAttr, buf, offs,
+ endOffs);
+ // Search last start tag.
+ for (int i = buf.size() - 1; i >= 0 && lastStartTag == null;
+ i--)
+ {
+ ElementSpec tag = (ElementSpec) buf.get(i);
+ if (tag.getType() == ElementSpec.StartTagType)
+ {
+ lastStartTag = tag;
+ }
+ }
+ }
+
+ }
+
+ // If we are not inserting after a newline, the paragraph attributes
+ // come from the paragraph under the insertion point.
+ if (! insertAfterNewline)
+ pAttr = paragraph2.getAttributes();
+
+ // Scan text and build up the specs.
+ getText(offs, len, s);
+ int end = s.offset + s.count;
+ int last = s.offset;
+ for (int i = s.offset; i < end; i++)
+ {
+ if (s.array[i] == '\n')
+ {
+ int breakOffs = i + 1;
+ buf.add(new ElementSpec(attr, ElementSpec.ContentType,
+ breakOffs - last));
+ buf.add(new ElementSpec(null, ElementSpec.EndTagType));
+ lastStartTag = new ElementSpec(pAttr,
+ ElementSpec.StartTagType);
+ buf.add(lastStartTag);
+ last = breakOffs;
+ }
+ }
+
+ // Need to add a tailing content tag if we didn't finish at a boundary.
+ if (last < end)
+ {
+ buf.add(new ElementSpec(attr, ElementSpec.ContentType,
+ end - last));
+ }
+
+ // Now we need to fix up the directions of the specs.
+ ElementSpec first = (ElementSpec) buf.get(0);
+ int doclen = getLength();
+
+ // Maybe join-previous the first tag if it is content and has
+ // the same attributes as the previous character run.
+ if (first.getType() == ElementSpec.ContentType && cAttr.isEqual(attr))
+ first.setDirection(ElementSpec.JoinPreviousDirection);
+
+ // Join-fracture or join-next the last start tag if necessary.
+ if (lastStartTag != null)
+ {
+ if (insertAfterNewline)
+ lastStartTag.setDirection(lastStartDir);
+ else if (paragraph2.getEndOffset() != endOffs)
+ lastStartTag.setDirection(ElementSpec.JoinFractureDirection);
+ else
+ {
+ Element par = paragraph2.getParentElement();
+ int par2Index = par.getElementIndex(offs);
+ if (par2Index + 1 < par.getElementCount()
+ && ! par.getElement(par2Index + 1).isLeaf())
+ lastStartTag.setDirection(ElementSpec.JoinNextDirection);
+ }
+ }
+
+ // Join-next last tag if possible.
+ if (insertAtBoundary && endOffs < doclen)
+ {
+ ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
+ if (lastTag.getType() == ElementSpec.ContentType
+ && ((lastStartTag == null
+ && (paragraph == paragraph2 || insertAfterNewline))
+ || (lastStartTag != null
+ && lastStartTag.getDirection() != ElementSpec.OriginateDirection)))
+ {
+ int nextIndex = paragraph.getElementIndex(endOffs);
+ Element nextRun = paragraph.getElement(nextIndex);
+ if (nextRun.isLeaf() && attr.isEqual(nextRun.getAttributes()))
+ lastTag.setDirection(ElementSpec.JoinNextDirection);
+ }
+ }
+
+ else if (! insertAtBoundary && lastStartTag != null
+ && lastStartTag.getDirection() == ElementSpec.JoinFractureDirection)
+ {
+ ElementSpec lastTag = (ElementSpec) buf.get(buf.size() - 1);
+ if (lastTag.getType() == ElementSpec.ContentType
+ && lastTag.getDirection() != ElementSpec.JoinPreviousDirection
+ && attr.isEqual(cAttr))
+ {
+ lastTag.setDirection(ElementSpec.JoinNextDirection);
+ }
+ }
+
+ ElementSpec[] specs = new ElementSpec[buf.size()];
+ specs = (ElementSpec[]) buf.toArray(specs);
+ buffer.insert(offs, len, specs, ev);
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore this. Comment out for debugging.
+ ex.printStackTrace();
+ }
+ super.insertUpdate(ev, attr);
+ }
+
+ private short insertAfterNewline(Element par1, Element par2,
+ AttributeSet attr, ArrayList buf,
+ int offs, int endOffs)
+ {
+ short dir = 0;
+ if (par1.getParentElement() == par2.getParentElement())
+ {
+ ElementSpec tag = new ElementSpec(attr, ElementSpec.EndTagType);
+ buf.add(tag);
+ tag = new ElementSpec(attr, ElementSpec.StartTagType);
+ buf.add(tag);
+ if (par2.getEndOffset() != endOffs)
+ dir = ElementSpec.JoinFractureDirection;
+ else
+ {
+ Element par = par2.getParentElement();
+ if (par.getElementIndex(offs) + 1 < par.getElementCount())
+ dir = ElementSpec.JoinNextDirection;
+ }
+ }
+ else
+ {
+ // For text with more than 2 levels, find the common parent of
+ // par1 and par2.
+ ArrayList parentsLeft = new ArrayList();
+ ArrayList parentsRight = new ArrayList();
+ Element e = par2;
+ while (e != null)
+ {
+ parentsLeft.add(e);
+ e = e.getParentElement();
+ }
+ e = par1;
+ int leftIndex = -1;
+ while (e != null && (leftIndex = parentsLeft.indexOf(e)) == 1)
+ {
+ parentsRight.add(e);
+ e = e.getParentElement();
+ }
+
+ if (e != null)
+
+ {
+ // e is now the common parent.
+ // Insert the end tags.
+ for (int c = 0; c < leftIndex; c++)
+ {
+ buf.add(new ElementSpec(null, ElementSpec.EndTagType));
+ }
+ // Insert the start tags.
+ for (int c = parentsRight.size() - 1; c >= 0; c--)
+ {
+ Element el = (Element) parentsRight.get(c);
+ ElementSpec tag = new ElementSpec(el.getAttributes(),
+ ElementSpec.StartTagType);
+ if (c > 0)
+ tag.setDirection(ElementSpec.JoinNextDirection);
+ buf.add(tag);
+ }
+ if (parentsRight.size() > 0)
+ dir = ElementSpec.JoinNextDirection;
+ else
+ dir = ElementSpec.JoinFractureDirection;
+ }
+ else
+ assert false;
+ }
+ return dir;
+ }
+
+ /**
+ * A helper method to set up the ElementSpec buffer for the special case of an
+ * insertion occurring immediately after a newline.
+ *
+ * @param specs
+ * the ElementSpec buffer to initialize.
+ */
+ short handleInsertAfterNewline(Vector specs, int offset, int endOffset,
+ Element prevParagraph, Element paragraph,
+ AttributeSet a)
+ {
+ if (prevParagraph.getParentElement() == paragraph.getParentElement())
+ {
+ specs.add(new ElementSpec(a, ElementSpec.EndTagType));
+ specs.add(new ElementSpec(a, ElementSpec.StartTagType));
+ if (paragraph.getStartOffset() != endOffset)
+ return ElementSpec.JoinFractureDirection;
+ // If there is an Element after this one, use JoinNextDirection.
+ Element parent = paragraph.getParentElement();
+ if (parent.getElementCount() > (parent.getElementIndex(offset) + 1))
+ return ElementSpec.JoinNextDirection;
+ }
+ return ElementSpec.OriginateDirection;
+ }
+
+ /**
+ * Updates the document structure in response to text removal. This is
+ * forwarded to the {@link ElementBuffer} of this document. Any changes to the
+ * document structure are added to the specified document event and sent to
+ * registered listeners.
+ *
+ * @param ev
+ * the document event that records the changes to the document
+ */
+ protected void removeUpdate(DefaultDocumentEvent ev)
+ {
+ super.removeUpdate(ev);
+ buffer.remove(ev.getOffset(), ev.getLength(), ev);
+ }
+
+ /**
+ * Returns an enumeration of all style names.
+ *
+ * @return an enumeration of all style names
+ */
+ public Enumeration<?> getStyleNames()
+ {
+ StyleContext context = (StyleContext) getAttributeContext();
+ return context.getStyleNames();
+ }
+
+ /**
+ * Called when any of this document's styles changes.
+ *
+ * @param style
+ * the style that changed
+ */
+ protected void styleChanged(Style style)
+ {
+ // Nothing to do here. This is intended to be overridden by subclasses.
+ }
+
+ /**
+ * Inserts a bulk of structured content at once.
+ *
+ * @param offset
+ * the offset at which the content should be inserted
+ * @param data
+ * the actual content spec to be inserted
+ */
+ protected void insert(int offset, ElementSpec[] data)
+ throws BadLocationException
+ {
+ if (data == null || data.length == 0)
+ return;
+ try
+ {
+ // writeLock() and writeUnlock() should always be in a try/finally
+ // block so that locking balance is guaranteed even if some
+ // exception is thrown.
+ writeLock();
+
+ // First we collect the content to be inserted.
+ CPStringBuilder contentBuffer = new CPStringBuilder();
+ for (int i = 0; i < data.length; i++)
+ {
+ // Collect all inserts into one so we can get the correct
+ // ElementEdit
+ ElementSpec spec = data[i];
+ if (spec.getArray() != null && spec.getLength() > 0)
+ contentBuffer.append(spec.getArray(), spec.getOffset(),
+ spec.getLength());
+ }
+
+ int length = contentBuffer.length();
+
+ // If there was no content inserted then exit early.
+ if (length == 0)
+ return;
+
+ Content c = getContent();
+ UndoableEdit edit = c.insertString(offset,
+ contentBuffer.toString());
+
+ // Create the DocumentEvent with the ElementEdit added
+ DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
+ length,
+ DocumentEvent.EventType.INSERT);
+
+ ev.addEdit(edit);
+
+ // Finally we must update the document structure and fire the insert
+ // update event.
+ buffer.insert(offset, length, data, ev);
+
+ super.insertUpdate(ev, null);
+
+ ev.end();
+ fireInsertUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Initializes the <code>DefaultStyledDocument</code> with the specified
+ * data.
+ *
+ * @param data
+ * the specification of the content with which the document is
+ * initialized
+ */
+ protected void create(ElementSpec[] data)
+ {
+ try
+ {
+
+ // Clear content if there is some.
+ int len = getLength();
+ if (len > 0)
+ remove(0, len);
+
+ writeLock();
+
+ // Now we insert the content.
+ StringBuilder b = new StringBuilder();
+ for (int i = 0; i < data.length; ++i)
+ {
+ ElementSpec el = data[i];
+ if (el.getArray() != null && el.getLength() > 0)
+ b.append(el.getArray(), el.getOffset(), el.getLength());
+ }
+ Content content = getContent();
+ UndoableEdit cEdit = content.insertString(0, b.toString());
+
+ len = b.length();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(0, b.length(),
+ DocumentEvent.EventType.INSERT);
+ ev.addEdit(cEdit);
+
+ buffer.create(len, data, ev);
+
+ // For the bidi update.
+ super.insertUpdate(ev, null);
+
+ ev.end();
+ fireInsertUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError("Unexpected bad location");
+ err.initCause(ex);
+ throw err;
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/DefaultTextUI.java b/libjava/classpath/javax/swing/text/DefaultTextUI.java
new file mode 100644
index 000000000..c347668b9
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultTextUI.java
@@ -0,0 +1,62 @@
+/* DefaultTextUI.java -- Deprecated base UI for text components
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.plaf.basic.BasicTextUI;
+
+/**
+ * This class is deprecated and should not be used anymore. The base UI for
+ * all text components is now {@link BasicTextUI}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @deprecated as of 1.5 use {@link BasicTextUI} instead
+ */
+public abstract class DefaultTextUI extends BasicTextUI
+{
+ /**
+ * This class is deprecated and should not be used anymore. The base UI for
+ * all text components is now {@link BasicTextUI}.
+ *
+ * @deprecated use {@link BasicTextUI} instead
+ */
+ public DefaultTextUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/Document.java b/libjava/classpath/javax/swing/text/Document.java
new file mode 100644
index 000000000..f23767f58
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Document.java
@@ -0,0 +1,221 @@
+/* Document.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.event.DocumentListener;
+import javax.swing.event.UndoableEditListener;
+
+/**
+ * A Document is the model that backs up all text components in Swing.
+ * This interface supports different kinds of implementations, from
+ * simple plain text model up to complex styled HTML or RTF models.
+ */
+public interface Document
+{
+ /**
+ * The key for the property that describes the source of a document.
+ */
+ String StreamDescriptionProperty = "stream";
+
+ /**
+ * The key for the property that is the title of a document.
+ */
+ String TitleProperty = "title";
+
+ /**
+ * Adds a {@link DocumentListener} to this document.
+ *
+ * @param listener the DocumentListener to add
+ */
+ void addDocumentListener(DocumentListener listener);
+
+ /**
+ * Adds an {@link UndoableEditListener} to this document.
+ *
+ * @param listener the UndoableEditListener to add
+ */
+ void addUndoableEditListener(UndoableEditListener listener);
+
+ /**
+ * Creates a mark in the character content at the specified offset.
+ *
+ * @param offs the offset where to place the mark
+ *
+ * @return the created Position object
+ *
+ * @throws BadLocationException of the specified offset is not a valid
+ * position in the documents content
+ */
+ Position createPosition(int offs)
+ throws BadLocationException;
+
+ /**
+ * Returns the default root element. Views should be using this element
+ * unless other mechanisms for assigning views to element structure is
+ * provided.
+ *
+ * @return the default root element
+ */
+ Element getDefaultRootElement();
+
+ /**
+ * Returns the position that marks the end of the document.
+ *
+ * @return the position that marks the end of the document
+ */
+ Position getEndPosition();
+
+ /**
+ * Returns the length of the document content.
+ *
+ * @return the length of the document content
+ */
+ int getLength();
+
+ /**
+ * Returns a document property with the specified key.
+ *
+ * @param key the (non-null) key for the property to fetch
+ *
+ * @return the property for <code>key</code> or null if no such property
+ * is stored
+ */
+ Object getProperty(Object key);
+
+ /**
+ * Returns the root elements of the document content.
+ *
+ * @return the root elements of the document content
+ */
+ Element[] getRootElements();
+
+ /**
+ * Returns the position that marks the beginning of the document
+ * content.
+ *
+ * @return the start position
+ */
+ Position getStartPosition();
+
+ /**
+ * Returns the textual content starting at <code>offset</code> with
+ * a length of <code>length</code>.
+ *
+ * @param offset the beginning of the text fragment to fetch
+ * @param length the length of the text fragment to fetch
+ *
+ * @return the text fragment starting at <code>offset</code> with
+ * a length of <code>length</code>
+ *
+ * @throws BadLocationException if <code>offset</code> or <code>length</code>
+ * are no valid locations in the document content
+ */
+ String getText(int offset, int length)
+ throws BadLocationException;
+
+ /**
+ * Fetch the textual content starting at <code>offset</code> with
+ * a length of <code>length</code> and store it in <code>txt</code>.
+ *
+ * @param offset the beginning of the text fragment to fetch
+ * @param length the length of the text fragment to fetch
+ * @param txt the Segment where to store the text fragment
+ *
+ * @throws BadLocationException if <code>offset</code> or <code>length</code>
+ * are no valid locations in the document content
+ */
+ void getText(int offset, int length, Segment txt)
+ throws BadLocationException;
+
+ /**
+ * Inserts a piece of text with an AttributeSet at the specified
+ * <code>offset</code>.
+ *
+ * @param offset the location where to insert the content
+ * @param str the textual content to insert
+ * @param a the Attributes associated with the piece of text
+ *
+ * @throws BadLocationException if <code>offset</code>
+ * is not a valid location in the document content
+ */
+ void insertString(int offset, String str, AttributeSet a)
+ throws BadLocationException;
+
+ /**
+ * Sets a document property.
+ *
+ * @param key the key of the property
+ * @param value the value of the property
+ */
+ void putProperty(Object key, Object value);
+
+ /**
+ * Removes a piece of content.
+ *
+ * @param offs the location of the fragment to remove
+ * @param len the length of the fragment to remove
+ *
+ * @throws BadLocationException if <code>offs</code> or <code>len</code>
+ * are no valid locations in the document content
+ */
+ void remove(int offs, int len)
+ throws BadLocationException;
+
+ /**
+ * Removes a DocumentListener from this Document.
+ *
+ * @param listener the DocumentListener to remove
+ */
+ void removeDocumentListener(DocumentListener listener);
+
+ /**
+ * Removes an UndoableEditListener from this Document.
+ *
+ * @param listener the UndoableEditListener to remove
+ */
+ void removeUndoableEditListener(UndoableEditListener listener);
+
+ /**
+ * This allows the Document to be rendered safely. It is made sure that
+ * the Runnable can read the document without any changes while reading.
+ * The Runnable is not allowed to change the Document itself.
+ *
+ * @param r the Runnable that renders the Document
+ */
+ void render(Runnable r);
+}
diff --git a/libjava/classpath/javax/swing/text/DocumentFilter.java b/libjava/classpath/javax/swing/text/DocumentFilter.java
new file mode 100644
index 000000000..1f7e8a689
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DocumentFilter.java
@@ -0,0 +1,83 @@
+/* DocumentFilter.java --
+ Copyright (C) 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+public class DocumentFilter
+{
+ public abstract static class FilterBypass
+ {
+ public FilterBypass()
+ {
+ // Do nothing here.
+ }
+
+ public abstract Document getDocument();
+
+ public abstract void insertString(int offset, String string,
+ AttributeSet attr)
+ throws BadLocationException;
+
+ public abstract void remove(int offset, int length)
+ throws BadLocationException;
+
+ public abstract void replace(int offset, int length, String string,
+ AttributeSet attrs)
+ throws BadLocationException;
+ }
+
+ public void insertString(DocumentFilter.FilterBypass fb, int offset,
+ String string, AttributeSet attr)
+ throws BadLocationException
+ {
+ fb.insertString(offset, string, attr);
+ }
+
+ public void remove(DocumentFilter.FilterBypass fb, int offset, int length)
+ throws BadLocationException
+ {
+ fb.remove(offset, length);
+ }
+
+ public void replace(DocumentFilter.FilterBypass fb, int offset, int length,
+ String text, AttributeSet attr)
+ throws BadLocationException
+ {
+ fb.replace(offset, length, text, attr);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/EditorKit.java b/libjava/classpath/javax/swing/text/EditorKit.java
new file mode 100644
index 000000000..62b4a6486
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/EditorKit.java
@@ -0,0 +1,98 @@
+/* EditorKit.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.Writer;
+
+import javax.swing.Action;
+import javax.swing.JEditorPane;
+
+public abstract class EditorKit implements Cloneable, Serializable
+{
+ private static final long serialVersionUID = -5044124649345887822L;
+
+ public EditorKit()
+ {
+ // Nothing to do here.
+ }
+
+ public Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Called when the kit is being removed from the JEditorPane.
+ */
+ public void deinstall(JEditorPane c)
+ {
+ // This default implementation does nothing.
+ }
+
+ public void install(JEditorPane c)
+ {
+ // This default implementation does nothing.
+ }
+
+ public abstract Caret createCaret();
+ public abstract Document createDefaultDocument();
+ public abstract Action[] getActions();
+ public abstract String getContentType();
+ public abstract ViewFactory getViewFactory();
+ public abstract void read(InputStream in, Document doc, int pos)
+ throws BadLocationException, IOException;
+ public abstract void read(Reader in, Document doc, int pos)
+ throws BadLocationException, IOException;
+ public abstract void write(OutputStream out, Document doc, int pos, int len)
+ throws BadLocationException, IOException;
+ public abstract void write(Writer out, Document doc, int pos, int len)
+ throws BadLocationException, IOException;
+}
diff --git a/libjava/classpath/javax/swing/text/Element.java b/libjava/classpath/javax/swing/text/Element.java
new file mode 100644
index 000000000..83d883565
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Element.java
@@ -0,0 +1,54 @@
+/* Element.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+
+public interface Element
+{
+ AttributeSet getAttributes();
+ Document getDocument();
+ Element getElement(int index);
+ int getElementCount();
+ int getElementIndex(int offset);
+ int getEndOffset();
+ String getName();
+ Element getParentElement();
+ int getStartOffset();
+ boolean isLeaf();
+ }
diff --git a/libjava/classpath/javax/swing/text/ElementIterator.java b/libjava/classpath/javax/swing/text/ElementIterator.java
new file mode 100644
index 000000000..141137e2c
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/ElementIterator.java
@@ -0,0 +1,272 @@
+/* ElementIterator.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.Stack;
+
+/**
+ * This class can be used to iterate over the {@link Element} tree of
+ * a {@link Document} or an {@link Element}. This iterator performs
+ * an "in-order" traversal -- first it visits a node, then each of the
+ * node's children in order. No locking is performed during the
+ * iteration; that is up to the caller.
+ */
+public class ElementIterator implements Cloneable
+{
+ /**
+ * Uses to track the iteration on the stack.
+ */
+ private class ElementRef
+ {
+ /**
+ * The element.
+ */
+ Element element;
+
+ /**
+ * The child index. -1 means the element itself. >= 0 values mean the
+ * n-th child of the element.
+ */
+ int index;
+
+ /**
+ * Creates a new ElementRef.
+ *
+ * @param el the element
+ */
+ ElementRef(Element el)
+ {
+ element = el;
+ index = -1;
+ }
+ }
+
+ // The root element.
+ private Element root;
+
+ /**
+ * Holds ElementRefs.
+ */
+ private Stack stack;
+
+ /**
+ * Create a new ElementIterator to iterate over the given document.
+ * @param document the Document over which we iterate
+ */
+ public ElementIterator(Document document)
+ {
+ root = document.getDefaultRootElement();
+ }
+
+ /**
+ * Create a new ElementIterator to iterate over the given document.
+ * @param root the Document over which we iterate
+ */
+ public ElementIterator(Element root)
+ {
+ this.root = root;
+ }
+
+ /**
+ * Returns a new ElementIterator which is a clone of this
+ * ElementIterator.
+ */
+ public Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException _)
+ {
+ // Can't happen.
+ return null;
+ }
+ }
+
+ /**
+ * Returns the current element.
+ */
+ public Element current()
+ {
+ Element current;
+ if (stack == null)
+ current = first();
+ else
+ {
+ current = null;
+ if (! stack.isEmpty())
+ {
+ ElementRef ref = (ElementRef) stack.peek();
+ Element el = ref.element;
+ int index = ref.index;
+ if (index == -1)
+ current = el;
+ else
+ current = el.getElement(index);
+ }
+ }
+ return current;
+ }
+
+ /**
+ * Returns the depth to which we have descended in the tree.
+ */
+ public int depth()
+ {
+ int depth = 0;
+ if (stack != null)
+ depth = stack.size();
+ return depth;
+ }
+
+ /**
+ * Returns the first element in the tree.
+ */
+ public Element first()
+ {
+ Element first = null;
+ if (root != null)
+ {
+ stack = new Stack();
+ if (root.getElementCount() > 0)
+ stack.push(new ElementRef(root));
+ first = root;
+ }
+ return first;
+ }
+
+ /**
+ * Advance the iterator and return the next element of the tree,
+ * performing an "in-order" traversal.
+ */
+ public Element next()
+ {
+ Element next;
+ if (stack == null)
+ next = first();
+ else
+ {
+ next = null;
+ if (! stack.isEmpty())
+ {
+ ElementRef ref = (ElementRef) stack.peek();
+ Element el = ref.element;
+ int index = ref.index;
+ if (el.getElementCount() > index + 1)
+ {
+ Element child = el.getElement(index + 1);
+ if (child.isLeaf())
+ ref.index++;
+ else
+ stack.push(new ElementRef(child));
+ next = child;
+ next = child;
+ }
+ else
+ {
+ stack.pop();
+ if (! stack.isEmpty())
+ {
+ ElementRef top = (ElementRef) stack.peek();
+ top.index++;
+ next = next();
+ }
+ }
+ }
+ // else return null.
+ }
+ return next;
+ }
+
+ /**
+ * Returns the previous item. Does not modify the iterator state.
+ */
+ public Element previous()
+ {
+ Element previous = null;
+ int stackSize;
+ if (stack != null && (stackSize = stack.size()) > 0)
+ {
+ ElementRef ref = (ElementRef) stack.peek();
+ Element el = ref.element;
+ int index = ref.index;
+ if (index > 0)
+ {
+ previous = deepestLeaf(el.getElement(--index));
+ }
+ else if (index == 0)
+ {
+ previous = el;
+ }
+ else if (index == -1)
+ {
+ ElementRef top = (ElementRef) stack.pop();
+ ElementRef item = (ElementRef) stack.peek();
+ stack.push(top);
+ index = item.index;
+ el = item.element;
+ previous = index == -1 ? el : deepestLeaf(el.getElement(index));
+ }
+ }
+ return previous;
+ }
+
+ /**
+ * Determines and returns the deepest leaf of the element <code>el</code>.
+ *
+ * @param el the base element
+ *
+ * @returnthe deepest leaf of the element <code>el</code>
+ */
+ private Element deepestLeaf(Element el)
+ {
+ Element leaf;
+ if (el.isLeaf())
+ leaf = el;
+ else
+ {
+ int count = el.getElementCount();
+ if (count == 0)
+ leaf = el;
+ else
+ leaf = deepestLeaf(el.getElement(count - 1));
+ }
+ return leaf;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/EmptyAttributeSet.java b/libjava/classpath/javax/swing/text/EmptyAttributeSet.java
new file mode 100644
index 000000000..92263bc1a
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/EmptyAttributeSet.java
@@ -0,0 +1,153 @@
+/* EmptyAttributeSet.java -- An empty attribute set
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * An immutable, empty attribute set.
+ *
+ * @see SimpleAttributeSet#EMPTY
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+final class EmptyAttributeSet
+ implements AttributeSet
+{
+
+ /**
+ * Always return false as this AttributeSet doesn't contain any attributes.
+ */
+ public boolean containsAttribute(Object name, Object value)
+ {
+ return false;
+ }
+
+ /**
+ * Return true only if the attributes argument also contains no attributes.
+ */
+ public boolean containsAttributes(AttributeSet attributes)
+ {
+ return attributes.getAttributeCount() == 0;
+ }
+
+ /**
+ * Return this, as this is immutable.
+ */
+ public AttributeSet copyAttributes()
+ {
+ return this;
+ }
+
+ /**
+ * Always return null as this AttributeSet doesn't contain any attributes.
+ */
+ public Object getAttribute(Object key)
+ {
+ return null;
+ }
+
+ /**
+ * Always return 0.
+ */
+ public int getAttributeCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns an empty Enumeration.
+ */
+ public Enumeration getAttributeNames()
+ {
+ return new Enumeration()
+ {
+ public boolean hasMoreElements()
+ {
+ return false;
+ }
+
+ public Object nextElement()
+ {
+ throw new NoSuchElementException("No more elements");
+ }
+
+ };
+ }
+
+ /**
+ * Always return null as this has no resolve parent.
+ */
+ public AttributeSet getResolveParent()
+ {
+ return null;
+ }
+
+ /**
+ * Always return false as this AttributeSet doesn't contain any attributes.
+ */
+ public boolean isDefined(Object attrName)
+ {
+ return false;
+ }
+
+ /**
+ * Other attribute sets are equal if they are empty too.
+ */
+ public boolean isEqual(AttributeSet attr)
+ {
+ return attr.getAttributeCount() == 0;
+ }
+
+ /**
+ * Other objects are equal if it's the same instance as this, or if
+ * it's another attribute set without attributes.
+ */
+ public boolean equals(Object o)
+ {
+ boolean eq = o == this;
+ if (! eq)
+ {
+ eq = (o instanceof AttributeSet)
+ && ((AttributeSet) o).getAttributeCount() == 0;
+ }
+ return eq;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/FieldView.java b/libjava/classpath/javax/swing/text/FieldView.java
new file mode 100644
index 000000000..c47bef9f7
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/FieldView.java
@@ -0,0 +1,310 @@
+/* FieldView.java --
+ Copyright (C) 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Component;
+import java.awt.Container;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.BoundedRangeModel;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+
+public class FieldView extends PlainView
+{
+ BoundedRangeModel horizontalVisibility;
+
+ /** Caches the preferred span of the X axis. It is invalidated by
+ * setting it to -1f. This is done when text in the document
+ * is inserted, removed or changed. The value is corrected as
+ * soon as calculateHorizontalSpan() is called.
+ */
+ float cachedSpan = -1f;
+
+ public FieldView(Element elem)
+ {
+ super(elem);
+
+ }
+
+ /** Checks whether the given container is a JTextField. If so
+ * it retrieves the textfield's horizontalVisibility instance.
+ *
+ * <p>This method should be only called when the view's container
+ * is valid. Naturally that would be the setParent() method however
+ * that method is not overridden in the RI and that is why we chose
+ * paint() instead.</p>
+ */
+ private void checkContainer()
+ {
+ Container c = getContainer();
+
+ if (c instanceof JTextField)
+ {
+ horizontalVisibility = ((JTextField) c).getHorizontalVisibility();
+
+ // Provokes a repaint when the BoundedRangeModel's values change
+ // (which is what the RI does).
+ horizontalVisibility.addChangeListener(new ChangeListener(){
+ public void stateChanged(ChangeEvent event) {
+ getContainer().repaint();
+ }
+ });
+
+ // It turned out that the span calculated at this point is wrong
+ // and needs to be recalculated (e.g. a different font setting is
+ // not taken into account).
+ calculateHorizontalSpan();
+
+ // Initializes the BoundedRangeModel properly.
+ updateVisibility();
+ }
+
+ }
+
+ private void updateVisibility()
+ {
+ JTextField tf = (JTextField) getContainer();
+ Insets insets = tf.getInsets();
+
+ int width = tf.getWidth() - insets.left - insets.right;
+
+ horizontalVisibility.setMaximum(Math.max((int) ((cachedSpan != -1f)
+ ? cachedSpan
+ : calculateHorizontalSpan()),
+ width));
+
+ horizontalVisibility.setExtent(width - 1);
+ }
+
+ protected FontMetrics getFontMetrics()
+ {
+ Component container = getContainer();
+ return container.getFontMetrics(container.getFont());
+ }
+
+ /**
+ * Vertically centers the single line of text within the
+ * bounds of the input shape. The returned Rectangle is centered
+ * vertically within <code>shape</code> and has a height of the
+ * preferred span along the Y axis. Horizontal adjustment is done according
+ * to the horizontalAligment property of the component that is rendered.
+ *
+ * @param shape the shape within which the line is beeing centered
+ */
+ protected Shape adjustAllocation(Shape shape)
+ {
+ // Return null when the original allocation is null (like the RI).
+ if (shape == null)
+ return null;
+
+ Rectangle rectIn = shape.getBounds();
+ // vertical adjustment
+ int height = (int) getPreferredSpan(Y_AXIS);
+ int y = rectIn.y + (rectIn.height - height) / 2;
+ // horizontal adjustment
+ JTextField textField = (JTextField) getContainer();
+ int width = (int) ((cachedSpan != -1f) ? cachedSpan : calculateHorizontalSpan());
+ int x;
+ if (horizontalVisibility != null && horizontalVisibility.getExtent() < width)
+ x = rectIn.x - horizontalVisibility.getValue();
+ else
+ switch (textField.getHorizontalAlignment())
+ {
+ case JTextField.CENTER:
+ x = rectIn.x + (rectIn.width - width) / 2;
+ break;
+ case JTextField.RIGHT:
+ x = rectIn.x + (rectIn.width - width - 1);
+ break;
+ case JTextField.TRAILING:
+ if (textField.getComponentOrientation().isLeftToRight())
+ x = rectIn.x + (rectIn.width - width - 1);
+ else
+ x = rectIn.x;
+ break;
+ case JTextField.LEADING:
+ if (textField.getComponentOrientation().isLeftToRight())
+ x = rectIn.x;
+ else
+ x = rectIn.x + (rectIn.width - width - 1);
+ break;
+ case JTextField.LEFT:
+ default:
+ x = rectIn.x;
+ break;
+ }
+
+ return new Rectangle(x, y, width, height);
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException();
+
+
+ if (axis == Y_AXIS)
+ return super.getPreferredSpan(axis);
+
+ if (cachedSpan != -1f)
+ return cachedSpan;
+
+ return calculateHorizontalSpan();
+ }
+
+ /** Calculates and sets the horizontal span and stores the value
+ * in cachedSpan.
+ */
+ private float calculateHorizontalSpan()
+ {
+ Segment s = getLineBuffer();
+ Element elem = getElement();
+
+ try
+ {
+ elem.getDocument().getText(elem.getStartOffset(),
+ elem.getEndOffset() - 1,
+ s);
+
+ return cachedSpan = Utilities.getTabbedTextWidth(s, getFontMetrics(), 0, this, s.offset);
+ }
+ catch (BadLocationException e)
+ {
+ // Should never happen
+ AssertionError ae = new AssertionError();
+ ae.initCause(e);
+ throw ae;
+ }
+ }
+
+ public int getResizeWeight(int axis)
+ {
+ return axis == X_AXIS ? 1 : 0;
+ }
+
+ public Shape modelToView(int pos, Shape a, Position.Bias bias)
+ throws BadLocationException
+ {
+ Shape newAlloc = adjustAllocation(a);
+ return super.modelToView(pos, newAlloc, bias);
+ }
+
+ public void paint(Graphics g, Shape s)
+ {
+ if (horizontalVisibility == null)
+ checkContainer();
+
+ Shape newAlloc = adjustAllocation(s);
+
+ Shape clip = g.getClip();
+ if (clip != null)
+ {
+ // Reason for this: The allocation area is always determined by the
+ // size of the component (and its insets) regardless of whether
+ // parts of the component are invisible or not (e.g. when the
+ // component is part of a JScrollPane and partly moved out of
+ // the user-visible range). However the clip of the Graphics
+ // instance may be adjusted properly to that condition but
+ // does not handle insets. By calculating the intersection
+ // we get the correct clip to paint the text in all cases.
+ Rectangle r = s.getBounds();
+ Rectangle cb = clip.getBounds();
+ SwingUtilities.computeIntersection(r.x, r.y, r.width, r.height, cb);
+
+ g.setClip(cb);
+ }
+ else
+ g.setClip(s);
+
+ super.paint(g, newAlloc);
+ g.setClip(clip);
+
+ }
+
+ public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ cachedSpan = -1f;
+
+ if (horizontalVisibility != null)
+ updateVisibility();
+
+ Shape newAlloc = adjustAllocation(shape);
+
+ super.insertUpdate(ev, newAlloc, vf);
+ getContainer().repaint();
+ }
+
+ public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ cachedSpan = -1f;
+
+ if (horizontalVisibility != null)
+ updateVisibility();
+
+ Shape newAlloc = adjustAllocation(shape);
+ super.removeUpdate(ev, newAlloc, vf);
+ getContainer().repaint();
+ }
+
+ public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ cachedSpan = -1f;
+
+ if (horizontalVisibility != null)
+ updateVisibility();
+
+ Shape newAlloc = adjustAllocation(shape);
+ super.changedUpdate(ev, newAlloc, vf);
+ getContainer().repaint();
+ }
+
+ public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias)
+ {
+ return super.viewToModel(fx, fy, adjustAllocation(a), bias);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java
new file mode 100644
index 000000000..a9a80833a
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/FlowView.java
@@ -0,0 +1,841 @@
+/* FlowView.java -- A composite View
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Component;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A <code>View</code> that can flows it's children into it's layout space.
+ *
+ * The <code>FlowView</code> manages a set of logical views (that are
+ * the children of the {@link #layoutPool} field). These are translated
+ * at layout time into a set of physical views. These are the views that
+ * are managed as the real child views. Each of these child views represents
+ * a row and are laid out within a box using the superclasses behaviour.
+ * The concrete implementation of the rows must be provided by subclasses.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public abstract class FlowView extends BoxView
+{
+ /**
+ * A strategy for translating the logical views of a <code>FlowView</code>
+ * into the real views.
+ */
+ public static class FlowStrategy
+ {
+ /**
+ * Creates a new instance of <code>FlowStragegy</code>.
+ */
+ public FlowStrategy()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification from a <code>FlowView</code> that some content
+ * has been inserted into the document at a location that the
+ * <code>FlowView</code> is responsible for.
+ *
+ * The default implementation simply calls {@link #layout}.
+ *
+ * @param fv the flow view that sends the notification
+ * @param e the document event describing the change
+ * @param alloc the current allocation of the flow view
+ */
+ public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
+ {
+ if (alloc == null)
+ {
+ fv.layoutChanged(X_AXIS);
+ fv.layoutChanged(Y_AXIS);
+ }
+ else
+ {
+ Component host = fv.getContainer();
+ if (host != null)
+ host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
+ }
+ }
+
+ /**
+ * Receives notification from a <code>FlowView</code> that some content
+ * has been removed from the document at a location that the
+ * <code>FlowView</code> is responsible for.
+ *
+ * The default implementation simply calls {@link #layout}.
+ *
+ * @param fv the flow view that sends the notification
+ * @param e the document event describing the change
+ * @param alloc the current allocation of the flow view
+ */
+ public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
+ {
+ if (alloc == null)
+ {
+ fv.layoutChanged(X_AXIS);
+ fv.layoutChanged(Y_AXIS);
+ }
+ else
+ {
+ Component host = fv.getContainer();
+ if (host != null)
+ host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
+ }
+ }
+
+ /**
+ * Receives notification from a <code>FlowView</code> that some attributes
+ * have changed in the document at a location that the
+ * <code>FlowView</code> is responsible for.
+ *
+ * The default implementation simply calls {@link #layout}.
+ *
+ * @param fv the flow view that sends the notification
+ * @param e the document event describing the change
+ * @param alloc the current allocation of the flow view
+ */
+ public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
+ {
+ if (alloc == null)
+ {
+ fv.layoutChanged(X_AXIS);
+ fv.layoutChanged(Y_AXIS);
+ }
+ else
+ {
+ Component host = fv.getContainer();
+ if (host != null)
+ host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
+ }
+ }
+
+ /**
+ * Returns the logical view of the managed <code>FlowView</code>.
+ *
+ * @param fv the flow view for which to return the logical view
+ *
+ * @return the logical view of the managed <code>FlowView</code>
+ */
+ protected View getLogicalView(FlowView fv)
+ {
+ return fv.layoutPool;
+ }
+
+ /**
+ * Performs the layout for the whole view. By default this rebuilds
+ * all the physical views from the logical views of the managed FlowView.
+ *
+ * This is called by {@link FlowView#layout} to update the layout of
+ * the view.
+ *
+ * @param fv the flow view for which we perform the layout
+ */
+ public void layout(FlowView fv)
+ {
+ int start = fv.getStartOffset();
+ int end = fv.getEndOffset();
+
+ // Preserve the views from the logical view from beeing removed.
+ View lv = getLogicalView(fv);
+ int viewCount = lv.getViewCount();
+ for (int i = 0; i < viewCount; i++)
+ {
+ View v = lv.getView(i);
+ v.setParent(lv);
+ }
+
+ // Then remove all views from the flow view.
+ fv.removeAll();
+
+ for (int rowIndex = 0; start < end; rowIndex++)
+ {
+ View row = fv.createRow();
+ fv.append(row);
+ int next = layoutRow(fv, rowIndex, start);
+ if (row.getViewCount() == 0)
+ {
+ row.append(createView(fv, start, Integer.MAX_VALUE, rowIndex));
+ next = row.getEndOffset();
+ }
+ if (start < next)
+ start = next;
+ else
+ assert false: "May not happen";
+ }
+ }
+
+ /**
+ * Lays out one row of the flow view. This is called by {@link #layout} to
+ * fill one row with child views until the available span is exhausted. The
+ * default implementation fills the row by calling
+ * {@link #createView(FlowView, int, int, int)} until the available space is
+ * exhausted, a forced break is encountered or there are no more views in
+ * the logical view. If the available space is exhausted,
+ * {@link #adjustRow(FlowView, int, int, int)} is called to fit the row into
+ * the available span.
+ *
+ * @param fv the flow view for which we perform the layout
+ * @param rowIndex the index of the row
+ * @param pos the model position for the beginning of the row
+ * @return the start position of the next row
+ */
+ protected int layoutRow(FlowView fv, int rowIndex, int pos)
+ {
+ View row = fv.getView(rowIndex);
+ int axis = fv.getFlowAxis();
+ int span = fv.getFlowSpan(rowIndex);
+ int x = fv.getFlowStart(rowIndex);
+ int end = fv.getEndOffset();
+
+ // Needed for adjusting indentation in adjustRow().
+ int preX = x;
+ int availableSpan = span;
+
+ TabExpander tabExp = fv instanceof TabExpander ? (TabExpander) fv : null;
+
+ boolean forcedBreak = false;
+ while (pos < end && span >= 0)
+ {
+ View view = createView(fv, pos, span, rowIndex);
+ if (view == null
+ || (span == 0 && view.getPreferredSpan(axis) > 0))
+ break;
+
+ int viewSpan;
+ if (axis == X_AXIS && view instanceof TabableView)
+ viewSpan = (int) ((TabableView) view).getTabbedSpan(x, tabExp);
+ else
+ viewSpan = (int) view.getPreferredSpan(axis);
+
+ // Break if the line if the view does not fit in this row or the
+ // line just must be broken.
+ int breakWeight = view.getBreakWeight(axis, pos, span);
+ if (breakWeight >= ForcedBreakWeight)
+ {
+ int rowViewCount = row.getViewCount();
+ if (rowViewCount > 0)
+ {
+ view = view.breakView(axis, pos, x, span);
+ if (view != null)
+ {
+ if (axis == X_AXIS && view instanceof TabableView)
+ viewSpan =
+ (int) ((TabableView) view).getTabbedSpan(x, tabExp);
+ else
+ viewSpan = (int) view.getPreferredSpan(axis);
+ }
+ else
+ viewSpan = 0;
+ }
+ forcedBreak = true;
+ }
+ span -= viewSpan;
+ x += viewSpan;
+ if (view != null)
+ {
+ row.append(view);
+ pos = view.getEndOffset();
+ }
+ if (forcedBreak)
+ break;
+ }
+
+ if (span < 0)
+ adjustRow(fv, rowIndex, availableSpan, preX);
+ else if (row.getViewCount() == 0)
+ {
+ View view = createView(fv, pos, Integer.MAX_VALUE, rowIndex);
+ row.append(view);
+ }
+ return row.getEndOffset();
+ }
+
+ /**
+ * Creates physical views that form the rows of the flow view. This
+ * can be an entire view from the logical view (if it fits within the
+ * available span), a fragment of such a view (if it doesn't fit in the
+ * available span and can be broken down) or <code>null</code> (if it does
+ * not fit in the available span and also cannot be broken down).
+ *
+ * The default implementation fetches the logical view at the specified
+ * <code>startOffset</code>. If that view has a different startOffset than
+ * specified in the argument, a fragment is created using
+ * {@link View#createFragment(int, int)} that has the correct startOffset
+ * and the logical view's endOffset.
+ *
+ * @param fv the flow view
+ * @param startOffset the start offset for the view to be created
+ * @param spanLeft the available span
+ * @param rowIndex the index of the row
+ *
+ * @return a view to fill the row with, or <code>null</code> if there
+ * is no view or view fragment that fits in the available span
+ */
+ protected View createView(FlowView fv, int startOffset, int spanLeft,
+ int rowIndex)
+ {
+ View logicalView = getLogicalView(fv);
+ int index = logicalView.getViewIndex(startOffset,
+ Position.Bias.Forward);
+ View retVal = logicalView.getView(index);
+ if (retVal.getStartOffset() != startOffset)
+ retVal = retVal.createFragment(startOffset, retVal.getEndOffset());
+ return retVal;
+ }
+
+ /**
+ * Tries to adjust the specified row to fit within the desired span. The
+ * default implementation iterates through the children of the specified
+ * row to find the view that has the highest break weight and - if there
+ * is more than one view with such a break weight - which is nearest to
+ * the end of the row. If there is such a view that has a break weight >
+ * {@link View#BadBreakWeight}, this view is broken using the
+ * {@link View#breakView(int, int, float, float)} method and this view and
+ * all views after the now broken view are replaced by the broken view.
+ *
+ * @param fv the flow view
+ * @param rowIndex the index of the row to be adjusted
+ * @param desiredSpan the layout span
+ * @param x the X location at which the row starts
+ */
+ protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
+ // Determine the last view that has the highest break weight.
+ int axis = fv.getFlowAxis();
+ View row = fv.getView(rowIndex);
+ int count = row.getViewCount();
+ int breakIndex = -1;
+ int breakWeight = BadBreakWeight;
+ int breakSpan = 0;
+ int currentSpan = 0;
+ for (int i = 0; i < count; ++i)
+ {
+ View view = row.getView(i);
+ int spanLeft = desiredSpan - currentSpan;
+ int weight = view.getBreakWeight(axis, x + currentSpan, spanLeft);
+ if (weight >= breakWeight && weight > BadBreakWeight)
+ {
+ breakIndex = i;
+ breakSpan = currentSpan;
+ breakWeight = weight;
+ if (weight >= ForcedBreakWeight)
+ // Don't search further.
+ break;
+ }
+ currentSpan += view.getPreferredSpan(axis);
+ }
+
+ // If there is a potential break location found, break the row at
+ // this location.
+ if (breakIndex >= 0)
+ {
+ int spanLeft = desiredSpan - breakSpan;
+ View toBeBroken = row.getView(breakIndex);
+ View brokenView = toBeBroken.breakView(axis,
+ toBeBroken.getStartOffset(),
+ x + breakSpan, spanLeft);
+ View lv = getLogicalView(fv);
+ for (int i = breakIndex; i < count; i++)
+ {
+ View tmp = row.getView(i);
+ if (contains(lv, tmp))
+ tmp.setParent(lv);
+ else if (tmp.getViewCount() > 0)
+ reparent(tmp, lv);
+ }
+ row.replace(breakIndex, count - breakIndex,
+ new View[]{ brokenView });
+ }
+
+ }
+
+ /**
+ * Helper method to determine if one view contains another as child.
+ */
+ private boolean contains(View view, View child)
+ {
+ boolean ret = false;
+ int n = view.getViewCount();
+ for (int i = 0; i < n && ret == false; i++)
+ {
+ if (view.getView(i) == child)
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Helper method that reparents the <code>view</code> and all of its
+ * decendents to the <code>parent</code> (the logical view).
+ *
+ * @param view the view to reparent
+ * @param parent the new parent
+ */
+ private void reparent(View view, View parent)
+ {
+ int n = view.getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View tmp = view.getView(i);
+ if (contains(parent, tmp))
+ tmp.setParent(parent);
+ else
+ reparent(tmp, parent);
+ }
+ }
+ }
+
+ /**
+ * This special subclass of <code>View</code> is used to represent
+ * the logical representation of this view. It does not support any
+ * visual representation, this is handled by the physical view implemented
+ * in the <code>FlowView</code>.
+ */
+ class LogicalView extends CompositeView
+ {
+ /**
+ * Creates a new LogicalView instance.
+ */
+ LogicalView(Element el)
+ {
+ super(el);
+ }
+
+ /**
+ * Overridden to return the attributes of the parent
+ * (== the FlowView instance).
+ */
+ public AttributeSet getAttributes()
+ {
+ View p = getParent();
+ return p != null ? p.getAttributes() : null;
+ }
+
+ protected void childAllocation(int index, Rectangle a)
+ {
+ // Nothing to do here (not visual).
+ }
+
+ protected View getViewAtPoint(int x, int y, Rectangle r)
+ {
+ // Nothing to do here (not visual).
+ return null;
+ }
+
+ protected boolean isAfter(int x, int y, Rectangle r)
+ {
+ // Nothing to do here (not visual).
+ return false;
+ }
+
+ protected boolean isBefore(int x, int y, Rectangle r)
+ {
+ // Nothing to do here (not visual).
+ return false;
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ float max = 0;
+ float pref = 0;
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View v = getView(i);
+ pref += v.getPreferredSpan(axis);
+ if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE)
+ >= ForcedBreakWeight)
+ {
+ max = Math.max(max, pref);
+ pref = 0;
+ }
+ }
+ max = Math.max(max, pref);
+ return max;
+ }
+
+ public float getMinimumSpan(int axis)
+ {
+ float max = 0;
+ float min = 0;
+ boolean wrap = true;
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View v = getView(i);
+ if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE)
+ == BadBreakWeight)
+ {
+ min += v.getPreferredSpan(axis);
+ wrap = false;
+ }
+ else if (! wrap)
+ {
+ max = Math.max(min, max);
+ wrap = true;
+ min = 0;
+ }
+ }
+ max = Math.max(max, min);
+ return max;
+ }
+
+ public void paint(Graphics g, Shape s)
+ {
+ // Nothing to do here (not visual).
+ }
+
+ /**
+ * Overridden to handle possible leaf elements.
+ */
+ protected void loadChildren(ViewFactory f)
+ {
+ Element el = getElement();
+ if (el.isLeaf())
+ {
+ View v = new LabelView(el);
+ append(v);
+ }
+ else
+ super.loadChildren(f);
+ }
+
+ /**
+ * Overridden to reparent the children to this logical view, in case
+ * they have been parented by a row.
+ */
+ protected void forwardUpdateToView(View v, DocumentEvent e, Shape a,
+ ViewFactory f)
+ {
+ v.setParent(this);
+ super.forwardUpdateToView(v, e, a, f);
+ }
+
+ /**
+ * Overridden to handle possible leaf element.
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ int index = 0;
+ if (! getElement().isLeaf())
+ index = super.getViewIndexAtPosition(pos);
+ return index;
+ }
+ }
+
+ /**
+ * The shared instance of FlowStrategy.
+ */
+ static final FlowStrategy sharedStrategy = new FlowStrategy();
+
+ /**
+ * The span of the <code>FlowView</code> that should be flowed.
+ */
+ protected int layoutSpan;
+
+ /**
+ * Represents the logical child elements of this view, encapsulated within
+ * one parent view (an instance of a package private <code>LogicalView</code>
+ * class). These will be translated to a set of real views that are then
+ * displayed on screen. This translation is performed by the inner class
+ * {@link FlowStrategy}.
+ */
+ protected View layoutPool;
+
+ /**
+ * The <code>FlowStrategy</code> to use for translating between the
+ * logical and physical view.
+ */
+ protected FlowStrategy strategy;
+
+ /**
+ * Creates a new <code>FlowView</code> for the given
+ * <code>Element</code> and <code>axis</code>.
+ *
+ * @param element the element that is rendered by this FlowView
+ * @param axis the axis along which the view is tiled, either
+ * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>, the flow
+ * axis is orthogonal to this one
+ */
+ public FlowView(Element element, int axis)
+ {
+ super(element, axis);
+ strategy = sharedStrategy;
+ layoutSpan = Short.MAX_VALUE;
+ }
+
+ /**
+ * Returns the axis along which the view should be flowed. This is
+ * orthogonal to the axis along which the boxes are tiled.
+ *
+ * @return the axis along which the view should be flowed
+ */
+ public int getFlowAxis()
+ {
+ int axis = getAxis();
+ int flowAxis;
+
+ if (axis == X_AXIS)
+ flowAxis = Y_AXIS;
+ else
+ flowAxis = X_AXIS;
+
+ return flowAxis;
+
+ }
+
+ /**
+ * Returns the span of the flow for the specified child view. A flow
+ * layout can be shaped by providing different span values for different
+ * child indices. The default implementation returns the entire available
+ * span inside the view.
+ *
+ * @param index the index of the child for which to return the span
+ *
+ * @return the span of the flow for the specified child view
+ */
+ public int getFlowSpan(int index)
+ {
+ return layoutSpan;
+ }
+
+ /**
+ * Returns the location along the flow axis where the flow span starts
+ * given a child view index. The flow can be shaped by providing
+ * different values here.
+ *
+ * @param index the index of the child for which to return the flow location
+ *
+ * @return the location along the flow axis where the flow span starts
+ */
+ public int getFlowStart(int index)
+ {
+ return 0;
+ }
+
+ /**
+ * Creates a new view that represents a row within a flow.
+ *
+ * @return a view for a new row
+ */
+ protected abstract View createRow();
+
+ /**
+ * Loads the children of this view. The <code>FlowView</code> does not
+ * directly load its children. Instead it creates a logical view
+ * ({@link #layoutPool}) which is filled by the logical child views.
+ * The real children are created at layout time and each represent one
+ * row.
+ *
+ * This method is called by {@link View#setParent} in order to initialize
+ * the view.
+ *
+ * @param vf the view factory to use for creating the child views
+ */
+ protected void loadChildren(ViewFactory vf)
+ {
+ if (layoutPool == null)
+ {
+ layoutPool = new LogicalView(getElement());
+ }
+ layoutPool.setParent(this);
+ // Initialize the flow strategy.
+ strategy.insertUpdate(this, null, null);
+ }
+
+ /**
+ * Performs the layout of this view. If the span along the flow axis changed,
+ * this first calls {@link FlowStrategy#layout} in order to rebuild the
+ * rows of this view. Then the superclass's behaviour is called to arrange
+ * the rows within the box.
+ *
+ * @param width the width of the view
+ * @param height the height of the view
+ */
+ protected void layout(int width, int height)
+ {
+ int flowAxis = getFlowAxis();
+ int span;
+ if (flowAxis == X_AXIS)
+ span = (int) width;
+ else
+ span = (int) height;
+
+ if (layoutSpan != span)
+ {
+ layoutChanged(flowAxis);
+ layoutChanged(getAxis());
+ layoutSpan = span;
+ }
+
+ if (! isLayoutValid(flowAxis))
+ {
+ int axis = getAxis();
+ int oldSpan = axis == X_AXIS ? getWidth() : getHeight();
+ strategy.layout(this);
+ int newSpan = (int) getPreferredSpan(axis);
+ if (oldSpan != newSpan)
+ {
+ View parent = getParent();
+ if (parent != null)
+ parent.preferenceChanged(this, axis == X_AXIS, axis == Y_AXIS);
+ }
+ }
+
+ super.layout(width, height);
+ }
+
+ /**
+ * Receice notification that some content has been inserted in the region
+ * that this view is responsible for. This calls
+ * {@link FlowStrategy#insertUpdate}.
+ *
+ * @param changes the document event describing the changes
+ * @param a the current allocation of the view
+ * @param vf the view factory that is used for creating new child views
+ */
+ public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
+ {
+ // First we must send the insertUpdate to the logical view so it can
+ // be updated accordingly.
+ layoutPool.insertUpdate(changes, a, vf);
+ strategy.insertUpdate(this, changes, getInsideAllocation(a));
+ }
+
+ /**
+ * Receice notification that some content has been removed from the region
+ * that this view is responsible for. This calls
+ * {@link FlowStrategy#removeUpdate}.
+ *
+ * @param changes the document event describing the changes
+ * @param a the current allocation of the view
+ * @param vf the view factory that is used for creating new child views
+ */
+ public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
+ {
+ layoutPool.removeUpdate(changes, a, vf);
+ strategy.removeUpdate(this, changes, getInsideAllocation(a));
+ }
+
+ /**
+ * Receice notification that some attributes changed in the region
+ * that this view is responsible for. This calls
+ * {@link FlowStrategy#changedUpdate}.
+ *
+ * @param changes the document event describing the changes
+ * @param a the current allocation of the view
+ * @param vf the view factory that is used for creating new child views
+ */
+ public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
+ {
+ layoutPool.changedUpdate(changes, a, vf);
+ strategy.changedUpdate(this, changes, getInsideAllocation(a));
+ }
+
+ /**
+ * Returns the index of the child <code>View</code> for the given model
+ * position.
+ *
+ * This is implemented to iterate over the children of this
+ * view (the rows) and return the index of the first view that contains
+ * the given position.
+ *
+ * @param pos the model position for whicht the child <code>View</code> is
+ * queried
+ *
+ * @return the index of the child <code>View</code> for the given model
+ * position
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ // First make sure we have a valid layout.
+ if (!isAllocationValid())
+ layout(getWidth(), getHeight());
+
+ int count = getViewCount();
+ int result = -1;
+
+ for (int i = 0; i < count; ++i)
+ {
+ View child = getView(i);
+ int start = child.getStartOffset();
+ int end = child.getEndOffset();
+ if (start <= pos && end > pos)
+ {
+ result = i;
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Calculates the size requirements of this <code>BoxView</code> along
+ * its minor axis, that is the axis opposite to the axis specified in the
+ * constructor.
+ *
+ * This is overridden and forwards the request to the logical view.
+ *
+ * @param axis the axis that is examined
+ * @param r the <code>SizeRequirements</code> object to hold the result,
+ * if <code>null</code>, a new one is created
+ *
+ * @return the size requirements for this <code>BoxView</code> along
+ * the specified axis
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ SizeRequirements res = r;
+ if (res == null)
+ res = new SizeRequirements();
+ res.minimum = (int) layoutPool.getMinimumSpan(axis);
+ res.preferred = Math.max(res.minimum,
+ (int) layoutPool.getPreferredSpan(axis));
+ res.maximum = Integer.MAX_VALUE;
+ res.alignment = 0.5F;
+ return res;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java
new file mode 100644
index 000000000..2e68fb5fe
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/GapContent.java
@@ -0,0 +1,1095 @@
+/* GapContent.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.CannotRedoException;
+import javax.swing.undo.CannotUndoException;
+import javax.swing.undo.UndoableEdit;
+
+/**
+ * This implementation of {@link AbstractDocument.Content} uses a gapped buffer.
+ * This takes advantage of the fact that text area content is mostly inserted
+ * sequentially. The buffer is a char array that maintains a gap at the current
+ * insertion point. If characters a inserted at gap boundaries, the cost is
+ * minimal (simple array access). The array only has to be shifted around when
+ * the insertion point moves (then the gap also moves and one array copy is
+ * necessary) or when the gap is filled up and the buffer has to be enlarged.
+ */
+public class GapContent
+ implements AbstractDocument.Content, Serializable
+{
+
+ /**
+ * A {@link Position} implementation for <code>GapContent</code>.
+ */
+ class GapContentPosition
+ implements Position
+ {
+
+ /**
+ * The index to the positionMarks array entry, which in turn holds the
+ * mark into the buffer array.
+ */
+ Mark mark;
+
+ /**
+ * Returns the current offset of this Position within the content.
+ *
+ * @return the current offset of this Position within the content.
+ */
+ public int getOffset()
+ {
+ return mark.getOffset();
+ }
+ }
+
+ /**
+ * Holds a mark into the buffer that is used by GapContentPosition to find
+ * the actual offset of the position. This is pulled out of the
+ * GapContentPosition object so that the mark and position can be handled
+ * independently, and most important, so that the GapContentPosition can
+ * be garbage collected while we still hold a reference to the Mark object.
+ */
+ private class Mark
+ extends WeakReference
+ {
+ /**
+ * The actual mark into the buffer.
+ */
+ int mark;
+
+ /**
+ * Creates a new Mark object for the specified offset.
+ *
+ * @param offset the offset
+ */
+ Mark(int offset)
+ {
+ super(null);
+ mark = offset;
+ }
+
+ Mark(int offset, GapContentPosition pos, ReferenceQueue queue)
+ {
+ super(pos, queue);
+ mark = offset;
+ }
+
+ /**
+ * Returns the offset of the mark.
+ *
+ * @return the offset of the mark
+ */
+ int getOffset()
+ {
+ int res = mark;
+ if (mark >= gapStart)
+ res -= (gapEnd - gapStart);
+ return Math.max(0, res);
+ }
+
+ /**
+ * Returns the GapContentPosition that is associated ith this mark.
+ * This fetches the weakly referenced position object.
+ *
+ * @return the GapContentPosition that is associated ith this mark
+ */
+ GapContentPosition getPosition()
+ {
+ return (GapContentPosition) get();
+ }
+
+ }
+
+ /**
+ * Stores a reference to a mark that can be resetted to the original value
+ * after a mark has been moved. This is used for undoing actions.
+ */
+ private class UndoPosRef
+ {
+ /**
+ * The mark that might need to be reset.
+ */
+ private Mark mark;
+
+ /**
+ * The original offset to reset the mark to.
+ */
+ private int undoOffset;
+
+ /**
+ * Creates a new UndoPosRef.
+ *
+ * @param m the mark
+ */
+ UndoPosRef(Mark m)
+ {
+ mark = m;
+ undoOffset = mark.getOffset();
+ }
+
+ /**
+ * Resets the position of the mark to the value that it had when
+ * creating this UndoPosRef.
+ */
+ void reset()
+ {
+ if (undoOffset <= gapStart)
+ mark.mark = undoOffset;
+ else
+ mark.mark = (gapEnd - gapStart) + undoOffset;
+ }
+ }
+
+ private class InsertUndo extends AbstractUndoableEdit
+ {
+ public int where, length;
+ String text;
+ private Vector positions;
+
+ public InsertUndo(int start, int len)
+ {
+ where = start;
+ length = len;
+ }
+
+ public void undo () throws CannotUndoException
+ {
+ super.undo();
+ try
+ {
+ positions = getPositionsInRange(null, where, length);
+ text = getString(where, length);
+ remove(where, length);
+ }
+ catch (BadLocationException ble)
+ {
+ throw new CannotUndoException();
+ }
+ }
+
+ public void redo () throws CannotUndoException
+ {
+ super.redo();
+ try
+ {
+ insertString(where, text);
+ if (positions != null)
+ {
+ updateUndoPositions(positions, where, length);
+ positions = null;
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ throw new CannotRedoException();
+ }
+ }
+
+ }
+
+ private class UndoRemove extends AbstractUndoableEdit
+ {
+ public int where;
+ String text;
+
+ /**
+ * The positions in the removed range.
+ */
+ private Vector positions;
+
+ public UndoRemove(int start, String removedText)
+ {
+ where = start;
+ text = removedText;
+ positions = getPositionsInRange(null, start, removedText.length());
+ }
+
+ public void undo () throws CannotUndoException
+ {
+ super.undo();
+ try
+ {
+ insertString(where, text);
+ if (positions != null)
+ updateUndoPositions(positions, where, text.length());
+ }
+ catch (BadLocationException ble)
+ {
+ throw new CannotUndoException();
+ }
+ }
+
+ public void redo () throws CannotUndoException
+ {
+ super.redo();
+ try
+ {
+ text = getString(where, text.length());
+ positions = getPositionsInRange(null, where, text.length());
+ remove(where, text.length());
+ }
+ catch (BadLocationException ble)
+ {
+ throw new CannotRedoException();
+ }
+ }
+
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -6226052713477823730L;
+
+ /**
+ * This is the default buffer size and the amount of bytes that a buffer is
+ * extended if it is full.
+ */
+ static final int DEFAULT_BUFSIZE = 10;
+
+ /**
+ * The text buffer.
+ */
+ char[] buffer;
+
+ /**
+ * The index of the first character of the gap.
+ */
+ int gapStart;
+
+ /**
+ * The index of the character after the last character of the gap.
+ */
+ int gapEnd;
+
+ // FIXME: We might want to track GC'ed GapContentPositions and remove their
+ // corresponding marks, or alternativly, perform some regular cleanup of
+ // the positionMarks array.
+
+ /**
+ * Holds the marks for positions. These marks are referenced by the
+ * GapContentPosition instances by an index into this array.
+ *
+ * This is package private to avoid accessor synthetic methods.
+ */
+ ArrayList marks;
+
+ /**
+ * The number of unused marks.
+ */
+ private int garbageMarks;
+
+ /**
+ * A 'static' mark that is used for searching.
+ */
+ private Mark searchMark = new Mark(0);
+
+ /**
+ * Queues all references to GapContentPositions that are about to be
+ * GC'ed. This is used to remove the corresponding marks from the
+ * positionMarks array if the number of references to that mark reaches zero.
+ *
+ * This is package private to avoid accessor synthetic methods.
+ */
+ ReferenceQueue queueOfDeath;
+
+ /**
+ * Creates a new GapContent object.
+ */
+ public GapContent()
+ {
+ this(DEFAULT_BUFSIZE);
+ }
+
+ /**
+ * Creates a new GapContent object with a specified initial size.
+ *
+ * @param size the initial size of the buffer
+ */
+ public GapContent(int size)
+ {
+ size = Math.max(size, 2);
+ buffer = (char[]) allocateArray(size);
+ gapStart = 1;
+ gapEnd = size;
+ buffer[0] = '\n';
+ marks = new ArrayList();
+ queueOfDeath = new ReferenceQueue();
+ }
+
+ /**
+ * Allocates an array of the specified length that can then be used as
+ * buffer.
+ *
+ * @param size the size of the array to be allocated
+ *
+ * @return the allocated array
+ */
+ protected Object allocateArray(int size)
+ {
+ return new char[size];
+ }
+
+ /**
+ * Returns the length of the allocated buffer array.
+ *
+ * @return the length of the allocated buffer array
+ */
+ protected int getArrayLength()
+ {
+ return buffer.length;
+ }
+
+ /**
+ * Returns the length of the content.
+ *
+ * @return the length of the content
+ */
+ public int length()
+ {
+ return buffer.length - (gapEnd - gapStart);
+ }
+
+ /**
+ * Inserts a string at the specified position.
+ *
+ * @param where the position where the string is inserted
+ * @param str the string that is to be inserted
+ *
+ * @return an UndoableEdit object
+ *
+ * @throws BadLocationException if <code>where</code> is not a valid
+ * location in the buffer
+ */
+ public UndoableEdit insertString(int where, String str)
+ throws BadLocationException
+ {
+ // check arguments
+ int length = length();
+ int strLen = str.length();
+
+ if (where < 0)
+ throw new BadLocationException("The where argument cannot be smaller"
+ + " than the zero", where);
+
+ if (where > length)
+ throw new BadLocationException("The where argument cannot be greater"
+ + " than the content length", where);
+
+ InsertUndo undo = new InsertUndo(where, strLen);
+ replace(where, 0, str.toCharArray(), strLen);
+
+ return undo;
+ }
+
+ /**
+ * Removes a piece of content at th specified position.
+ *
+ * @param where the position where the content is to be removed
+ * @param nitems number of characters to be removed
+ *
+ * @return an UndoableEdit object
+ *
+ * @throws BadLocationException if <code>where</code> is not a valid
+ * location in the buffer
+ */
+ public UndoableEdit remove(int where, int nitems) throws BadLocationException
+ {
+ // check arguments
+ int length = length();
+
+ if ((where + nitems) >= length)
+ throw new BadLocationException("where + nitems cannot be greater"
+ + " than the content length", where + nitems);
+
+ String removedText = getString(where, nitems);
+ UndoRemove undoRemove = new UndoRemove(where, removedText);
+ replace(where, nitems, null, 0);
+
+ return undoRemove;
+ }
+
+ /**
+ * Returns a piece of content as String.
+ *
+ * @param where the start location of the fragment
+ * @param len the length of the fragment
+ *
+ * @throws BadLocationException if <code>where</code> or
+ * <code>where + len</code> are no valid locations in the buffer
+ */
+ public String getString(int where, int len) throws BadLocationException
+ {
+ Segment seg = new Segment();
+ try
+ {
+ getChars(where, len, seg);
+ return new String(seg.array, seg.offset, seg.count);
+ }
+ catch (StringIndexOutOfBoundsException ex)
+ {
+ int invalid = 0;
+ if (seg.offset < 0 || seg.offset >= seg.array.length)
+ invalid = seg.offset;
+ else
+ invalid = seg.offset + seg.count;
+ throw new BadLocationException("Illegal location: array.length = "
+ + seg.array.length + ", offset = "
+ + seg.offset + ", count = "
+ + seg.count, invalid);
+ }
+ }
+
+ /**
+ * Fetches a piece of content and stores it in a {@link Segment} object.
+ *
+ * If the requested piece of text spans the gap, the content is copied into a
+ * new array. If it doesn't then it is contiguous and the actual content
+ * store is returned.
+ *
+ * @param where the start location of the fragment
+ * @param len the length of the fragment
+ * @param txt the Segment object to store the fragment in
+ *
+ * @throws BadLocationException if <code>where</code> or
+ * <code>where + len</code> are no valid locations in the buffer
+ */
+ public void getChars(int where, int len, Segment txt)
+ throws BadLocationException
+ {
+ // check arguments
+ int length = length();
+ if (where < 0)
+ throw new BadLocationException("the where argument may not be below zero", where);
+ if (where >= length)
+ throw new BadLocationException("the where argument cannot be greater"
+ + " than the content length", where);
+ if ((where + len) > length)
+ throw new BadLocationException("len plus where cannot be greater"
+ + " than the content length", len + where);
+ if (len < 0)
+ throw new BadLocationException("negative length not allowed: ", len);
+
+ // Optimized to copy only when really needed.
+ if (where + len <= gapStart)
+ {
+ // Simple case: completely before gap.
+ txt.array = buffer;
+ txt.offset = where;
+ txt.count = len;
+ }
+ else if (where > gapStart)
+ {
+ // Completely after gap, adjust offset.
+ txt.array = buffer;
+ txt.offset = gapEnd + where - gapStart;
+ txt.count = len;
+ }
+ else
+ {
+ // Spans the gap.
+ int beforeGap = gapStart - where;
+ if (txt.isPartialReturn())
+ {
+ // Return the part before the gap when partial return is allowed.
+ txt.array = buffer;
+ txt.offset = where;
+ txt.count = beforeGap;
+ }
+ else
+ {
+ // Copy pieces together otherwise.
+ txt.array = new char[len];
+ txt.offset = 0;
+ System.arraycopy(buffer, where, txt.array, 0, beforeGap);
+ System.arraycopy(buffer, gapEnd, txt.array, beforeGap,
+ len - beforeGap);
+ txt.count = len;
+ }
+ }
+ }
+
+ /**
+ * Creates and returns a mark at the specified position.
+ *
+ * @param offset the position at which to create the mark
+ *
+ * @return the create Position object for the mark
+ *
+ * @throws BadLocationException if the offset is not a valid position in the
+ * buffer
+ */
+ public Position createPosition(final int offset) throws BadLocationException
+ {
+ // Implementation note: We used to perform explicit check on the offset
+ // here. However, this makes some Mauve and Intel/Harmony tests fail
+ // and luckily enough the GapContent can very well deal with offsets
+ // outside the buffer bounds. So I removed that check.
+
+ // First do some garbage collections.
+ while (queueOfDeath.poll() != null)
+ garbageMarks++;
+ if (garbageMarks > Math.max(5, marks.size() / 10))
+ garbageCollect();
+
+ // We try to find a GapContentPosition at the specified offset and return
+ // that. Otherwise we must create a new one.
+ Mark m;
+ GapContentPosition pos;
+ int index = offset;
+ if (offset >= gapStart)
+ index += (gapEnd - gapStart);
+ searchMark.mark = index;
+ int insertIndex = search(searchMark);
+ if (!(insertIndex < marks.size()
+ && (m = (Mark) marks.get(insertIndex)).mark == index
+ && (pos = m.getPosition()) != null))
+ {
+ // Create new position if none was found.
+ pos = new GapContentPosition();
+ m = new Mark(index, pos, queueOfDeath);
+ pos.mark = m;
+ marks.add(insertIndex, m);
+ }
+ // Otherwise use the found position.
+
+ return pos;
+ }
+
+ /**
+ * Enlarges the gap. This allocates a new bigger buffer array, copy the
+ * segment before the gap as it is and the segment after the gap at the end
+ * of the new buffer array. This does change the gapEnd mark but not the
+ * gapStart mark.
+ *
+ * @param newSize the new size of the gap
+ */
+ protected void shiftEnd(int newSize)
+ {
+ assert newSize > (gapEnd - gapStart) : "The new gap size must be greater "
+ + "than the old gap size";
+
+ int oldEnd = getGapEnd();
+ int oldSize = getArrayLength();
+ int upper = oldSize - oldEnd;
+ int size = (newSize + 1) * 2;
+ int newEnd = size - upper;
+
+ // Copy the data around.
+ char[] newBuf = (char[]) allocateArray(size);
+ System.arraycopy(buffer, 0, newBuf, 0, Math.min(size, oldSize));
+ buffer = newBuf;
+ gapEnd = newEnd;
+ if (upper != 0)
+ System.arraycopy(buffer, oldEnd, buffer, newEnd, upper);
+
+ // Adjust marks.
+ int delta = gapEnd - oldEnd;
+ int adjIndex = searchFirst(oldEnd);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ m.mark += delta;
+ }
+ }
+
+ /**
+ * Shifts the gap to the specified position.
+ *
+ * @param newGapStart the new start position of the gap
+ */
+ protected void shiftGap(int newGapStart)
+ {
+ int oldStart = gapStart;
+ int delta = newGapStart - oldStart;
+ int oldEnd = gapEnd;
+ int newGapEnd = oldEnd + delta;
+ int size = oldEnd - oldStart;
+
+ // Shift gap in array.
+ gapStart = newGapStart;
+ gapEnd = newGapEnd;
+ if (delta > 0)
+ System.arraycopy(buffer, oldEnd, buffer, oldStart, delta);
+ else
+ System.arraycopy(buffer, newGapStart, buffer, newGapEnd, -delta);
+
+ // Adjust marks.
+ if (delta > 0)
+ {
+ int adjIndex = searchFirst(oldStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= newGapEnd)
+ break;
+ m.mark -= size;
+ }
+ }
+ else if (delta < 0)
+ {
+ int adjIndex = searchFirst(newGapStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= oldEnd)
+ break;
+ m.mark += size;
+ }
+ }
+ resetMarksAtZero();
+ }
+
+ /**
+ * Shifts the gap start downwards. This does not affect the content of the
+ * buffer. This only updates the gap start and all the marks that are between
+ * the old gap start and the new gap start. They all are squeezed to the start
+ * of the gap, because their location has been removed.
+ *
+ * @param newGapStart the new gap start
+ */
+ protected void shiftGapStartDown(int newGapStart)
+ {
+ if (newGapStart == gapStart)
+ return;
+
+ assert newGapStart < gapStart : "The new gap start must be less than the "
+ + "old gap start.";
+
+ // Adjust positions.
+ int adjIndex = searchFirst(newGapStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark > gapStart)
+ break;
+ m.mark = gapEnd;
+ }
+
+ gapStart = newGapStart;
+ resetMarksAtZero();
+ }
+
+ /**
+ * Shifts the gap end upwards. This does not affect the content of the
+ * buffer. This only updates the gap end and all the marks that are between
+ * the old gap end and the new end start. They all are squeezed to the end
+ * of the gap, because their location has been removed.
+ *
+ * @param newGapEnd the new gap start
+ */
+ protected void shiftGapEndUp(int newGapEnd)
+ {
+ if (newGapEnd == gapEnd)
+ return;
+
+ assert newGapEnd > gapEnd : "The new gap end must be greater than the "
+ + "old gap end.";
+
+ // Adjust marks.
+ int adjIndex = searchFirst(gapEnd);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= newGapEnd)
+ break;
+ m.mark = newGapEnd;
+ }
+
+
+ gapEnd = newGapEnd;
+ resetMarksAtZero();
+ }
+
+ /**
+ * Returns the allocated buffer array.
+ *
+ * @return the allocated buffer array
+ */
+ protected final Object getArray()
+ {
+ return buffer;
+ }
+
+ /**
+ * Replaces a portion of the storage with the specified items.
+ *
+ * @param position the position at which to remove items
+ * @param rmSize the number of items to remove
+ * @param addItems the items to add at location
+ * @param addSize the number of items to add
+ */
+ protected void replace(int position, int rmSize, Object addItems,
+ int addSize)
+ {
+ if (addSize == 0)
+ {
+ removeImpl(position, rmSize);
+ return;
+ }
+ else if (rmSize > addSize)
+ {
+ removeImpl(position + addSize, rmSize - addSize);
+ }
+ else
+ {
+ int endSize = addSize - rmSize;
+ int end = addImpl(position + rmSize, endSize);
+ System.arraycopy(addItems, rmSize, buffer, end, endSize);
+ addSize = rmSize;
+ }
+ System.arraycopy(addItems, 0, buffer, position, addSize);
+ }
+
+ /**
+ * Adjusts the positions and gap in response to a remove operation.
+ *
+ * @param pos the position at which to remove
+ * @param num the number of removed items
+ */
+ private void removeImpl(int pos, int num)
+ {
+ if (num > 0)
+ {
+ int end = pos + num;
+ int newGapSize = (gapEnd - gapStart) + num;
+ if (end <= gapStart)
+ {
+ if (gapStart != end)
+ {
+ shiftGap(end);
+ }
+ shiftGapStartDown(gapStart - num);
+ }
+ else if (pos >= gapStart)
+ {
+ if (gapStart != pos)
+ {
+ shiftGap(pos);
+ }
+ shiftGapEndUp(gapStart + newGapSize);
+ }
+ else
+ {
+ shiftGapStartDown(pos);
+ shiftGapEndUp(gapStart + newGapSize);
+ }
+ }
+ }
+
+ /**
+ * Adjusts the positions and gap in response to an add operation.
+ *
+ * @param pos the position at which to add
+ * @param num the number of added items
+ *
+ * @return the adjusted position
+ */
+ private int addImpl(int pos, int num)
+ {
+ int size = gapEnd - gapStart;
+ if (num == 0)
+ {
+ if (pos > gapStart)
+ pos += size;
+ return pos;
+ }
+
+ shiftGap(pos);
+ if (num >= size)
+ {
+ shiftEnd(getArrayLength() - size + num);
+ size = gapEnd - gapStart;
+ }
+
+ gapStart += num;
+ return pos;
+ }
+
+ /**
+ * Returns the start index of the gap within the buffer array.
+ *
+ * @return the start index of the gap within the buffer array
+ */
+ protected final int getGapStart()
+ {
+ return gapStart;
+ }
+
+ /**
+ * Returns the end index of the gap within the buffer array.
+ *
+ * @return the end index of the gap within the buffer array
+ */
+ protected final int getGapEnd()
+ {
+ return gapEnd;
+ }
+
+ /**
+ * Returns all <code>Position</code>s that are in the range specified by
+ * <code>offset</code> and </code>length</code> within the buffer array.
+ *
+ * @param v the vector to use; if <code>null</code>, a new Vector is allocated
+ * @param offset the start offset of the range to search
+ * @param length the length of the range to search
+ *
+ * @return the positions within the specified range
+ */
+ protected Vector getPositionsInRange(Vector v, int offset, int length)
+ {
+ int end = offset + length;
+ int startIndex;
+ int endIndex;
+ if (offset < gapStart)
+ {
+ if (offset == 0)
+ startIndex = 0;
+ else
+ startIndex = searchFirst(offset);
+ if (end >= gapStart)
+ endIndex = searchFirst(end + (gapEnd - gapStart) + 1);
+ else
+ endIndex = searchFirst(end + 1);
+ }
+ else
+ {
+ startIndex = searchFirst(offset + (gapEnd - gapStart));
+ endIndex = searchFirst(end + (gapEnd - gapStart) + 1);
+ }
+ if (v == null)
+ v = new Vector();
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ v.add(new UndoPosRef((Mark) marks.get(i)));
+ }
+ return v;
+ }
+
+ /**
+ * Resets all <code>Position</code> that have an offset of <code>0</code>,
+ * to also have an array index of <code>0</code>. This might be necessary
+ * after a call to <code>shiftGap(0)</code>, since then the marks at offset
+ * <code>0</code> get shifted to <code>gapEnd</code>.
+ */
+ protected void resetMarksAtZero()
+ {
+ if (gapStart != 0)
+ return;
+
+ for (int i = 0; i < marks.size(); i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark <= gapEnd)
+ m.mark = 0;
+ }
+ }
+
+ /**
+ * Resets the positions in the specified range to their original offset
+ * after a undo operation is performed. For example, after removing some
+ * content, the positions in the removed range will all be set to one
+ * offset. This method restores the positions to their original offsets
+ * after an undo.
+ *
+ * @param positions the positions to update
+ * @param offset
+ * @param length
+ */
+ protected void updateUndoPositions(Vector positions, int offset, int length)
+ {
+ for (Iterator i = positions.iterator(); i.hasNext();)
+ {
+ UndoPosRef undoPosRef = (UndoPosRef) i.next();
+ undoPosRef.reset();
+ }
+
+ // Resort marks.
+ Collections.sort(marks);
+ }
+
+ /**
+ * Outputs debugging info to System.err. It prints out the buffer array,
+ * the gapStart is marked by a &lt; sign, the gapEnd is marked by a &gt;
+ * sign and each position is marked by a # sign.
+ */
+ private void dump()
+ {
+ System.err.println("GapContent debug information");
+ System.err.println("buffer length: " + buffer.length);
+ System.err.println("gap start: " + gapStart);
+ System.err.println("gap end: " + gapEnd);
+ for (int i = 0; i < buffer.length; i++)
+ {
+ if (i == gapStart)
+ System.err.print('<');
+ if (i == gapEnd)
+ System.err.print('>');
+
+ if (!Character.isISOControl(buffer[i]))
+ System.err.print(buffer[i]);
+ else
+ System.err.print('.');
+ }
+ System.err.println();
+ }
+
+ /**
+ * Prints out the position marks.
+ */
+ private void dumpMarks()
+ {
+ System.out.print("positionMarks: ");
+ for (int i = 0; i < marks.size(); i++)
+ System.out.print(((Mark) marks.get(i)).mark + ", ");
+ System.out.println();
+ }
+
+ /**
+ * Searches the first occurance of object <code>o</code> in list
+ * <code>l</code>. This performs a binary search by calling
+ * {@link Collections#binarySearch(List, Object)} and when an object has been
+ * found, it searches backwards to the first occurance of that object in the
+ * list. The meaning of the return value is the same as in
+ * <code>Collections.binarySearch()</code>.
+ *
+ * @param o the object to be searched
+ *
+ * @return the index of the first occurance of o in l, or -i + 1 if not found
+ */
+ int search(Mark o)
+ {
+ int foundInd = 0;
+ boolean found = false;
+ int low = 0;
+ int up = marks.size() - 1;
+ int mid = 0;
+ if (up > -1)
+ {
+ int cmp = 0;
+ Mark last = (Mark) marks.get(up);
+ cmp = compare(o, last);
+ if (cmp > 0)
+ {
+ foundInd = up + 1;
+ found = true;
+ }
+ else
+ {
+ while (low <= up && ! found)
+ {
+ mid = low + (up - low) / 2;
+ Mark m = (Mark) marks.get(mid);
+ cmp = compare(o, m);
+ if (cmp == 0)
+ {
+ foundInd = mid;
+ found = true;
+ }
+ else if (cmp < 0)
+ up = mid - 1;
+ else
+ low = mid + 1;
+ }
+
+ if (! found)
+ foundInd = cmp < 0 ? mid : mid + 1;
+ }
+ }
+ return foundInd;
+ }
+
+ private int searchFirst(int index)
+ {
+ searchMark.mark = Math.max(index, 1);
+ int i = search(searchMark);
+ for (int j = i - 1; j >= 0; j--)
+ {
+ Mark m = (Mark) marks.get(j);
+ if (m.mark != index)
+ break;
+ i--;
+ }
+ return i;
+ }
+
+ /**
+ * Compares two marks.
+ *
+ * @param m1 the first mark
+ * @param m2 the second mark
+ *
+ * @return negative when m1 < m2, positive when m1 > m2 and 0 when equal
+ */
+ private int compare(Mark m1, Mark m2)
+ {
+ return m1.mark - m2.mark;
+ }
+
+ /**
+ * Collects and frees unused marks.
+ */
+ private void garbageCollect()
+ {
+ int count = marks.size();
+ ArrayList clean = new ArrayList();
+ for (int i = 0; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.get() != null)
+ clean.add(m);
+ }
+ marks = clean;
+ garbageMarks = 0;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java
new file mode 100644
index 000000000..3f4ccf9c2
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/GlyphView.java
@@ -0,0 +1,1333 @@
+/* GlyphView.java -- A view to render styled text
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextHitInfo;
+import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
+
+import javax.swing.SwingConstants;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.Position.Bias;
+
+/**
+ * Renders a run of styled text. This {@link View} subclass paints the
+ * characters of the <code>Element</code> it is responsible for using
+ * the style information from that <code>Element</code>.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class GlyphView extends View implements TabableView, Cloneable
+{
+
+ /**
+ * An abstract base implementation for a glyph painter for
+ * <code>GlyphView</code>.
+ */
+ public abstract static class GlyphPainter
+ {
+ /**
+ * Creates a new <code>GlyphPainer</code>.
+ */
+ public GlyphPainter()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the ascent of the font that is used by this glyph painter.
+ *
+ * @param v the glyph view
+ *
+ * @return the ascent of the font that is used by this glyph painter
+ */
+ public abstract float getAscent(GlyphView v);
+
+ /**
+ * Returns the descent of the font that is used by this glyph painter.
+ *
+ * @param v the glyph view
+ *
+ * @return the descent of the font that is used by this glyph painter
+ */
+ public abstract float getDescent(GlyphView v);
+
+ /**
+ * Returns the full height of the rendered text.
+ *
+ * @return the full height of the rendered text
+ */
+ public abstract float getHeight(GlyphView view);
+
+ /**
+ * Determines the model offset, so that the text between <code>p0</code>
+ * and this offset fits within the span starting at <code>x</code> with
+ * the length of <code>len</code>.
+ *
+ * @param v the glyph view
+ * @param p0 the starting offset in the model
+ * @param x the start location in the view
+ * @param len the length of the span in the view
+ */
+ public abstract int getBoundedPosition(GlyphView v, int p0, float x,
+ float len);
+
+ /**
+ * Paints the glyphs.
+ *
+ * @param view the glyph view to paint
+ * @param g the graphics context to use for painting
+ * @param a the allocation of the glyph view
+ * @param p0 the start position (in the model) from which to paint
+ * @param p1 the end position (in the model) to which to paint
+ */
+ public abstract void paint(GlyphView view, Graphics g, Shape a, int p0,
+ int p1);
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * @param view the glyph view
+ * @param pos the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param b either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public abstract Shape modelToView(GlyphView view, int pos, Position.Bias b,
+ Shape a)
+ throws BadLocationException;
+
+ /**
+ * Maps a visual position into a document location.
+ *
+ * @param v the glyph view
+ * @param x the X coordinate of the visual position
+ * @param y the Y coordinate of the visual position
+ * @param a the allocated region
+ * @param biasRet filled with the bias of the model location on method exit
+ *
+ * @return the model location that represents the specified view location
+ */
+ public abstract int viewToModel(GlyphView v, float x, float y, Shape a,
+ Position.Bias[] biasRet);
+
+ /**
+ * Determine the span of the glyphs from location <code>p0</code> to
+ * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
+ * then TABs are expanded using this <code>TabExpander</code>.
+ * The parameter <code>x</code> is the location at which the view is
+ * located (this is important when using TAB expansion).
+ *
+ * @param view the glyph view
+ * @param p0 the starting location in the document model
+ * @param p1 the end location in the document model
+ * @param te the tab expander to use
+ * @param x the location at which the view is located
+ *
+ * @return the span of the glyphs from location <code>p0</code> to
+ * location <code>p1</code>, possibly using TAB expansion
+ */
+ public abstract float getSpan(GlyphView view, int p0, int p1,
+ TabExpander te, float x);
+
+
+ /**
+ * Returns the model location that should be used to place a caret when
+ * moving the caret through the document.
+ *
+ * @param v the glyph view
+ * @param pos the current model location
+ * @param b the bias for <code>p</code>
+ * @param a the allocated region for the glyph view
+ * @param direction the direction from the current position; Must be one of
+ * {@link SwingConstants#EAST}, {@link SwingConstants#WEST},
+ * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH}
+ * @param biasRet filled with the bias of the resulting location when method
+ * returns
+ *
+ * @return the location within the document that should be used to place the
+ * caret when moving the caret around the document
+ *
+ * @throws BadLocationException if <code>pos</code> is an invalid model
+ * location
+ * @throws IllegalArgumentException if <code>d</code> is invalid
+ */
+ public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b,
+ Shape a, int direction,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+
+ {
+ int result = pos;
+ switch (direction)
+ {
+ case SwingConstants.EAST:
+ result = pos + 1;
+ break;
+ case SwingConstants.WEST:
+ result = pos - 1;
+ break;
+ case SwingConstants.NORTH:
+ case SwingConstants.SOUTH:
+ default:
+ // This should be handled in enclosing view, since the glyph view
+ // does not layout vertically.
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns a painter that can be used to render the specified glyph view.
+ * If this glyph painter is stateful, then it should return a new instance.
+ * However, if this painter is stateless it should return itself. The
+ * default behaviour is to return itself.
+ *
+ * @param v the glyph view for which to create a painter
+ * @param p0 the start offset of the rendered area
+ * @param p1 the end offset of the rendered area
+ *
+ * @return a painter that can be used to render the specified glyph view
+ */
+ public GlyphPainter getPainter(GlyphView v, int p0, int p1)
+ {
+ return this;
+ }
+ }
+
+ /**
+ * A GlyphPainter implementation based on TextLayout. This should give
+ * better performance in Java2D environments.
+ */
+ private static class J2DGlyphPainter
+ extends GlyphPainter
+ {
+
+ /**
+ * The text layout.
+ */
+ TextLayout textLayout;
+
+ /**
+ * Creates a new J2DGlyphPainter.
+ *
+ * @param str the string
+ * @param font the font
+ * @param frc the font render context
+ */
+ J2DGlyphPainter(String str, Font font, FontRenderContext frc)
+ {
+ textLayout = new TextLayout(str, font, frc);
+ }
+
+ /**
+ * Returns null so that GlyphView.checkPainter() creates a new instance.
+ */
+ public GlyphPainter getPainter(GlyphView v, int p0, int p1)
+ {
+ return null;
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public float getAscent(GlyphView v)
+ {
+ return textLayout.getAscent();
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public int getBoundedPosition(GlyphView v, int p0, float x, float len)
+ {
+ int pos;
+ TextHitInfo hit = textLayout.hitTestChar(len, 0);
+ if (hit.getCharIndex() == -1 && ! textLayout.isLeftToRight())
+ pos = v.getEndOffset();
+ else
+ {
+ pos = hit.isLeadingEdge() ? hit.getInsertionIndex()
+ : hit.getInsertionIndex() - 1;
+ pos += v.getStartOffset();
+ }
+ return pos;
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public float getDescent(GlyphView v)
+ {
+ return textLayout.getDescent();
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public float getHeight(GlyphView view)
+ {
+ return textLayout.getAscent() + textLayout.getDescent()
+ + textLayout.getLeading();
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public float getSpan(GlyphView v, int p0, int p1, TabExpander te, float x)
+ {
+ float span;
+ if (p0 == v.getStartOffset() && p1 == v.getEndOffset())
+ span = textLayout.getAdvance();
+ else
+ {
+ int start = v.getStartOffset();
+ int i0 = p0 - start;
+ int i1 = p1 - start;
+ TextHitInfo hit0 = TextHitInfo.afterOffset(i0);
+ TextHitInfo hit1 = TextHitInfo.afterOffset(i1);
+ float x0 = textLayout.getCaretInfo(hit0)[0];
+ float x1 = textLayout.getCaretInfo(hit1)[0];
+ span = Math.abs(x1 - x0);
+ }
+ return span;
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public Shape modelToView(GlyphView v, int pos, Bias b, Shape a)
+ throws BadLocationException
+ {
+ int offs = pos - v.getStartOffset();
+ // Create copy here to protect original shape.
+ Rectangle2D bounds = a.getBounds2D();
+ TextHitInfo hit =
+ b == Position.Bias.Forward ? TextHitInfo.afterOffset(offs)
+ : TextHitInfo.beforeOffset(offs);
+ float[] loc = textLayout.getCaretInfo(hit);
+ bounds.setRect(bounds.getX() + loc[0], bounds.getY(), 1,
+ bounds.getHeight());
+ return bounds;
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public void paint(GlyphView view, Graphics g, Shape a, int p0, int p1)
+ {
+ // Can't paint this with plain graphics.
+ if (g instanceof Graphics2D)
+ {
+ Graphics2D g2d = (Graphics2D) g;
+ Rectangle2D b = a instanceof Rectangle2D ? (Rectangle2D) a
+ : a.getBounds2D();
+ float x = (float) b.getX();
+ float y = (float) b.getY() + textLayout.getAscent()
+ + textLayout.getLeading();
+ // TODO: Try if clipping makes things faster for narrow views.
+ textLayout.draw(g2d, x, y);
+ }
+ }
+
+ /**
+ * Delegates to the text layout.
+ */
+ public int viewToModel(GlyphView v, float x, float y, Shape a,
+ Bias[] biasRet)
+ {
+ Rectangle2D bounds = a instanceof Rectangle2D ? (Rectangle2D) a
+ : a.getBounds2D();
+ TextHitInfo hit = textLayout.hitTestChar(x - (float) bounds.getX(), 0);
+ int pos = hit.getInsertionIndex();
+ biasRet[0] = hit.isLeadingEdge() ? Position.Bias.Forward
+ : Position.Bias.Backward;
+ return pos + v.getStartOffset();
+ }
+
+ }
+
+ /**
+ * The default <code>GlyphPainter</code> used in <code>GlyphView</code>.
+ */
+ static class DefaultGlyphPainter extends GlyphPainter
+ {
+ FontMetrics fontMetrics;
+
+ /**
+ * Returns the full height of the rendered text.
+ *
+ * @return the full height of the rendered text
+ */
+ public float getHeight(GlyphView view)
+ {
+ updateFontMetrics(view);
+ float height = fontMetrics.getHeight();
+ return height;
+ }
+
+ /**
+ * Paints the glyphs.
+ *
+ * @param view the glyph view to paint
+ * @param g the graphics context to use for painting
+ * @param a the allocation of the glyph view
+ * @param p0 the start position (in the model) from which to paint
+ * @param p1 the end position (in the model) to which to paint
+ */
+ public void paint(GlyphView view, Graphics g, Shape a, int p0,
+ int p1)
+ {
+ updateFontMetrics(view);
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ TabExpander tabEx = view.getTabExpander();
+ Segment txt = view.getText(p0, p1);
+
+ // Find out the X location at which we have to paint.
+ int x = r.x;
+ int p = view.getStartOffset();
+ if (p != p0)
+ {
+ int width = Utilities.getTabbedTextWidth(txt, fontMetrics,x, tabEx,
+ p);
+ x += width;
+ }
+ // Find out Y location.
+ int y = r.y + fontMetrics.getHeight() - fontMetrics.getDescent();
+
+ // Render the thing.
+ g.setFont(fontMetrics.getFont());
+ Utilities.drawTabbedText(txt, x, y, g, tabEx, p0);
+
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * @param view the glyph view
+ * @param pos the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param b either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Shape modelToView(GlyphView view, int pos, Position.Bias b,
+ Shape a)
+ throws BadLocationException
+ {
+ updateFontMetrics(view);
+ Element el = view.getElement();
+ Segment txt = view.getText(el.getStartOffset(), pos);
+ Rectangle bounds = a instanceof Rectangle ? (Rectangle) a
+ : a.getBounds();
+ TabExpander expander = view.getTabExpander();
+ int width = Utilities.getTabbedTextWidth(txt, fontMetrics, bounds.x,
+ expander,
+ view.getStartOffset());
+ int height = fontMetrics.getHeight();
+ Rectangle result = new Rectangle(bounds.x + width, bounds.y,
+ 0, height);
+ return result;
+ }
+
+ /**
+ * Determine the span of the glyphs from location <code>p0</code> to
+ * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
+ * then TABs are expanded using this <code>TabExpander</code>.
+ * The parameter <code>x</code> is the location at which the view is
+ * located (this is important when using TAB expansion).
+ *
+ * @param view the glyph view
+ * @param p0 the starting location in the document model
+ * @param p1 the end location in the document model
+ * @param te the tab expander to use
+ * @param x the location at which the view is located
+ *
+ * @return the span of the glyphs from location <code>p0</code> to
+ * location <code>p1</code>, possibly using TAB expansion
+ */
+ public float getSpan(GlyphView view, int p0, int p1,
+ TabExpander te, float x)
+ {
+ updateFontMetrics(view);
+ Segment txt = view.getText(p0, p1);
+ int span = Utilities.getTabbedTextWidth(txt, fontMetrics, (int) x, te,
+ p0);
+ return span;
+ }
+
+ /**
+ * Returns the ascent of the text run that is rendered by this
+ * <code>GlyphPainter</code>.
+ *
+ * @param v the glyph view
+ *
+ * @return the ascent of the text run that is rendered by this
+ * <code>GlyphPainter</code>
+ *
+ * @see FontMetrics#getAscent()
+ */
+ public float getAscent(GlyphView v)
+ {
+ updateFontMetrics(v);
+ return fontMetrics.getAscent();
+ }
+
+ /**
+ * Returns the descent of the text run that is rendered by this
+ * <code>GlyphPainter</code>.
+ *
+ * @param v the glyph view
+ *
+ * @return the descent of the text run that is rendered by this
+ * <code>GlyphPainter</code>
+ *
+ * @see FontMetrics#getDescent()
+ */
+ public float getDescent(GlyphView v)
+ {
+ updateFontMetrics(v);
+ return fontMetrics.getDescent();
+ }
+
+ /**
+ * Determines the model offset, so that the text between <code>p0</code>
+ * and this offset fits within the span starting at <code>x</code> with
+ * the length of <code>len</code>.
+ *
+ * @param v the glyph view
+ * @param p0 the starting offset in the model
+ * @param x the start location in the view
+ * @param len the length of the span in the view
+ */
+ public int getBoundedPosition(GlyphView v, int p0, float x, float len)
+ {
+ updateFontMetrics(v);
+ TabExpander te = v.getTabExpander();
+ Segment txt = v.getText(p0, v.getEndOffset());
+ int pos = Utilities.getTabbedTextOffset(txt, fontMetrics, (int) x,
+ (int) (x + len), te, p0, false);
+ return pos + p0;
+ }
+
+ /**
+ * Maps a visual position into a document location.
+ *
+ * @param v the glyph view
+ * @param x the X coordinate of the visual position
+ * @param y the Y coordinate of the visual position
+ * @param a the allocated region
+ * @param biasRet filled with the bias of the model location on method exit
+ *
+ * @return the model location that represents the specified view location
+ */
+ public int viewToModel(GlyphView v, float x, float y, Shape a,
+ Bias[] biasRet)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ int p0 = v.getStartOffset();
+ int p1 = v.getEndOffset();
+ TabExpander te = v.getTabExpander();
+ Segment s = v.getText(p0, p1);
+ int offset = Utilities.getTabbedTextOffset(s, fontMetrics, r.x, (int) x,
+ te, p0);
+ int ret = p0 + offset;
+ if (ret == p1)
+ ret--;
+ biasRet[0] = Position.Bias.Forward;
+ return ret;
+ }
+
+ private void updateFontMetrics(GlyphView v)
+ {
+ Font font = v.getFont();
+ if (fontMetrics == null || ! font.equals(fontMetrics.getFont()))
+ {
+ Container c = v.getContainer();
+ FontMetrics fm;
+ if (c != null)
+ fm = c.getFontMetrics(font);
+ else
+ fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
+ fontMetrics = fm;
+ }
+ }
+ }
+
+ /**
+ * The GlyphPainer used for painting the glyphs.
+ */
+ GlyphPainter glyphPainter;
+
+ /**
+ * The start offset within the document for this view.
+ */
+ private int offset;
+
+ /**
+ * The end offset within the document for this view.
+ */
+ private int length;
+
+ /**
+ * The x location against which the tab expansion is done.
+ */
+ private float tabX;
+
+ /**
+ * The tab expander that is used in this view.
+ */
+ private TabExpander tabExpander;
+
+ /**
+ * Creates a new <code>GlyphView</code> for the given <code>Element</code>.
+ *
+ * @param element the element that is rendered by this GlyphView
+ */
+ public GlyphView(Element element)
+ {
+ super(element);
+ offset = 0;
+ length = 0;
+ }
+
+ /**
+ * Returns the <code>GlyphPainter</code> that is used by this
+ * <code>GlyphView</code>. If no <code>GlyphPainer</code> has been installed
+ * <code>null</code> is returned.
+ *
+ * @return the glyph painter that is used by this
+ * glyph view or <code>null</code> if no glyph painter has been
+ * installed
+ */
+ public GlyphPainter getGlyphPainter()
+ {
+ return glyphPainter;
+ }
+
+ /**
+ * Sets the {@link GlyphPainter} to be used for this <code>GlyphView</code>.
+ *
+ * @param painter the glyph painter to be used for this glyph view
+ */
+ public void setGlyphPainter(GlyphPainter painter)
+ {
+ glyphPainter = painter;
+ }
+
+ /**
+ * Checks if a <code>GlyphPainer</code> is installed. If this is not the
+ * case, a default painter is installed.
+ */
+ protected void checkPainter()
+ {
+ if (glyphPainter == null)
+ {
+ if ("true".equals(
+ SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")))
+ {
+ glyphPainter = new DefaultGlyphPainter();
+ }
+ else
+ {
+ Segment s = getText(getStartOffset(), getEndOffset());
+ glyphPainter = new J2DGlyphPainter(s.toString(), getFont(),
+ new FontRenderContext(null,
+ false,
+ false));
+ }
+ }
+ }
+
+ /**
+ * Renders the <code>Element</code> that is associated with this
+ * <code>View</code>.
+ *
+ * @param g the <code>Graphics</code> context to render to
+ * @param a the allocated region for the <code>Element</code>
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ checkPainter();
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ Container c = getContainer();
+
+ Color fg = getForeground();
+ JTextComponent tc = null;
+ if (c instanceof JTextComponent)
+ {
+ tc = (JTextComponent) c;
+ if (! tc.isEnabled())
+ fg = tc.getDisabledTextColor();
+ }
+ Color bg = getBackground();
+ if (bg != null)
+ {
+ g.setColor(bg);
+ g.fillRect(r.x, r.y, r.width, r.height);
+ }
+
+
+ // Paint layered highlights if there are any.
+ if (tc != null)
+ {
+ Highlighter h = tc.getHighlighter();
+ if (h instanceof LayeredHighlighter)
+ {
+ LayeredHighlighter lh = (LayeredHighlighter) h;
+ lh.paintLayeredHighlights(g, p0, p1, a, tc, this);
+ }
+ }
+
+ g.setColor(fg);
+ glyphPainter.paint(this, g, a, p0, p1);
+ boolean underline = isUnderline();
+ boolean striked = isStrikeThrough();
+ if (underline || striked)
+ {
+ View parent = getParent();
+ // X coordinate.
+ if (parent != null && parent.getEndOffset() == p1)
+ {
+ // Strip whitespace.
+ Segment s = getText(p0, p1);
+ while (s.count > 0 && Character.isWhitespace(s.array[s.count - 1]))
+ {
+ p1--;
+ s.count--;
+ }
+ }
+ int x0 = r.x;
+ int p = getStartOffset();
+ TabExpander tabEx = getTabExpander();
+ if (p != p0)
+ x0 += (int) glyphPainter.getSpan(this, p, p0, tabEx, x0);
+ int x1 = x0 + (int) glyphPainter.getSpan(this, p0, p1, tabEx, x0);
+ // Y coordinate.
+ int y = r.y + r.height - (int) glyphPainter.getDescent(this);
+ if (underline)
+ {
+ int yTmp = y;
+ yTmp += 1;
+ g.drawLine(x0, yTmp, x1, yTmp);
+ }
+ if (striked)
+ {
+ int yTmp = y;
+ yTmp -= (int) glyphPainter.getAscent(this);
+ g.drawLine(x0, yTmp, x1, yTmp);
+ }
+ }
+ }
+
+
+ /**
+ * Returns the preferred span of the content managed by this
+ * <code>View</code> along the specified <code>axis</code>.
+ *
+ * @param axis the axis
+ *
+ * @return the preferred span of this <code>View</code>.
+ */
+ public float getPreferredSpan(int axis)
+ {
+ float span = 0;
+ checkPainter();
+ GlyphPainter painter = getGlyphPainter();
+ switch (axis)
+ {
+ case X_AXIS:
+ TabExpander tabEx = null;
+ View parent = getParent();
+ if (parent instanceof TabExpander)
+ tabEx = (TabExpander) parent;
+ span = painter.getSpan(this, getStartOffset(), getEndOffset(),
+ tabEx, 0.F);
+ break;
+ case Y_AXIS:
+ span = painter.getHeight(this);
+ if (isSuperscript())
+ span += span / 3;
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal axis");
+ }
+ return span;
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * @param pos the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param b either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Shape modelToView(int pos, Shape a, Position.Bias b)
+ throws BadLocationException
+ {
+ GlyphPainter p = getGlyphPainter();
+ return p.modelToView(this, pos, b, a);
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ checkPainter();
+ GlyphPainter painter = getGlyphPainter();
+ return painter.viewToModel(this, x, y, a, b);
+ }
+
+ /**
+ * Return the {@link TabExpander} to use.
+ *
+ * @return the {@link TabExpander} to use
+ */
+ public TabExpander getTabExpander()
+ {
+ return tabExpander;
+ }
+
+ /**
+ * Returns the preferred span of this view for tab expansion.
+ *
+ * @param x the location of the view
+ * @param te the tab expander to use
+ *
+ * @return the preferred span of this view for tab expansion
+ */
+ public float getTabbedSpan(float x, TabExpander te)
+ {
+ checkPainter();
+ TabExpander old = tabExpander;
+ tabExpander = te;
+ if (tabExpander != old)
+ {
+ // Changing the tab expander will lead to a relayout in the X_AXIS.
+ preferenceChanged(null, true, false);
+ }
+ tabX = x;
+ return getGlyphPainter().getSpan(this, getStartOffset(),
+ getEndOffset(), tabExpander, x);
+ }
+
+ /**
+ * Returns the span of a portion of the view. This is used in TAB expansion
+ * for fragments that don't contain TABs.
+ *
+ * @param p0 the start index
+ * @param p1 the end index
+ *
+ * @return the span of the specified portion of the view
+ */
+ public float getPartialSpan(int p0, int p1)
+ {
+ checkPainter();
+ return glyphPainter.getSpan(this, p0, p1, tabExpander, tabX);
+ }
+
+ /**
+ * Returns the start offset in the document model of the portion
+ * of text that this view is responsible for.
+ *
+ * @return the start offset in the document model of the portion
+ * of text that this view is responsible for
+ */
+ public int getStartOffset()
+ {
+ Element el = getElement();
+ int offs = el.getStartOffset();
+ if (length > 0)
+ offs += offset;
+ return offs;
+ }
+
+ /**
+ * Returns the end offset in the document model of the portion
+ * of text that this view is responsible for.
+ *
+ * @return the end offset in the document model of the portion
+ * of text that this view is responsible for
+ */
+ public int getEndOffset()
+ {
+ Element el = getElement();
+ int offs;
+ if (length > 0)
+ offs = el.getStartOffset() + offset + length;
+ else
+ offs = el.getEndOffset();
+ return offs;
+ }
+
+ private Segment cached = new Segment();
+
+ /**
+ * Returns the text segment that this view is responsible for.
+ *
+ * @param p0 the start index in the document model
+ * @param p1 the end index in the document model
+ *
+ * @return the text segment that this view is responsible for
+ */
+ public Segment getText(int p0, int p1)
+ {
+ try
+ {
+ getDocument().getText(p0, p1 - p0, cached);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError ae;
+ ae = new AssertionError("BadLocationException should not be "
+ + "thrown here. p0 = " + p0 + ", p1 = " + p1);
+ ae.initCause(ex);
+ throw ae;
+ }
+
+ return cached;
+ }
+
+ /**
+ * Returns the font for the text run for which this <code>GlyphView</code>
+ * is responsible.
+ *
+ * @return the font for the text run for which this <code>GlyphView</code>
+ * is responsible
+ */
+ public Font getFont()
+ {
+ Document doc = getDocument();
+ Font font = null;
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument styledDoc = (StyledDocument) doc;
+ font = styledDoc.getFont(getAttributes());
+ }
+ else
+ {
+ Container c = getContainer();
+ if (c != null)
+ font = c.getFont();
+ }
+ return font;
+ }
+
+ /**
+ * Returns the foreground color which should be used to paint the text.
+ * This is fetched from the associated element's text attributes using
+ * {@link StyleConstants#getForeground}.
+ *
+ * @return the foreground color which should be used to paint the text
+ */
+ public Color getForeground()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.getForeground(atts);
+ }
+
+ /**
+ * Returns the background color which should be used to paint the text.
+ * This is fetched from the associated element's text attributes using
+ * {@link StyleConstants#getBackground}.
+ *
+ * @return the background color which should be used to paint the text
+ */
+ public Color getBackground()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ // We cannot use StyleConstants.getBackground() here, because that returns
+ // BLACK as default (when background == null). What we need is the
+ // background setting of the text component instead, which is what we get
+ // when background == null anyway.
+ return (Color) atts.getAttribute(StyleConstants.Background);
+ }
+
+ /**
+ * Determines whether the text should be rendered strike-through or not. This
+ * is determined using the method
+ * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of
+ * this view.
+ *
+ * @return whether the text should be rendered strike-through or not
+ */
+ public boolean isStrikeThrough()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.isStrikeThrough(atts);
+ }
+
+ /**
+ * Determines whether the text should be rendered as subscript or not. This
+ * is determined using the method
+ * {@link StyleConstants#isSubscript(AttributeSet)} on the element of
+ * this view.
+ *
+ * @return whether the text should be rendered as subscript or not
+ */
+ public boolean isSubscript()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.isSubscript(atts);
+ }
+
+ /**
+ * Determines whether the text should be rendered as superscript or not. This
+ * is determined using the method
+ * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of
+ * this view.
+ *
+ * @return whether the text should be rendered as superscript or not
+ */
+ public boolean isSuperscript()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.isSuperscript(atts);
+ }
+
+ /**
+ * Determines whether the text should be rendered as underlined or not. This
+ * is determined using the method
+ * {@link StyleConstants#isUnderline(AttributeSet)} on the element of
+ * this view.
+ *
+ * @return whether the text should be rendered as underlined or not
+ */
+ public boolean isUnderline()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.isUnderline(atts);
+ }
+
+ /**
+ * Creates and returns a shallow clone of this GlyphView. This is used by
+ * the {@link #createFragment} and {@link #breakView} methods.
+ *
+ * @return a shallow clone of this GlyphView
+ */
+ protected final Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ AssertionError err = new AssertionError("CloneNotSupportedException "
+ + "must not be thrown here");
+ err.initCause(ex);
+ throw err;
+ }
+ }
+
+ /**
+ * Tries to break the view near the specified view span <code>len</code>.
+ * The glyph view can only be broken in the X direction. For Y direction it
+ * returns itself.
+ *
+ * @param axis the axis for breaking, may be {@link View#X_AXIS} or
+ * {@link View#Y_AXIS}
+ * @param p0 the model location where the fragment should start
+ * @param pos the view position along the axis where the fragment starts
+ * @param len the desired length of the fragment view
+ *
+ * @return the fragment view, or <code>this</code> if breaking was not
+ * possible
+ */
+ public View breakView(int axis, int p0, float pos, float len)
+ {
+ View brokenView = this;
+ if (axis == X_AXIS)
+ {
+ checkPainter();
+ int end = glyphPainter.getBoundedPosition(this, p0, pos, len);
+ int breakLoc = getBreakLocation(p0, end);
+ if (breakLoc != -1)
+ end = breakLoc;
+ if (p0 != getStartOffset() || end != getEndOffset())
+ {
+ brokenView = createFragment(p0, end);
+ if (brokenView instanceof GlyphView)
+ ((GlyphView) brokenView).tabX = pos;
+ }
+ }
+ return brokenView;
+ }
+
+ /**
+ * Determines how well the specified view location is suitable for inserting
+ * a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then
+ * this method forwards to the superclass, if <code>axis</code> is
+ * <code>View.X_AXIS</code> then this method returns
+ * {@link View#ExcellentBreakWeight} if there is a suitable break location
+ * (usually whitespace) within the specified view span, or
+ * {@link View#GoodBreakWeight} if not.
+ *
+ * @param axis the axis along which the break weight is requested
+ * @param pos the starting view location
+ * @param len the length of the span at which the view should be broken
+ *
+ * @return the break weight
+ */
+ public int getBreakWeight(int axis, float pos, float len)
+ {
+ int weight;
+ if (axis == Y_AXIS)
+ weight = super.getBreakWeight(axis, pos, len);
+ else
+ {
+ checkPainter();
+ int start = getStartOffset();
+ int end = glyphPainter.getBoundedPosition(this, start, pos, len);
+ if (end == 0)
+ weight = BadBreakWeight;
+ else
+ {
+ if (getBreakLocation(start, end) != -1)
+ weight = ExcellentBreakWeight;
+ else
+ weight = GoodBreakWeight;
+ }
+ }
+ return weight;
+ }
+
+ private int getBreakLocation(int start, int end)
+ {
+ int loc = -1;
+ Segment s = getText(start, end);
+ for (char c = s.last(); c != Segment.DONE && loc == -1; c = s.previous())
+ {
+ if (Character.isWhitespace(c))
+ {
+ loc = s.getIndex() - s.getBeginIndex() + 1 + start;
+ }
+ }
+ return loc;
+ }
+
+ /**
+ * Receives notification that some text attributes have changed within the
+ * text fragment that this view is responsible for. This calls
+ * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
+ * both width and height.
+ *
+ * @param e the document event describing the change; not used here
+ * @param a the view allocation on screen; not used here
+ * @param vf the view factory; not used here
+ */
+ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ preferenceChanged(null, true, true);
+ }
+
+ /**
+ * Receives notification that some text has been inserted within the
+ * text fragment that this view is responsible for. This calls
+ * {@link View#preferenceChanged(View, boolean, boolean)} for the
+ * direction in which the glyphs are rendered.
+ *
+ * @param e the document event describing the change; not used here
+ * @param a the view allocation on screen; not used here
+ * @param vf the view factory; not used here
+ */
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ preferenceChanged(null, true, false);
+ }
+
+ /**
+ * Receives notification that some text has been removed within the
+ * text fragment that this view is responsible for. This calls
+ * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
+ * width.
+ *
+ * @param e the document event describing the change; not used here
+ * @param a the view allocation on screen; not used here
+ * @param vf the view factory; not used here
+ */
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ preferenceChanged(null, true, false);
+ }
+
+ /**
+ * Creates a fragment view of this view that starts at <code>p0</code> and
+ * ends at <code>p1</code>.
+ *
+ * @param p0 the start location for the fragment view
+ * @param p1 the end location for the fragment view
+ *
+ * @return the fragment view
+ */
+ public View createFragment(int p0, int p1)
+ {
+ checkPainter();
+ Element el = getElement();
+ GlyphView fragment = (GlyphView) clone();
+ fragment.offset = p0 - el.getStartOffset();
+ fragment.length = p1 - p0;
+ fragment.glyphPainter = glyphPainter.getPainter(fragment, p0, p1);
+ return fragment;
+ }
+
+ /**
+ * Returns the alignment of this view along the specified axis. For the Y
+ * axis this is <code>(height - descent) / height</code> for the used font,
+ * so that it is aligned along the baseline.
+ * For the X axis the superclass is called.
+ */
+ public float getAlignment(int axis)
+ {
+ checkPainter();
+ float align;
+ if (axis == Y_AXIS)
+ {
+ GlyphPainter painter = getGlyphPainter();
+ float height = painter.getHeight(this);
+ float descent = painter.getDescent(this);
+ float ascent = painter.getAscent(this);
+ if (isSuperscript())
+ align = 1.0F;
+ else if (isSubscript())
+ align = height > 0 ? (height - (descent + (ascent / 2))) / height
+ : 0;
+ else
+ align = height > 0 ? (height - descent) / height : 0;
+ }
+ else
+ align = super.getAlignment(axis);
+
+ return align;
+ }
+
+ /**
+ * Returns the model location that should be used to place a caret when
+ * moving the caret through the document.
+ *
+ * @param pos the current model location
+ * @param bias the bias for <code>p</code>
+ * @param a the allocated region for the glyph view
+ * @param direction the direction from the current position; Must be one of
+ * {@link SwingConstants#EAST}, {@link SwingConstants#WEST},
+ * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH}
+ * @param biasRet filled with the bias of the resulting location when method
+ * returns
+ *
+ * @return the location within the document that should be used to place the
+ * caret when moving the caret around the document
+ *
+ * @throws BadLocationException if <code>pos</code> is an invalid model
+ * location
+ * @throws IllegalArgumentException if <code>d</code> is invalid
+ */
+ public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a,
+ int direction, Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ checkPainter();
+ GlyphPainter painter = getGlyphPainter();
+ return painter.getNextVisualPositionFrom(this, pos, bias, a, direction,
+ biasRet);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/Highlighter.java b/libjava/classpath/javax/swing/text/Highlighter.java
new file mode 100644
index 000000000..b4b671ac4
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Highlighter.java
@@ -0,0 +1,78 @@
+/* Highlighter.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Graphics;
+import java.awt.Shape;
+
+
+public interface Highlighter
+{
+ public interface Highlight
+ {
+ int getEndOffset();
+
+ int getStartOffset();
+
+ HighlightPainter getPainter();
+ }
+
+ public interface HighlightPainter
+ {
+ void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c);
+ }
+
+ void install(JTextComponent c);
+
+ void deinstall(JTextComponent c);
+
+ Object addHighlight(int p0, int p1, HighlightPainter p)
+ throws BadLocationException;
+
+ void removeAllHighlights();
+
+ void removeHighlight(Object tag);
+
+ void changeHighlight(Object tag, int p0, int p1)
+ throws BadLocationException;
+
+ Highlight[] getHighlights();
+
+ void paint(Graphics g);
+}
diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java
new file mode 100644
index 000000000..7bb7635b4
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/IconView.java
@@ -0,0 +1,175 @@
+/* IconView.java -- A view to render icons
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.Icon;
+import javax.swing.JTextPane;
+
+/**
+ * A View that can render an icon. This view is created by the
+ * {@link StyledEditorKit}'s view factory for all elements that have name
+ * {@link StyleConstants#IconElementName}. This is usually created by
+ * inserting an icon into <code>JTextPane</code> using
+ * {@link JTextPane#insertIcon(Icon)}
+ *
+ * The icon is determined using the attribute
+ * {@link StyleConstants#IconAttribute}, which's value must be an {@link Icon}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class IconView
+ extends View
+{
+
+ /**
+ * Creates a new <code>IconView</code> for the given <code>Element</code>.
+ *
+ * @param element the element that is rendered by this IconView
+ */
+ public IconView(Element element)
+ {
+ super(element);
+ }
+
+ /**
+ * Renders the <code>Element</code> that is associated with this
+ * <code>View</code>.
+ *
+ * @param g the <code>Graphics</code> context to render to
+ * @param a the allocated region for the <code>Element</code>
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ Icon icon = StyleConstants.getIcon(getElement().getAttributes());
+ Rectangle b = a.getBounds();
+ icon.paintIcon(getContainer(), g, b.x, b.y);
+ }
+
+ /**
+ * Returns the preferred span of the content managed by this
+ * <code>View</code> along the specified <code>axis</code>.
+ *
+ * @param axis the axis
+ *
+ * @return the preferred span of this <code>View</code>.
+ */
+ public float getPreferredSpan(int axis)
+ {
+ Icon icon = StyleConstants.getIcon(getElement().getAttributes());
+ float span;
+ if (axis == X_AXIS)
+ span = icon.getIconWidth();
+ else if (axis == Y_AXIS)
+ span = icon.getIconHeight();
+ else
+ throw new IllegalArgumentException();
+ return span;
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * @param pos the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param b either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Shape modelToView(int pos, Shape a, Position.Bias b)
+ throws BadLocationException
+ {
+ Element el = getElement();
+ Rectangle r = a.getBounds();
+ Icon icon = StyleConstants.getIcon(el.getAttributes());
+ return new Rectangle(r.x, r.y, icon.getIconWidth(), icon.getIconHeight());
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ // The element should only have one character position and it is clear
+ // that this position is the position that best matches the given screen
+ // coordinates, simply because this view has only this one position.
+ Element el = getElement();
+ return el.getStartOffset();
+ }
+
+ /**
+ * Returns the alignment for this view. This will be 1.0 for the Y_AXIS,
+ * and the super behaviour for the X_AXIS.
+ *
+ * @param axis the axis for which to calculate the alignment
+ *
+ * @return the alignment
+ */
+ public float getAlignment(int axis)
+ {
+ float align;
+ if (axis == Y_AXIS)
+ align = 1.0F;
+ else
+ align = super.getAlignment(axis);
+ return align;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/InternationalFormatter.java b/libjava/classpath/javax/swing/text/InternationalFormatter.java
new file mode 100644
index 000000000..8dcd03a3f
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/InternationalFormatter.java
@@ -0,0 +1,356 @@
+/* InternationalFormatter.java --
+Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.text.AttributedCharacterIterator;
+import java.text.Format;
+import java.text.ParseException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.Action;
+import javax.swing.JFormattedTextField;
+
+/**
+ * This extends {@link DefaultFormatter} so that the value to string
+ * conversion is done via a {@link Format} object. This allows
+ * various additional formats to be handled by JFormattedField.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class InternationalFormatter
+ extends DefaultFormatter
+{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 2436068675711756856L;
+
+ /** The format that handles value to string conversion. */
+ Format format;
+
+ /** The minimal permissable value. */
+ Comparable minimum;
+
+ /** The maximal permissable value. */
+ Comparable maximum;
+
+ /**
+ * Creates a new InternationalFormatter with no Format specified.
+ */
+ public InternationalFormatter()
+ {
+ super();
+ minimum = null;
+ maximum = null;
+ format = null;
+ setCommitsOnValidEdit(false);
+ setOverwriteMode(false);
+ }
+
+ /**
+ * Creates a new InternationalFormatter that uses the specified
+ * Format object for value to string conversion.
+ *
+ * @param format the Format object to use for value to string conversion
+ */
+ public InternationalFormatter(Format format)
+ {
+ this();
+ setFormat(format);
+ }
+
+ /**
+ * Sets the Format object that is used to convert values to strings.
+ *
+ * @param format the Format to use for value to string conversion
+ *
+ * @see Format
+ */
+ public void setFormat(Format format)
+ {
+ this.format = format;
+ }
+
+ /**
+ * Returns the currently used Format object that is used to format
+ * the JFormattedField.
+ *
+ * @return the current Format
+ */
+ public Format getFormat()
+ {
+ return format;
+ }
+
+ /**
+ * Sets the minimum value that is allowed by this Formatter. The minimum
+ * value is given as an object that implements the {@link Comparable}
+ * interface.
+ *
+ * If <code>minValue</code> is null, then the Formatter has no restrictions
+ * at the lower end.
+ *
+ * If value class is not yet specified and <code>minValue</code> is not
+ * null, then <code>valueClass</code> is set to the class of the minimum
+ * value.
+ *
+ * @param minValue the minimum permissable value
+ *
+ * @see Comparable
+ */
+ public void setMinimum(Comparable minValue)
+ {
+ minimum = minValue;
+ if (valueClass == null && minValue != null)
+ valueClass = minValue.getClass();
+ }
+
+ /**
+ * Returns the minimal value that is allowed by this Formatter.
+ *
+ * A <code>null</code> value means that there is no restriction.
+ *
+ * @return the minimal value that is allowed by this Formatter or
+ * <code>null</code> if there is no restriction
+ */
+ public Comparable getMinimum()
+ {
+ return minimum;
+ }
+
+ /**
+ * Sets the maximum value that is allowed by this Formatter. The maximum
+ * value is given as an object that implements the {@link Comparable}
+ * interface.
+ *
+ * If <code>maxValue</code> is null, then the Formatter has no restrictions
+ * at the upper end.
+ *
+ * If value class is not yet specified and <code>maxValue</code> is not
+ * null, then <code>valueClass</code> is set to the class of the maximum
+ * value.
+ *
+ * @param maxValue the maximum permissable value
+ *
+ * @see Comparable
+ */
+ public void setMaximum(Comparable maxValue)
+ {
+ maximum = maxValue;
+ if (valueClass == null && maxValue != null)
+ valueClass = maxValue.getClass();
+ }
+
+ /**
+ * Returns the maximal value that is allowed by this Formatter.
+ *
+ * A <code>null</code> value means that there is no restriction.
+ *
+ * @return the maximal value that is allowed by this Formatter or
+ * <code>null</code> if there is no restriction
+ */
+ public Comparable getMaximum()
+ {
+ return maximum;
+ }
+
+ /**
+ * Installs the formatter on the specified {@link JFormattedTextField}.
+ *
+ * This method does the following things:
+ * <ul>
+ * <li>Display the value of #valueToString in the
+ * <code>JFormattedTextField</code></li>
+ * <li>Install the Actions from #getActions on the <code>JTextField</code>
+ * </li>
+ * <li>Install the DocumentFilter returned by #getDocumentFilter</li>
+ * <li>Install the NavigationFilter returned by #getNavigationFilter</li>
+ * </ul>
+ *
+ * This method is typically not overridden by subclasses. Instead override
+ * one of the mentioned methods in order to customize behaviour.
+ *
+ * @param ftf the {@link JFormattedTextField} in which this formatter
+ * is installed
+ */
+ public void install(JFormattedTextField ftf)
+ {
+ super.install(ftf);
+ }
+
+ /**
+ * Converts a value object into a String. This is done by invoking
+ * {@link Format#format(Object)} on the specified <code>Format</code> object.
+ * If no format is set, then {@link DefaultFormatter#valueToString(Object)}
+ * is called as a fallback.
+ *
+ * @param value the value to be converted
+ *
+ * @return the string representation of the value
+ *
+ * @throws ParseException if the value cannot be converted
+ */
+ public String valueToString(Object value)
+ throws ParseException
+ {
+ if (value == null)
+ return "";
+ if (format != null)
+ return format.format(value);
+ else
+ return super.valueToString(value);
+ }
+
+ /**
+ * Converts a String (from the JFormattedTextField input) to a value.
+ * This is achieved by invoking {@link Format#parseObject(String)} on
+ * the specified <code>Format</code> object.
+ *
+ * This implementation differs slightly from {@link DefaultFormatter},
+ * it does:
+ * <ol>
+ * <li>Convert the string to an <code>Object</code> using the
+ * <code>Formatter</code>.</li>
+ * <li>If a <code>valueClass</code> has been set, this object is passed to
+ * {@link DefaultFormatter#stringToValue(String)} so that the value
+ * has the correct type. This may or may not work correctly, depending on
+ * the implementation of toString() in the value class and if the class
+ * implements a constructor that takes one String as argument.</li>
+ * <li>If no {@link ParseException} has been thrown so far, we check if the
+ * value exceeds either <code>minimum</code> or <code>maximum</code> if
+ * one of those has been specified and throw a <code>ParseException</code>
+ * if it does.</li>
+ * <li>Return the value.</li>
+ * </ol>
+ *
+ * If no format has been specified, then
+ * {@link DefaultFormatter#stringToValue(String)} is invoked as fallback.
+ *
+ * @param string the string to convert
+ *
+ * @return the value for the string
+ *
+ * @throws ParseException if the string cannot be converted into
+ * a value object (e.g. invalid input)
+ */
+ public Object stringToValue(String string)
+ throws ParseException
+ {
+ if (format != null)
+ {
+ Object o = format.parseObject(string);
+
+ // If a value class has been set, call super in order to get
+ // the class right. That is what the JDK API docs suggest, so we do
+ // it that way.
+ if (valueClass != null)
+ o = super.stringToValue(o.toString());
+
+ // Check for minimum and maximum bounds
+ if (minimum != null && minimum.compareTo(o) > 0)
+ throw new ParseException("The value may not be less than the"
+ + " specified minimum", 0);
+ if (maximum != null && maximum.compareTo(o) < 0)
+ throw new ParseException("The value may not be greater than the"
+ + " specified maximum", 0);
+ return o;
+ }
+ else
+ return super.stringToValue(string);
+ }
+
+ /**
+ * Returns the {@link Format.Field} constants that are associated with
+ * the specified position in the text.
+ *
+ * If <code>offset</code> is not a valid location in the input field,
+ * an empty array of fields is returned.
+ *
+ * @param offset the position in the text from which we want to fetch
+ * the fields constants
+ *
+ * @return the field values associated with the specified position in
+ * the text
+ */
+ public Format.Field[] getFields(int offset)
+ {
+ // TODO: don't know if this is correct
+ AttributedCharacterIterator aci = format.formatToCharacterIterator
+ (getFormattedTextField().getValue());
+ aci.setIndex(offset);
+ Map atts = aci.getAttributes();
+ Set keys = atts.keySet();
+ Format.Field[] fields = new Format.Field[keys.size()];
+ int index = 0;
+ for (Iterator i = keys.iterator(); i.hasNext(); index++)
+ fields[index] = (Format.Field) i.next();
+ return fields;
+ }
+
+ /**
+ * This creates and returns a clone of this Formatter.
+ *
+ * @return a clone of this formatter
+ *
+ * @throws CloneNotSupportedException not thrown here, since cloning is
+ * supported
+ */
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ // TODO: it has to be considered, if we should return a deep or shallow
+ // clone here. for now we return a shallow clone
+ Object clone = super.clone();
+ return clone;
+ }
+
+ /**
+ * Returns the Actions that are supported by this Formatter.
+ *
+ * @specnote the JDK API docs say here: <cite>If
+ * <code>getSupportsIncrement</code> returns true, this returns two
+ * Actions suitable for incrementing/decrementing the value.</cite>
+ * The questsion is, which method <code>getSupportsIncrement</code>?
+ * There is no such method in the whole API! So we just call
+ * super.getActions here.
+ */
+ protected Action[] getActions()
+ {
+ return super.getActions();
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java
new file mode 100644
index 000000000..a118cf86d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/JTextComponent.java
@@ -0,0 +1,2059 @@
+/* JTextComponent.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.awt.AWTEvent;
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputMethodListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.text.BreakIterator;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleEditableText;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JViewport;
+import javax.swing.KeyStroke;
+import javax.swing.Scrollable;
+import javax.swing.SwingConstants;
+import javax.swing.TransferHandler;
+import javax.swing.UIManager;
+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;
+
+public abstract class JTextComponent extends JComponent
+ implements Scrollable, Accessible
+{
+ /**
+ * AccessibleJTextComponent implements accessibility hooks for
+ * JTextComponent. It allows an accessibility driver to read and
+ * manipulate the text component's contents as well as update UI
+ * elements such as the caret.
+ */
+ public class AccessibleJTextComponent extends AccessibleJComponent implements
+ AccessibleText, CaretListener, DocumentListener, AccessibleAction,
+ AccessibleEditableText
+ {
+ private static final long serialVersionUID = 7664188944091413696L;
+
+ /**
+ * The caret's offset.
+ */
+ private int caretDot;
+
+ /**
+ * Construct an AccessibleJTextComponent.
+ */
+ public AccessibleJTextComponent()
+ {
+ super();
+ JTextComponent.this.addCaretListener(this);
+ caretDot = getCaretPosition();
+ }
+
+ /**
+ * Retrieve the current caret position. The index of the first
+ * caret position is 0.
+ *
+ * @return caret position
+ */
+ public int getCaretPosition()
+ {
+ return JTextComponent.this.getCaretPosition();
+ }
+
+ /**
+ * Retrieve the current text selection. If no text is selected
+ * this method returns null.
+ *
+ * @return the currently selected text or null
+ */
+ public String getSelectedText()
+ {
+ return JTextComponent.this.getSelectedText();
+ }
+
+ /**
+ * Retrieve the index of the first character in the current text
+ * selection. If there is no text in the text component, this
+ * method returns 0. If there is text in the text component, but
+ * there is no selection, this method returns the current caret
+ * position.
+ *
+ * @return the index of the first character in the selection, the
+ * current caret position or 0
+ */
+ public int getSelectionStart()
+ {
+ if (getSelectedText() == null
+ || (JTextComponent.this.getText().equals("")))
+ return 0;
+ return JTextComponent.this.getSelectionStart();
+ }
+
+ /**
+ * Retrieve the index of the last character in the current text
+ * selection. If there is no text in the text component, this
+ * method returns 0. If there is text in the text component, but
+ * there is no selection, this method returns the current caret
+ * position.
+ *
+ * @return the index of the last character in the selection, the
+ * current caret position or 0
+ */
+ public int getSelectionEnd()
+ {
+ return JTextComponent.this.getSelectionEnd();
+ }
+
+ /**
+ * Handle a change in the caret position and fire any applicable
+ * property change events.
+ *
+ * @param e - the caret update event
+ */
+ public void caretUpdate(CaretEvent e)
+ {
+ int dot = e.getDot();
+ int mark = e.getMark();
+ if (caretDot != dot)
+ {
+ firePropertyChange(ACCESSIBLE_CARET_PROPERTY, new Integer(caretDot),
+ new Integer(dot));
+ caretDot = dot;
+ }
+ if (mark != dot)
+ {
+ firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
+ getSelectedText());
+ }
+ }
+
+ /**
+ * Retreive the accessible state set of this component.
+ *
+ * @return the accessible state set of this component
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ if (isEditable())
+ state.add(AccessibleState.EDITABLE);
+ return state;
+ }
+
+ /**
+ * Retrieve the accessible role of this component.
+ *
+ * @return the accessible role of this component
+ *
+ * @see AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TEXT;
+ }
+
+ /**
+ * Retrieve an AccessibleEditableText object that controls this
+ * text component.
+ *
+ * @return this
+ */
+ public AccessibleEditableText getAccessibleEditableText()
+ {
+ return this;
+ }
+
+ /**
+ * Retrieve an AccessibleText object that controls this text
+ * component.
+ *
+ * @return this
+ *
+ * @see AccessibleText
+ */
+ public AccessibleText getAccessibleText()
+ {
+ return this;
+ }
+
+ /**
+ * Handle a text insertion event and fire an
+ * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
+ * event.
+ *
+ * @param e - the insertion event
+ */
+ public void insertUpdate(DocumentEvent e)
+ {
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
+ }
+
+ /**
+ * Handle a text removal event and fire an
+ * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
+ * event.
+ *
+ * @param e - the removal event
+ */
+ public void removeUpdate(DocumentEvent e)
+ {
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
+ }
+
+ /**
+ * Handle a text change event and fire an
+ * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
+ * event.
+ *
+ * @param e - text change event
+ */
+ public void changedUpdate(DocumentEvent e)
+ {
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+ new Integer(e.getOffset()));
+ }
+
+ /**
+ * Get the index of the character at the given point, in component
+ * pixel co-ordinates. If the point argument is invalid this
+ * method returns -1.
+ *
+ * @param p - a point in component pixel co-ordinates
+ *
+ * @return a character index, or -1
+ */
+ public int getIndexAtPoint(Point p)
+ {
+ return viewToModel(p);
+ }
+
+ /**
+ * Calculate the bounding box of the character at the given index.
+ * The returned x and y co-ordinates are relative to this text
+ * component's top-left corner. If the index is invalid this
+ * method returns null.
+ *
+ * @param index - the character index
+ *
+ * @return a character's bounding box, or null
+ */
+ public Rectangle getCharacterBounds(int index)
+ {
+ // This is basically the same as BasicTextUI.modelToView().
+
+ Rectangle bounds = null;
+ if (index >= 0 && index < doc.getLength() - 1)
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ TextUI ui = getUI();
+ if (ui != null)
+ {
+ // Get editor rectangle.
+ Rectangle rect = new Rectangle();
+ Insets insets = getInsets();
+ rect.x = insets.left;
+ rect.y = insets.top;
+ rect.width = getWidth() - insets.left - insets.right;
+ rect.height = getHeight() - insets.top - insets.bottom;
+ View rootView = ui.getRootView(JTextComponent.this);
+ if (rootView != null)
+ {
+ rootView.setSize(rect.width, rect.height);
+ Shape s = rootView.modelToView(index,
+ Position.Bias.Forward,
+ index + 1,
+ Position.Bias.Backward,
+ rect);
+ if (s != null)
+ bounds = s.getBounds();
+ }
+ }
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore (return null).
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ }
+ return bounds;
+ }
+
+ /**
+ * Return the length of the text in this text component.
+ *
+ * @return a character length
+ */
+ public int getCharCount()
+ {
+ return JTextComponent.this.getText().length();
+ }
+
+ /**
+ * Gets the character attributes of the character at index. If
+ * the index is out of bounds, null is returned.
+ *
+ * @param index - index of the character
+ *
+ * @return the character's attributes
+ */
+ public AttributeSet getCharacterAttribute(int index)
+ {
+ AttributeSet atts;
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Element el = doc.getDefaultRootElement();
+ while (! el.isLeaf())
+ {
+ int i = el.getElementIndex(index);
+ el = el.getElement(i);
+ }
+ atts = el.getAttributes();
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return atts;
+ }
+
+ /**
+ * Gets the text located at index. null is returned if the index
+ * or part is invalid.
+ *
+ * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
+ * @param index - index of the part
+ *
+ * @return the part of text at that index, or null
+ */
+ public String getAtIndex(int part, int index)
+ {
+ return getAtIndexImpl(part, index, 0);
+ }
+
+ /**
+ * Gets the text located after index. null is returned if the index
+ * or part is invalid.
+ *
+ * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
+ * @param index - index after the part
+ *
+ * @return the part of text after that index, or null
+ */
+ public String getAfterIndex(int part, int index)
+ {
+ return getAtIndexImpl(part, index, 1);
+ }
+
+ /**
+ * Gets the text located before index. null is returned if the index
+ * or part is invalid.
+ *
+ * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE}
+ * @param index - index before the part
+ *
+ * @return the part of text before that index, or null
+ */
+ public String getBeforeIndex(int part, int index)
+ {
+ return getAtIndexImpl(part, index, -1);
+ }
+
+ /**
+ * Implements getAtIndex(), getBeforeIndex() and getAfterIndex().
+ *
+ * @param part the part to return, either CHARACTER, WORD or SENTENCE
+ * @param index the index
+ * @param dir the direction, -1 for backwards, 0 for here, +1 for forwards
+ *
+ * @return the resulting string
+ */
+ private String getAtIndexImpl(int part, int index, int dir)
+ {
+ String ret = null;
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ BreakIterator iter = null;
+ switch (part)
+ {
+ case CHARACTER:
+ iter = BreakIterator.getCharacterInstance(getLocale());
+ break;
+ case WORD:
+ iter = BreakIterator.getWordInstance(getLocale());
+ break;
+ case SENTENCE:
+ iter = BreakIterator.getSentenceInstance(getLocale());
+ break;
+ default:
+ break;
+ }
+ String text = doc.getText(0, doc.getLength() - 1);
+ iter.setText(text);
+ int start = index;
+ int end = index;
+ switch (dir)
+ {
+ case 0:
+ if (iter.isBoundary(index))
+ {
+ start = index;
+ end = iter.following(index);
+ }
+ else
+ {
+ start = iter.preceding(index);
+ end = iter.next();
+ }
+ break;
+ case 1:
+ start = iter.following(index);
+ end = iter.next();
+ break;
+ case -1:
+ end = iter.preceding(index);
+ start = iter.previous();
+ break;
+ default:
+ assert false;
+ }
+ ret = text.substring(start, end);
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore (return null).
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the number of actions for this object. The zero-th
+ * object represents the default action.
+ *
+ * @return the number of actions (0-based).
+ */
+ public int getAccessibleActionCount()
+ {
+ return getActions().length;
+ }
+
+ /**
+ * Returns the description of the i-th action. Null is returned if
+ * i is out of bounds.
+ *
+ * @param i - the action to get the description for
+ *
+ * @return description of the i-th action
+ */
+ public String getAccessibleActionDescription(int i)
+ {
+ String desc = null;
+ Action[] actions = getActions();
+ if (i >= 0 && i < actions.length)
+ desc = (String) actions[i].getValue(Action.NAME);
+ return desc;
+ }
+
+ /**
+ * Performs the i-th action. Nothing happens if i is
+ * out of bounds.
+ *
+ * @param i - the action to perform
+ *
+ * @return true if the action was performed successfully
+ */
+ public boolean doAccessibleAction(int i)
+ {
+ boolean ret = false;
+ Action[] actions = getActions();
+ if (i >= 0 && i < actions.length)
+ {
+ ActionEvent ev = new ActionEvent(JTextComponent.this,
+ ActionEvent.ACTION_PERFORMED, null);
+ actions[i].actionPerformed(ev);
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Sets the text contents.
+ *
+ * @param s - the new text contents.
+ */
+ public void setTextContents(String s)
+ {
+ setText(s);
+ }
+
+ /**
+ * Inserts the text at the given index.
+ *
+ * @param index - the index to insert the new text at.
+ * @param s - the new text
+ */
+ public void insertTextAtIndex(int index, String s)
+ {
+ try
+ {
+ doc.insertString(index, s, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // What should we do with this?
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Gets the text between two indexes.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ */
+ public String getTextRange(int start, int end)
+ {
+ try
+ {
+ return JTextComponent.this.getText(start, end - start);
+ }
+ catch (BadLocationException ble)
+ {
+ return "";
+ }
+ }
+
+ /**
+ * Deletes the text between two indexes.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ */
+ public void delete(int start, int end)
+ {
+ replaceText(start, end, "");
+ }
+
+ /**
+ * Cuts the text between two indexes. The text is put
+ * into the system clipboard.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ */
+ public void cut(int start, int end)
+ {
+ JTextComponent.this.select(start, end);
+ JTextComponent.this.cut();
+ }
+
+ /**
+ * Pastes the text from the system clipboard to the given index.
+ *
+ * @param start - the starting index
+ */
+ public void paste(int start)
+ {
+ JTextComponent.this.setCaretPosition(start);
+ JTextComponent.this.paste();
+ }
+
+ /**
+ * Replaces the text between two indexes with the given text.
+ *
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ * @param s - the text to paste
+ */
+ public void replaceText(int start, int end, String s)
+ {
+ JTextComponent.this.select(start, end);
+ JTextComponent.this.replaceSelection(s);
+ }
+
+ /**
+ * Selects the text between two indexes.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ */
+ public void selectText(int start, int end)
+ {
+ JTextComponent.this.select(start, end);
+ }
+
+ /**
+ * Sets the attributes of all the text between two indexes.
+ *
+ * @param start - the starting index (inclusive)
+ * @param end - the ending index (exclusive)
+ * @param s - the new attribute set for the text in the range
+ */
+ public void setAttributes(int start, int end, AttributeSet s)
+ {
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument sdoc = (StyledDocument) doc;
+ sdoc.setCharacterAttributes(start, end - start, s, true);
+ }
+ }
+ }
+
+ public static class KeyBinding
+ {
+ public KeyStroke key;
+ public String actionName;
+
+ /**
+ * Creates a new <code>KeyBinding</code> instance.
+ *
+ * @param key a <code>KeyStroke</code> value
+ * @param actionName a <code>String</code> value
+ */
+ public KeyBinding(KeyStroke key, String actionName)
+ {
+ this.key = key;
+ this.actionName = actionName;
+ }
+ }
+
+ /**
+ * 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>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();
+ int skl = 0;
+ int mkl = 0;
+ if (superKeys != null)
+ skl = superKeys.length;
+ if (mapKeys != null)
+ mkl = mapKeys.length;
+ KeyStroke[] bothKeys = new KeyStroke[skl + mkl];
+ for (int i = 0; i < skl; ++i)
+ bothKeys[i] = superKeys[i];
+ for (int i = 0; i < mkl; ++i)
+ bothKeys[i + skl] = 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;
+ }
+ }
+
+ class DefaultTransferHandler extends TransferHandler
+ {
+ public boolean canImport(JComponent component, DataFlavor[] flavors)
+ {
+ JTextComponent textComponent = (JTextComponent) component;
+
+ if (! (textComponent.isEnabled()
+ && textComponent.isEditable()
+ && flavors != null))
+ return false;
+
+ for (int i = 0; i < flavors.length; ++i)
+ if (flavors[i].equals(DataFlavor.stringFlavor))
+ return true;
+
+ return false;
+ }
+
+ public void exportToClipboard(JComponent component, Clipboard clipboard,
+ int action)
+ {
+ JTextComponent textComponent = (JTextComponent) component;
+ int start = textComponent.getSelectionStart();
+ int end = textComponent.getSelectionEnd();
+
+ if (start == end)
+ return;
+
+ try
+ {
+ // Copy text to clipboard.
+ String data = textComponent.getDocument().getText(start, end);
+ StringSelection selection = new StringSelection(data);
+ clipboard.setContents(selection, null);
+
+ // Delete selected text on cut action.
+ if (action == MOVE)
+ doc.remove(start, end - start);
+ }
+ catch (BadLocationException e)
+ {
+ // Ignore this and do nothing.
+ }
+ }
+
+ public int getSourceActions()
+ {
+ return NONE;
+ }
+
+ public boolean importData(JComponent component, Transferable transferable)
+ {
+ DataFlavor flavor = null;
+ DataFlavor[] flavors = transferable.getTransferDataFlavors();
+
+ if (flavors == null)
+ return false;
+
+ for (int i = 0; i < flavors.length; ++i)
+ if (flavors[i].equals(DataFlavor.stringFlavor))
+ flavor = flavors[i];
+
+ if (flavor == null)
+ return false;
+
+ try
+ {
+ JTextComponent textComponent = (JTextComponent) component;
+ String data = (String) transferable.getTransferData(flavor);
+ textComponent.replaceSelection(data);
+ return true;
+ }
+ catch (IOException e)
+ {
+ // Ignored.
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ // Ignored.
+ }
+
+ return false;
+ }
+ }
+
+ private static final long serialVersionUID = -8796518220218978795L;
+
+ public static final String DEFAULT_KEYMAP = "default";
+ public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
+
+ private static DefaultTransferHandler defaultTransferHandler;
+ private static Hashtable keymaps = new Hashtable();
+ private Keymap keymap;
+ private char focusAccelerator = '\0';
+ private NavigationFilter navigationFilter;
+
+ /**
+ * 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
+ */
+ public 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();
+ }
+
+ // These are package-private to avoid an accessor method.
+ Document doc;
+ Caret caret;
+ boolean editable;
+
+ private Highlighter highlighter;
+ private Color caretColor;
+ private Color disabledTextColor;
+ private Color selectedTextColor;
+ private Color selectionColor;
+ private Insets margin;
+ private boolean dragEnabled;
+
+ /**
+ * Creates a new <code>JTextComponent</code> instance.
+ */
+ public JTextComponent()
+ {
+ Keymap defkeymap = getKeymap(DEFAULT_KEYMAP);
+ if (defkeymap == null)
+ {
+ defkeymap = addKeymap(DEFAULT_KEYMAP, null);
+ defkeymap.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
+ }
+
+ setFocusable(true);
+ setEditable(true);
+ enableEvents(AWTEvent.KEY_EVENT_MASK);
+ setOpaque(true);
+ updateUI();
+ }
+
+ public void setDocument(Document newDoc)
+ {
+ Document oldDoc = doc;
+ try
+ {
+ if (oldDoc instanceof AbstractDocument)
+ ((AbstractDocument) oldDoc).readLock();
+
+ doc = newDoc;
+ firePropertyChange("document", oldDoc, newDoc);
+ }
+ finally
+ {
+ if (oldDoc instanceof AbstractDocument)
+ ((AbstractDocument) oldDoc).readUnlock();
+ }
+ revalidate();
+ repaint();
+ }
+
+ public Document getDocument()
+ {
+ return doc;
+ }
+
+ /**
+ * Get the <code>AccessibleContext</code> of this object.
+ *
+ * @return an <code>AccessibleContext</code> object
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return new AccessibleJTextComponent();
+ }
+
+ public void setMargin(Insets m)
+ {
+ margin = m;
+ }
+
+ public Insets getMargin()
+ {
+ return margin;
+ }
+
+ public void setText(String text)
+ {
+ try
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).replace(0, doc.getLength(), text, null);
+ else
+ {
+ doc.remove(0, doc.getLength());
+ doc.insertString(0, text, null);
+ }
+ }
+ catch (BadLocationException e)
+ {
+ // This can never happen.
+ throw (InternalError) new InternalError().initCause(e);
+ }
+ }
+
+ /**
+ * Retrieves the current text in this text document.
+ *
+ * @return the text
+ *
+ * @exception NullPointerException if the underlaying document is null
+ */
+ public String getText()
+ {
+ if (doc == null)
+ return null;
+
+ try
+ {
+ return doc.getText(0, doc.getLength());
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ return "";
+ }
+ }
+
+ /**
+ * Retrieves a part of the current text in this document.
+ *
+ * @param offset the postion of the first character
+ * @param length the length of the text to retrieve
+ *
+ * @return the text
+ *
+ * @exception BadLocationException if arguments do not hold pre-conditions
+ */
+ public String getText(int offset, int length)
+ throws BadLocationException
+ {
+ return getDocument().getText(offset, length);
+ }
+
+ /**
+ * Retrieves the currently selected text in this text document.
+ *
+ * @return the selected text
+ *
+ * @exception NullPointerException if the underlaying document is null
+ */
+ public String getSelectedText()
+ {
+ int start = getSelectionStart();
+ int offset = getSelectionEnd() - start;
+
+ if (offset <= 0)
+ return null;
+
+ try
+ {
+ return doc.getText(start, offset);
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ return null;
+ }
+ }
+
+ /**
+ * Returns a string that specifies the name of the Look and Feel class
+ * that renders this component.
+ *
+ * @return the string "TextComponentUI"
+ */
+ public String getUIClassID()
+ {
+ return "TextComponentUI";
+ }
+
+ /**
+ * Returns a string representation of this JTextComponent.
+ */
+ protected String paramString()
+ {
+ // TODO: Do something useful here.
+ return super.paramString();
+ }
+
+ /**
+ * This method returns the label's UI delegate.
+ *
+ * @return The label's UI delegate.
+ */
+ public TextUI getUI()
+ {
+ return (TextUI) ui;
+ }
+
+ /**
+ * This method sets the label's UI delegate.
+ *
+ * @param newUI The label's UI delegate.
+ */
+ public void setUI(TextUI newUI)
+ {
+ super.setUI(newUI);
+ }
+
+ /**
+ * This method resets the label's UI delegate to the default UI for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((TextUI) UIManager.getUI(this));
+ }
+
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ return getPreferredSize();
+ }
+
+ public int getScrollableUnitIncrement(Rectangle visible, int orientation,
+ int direction)
+ {
+ // We return 1/10 of the visible area as documented in Sun's API docs.
+ if (orientation == SwingConstants.HORIZONTAL)
+ return visible.width / 10;
+ else if (orientation == SwingConstants.VERTICAL)
+ return visible.height / 10;
+ else
+ throw new IllegalArgumentException("orientation must be either "
+ + "javax.swing.SwingConstants.VERTICAL "
+ + "or "
+ + "javax.swing.SwingConstants.HORIZONTAL"
+ );
+ }
+
+ public int getScrollableBlockIncrement(Rectangle visible, int orientation,
+ int direction)
+ {
+ // We return the whole visible area as documented in Sun's API docs.
+ if (orientation == SwingConstants.HORIZONTAL)
+ return visible.width;
+ else if (orientation == SwingConstants.VERTICAL)
+ return visible.height;
+ else
+ throw new IllegalArgumentException("orientation must be either "
+ + "javax.swing.SwingConstants.VERTICAL "
+ + "or "
+ + "javax.swing.SwingConstants.HORIZONTAL"
+ );
+ }
+
+ /**
+ * Checks whether this text component it editable.
+ *
+ * @return true if editable, false otherwise
+ */
+ public boolean isEditable()
+ {
+ return editable;
+ }
+
+ /**
+ * Enables/disabled this text component's editability.
+ *
+ * @param newValue true to make it editable, false otherwise.
+ */
+ public void setEditable(boolean newValue)
+ {
+ if (editable == newValue)
+ return;
+
+ boolean oldValue = editable;
+ editable = newValue;
+ firePropertyChange("editable", oldValue, newValue);
+ }
+
+ /**
+ * The <code>Caret</code> object used in this text component.
+ *
+ * @return the caret object
+ */
+ public Caret getCaret()
+ {
+ return caret;
+ }
+
+ /**
+ * Sets a new <code>Caret</code> for this text component.
+ *
+ * @param newCaret the new <code>Caret</code> to set
+ */
+ public void setCaret(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()
+ {
+ return caretColor;
+ }
+
+ public void setCaretColor(Color newColor)
+ {
+ Color oldCaretColor = caretColor;
+ caretColor = newColor;
+ firePropertyChange("caretColor", oldCaretColor, newColor);
+ }
+
+ public Color getDisabledTextColor()
+ {
+ return disabledTextColor;
+ }
+
+ public void setDisabledTextColor(Color newColor)
+ {
+ Color oldColor = disabledTextColor;
+ disabledTextColor = newColor;
+ firePropertyChange("disabledTextColor", oldColor, newColor);
+ }
+
+ public Color getSelectedTextColor()
+ {
+ return selectedTextColor;
+ }
+
+ public void setSelectedTextColor(Color newColor)
+ {
+ Color oldColor = selectedTextColor;
+ selectedTextColor = newColor;
+ firePropertyChange("selectedTextColor", oldColor, newColor);
+ }
+
+ public Color getSelectionColor()
+ {
+ return selectionColor;
+ }
+
+ public void setSelectionColor(Color newColor)
+ {
+ Color oldColor = selectionColor;
+ selectionColor = newColor;
+ firePropertyChange("selectionColor", oldColor, newColor);
+ }
+
+ /**
+ * Retrisves the current caret position.
+ *
+ * @return the current position
+ */
+ public int getCaretPosition()
+ {
+ return caret.getDot();
+ }
+
+ /**
+ * Sets the caret to a new position.
+ *
+ * @param position the new position
+ */
+ public void setCaretPosition(int position)
+ {
+ if (doc == null)
+ return;
+
+ if (position < 0 || position > doc.getLength())
+ throw new IllegalArgumentException();
+
+ caret.setDot(position);
+ }
+
+ /**
+ * Moves the caret to a given position. This selects the text between
+ * the old and the new position of the caret.
+ */
+ public void moveCaretPosition(int position)
+ {
+ if (doc == null)
+ return;
+
+ if (position < 0 || position > doc.getLength())
+ throw new IllegalArgumentException();
+
+ caret.moveDot(position);
+ }
+
+ public Highlighter getHighlighter()
+ {
+ return highlighter;
+ }
+
+ public void setHighlighter(Highlighter newHighlighter)
+ {
+ if (highlighter != null)
+ highlighter.deinstall(this);
+
+ Highlighter oldHighlighter = highlighter;
+ highlighter = newHighlighter;
+
+ if (highlighter != null)
+ highlighter.install(this);
+
+ firePropertyChange("highlighter", oldHighlighter, newHighlighter);
+ }
+
+ /**
+ * Returns the start postion of the currently selected text.
+ *
+ * @return the start postion
+ */
+ public int getSelectionStart()
+ {
+ return Math.min(caret.getDot(), caret.getMark());
+ }
+
+ /**
+ * Selects the text from the given postion to the selection end position.
+ *
+ * @param start the start positon of the selected text.
+ */
+ public void setSelectionStart(int start)
+ {
+ select(start, getSelectionEnd());
+ }
+
+ /**
+ * Returns the end postion of the currently selected text.
+ *
+ * @return the end postion
+ */
+ public int getSelectionEnd()
+ {
+ return Math.max(caret.getDot(), caret.getMark());
+ }
+
+ /**
+ * Selects the text from the selection start postion to the given position.
+ *
+ * @param end the end positon of the selected text.
+ */
+ public void setSelectionEnd(int end)
+ {
+ select(getSelectionStart(), end);
+ }
+
+ /**
+ * Selects a part of the content of the text component.
+ *
+ * @param start the start position of the selected text
+ * @param end the end position of the selected text
+ */
+ public void select(int start, int end)
+ {
+ int length = doc.getLength();
+
+ start = Math.max(start, 0);
+ start = Math.min(start, length);
+
+ end = Math.max(end, start);
+ end = Math.min(end, length);
+
+ setCaretPosition(start);
+ moveCaretPosition(end);
+ }
+
+ /**
+ * Selects the whole content of the text component.
+ */
+ public void selectAll()
+ {
+ select(0, doc.getLength());
+ }
+
+ public synchronized void replaceSelection(String content)
+ {
+ int dot = caret.getDot();
+ int mark = caret.getMark();
+
+ // If content is empty delete selection.
+ if (content == null)
+ {
+ caret.setDot(dot);
+ return;
+ }
+
+ try
+ {
+ int start = getSelectionStart();
+ int end = getSelectionEnd();
+
+ // Remove selected text.
+ if (dot != mark)
+ doc.remove(start, end - start);
+
+ // Insert new text.
+ doc.insertString(start, content, null);
+
+ // Set dot to new position,
+ dot = start + content.length();
+ setCaretPosition(dot);
+
+ // and update it's magic position.
+ caret.setMagicCaretPosition(modelToView(dot).getLocation());
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ }
+ }
+
+ public boolean getScrollableTracksViewportHeight()
+ {
+ if (getParent() instanceof JViewport)
+ return getParent().getHeight() > getPreferredSize().height;
+
+ return false;
+ }
+
+ public boolean getScrollableTracksViewportWidth()
+ {
+ boolean res = false;
+ Container c = getParent();
+ if (c instanceof JViewport)
+ res = ((JViewport) c).getExtentSize().width > getPreferredSize().width;
+
+ return res;
+ }
+
+ /**
+ * Adds a <code>CaretListener</code> object to this text component.
+ *
+ * @param listener the listener to add
+ */
+ public void addCaretListener(CaretListener listener)
+ {
+ listenerList.add(CaretListener.class, listener);
+ }
+
+ /**
+ * Removed a <code>CaretListener</code> object from this text component.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeCaretListener(CaretListener listener)
+ {
+ listenerList.remove(CaretListener.class, listener);
+ }
+
+ /**
+ * Returns all added <code>CaretListener</code> objects.
+ *
+ * @return an array of listeners
+ */
+ public CaretListener[] getCaretListeners()
+ {
+ return (CaretListener[]) getListeners(CaretListener.class);
+ }
+
+ /**
+ * Notifies all registered <code>CaretListener</code> objects that the caret
+ * was updated.
+ *
+ * @param event the event to send
+ */
+ protected void fireCaretUpdate(CaretEvent event)
+ {
+ CaretListener[] listeners = getCaretListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].caretUpdate(event);
+ }
+
+ /**
+ * Adds an <code>InputListener</code> object to this text component.
+ *
+ * @param listener the listener to add
+ */
+ public void addInputMethodListener(InputMethodListener listener)
+ {
+ listenerList.add(InputMethodListener.class, listener);
+ }
+
+ /**
+ * Removes an <code>InputListener</code> object from this text component.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeInputMethodListener(InputMethodListener listener)
+ {
+ listenerList.remove(InputMethodListener.class, listener);
+ }
+
+ /**
+ * Returns all added <code>InputMethodListener</code> objects.
+ *
+ * @return an array of listeners
+ */
+ public InputMethodListener[] getInputMethodListeners()
+ {
+ return (InputMethodListener[]) getListeners(InputMethodListener.class);
+ }
+
+ public Rectangle modelToView(int position) throws BadLocationException
+ {
+ return getUI().modelToView(this, position);
+ }
+
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ public void setDragEnabled(boolean enabled)
+ {
+ dragEnabled = enabled;
+ }
+
+ public int viewToModel(Point pt)
+ {
+ return getUI().viewToModel(this, pt);
+ }
+
+ public void copy()
+ {
+ if (isEnabled())
+ doTransferAction("copy", TransferHandler.getCopyAction());
+ }
+
+ public void cut()
+ {
+ if (editable && isEnabled())
+ doTransferAction("cut", TransferHandler.getCutAction());
+ }
+
+ public void paste()
+ {
+ if (editable && isEnabled())
+ doTransferAction("paste", TransferHandler.getPasteAction());
+ }
+
+ private void doTransferAction(String name, Action action)
+ {
+ // Install default TransferHandler if none set.
+ if (getTransferHandler() == null)
+ {
+ if (defaultTransferHandler == null)
+ defaultTransferHandler = new DefaultTransferHandler();
+
+ setTransferHandler(defaultTransferHandler);
+ }
+
+ // Perform action.
+ ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ action.getValue(Action.NAME).toString());
+ action.actionPerformed(event);
+ }
+
+ public void setFocusAccelerator(char newKey)
+ {
+ if (focusAccelerator == newKey)
+ return;
+
+ char oldKey = focusAccelerator;
+ focusAccelerator = newKey;
+ firePropertyChange(FOCUS_ACCELERATOR_KEY, oldKey, newKey);
+ }
+
+ public char getFocusAccelerator()
+ {
+ return focusAccelerator;
+ }
+
+ /**
+ * @since 1.4
+ */
+ public NavigationFilter getNavigationFilter()
+ {
+ return navigationFilter;
+ }
+
+ /**
+ * @since 1.4
+ */
+ public void setNavigationFilter(NavigationFilter filter)
+ {
+ navigationFilter = filter;
+ }
+
+ /**
+ * Read and set the content this component. If not overridden, the
+ * method reads the component content as a plain text.
+ *
+ * The second parameter of this method describes the input stream. It can
+ * be String, URL, File and so on. If not null, this object is added to
+ * the properties of the associated document under the key
+ * {@link Document#StreamDescriptionProperty}.
+ *
+ * @param input an input stream to read from.
+ * @param streamDescription an object, describing the stream.
+ *
+ * @throws IOException if the reader throws it.
+ *
+ * @see #getDocument()
+ * @see Document#getProperty(Object)
+ */
+ public void read(Reader input, Object streamDescription)
+ throws IOException
+ {
+ if (streamDescription != null)
+ {
+ Document d = getDocument();
+ if (d != null)
+ d.putProperty(Document.StreamDescriptionProperty, streamDescription);
+ }
+
+ CPStringBuilder b = new CPStringBuilder();
+ int c;
+
+ // Read till -1 (EOF).
+ while ((c = input.read()) >= 0)
+ b.append((char) c);
+
+ setText(b.toString());
+ }
+
+ /**
+ * Write the content of this component to the given stream. If not
+ * overridden, the method writes the component content as a plain text.
+ *
+ * @param output the writer to write into.
+ *
+ * @throws IOException if the writer throws it.
+ */
+ public void write(Writer output)
+ throws IOException
+ {
+ output.write(getText());
+ }
+
+ /**
+ * Returns the tooltip text for this text component for the given mouse
+ * event. This forwards the call to
+ * {@link TextUI#getToolTipText(JTextComponent, Point)}.
+ *
+ * @param ev the mouse event
+ *
+ * @return the tooltip text for this text component for the given mouse
+ * event
+ */
+ public String getToolTipText(MouseEvent ev)
+ {
+ return getUI().getToolTipText(this, ev.getPoint());
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/Keymap.java b/libjava/classpath/javax/swing/text/Keymap.java
new file mode 100644
index 000000000..e1b305f5f
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Keymap.java
@@ -0,0 +1,58 @@
+/* Keymap.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.Action;
+import javax.swing.KeyStroke;
+
+public interface Keymap
+{
+ void addActionForKeyStroke(KeyStroke key, Action a);
+ Action getAction(KeyStroke key);
+ Action[] getBoundActions();
+ KeyStroke[] getBoundKeyStrokes();
+ Action getDefaultAction();
+ KeyStroke[] getKeyStrokesForAction(Action a);
+ String getName();
+ Keymap getResolveParent();
+ boolean isLocallyDefined(KeyStroke key);
+ void removeBindings();
+ void removeKeyStrokeBinding(KeyStroke keys);
+ void setDefaultAction(Action a);
+ void setResolveParent(Keymap parent);
+}
diff --git a/libjava/classpath/javax/swing/text/LabelView.java b/libjava/classpath/javax/swing/text/LabelView.java
new file mode 100644
index 000000000..7cfeae862
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/LabelView.java
@@ -0,0 +1,322 @@
+/* LabelView.java -- A view to render styled text
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Container;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Shape;
+import java.awt.Toolkit;
+
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A {@link GlyphView} that caches the textattributes for most effective
+ * rendering.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class LabelView extends GlyphView
+{
+
+ /**
+ * The background color.
+ */
+ Color background;
+
+ /**
+ * The foreground color.
+ */
+ Color foreground;
+
+ /**
+ * The background color.
+ */
+ Font font;
+
+ /**
+ * The strikethrough flag.
+ */
+ boolean strikeThrough;
+
+ /**
+ * The underline flag.
+ */
+ boolean underline;
+
+ /**
+ * The subscript flag.
+ */
+ boolean subscript;
+
+ /**
+ * The superscript flag.
+ */
+ boolean superscript;
+
+ /**
+ * Indicates if the attributes must be refetched.
+ */
+ private boolean valid;
+
+ /**
+ * Creates a new <code>GlyphView</code> for the given <code>Element</code>.
+ *
+ * @param element the element that is rendered by this GlyphView
+ */
+ public LabelView(Element element)
+ {
+ super(element);
+ valid = false;
+ }
+
+ /**
+ * Loads the properties of this label view from the element's text
+ * attributes. This method is called from the constructor and the
+ * {@link #changedUpdate} method
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ AttributeSet atts = getAttributes();
+ setStrikeThrough(StyleConstants.isStrikeThrough(atts));
+ setSubscript(StyleConstants.isSubscript(atts));
+ setSuperscript(StyleConstants.isSuperscript(atts));
+ setUnderline(StyleConstants.isUnderline(atts));
+
+ // Determine the font and colors.
+ Document d = getDocument();
+ if (d instanceof StyledDocument)
+ {
+ StyledDocument doc = (StyledDocument) d;
+ font = doc.getFont(atts);
+ if (atts.isDefined(StyleConstants.Background))
+ background = doc.getBackground(atts);
+ else
+ background = null;
+ foreground = doc.getForeground(atts);
+ }
+ valid = true;
+ }
+
+ /**
+ * Receives notification when text attributes change in the chunk of
+ * text that this view is responsible for. This simply calls
+ * {@link #setPropertiesFromAttributes()}.
+ *
+ * @param e the document event
+ * @param a the allocation of this view
+ * @param vf the view factory to use for creating new views
+ */
+ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ valid = false;
+ super.changedUpdate(e, a, vf);
+ }
+
+ /**
+ * Returns the background color for the glyphs.
+ *
+ * @return the background color for the glyphs
+ */
+ public Color getBackground()
+ {
+ if (! valid)
+ setPropertiesFromAttributes();
+ return background;
+ }
+
+ /**
+ * Sets the background color for the glyphs. A value of <code>null</code>
+ * means the background of the parent view should shine through.
+ *
+ * @param bg the background to set or <code>null</code>
+ *
+ * @since 1.5
+ */
+ protected void setBackground(Color bg)
+ {
+ background = bg;
+ }
+
+ /**
+ * Returns the foreground color for the glyphs.
+ *
+ * @return the foreground color for the glyphs
+ */
+ public Color getForeground()
+ {
+ if (! valid)
+ setPropertiesFromAttributes();
+ return foreground;
+ }
+
+ /**
+ * Returns the font for the glyphs.
+ *
+ * @return the font for the glyphs
+ */
+ public Font getFont()
+ {
+ if (! valid)
+ setPropertiesFromAttributes();
+ return font;
+ }
+
+ /**
+ * Returns the font metrics of the current font.
+ *
+ * @return the font metrics of the current font
+ *
+ * @deprecated this is not used anymore
+ */
+ protected FontMetrics getFontMetrics()
+ {
+ if (! valid)
+ setPropertiesFromAttributes();
+
+ Container c = getContainer();
+ FontMetrics fm;
+ if (c != null)
+ fm = c.getFontMetrics(font);
+ else
+ fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
+ return fm;
+ }
+
+ /**
+ * Returns <code>true</code> if the glyphs are rendered underlined,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the glyphs are rendered underlined,
+ * <code>false</code> otherwise
+ */
+ public boolean isUnderline()
+ {
+ if (! valid)
+ setPropertiesFromAttributes();
+ return underline;
+ }
+
+ /**
+ * Sets the underline flag.
+ *
+ * @param flag <code>true</code> if the glyphs are rendered underlined,
+ * <code>false</code> otherwise
+ */
+ protected void setUnderline(boolean flag)
+ {
+ underline = flag;
+ }
+
+ /**
+ * Returns <code>true</code> if the glyphs are rendered as subscript,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the glyphs are rendered as subscript,
+ * <code>false</code> otherwise
+ */
+ public boolean isSubscript()
+ {
+ if (! valid)
+ setPropertiesFromAttributes();
+ return subscript;
+ }
+
+ /**
+ * Sets the subscript flag.
+ *
+ * @param flag <code>true</code> if the glyphs are rendered as subscript,
+ * <code>false</code> otherwise
+ */
+ protected void setSubscript(boolean flag)
+ {
+ subscript = flag;
+ }
+
+ /**
+ * Returns <code>true</code> if the glyphs are rendered as superscript,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the glyphs are rendered as superscript,
+ * <code>false</code> otherwise
+ */
+ public boolean isSuperscript()
+ {
+ if (! valid)
+ setPropertiesFromAttributes();
+ return superscript;
+ }
+
+ /**
+ * Sets the superscript flag.
+ *
+ * @param flag <code>true</code> if the glyphs are rendered as superscript,
+ * <code>false</code> otherwise
+ */
+ protected void setSuperscript(boolean flag)
+ {
+ superscript = flag;
+ }
+
+ /**
+ * Returns <code>true</code> if the glyphs are rendered strike-through,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the glyphs are rendered strike-through,
+ * <code>false</code> otherwise
+ */
+ public boolean isStrikeThrough()
+ {
+ if (! valid)
+ setPropertiesFromAttributes();
+ return strikeThrough;
+ }
+
+ /**
+ * Sets the strike-through flag.
+ *
+ * @param flag <code>true</code> if the glyphs are rendered strike-through,
+ * <code>false</code> otherwise
+ */
+ protected void setStrikeThrough(boolean flag)
+ {
+ strikeThrough = flag;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/LayeredHighlighter.java b/libjava/classpath/javax/swing/text/LayeredHighlighter.java
new file mode 100644
index 000000000..3eac26b38
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/LayeredHighlighter.java
@@ -0,0 +1,57 @@
+/* LayeredHighlighter.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Graphics;
+import java.awt.Shape;
+
+public abstract class LayeredHighlighter
+ implements Highlighter
+{
+ public abstract static class LayerPainter
+ implements Highlighter.HighlightPainter
+ {
+ public abstract Shape paintLayer(Graphics g, int p0, int p1,
+ Shape viewBounds, JTextComponent editor,
+ View view);
+ }
+
+ public abstract void paintLayeredHighlights(Graphics g, int p0, int p1,
+ Shape viewBounds,
+ JTextComponent editor, View view);
+}
diff --git a/libjava/classpath/javax/swing/text/LayoutQueue.java b/libjava/classpath/javax/swing/text/LayoutQueue.java
new file mode 100644
index 000000000..10fadd55e
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/LayoutQueue.java
@@ -0,0 +1,116 @@
+/* LayoutQueue.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.LinkedList;
+
+/**
+ * This is a queue which holds {@link Runnable} objects. It is
+ * intended for deferring layout operations.
+ */
+public class LayoutQueue
+{
+ // The default layout queue.
+ private static LayoutQueue defaultQueue = new LayoutQueue();
+
+ // The queue of tasks.
+ private LinkedList list = new LinkedList();
+
+ /**
+ * Create a new layout queue.
+ */
+ public LayoutQueue()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Add a layout task to the queue.
+ */
+ public void addTask(Runnable task)
+ {
+ synchronized (list)
+ {
+ list.addLast(task);
+ list.notify();
+ }
+ }
+
+ /**
+ * Called by a worker thread to retrieve the next layout task. This
+ * will block until a new task is available. This method will
+ * return null if the thread is interrupted while waiting.
+ */
+ protected Runnable waitForWork()
+ {
+ synchronized (list)
+ {
+ while (list.size() == 0)
+ {
+ try
+ {
+ list.wait();
+ }
+ catch (InterruptedException _)
+ {
+ // This seemed like a good idea, but it has not been
+ // tested on the JDK.
+ return null;
+ }
+ }
+ return (Runnable) list.removeFirst();
+ }
+ }
+
+ /**
+ * Return the default layout queue.
+ */
+ public static synchronized LayoutQueue getDefaultQueue()
+ {
+ return defaultQueue;
+ }
+
+ /**
+ * Set the default layout queue.
+ */
+ public static synchronized void setDefaultQueue(LayoutQueue q)
+ {
+ defaultQueue = q;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/MaskFormatter.java b/libjava/classpath/javax/swing/text/MaskFormatter.java
new file mode 100644
index 000000000..c8f631ae3
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/MaskFormatter.java
@@ -0,0 +1,558 @@
+/* MaskFormatter.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.text.ParseException;
+
+import javax.swing.JFormattedTextField;
+
+/**
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ */
+public class MaskFormatter extends DefaultFormatter
+{
+ // The declaration of the valid mask characters
+ private static final char NUM_CHAR = '#';
+ private static final char ESCAPE_CHAR = '\'';
+ private static final char UPPERCASE_CHAR = 'U';
+ private static final char LOWERCASE_CHAR = 'L';
+ private static final char ALPHANUM_CHAR = 'A';
+ private static final char LETTER_CHAR = '?';
+ private static final char ANYTHING_CHAR = '*';
+ private static final char HEX_CHAR = 'H';
+
+ /** The mask for this MaskFormatter **/
+ private String mask;
+
+ /**
+ * A String made up of the characters that are not valid for input for
+ * this MaskFormatter.
+ */
+ private String invalidChars;
+
+ /**
+ * A String made up of the characters that are valid for input for
+ * this MaskFormatter.
+ */
+ private String validChars;
+
+ /** A String used in place of missing chracters if the value does not
+ * completely fill in the spaces in the mask.
+ */
+ private String placeHolder;
+
+ /** A character used in place of missing characters if the value does
+ * not completely fill in the spaces in the mask.
+ */
+ private char placeHolderChar = ' ';
+
+ /**
+ * Whether or not stringToValue should return literal characters in the mask.
+ */
+ private boolean valueContainsLiteralCharacters = true;
+
+ /** A String used for easy access to valid HEX characters **/
+ private static String hexString = "0123456789abcdefABCDEF";
+
+ /** An int to hold the length of the mask, accounting for escaped characters **/
+ int maskLength = 0;
+
+ public MaskFormatter ()
+ {
+ // Override super's default behaviour, in MaskFormatter the default
+ // is not to allow invalid values
+ setAllowsInvalid(false);
+ }
+
+ /**
+ * Creates a MaskFormatter with the specified mask.
+ * @specnote doesn't actually throw a ParseException although it
+ * is declared to do so
+ * @param mask
+ * @throws java.text.ParseException
+ */
+ public MaskFormatter (String mask) throws java.text.ParseException
+ {
+ this();
+ setMask (mask);
+ }
+
+ /**
+ * Returns the mask used in this MaskFormatter.
+ * @return the mask used in this MaskFormatter.
+ */
+ public String getMask()
+ {
+ return mask;
+ }
+
+ /**
+ * Returns a String containing the characters that are not valid for input
+ * for this MaskFormatter.
+ * @return a String containing the invalid characters.
+ */
+ public String getInvalidCharacters()
+ {
+ return invalidChars;
+ }
+
+ /**
+ * Sets characters that are not valid for input. If
+ * <code>invalidCharacters</code> is non-null then no characters contained
+ * in it will be allowed to be input.
+ *
+ * @param invalidCharacters the String specifying invalid characters.
+ */
+ public void setInvalidCharacters (String invalidCharacters)
+ {
+ this.invalidChars = invalidCharacters;
+ }
+
+ /**
+ * Returns a String containing the characters that are valid for input
+ * for this MaskFormatter.
+ * @return a String containing the valid characters.
+ */
+ public String getValidCharacters()
+ {
+ return validChars;
+ }
+
+ /**
+ * Sets characters that are valid for input. If
+ * <code>validCharacters</code> is non-null then no characters that are
+ * not contained in it will be allowed to be input.
+ *
+ * @param validCharacters the String specifying valid characters.
+ */
+ public void setValidCharacters (String validCharacters)
+ {
+ this.validChars = validCharacters;
+ }
+
+ /**
+ * Returns the place holder String that is used in place of missing
+ * characters when the value doesn't completely fill in the spaces
+ * in the mask.
+ * @return the place holder String.
+ */
+ public String getPlaceholder()
+ {
+ return placeHolder;
+ }
+
+ /**
+ * Sets the string to use if the value does not completely fill in the mask.
+ * If this is null, the place holder character will be used instead.
+ * @param placeholder the String to use if the value doesn't completely
+ * fill in the mask.
+ */
+ public void setPlaceholder (String placeholder)
+ {
+ this.placeHolder = placeholder;
+ }
+
+ /**
+ * Returns the character used in place of missing characters when the
+ * value doesn't completely fill the mask.
+ * @return the place holder character
+ */
+ public char getPlaceholderCharacter()
+ {
+ return placeHolderChar;
+ }
+
+ /**
+ * Sets the char to use if the value does not completely fill in the mask.
+ * This is only used if the place holder String has not been set or does
+ * not completely fill in the mask.
+ * @param placeholder the char to use if the value doesn't completely
+ * fill in the mask.
+ */
+ public void setPlaceholderCharacter (char placeholder)
+ {
+ this.placeHolderChar = placeholder;
+ }
+
+ /**
+ * Returns true if stringToValue should return the literal
+ * characters in the mask.
+ * @return true if stringToValue should return the literal
+ * characters in the mask
+ */
+ public boolean getValueContainsLiteralCharacters()
+ {
+ return valueContainsLiteralCharacters;
+ }
+
+ /**
+ * Determines whether stringToValue will return literal characters or not.
+ * @param containsLiteralChars if true, stringToValue will return the
+ * literal characters in the mask, otherwise it will not.
+ */
+ public void setValueContainsLiteralCharacters (boolean containsLiteralChars)
+ {
+ this.valueContainsLiteralCharacters = containsLiteralChars;
+ }
+
+ /**
+ * Sets the mask for this MaskFormatter.
+ * @specnote doesn't actually throw a ParseException even though it is
+ * declared to do so
+ * @param mask the new mask for this MaskFormatter
+ * @throws ParseException if <code>mask</code> is not valid.
+ */
+ public void setMask (String mask) throws ParseException
+ {
+ this.mask = mask;
+
+ // Update the cached maskLength.
+ int end = mask.length() - 1;
+ maskLength = 0;
+ for (int i = 0; i <= end; i++)
+ {
+ // Handle escape characters properly - they don't add to the maskLength
+ // but 2 escape characters in a row is really one escape character and
+ // one literal single quote, so that does add 1 to the maskLength.
+ if (mask.charAt(i) == '\'')
+ {
+ // Escape characters at the end of the mask don't do anything.
+ if (i != end)
+ maskLength++;
+ i++;
+ }
+ else
+ maskLength++;
+ }
+ }
+
+ /**
+ * Installs this MaskFormatter on the JFormattedTextField.
+ * Invokes valueToString to convert the current value from the
+ * JFormattedTextField to a String, then installs the Actions from
+ * getActions, the DocumentFilter from getDocumentFilter, and the
+ * NavigationFilter from getNavigationFilter.
+ *
+ * If valueToString throws a ParseException, this method sets the text
+ * to an empty String and marks the JFormattedTextField as invalid.
+ */
+ public void install (JFormattedTextField ftf)
+ {
+ super.install(ftf);
+ if (ftf != null)
+ {
+ try
+ {
+ valueToString(ftf.getValue());
+ }
+ catch (ParseException pe)
+ {
+ // Set the text to an empty String and mark the JFormattedTextField
+ // as invalid.
+ ftf.setText("");
+ setEditValid(false);
+ }
+ }
+ }
+
+ /**
+ * Parses the text using the mask, valid characters, and invalid characters
+ * to determine the appropriate Object to return. This strips the literal
+ * characters if necessary and invokes super.stringToValue. If the paramter
+ * is invalid for the current mask and valid/invalid character sets this
+ * method will throw a ParseException.
+ *
+ * @param value the String to parse
+ * @throws ParseException if value doesn't match the mask and valid/invalid
+ * character sets
+ */
+ public Object stringToValue (String value) throws ParseException
+ {
+ return super.stringToValue(convertStringToValue(value));
+ }
+
+ private String convertStringToValue(String value)
+ throws ParseException
+ {
+ CPStringBuilder result = new CPStringBuilder();
+ char valueChar;
+ boolean isPlaceHolder;
+
+ int length = mask.length();
+ for (int i = 0, j = 0; j < length; j++)
+ {
+ char maskChar = mask.charAt(j);
+
+ if (i < value.length())
+ {
+ isPlaceHolder = false;
+ valueChar = value.charAt(i);
+ if (maskChar != ESCAPE_CHAR && maskChar != valueChar)
+ {
+ if (invalidChars != null
+ && invalidChars.indexOf(valueChar) != -1)
+ throw new ParseException("Invalid character: " + valueChar, i);
+ if (validChars != null
+ && validChars.indexOf(valueChar) == -1)
+ throw new ParseException("Invalid character: " + valueChar, i);
+ }
+ }
+ else if (placeHolder != null && i < placeHolder.length())
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolder.charAt(i);
+ }
+ else
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolderChar;
+ }
+
+ // This switch block on the mask character checks that the character
+ // within <code>value</code> at that point is valid according to the
+ // mask and also converts to upper/lowercase as needed.
+ switch (maskChar)
+ {
+ case NUM_CHAR:
+ if (! Character.isDigit(valueChar))
+ throw new ParseException("Number expected: " + valueChar, i);
+ result.append(valueChar);
+ i++;
+ break;
+ case UPPERCASE_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(Character.toUpperCase(valueChar));
+ i++;
+ break;
+ case LOWERCASE_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(Character.toLowerCase(valueChar));
+ i++;
+ break;
+ case ALPHANUM_CHAR:
+ if (! Character.isLetterOrDigit(valueChar))
+ throw new ParseException("Letter or number expected", i);
+ result.append(valueChar);
+ i++;
+ break;
+ case LETTER_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(valueChar);
+ i++;
+ break;
+ case HEX_CHAR:
+ if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
+ throw new ParseException("Hexadecimal character expected", i);
+ result.append(valueChar);
+ i++;
+ break;
+ case ANYTHING_CHAR:
+ result.append(valueChar);
+ i++;
+ break;
+ case ESCAPE_CHAR:
+ // Escape character, check the next character to make sure that
+ // the literals match
+ j++;
+ if (j < length)
+ {
+ maskChar = mask.charAt(j);
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ {
+ result.append(maskChar);
+ }
+ i++;
+ }
+ else if (! isPlaceHolder)
+ throw new ParseException("Bad match at trailing escape: ", i);
+ break;
+ default:
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ {
+ result.append(maskChar);
+ }
+ i++;
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Returns a String representation of the Object value based on the mask.
+ *
+ * @param value the value to convert
+ * @throws ParseException if value is invalid for this mask and valid/invalid
+ * character sets
+ */
+ public String valueToString(Object value) throws ParseException
+ {
+ String string = value != null ? value.toString() : "";
+ return convertValueToString(string);
+ }
+
+ /**
+ * This method takes in a String and runs it through the mask to make
+ * sure that it is valid. If <code>convert</code> is true, it also
+ * converts letters to upper/lowercase as required by the mask.
+ * @param value the String to convert
+ * @return the converted String
+ * @throws ParseException if the given String isn't valid for the mask
+ */
+ private String convertValueToString(String value)
+ throws ParseException
+ {
+ CPStringBuilder result = new CPStringBuilder();
+ char valueChar;
+ boolean isPlaceHolder;
+
+ int length = mask.length();
+ for (int i = 0, j = 0; j < length; j++)
+ {
+ char maskChar = mask.charAt(j);
+ if (i < value.length())
+ {
+ isPlaceHolder = false;
+ valueChar = value.charAt(i);
+ if (maskChar != ESCAPE_CHAR && valueChar != maskChar)
+ {
+ if (invalidChars != null
+ && invalidChars.indexOf(valueChar) != -1)
+ throw new ParseException("Invalid character: " + valueChar,
+ i);
+ if (validChars != null && validChars.indexOf(valueChar) == -1)
+ throw new ParseException("Invalid character: " + valueChar +" maskChar: " + maskChar,
+ i);
+ }
+ }
+ else if (placeHolder != null && i < placeHolder.length())
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolder.charAt(i);
+ }
+ else
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolderChar;
+ }
+
+ // This switch block on the mask character checks that the character
+ // within <code>value</code> at that point is valid according to the
+ // mask and also converts to upper/lowercase as needed.
+ switch (maskChar)
+ {
+ case NUM_CHAR:
+ if ( ! isPlaceHolder && ! Character.isDigit(valueChar))
+ throw new ParseException("Number expected: " + valueChar, i);
+ result.append(valueChar);
+ i++;
+ break;
+ case UPPERCASE_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(Character.toUpperCase(valueChar));
+ i++;
+ break;
+ case LOWERCASE_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(Character.toLowerCase(valueChar));
+ i++;
+ break;
+ case ALPHANUM_CHAR:
+ if (! Character.isLetterOrDigit(valueChar))
+ throw new ParseException("Letter or number expected", i);
+ result.append(valueChar);
+ i++;
+ break;
+ case LETTER_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(valueChar);
+ i++;
+ break;
+ case HEX_CHAR:
+ if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
+ throw new ParseException("Hexadecimal character expected", i);
+ result.append(valueChar);
+ i++;
+ break;
+ case ANYTHING_CHAR:
+ result.append(valueChar);
+ i++;
+ break;
+ case ESCAPE_CHAR:
+ // Escape character, check the next character to make sure that
+ // the literals match
+ j++;
+ if (j < length)
+ {
+ maskChar = mask.charAt(j);
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ i++;
+ result.append(maskChar);
+ }
+ break;
+ default:
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ i++;
+ result.append(maskChar);
+ }
+ }
+ return result.toString();
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/MutableAttributeSet.java b/libjava/classpath/javax/swing/text/MutableAttributeSet.java
new file mode 100644
index 000000000..eb52be5c8
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/MutableAttributeSet.java
@@ -0,0 +1,117 @@
+/* MutableAttributeSet.java --
+ Copyright (C) 2002, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.Enumeration;
+
+/**
+ * An {@link AttributeSet} that supports modification of the stored
+ * attributes.
+ *
+ * @author Andrew Selkirk
+ * @since 1.2
+ */
+public interface MutableAttributeSet extends AttributeSet
+{
+ /**
+ * Adds an attribute with the given <code>name</code> and <code>value</code>
+ * to the set. If the set already contains an attribute with the given
+ * <code>name</code>, the attribute value is updated.
+ *
+ * @param name the attribute name (<code>null</code> not permitted).
+ * @param value the value (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ */
+ void addAttribute(Object name, Object value);
+
+ /**
+ * Adds all the attributes from <code>attributes</code> to this set.
+ *
+ * @param attributes the set of attributes to add (<code>null</code> not
+ * permitted).
+ *
+ * @throws NullPointerException if <code>attributes</code> is
+ * <code>null</code>.
+ */
+ void addAttributes(AttributeSet attributes);
+
+ /**
+ * Removes the attribute with the specified <code>name</code>, if this
+ * attribute is defined. This method will only remove an attribute from
+ * this set, not from the resolving parent.
+ *
+ * @param name the attribute name (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ void removeAttribute(Object name);
+
+ /**
+ * Removes the attributes listed in <code>names</code>.
+ *
+ * @param names the attribute names (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>names</code> is <code>null</code>
+ * or contains any <code>null</code> values.
+ */
+ void removeAttributes(Enumeration<?> names);
+
+ /**
+ * Removes attributes from this set if they are found in the
+ * given set. Only attributes whose key AND value are removed.
+ * Removes attributes only from this set, not from the resolving parent.
+ * Since the resolving parent is stored as an attribute, if
+ * <code>attributes</code> has the same resolving parent as this set, the
+ * parent will be removed from this set.
+ *
+ * @param attributes the attributes (<code>null</code> not permitted).
+ */
+ void removeAttributes(AttributeSet attributes);
+
+ /**
+ * Sets the reolving parent for this set. When looking up an attribute, if
+ * it is not found in this set, then the resolving parent is also used for
+ * the lookup.
+ *
+ * @param parent the parent attribute set (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>parent</code> is <code>null</code>.
+ */
+ void setResolveParent(AttributeSet parent);
+}
diff --git a/libjava/classpath/javax/swing/text/NavigationFilter.java b/libjava/classpath/javax/swing/text/NavigationFilter.java
new file mode 100644
index 000000000..647ac70bf
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/NavigationFilter.java
@@ -0,0 +1,98 @@
+/* NavigationFilter.java --
+ Copyright (C) 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+public class NavigationFilter
+{
+ public abstract static class FilterBypass
+ {
+ public FilterBypass()
+ {
+ // Do nothing here.
+ }
+
+ public abstract Caret getCaret();
+ public abstract void moveDot(int dot, Position.Bias bias);
+ public abstract void setDot(int dot, Position.Bias bias);
+ }
+
+ public NavigationFilter()
+ {
+ // Do nothing here.
+ }
+
+ public void moveDot(NavigationFilter.FilterBypass fb, int dot,
+ Position.Bias bias)
+ {
+ fb.moveDot(dot, bias);
+ }
+
+ public void setDot(NavigationFilter.FilterBypass fb, int dot,
+ Position.Bias bias)
+ {
+ fb.setDot(dot, bias);
+ }
+
+ /**
+ * Returns the next visual position in the specified direction at which one
+ * would place a caret. The default implementation forwards to the text
+ * component's root view. Subclasses may wish to restrict that more.
+ *
+ * @param c the text component
+ * @param pos the current model position
+ * @param bias the bias of <code>pos</code>
+ * @param dir the direction, one of {@link javax.swing.SwingConstants#NORTH},
+ * {@link javax.swing.SwingConstants#SOUTH},
+ * {@link javax.swing.SwingConstants#WEST} or
+ * {@link javax.swing.SwingConstants#EAST}
+ * @param retBias the bias of the returned position
+ *
+ * @return the next model location to place the caret
+ *
+ * @throws BadLocationException when <code>pos</code> is not a valid model
+ * position
+ */
+ public int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias bias, int dir,
+ Position.Bias[] retBias)
+ throws BadLocationException
+ {
+ return c.getUI().getNextVisualPositionFrom(c, pos, bias, dir, retBias);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/NumberFormatter.java b/libjava/classpath/javax/swing/text/NumberFormatter.java
new file mode 100644
index 000000000..ce5eef990
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/NumberFormatter.java
@@ -0,0 +1,86 @@
+/* NumberFormatter.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.text.Format;
+import java.text.NumberFormat;
+
+/**
+ * <code>NumberFormatter</code> is an {@link InternationalFormatter}
+ * that implements value to string and string to value conversion via
+ * an instance of {@link NumberFormat}.
+ *
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ * @since 1.4
+ */
+public class NumberFormatter extends InternationalFormatter
+{
+
+ /**
+ * Creates a NumberFormatter with the default NumberFormat from
+ * NumberFormat.getNumberInstance().
+ */
+ public NumberFormatter ()
+ {
+ this (NumberFormat.getNumberInstance());
+ }
+
+ /**
+ * Creates a NumberFormatter with the specified NumberFormat.
+ * @param format the NumberFormat to use for this NumberFormatter.
+ */
+ public NumberFormatter (NumberFormat format)
+ {
+ super(format);
+ setFormat(format);
+ }
+
+ /**
+ * Sets the NumberFormat that this NumberFormatter will use to determine
+ * legal values for editing and displaying.
+ *
+ * @param format the Format to use to determine legal values.
+ */
+ public void setFormat (Format format)
+ {
+ // TODO: This should be different from the super implementation
+ // but I don't yet know how.
+ super.setFormat(format);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/ParagraphView.java b/libjava/classpath/javax/swing/text/ParagraphView.java
new file mode 100644
index 000000000..4d4c7a044
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/ParagraphView.java
@@ -0,0 +1,528 @@
+/* ParagraphView.java -- A composite View
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Shape;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A {@link FlowView} that flows it's children horizontally and boxes the rows
+ * vertically.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class ParagraphView extends FlowView implements TabExpander
+{
+ /**
+ * A specialized horizontal <code>BoxView</code> that represents exactly
+ * one row in a <code>ParagraphView</code>.
+ */
+ class Row extends BoxView
+ {
+ /**
+ * Creates a new instance of <code>Row</code>.
+ */
+ Row(Element el)
+ {
+ super(el, X_AXIS);
+ }
+
+ /**
+ * Overridden to adjust when we are the first line, and firstLineIndent
+ * is not 0.
+ */
+ public short getLeftInset()
+ {
+ short leftInset = super.getLeftInset();
+ View parent = getParent();
+ if (parent != null)
+ {
+ if (parent.getView(0) == this)
+ leftInset += firstLineIndent;
+ }
+ return leftInset;
+ }
+
+ public float getAlignment(int axis)
+ {
+ float align;
+ if (axis == X_AXIS)
+ switch (justification)
+ {
+ case StyleConstants.ALIGN_RIGHT:
+ align = 1.0F;
+ break;
+ case StyleConstants.ALIGN_CENTER:
+ case StyleConstants.ALIGN_JUSTIFIED:
+ align = 0.5F;
+ break;
+ case StyleConstants.ALIGN_LEFT:
+ default:
+ align = 0.0F;
+ }
+ else
+ align = super.getAlignment(axis);
+ return align;
+ }
+
+ /**
+ * Overridden because child views are not necessarily laid out in model
+ * order.
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ int index = -1;
+ if (pos >= getStartOffset() && pos < getEndOffset())
+ {
+ int nviews = getViewCount();
+ for (int i = 0; i < nviews && index == -1; i++)
+ {
+ View child = getView(i);
+ if (pos >= child.getStartOffset() && pos < child.getEndOffset())
+ index = i;
+ }
+ }
+ return index;
+ }
+
+
+ /**
+ * Overridden to perform a baseline layout. The normal BoxView layout
+ * isn't completely suitable for rows.
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ baselineLayout(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Overridden to perform a baseline layout. The normal BoxView layout
+ * isn't completely suitable for rows.
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ return baselineRequirements(axis, r);
+ }
+
+ protected void loadChildren(ViewFactory vf)
+ {
+ // Do nothing here. The children are added while layouting.
+ }
+
+ /**
+ * Overridden to determine the minimum start offset of the row's children.
+ */
+ public int getStartOffset()
+ {
+ // Determine minimum start offset of the children.
+ int offset = Integer.MAX_VALUE;
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View v = getView(i);
+ offset = Math.min(offset, v.getStartOffset());
+ }
+ return offset;
+ }
+
+ /**
+ * Overridden to determine the maximum end offset of the row's children.
+ */
+ public int getEndOffset()
+ {
+ // Determine minimum start offset of the children.
+ int offset = 0;
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View v = getView(i);
+ offset = Math.max(offset, v.getEndOffset());
+ }
+ return offset;
+ }
+ }
+
+ /**
+ * The indentation of the first line of the paragraph.
+ */
+ protected int firstLineIndent;
+
+ /**
+ * The justification of the paragraph.
+ */
+ private int justification;
+
+ /**
+ * The line spacing of this paragraph.
+ */
+ private float lineSpacing;
+
+ /**
+ * The TabSet of this paragraph.
+ */
+ private TabSet tabSet;
+
+ /**
+ * Creates a new <code>ParagraphView</code> for the given
+ * <code>Element</code>.
+ *
+ * @param element the element that is rendered by this ParagraphView
+ */
+ public ParagraphView(Element element)
+ {
+ super(element, Y_AXIS);
+ }
+
+ public float nextTabStop(float x, int tabOffset)
+ {
+ throw new InternalError("Not implemented yet");
+ }
+
+ /**
+ * Creates a new view that represents a row within a flow.
+ *
+ * @return a view for a new row
+ */
+ protected View createRow()
+ {
+ return new Row(getElement());
+ }
+
+ /**
+ * Returns the alignment for this paragraph view for the specified axis.
+ * For the X_AXIS the paragraph view will be aligned at it's left edge
+ * (0.0F). For the Y_AXIS the paragraph view will be aligned at the
+ * center of it's first row.
+ *
+ * @param axis the axis which is examined
+ *
+ * @return the alignment for this paragraph view for the specified axis
+ */
+ public float getAlignment(int axis)
+ {
+ float align;
+ if (axis == X_AXIS)
+ align = 0.5F;
+ else if (getViewCount() > 0)
+ {
+ float prefHeight = getPreferredSpan(Y_AXIS);
+ float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS);
+ align = (firstRowHeight / 2.F) / prefHeight;
+ }
+ else
+ align = 0.5F;
+ return align;
+ }
+
+ /**
+ * Receives notification when some attributes of the displayed element
+ * changes. This triggers a refresh of the cached attributes of this
+ * paragraph.
+ *
+ * @param ev the document event
+ * @param a the allocation of this view
+ * @param vf the view factory to use for creating new child views
+ */
+ public void changedUpdate(DocumentEvent ev, Shape a, ViewFactory vf)
+ {
+ setPropertiesFromAttributes();
+ layoutChanged(X_AXIS);
+ layoutChanged(Y_AXIS);
+ super.changedUpdate(ev, a, vf);
+ }
+
+ /**
+ * Fetches the cached properties from the element's attributes.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ setFirstLineIndent(StyleConstants.getFirstLineIndent(atts));
+ setLineSpacing(StyleConstants.getLineSpacing(atts));
+ setJustification(StyleConstants.getAlignment(atts));
+ tabSet = StyleConstants.getTabSet(atts);
+ }
+
+ /**
+ * Sets the indentation of the first line of the paragraph.
+ *
+ * @param i the indentation to set
+ */
+ protected void setFirstLineIndent(float i)
+ {
+ firstLineIndent = (int) i;
+ }
+
+ /**
+ * Sets the justification of the paragraph.
+ *
+ * @param j the justification to set
+ */
+ protected void setJustification(int j)
+ {
+ justification = j;
+ }
+
+ /**
+ * Sets the line spacing for this paragraph.
+ *
+ * @param s the line spacing to set
+ */
+ protected void setLineSpacing(float s)
+ {
+ lineSpacing = s;
+ }
+
+ /**
+ * Returns the i-th view from the logical views, before breaking into rows.
+ *
+ * @param i the index of the logical view to return
+ *
+ * @return the i-th view from the logical views, before breaking into rows
+ */
+ protected View getLayoutView(int i)
+ {
+ return layoutPool.getView(i);
+ }
+
+ /**
+ * Returns the number of logical child views.
+ *
+ * @return the number of logical child views
+ */
+ protected int getLayoutViewCount()
+ {
+ return layoutPool.getViewCount();
+ }
+
+ /**
+ * Returns the TabSet used by this ParagraphView.
+ *
+ * @return the TabSet used by this ParagraphView
+ */
+ protected TabSet getTabSet()
+ {
+ return tabSet;
+ }
+
+ /**
+ * Finds the next offset in the document that has one of the characters
+ * specified in <code>string</code>. If there is no such character found,
+ * this returns -1.
+ *
+ * @param string the characters to search for
+ * @param start the start offset
+ *
+ * @return the next offset in the document that has one of the characters
+ * specified in <code>string</code>
+ */
+ protected int findOffsetToCharactersInString(char[] string, int start)
+ {
+ int offset = -1;
+ Document doc = getDocument();
+ Segment text = new Segment();
+ try
+ {
+ doc.getText(start, doc.getLength() - start, text);
+ int index = start;
+
+ searchLoop:
+ while (true)
+ {
+ char ch = text.next();
+ if (ch == Segment.DONE)
+ break;
+
+ for (int j = 0; j < string.length; ++j)
+ {
+ if (string[j] == ch)
+ {
+ offset = index;
+ break searchLoop;
+ }
+ }
+ index++;
+ }
+ }
+ catch (BadLocationException ex)
+ {
+ // Ignore this and return -1.
+ }
+ return offset;
+ }
+
+ protected int getClosestPositionTo(int pos, Position.Bias bias, Shape a,
+ int direction, Position.Bias[] biasRet,
+ int rowIndex, int x)
+ throws BadLocationException
+ {
+ // FIXME: Implement this properly. However, this looks like it might
+ // have been replaced by viewToModel.
+ return pos;
+ }
+
+ /**
+ * Returns the size that is used by this view (or it's child views) between
+ * <code>startOffset</code> and <code>endOffset</code>. If the child views
+ * implement the {@link TabableView} interface, then this is used to
+ * determine the span, otherwise we use the preferred span of the child
+ * views.
+ *
+ * @param startOffset the start offset
+ * @param endOffset the end offset
+ *
+ * @return the span used by the view between <code>startOffset</code> and
+ * <code>endOffset</cod>
+ */
+ protected float getPartialSize(int startOffset, int endOffset)
+ {
+ int startIndex = getViewIndex(startOffset, Position.Bias.Backward);
+ int endIndex = getViewIndex(endOffset, Position.Bias.Forward);
+ float span;
+ if (startIndex == endIndex)
+ {
+ View child = getView(startIndex);
+ if (child instanceof TabableView)
+ {
+ TabableView tabable = (TabableView) child;
+ span = tabable.getPartialSpan(startOffset, endOffset);
+ }
+ else
+ span = child.getPreferredSpan(X_AXIS);
+ }
+ else if (endIndex - startIndex == 1)
+ {
+ View child1 = getView(startIndex);
+ if (child1 instanceof TabableView)
+ {
+ TabableView tabable = (TabableView) child1;
+ span = tabable.getPartialSpan(startOffset, child1.getEndOffset());
+ }
+ else
+ span = child1.getPreferredSpan(X_AXIS);
+ View child2 = getView(endIndex);
+ if (child2 instanceof TabableView)
+ {
+ TabableView tabable = (TabableView) child2;
+ span += tabable.getPartialSpan(child2.getStartOffset(), endOffset);
+ }
+ else
+ span += child2.getPreferredSpan(X_AXIS);
+ }
+ else
+ {
+ // Start with the first view.
+ View child1 = getView(startIndex);
+ if (child1 instanceof TabableView)
+ {
+ TabableView tabable = (TabableView) child1;
+ span = tabable.getPartialSpan(startOffset, child1.getEndOffset());
+ }
+ else
+ span = child1.getPreferredSpan(X_AXIS);
+
+ // Add up the view spans between the start and the end view.
+ for (int i = startIndex + 1; i < endIndex; i++)
+ {
+ View child = getView(i);
+ span += child.getPreferredSpan(X_AXIS);
+ }
+
+ // Add the span of the last view.
+ View child2 = getView(endIndex);
+ if (child2 instanceof TabableView)
+ {
+ TabableView tabable = (TabableView) child2;
+ span += tabable.getPartialSpan(child2.getStartOffset(), endOffset);
+ }
+ else
+ span += child2.getPreferredSpan(X_AXIS);
+ }
+ return span;
+ }
+
+ /**
+ * Returns the location where the tabs are calculated from. This returns
+ * <code>0.0F</code> by default.
+ *
+ * @return the location where the tabs are calculated from
+ */
+ protected float getTabBase()
+ {
+ return 0.0F;
+ }
+
+ /**
+ * @specnote This method is specified to take a Row parameter, which is a
+ * private inner class of that class, which makes it unusable from
+ * application code. Also, this method seems to be replaced by
+ * {@link FlowStrategy#adjustRow(FlowView, int, int, int)}.
+ *
+ */
+ protected void adjustRow(Row r, int desiredSpan, int x)
+ {
+ }
+
+ /**
+ * @specnote This method's signature differs from the one defined in
+ * {@link View} and is therefore never called. It is probably there
+ * for historical reasons.
+ */
+ public View breakView(int axis, float len, Shape a)
+ {
+ // This method is not used.
+ return null;
+ }
+
+ /**
+ * @specnote This method's signature differs from the one defined in
+ * {@link View} and is therefore never called. It is probably there
+ * for historical reasons.
+ */
+ public int getBreakWeight(int axis, float len)
+ {
+ // This method is not used.
+ return 0;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/PasswordView.java b/libjava/classpath/javax/swing/text/PasswordView.java
new file mode 100644
index 000000000..62b141932
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/PasswordView.java
@@ -0,0 +1,254 @@
+/* PasswordView.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 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.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.JPasswordField;
+
+public class PasswordView
+ extends FieldView
+{
+ /**
+ * Buffer for putting the echo char into it and
+ * then using it to draw it into the view.
+ */
+ private char[] oneCharBuffer = new char[1];
+
+ public PasswordView(Element elem)
+ {
+ super(elem);
+ }
+
+ /**
+ * Draws one echo character at a given position.
+ *
+ * @param g the <code>Graphics</code> object to draw to
+ * @param x the x-position
+ * @param y the y-position
+ * @param ch the echo character
+ *
+ * @return the next x position right of the drawn character
+ */
+ protected int drawEchoCharacter(Graphics g, int x, int y, char ch)
+ {
+ // Update font metrics.
+ updateMetrics();
+
+ // Draw character.
+ oneCharBuffer[0] = ch;
+ g.drawChars(oneCharBuffer, 0, 1, x, y);
+
+ // Return new x position right of drawn character.
+ return x + metrics.charWidth(ch);
+ }
+
+ private char getEchoChar()
+ {
+ char ch = ((JPasswordField) getContainer()).getEchoChar();
+
+ if (ch == 0)
+ ch = '*';
+
+ return ch;
+ }
+
+ /**
+ * Draws selected text at a given position.
+ *
+ * @param g the <code>Graphics</code> object to draw to
+ * @param x the x-position
+ * @param y the y-position
+ * @param p0 the position of the first character to draw
+ * @param p1 the position of the first character not to draw
+ *
+ * @return the next x position right of the drawn character
+ */
+ protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
+ throws BadLocationException
+ {
+ // Update font metrics.
+ updateMetrics();
+
+ // Get echo character.
+ char ch = getEchoChar();
+
+ // Set color for selected text.
+ g.setColor(selectedColor);
+ g.setColor(Color.BLACK);
+
+ // Draw echo character using drawEchoCharacter() method.
+ for (int index = p0; index < p1; ++index)
+ x = drawEchoCharacter(g, x, y, ch);
+ return x;
+ }
+
+ /**
+ * Draws unselected text at a given position.
+ *
+ * @param g the <code>Graphics</code> object to draw to
+ * @param x the x-position of the start of the baseline
+ * @param y the y-position of the start of the baseline
+ * @param p0 the position of the first character to draw
+ * @param p1 the position of the first character not to draw
+ *
+ * @return the next x position right of the drawn character
+ */
+ protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
+ throws BadLocationException
+ {
+ // Update font metrics.
+ updateMetrics();
+
+ // Get echo character.
+ char ch = getEchoChar();
+
+ // Set color for unselected text.
+ g.setColor(unselectedColor);
+ g.setColor(Color.BLACK);
+
+ // Draw echo character using drawEchoCharacter() method.
+ for (int index = p0; index < p1; ++index)
+ x = drawEchoCharacter(g, x, y, ch);
+ return x;
+ }
+
+ /**
+ * Determines the preferred span for this view along an axis.
+ *
+ * @param axis to get the preferred span of
+ * @return the preferred span of the axis
+ */
+ public float getPreferredSpan(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException();
+
+ FontMetrics fm = getFontMetrics();
+
+ if (axis == Y_AXIS)
+ return fm.getHeight();
+
+ String text;
+ Element elem = getElement();
+
+ try
+ {
+ text = elem.getDocument().getText(elem.getStartOffset(),
+ elem.getEndOffset());
+ }
+ catch (BadLocationException e)
+ {
+ // This should never happen.
+ text = "";
+ }
+ return fm.stringWidth(text);
+ }
+
+ /**
+ * Provides a mapping from the document model coordinate space to the
+ * coordinate space of the view mapped to it.
+ *
+ * This method is overridden to provide a correct mapping with respect to the
+ * echo char and not to the real content.
+ *
+ * @param pos - the position to convert >= 0
+ * @param a - the allocated region to render into
+ * @param b - typesafe enumeration to indicate bias to a position in the model.
+ * @return the bounding box of the given position
+ * @throws BadLocationException if the given position does not
+ * represent a valid location in the associated document
+ */
+ public Shape modelToView(int pos, Shape a, Position.Bias b)
+ throws BadLocationException
+ {
+ Shape newAlloc = adjustAllocation(a);
+
+ // Ensure metrics are up-to-date.
+ updateMetrics();
+
+ // Get rectangle of the line containing position.
+ int lineIndex = getElement().getElementIndex(pos);
+ Rectangle rect = lineToRect(newAlloc, lineIndex);
+
+ // Get the rectangle for position.
+ Element line = getElement().getElement(lineIndex);
+ int lineStart = line.getStartOffset();
+ Segment segment = getLineBuffer();
+ segment.array = new char[pos - lineStart];
+ char echoChar = getEchoChar();
+ for (int i = 0; i < segment.array.length; ++i)
+ segment.array[i] = echoChar;
+ segment.offset = 0;
+ segment.count = segment.array.length;
+
+ 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;
+ }
+
+ /**
+ * Provides a mapping from the view coordinate space to the logical
+ * coordinate space of the model.
+ *
+ * @param fx - the X coordinate >= 0.0f
+ * @param fy - the Y coordinate >= 0.0f
+ * @param a - the allocated region to render into
+ * @param bias - typesafe enumeration to indicate bias to a position in the model.
+ * @return the location within the model that best represents
+ * the given point in the view
+ *
+ */
+ public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias)
+ {
+ // FIXME: This only provides a view->model mapping for the real text
+ // content and does not respect the echo char.
+ return super.viewToModel(fx, fy, a, bias);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java
new file mode 100644
index 000000000..070c760c0
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/PlainDocument.java
@@ -0,0 +1,292 @@
+/* PlainDocument.java --
+ Copyright (C) 2002, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.util.ArrayList;
+
+/**
+ * A simple document class which maps lines to {@link Element}s.
+ *
+ * @author Anthony Balkissoon (abalkiss@redhat.com)
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Roman Kennke (roman@kennke.org)
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+public class PlainDocument extends AbstractDocument
+{
+ private static final long serialVersionUID = 4758290289196893664L;
+
+ public static final String lineLimitAttribute = "lineLimit";
+ public static final String tabSizeAttribute = "tabSize";
+
+ /**
+ * The default root element of this document. This is made type Element
+ * because the RI seems to accept other types of elements as well from
+ * createDefaultRoot() (when overridden by a subclass).
+ */
+ private Element rootElement;
+
+ public PlainDocument()
+ {
+ this(new GapContent());
+ }
+
+ public PlainDocument(AbstractDocument.Content content)
+ {
+ super(content);
+ rootElement = createDefaultRoot();
+
+ // This property has been determined using a Mauve test.
+ putProperty("tabSize", new Integer(8));
+ }
+
+ private void reindex()
+ {
+ Element[] lines;
+ try
+ {
+ String str = content.getString(0, content.length());
+
+ ArrayList elts = new ArrayList();
+ int j = 0;
+ for (int i = str.indexOf('\n', 0); i != -1; i = str.indexOf('\n', i + 1))
+ {
+ elts.add(createLeafElement(rootElement, SimpleAttributeSet.EMPTY, j, i + 1));
+ j = i + 1;
+ }
+
+ if (j < content.length())
+ elts.add(createLeafElement(rootElement, SimpleAttributeSet.EMPTY, j, content.length()));
+
+ lines = new Element[elts.size()];
+ for (int i = 0; i < elts.size(); ++i)
+ lines[i] = (Element) elts.get(i);
+ }
+ catch (BadLocationException e)
+ {
+ lines = new Element[1];
+ lines[0] = createLeafElement(rootElement, SimpleAttributeSet.EMPTY, 0, 1);
+ }
+
+ ((BranchElement) rootElement).replace(0, rootElement.getElementCount(), lines);
+ }
+
+ protected AbstractDocument.AbstractElement createDefaultRoot()
+ {
+ BranchElement root =
+ (BranchElement) createBranchElement(null, null);
+
+ Element[] array = new Element[1];
+ array[0] = createLeafElement(root, null, 0, 1);
+ root.replace(0, 0, array);
+
+ return root;
+ }
+
+ protected void insertUpdate(DefaultDocumentEvent event,
+ AttributeSet attributes)
+ {
+
+ String text = null;
+ int offset = event.getOffset();
+ int length = event.getLength();
+ try
+ {
+ text = getText(offset, length);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError();
+ err.initCause(ex);
+ throw err;
+ }
+
+ boolean hasLineBreak = text.indexOf('\n') != -1;
+ boolean prevCharIsLineBreak = false;
+ try
+ {
+ prevCharIsLineBreak =
+ offset > 0 && getText(offset - 1, 1).charAt(0) == '\n';
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError();
+ err.initCause(ex);
+ throw err;
+ }
+ boolean lastCharIsLineBreak = text.charAt(text.length() - 1) == '\n';
+ int lineIndex = -1;
+ int lineStart = -1;
+ int lineEnd = -1;
+ Element[] removed = null;
+ BranchElement root = (BranchElement) rootElement;
+ boolean updateStructure = true;
+
+ if (prevCharIsLineBreak && ! lastCharIsLineBreak)
+ {
+ // We must fix the structure a little if the previous char
+ // is a linebreak and the last char isn't.
+ lineIndex = root.getElementIndex(offset - 1);
+ Element prevLine = root.getElement(lineIndex);
+ Element nextLine = root.getElement(lineIndex + 1);
+ lineStart = prevLine.getStartOffset();
+ lineEnd = nextLine.getEndOffset();
+ removed = new Element[]{ prevLine, nextLine };
+ }
+ else if (hasLineBreak)
+ {
+ lineIndex = root.getElementIndex(offset);
+ Element line = root.getElement(lineIndex);
+ lineStart = line.getStartOffset();
+ lineEnd = line.getEndOffset();
+ removed = new Element[]{ line };
+ }
+ else
+ {
+ updateStructure = false;
+ }
+
+ if (updateStructure)
+ {
+ // Break the lines between lineStart and lineEnd.
+ ArrayList lines = new ArrayList();
+ int len = lineEnd - lineStart;
+ try
+ {
+ text = getText(lineStart, len);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError();
+ err.initCause(ex);
+ throw err;
+ }
+ int prevLineBreak = 0;
+ int lineBreak = text.indexOf('\n');
+ do
+ {
+ lineBreak++;
+ lines.add(createLeafElement(root, null, lineStart + prevLineBreak,
+ lineStart + lineBreak));
+ prevLineBreak = lineBreak;
+ lineBreak = text.indexOf('\n', prevLineBreak);
+ } while (prevLineBreak < len);
+
+ // Update the element structure and prepare document event.
+ Element[] added = (Element[]) lines.toArray(new Element[lines.size()]);
+ event.addEdit(new ElementEdit(root, lineIndex, removed, added));
+ root.replace(lineIndex, removed.length, added);
+ }
+ super.insertUpdate(event, attributes);
+ }
+
+ protected void removeUpdate(DefaultDocumentEvent event)
+ {
+ super.removeUpdate(event);
+
+ // added and removed are Element arrays used to add an ElementEdit
+ // to the DocumentEvent if there were entire lines added or removed
+ // from the Document
+ Element[] added = new Element[1];
+ Element[] removed;
+ int p0 = event.getOffset();
+
+ // check if we must collapse some elements
+ int i1 = rootElement.getElementIndex(p0);
+ int i2 = rootElement.getElementIndex(p0 + event.getLength());
+ if (i1 != i2)
+ {
+ // If there were lines removed then we have to add an ElementEdit
+ // to the DocumentEvent so we set it up now by filling the Element
+ // arrays "removed" and "added" appropriately
+ removed = new Element [i2 - i1 + 1];
+ for (int i = i1; i <= i2; i++)
+ removed[i-i1] = rootElement.getElement(i);
+
+ int start = rootElement.getElement(i1).getStartOffset();
+ int end = rootElement.getElement(i2).getEndOffset();
+ added[0] = createLeafElement(rootElement,
+ SimpleAttributeSet.EMPTY,
+ start, end);
+
+ // Now create and add the ElementEdit
+ ElementEdit e = new ElementEdit(rootElement, i1, removed, added);
+ event.addEdit(e);
+
+ // collapse elements if the removal spans more than 1 line
+ ((BranchElement) rootElement).replace(i1, i2 - i1 + 1, added);
+ }
+ }
+
+ public Element getDefaultRootElement()
+ {
+ return rootElement;
+ }
+
+ public Element getParagraphElement(int pos)
+ {
+ Element root = getDefaultRootElement();
+ return root.getElement(root.getElementIndex(pos));
+ }
+
+ /**
+ * Inserts a string into the document. If the document property
+ * '<code>filterNewLines</code>' is set to <code>Boolean.TRUE</code>, then
+ * all newlines in the inserted string are replaced by space characters,
+ * otherwise the superclasses behaviour is executed.
+ *
+ * Inserting content causes a write lock to be acquired during this method
+ * call.
+ *
+ * @param offs the offset at which to insert the string
+ * @param str the string to be inserted
+ * @param atts the text attributes of the string to be inserted
+ *
+ * @throws BadLocationException
+ */
+ public void insertString(int offs, String str, AttributeSet atts)
+ throws BadLocationException
+ {
+ String string = str;
+ if (str != null && Boolean.TRUE.equals(getProperty("filterNewlines")))
+ string = str.replaceAll("\n", " ");
+ super.insertString(offs, string, atts);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java
new file mode 100644
index 000000000..16112fdb1
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/PlainView.java
@@ -0,0 +1,724 @@
+/* PlainView.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentEvent.ElementChange;
+
+public class PlainView extends View implements TabExpander
+{
+ Color selectedColor;
+ Color unselectedColor;
+
+ /**
+ * The color that is used to draw disabled text fields.
+ */
+ Color disabledColor;
+
+ /**
+ * While painting this is the textcomponent's current start index
+ * of the selection.
+ */
+ int selectionStart;
+
+ /**
+ * While painting this is the textcomponent's current end index
+ * of the selection.
+ */
+ int selectionEnd;
+
+ Font font;
+
+ /** The length of the longest line in the Document **/
+ float maxLineLength = -1;
+
+ /** The longest line in the Document **/
+ Element longestLine = null;
+
+ protected FontMetrics metrics;
+
+ /**
+ * The instance returned by {@link #getLineBuffer()}.
+ */
+ private transient Segment lineBuffer;
+
+ /**
+ * The base offset for tab calculations.
+ */
+ private int tabBase;
+
+ /**
+ * The tab size.
+ */
+ private int tabSize;
+
+ 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);
+ tabSize = getTabSize() * metrics.charWidth('m');
+ }
+ }
+
+ /**
+ * @since 1.4
+ */
+ protected Rectangle lineToRect(Shape a, int line)
+ {
+ // Ensure metrics are up-to-date.
+ updateMetrics();
+
+ Rectangle rect = a instanceof Rectangle ? (Rectangle) a : 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
+ {
+ // Ensure metrics are up-to-date.
+ updateMetrics();
+
+ Document document = getDocument();
+
+ // Get rectangle of the line containing position.
+ int lineIndex = getElement().getElementIndex(position);
+ Rectangle rect = lineToRect(a, lineIndex);
+ tabBase = rect.x;
+
+ // Get the rectangle for position.
+ Element line = getElement().getElement(lineIndex);
+ int lineStart = line.getStartOffset();
+ Segment segment = getLineBuffer();
+ document.getText(lineStart, position - lineStart, segment);
+ int xoffset = Utilities.getTabbedTextWidth(segment, metrics, tabBase,
+ this, lineStart);
+
+ // Calc the real rectangle.
+ rect.x += xoffset;
+ rect.width = 1;
+ rect.height = metrics.getHeight();
+
+ return rect;
+ }
+
+ /**
+ * Draws a line of text. The X and Y coordinates specify the start of
+ * the <em>baseline</em> of the line.
+ *
+ * @param lineIndex the index of the line
+ * @param g the graphics to use for drawing the text
+ * @param x the X coordinate of the baseline
+ * @param y the Y coordinate of the baseline
+ */
+ protected void drawLine(int lineIndex, Graphics g, int x, int y)
+ {
+ try
+ {
+ Element line = getElement().getElement(lineIndex);
+ int startOffset = line.getStartOffset();
+ int endOffset = line.getEndOffset() - 1;
+
+ if (selectionStart <= startOffset)
+ // Selection starts before the line ...
+ if (selectionEnd <= startOffset)
+ {
+ // end ends before the line: Draw completely unselected text.
+ drawUnselectedText(g, x, y, startOffset, endOffset);
+ }
+ else if (selectionEnd <= endOffset)
+ {
+ // and ends within the line: First part is selected,
+ // second is not.
+ x = drawSelectedText(g, x, y, startOffset, selectionEnd);
+ drawUnselectedText(g, x, y, selectionEnd, endOffset);
+ }
+ else
+ // and ends behind the line: Draw completely selected text.
+ drawSelectedText(g, x, y, startOffset, endOffset);
+ else if (selectionStart < endOffset)
+ // Selection starts within the line ..
+ if (selectionEnd < endOffset)
+ {
+ // and ends within it: First part unselected, second part
+ // selected, third part unselected.
+ x = drawUnselectedText(g, x, y, startOffset, selectionStart);
+ x = drawSelectedText(g, x, y, selectionStart, selectionEnd);
+ drawUnselectedText(g, x, y, selectionEnd, endOffset);
+ }
+ else
+ {
+ // and ends behind the line: First part unselected, second
+ // part selected.
+ x = drawUnselectedText(g, x, y, startOffset, selectionStart);
+ drawSelectedText(g, x, y, selectionStart, endOffset);
+ }
+ else
+ // Selection is behind this line: Draw completely unselected text.
+ drawUnselectedText(g, x, y, startOffset, endOffset);
+ }
+ catch (BadLocationException e)
+ {
+ AssertionError ae = new AssertionError("Unexpected bad location");
+ ae.initCause(e);
+ throw ae;
+ }
+ }
+
+ protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
+ throws BadLocationException
+ {
+ g.setColor(selectedColor);
+ Segment segment = getLineBuffer();
+ getDocument().getText(p0, p1 - p0, segment);
+ return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset);
+ }
+
+ /**
+ * Draws a chunk of unselected text.
+ *
+ * @param g the graphics to use for drawing the text
+ * @param x the X coordinate of the baseline
+ * @param y the Y coordinate of the baseline
+ * @param p0 the start position in the text model
+ * @param p1 the end position in the text model
+ *
+ * @return the X location of the end of the range
+ *
+ * @throws BadLocationException if <code>p0</code> or <code>p1</code> are
+ * invalid
+ */
+ protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
+ throws BadLocationException
+ {
+ JTextComponent textComponent = (JTextComponent) getContainer();
+ if (textComponent.isEnabled())
+ g.setColor(unselectedColor);
+ else
+ g.setColor(disabledColor);
+
+ Segment segment = getLineBuffer();
+ getDocument().getText(p0, p1 - p0, segment);
+ return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset);
+ }
+
+ public void paint(Graphics g, Shape s)
+ {
+ // Ensure metrics are up-to-date.
+ updateMetrics();
+
+ JTextComponent textComponent = (JTextComponent) getContainer();
+
+ selectedColor = textComponent.getSelectedTextColor();
+ unselectedColor = textComponent.getForeground();
+ disabledColor = textComponent.getDisabledTextColor();
+ selectionStart = textComponent.getSelectionStart();
+ selectionEnd = textComponent.getSelectionEnd();
+
+ Rectangle rect = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
+ tabBase = rect.x;
+
+ // FIXME: Text may be scrolled.
+ Document document = textComponent.getDocument();
+ Element root = getElement();
+ int height = metrics.getHeight();
+
+ // For layered highlighters we need to paint the layered highlights
+ // before painting any text.
+ LayeredHighlighter hl = null;
+ Highlighter h = textComponent.getHighlighter();
+ if (h instanceof LayeredHighlighter)
+ hl = (LayeredHighlighter) h;
+
+ int count = root.getElementCount();
+
+ // Determine first and last line inside the clip.
+ Rectangle clip = g.getClipBounds();
+ SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, rect.height,
+ clip);
+ int line0 = (clip.y - rect.y) / height;
+ line0 = Math.max(0, Math.min(line0, count - 1));
+ int line1 = (clip.y + clip.height - rect.y) / height;
+ line1 = Math.max(0, Math.min(line1, count - 1));
+ int y = rect.y + metrics.getAscent() + height * line0;
+ for (int i = line0; i <= line1; i++)
+ {
+ if (hl != null)
+ {
+ Element lineEl = root.getElement(i);
+ // Exclude the trailing newline from beeing highlighted.
+ if (i == count)
+ hl.paintLayeredHighlights(g, lineEl.getStartOffset(),
+ lineEl.getEndOffset(), s, textComponent,
+ this);
+ else
+ hl.paintLayeredHighlights(g, lineEl.getStartOffset(),
+ lineEl.getEndOffset() - 1, s,
+ textComponent, this);
+ }
+ drawLine(i, g, rect.x, y);
+ y += height;
+ }
+ }
+
+ /**
+ * Returns the tab size of a tab. Checks the Document's
+ * properties for PlainDocument.tabSizeAttribute and returns it if it is
+ * defined, otherwise returns 8.
+ *
+ * @return the tab size.
+ */
+ protected int getTabSize()
+ {
+ Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute);
+ if (tabSize == null)
+ return 8;
+ return ((Integer)tabSize).intValue();
+ }
+
+ /**
+ * 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)
+ {
+ float next = x;
+ if (tabSize != 0)
+ {
+ int numTabs = (((int) x) - tabBase) / tabSize;
+ next = tabBase + (numTabs + 1) * tabSize;
+ }
+ return next;
+ }
+
+ /**
+ * Returns the length of the longest line, used for getting the span
+ * @return the length of the longest line
+ */
+ float determineMaxLineLength()
+ {
+ // if the longest line is cached, return the cached value
+ if (maxLineLength != -1)
+ return maxLineLength;
+
+ // otherwise we have to go through all the lines and find it
+ Element el = getElement();
+ Segment seg = getLineBuffer();
+ float span = 0;
+ for (int i = 0; i < el.getElementCount(); i++)
+ {
+ Element child = el.getElement(i);
+ int start = child.getStartOffset();
+ int end = child.getEndOffset() - 1;
+ try
+ {
+ el.getDocument().getText(start, end - start, seg);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError ae = new AssertionError("Unexpected bad location");
+ ae.initCause(ex);
+ throw ae;
+ }
+
+ if (seg == null || seg.array == null || seg.count == 0)
+ continue;
+
+ int width = metrics.charsWidth(seg.array, seg.offset, seg.count);
+ if (width > span)
+ {
+ longestLine = child;
+ span = width;
+ }
+ }
+ maxLineLength = span;
+ return maxLineLength;
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException();
+
+ // make sure we have the metrics
+ updateMetrics();
+
+ Element el = getElement();
+ float span;
+
+ switch (axis)
+ {
+ case X_AXIS:
+ span = determineMaxLineLength();
+ break;
+ case Y_AXIS:
+ default:
+ span = metrics.getHeight() * el.getElementCount();
+ break;
+ }
+
+ return span;
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ Rectangle rec = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ tabBase = rec.x;
+
+ int pos;
+ if ((int) y < rec.y)
+ // Above our area vertically. Return start offset.
+ pos = getStartOffset();
+ else if ((int) y > rec.y + rec.height)
+ // Below our area vertically. Return end offset.
+ pos = getEndOffset() - 1;
+ else
+ {
+ // Inside the allocation vertically. Determine line and X offset.
+ Document doc = getDocument();
+ Element root = doc.getDefaultRootElement();
+ int line = Math.abs(((int) y - rec.y) / metrics.getHeight());
+ if (line >= root.getElementCount())
+ pos = getEndOffset() - 1;
+ else
+ {
+ Element lineEl = root.getElement(line);
+ if (x < rec.x)
+ // To the left of the allocation area.
+ pos = lineEl.getStartOffset();
+ else if (x > rec.x + rec.width)
+ // To the right of the allocation area.
+ pos = lineEl.getEndOffset() - 1;
+ else
+ {
+ try
+ {
+ int p0 = lineEl.getStartOffset();
+ int p1 = lineEl.getEndOffset();
+ Segment s = new Segment();
+ doc.getText(p0, p1 - p0, s);
+ tabBase = rec.x;
+ pos = p0 + Utilities.getTabbedTextOffset(s, metrics,
+ tabBase, (int) x,
+ this, p0);
+ }
+ catch (BadLocationException ex)
+ {
+ // Should not happen.
+ pos = -1;
+ }
+ }
+
+ }
+ }
+ // Bias is always forward.
+ b[0] = Position.Bias.Forward;
+ return pos;
+ }
+
+ /**
+ * Since insertUpdate and removeUpdate each deal with children
+ * Elements being both added and removed, they both have to perform
+ * the same checks. So they both simply call this method.
+ * @param changes the DocumentEvent for the changes to the Document.
+ * @param a the allocation of the View.
+ * @param f the ViewFactory to use for rebuilding.
+ */
+ protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ // This happens during initialization.
+ if (metrics == null)
+ {
+ updateMetrics();
+ preferenceChanged(null, true, true);
+ return;
+ }
+
+ Element element = getElement();
+
+ // Find longest line if it hasn't been initialized yet.
+ if (longestLine == null)
+ findLongestLine(0, element.getElementCount() - 1);
+
+ ElementChange change = changes.getChange(element);
+ if (changes.getType() == DocumentEvent.EventType.INSERT)
+ {
+ // Handles character/line insertion.
+
+ // Determine if lines have been added. In this case we repaint
+ // differently.
+ boolean linesAdded = true;
+ if (change == null)
+ linesAdded = false;
+
+ // Determine the start line.
+ int start;
+ if (linesAdded)
+ start = change.getIndex();
+ else
+ start = element.getElementIndex(changes.getOffset());
+
+ // Determine the length of the updated region.
+ int length = 0;
+ if (linesAdded)
+ length = change.getChildrenAdded().length - 1;
+
+ // Update the longest line and length.
+ int oldMaxLength = (int) maxLineLength;
+ if (longestLine.getEndOffset() < changes.getOffset()
+ || longestLine.getStartOffset() > changes.getOffset()
+ + changes.getLength())
+ {
+ findLongestLine(start, start + length);
+ }
+ else
+ {
+ findLongestLine(0, element.getElementCount() - 1);
+ }
+
+ // Trigger a preference change so that the layout gets updated
+ // correctly.
+ preferenceChanged(null, maxLineLength != oldMaxLength, linesAdded);
+
+ // Damage the updated line range.
+ int endLine = start;
+ if (linesAdded)
+ endLine = element.getElementCount() - 1;
+ damageLineRange(start, endLine, a, getContainer());
+
+ }
+ else
+ {
+ // Handles character/lines removals.
+
+ // Update the longest line and length and trigger preference changed.
+ int oldMaxLength = (int) maxLineLength;
+ if (change != null)
+ {
+ // Line(s) have been removed.
+ findLongestLine(0, element.getElementCount() - 1);
+ preferenceChanged(null, maxLineLength != oldMaxLength, true);
+ }
+ else
+ {
+ // No line has been removed.
+ int lineNo = getElement().getElementIndex(changes.getOffset());
+ Element line = getElement().getElement(lineNo);
+ if (longestLine == line)
+ {
+ findLongestLine(0, element.getElementCount() - 1);
+ preferenceChanged(null, maxLineLength != oldMaxLength, false);
+ }
+ damageLineRange(lineNo, lineNo, a, getContainer());
+ }
+ }
+ }
+
+ /**
+ * This method is called when something is inserted into the Document
+ * that this View is displaying.
+ *
+ * @param changes the DocumentEvent for the changes.
+ * @param a the allocation of the View
+ * @param f the ViewFactory used to rebuild
+ */
+ public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ updateDamage(changes, a, f);
+ }
+
+ /**
+ * This method is called when something is removed from the Document
+ * that this View is displaying.
+ *
+ * @param changes the DocumentEvent for the changes.
+ * @param a the allocation of the View
+ * @param f the ViewFactory used to rebuild
+ */
+ public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ updateDamage(changes, a, f);
+ }
+
+ /**
+ * This method is called when attributes were changed in the
+ * Document in a location that this view is responsible for.
+ */
+ public void changedUpdate (DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ updateDamage(changes, a, f);
+ }
+
+ /**
+ * Repaint the given line range. This is called from insertUpdate,
+ * changedUpdate, and removeUpdate when no new lines were added
+ * and no lines were removed, to repaint the line that was
+ * modified.
+ *
+ * @param line0 the start of the range
+ * @param line1 the end of the range
+ * @param a the rendering region of the host
+ * @param host the Component that uses this View (used to call repaint
+ * on that Component)
+ *
+ * @since 1.4
+ */
+ protected void damageLineRange (int line0, int line1, Shape a, Component host)
+ {
+ if (a == null)
+ return;
+
+ Rectangle rec0 = lineToRect(a, line0);
+ Rectangle rec1 = lineToRect(a, line1);
+
+ if (rec0 == null || rec1 == null)
+ // something went wrong, repaint the entire host to be safe
+ host.repaint();
+ else
+ {
+ Rectangle repaintRec = SwingUtilities.computeUnion(rec0.x, rec0.y,
+ rec0.width,
+ rec0.height, rec1);
+ host.repaint(repaintRec.x, repaintRec.y, repaintRec.width,
+ repaintRec.height);
+ }
+ }
+
+ /**
+ * Provides a {@link Segment} object, that can be used to fetch text from
+ * the document.
+ *
+ * @returna {@link Segment} object, that can be used to fetch text from
+ * the document
+ */
+ protected final Segment getLineBuffer()
+ {
+ if (lineBuffer == null)
+ lineBuffer = new Segment();
+ return lineBuffer;
+ }
+
+ /**
+ * Finds and updates the longest line in the view inside an interval of
+ * lines.
+ *
+ * @param start the start of the search interval
+ * @param end the end of the search interval
+ */
+ private void findLongestLine(int start, int end)
+ {
+ for (int i = start; i <= end; i++)
+ {
+ int w = getLineLength(i);
+ if (w > maxLineLength)
+ {
+ maxLineLength = w;
+ longestLine = getElement().getElement(i);
+ }
+ }
+ }
+
+ /**
+ * Determines the length of the specified line.
+ *
+ * @param line the number of the line
+ *
+ * @return the length of the line in pixels
+ */
+ private int getLineLength(int line)
+ {
+ Element lineEl = getElement().getElement(line);
+ Segment buffer = getLineBuffer();
+ try
+ {
+ Document doc = getDocument();
+ doc.getText(lineEl.getStartOffset(),
+ lineEl.getEndOffset() - lineEl.getStartOffset() - 1,
+ buffer);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError("Unexpected bad location");
+ err.initCause(ex);
+ throw err;
+ }
+
+ return Utilities.getTabbedTextWidth(buffer, metrics, tabBase, this,
+ lineEl.getStartOffset());
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/Position.java b/libjava/classpath/javax/swing/text/Position.java
new file mode 100644
index 000000000..56c8b6e68
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Position.java
@@ -0,0 +1,62 @@
+/* Position.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+public interface Position
+{
+ static final class Bias
+ {
+ public static final Bias Backward = new Bias("Backward");
+ public static final Bias Forward = new Bias("Forward");
+
+ private String name;
+
+ private Bias(String n)
+ {
+ name = n;
+ }
+
+ public String toString()
+ {
+ return name;
+ }
+ }
+
+ int getOffset();
+}
diff --git a/libjava/classpath/javax/swing/text/Segment.java b/libjava/classpath/javax/swing/text/Segment.java
new file mode 100644
index 000000000..7486ab338
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Segment.java
@@ -0,0 +1,297 @@
+/* Segment.java --
+ Copyright (C) 2002, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.text.CharacterIterator;
+
+/**
+ * A text fragment represented by a sequence of characters stored in an array.
+ */
+public class Segment implements Cloneable, CharacterIterator
+{
+ private boolean partialReturn;
+
+ /** The current index. */
+ private int current;
+
+ /** Storage for the characters (may contain additional characters). */
+ public char[] array;
+
+ /** The number of characters in the segment. */
+ public int count;
+
+ /** The offset of the first character in the segment. */
+ public int offset;
+
+ /**
+ * Creates a new <code>Segment</code>.
+ */
+ public Segment()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a new <code>Segment</code>.
+ *
+ * @param array the underlying character data.
+ * @param offset the offset of the first character in the segment.
+ * @param count the number of characters in the segment.
+ */
+ public Segment(char[] array, int offset, int count)
+ {
+ this.array = array;
+ this.offset = offset;
+ this.count = count;
+ }
+
+ /**
+ * Clones the segment (note that the underlying character array is not cloned,
+ * just the reference to it).
+ *
+ * @return A clone of the segment.
+ */
+ public Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the character at the current index. If the segment consists of
+ * zero characters, or the current index has passed the end of the
+ * characters in the segment, this method returns {@link #DONE}.
+ *
+ * @return The character at the current index.
+ */
+ public char current()
+ {
+ if (count == 0
+ || current >= getEndIndex())
+ return DONE;
+
+ return array[current];
+ }
+
+ /**
+ * Sets the current index to the first character in the segment and returns
+ * that character. If the segment contains zero characters, this method
+ * returns {@link #DONE}.
+ *
+ * @return The first character in the segment, or {@link #DONE} if the
+ * segment contains zero characters.
+ */
+ public char first()
+ {
+ if (count == 0)
+ return DONE;
+
+ current = getBeginIndex();
+ return array[current];
+ }
+
+ /**
+ * Returns the index of the first character in the segment.
+ *
+ * @return The index of the first character.
+ */
+ public int getBeginIndex()
+ {
+ return offset;
+ }
+
+ /**
+ * Returns the end index for the segment (one position beyond the last
+ * character in the segment - note that this can be outside the range of the
+ * underlying character array).
+ *
+ * @return The end index for the segment.
+ */
+ public int getEndIndex()
+ {
+ return offset + count;
+ }
+
+ /**
+ * Returns the index of the current character in the segment.
+ *
+ * @return The index of the current character.
+ */
+ public int getIndex()
+ {
+ return current;
+ }
+
+ /**
+ * Sets the current index to point to the last character in the segment and
+ * returns that character. If the segment contains zero characters, the
+ * current index is set to {@link #getEndIndex()} and this method returns
+ * {@link #DONE}.
+ *
+ * @return The last character in the segment, or {@link #DONE} if the
+ * segment contains zero characters.
+ */
+ public char last()
+ {
+ if (count == 0)
+ {
+ current = getEndIndex();
+ return DONE;
+ }
+
+ current = getEndIndex() - 1;
+ return array[current];
+ }
+
+ /**
+ * Sets the current index to point to the next character in the segment and
+ * returns that character. If the next character position is past the end of
+ * the segment, the index is set to {@link #getEndIndex()} and the method
+ * returns {@link #DONE}. If the segment contains zero characters, this
+ * method returns {@link #DONE}.
+ *
+ * @return The next character in the segment or {@link #DONE} (if the next
+ * character position is past the end of the segment or if the
+ * segment contains zero characters).
+ */
+ public char next()
+ {
+ if (count == 0)
+ return DONE;
+
+ if ((current + 1) >= getEndIndex())
+ {
+ current = getEndIndex();
+ return DONE;
+ }
+
+ current++;
+ return array[current];
+ }
+
+ /**
+ * Sets the current index to point to the previous character in the segment
+ * and returns that character. If the current index is equal to
+ * {@link #getBeginIndex()}, or if the segment contains zero characters, this
+ * method returns {@link #DONE}.
+ *
+ * @return The previous character in the segment or {@link #DONE} (if the
+ * current character position is at the beginning of the segment or
+ * if the segment contains zero characters).
+ */
+ public char previous()
+ {
+ if (count == 0
+ || current == getBeginIndex())
+ return DONE;
+
+ current--;
+ return array[current];
+ }
+
+ /**
+ * Sets the current index and returns the character at that position (or
+ * {@link #DONE} if the index is equal to {@link #getEndIndex()}.
+ *
+ * @param position the current position.
+ *
+ * @return The character at the specified <code>position</code>, or
+ * {@link #DONE} if <code>position</code> is equal to
+ * {@link #getEndIndex()}.
+ *
+ * @throws IllegalArgumentException if <code>position</code> is not in the
+ * range {@link #getBeginIndex()} to {@link #getEndIndex()}.
+ */
+ public char setIndex(int position)
+ {
+ if (position < getBeginIndex()
+ || position > getEndIndex())
+ throw new IllegalArgumentException("position: " + position
+ + ", beginIndex: " + getBeginIndex()
+ + ", endIndex: " + getEndIndex()
+ + ", text: " + toString());
+
+ current = position;
+
+ if (position == getEndIndex())
+ return DONE;
+
+ return array[current];
+ }
+
+ /**
+ * Returns a <code>String</code> containing the same characters as this
+ * <code>Segment</code>.
+ *
+ * @return A <code>String</code> containing the same characters as this
+ * <code>Segment</code>.
+ */
+ public String toString()
+ {
+ return (array != null) ? new String(array, offset, count) : "";
+ }
+
+ /**
+ * Sets the partial return flag.
+ *
+ * @param p the new value of the flag.
+ *
+ * @since 1.4
+ */
+ public void setPartialReturn(boolean p)
+ {
+ partialReturn = p;
+ }
+
+ /**
+ * Returns the partial return flag.
+ *
+ * @return The partial return flag.
+ * @since 1.4
+ */
+ public boolean isPartialReturn()
+ {
+ return partialReturn;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java
new file mode 100644
index 000000000..02299019b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java
@@ -0,0 +1,415 @@
+/* SimpleAttributeSet.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * A set of attributes.
+ */
+public class SimpleAttributeSet
+ implements MutableAttributeSet, Serializable, Cloneable
+{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 8267656273837665219L;
+
+ /**
+ * An empty attribute set.
+ */
+ public static final AttributeSet EMPTY = new EmptyAttributeSet();
+
+ /** Storage for the attributes. */
+ Hashtable tab;
+
+ /**
+ * Creates a new attribute set that is initially empty.
+ */
+ public SimpleAttributeSet()
+ {
+ tab = new Hashtable();
+ }
+
+ /**
+ * Creates a new <code>SimpleAttributeSet</code> with the same attributes
+ * and resolve parent as the specified set.
+ *
+ * @param a the attributes (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ */
+ public SimpleAttributeSet(AttributeSet a)
+ {
+ tab = new Hashtable();
+ addAttributes(a);
+ }
+
+ /**
+ * Adds an attribute with the given <code>name</code> and <code>value</code>
+ * to the set. If the set already contains an attribute with the given
+ * <code>name</code>, the attribute value is updated.
+ *
+ * @param name the attribute name (<code>null</code> not permitted).
+ * @param value the value (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ */
+ public void addAttribute(Object name, Object value)
+ {
+ tab.put(name, value);
+ }
+
+ /**
+ * Adds all the attributes from <code>attributes</code> to this set.
+ *
+ * @param attributes the set of attributes to add (<code>null</code> not
+ * permitted).
+ *
+ * @throws NullPointerException if <code>attributes</code> is
+ * <code>null</code>.
+ */
+ 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);
+ }
+ }
+
+ /**
+ * Returns a clone of the attribute set.
+ *
+ * @return A clone of the attribute set.
+ */
+ public Object clone()
+ {
+ SimpleAttributeSet attr = null;
+ try
+ {
+ attr = (SimpleAttributeSet) super.clone();
+ attr.tab = (Hashtable) tab.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ assert false;
+ }
+ return attr;
+ }
+
+ /**
+ * Returns true if the given name and value represent an attribute
+ * found either in this AttributeSet or in its resolve parent hierarchy.
+ * @param name the key for the attribute
+ * @param value the value for the attribute
+ * @return true if the attribute is found here or in this set's resolve
+ * parent hierarchy
+ */
+ public boolean containsAttribute(Object name, Object value)
+ {
+ if (value == null)
+ throw new NullPointerException("Null 'value' argument.");
+ if (tab.containsKey(name))
+ return tab.get(name).equals(value);
+ else
+ {
+ AttributeSet p = getResolveParent();
+ if (p != null)
+ return p.containsAttribute(name, value);
+ else
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the given name and value are found in this AttributeSet.
+ * Does not check the resolve parent.
+ * @param name the key for the attribute
+ * @param value the value for the attribute
+ * @return true if the attribute is found in this AttributeSet
+ */
+ boolean containsAttributeLocally(Object name, Object value)
+ {
+ return tab.containsKey(name)
+ && tab.get(name).equals(value);
+ }
+
+ /**
+ * Returns <code>true</code> of this <code>AttributeSet</code> contains all
+ * of the specified <code>attributes</code>.
+ *
+ * @param attributes the requested attributes
+ *
+ * @return <code>true</code> of this <code>AttributeSet</code> contains all
+ * of the specified <code>attributes</code>
+ */
+ 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;
+ }
+
+ /**
+ * Creates and returns a copy of this <code>AttributeSet</code>.
+ *
+ * @return a copy of this <code>AttributeSet</code>
+ */
+ public AttributeSet copyAttributes()
+ {
+ return (AttributeSet) clone();
+ }
+
+ /**
+ * Checks this set for equality with an arbitrary object.
+ *
+ * @param obj the object (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if this set is equal to <code>obj</code>, and
+ * <code>false</code> otherwise.
+ */
+ public boolean equals(Object obj)
+ {
+ return
+ (obj instanceof AttributeSet)
+ && this.isEqual((AttributeSet) obj);
+ }
+
+ /**
+ * Returns the value of the specified attribute, or <code>null</code> if
+ * there is no attribute with that name. If the attribute is not defined
+ * directly in this set, the parent hierarchy (if there is one) will be
+ * used.
+ *
+ * @param name the attribute (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ public Object getAttribute(Object name)
+ {
+ Object val = tab.get(name);
+ if (val != null)
+ return val;
+
+ AttributeSet p = getResolveParent();
+ if (p != null)
+ return p.getAttribute(name);
+
+ return null;
+ }
+
+ /**
+ * Returns the number of attributes stored in this set, plus 1 if a parent
+ * has been specified (the reference to the parent is stored as a special
+ * attribute). The attributes stored in the parent do NOT contribute
+ * to the count.
+ *
+ * @return The attribute count.
+ */
+ public int getAttributeCount()
+ {
+ return tab.size();
+ }
+
+ /**
+ * Returns an enumeration of the attribute names.
+ *
+ * @return An enumeration of the attribute names.
+ */
+ public Enumeration<?> getAttributeNames()
+ {
+ return tab.keys();
+ }
+
+ /**
+ * Returns the resolving parent.
+ *
+ * @return The resolving parent (possibly <code>null</code>).
+ *
+ * @see #setResolveParent(AttributeSet)
+ */
+ public AttributeSet getResolveParent()
+ {
+ return (AttributeSet) tab.get(ResolveAttribute);
+ }
+
+ /**
+ * Returns a hash code for this instance.
+ *
+ * @return A hash code.
+ */
+ public int hashCode()
+ {
+ return tab.hashCode();
+ }
+
+ /**
+ * Returns <code>true</code> if the given attribute is defined in this set,
+ * and <code>false</code> otherwise. The parent attribute set is not
+ * checked.
+ *
+ * @param attrName the attribute name (<code>null</code> not permitted).
+ */
+ public boolean isDefined(Object attrName)
+ {
+ return tab.containsKey(attrName);
+ }
+
+ /**
+ * Returns <code>true</code> if the set contains no attributes, and
+ * <code>false</code> otherwise. Note that the resolving parent is
+ * stored as an attribute, so this method will return <code>false</code> if
+ * a resolving parent is set.
+ *
+ * @return <code>true</code> if the set contains no attributes, and
+ * <code>false</code> otherwise.
+ */
+ public boolean isEmpty()
+ {
+ return tab.isEmpty();
+ }
+
+ /**
+ * Returns true if the given set has the same number of attributes
+ * as this set and <code>containsAttributes(attr)</code> returns
+ * <code>true</code>.
+ *
+ * @param attr the attribute set (<code>null</code> not permitted).
+ *
+ * @return A boolean.
+ *
+ * @throws NullPointerException if <code>attr</code> is <code>null</code>.
+ */
+ public boolean isEqual(AttributeSet attr)
+ {
+ return getAttributeCount() == attr.getAttributeCount()
+ && this.containsAttributes(attr);
+ }
+
+ /**
+ * Removes the attribute with the specified <code>name</code>, if this
+ * attribute is defined. This method will only remove an attribute from
+ * this set, not from the resolving parent.
+ *
+ * @param name the attribute name (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ public void removeAttribute(Object name)
+ {
+ tab.remove(name);
+ }
+
+ /**
+ * Removes attributes from this set if they are found in the
+ * given set. Only attributes whose key AND value are removed.
+ * Removes attributes only from this set, not from the resolving parent.
+ * Since the resolving parent is stored as an attribute, if
+ * <code>attributes</code> has the same resolving parent as this set, the
+ * parent will be removed from this set.
+ *
+ * @param attributes the attributes (<code>null</code> not permitted).
+ */
+ public void removeAttributes(AttributeSet attributes)
+ {
+ Enumeration e = attributes.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ Object name = e.nextElement();
+ Object val = attributes.getAttribute(name);
+ if (containsAttributeLocally(name, val))
+ removeAttribute(name);
+ }
+ }
+
+ /**
+ * Removes the attributes listed in <code>names</code>.
+ *
+ * @param names the attribute names (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>names</code> is <code>null</code>
+ * or contains any <code>null</code> values.
+ */
+ public void removeAttributes(Enumeration<?> names)
+ {
+ while (names.hasMoreElements())
+ {
+ removeAttribute(names.nextElement());
+ }
+ }
+
+ /**
+ * Sets the reolving parent for this set. When looking up an attribute, if
+ * it is not found in this set, then the resolving parent is also used for
+ * the lookup.
+ * <p>
+ * Note that the parent is stored as an attribute, and will contribute 1 to
+ * the count returned by {@link #getAttributeCount()}.
+ *
+ * @param parent the parent attribute set (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>parent</code> is <code>null</code>.
+ *
+ * @see #setResolveParent(AttributeSet)
+ */
+ public void setResolveParent(AttributeSet parent)
+ {
+ addAttribute(ResolveAttribute, parent);
+ }
+
+ /**
+ * Returns a string representation of this instance, typically used for
+ * debugging purposes.
+ *
+ * @return A string representation of this instance.
+ */
+ public String toString()
+ {
+ return tab.toString();
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/StringContent.java b/libjava/classpath/javax/swing/text/StringContent.java
new file mode 100644
index 000000000..a017de1c9
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/StringContent.java
@@ -0,0 +1,569 @@
+/* StringContent.java --
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.CannotRedoException;
+import javax.swing.undo.CannotUndoException;
+import javax.swing.undo.UndoableEdit;
+
+/**
+ * An implementation of the <code>AbstractDocument.Content</code>
+ * interface useful for small documents or debugging. The character
+ * content is a simple character array. It's not really efficient.
+ *
+ * <p>Do not use this class for large size.</p>
+ */
+public final class StringContent
+ implements AbstractDocument.Content, Serializable
+{
+ /**
+ * Stores a reference to a mark that can be resetted to the original value
+ * after a mark has been moved. This is used for undoing actions.
+ */
+ private class UndoPosRef
+ {
+ /**
+ * The mark that might need to be reset.
+ */
+ private Mark mark;
+
+ /**
+ * The original offset to reset the mark to.
+ */
+ private int undoOffset;
+
+ /**
+ * Creates a new UndoPosRef.
+ *
+ * @param m the mark
+ */
+ UndoPosRef(Mark m)
+ {
+ mark = m;
+ undoOffset = mark.mark;
+ }
+
+ /**
+ * Resets the position of the mark to the value that it had when
+ * creating this UndoPosRef.
+ */
+ void reset()
+ {
+ mark.mark = undoOffset;
+ }
+ }
+
+ /**
+ * Holds a mark into the buffer that is used by StickyPosition to find
+ * the actual offset of the position. This is pulled out of the
+ * GapContentPosition object so that the mark and position can be handled
+ * independently, and most important, so that the StickyPosition can
+ * be garbage collected while we still hold a reference to the Mark object.
+ */
+ private class Mark
+ {
+ /**
+ * The actual mark into the buffer.
+ */
+ int mark;
+
+
+ /**
+ * The number of GapContentPosition object that reference this mark. If
+ * it reaches zero, it get's deleted by
+ * {@link StringContent#garbageCollect()}.
+ */
+ int refCount;
+
+ /**
+ * Creates a new Mark object for the specified offset.
+ *
+ * @param offset the offset
+ */
+ Mark(int offset)
+ {
+ mark = offset;
+ }
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 4755994433709540381L;
+
+ // This is package-private to avoid an accessor method.
+ char[] content;
+
+ private int count;
+
+ /**
+ * Holds the marks for the positions.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Vector marks;
+
+ private class InsertUndo extends AbstractUndoableEdit
+ {
+ private int start;
+
+ private int length;
+
+ private String redoContent;
+
+ private Vector positions;
+
+ public InsertUndo(int start, int length)
+ {
+ super();
+ this.start = start;
+ this.length = length;
+ }
+
+ public void undo()
+ {
+ super.undo();
+ try
+ {
+ if (marks != null)
+ positions = getPositionsInRange(null, start, length);
+ redoContent = getString(start, length);
+ remove(start, length);
+ }
+ catch (BadLocationException b)
+ {
+ throw new CannotUndoException();
+ }
+ }
+
+ public void redo()
+ {
+ super.redo();
+ try
+ {
+ insertString(start, redoContent);
+ redoContent = null;
+ if (positions != null)
+ {
+ updateUndoPositions(positions);
+ positions = null;
+ }
+ }
+ catch (BadLocationException b)
+ {
+ throw new CannotRedoException();
+ }
+ }
+ }
+
+ private class RemoveUndo extends AbstractUndoableEdit
+ {
+ private int start;
+ private int len;
+ private String undoString;
+
+ Vector positions;
+
+ public RemoveUndo(int start, String str)
+ {
+ super();
+ this.start = start;
+ len = str.length();
+ this.undoString = str;
+ if (marks != null)
+ positions = getPositionsInRange(null, start, str.length());
+ }
+
+ public void undo()
+ {
+ super.undo();
+ try
+ {
+ StringContent.this.insertString(this.start, this.undoString);
+ if (positions != null)
+ {
+ updateUndoPositions(positions);
+ positions = null;
+ }
+ undoString = null;
+ }
+ catch (BadLocationException bad)
+ {
+ throw new CannotUndoException();
+ }
+ }
+
+ public void redo()
+ {
+ super.redo();
+ try
+ {
+ undoString = getString(start, len);
+ if (marks != null)
+ positions = getPositionsInRange(null, start, len);
+ remove(this.start, len);
+ }
+ catch (BadLocationException bad)
+ {
+ throw new CannotRedoException();
+ }
+ }
+ }
+
+ private class StickyPosition implements Position
+ {
+ Mark mark;
+
+ public StickyPosition(int offset)
+ {
+ // Try to make space.
+ garbageCollect();
+
+ mark = new Mark(offset);
+ mark.refCount++;
+ marks.add(mark);
+
+ new WeakReference(this, queueOfDeath);
+ }
+
+ /**
+ * Should be >=0.
+ */
+ public int getOffset()
+ {
+ return mark.mark;
+ }
+ }
+
+ /**
+ * Used in {@link #remove(int,int)}.
+ */
+ private static final char[] EMPTY = new char[0];
+
+ /**
+ * Queues all references to GapContentPositions that are about to be
+ * GC'ed. This is used to remove the corresponding marks from the
+ * positionMarks array if the number of references to that mark reaches zero.
+ *
+ * This is package private to avoid accessor synthetic methods.
+ */
+ ReferenceQueue queueOfDeath;
+
+ /**
+ * Creates a new instance containing the string "\n". This is equivalent
+ * to calling {@link #StringContent(int)} with an <code>initialLength</code>
+ * of 10.
+ */
+ public StringContent()
+ {
+ this(10);
+ }
+
+ /**
+ * Creates a new instance containing the string "\n".
+ *
+ * @param initialLength the initial length of the underlying character
+ * array used to store the content.
+ */
+ public StringContent(int initialLength)
+ {
+ super();
+ queueOfDeath = new ReferenceQueue();
+ if (initialLength < 1)
+ initialLength = 1;
+ this.content = new char[initialLength];
+ this.content[0] = '\n';
+ this.count = 1;
+ }
+
+ protected Vector getPositionsInRange(Vector v,
+ int offset,
+ int length)
+ {
+ Vector refPos = v == null ? new Vector() : v;
+ Iterator iter = marks.iterator();
+ while(iter.hasNext())
+ {
+ Mark m = (Mark) iter.next();
+ if (offset <= m.mark && m.mark <= offset + length)
+ refPos.add(new UndoPosRef(m));
+ }
+ return refPos;
+ }
+
+ /**
+ * Creates a position reference for the character at the given offset. The
+ * position offset will be automatically updated when new characters are
+ * inserted into or removed from the content.
+ *
+ * @param offset the character offset.
+ *
+ * @throws BadLocationException if offset is outside the bounds of the
+ * content.
+ */
+ public Position createPosition(int offset) throws BadLocationException
+ {
+ // Lazily create marks vector.
+ if (marks == null)
+ marks = new Vector();
+ StickyPosition sp = new StickyPosition(offset);
+ return sp;
+ }
+
+ /**
+ * Returns the length of the string content, including the '\n' character at
+ * the end.
+ *
+ * @return The length of the string content.
+ */
+ public int length()
+ {
+ return count;
+ }
+
+ /**
+ * Inserts <code>str</code> at the given position and returns an
+ * {@link UndoableEdit} that enables undo/redo support.
+ *
+ * @param where the insertion point (must be less than
+ * <code>length()</code>).
+ * @param str the string to insert (<code>null</code> not permitted).
+ *
+ * @return An object that can undo the insertion.
+ */
+ public UndoableEdit insertString(int where, String str)
+ throws BadLocationException
+ {
+ checkLocation(where, 0);
+ if (where == this.count)
+ throw new BadLocationException("Invalid location", 1);
+ if (str == null)
+ throw new NullPointerException();
+ char[] insert = str.toCharArray();
+ replace(where, 0, insert);
+
+ // Move all the positions.
+ if (marks != null)
+ {
+ Iterator iter = marks.iterator();
+ int start = where;
+ if (start == 0)
+ start = 1;
+ while (iter.hasNext())
+ {
+ Mark m = (Mark) iter.next();
+ if (m.mark >= start)
+ m.mark += str.length();
+ }
+ }
+
+ InsertUndo iundo = new InsertUndo(where, insert.length);
+ return iundo;
+ }
+
+ /**
+ * Removes the specified range of characters and returns an
+ * {@link UndoableEdit} that enables undo/redo support.
+ *
+ * @param where the starting index.
+ * @param nitems the number of characters.
+ *
+ * @return An object that can undo the removal.
+ *
+ * @throws BadLocationException if the character range extends outside the
+ * bounds of the content OR includes the last character.
+ */
+ public UndoableEdit remove(int where, int nitems) throws BadLocationException
+ {
+ checkLocation(where, nitems + 1);
+ RemoveUndo rundo = new RemoveUndo(where, new String(this.content, where,
+ nitems));
+
+ replace(where, nitems, EMPTY);
+ // Move all the positions.
+ if (marks != null)
+ {
+ Iterator iter = marks.iterator();
+ while (iter.hasNext())
+ {
+ Mark m = (Mark) iter.next();
+ if (m.mark >= where + nitems)
+ m.mark -= nitems;
+ else if (m.mark >= where)
+ m.mark = where;
+ }
+ }
+ return rundo;
+ }
+
+ private void replace(int offs, int numRemove, char[] insert)
+ {
+ int insertLength = insert.length;
+ int delta = insertLength - numRemove;
+ int src = offs + numRemove;
+ int numMove = count - src;
+ int dest = src + delta;
+ if (count + delta >= content.length)
+ {
+ // Grow data array.
+ int newLength = Math.max(2 * content.length, count + delta);
+ char[] newContent = new char[newLength];
+ System.arraycopy(content, 0, newContent, 0, offs);
+ System.arraycopy(insert, 0, newContent, offs, insertLength);
+ System.arraycopy(content, src, newContent, dest, numMove);
+ content = newContent;
+ }
+ else
+ {
+ System.arraycopy(content, src, content, dest, numMove);
+ System.arraycopy(insert, 0, content, offs, insertLength);
+ }
+ count += delta;
+ }
+
+ /**
+ * Returns a new <code>String</code> containing the characters in the
+ * specified range.
+ *
+ * @param where the start index.
+ * @param len the number of characters.
+ *
+ * @return A string.
+ *
+ * @throws BadLocationException if the requested range of characters extends
+ * outside the bounds of the content.
+ */
+ public String getString(int where, int len) throws BadLocationException
+ {
+ // The RI throws a StringIndexOutOfBoundsException here, which
+ // smells like a bug. We throw a BadLocationException instead.
+ checkLocation(where, len);
+ return new String(this.content, where, len);
+ }
+
+ /**
+ * Updates <code>txt</code> to contain a direct reference to the underlying
+ * character array.
+ *
+ * @param where the index of the first character.
+ * @param len the number of characters.
+ * @param txt a carrier for the return result (<code>null</code> not
+ * permitted).
+ *
+ * @throws BadLocationException if the requested character range is not
+ * within the bounds of the content.
+ * @throws NullPointerException if <code>txt</code> is <code>null</code>.
+ */
+ public void getChars(int where, int len, Segment txt)
+ throws BadLocationException
+ {
+ if (where + len > count)
+ throw new BadLocationException("Invalid location", where + len);
+ txt.array = content;
+ txt.offset = where;
+ txt.count = len;
+ }
+
+
+ /**
+ * Resets the positions in the specified vector to their original offset
+ * after a undo operation is performed. For example, after removing some
+ * content, the positions in the removed range will all be set to one
+ * offset. This method restores the positions to their original offsets
+ * after an undo.
+ */
+ protected void updateUndoPositions(Vector positions)
+ {
+ for (Iterator i = positions.iterator(); i.hasNext();)
+ {
+ UndoPosRef pos = (UndoPosRef) i.next();
+ pos.reset();
+ }
+ }
+
+ /**
+ * A utility method that checks the validity of the specified character
+ * range.
+ *
+ * @param where the first character in the range.
+ * @param len the number of characters in the range.
+ *
+ * @throws BadLocationException if the specified range is not within the
+ * bounds of the content.
+ */
+ void checkLocation(int where, int len) throws BadLocationException
+ {
+ if (where < 0)
+ throw new BadLocationException("Invalid location", 1);
+ else if (where > this.count)
+ throw new BadLocationException("Invalid location", this.count);
+ else if ((where + len) > this.count)
+ throw new BadLocationException("Invalid range", this.count);
+ }
+
+ /**
+ * Polls the queue of death for GapContentPositions, updates the
+ * corresponding reference count and removes the corresponding mark
+ * if the refcount reaches zero.
+ *
+ * This is package private to avoid accessor synthetic methods.
+ */
+ void garbageCollect()
+ {
+ Reference ref = queueOfDeath.poll();
+ while (ref != null)
+ {
+ if (ref != null)
+ {
+ StickyPosition pos = (StickyPosition) ref.get();
+ Mark m = pos.mark;
+ m.refCount--;
+ if (m.refCount == 0)
+ marks.remove(m);
+ }
+ ref = queueOfDeath.poll();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/Style.java b/libjava/classpath/javax/swing/text/Style.java
new file mode 100644
index 000000000..801082874
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Style.java
@@ -0,0 +1,64 @@
+/* Style.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 javax.swing.event.ChangeListener;
+
+public interface Style extends MutableAttributeSet
+{
+ /**
+ * Returns the name of the style.
+ *
+ * @return the name
+ */
+ String getName();
+
+ /**
+ * Adds a <code>ChangeListener</code> object to the style.
+ *
+ * @param listener the listener object to add
+ */
+ void addChangeListener(ChangeListener listener);
+
+ /**
+ * Removes a <code>ChangeListener</code> from to the style.
+ *
+ * @param listener the listener object to remove,
+ */
+ void removeChangeListener(ChangeListener listener);
+}
diff --git a/libjava/classpath/javax/swing/text/StyleConstants.java b/libjava/classpath/javax/swing/text/StyleConstants.java
new file mode 100644
index 000000000..bdc63719d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/StyleConstants.java
@@ -0,0 +1,1083 @@
+/* StyleConstants.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 java.util.ArrayList;
+
+import javax.swing.Icon;
+
+/**
+ * Represents standard attribute keys. This class also contains a set of
+ * useful static utility methods for querying and populating an
+ * {@link AttributeSet}.
+ *
+ * @since 1.2
+ */
+public class StyleConstants
+{
+ /**
+ * A value representing left alignment for the
+ * {@link ParagraphConstants#Alignment} attribute.
+ */
+ public static final int ALIGN_LEFT = 0;
+
+ /**
+ * A value representing center alignment for the
+ * {@link ParagraphConstants#Alignment} attribute.
+ */
+ public static final int ALIGN_CENTER = 1;
+
+ /**
+ * A value representing right alignment for the
+ * {@link ParagraphConstants#Alignment} attribute.
+ */
+ public static final int ALIGN_RIGHT = 2;
+
+ /**
+ * A value representing ful justification for the
+ * {@link ParagraphConstants#Alignment} attribute.
+ */
+ public static final int ALIGN_JUSTIFIED = 3;
+
+ /** An alias for {@link CharacterConstants#Background}. */
+ public static final Object Background = CharacterConstants.Background;
+
+ /** An alias for {@link CharacterConstants#BidiLevel}. */
+ public static final Object BidiLevel = CharacterConstants.BidiLevel;
+
+ /** An alias for {@link CharacterConstants#Bold}. */
+ public static final Object Bold = CharacterConstants.Bold;
+
+ /** An alias for {@link CharacterConstants#ComponentAttribute}. */
+ public static final Object ComponentAttribute
+ = CharacterConstants.ComponentAttribute;
+
+ /** An alias for {@link CharacterConstants#Family}. */
+ public static final Object Family = CharacterConstants.Family;
+
+ /** An alias for {@link CharacterConstants#Family}. */
+ public static final Object FontFamily = CharacterConstants.Family;
+
+ /** An alias for {@link CharacterConstants#Size}. */
+ public static final Object FontSize = CharacterConstants.Size;
+
+ /** An alias for {@link CharacterConstants#Foreground}. */
+ public static final Object Foreground = CharacterConstants.Foreground;
+
+ /** An alias for {@link CharacterConstants#IconAttribute}. */
+ public static final Object IconAttribute = CharacterConstants.IconAttribute;
+
+ /** An alias for {@link CharacterConstants#Italic}. */
+ public static final Object Italic = CharacterConstants.Italic;
+
+ /** An alias for {@link CharacterConstants#Size}. */
+ public static final Object Size = CharacterConstants.Size;
+
+ /** An alias for {@link CharacterConstants#StrikeThrough}. */
+ public static final Object StrikeThrough = CharacterConstants.StrikeThrough;
+
+ /** An alias for {@link CharacterConstants#Subscript}. */
+ public static final Object Subscript = CharacterConstants.Subscript;
+
+ /** An alias for {@link CharacterConstants#Superscript}. */
+ public static final Object Superscript = CharacterConstants.Superscript;
+
+ /** An alias for {@link CharacterConstants#Underline}. */
+ public static final Object Underline = CharacterConstants.Underline;
+
+ /** An alias for {@link ParagraphConstants#Alignment}. */
+ public static final Object Alignment = ParagraphConstants.Alignment;
+
+ /** An alias for {@link ParagraphConstants#FirstLineIndent}. */
+ public static final Object FirstLineIndent
+ = ParagraphConstants.FirstLineIndent;
+
+ /** An alias for {@link ParagraphConstants#LeftIndent}. */
+ public static final Object LeftIndent = ParagraphConstants.LeftIndent;
+
+ /** An alias for {@link ParagraphConstants#LineSpacing}. */
+ public static final Object LineSpacing = ParagraphConstants.LineSpacing;
+
+ /** An alias for {@link ParagraphConstants#Orientation}. */
+ public static final Object Orientation = ParagraphConstants.Orientation;
+
+ /** An alias for {@link ParagraphConstants#RightIndent}. */
+ public static final Object RightIndent = ParagraphConstants.RightIndent;
+
+ /** An alias for {@link ParagraphConstants#SpaceAbove}. */
+ public static final Object SpaceAbove = ParagraphConstants.SpaceAbove;
+
+ /** An alias for {@link ParagraphConstants#SpaceBelow}. */
+ public static final Object SpaceBelow = ParagraphConstants.SpaceBelow;
+
+ /** An alias for {@link ParagraphConstants#TabSet}. */
+ public static final Object TabSet = ParagraphConstants.TabSet;
+
+ public static final String ComponentElementName = "component";
+
+ public static final String IconElementName = "icon";
+
+ public static final Object ComposedTextAttribute
+ = new StyleConstants("composed text");
+
+ public static final Object ModelAttribute = new StyleConstants("model");
+
+ public static final Object NameAttribute = new StyleConstants("name");
+
+ public static final Object ResolveAttribute = new StyleConstants("resolver");
+
+ /**
+ * All StyleConstants keys. This is used in StyleContext to register
+ * all known keys as static attribute keys for serialization.
+ */
+ static ArrayList keys;
+
+ String keyname;
+
+ // Package-private to avoid accessor constructor for use by
+ // subclasses.
+ StyleConstants(String k)
+ {
+ keyname = k;
+ if (keys == null)
+ keys = new ArrayList();
+ keys.add(this);
+ }
+
+ /**
+ * Returns a string representation of the attribute key.
+ *
+ * @return A string representation of the attribute key.
+ */
+ public String toString()
+ {
+ return keyname;
+ }
+
+ /**
+ * Returns the alignment specified in the given attributes, or
+ * {@link #ALIGN_LEFT} if no alignment is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The alignment (typically one of {@link #ALIGN_LEFT},
+ * {@link #ALIGN_RIGHT}, {@link #ALIGN_CENTER} or
+ * {@link #ALIGN_JUSTIFIED}).
+ *
+ * @see #setAlignment(MutableAttributeSet, int)
+ */
+ public static int getAlignment(AttributeSet a)
+ {
+ Integer i = (Integer) a.getAttribute(Alignment);
+ if (i != null)
+ return i.intValue();
+ else
+ return ALIGN_LEFT;
+ }
+
+ /**
+ * Returns the background color specified in the given attributes, or
+ * {@link Color#BLACK} if no background color is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The background color.
+ *
+ * @see #setBackground(MutableAttributeSet, Color)
+ */
+ public static Color getBackground(AttributeSet a)
+ {
+ Color c = (Color) a.getAttribute(Background);
+ if (c != null)
+ return c;
+ else
+ return Color.BLACK;
+ }
+
+ /**
+ * Returns the bidi level specified in the given attributes, or
+ * <code>0</code> if no bidi level is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The bidi level.
+ *
+ * @see #setBidiLevel(MutableAttributeSet, int)
+ */
+ public static int getBidiLevel(AttributeSet a)
+ {
+ Integer i = (Integer) a.getAttribute(BidiLevel);
+ if (i != null)
+ return i.intValue();
+ else
+ return 0;
+ }
+
+ /**
+ * Returns the component specified in the given attributes, or
+ * <code>null</code> if no component is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The component (possibly <code>null</code>).
+ *
+ * @see #setComponent(MutableAttributeSet, Component)
+ */
+ public static Component getComponent(AttributeSet a)
+ {
+ Component c = (Component) a.getAttribute(ComponentAttribute);
+ if (c != null)
+ return c;
+ else
+ return null;
+ }
+
+ /**
+ * Returns the indentation specified in the given attributes, or
+ * <code>0.0f</code> if no indentation is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The indentation.
+ *
+ * @see #setFirstLineIndent(MutableAttributeSet, float)
+ */
+ public static float getFirstLineIndent(AttributeSet a)
+ {
+ Float f = (Float) a.getAttribute(FirstLineIndent);
+ if (f != null)
+ return f.floatValue();
+ else
+ return 0.0f;
+ }
+
+ /**
+ * Returns the font family specified in the given attributes, or
+ * <code>Monospaced</code> if no font family is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The font family.
+ *
+ * @see #setFontFamily(MutableAttributeSet, String)
+ */
+ public static String getFontFamily(AttributeSet a)
+ {
+ String ff = (String) a.getAttribute(FontFamily);
+ if (ff != null)
+ return ff;
+ else
+ return "Monospaced";
+ }
+
+ /**
+ * Returns the font size specified in the given attributes, or
+ * <code>12</code> if no font size is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The font size.
+ *
+ * @see #setFontSize(MutableAttributeSet, int)
+ */
+ public static int getFontSize(AttributeSet a)
+ {
+ Integer i = (Integer) a.getAttribute(FontSize);
+ if (i != null)
+ return i.intValue();
+ else
+ return 12;
+ }
+
+ /**
+ * Returns the foreground color specified in the given attributes, or
+ * {@link Color#BLACK} if no foreground color is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The foreground color.
+ *
+ * @see #setForeground(MutableAttributeSet, Color)
+ */
+ public static Color getForeground(AttributeSet a)
+ {
+ Color c = (Color) a.getAttribute(Foreground);
+ if (c != null)
+ return c;
+ else
+ return Color.BLACK;
+ }
+
+ /**
+ * Returns the icon specified in the given attributes, or
+ * <code>null</code> if no icon is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The icon (possibly <code>null</code>).
+ *
+ * @see #setIcon(MutableAttributeSet, Icon)
+ */
+ public static Icon getIcon(AttributeSet a)
+ {
+ return (Icon) a.getAttribute(IconAttribute);
+ }
+
+ /**
+ * Returns the left indentation specified in the given attributes, or
+ * <code>0.0f</code> if no left indentation is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The left indentation.
+ *
+ * @see #setLeftIndent(MutableAttributeSet, float)
+ */
+ public static float getLeftIndent(AttributeSet a)
+ {
+ Float f = (Float) a.getAttribute(LeftIndent);
+ if (f != null)
+ return f.floatValue();
+ else
+ return 0.0f;
+ }
+
+ /**
+ * Returns the line spacing specified in the given attributes, or
+ * <code>0.0f</code> if no line spacing is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The line spacing.
+ *
+ * @see #setLineSpacing(MutableAttributeSet, float)
+ */
+ public static float getLineSpacing(AttributeSet a)
+ {
+ Float f = (Float) a.getAttribute(LineSpacing);
+ if (f != null)
+ return f.floatValue();
+ else
+ return 0.0f;
+ }
+
+ /**
+ * Returns the right indentation specified in the given attributes, or
+ * <code>0.0f</code> if no right indentation is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The right indentation.
+ *
+ * @see #setRightIndent(MutableAttributeSet, float)
+ */
+ public static float getRightIndent(AttributeSet a)
+ {
+ Float f = (Float) a.getAttribute(RightIndent);
+ if (f != null)
+ return f.floatValue();
+ else
+ return 0.0f;
+ }
+
+ /**
+ * Returns the 'space above' specified in the given attributes, or
+ * <code>0.0f</code> if no 'space above' is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The 'space above'.
+ *
+ * @see #setSpaceAbove(MutableAttributeSet, float)
+ */
+ public static float getSpaceAbove(AttributeSet a)
+ {
+ Float f = (Float) a.getAttribute(SpaceAbove);
+ if (f != null)
+ return f.floatValue();
+ else
+ return 0.0f;
+ }
+
+ /**
+ * Returns the 'space below' specified in the given attributes, or
+ * <code>0.0f</code> if no 'space below' is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The 'space below'.
+ *
+ * @see #setSpaceBelow(MutableAttributeSet, float)
+ */
+ public static float getSpaceBelow(AttributeSet a)
+ {
+ Float f = (Float) a.getAttribute(SpaceBelow);
+ if (f != null)
+ return f.floatValue();
+ else
+ return 0.0f;
+ }
+
+ /**
+ * Returns the tab set specified in the given attributes, or
+ * <code>null</code> if no tab set is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The tab set.
+ *
+ * @see #setTabSet(MutableAttributeSet, javax.swing.text.TabSet)
+ */
+ public static javax.swing.text.TabSet getTabSet(AttributeSet a)
+ {
+ // I'm guessing that the fully qualified class name is to differentiate
+ // between the TabSet class and the TabSet (attribute) instance on some
+ // compiler...
+ return (javax.swing.text.TabSet) a.getAttribute(StyleConstants.TabSet);
+ }
+
+ /**
+ * Returns the value of the bold flag in the given attributes, or
+ * <code>false</code> if no bold flag is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The bold flag.
+ *
+ * @see #setBold(MutableAttributeSet, boolean)
+ */
+ public static boolean isBold(AttributeSet a)
+ {
+ Boolean b = (Boolean) a.getAttribute(Bold);
+ if (b != null)
+ return b.booleanValue();
+ else
+ return false;
+ }
+
+ /**
+ * Returns the value of the italic flag in the given attributes, or
+ * <code>false</code> if no italic flag is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The italic flag.
+ *
+ * @see #setItalic(MutableAttributeSet, boolean)
+ */
+ public static boolean isItalic(AttributeSet a)
+ {
+ Boolean b = (Boolean) a.getAttribute(Italic);
+ if (b != null)
+ return b.booleanValue();
+ else
+ return false;
+ }
+
+ /**
+ * Returns the value of the strike-through flag in the given attributes, or
+ * <code>false</code> if no strike-through flag is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The strike-through flag.
+ *
+ * @see #setStrikeThrough(MutableAttributeSet, boolean)
+ */
+ public static boolean isStrikeThrough(AttributeSet a)
+ {
+ Boolean b = (Boolean) a.getAttribute(StrikeThrough);
+ if (b != null)
+ return b.booleanValue();
+ else
+ return false;
+ }
+
+ /**
+ * Returns the value of the subscript flag in the given attributes, or
+ * <code>false</code> if no subscript flag is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The subscript flag.
+ *
+ * @see #setSubscript(MutableAttributeSet, boolean)
+ */
+ public static boolean isSubscript(AttributeSet a)
+ {
+ Boolean b = (Boolean) a.getAttribute(Subscript);
+ if (b != null)
+ return b.booleanValue();
+ else
+ return false;
+ }
+
+ /**
+ * Returns the value of the superscript flag in the given attributes, or
+ * <code>false</code> if no superscript flag is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The superscript flag.
+ *
+ * @see #setSuperscript(MutableAttributeSet, boolean)
+ */
+ public static boolean isSuperscript(AttributeSet a)
+ {
+ Boolean b = (Boolean) a.getAttribute(Superscript);
+ if (b != null)
+ return b.booleanValue();
+ else
+ return false;
+ }
+
+ /**
+ * Returns the value of the underline flag in the given attributes, or
+ * <code>false</code> if no underline flag is specified.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ *
+ * @return The underline flag.
+ *
+ * @see #setUnderline(MutableAttributeSet, boolean)
+ */
+ public static boolean isUnderline(AttributeSet a)
+ {
+ Boolean b = (Boolean) a.getAttribute(Underline);
+ if (b != null)
+ return b.booleanValue();
+ else
+ return false;
+ }
+
+ /**
+ * Adds an alignment attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param align the alignment (typically one of
+ * {@link StyleConstants#ALIGN_LEFT},
+ * {@link StyleConstants#ALIGN_RIGHT},
+ * {@link StyleConstants#ALIGN_CENTER} or
+ * {@link StyleConstants#ALIGN_JUSTIFIED}).
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getAlignment(AttributeSet)
+ */
+ public static void setAlignment(MutableAttributeSet a, int align)
+ {
+ a.addAttribute(Alignment, new Integer(align));
+ }
+
+ /**
+ * Adds a background attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param bg the background (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ *
+ * @see #getBackground(AttributeSet)
+ */
+ public static void setBackground(MutableAttributeSet a, Color bg)
+ {
+ a.addAttribute(Background, bg);
+ }
+
+ /**
+ * Adds a bidi-level attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param lev the level.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getBidiLevel(AttributeSet)
+ */
+ public static void setBidiLevel(MutableAttributeSet a, int lev)
+ {
+ a.addAttribute(BidiLevel, new Integer(lev));
+ }
+
+ /**
+ * Adds a bold attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param b the new value of the bold attribute.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #isBold(AttributeSet)
+ */
+ public static void setBold(MutableAttributeSet a, boolean b)
+ {
+ a.addAttribute(Bold, Boolean.valueOf(b));
+ }
+
+ /**
+ * Adds a component attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param c the component (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ *
+ * @see #getComponent(AttributeSet)
+ */
+ public static void setComponent(MutableAttributeSet a, Component c)
+ {
+ a.addAttribute(ComponentAttribute, c);
+ }
+
+ /**
+ * Adds a first line indentation attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param i the indentation.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getFirstLineIndent(AttributeSet)
+ */
+ public static void setFirstLineIndent(MutableAttributeSet a, float i)
+ {
+ a.addAttribute(FirstLineIndent, new Float(i));
+ }
+
+ /**
+ * Adds a font family attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param fam the font family name (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ *
+ * @see #getFontFamily(AttributeSet)
+ */
+ public static void setFontFamily(MutableAttributeSet a, String fam)
+ {
+ a.addAttribute(FontFamily, fam);
+ }
+
+ /**
+ * Adds a font size attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param s the font size (in points).
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getFontSize(AttributeSet)
+ */
+ public static void setFontSize(MutableAttributeSet a, int s)
+ {
+ a.addAttribute(FontSize, new Integer(s));
+ }
+
+ /**
+ * Adds a foreground color attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param fg the foreground color (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ *
+ * @see #getForeground(AttributeSet)
+ */
+ public static void setForeground(MutableAttributeSet a, Color fg)
+ {
+ a.addAttribute(Foreground, fg);
+ }
+
+ /**
+ * Adds an icon attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param c the icon (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ *
+ * @see #getIcon(AttributeSet)
+ */
+ public static void setIcon(MutableAttributeSet a, Icon c)
+ {
+ a.addAttribute(AbstractDocument.ElementNameAttribute, IconElementName);
+ a.addAttribute(IconAttribute, c);
+ }
+
+ /**
+ * Adds an italic attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param b the new value of the italic attribute.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #isItalic(AttributeSet)
+ */
+ public static void setItalic(MutableAttributeSet a, boolean b)
+ {
+ a.addAttribute(Italic, Boolean.valueOf(b));
+ }
+
+ /**
+ * Adds a left indentation attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param i the indentation.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getLeftIndent(AttributeSet)
+ */
+ public static void setLeftIndent(MutableAttributeSet a, float i)
+ {
+ a.addAttribute(LeftIndent, new Float(i));
+ }
+
+ /**
+ * Adds a line spacing attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param i the line spacing.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getLineSpacing(AttributeSet)
+ */
+ public static void setLineSpacing(MutableAttributeSet a, float i)
+ {
+ a.addAttribute(LineSpacing, new Float(i));
+ }
+
+ /**
+ * Adds a right indentation attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param i the right indentation.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getRightIndent(AttributeSet)
+ */
+ public static void setRightIndent(MutableAttributeSet a, float i)
+ {
+ a.addAttribute(RightIndent, new Float(i));
+ }
+
+ /**
+ * Adds a 'space above' attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param i the space above attribute value.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getSpaceAbove(AttributeSet)
+ */
+ public static void setSpaceAbove(MutableAttributeSet a, float i)
+ {
+ a.addAttribute(SpaceAbove, new Float(i));
+ }
+
+ /**
+ * Adds a 'space below' attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param i the space below attribute value.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #getSpaceBelow(AttributeSet)
+ */
+ public static void setSpaceBelow(MutableAttributeSet a, float i)
+ {
+ a.addAttribute(SpaceBelow, new Float(i));
+ }
+
+ /**
+ * Adds a strike-through attribue to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param b the strike-through attribute value.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #isStrikeThrough(AttributeSet)
+ */
+ public static void setStrikeThrough(MutableAttributeSet a, boolean b)
+ {
+ a.addAttribute(StrikeThrough, Boolean.valueOf(b));
+ }
+
+ /**
+ * Adds a subscript attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param b the subscript attribute value.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #isSubscript(AttributeSet)
+ */
+ public static void setSubscript(MutableAttributeSet a, boolean b)
+ {
+ a.addAttribute(Subscript, Boolean.valueOf(b));
+ }
+
+ /**
+ * Adds a superscript attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param b the superscript attribute value.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #isSuperscript(AttributeSet)
+ */
+ public static void setSuperscript(MutableAttributeSet a, boolean b)
+ {
+ a.addAttribute(Superscript, Boolean.valueOf(b));
+ }
+
+ /**
+ * Adds a {@link TabSet} attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param tabs the tab set (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if either argument is <code>null</code>.
+ *
+ * @see #getTabSet(AttributeSet)
+ */
+ public static void setTabSet(MutableAttributeSet a,
+ javax.swing.text.TabSet tabs)
+ {
+ a.addAttribute(StyleConstants.TabSet, tabs);
+ }
+
+ /**
+ * Adds an underline attribute to the specified set.
+ *
+ * @param a the attribute set (<code>null</code> not permitted).
+ * @param b the underline attribute value.
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ *
+ * @see #isUnderline(AttributeSet)
+ */
+ public static void setUnderline(MutableAttributeSet a, boolean b)
+ {
+ a.addAttribute(Underline, Boolean.valueOf(b));
+ }
+
+ // The remainder are so-called "typesafe enumerations" which
+ // alias subsets of the above constants.
+
+ /**
+ * A set of keys for attributes that apply to characters.
+ */
+ public static class CharacterConstants
+ extends StyleConstants
+ implements AttributeSet.CharacterAttribute
+ {
+ /**
+ * Private constructor prevents new instances being created.
+ *
+ * @param k the key name.
+ */
+ private CharacterConstants(String k)
+ {
+ super(k);
+ }
+
+ /** An alias for {@link ColorConstants#Background}. */
+ public static final Object Background = ColorConstants.Background;
+
+ /** A key for the bidi level character attribute. */
+ public static final Object BidiLevel = new CharacterConstants("bidiLevel");
+
+ /** An alias for {@link FontConstants#Bold}. */
+ public static final Object Bold = FontConstants.Bold;
+
+ /** A key for the component character attribute. */
+ public static final Object ComponentAttribute
+ = new CharacterConstants("component");
+
+ /** An alias for {@link FontConstants#Family}. */
+ public static final Object Family = FontConstants.Family;
+
+ /** An alias for {@link FontConstants#Size}. */
+ public static final Object Size = FontConstants.Size;
+
+ /** An alias for {@link ColorConstants#Foreground}. */
+ public static final Object Foreground = ColorConstants.Foreground;
+
+ /** A key for the icon character attribute. */
+ public static final Object IconAttribute = new CharacterConstants("icon");
+
+ /** A key for the italic character attribute. */
+ public static final Object Italic = FontConstants.Italic;
+
+ /** A key for the strike through character attribute. */
+ public static final Object StrikeThrough
+ = new CharacterConstants("strikethrough");
+
+ /** A key for the subscript character attribute. */
+ public static final Object Subscript = new CharacterConstants("subscript");
+
+ /** A key for the superscript character attribute. */
+ public static final Object Superscript
+ = new CharacterConstants("superscript");
+
+ /** A key for the underline character attribute. */
+ public static final Object Underline = new CharacterConstants("underline");
+
+ }
+
+ /**
+ * A set of keys for attributes that relate to colors.
+ */
+ public static class ColorConstants
+ extends StyleConstants
+ implements AttributeSet.ColorAttribute, AttributeSet.CharacterAttribute
+ {
+ /**
+ * Private constructor prevents new instances being created.
+ *
+ * @param k the key name.
+ */
+ private ColorConstants(String k)
+ {
+ super(k);
+ }
+
+ /** A key for the foreground color attribute. */
+ public static final Object Foreground = new ColorConstants("foreground");
+
+ /** A key for the background color attribute. */
+ public static final Object Background = new ColorConstants("background");
+ }
+
+ /**
+ * A set of keys for attributes that apply to fonts.
+ */
+ public static class FontConstants
+ extends StyleConstants
+ implements AttributeSet.FontAttribute, AttributeSet.CharacterAttribute
+ {
+ /**
+ * Private constructor prevents new instances being created.
+ *
+ * @param k the key name.
+ */
+ private FontConstants(String k)
+ {
+ super(k);
+ }
+
+ /** A key for the bold font attribute. */
+ public static final Object Bold = new FontConstants("bold");
+
+ /** A key for the family font attribute. */
+ public static final Object Family = new FontConstants("family");
+
+ /** A key for the italic font attribute. */
+ public static final Object Italic = new FontConstants("italic");
+
+ /** A key for the size font attribute. */
+ public static final Object Size = new FontConstants("size");
+ }
+
+ /**
+ * A set of keys for attributes that apply to paragraphs.
+ */
+ public static class ParagraphConstants
+ extends StyleConstants
+ implements AttributeSet.ParagraphAttribute
+ {
+ /**
+ * Private constructor prevents new instances being created.
+ *
+ * @param k the key name.
+ */
+ private ParagraphConstants(String k)
+ {
+ super(k);
+ }
+
+ /** A key for the alignment paragraph attribute. */
+ public static final Object Alignment = new ParagraphConstants("Alignment");
+
+ /** A key for the first line indentation paragraph attribute. */
+ public static final Object FirstLineIndent
+ = new ParagraphConstants("FirstLineIndent");
+
+ /** A key for the left indentation paragraph attribute. */
+ public static final Object LeftIndent
+ = new ParagraphConstants("LeftIndent");
+
+ /** A key for the line spacing paragraph attribute. */
+ public static final Object LineSpacing
+ = new ParagraphConstants("LineSpacing");
+
+ /** A key for the orientation paragraph attribute. */
+ public static final Object Orientation
+ = new ParagraphConstants("Orientation");
+
+ /** A key for the right indentation paragraph attribute. */
+ public static final Object RightIndent
+ = new ParagraphConstants("RightIndent");
+
+ /** A key for the 'space above' paragraph attribute. */
+ public static final Object SpaceAbove
+ = new ParagraphConstants("SpaceAbove");
+
+ /** A key for the 'space below' paragraph attribute. */
+ public static final Object SpaceBelow
+ = new ParagraphConstants("SpaceBelow");
+
+ /** A key for the tabset paragraph attribute. */
+ public static final Object TabSet = new ParagraphConstants("TabSet");
+
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java
new file mode 100644
index 000000000..295398835
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/StyleContext.java
@@ -0,0 +1,990 @@
+/* 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+public class StyleContext
+ implements Serializable, AbstractDocument.AttributeContext
+{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 8042858831190784241L;
+
+ public class NamedStyle
+ implements Serializable, Style
+ {
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -6690628971806226374L;
+
+ protected transient ChangeEvent changeEvent;
+ protected EventListenerList listenerList;
+
+ private transient AttributeSet attributes;
+
+ public NamedStyle()
+ {
+ this(null, null);
+ }
+
+ public NamedStyle(Style parent)
+ {
+ this(null, parent);
+ }
+
+ public NamedStyle(String name, Style parent)
+ {
+ attributes = getEmptySet();
+ listenerList = new EventListenerList();
+ if (name != null)
+ setName(name);
+ if (parent != null)
+ setResolveParent(parent);
+ }
+
+ public String getName()
+ {
+ String name = null;
+ if (isDefined(StyleConstants.NameAttribute))
+ name = getAttribute(StyleConstants.NameAttribute).toString();
+ return name;
+ }
+
+ public void setName(String n)
+ {
+ if (n != null)
+ addAttribute(StyleConstants.NameAttribute, n);
+ }
+
+ public void addChangeListener(ChangeListener l)
+ {
+ listenerList.add(ChangeListener.class, l);
+ }
+
+ public void removeChangeListener(ChangeListener l)
+ {
+ listenerList.remove(ChangeListener.class, l);
+ }
+
+ public <T extends EventListener> T[] getListeners(Class<T> 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)
+ {
+ // Lazily create event.
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ 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()
+ {
+ // The RI returns a NamedStyle as copy, so do we.
+ NamedStyle copy = new NamedStyle();
+ copy.attributes = attributes.copyAttributes();
+ return copy;
+ }
+
+ 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)
+ {
+ if (parent != null)
+ addAttribute(StyleConstants.ResolveAttribute, parent);
+ else
+ removeAttribute(StyleConstants.ResolveAttribute);
+ }
+
+ public String toString()
+ {
+ return "NamedStyle:" + getName() + " " + attributes;
+ }
+
+ private void writeObject(ObjectOutputStream s)
+ throws IOException
+ {
+ s.defaultWriteObject();
+ writeAttributeSet(s, attributes);
+ }
+
+ private void readObject(ObjectInputStream s)
+ throws ClassNotFoundException, IOException
+ {
+ s.defaultReadObject();
+ attributes = SimpleAttributeSet.EMPTY;
+ readAttributeSet(s, this);
+ }
+ }
+
+ public class SmallAttributeSet
+ implements AttributeSet
+ {
+ final Object [] attrs;
+ private AttributeSet resolveParent;
+ public SmallAttributeSet(AttributeSet a)
+ {
+ int n = a.getAttributeCount();
+ int i = 0;
+ attrs = new Object[n * 2];
+ Enumeration e = a.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ Object name = e.nextElement();
+ Object value = a.getAttribute(name);
+ if (name == ResolveAttribute)
+ resolveParent = (AttributeSet) value;
+ attrs[i++] = name;
+ attrs[i++] = value;
+ }
+ }
+
+ public SmallAttributeSet(Object [] a)
+ {
+ attrs = a;
+ for (int i = 0; i < attrs.length; i += 2)
+ {
+ if (attrs[i] == ResolveAttribute)
+ resolveParent = (AttributeSet) attrs[i + 1];
+ }
+ }
+
+ public Object clone()
+ {
+ return this;
+ }
+
+ public boolean containsAttribute(Object name, Object value)
+ {
+ return value.equals(getAttribute(name));
+ }
+
+ public boolean containsAttributes(AttributeSet a)
+ {
+ boolean res = true;
+ Enumeration e = a.getAttributeNames();
+ while (e.hasMoreElements() && res)
+ {
+ Object name = e.nextElement();
+ res = a.getAttribute(name).equals(getAttribute(name));
+ }
+ return res;
+ }
+
+ public AttributeSet copyAttributes()
+ {
+ return this;
+ }
+
+ public boolean equals(Object obj)
+ {
+ boolean eq = false;
+ if (obj instanceof AttributeSet)
+ {
+ AttributeSet atts = (AttributeSet) obj;
+ eq = getAttributeCount() == atts.getAttributeCount()
+ && containsAttributes(atts);
+ }
+ return eq;
+ }
+
+ public Object getAttribute(Object key)
+ {
+ Object att = null;
+ if (key == StyleConstants.ResolveAttribute)
+ att = resolveParent;
+
+ for (int i = 0; i < attrs.length && att == null; i += 2)
+ {
+ if (attrs[i].equals(key))
+ att = attrs[i + 1];
+ }
+
+ // Check the resolve parent, unless we're looking for the
+ // ResolveAttribute, which must not be looked up
+ if (att == null)
+ {
+ AttributeSet parent = getResolveParent();
+ if (parent != null)
+ att = parent.getAttribute(key);
+ }
+
+ return att;
+ }
+
+ 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 resolveParent;
+ }
+
+ 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)
+ {
+ boolean eq;
+ // If the other one is also a SmallAttributeSet, it is only considered
+ // equal if it's the same instance.
+ if (attr instanceof SmallAttributeSet)
+ eq = attr == this;
+ else
+ eq = getAttributeCount() == attr.getAttributeCount()
+ && this.containsAttributes(attr);
+ return eq;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ for (int i = 0; i < attrs.length; i += 2)
+ {
+ if (attrs[i + 1] instanceof AttributeSet)
+ {
+ sb.append(attrs[i]);
+ sb.append("=AttributeSet,");
+ }
+ else
+ {
+ sb.append(attrs[i]);
+ sb.append('=');
+ sb.append(attrs[i + 1]);
+ sb.append(',');
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Register StyleConstant keys as static attribute keys for serialization.
+ */
+ static
+ {
+ // Don't let problems while doing this prevent class loading.
+ try
+ {
+ for (Iterator i = StyleConstants.keys.iterator(); i.hasNext();)
+ registerStaticAttributeKey(i.next());
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ }
+
+ /**
+ * The name of the default style.
+ */
+ public static final String DEFAULT_STYLE = "default";
+
+ static Hashtable sharedAttributeSets = new Hashtable();
+ static Hashtable sharedFonts = new Hashtable();
+
+ static StyleContext defaultStyleContext;
+ static final int compressionThreshold = 9;
+
+ /**
+ * These attribute keys are handled specially in serialization.
+ */
+ private static Hashtable writeAttributeKeys;
+ private static Hashtable readAttributeKeys;
+
+ private NamedStyle styles;
+
+ /**
+ * Used for searching attributes in the pool.
+ */
+ private transient MutableAttributeSet search = new SimpleAttributeSet();
+
+ /**
+ * A pool of immutable AttributeSets.
+ */
+ private transient Map attributeSetPool =
+ Collections.synchronizedMap(new WeakHashMap());
+
+ /**
+ * Creates a new instance of the style context. Add the default style
+ * to the style table.
+ */
+ public StyleContext()
+ {
+ styles = new NamedStyle(null);
+ addStyle(DEFAULT_STYLE, null);
+ }
+
+ protected SmallAttributeSet createSmallAttributeSet(AttributeSet a)
+ {
+ return new SmallAttributeSet(a);
+ }
+
+ protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
+ {
+ return new SimpleAttributeSet(a);
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ styles.addChangeListener(listener);
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ styles.removeChangeListener(listener);
+ }
+
+ public ChangeListener[] getChangeListeners()
+ {
+ return styles.getChangeListeners();
+ }
+
+ public Style addStyle(String name, Style parent)
+ {
+ Style newStyle = new NamedStyle(name, parent);
+ if (name != null)
+ styles.addAttribute(name, newStyle);
+ return newStyle;
+ }
+
+ public void removeStyle(String name)
+ {
+ styles.removeAttribute(name);
+ }
+
+ /**
+ * Get the style from the style table. If the passed name
+ * matches {@link #DEFAULT_STYLE}, returns the default style.
+ * Otherwise returns the previously defined style of
+ * <code>null</code> if the style with the given name is not defined.
+ *
+ * @param name the name of the style.
+ *
+ * @return the style with the given name or null if no such defined.
+ */
+ public Style getStyle(String name)
+ {
+ return (Style) styles.getAttribute(name);
+ }
+
+ /**
+ * Get the names of the style. The returned enumeration always
+ * contains at least one member, the default style.
+ */
+ public Enumeration<?> getStyleNames()
+ {
+ return styles.getAttributeNames();
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws ClassNotFoundException, IOException
+ {
+ search = new SimpleAttributeSet();
+ attributeSetPool = Collections.synchronizedMap(new WeakHashMap());
+ in.defaultReadObject();
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ cleanupPool();
+ out.defaultWriteObject();
+ }
+
+ //
+ // 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()
+ {
+ if (defaultStyleContext == null)
+ defaultStyleContext = new StyleContext();
+ return defaultStyleContext;
+ }
+
+ public synchronized AttributeSet addAttribute(AttributeSet old, Object name,
+ Object value)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() + 1 < getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.addAttribute(name, value);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.addAttribute(name, value);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ public synchronized AttributeSet addAttributes(AttributeSet old,
+ AttributeSet attributes)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() + attributes.getAttributeCount()
+ < getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.addAttributes(attributes);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.addAttributes(attributes);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ public AttributeSet getEmptySet()
+ {
+ return SimpleAttributeSet.EMPTY;
+ }
+
+ public void reclaim(AttributeSet attributes)
+ {
+ cleanupPool();
+ }
+
+ public synchronized AttributeSet removeAttribute(AttributeSet old,
+ Object name)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() - 1 <= getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttribute(name);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttribute(name);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ public synchronized AttributeSet removeAttributes(AttributeSet old,
+ AttributeSet attributes)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() <= getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttributes(attributes);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttributes(attributes);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ public synchronized AttributeSet removeAttributes(AttributeSet old,
+ Enumeration<?> names)
+ {
+ AttributeSet ret;
+ if (old.getAttributeCount() <= getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttributes(names);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttributes(names);
+ ret = mas;
+ }
+ return ret;
+ }
+
+ /**
+ * Gets the object previously registered with registerStaticAttributeKey.
+ *
+ * @param key - the key that was registered.
+ * @return the object previously registered with registerStaticAttributeKey.
+ */
+ public static Object getStaticAttribute(Object key)
+ {
+ if (key == null)
+ return null;
+ return readAttributeKeys.get(key);
+ }
+
+ /**
+ * Returns the String that key will be registered with
+ * registerStaticAttributeKey.
+ *
+ * @param key - the key that will be registered.
+ * @return the string the key will be registered with.
+ */
+ public static Object getStaticAttributeKey(Object key)
+ {
+ return key.getClass().getName() + "." + key.toString();
+ }
+
+ /**
+ * Reads a set of attributes from the given object input stream. This will
+ * attempt to restore keys that were static objects by considering only the
+ * keys that have were registered with registerStaticAttributeKey. The
+ * attributes retrieved will be placed into the given set.
+ *
+ * @param in - the stream to read from
+ * @param a - the set of attributes
+ * @throws ClassNotFoundException - may be encountered when reading from
+ * stream
+ * @throws IOException - any I/O error
+ */
+ public static void readAttributeSet(ObjectInputStream in,
+ MutableAttributeSet a)
+ throws ClassNotFoundException, IOException
+ {
+ int count = in.readInt();
+ for (int i = 0; i < count; i++)
+ {
+ Object key = in.readObject();
+ Object val = in.readObject();
+ if (readAttributeKeys != null)
+ {
+ Object staticKey = readAttributeKeys.get(key);
+ if (staticKey != null)
+ key = staticKey;
+ Object staticVal = readAttributeKeys.get(val);
+ if (staticVal != null)
+ val = staticVal;
+ }
+ a.addAttribute(key, val);
+ }
+ }
+
+ /**
+ * Serialize an attribute set in a way that is compatible with it
+ * being read in again by {@link #readAttributeSet(ObjectInputStream, MutableAttributeSet)}.
+ * In particular registered static keys are transformed properly.
+ *
+ * @param out - stream to write to
+ * @param a - the attribute set
+ * @throws IOException - any I/O error
+ */
+ public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a)
+ throws IOException
+ {
+ int count = a.getAttributeCount();
+ out.writeInt(count);
+ Enumeration e = a.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ // Write key.
+ if (key instanceof Serializable)
+ out.writeObject(key);
+ else
+ {
+ Object io = writeAttributeKeys.get(key);
+ if (io == null)
+ throw new NotSerializableException(key.getClass().getName()
+ + ", key: " + key);
+ out.writeObject(io);
+ }
+ // Write value.
+ Object val = a.getAttribute(key);
+ Object io = writeAttributeKeys.get(val);
+ if (val instanceof Serializable)
+ out.writeObject(io != null ? io : val);
+ else
+ {
+ if (io == null)
+ throw new NotSerializableException(val.getClass().getName());
+ out.writeObject(io);
+ }
+ }
+ }
+
+ /**
+ * Handles reading in the attributes.
+ * @see #readAttributeSet(ObjectInputStream, MutableAttributeSet)
+ *
+ * @param in - the stream to read from
+ * @param a - the set of attributes
+ * @throws ClassNotFoundException - may be encountered when reading from stream
+ * @throws IOException - any I/O error
+ */
+ public void readAttributes(ObjectInputStream in, MutableAttributeSet a)
+ throws ClassNotFoundException, IOException
+ {
+ readAttributeSet(in, a);
+ }
+
+ /**
+ * Handles writing of the given attributes.
+ * @see #writeAttributeSet(ObjectOutputStream, AttributeSet)
+ *
+ * @param out - stream to write to
+ * @param a - the attribute set
+ * @throws IOException - any I/O error
+ */
+ public void writeAttributes(ObjectOutputStream out, AttributeSet a)
+ throws IOException
+ {
+ writeAttributeSet(out, a);
+ }
+
+ /**
+ * Registers an attribute key as a well-known keys. When an attribute with
+ * such a key is written to a stream, a special syntax is used so that it
+ * can be recognized when it is read back in. All attribute keys defined
+ * in <code>StyleContext</code> are registered as static keys. If you define
+ * additional attribute keys that you want to exist as nonreplicated objects,
+ * then you should register them using this method.
+ *
+ * @param key the key to register as static attribute key
+ */
+ public static void registerStaticAttributeKey(Object key)
+ {
+ String io = key.getClass().getName() + "." + key.toString();
+ if (writeAttributeKeys == null)
+ writeAttributeKeys = new Hashtable();
+ if (readAttributeKeys == null)
+ readAttributeKeys = new Hashtable();
+ writeAttributeKeys.put(key, io);
+ readAttributeKeys.put(io, key);
+ }
+
+ /**
+ * Returns a string representation of this StyleContext.
+ *
+ * @return a string representation of this StyleContext
+ */
+ public String toString()
+ {
+ cleanupPool();
+ StringBuilder b = new StringBuilder();
+ Iterator i = attributeSetPool.keySet().iterator();
+ while (i.hasNext())
+ {
+ Object att = i.next();
+ b.append(att);
+ b.append('\n');
+ }
+ return b.toString();
+ }
+
+ /**
+ * Searches the AttributeSet pool and returns a pooled instance if available,
+ * or pool a new one.
+ *
+ * @return an immutable attribute set that equals the current search key
+ */
+ private AttributeSet searchImmutableSet()
+ {
+ SmallAttributeSet k = createSmallAttributeSet(search);
+ WeakReference ref = (WeakReference) attributeSetPool.get(k);
+ SmallAttributeSet a;
+ if (ref == null || (a = (SmallAttributeSet) ref.get()) == null)
+ {
+ a = k;
+ attributeSetPool.put(a, new WeakReference(a));
+ }
+ return a;
+ }
+
+ /**
+ * Cleans up the attribute set pool from entries that are no longer
+ * referenced.
+ */
+ private void cleanupPool()
+ {
+ // TODO: How else can we force cleaning up the WeakHashMap?
+ attributeSetPool.size();
+ }
+
+ /**
+ * Returns a MutableAttributeSet that holds a. If a itself is mutable,
+ * this returns a itself, otherwise it creates a new SimpleAtttributeSet
+ * via {@link #createLargeAttributeSet(AttributeSet)}.
+ *
+ * @param a the AttributeSet to create a mutable set for
+ *
+ * @return a mutable attribute set that corresponds to a
+ */
+ private MutableAttributeSet getMutableAttributeSet(AttributeSet a)
+ {
+ MutableAttributeSet mas;
+ if (a instanceof MutableAttributeSet)
+ mas = (MutableAttributeSet) a;
+ else
+ mas = createLargeAttributeSet(a);
+ return mas;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/StyledDocument.java b/libjava/classpath/javax/swing/text/StyledDocument.java
new file mode 100644
index 000000000..49e471c9c
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/StyledDocument.java
@@ -0,0 +1,140 @@
+/* StyledDcoument.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * StyledDocument
+ * @author Andrew Selkirk
+ * @version 1.0
+ */
+public interface StyledDocument extends Document
+{
+ /**
+ * addStyle
+ * @param nm TODO
+ * @param parent TODO
+ * @returns Style
+ */
+ Style addStyle(String nm, Style parent);
+
+ /**
+ * removeStyle
+ * @param nm TODO
+ */
+ void removeStyle(String nm);
+
+ /**
+ * getStyle
+ * @param nm TODO
+ * @returns Style
+ */
+ Style getStyle(String nm);
+
+ /**
+ * setCharacterAttributes
+ * @param offset TODO
+ * @param length TODO
+ * @param set TODO
+ * @param replace TODO
+ */
+ void setCharacterAttributes(int offset, int length, AttributeSet set,
+ boolean replace);
+
+ /**
+ * setParagraphAttributes
+ * @param offset TODO
+ * @param length TODO
+ * @param set TODO
+ * @param replace TODO
+ */
+ void setParagraphAttributes(int offset, int length, AttributeSet set,
+ boolean replace);
+
+ /**
+ * getLogicalStyle
+ * @param position TODO
+ * @returns Style
+ */
+ Style getLogicalStyle(int position);
+
+ /**
+ * setLogicalStyle
+ * @param position TODO
+ * @param style TODO
+ */
+ void setLogicalStyle(int position, Style style);
+
+ /**
+ * getParagraphElement
+ * @param position TODO
+ * @returns Element
+ */
+ Element getParagraphElement(int position);
+
+ /**
+ * getCharacterElement
+ * @param position TODO
+ * @returns Element
+ */
+ Element getCharacterElement(int position);
+
+ /**
+ * getForeground
+ * @param set TODO
+ * @returns Color
+ */
+ Color getForeground(AttributeSet set);
+
+ /**
+ * getBackground
+ * @param set TODO
+ * @returns Color
+ */
+ Color getBackground(AttributeSet set);
+
+ /**
+ * getFont
+ * @param set TODO
+ * @returns Font
+ */
+ Font getFont(AttributeSet set);
+
+}
diff --git a/libjava/classpath/javax/swing/text/StyledEditorKit.java b/libjava/classpath/javax/swing/text/StyledEditorKit.java
new file mode 100644
index 000000000..744585f08
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/StyledEditorKit.java
@@ -0,0 +1,707 @@
+/* StyledEditorKit.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.event.ActionEvent;
+
+import javax.swing.Action;
+import javax.swing.JEditorPane;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+
+/**
+ * An {@link EditorKit} that supports editing styled text.
+ *
+ * @author Andrew Selkirk
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class StyledEditorKit extends DefaultEditorKit
+{
+ /** The serialVersionUID. */
+ private static final long serialVersionUID = 7002391892985555948L;
+
+ /**
+ * Toggles the underline attribute for the selected text.
+ */
+ public static class UnderlineAction extends StyledEditorKit.StyledTextAction
+ {
+ /**
+ * Creates an instance of <code>UnderlineAction</code>.
+ */
+ public UnderlineAction()
+ {
+ super("font-underline");
+ }
+
+ /**
+ * Performs the action.
+ *
+ * @param event the <code>ActionEvent</code> that describes the action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JEditorPane editor = getEditor(event);
+ StyledDocument doc = getStyledDocument(editor);
+ Element el = doc.getCharacterElement(editor.getSelectionStart());
+ boolean isUnderline = StyleConstants.isUnderline(el.getAttributes());
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ StyleConstants.setUnderline(atts, ! isUnderline);
+ setCharacterAttributes(editor, atts, false);
+ }
+ }
+
+ /**
+ * Toggles the italic attribute for the selected text.
+ */
+ public static class ItalicAction extends StyledEditorKit.StyledTextAction
+ {
+ /**
+ * Creates an instance of <code>ItalicAction</code>.
+ */
+ public ItalicAction()
+ {
+ super("font-italic");
+ }
+
+ /**
+ * Performs the action.
+ *
+ * @param event the <code>ActionEvent</code> that describes the action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JEditorPane editor = getEditor(event);
+ StyledDocument doc = getStyledDocument(editor);
+ Element el = doc.getCharacterElement(editor.getSelectionStart());
+ boolean isItalic = StyleConstants.isItalic(el.getAttributes());
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ StyleConstants.setItalic(atts, ! isItalic);
+ setCharacterAttributes(editor, atts, false);
+ }
+ }
+
+ /**
+ * Toggles the bold attribute for the selected text.
+ */
+ public static class BoldAction extends StyledEditorKit.StyledTextAction
+ {
+ /**
+ * Creates an instance of <code>BoldAction</code>.
+ */
+ public BoldAction()
+ {
+ super("font-bold");
+ }
+
+ /**
+ * Performs the action.
+ *
+ * @param event the <code>ActionEvent</code> that describes the action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JEditorPane editor = getEditor(event);
+ StyledDocument doc = getStyledDocument(editor);
+ Element el = doc.getCharacterElement(editor.getSelectionStart());
+ boolean isBold = StyleConstants.isBold(el.getAttributes());
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ StyleConstants.setBold(atts, ! isBold);
+ setCharacterAttributes(editor, atts, false);
+ }
+ }
+
+ /**
+ * Sets the alignment attribute on the selected text.
+ */
+ public static class AlignmentAction extends StyledEditorKit.StyledTextAction
+ {
+ /**
+ * The aligment to set.
+ */
+ private int a;
+
+ /**
+ * Creates a new instance of <code>AlignmentAction</code> to set the
+ * alignment to <code>a</code>.
+ *
+ * @param nm the name of the Action
+ * @param a the alignment to set
+ */
+ public AlignmentAction(String nm, int a)
+ {
+ super(nm);
+ this.a = a;
+ }
+
+ /**
+ * Performs the action.
+ *
+ * @param event the <code>ActionEvent</code> that describes the action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ StyleConstants.setAlignment(atts, a);
+ setParagraphAttributes(getEditor(event), atts, false);
+ }
+ }
+
+ /**
+ * Sets the foreground color attribute on the selected text.
+ */
+ public static class ForegroundAction extends StyledEditorKit.StyledTextAction
+ {
+ /**
+ * The foreground color to set.
+ */
+ private Color fg;
+
+ /**
+ * Creates a new instance of <code>ForegroundAction</code> to set the
+ * foreground color to <code>fg</code>.
+ *
+ * @param nm the name of the Action
+ * @param fg the foreground color to set
+ */
+ public ForegroundAction(String nm, Color fg)
+ {
+ super(nm);
+ this.fg = fg;
+ }
+
+ /**
+ * Performs the action.
+ *
+ * @param event the <code>ActionEvent</code> that describes the action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ StyleConstants.setForeground(atts, fg);
+ setCharacterAttributes(getEditor(event), atts, false);
+ }
+ }
+
+ /**
+ * Sets the font size attribute on the selected text.
+ */
+ public static class FontSizeAction extends StyledEditorKit.StyledTextAction
+ {
+ /**
+ * The font size to set.
+ */
+ private int size;
+
+ /**
+ * Creates a new instance of <code>FontSizeAction</code> to set the
+ * font size to <code>size</code>.
+ *
+ * @param nm the name of the Action
+ * @param size the font size to set
+ */
+ public FontSizeAction(String nm, int size)
+ {
+ super(nm);
+ this.size = size;
+ }
+
+ /**
+ * Performs the action.
+ *
+ * @param event the <code>ActionEvent</code> that describes the action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ StyleConstants.setFontSize(atts, size);
+ setCharacterAttributes(getEditor(event), atts, false);
+ }
+ }
+
+ /**
+ * Sets the font family attribute on the selected text.
+ */
+ public static class FontFamilyAction extends StyledEditorKit.StyledTextAction
+ {
+ /**
+ * The font family to set.
+ */
+ private String family;
+
+ /**
+ * Creates a new instance of <code>FontFamilyAction</code> to set the
+ * font family to <code>family</code>.
+ *
+ * @param nm the name of the Action
+ * @param family the font family to set
+ */
+ public FontFamilyAction(String nm, String family)
+ {
+ super(nm);
+ this.family = family;
+ }
+
+ /**
+ * Performs the action.
+ *
+ * @param event the <code>ActionEvent</code> that describes the action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ StyleConstants.setFontFamily(atts, family);
+ setCharacterAttributes(getEditor(event), atts, false);
+ }
+ }
+
+ /**
+ * The abstract superclass of all styled TextActions. This class
+ * provides some useful methods to manipulate the text attributes.
+ */
+ public abstract static class StyledTextAction extends TextAction
+ {
+ /**
+ * Creates a new instance of <code>StyledTextAction</code>.
+ *
+ * @param nm the name of the <code>StyledTextAction</code>
+ */
+ public StyledTextAction(String nm)
+ {
+ super(nm);
+ }
+
+ /**
+ * Returns the <code>JEditorPane</code> component from which the
+ * <code>ActionEvent</code> originated.
+ *
+ * @param event the <code>ActionEvent</code>
+ * @return the <code>JEditorPane</code> component from which the
+ * <code>ActionEvent</code> originated
+ */
+ protected final JEditorPane getEditor(ActionEvent event)
+ {
+ return (JEditorPane) getTextComponent(event);
+ }
+
+ /**
+ * Sets the specified character attributes on the currently selected
+ * text of <code>editor</code>. If <code>editor</code> does not have
+ * a selection, then the attributes are used as input attributes
+ * for newly inserted content.
+ *
+ * @param editor the <code>JEditorPane</code> component
+ * @param atts the text attributes to set
+ * @param replace if <code>true</code> the current attributes of the
+ * selection are replaces, otherwise they are merged
+ */
+ protected final void setCharacterAttributes(JEditorPane editor,
+ AttributeSet atts,
+ boolean replace)
+ {
+ int p0 = editor.getSelectionStart();
+ int p1 = editor.getSelectionEnd();
+ if (p0 != p1)
+ {
+ StyledDocument doc = getStyledDocument(editor);
+ doc.setCharacterAttributes(p0, p1 - p0, atts, replace);
+ }
+ // Update input attributes.
+ StyledEditorKit kit = getStyledEditorKit(editor);
+ MutableAttributeSet inputAtts = kit.getInputAttributes();
+ if (replace)
+ {
+ inputAtts.removeAttributes(inputAtts);
+ }
+ inputAtts.addAttributes(atts);
+ }
+
+ /**
+ * Returns the {@link StyledDocument} that is used by <code>editor</code>.
+ *
+ * @param editor the <code>JEditorPane</code> from which to get the
+ * <code>StyledDocument</code>
+ *
+ * @return the {@link StyledDocument} that is used by <code>editor</code>
+ */
+ protected final StyledDocument getStyledDocument(JEditorPane editor)
+ {
+ Document doc = editor.getDocument();
+ if (!(doc instanceof StyledDocument))
+ throw new AssertionError("The Document for StyledEditorKits is "
+ + "expected to be a StyledDocument.");
+
+ return (StyledDocument) doc;
+ }
+
+ /**
+ * Returns the {@link StyledEditorKit} that is used by <code>editor</code>.
+ *
+ * @param editor the <code>JEditorPane</code> from which to get the
+ * <code>StyledEditorKit</code>
+ *
+ * @return the {@link StyledEditorKit} that is used by <code>editor</code>
+ */
+ protected final StyledEditorKit getStyledEditorKit(JEditorPane editor)
+ {
+ EditorKit kit = editor.getEditorKit();
+ if (!(kit instanceof StyledEditorKit))
+ throw new AssertionError("The EditorKit for StyledDocuments is "
+ + "expected to be a StyledEditorKit.");
+
+ return (StyledEditorKit) kit;
+ }
+
+ /**
+ * Sets the specified character attributes on the paragraph that
+ * contains the currently selected
+ * text of <code>editor</code>. If <code>editor</code> does not have
+ * a selection, then the attributes are set on the paragraph that
+ * contains the current caret position.
+ *
+ * @param editor the <code>JEditorPane</code> component
+ * @param atts the text attributes to set
+ * @param replace if <code>true</code> the current attributes of the
+ * selection are replaces, otherwise they are merged
+ */
+ protected final void setParagraphAttributes(JEditorPane editor,
+ AttributeSet atts,
+ boolean replace)
+ {
+ Document doc = editor.getDocument();
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument styleDoc = (StyledDocument) editor.getDocument();
+ EditorKit kit = editor.getEditorKit();
+ if (!(kit instanceof StyledEditorKit))
+ {
+ StyledEditorKit styleKit = (StyledEditorKit) kit;
+ int start = editor.getSelectionStart();
+ int end = editor.getSelectionEnd();
+ int dot = editor.getCaret().getDot();
+ if (start == dot && end == dot)
+ {
+ // If there is no selection, then we only update the
+ // input attributes.
+ MutableAttributeSet inputAttributes =
+ styleKit.getInputAttributes();
+ inputAttributes.addAttributes(atts);
+ }
+ else
+ styleDoc.setParagraphAttributes(start, end, atts, replace);
+ }
+ else
+ throw new AssertionError("The EditorKit for StyledTextActions "
+ + "is expected to be a StyledEditorKit");
+ }
+ else
+ throw new AssertionError("The Document for StyledTextActions is "
+ + "expected to be a StyledDocument.");
+ }
+ }
+
+ /**
+ * A {@link ViewFactory} that is able to create {@link View}s for
+ * the <code>Element</code>s that are supported by
+ * <code>StyledEditorKit</code>, namely the following types of Elements:
+ *
+ * <ul>
+ * <li>{@link AbstractDocument#ContentElementName}</li>
+ * <li>{@link AbstractDocument#ParagraphElementName}</li>
+ * <li>{@link AbstractDocument#SectionElementName}</li>
+ * <li>{@link StyleConstants#ComponentElementName}</li>
+ * <li>{@link StyleConstants#IconElementName}</li>
+ * </ul>
+ */
+ static class StyledViewFactory
+ implements ViewFactory
+ {
+ /**
+ * Creates a {@link View} for the specified <code>Element</code>.
+ *
+ * @param element the <code>Element</code> to create a <code>View</code>
+ * for
+ * @return the <code>View</code> for the specified <code>Element</code>
+ * or <code>null</code> if the type of <code>element</code> is
+ * not supported
+ */
+ public View create(Element element)
+ {
+ String name = element.getName();
+ View view = null;
+ if (name.equals(AbstractDocument.ContentElementName))
+ view = new LabelView(element);
+ else if (name.equals(AbstractDocument.ParagraphElementName))
+ view = new ParagraphView(element);
+ else if (name.equals(AbstractDocument.SectionElementName))
+ view = new BoxView(element, View.Y_AXIS);
+ else if (name.equals(StyleConstants.ComponentElementName))
+ view = new ComponentView(element);
+ else if (name.equals(StyleConstants.IconElementName))
+ view = new IconView(element);
+ else
+ throw new AssertionError("Unknown Element type: "
+ + element.getClass().getName() + " : "
+ + name);
+ return view;
+ }
+ }
+
+ /**
+ * Keeps track of the caret position and updates the currentRun
+ * <code>Element</code> and the <code>inputAttributes</code>.
+ */
+ class CaretTracker
+ implements CaretListener
+ {
+ /**
+ * Notifies an update of the caret position.
+ *
+ * @param ev the event for the caret update
+ */
+ public void caretUpdate(CaretEvent ev)
+ {
+ Object source = ev.getSource();
+ if (!(source instanceof JTextComponent))
+ throw new AssertionError("CaretEvents are expected to come from a"
+ + "JTextComponent.");
+
+ JTextComponent text = (JTextComponent) source;
+ Document doc = text.getDocument();
+ if (!(doc instanceof StyledDocument))
+ throw new AssertionError("The Document used by StyledEditorKits is"
+ + "expected to be a StyledDocument");
+
+ StyledDocument styleDoc = (StyledDocument) doc;
+ currentRun = styleDoc.getCharacterElement(ev.getDot());
+ createInputAttributes(currentRun, inputAttributes);
+ }
+ }
+
+ /**
+ * Stores the <code>Element</code> at the current caret position. This
+ * is updated by {@link CaretTracker}.
+ */
+ Element currentRun;
+
+ /**
+ * The current input attributes. This is updated by {@link CaretTracker}.
+ */
+ MutableAttributeSet inputAttributes;
+
+ /**
+ * The CaretTracker that keeps track of the current input attributes, and
+ * the current character run Element.
+ */
+ CaretTracker caretTracker;
+
+ /**
+ * The ViewFactory for StyledEditorKits.
+ */
+ StyledViewFactory viewFactory;
+
+ /**
+ * Creates a new instance of <code>StyledEditorKit</code>.
+ */
+ public StyledEditorKit()
+ {
+ inputAttributes = new SimpleAttributeSet();
+ }
+
+ /**
+ * Creates an exact copy of this <code>StyledEditorKit</code>.
+ *
+ * @return an exact copy of this <code>StyledEditorKit</code>
+ */
+ public Object clone()
+ {
+ StyledEditorKit clone = (StyledEditorKit) super.clone();
+ // FIXME: Investigate which fields must be copied.
+ return clone;
+ }
+
+ /**
+ * Returns the <code>Action</code>s supported by this {@link EditorKit}.
+ * This includes the {@link BoldAction}, {@link ItalicAction} and
+ * {@link UnderlineAction} as well as the <code>Action</code>s supported
+ * by {@link DefaultEditorKit}.
+ *
+ * The other <code>Action</code>s of <code>StyledEditorKit</code> are not
+ * returned here, since they require a parameter and thus custom
+ * instantiation.
+ *
+ * @return the <code>Action</code>s supported by this {@link EditorKit}
+ */
+ public Action[] getActions()
+ {
+ Action[] actions1 = super.getActions();
+ Action[] myActions = new Action[] {
+ new FontSizeAction("font-size-8", 8),
+ new FontSizeAction("font-size-10", 10),
+ new FontSizeAction("font-size-12", 12),
+ new FontSizeAction("font-size-14", 14),
+ new FontSizeAction("font-size-16", 16),
+ new FontSizeAction("font-size-18", 18),
+ new FontSizeAction("font-size-24", 24),
+ new FontSizeAction("font-size-36", 36),
+ new FontSizeAction("font-size-48", 48),
+ new FontFamilyAction("font-family-Serif", "Serif"),
+ new FontFamilyAction("font-family-Monospaced", "Monospaced"),
+ new FontFamilyAction("font-family-SansSerif", "SansSerif"),
+ new AlignmentAction("left-justify", StyleConstants.ALIGN_LEFT),
+ new AlignmentAction("center-justify", StyleConstants.ALIGN_CENTER),
+ new AlignmentAction("right-justify", StyleConstants.ALIGN_RIGHT),
+ new BoldAction(),
+ new ItalicAction(),
+ new UnderlineAction()
+ };
+ return TextAction.augmentList(actions1, myActions);
+ }
+
+ /**
+ * Returns the current input attributes. These are automatically set on
+ * any newly inserted content, if not specified otherwise.
+ *
+ * @return the current input attributes
+ */
+ public MutableAttributeSet getInputAttributes()
+ {
+ return inputAttributes;
+ }
+
+ /**
+ * Returns the {@link Element} that represents the character run at the
+ * current caret position.
+ *
+ * @return the {@link Element} that represents the character run at the
+ * current caret position
+ */
+ public Element getCharacterAttributeRun()
+ {
+ return currentRun;
+ }
+
+ /**
+ * Creates the default {@link Document} supported by this
+ * <code>EditorKit</code>. This is an instance of
+ * {@link DefaultStyledDocument} in this case but may be overridden by
+ * subclasses.
+ *
+ * @return an instance of <code>DefaultStyledDocument</code>
+ */
+ public Document createDefaultDocument()
+ {
+ return new DefaultStyledDocument();
+ }
+
+ /**
+ * Installs this <code>EditorKit</code> on the specified {@link JEditorPane}.
+ * This basically involves setting up required listeners on the
+ * <code>JEditorPane</code>.
+ *
+ * @param component the <code>JEditorPane</code> to install this
+ * <code>EditorKit</code> on
+ */
+ public void install(JEditorPane component)
+ {
+ CaretTracker tracker = new CaretTracker();
+ component.addCaretListener(tracker);
+ }
+
+ /**
+ * Deinstalls this <code>EditorKit</code> from the specified
+ * {@link JEditorPane}. This basically involves removing all listeners from
+ * <code>JEditorPane</code> that have been set up by this
+ * <code>EditorKit</code>.
+ *
+ * @param component the <code>JEditorPane</code> from which to deinstall this
+ * <code>EditorKit</code>
+ */
+ public void deinstall(JEditorPane component)
+ {
+ CaretTracker t = caretTracker;
+ if (t != null)
+ component.removeCaretListener(t);
+ caretTracker = null;
+ }
+
+ /**
+ * Returns a {@link ViewFactory} that is able to create {@link View}s
+ * for {@link Element}s that are supported by this <code>EditorKit</code>,
+ * namely the following types of <code>Element</code>s:
+ *
+ * <ul>
+ * <li>{@link AbstractDocument#ContentElementName}</li>
+ * <li>{@link AbstractDocument#ParagraphElementName}</li>
+ * <li>{@link AbstractDocument#SectionElementName}</li>
+ * <li>{@link StyleConstants#ComponentElementName}</li>
+ * <li>{@link StyleConstants#IconElementName}</li>
+ * </ul>
+ *
+ * @return a {@link ViewFactory} that is able to create {@link View}s
+ * for {@link Element}s that are supported by this <code>EditorKit</code>
+ */
+ public ViewFactory getViewFactory()
+ {
+ if (viewFactory == null)
+ viewFactory = new StyledViewFactory();
+ return viewFactory;
+ }
+
+ /**
+ * Copies the text attributes from <code>element</code> to <code>set</code>.
+ * This is called everytime when the caret position changes to keep
+ * track of the current input attributes. The attributes in <code>set</code>
+ * are cleaned before adding the attributes of <code>element</code>.
+ *
+ * This method filters out attributes for element names, <code>Icon</code>s
+ * and <code>Component</code>s.
+ *
+ * @param element the <code>Element</code> from which to copy the text
+ * attributes
+ * @param set the inputAttributes to copy the attributes to
+ */
+ protected void createInputAttributes(Element element,
+ MutableAttributeSet set)
+ {
+ // FIXME: Filter out component, icon and element name attributes.
+ set.removeAttributes(set);
+ set.addAttributes(element.getAttributes());
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/TabExpander.java b/libjava/classpath/javax/swing/text/TabExpander.java
new file mode 100644
index 000000000..a1e4571cf
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/TabExpander.java
@@ -0,0 +1,43 @@
+/* TabExpander.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+public interface TabExpander
+{
+ float nextTabStop(float x, int tabOffset);
+}
diff --git a/libjava/classpath/javax/swing/text/TabSet.java b/libjava/classpath/javax/swing/text/TabSet.java
new file mode 100644
index 000000000..c32596536
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/TabSet.java
@@ -0,0 +1,210 @@
+/* TabSet.java --
+ Copyright (C) 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.Serializable;
+
+/**
+ * A set of tab stops. Instances of this class are immutable.
+ */
+public class TabSet implements Serializable
+{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 2367703481999080593L;
+
+ /** Storage for the tab stops. */
+ TabStop[] tabs;
+
+ /**
+ * Creates a new <code>TabSet</code> containing the specified tab stops.
+ *
+ * @param t the tab stops (<code>null</code> permitted).
+ */
+ public TabSet(TabStop[] t)
+ {
+ if (t != null)
+ tabs = (TabStop[]) t.clone();
+ else
+ tabs = new TabStop[0];
+ }
+
+ /**
+ * Returns the tab stop with the specified index.
+ *
+ * @param i the index.
+ *
+ * @return The tab stop.
+ *
+ * @throws IllegalArgumentException if <code>i</code> is not in the range
+ * <code>0</code> to <code>getTabCount() - 1</code>.
+ */
+ public TabStop getTab(int i)
+ {
+ if (i < 0 || i >= tabs.length)
+ throw new IllegalArgumentException("Index out of bounds.");
+ return tabs[i];
+ }
+
+ /**
+ * Returns the tab following the specified location.
+ *
+ * @param location the location.
+ *
+ * @return The tab following the specified location (or <code>null</code>).
+ */
+ public TabStop getTabAfter(float location)
+ {
+ int idx = getTabIndexAfter(location);
+ if (idx == -1)
+ return null;
+ else
+ return tabs[idx];
+ }
+
+ /**
+ * Returns the number of tab stops in this tab set.
+ *
+ * @return The number of tab stops in this tab set.
+ */
+ public int getTabCount()
+ {
+ return tabs.length;
+ }
+
+ /**
+ * Returns the index of the specified tab, or -1 if the tab is not found.
+ *
+ * @param tab the tab (<code>null</code> permitted).
+ *
+ * @return The index of the specified tab, or -1.
+ */
+ public int getTabIndex(TabStop tab)
+ {
+ for (int i = 0; i < tabs.length; ++i)
+ if (tabs[i] == tab)
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the index of the tab at or after the specified location.
+ *
+ * @param location the tab location.
+ *
+ * @return The index of the tab stop, or -1.
+ */
+ public int getTabIndexAfter(float location)
+ {
+ for (int i = 0; i < tabs.length; i++)
+ {
+ if (location <= tabs[i].getPosition())
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Tests this <code>TabSet</code> for equality with an arbitrary object.
+ *
+ * @param obj the object (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if this <code>TabSet</code> is equal to
+ * <code>obj</code>, and <code>false</code> otherwise.
+ *
+ * @since 1.5
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof TabSet))
+ return false;
+ TabSet that = (TabSet) obj;
+ int tabCount = getTabCount();
+ if (tabCount != that.getTabCount())
+ return false;
+ for (int i = 0; i < tabCount; i++)
+ {
+ if (!this.getTab(i).equals(that.getTab(i)))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns a hash code for this <code>TabSet</code>.
+ *
+ * @return A hash code.
+ *
+ * @since 1.5
+ */
+ public int hashCode()
+ {
+ // this hash code won't match Sun's, but that shouldn't matter...
+ int result = 193;
+ int tabs = getTabCount();
+ for (int i = 0; i < tabs; i++)
+ {
+ TabStop t = getTab(i);
+ if (t != null)
+ result = 37 * result + t.hashCode();
+ }
+ return result;
+ }
+
+ /**
+ * Returns a string representation of this <code>TabSet</code>.
+ *
+ * @return A string representation of this <code>TabSet</code>.
+ */
+ public String toString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ 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/libjava/classpath/javax/swing/text/TabStop.java b/libjava/classpath/javax/swing/text/TabStop.java
new file mode 100644
index 000000000..03a56116f
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/TabStop.java
@@ -0,0 +1,190 @@
+/* TabStop.java --
+ Copyright (C) 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Represents a tab position in some text.
+ */
+public class TabStop implements Serializable
+{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -5381995917363605058L;
+
+ 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;
+
+ /**
+ * Creates a new <code>TabStop</code> for the specified tab position.
+ *
+ * @param pos the tab position.
+ */
+ public TabStop(float pos)
+ {
+ this(pos, ALIGN_LEFT, LEAD_NONE);
+ }
+
+ /**
+ * Creates a new <code>TabStop</code> with the specified attributes.
+ *
+ * @param pos the tab position.
+ * @param align the alignment (one of {@link #ALIGN_LEFT},
+ * {@link #ALIGN_CENTER}, {@link #ALIGN_RIGHT}, {@link #ALIGN_DECIMAL}
+ * or {@link #ALIGN_BAR}).
+ * @param leader the leader (one of {@link #LEAD_NONE}, {@link #LEAD_DOTS},
+ * {@link #LEAD_EQUALS}, {@link #LEAD_HYPHENS}, {@link #LEAD_THICKLINE}
+ * or {@link #LEAD_UNDERLINE}).
+ */
+ public TabStop(float pos, int align, int leader)
+ {
+ this.pos = pos;
+ this.align = align;
+ this.leader = leader;
+ }
+
+ /**
+ * Tests this <code>TabStop</code> for equality with an arbitrary object.
+ *
+ * @param other the other object (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if this <code>TabStop</code> is equal to
+ * the specified object, and <code>false</code> otherwise.
+ */
+ 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());
+ }
+
+ /**
+ * Returns the tab alignment. This should be one of {@link #ALIGN_LEFT},
+ * {@link #ALIGN_CENTER}, {@link #ALIGN_RIGHT}, {@link #ALIGN_DECIMAL} or
+ * {@link #ALIGN_BAR}.
+ *
+ * @return The tab alignment.
+ */
+ public int getAlignment()
+ {
+ return align;
+ }
+
+ /**
+ * Returns the leader type. This should be one of {@link #LEAD_NONE},
+ * {@link #LEAD_DOTS}, {@link #LEAD_EQUALS}, {@link #LEAD_HYPHENS},
+ * {@link #LEAD_THICKLINE} or {@link #LEAD_UNDERLINE}.
+ *
+ * @return The leader type.
+ */
+ public int getLeader()
+ {
+ return leader;
+ }
+
+ /**
+ * Returns the tab position.
+ *
+ * @return The tab position.
+ */
+ public float getPosition()
+ {
+ return pos;
+ }
+
+ /**
+ * Returns a hash code for this <code>TabStop</code>.
+ *
+ * @return A hash code.
+ */
+ public int hashCode()
+ {
+ return (int) pos + (int) leader + (int) align;
+ }
+
+ /**
+ * Returns a string describing this <code>TabStop</code>.
+ *
+ * @return A string describing this <code>TabStop</code>.
+ */
+ public String toString()
+ {
+ String prefix = "";
+ switch (align)
+ {
+ 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/libjava/classpath/javax/swing/text/TabableView.java b/libjava/classpath/javax/swing/text/TabableView.java
new file mode 100644
index 000000000..e50270bb6
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/TabableView.java
@@ -0,0 +1,44 @@
+/* TabableView.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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+public interface TabableView
+{
+ float getPartialSpan(int p0, int p1);
+ float getTabbedSpan(float x, TabExpander expander);
+}
diff --git a/libjava/classpath/javax/swing/text/TableView.java b/libjava/classpath/javax/swing/text/TableView.java
new file mode 100644
index 000000000..bdf5004a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/TableView.java
@@ -0,0 +1,491 @@
+/* TableView.java -- A view impl for tables inside styled text
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A {@link View} implementation for rendering tables inside styled text.
+ * Tables are rendered as vertical boxes (see {@link BoxView}). These boxes
+ * have a number of child views, which are the rows of the table. These are
+ * horizontal boxes containing the actuall cells of the table. These cells
+ * can be arbitrary view implementations and are fetched via the
+ * {@link ViewFactory} returned by {@link View#getViewFactory}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public abstract class TableView
+ extends BoxView
+{
+
+ /**
+ * A view implementation that renders a row of a <code>TableView</code>.
+ * This is implemented as a horizontal box that contains the actual cells
+ * of the table.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class TableRow
+ extends BoxView
+ {
+ /**
+ * Creates a new instance of <code>TableRow</code>.
+ *
+ * @param el the element for which to create a row view
+ */
+ public TableRow(Element el)
+ {
+ super(el, X_AXIS);
+ }
+
+ /**
+ * Replaces some child views with a new set of child views. This is
+ * implemented to call the superclass behaviour and invalidates the row
+ * grid so that rows and columns will be recalculated.
+ *
+ * @param offset the start offset at which to replace views
+ * @param length the number of views to remove
+ * @param views the new set of views
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ super.replace(offset, length, views);
+ int viewCount = getViewCount();
+ if (columnRequirements == null
+ || viewCount > columnRequirements.length)
+ {
+ columnRequirements = new SizeRequirements[viewCount];
+ for (int i = 0; i < columnRequirements.length; i++)
+ columnRequirements[i] = new SizeRequirements();
+ }
+ if (columnOffsets == null || columnOffsets.length < viewCount)
+ columnOffsets = new int[viewCount];
+ if (columnSpans == null || columnSpans.length < viewCount)
+ columnSpans = new int[viewCount];
+ layoutChanged(X_AXIS);
+ }
+
+ /**
+ * Lays out the box's child views along the major axis. This is
+ * reimplemented so that the child views all have the width of their
+ * column.
+ *
+ * @param targetSpan the total span of the view
+ * @param axis the axis that is laid out
+ * @param offsets an array that holds the offsets of the child views after
+ * this method returned
+ * @param spans an array that holds the spans of the child views after this
+ * method returned
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ // Some sanity checks. If these preconditions are not met, then the
+ // following code will not work. Also, there must be something
+ // seriously wrong then.
+ assert(offsets.length == columnOffsets.length);
+ assert(spans.length == columnSpans.length);
+ assert(offsets.length == spans.length);
+ for (int i = 0; i < offsets.length; ++i)
+ {
+ offsets[i] = columnOffsets[i];
+ spans[i] = columnSpans[i];
+ }
+ }
+
+ /**
+ * Lays out the box's child views along the minor axis (the orthogonal axis
+ * to the major axis). This is reimplemented to call the super behaviour
+ * and then adjust the span of the child views that span multiple rows.
+ *
+ * @param targetSpan the total span of the view
+ * @param axis the axis that is laid out
+ * @param offsets an array that holds the offsets of the child views after
+ * this method returned
+ * @param spans an array that holds the spans of the child views after this
+ * method returned
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ // FIXME: Figure out how to fetch the row heights from the TableView's
+ // element.
+ super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Determines the resizeability of this view along the specified axis.
+ *
+ * @param axis the axis of which to fetch the resizability
+ *
+ * @return the resize weight or &lt;= 0 if this view is not resizable
+ *
+ * @throws IllegalArgumentException when an illegal axis is specified
+ */
+ public int getResizeWeight(int axis)
+ {
+ // TODO: Figure out if this is ok. I would think so, but better test
+ // this.
+ return 0;
+ }
+
+ /**
+ * Returns the child view that represents the specified position in the
+ * model. This is reimplemented because in this view we do not necessarily
+ * have a one to one mapping of child elements to child views.
+ *
+ * @param pos the model position for which to query the view
+ * @param a the allocation of this view
+ *
+ * @return the view that corresponds to the specified model position or
+ * <code>null</code> if there is none
+ */
+ protected View getViewAtPosition(int pos, Rectangle a)
+ {
+ // FIXME: Do not call super here. Instead walk through the child views
+ // and look for a range that contains the given position.
+ return super.getViewAtPosition(pos, a);
+ }
+ }
+
+ /**
+ * This class is deprecated and not used anymore. Table cells are
+ * rendered by an arbitrary <code>View</code> implementation.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @deprecated Table cells are now rendered by an arbitrary <code>View</code>
+ * implementation.
+ */
+ public class TableCell
+ extends BoxView
+ {
+
+ /**
+ * The row number of this cell.
+ */
+ private int row;
+
+ /**
+ * The column number of this cell.
+ */
+ private int column;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param el the element
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public TableCell(Element el)
+ {
+ super(el, X_AXIS);
+ }
+
+ /**
+ * Returns the number of columns that this cell spans.
+ *
+ * @return the number of columns that this cell spans
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public int getColumnCount()
+ {
+ // TODO: Figure out if this is right. However, this is not so important
+ // since this class isn't used anyway (except maybe be application code
+ // that still uses this deprecated class).
+ return 1;
+ }
+
+ /**
+ * Returns the number of rows that this cell spans.
+ *
+ * @return the number of rows that this cell spans
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public int getRowCount()
+ {
+ // TODO: Figure out if this is right. However, this is not so important
+ // since this class isn't used anyway (except maybe be application code
+ // that still uses this deprecated class).
+ return 1;
+ }
+
+ /**
+ * Sets the grid location of this table cell.
+ *
+ * @param r the row of this cell
+ * @param c the column of this cell
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public void setGridLocation(int r, int c)
+ {
+ row = r;
+ column = c;
+ }
+
+ /**
+ * Returns the row number of this cell.
+ *
+ * @return the row number of this cell
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public int getGridRow()
+ {
+ return row;
+ }
+
+ /**
+ * Returns the column number of this cell.
+ *
+ * @return the column number of this cell
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ public int getGridColumn()
+ {
+ return column;
+ }
+ }
+
+ /**
+ * The offsets of the columns of this table. Package private to avoid
+ * synthetic accessor methods.
+ */
+ int[] columnOffsets;
+
+ /**
+ * The spans of the columns of this table. Package private to avoid
+ * synthetic accessor methods.
+ */
+ int[] columnSpans;
+
+ /**
+ * The size requirements of the columns.
+ */
+ SizeRequirements[] columnRequirements = new SizeRequirements[0];
+
+ /**
+ * Creates a new instance of <code>TableView</code>.
+ *
+ * @param el the element for which to create a table view
+ */
+ public TableView(Element el)
+ {
+ super(el, Y_AXIS);
+ }
+
+ /**
+ * Replaces a number of child views with a set of new child views. This is
+ * implemented to call the superclass behaviour and invalidate the layout.
+ *
+ * @param offset the offset at which to replace child views
+ * @param length the number of child views to remove
+ * @param views the new set of views
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ super.replace(offset, length, views);
+ layoutChanged(Y_AXIS);
+ }
+
+ /**
+ * Creates a view for a table row.
+ *
+ * @param el the element that represents the table row
+ *
+ * @return a view for rendering the table row
+ */
+ protected TableRow createTableRow(Element el)
+ {
+ return new TableRow(el);
+ }
+
+ /**
+ * Creates a view for a table cell. This method is deprecated and not used
+ * anymore.
+ *
+ * @param el the element that represents the table cell
+ *
+ * @return a view for rendering the table cell
+ *
+ * @deprecated Table cells are now rendered by an arbitrary
+ * <code>View</code> implementation.
+ */
+ protected TableCell createTableCell(Element el)
+ {
+ return new TableCell(el);
+ }
+
+ protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e,
+ Shape a, ViewFactory vf)
+ {
+ // TODO: Figure out what to do here.
+ }
+
+ /**
+ * Lays out the columns to fit within the specified target span.
+ *
+ * @param targetSpan the total span for the columns
+ * @param offsets an array that holds the offsets of the columns when this
+ * method returns
+ * @param spans an array that holds the spans of the columns when this method
+ * returns
+ * @param reqs the size requirements for each column
+ */
+ protected void layoutColumns(int targetSpan, int[] offsets, int spans[],
+ SizeRequirements[] reqs)
+ {
+ updateColumnRequirements();
+ SizeRequirements r = calculateMinorAxisRequirements(X_AXIS, null);
+ SizeRequirements.calculateTiledPositions(targetSpan, r, columnRequirements,
+ offsets, spans);
+ }
+
+ /**
+ * Lays out the child views along the minor axis of the table (that is the
+ * horizontal axis). This is implemented to call {@link #layoutColumns} to
+ * layout the column layout of this table, and then forward to the superclass
+ * to actually lay out the rows.
+ *
+ * @param targetSpan the available span along the minor (horizontal) axis
+ * @param axis the axis
+ * @param offsets an array that holds the offsets of the columns when this
+ * method returns
+ * @param spans an array that holds the spans of the columns when this method
+ * returns
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ // TODO: Prepare size requirements for the columns.
+ layoutColumns(targetSpan, columnOffsets, columnSpans, columnRequirements);
+ super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Calculates the requirements of this view for the minor (== horizontal)
+ * axis.
+ *
+ * This is reimplemented to calculate the requirements as the sum of the
+ * size requirements of the columns.
+ *
+ * @param axis the axis
+ * @param req the size requirements object to use, if <code>null</code> a new
+ * one will be created
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements req)
+ {
+ // TODO: Maybe prepare columnRequirements.
+ SizeRequirements res = req;
+ if (res == null)
+ res = new SizeRequirements();
+ else
+ {
+ res.alignment = 0.5f;
+ res.maximum = 0;
+ res.minimum = 0;
+ res.preferred = 0;
+ }
+
+ for (int i = 0; i < columnRequirements.length; ++i)
+ {
+ res.minimum += columnRequirements[i].minimum;
+ res.preferred += columnRequirements[i].preferred;
+ res.maximum += columnRequirements[i].maximum;
+ // TODO: Do we have to handle alignment somehow?
+ }
+ return res;
+ }
+
+ /**
+ * Returns the child view that represents the specified position in the
+ * model. This is reimplemented because in this view we do not necessarily
+ * have a one to one mapping of child elements to child views.
+ *
+ * @param pos the model position for which to query the view
+ * @param a the allocation of this view
+ *
+ * @return the view that corresponds to the specified model position or
+ * <code>null</code> if there is none
+ */
+ protected View getViewAtPosition(int pos, Rectangle a)
+ {
+ // FIXME: Do not call super here. Instead walk through the child views
+ // and look for a range that contains the given position.
+ return super.getViewAtPosition(pos, a);
+ }
+
+ /**
+ * Updates the column requirements.
+ */
+ private void updateColumnRequirements()
+ {
+ int rowCount = getViewCount();
+ for (int r = 0; r < rowCount; ++r)
+ {
+ TableRow row = (TableRow) getView(r);
+ int columnCount = row.getViewCount();
+ for (int c = 0; c < columnCount; ++c)
+ {
+ View cell = row.getView(c);
+ SizeRequirements cr = columnRequirements[c];
+ cr.minimum = Math.max(cr.minimum, (int) cell.getMinimumSpan(X_AXIS));
+ cr.preferred = Math.max(cr.preferred,
+ (int) cell.getPreferredSpan(X_AXIS));
+ cr.maximum = Math.max(cr.maximum, (int) cell.getMaximumSpan(X_AXIS));
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/TextAction.java b/libjava/classpath/javax/swing/text/TextAction.java
new file mode 100644
index 000000000..70e19ef1d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/TextAction.java
@@ -0,0 +1,240 @@
+/* TextAction.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Component;
+import java.awt.KeyboardFocusManager;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+/**
+ * TextAction
+ * @author Andrew Selkirk
+ */
+public abstract class TextAction extends AbstractAction
+{
+ /**
+ * Constructor TextAction
+ * @param name TODO
+ */
+ public TextAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Returns the <code>JTextComponent</code> object associated with the given
+ * <code>ActionEvent</code>. If the source of the event is not a
+ * <code>JTextComponent</code> the currently focused text component is returned.
+ *
+ * @param event the action event
+ *
+ * @return the <code>JTextComponent</code>
+ */
+ protected final JTextComponent getTextComponent(ActionEvent event)
+ {
+ JTextComponent target = null;
+ if (event != null)
+ {
+ Object source = event.getSource();
+ if (source instanceof JTextComponent)
+ target = (JTextComponent) source;
+ }
+ if (target == null)
+ target = getFocusedComponent();
+ return target;
+ }
+
+ /**
+ * Creates a new array of <code>Action</code> containing both given arrays.
+ *
+ * @param list1 the first action array
+ * @param list2 the second action array
+ *
+ * @return the augmented array of actions
+ */
+ public static final Action[] augmentList(Action[] list1, Action[] list2)
+ {
+ HashMap<Object,Action> actions = new HashMap<Object,Action>();
+
+ for (int i = 0; i < list1.length; ++i)
+ {
+ Action a = list1[i];
+ Object name = a.getValue(Action.NAME);
+ actions.put(name != null ? name : "", a);
+ }
+
+ for (int i = 0; i < list2.length; ++i)
+ {
+ Action a = list2[i];
+ Object name = a.getValue(Action.NAME);
+ actions.put(name != null ? name : "", a);
+ }
+ Action[] augmented = new Action[actions.size()];
+
+ int i = 0;
+ for (Iterator<Action> it = actions.values().iterator(); it.hasNext(); i++)
+ augmented[i] = it.next();
+ return augmented;
+
+ }
+
+ /**
+ * Returns the current focused <code>JTextComponent</code> object.
+ *
+ * @return the <code>JTextComponent</code>
+ */
+ protected final JTextComponent getFocusedComponent()
+ {
+ KeyboardFocusManager kfm =
+ KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ Component focused = kfm.getPermanentFocusOwner();
+ JTextComponent textComp = null;
+ if (focused instanceof JTextComponent)
+ textComp = (JTextComponent) focused;
+ return textComp;
+ }
+
+ /** Abstract helper class which implements everything needed for an
+ * Action implementation in <code>DefaultEditorKit</code> which
+ * does horizontal movement (and selection).
+ */
+ abstract static class HorizontalMovementAction extends TextAction
+ {
+ int dir;
+
+ HorizontalMovementAction(String name, int direction)
+ {
+ super(name);
+ dir = direction;
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ try
+ {
+ if (t != null)
+ {
+ int offs
+ = Utilities.getNextVisualPositionFrom(t,
+ t.getCaretPosition(),
+ dir);
+
+ Caret c = t.getCaret();
+
+ actionPerformedImpl(c, offs);
+
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ throw
+ (InternalError) new InternalError("Illegal offset").initCause(ble);
+ }
+
+ }
+
+ protected abstract void actionPerformedImpl(Caret c, int offs)
+ throws BadLocationException;
+ }
+
+ /** Abstract helper class which implements everything needed for an
+ * Action implementation in <code>DefaultEditorKit</code> which
+ * does vertical movement (and selection).
+ */
+ abstract static class VerticalMovementAction extends TextAction
+ {
+ int dir;
+
+ VerticalMovementAction(String name, int direction)
+ {
+ super(name);
+ dir = direction;
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextComponent t = getTextComponent(event);
+ try
+ {
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ // The magic caret position may be null when the caret
+ // has not moved yet.
+ Point mcp = c.getMagicCaretPosition();
+
+ int pos;
+ if (mcp != null)
+ {
+ mcp.y = t.modelToView(c.getDot()).y;
+ pos = t.viewToModel(mcp);
+ }
+ else
+ pos = c.getDot();
+
+ pos = Utilities.getNextVisualPositionFrom(t,
+ t.getCaretPosition(),
+ dir);
+
+ if (pos > -1)
+ actionPerformedImpl(c, pos);
+ }
+ }
+ catch(BadLocationException ble)
+ {
+ throw
+ (InternalError) new InternalError("Illegal offset").initCause(ble);
+ }
+ }
+
+ protected abstract void actionPerformedImpl(Caret c, int offs)
+ throws BadLocationException;
+
+ }
+
+
+}
diff --git a/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java
new file mode 100644
index 000000000..622139232
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/Utilities.java
@@ -0,0 +1,730 @@
+/* Utilities.java --
+ Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+import java.awt.Point;
+import java.text.BreakIterator;
+
+import javax.swing.text.Position.Bias;
+
+/**
+ * A set of utilities to deal with text. This is used by several other classes
+ * inside this package.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+public class Utilities
+{
+
+ /**
+ * 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}.
+ *
+ *
+ * The X and Y coordinates denote the start of the <em>baseline</em> where
+ * the text should be drawn.
+ *
+ * @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 font metrics of the current selected font.
+ FontMetrics metrics = g.getFontMetrics();
+
+ int ascent = metrics.getAscent();
+
+ // The current x and y pixel coordinates.
+ int pixelX = x;
+
+ int pos = s.offset;
+ int len = 0;
+
+ int end = s.offset + s.count;
+
+ for (int offset = s.offset; offset < end; ++offset)
+ {
+ char c = buffer[offset];
+ switch (c)
+ {
+ case '\t':
+ if (len > 0) {
+ g.drawChars(buffer, pos, len, pixelX, y);
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ len = 0;
+ }
+ pos = offset+1;
+ if (e != null)
+ pixelX = (int) e.nextTabStop((float) pixelX, startOffset + offset
+ - s.offset);
+ else
+ pixelX += metrics.charWidth(' ');
+ x = pixelX;
+ break;
+ case '\n':
+ case '\r':
+ if (len > 0) {
+ g.drawChars(buffer, pos, len, pixelX, y);
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ len = 0;
+ }
+ x = pixelX;
+ break;
+ default:
+ len += 1;
+ }
+ }
+
+ if (len > 0)
+ {
+ g.drawChars(buffer, pos, len, pixelX, y);
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ }
+
+ 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;
+
+ int end = s.offset + s.count;
+ int count = 0;
+ for (int offset = s.offset; offset < end; 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(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.charsWidth(buffer, offset - count, count);
+ count = 0;
+ break;
+ default:
+ count++;
+ }
+ }
+
+ // Take the last line into account.
+ pixelX += metrics.charsWidth(buffer, end - count, count);
+
+ return pixelX - x;
+ }
+
+ /**
+ * Provides a facility to map screen coordinates into a model location. For a
+ * given text fragment and start location within this fragment, this method
+ * determines the model location so that the resulting fragment fits best
+ * into the span <code>[x0, x]</code>.
+ *
+ * The parameter <code>round</code> controls which model location is returned
+ * if the view coordinates are on a character: If <code>round</code> is
+ * <code>true</code>, then the result is rounded up to the next character, so
+ * that the resulting fragment is the smallest fragment that is larger than
+ * the specified span. If <code>round</code> is <code>false</code>, then the
+ * resulting fragment is the largest fragment that is smaller than the
+ * specified span.
+ *
+ * @param s the text segment
+ * @param fm the font metrics to use
+ * @param x0 the starting screen location
+ * @param x the target screen location at which the requested fragment should
+ * end
+ * @param te the tab expander to use; if this is <code>null</code>, TABs are
+ * expanded to one space character
+ * @param p0 the starting model location
+ * @param round if <code>true</code> round up to the next location, otherwise
+ * round down to the current location
+ *
+ * @return the model location, so that the resulting fragment fits within the
+ * specified span
+ */
+ public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0,
+ int x, TabExpander te, int p0,
+ boolean round)
+ {
+ int found = s.count;
+ int currentX = x0;
+ int nextX = currentX;
+
+ int end = s.offset + s.count;
+ for (int pos = s.offset; pos < end && found == s.count; pos++)
+ {
+ char nextChar = s.array[pos];
+
+ if (nextChar != '\t')
+ nextX += fm.charWidth(nextChar);
+ else
+ {
+ if (te == null)
+ nextX += fm.charWidth(' ');
+ else
+ nextX += ((int) te.nextTabStop(nextX, p0 + pos - s.offset));
+ }
+
+ if (x >= currentX && x < nextX)
+ {
+ // Found position.
+ if ((! round) || ((x - currentX) < (nextX - x)))
+ {
+ found = pos - s.offset;
+ }
+ else
+ {
+ found = pos + 1 - s.offset;
+ }
+ }
+ currentX = nextX;
+ }
+
+ return found;
+ }
+
+ /**
+ * Provides a facility to map screen coordinates into a model location. For a
+ * given text fragment and start location within this fragment, this method
+ * determines the model location so that the resulting fragment fits best
+ * into the span <code>[x0, x]</code>.
+ *
+ * This method rounds up to the next location, so that the resulting fragment
+ * will be the smallest fragment of the text, that is greater than the
+ * specified span.
+ *
+ * @param s the text segment
+ * @param fm the font metrics to use
+ * @param x0 the starting screen location
+ * @param x the target screen location at which the requested fragment should
+ * end
+ * @param te the tab expander to use; if this is <code>null</code>, TABs are
+ * expanded to one space character
+ * @param p0 the starting model location
+ *
+ * @return the model location, so that the resulting fragment fits within the
+ * specified span
+ */
+ public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0,
+ int x, TabExpander te, int p0)
+ {
+ return getTabbedTextOffset(s, fm, x0, x, te, p0, true);
+ }
+
+ /**
+ * Finds the start of the next word for the given offset.
+ *
+ * @param c
+ * the text component
+ * @param offs
+ * the offset in the document
+ * @return the location in the model of the start of the next word.
+ * @throws BadLocationException
+ * if the offset is invalid.
+ */
+ public static final int getNextWord(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ if (offs < 0 || offs > (c.getText().length() - 1))
+ throw new BadLocationException("invalid offset specified", offs);
+ String text = c.getText();
+ BreakIterator wb = BreakIterator.getWordInstance();
+ wb.setText(text);
+
+ int last = wb.following(offs);
+ int current = wb.next();
+ int cp;
+
+ while (current != BreakIterator.DONE)
+ {
+ for (int i = last; i < current; i++)
+ {
+ cp = text.codePointAt(i);
+
+ // Return the last found bound if there is a letter at the current
+ // location or is not whitespace (meaning it is a number or
+ // punctuation). The first case means that 'last' denotes the
+ // beginning of a word while the second case means it is the start
+ // of something else.
+ if (Character.isLetter(cp)
+ || !Character.isWhitespace(cp))
+ return last;
+ }
+ last = current;
+ current = wb.next();
+ }
+
+ throw new BadLocationException("no more words", offs);
+ }
+
+ /**
+ * Finds the start of the previous word for the given offset.
+ *
+ * @param c
+ * the text component
+ * @param offs
+ * the offset in the document
+ * @return the location in the model of the start of the previous word.
+ * @throws BadLocationException
+ * if the offset is invalid.
+ */
+ public static final int getPreviousWord(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ String text = c.getText();
+
+ if (offs <= 0 || offs > text.length())
+ throw new BadLocationException("invalid offset specified", offs);
+
+ BreakIterator wb = BreakIterator.getWordInstance();
+ wb.setText(text);
+ int last = wb.preceding(offs);
+ int current = wb.previous();
+ int cp;
+
+ while (current != BreakIterator.DONE)
+ {
+ for (int i = last; i < offs; i++)
+ {
+ cp = text.codePointAt(i);
+
+ // Return the last found bound if there is a letter at the current
+ // location or is not whitespace (meaning it is a number or
+ // punctuation). The first case means that 'last' denotes the
+ // beginning of a word while the second case means it is the start
+ // of some else.
+ if (Character.isLetter(cp)
+ || !Character.isWhitespace(cp))
+ return last;
+ }
+ last = current;
+ current = wb.previous();
+ }
+
+ return 0;
+ }
+
+ /**
+ * Finds the start of a word for the given location.
+ * @param c the text component
+ * @param offs the offset location
+ * @return the location of the word beginning
+ * @throws BadLocationException if the offset location is invalid
+ */
+ public static final int getWordStart(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ String text = c.getText();
+
+ if (offs < 0 || offs > text.length())
+ throw new BadLocationException("invalid offset specified", offs);
+
+ BreakIterator wb = BreakIterator.getWordInstance();
+ wb.setText(text);
+
+ if (wb.isBoundary(offs))
+ return offs;
+
+ return wb.preceding(offs);
+ }
+
+ /**
+ * Finds the end of a word for the given location.
+ * @param c the text component
+ * @param offs the offset location
+ * @return the location of the word end
+ * @throws BadLocationException if the offset location is invalid
+ */
+ public static final int getWordEnd(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ if (offs < 0 || offs >= c.getText().length())
+ throw new BadLocationException("invalid offset specified", offs);
+
+ String text = c.getText();
+ BreakIterator wb = BreakIterator.getWordInstance();
+ wb.setText(text);
+ return wb.following(offs);
+ }
+
+ /**
+ * Get the model position of the end of the row that contains the
+ * specified model position. Return null if the given JTextComponent
+ * does not have a size.
+ * @param c the JTextComponent
+ * @param offs the model position
+ * @return the model position of the end of the row containing the given
+ * offset
+ * @throws BadLocationException if the offset is invalid
+ */
+ public static final int getRowEnd(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ String text = c.getText();
+ if (text == null)
+ return -1;
+
+ // Do a binary search for the smallest position X > offs
+ // such that that character at positino X is not on the same
+ // line as the character at position offs
+ int high = offs + ((text.length() - 1 - offs) / 2);
+ int low = offs;
+ int oldHigh = text.length() + 1;
+ while (true)
+ {
+ if (c.modelToView(high).y != c.modelToView(offs).y)
+ {
+ oldHigh = high;
+ high = low + ((high + 1 - low) / 2);
+ if (oldHigh == high)
+ return high - 1;
+ }
+ else
+ {
+ low = high;
+ high += ((oldHigh - high) / 2);
+ if (low == high)
+ return low;
+ }
+ }
+ }
+
+ /**
+ * Get the model position of the start of the row that contains the specified
+ * model position. Return null if the given JTextComponent does not have a
+ * size.
+ *
+ * @param c the JTextComponent
+ * @param offs the model position
+ * @return the model position of the start of the row containing the given
+ * offset
+ * @throws BadLocationException if the offset is invalid
+ */
+ public static final int getRowStart(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ String text = c.getText();
+ if (text == null)
+ return -1;
+
+ // Do a binary search for the greatest position X < offs
+ // such that the character at position X is not on the same
+ // row as the character at position offs
+ int high = offs;
+ int low = 0;
+ int oldLow = 0;
+ while (true)
+ {
+ if (c.modelToView(low).y != c.modelToView(offs).y)
+ {
+ oldLow = low;
+ low = high - ((high + 1 - low) / 2);
+ if (oldLow == low)
+ return low + 1;
+ }
+ else
+ {
+ high = low;
+ low -= ((low - oldLow) / 2);
+ if (low == high)
+ return low;
+ }
+ }
+ }
+
+ /**
+ * Determine where to break the text in the given Segment, attempting to find
+ * a word boundary.
+ * @param s the Segment that holds the text
+ * @param metrics the font metrics used for calculating the break point
+ * @param x0 starting view location representing the start of the text
+ * @param x the target view location
+ * @param e the TabExpander used for expanding tabs (if this is null tabs
+ * are expanded to 1 space)
+ * @param startOffset the offset in the Document of the start of the text
+ * @return the offset at which we should break the text
+ */
+ public static final int getBreakLocation(Segment s, FontMetrics metrics,
+ int x0, int x, TabExpander e,
+ int startOffset)
+ {
+ int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset,
+ false);
+ int breakLoc = mark;
+ // If mark is equal to the end of the string, just use that position.
+ if (mark < s.count - 1)
+ {
+ for (int i = s.offset + mark; i >= s.offset; i--)
+ {
+ char ch = s.array[i];
+ if (ch < 256)
+ {
+ // For ASCII simply scan backwards for whitespace.
+ if (Character.isWhitespace(ch))
+ {
+ breakLoc = i - s.offset + 1;
+ break;
+ }
+ }
+ else
+ {
+ // Only query BreakIterator for complex chars.
+ BreakIterator bi = BreakIterator.getLineInstance();
+ bi.setText(s);
+ int pos = bi.preceding(i + 1);
+ if (pos > s.offset)
+ {
+ breakLoc = breakLoc - s.offset;
+ }
+ break;
+ }
+ }
+ }
+ return breakLoc;
+ }
+
+ /**
+ * Returns the paragraph element in the text component <code>c</code> at
+ * the specified location <code>offset</code>.
+ *
+ * @param c the text component
+ * @param offset the offset of the paragraph element to return
+ *
+ * @return the paragraph element at <code>offset</code>
+ */
+ public static final Element getParagraphElement(JTextComponent c, int offset)
+ {
+ Document doc = c.getDocument();
+ Element par = null;
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument styledDoc = (StyledDocument) doc;
+ par = styledDoc.getParagraphElement(offset);
+ }
+ else
+ {
+ Element root = c.getDocument().getDefaultRootElement();
+ int parIndex = root.getElementIndex(offset);
+ par = root.getElement(parIndex);
+ }
+ return par;
+ }
+
+ /**
+ * Returns the document position that is closest above to the specified x
+ * coordinate in the row containing <code>offset</code>.
+ *
+ * @param c the text component
+ * @param offset the offset
+ * @param x the x coordinate
+ *
+ * @return the document position that is closest above to the specified x
+ * coordinate in the row containing <code>offset</code>
+ *
+ * @throws BadLocationException if <code>offset</code> is not a valid offset
+ */
+ public static final int getPositionAbove(JTextComponent c, int offset, int x)
+ throws BadLocationException
+ {
+ int offs = getRowStart(c, offset);
+
+ if(offs == -1)
+ return -1;
+
+ // Effectively calculates the y value of the previous line.
+ Point pt = c.modelToView(offs-1).getLocation();
+
+ pt.x = x;
+
+ // Calculate a simple fitting offset.
+ offs = c.viewToModel(pt);
+
+ // Find out the real x positions of the calculated character and its
+ // neighbour.
+ int offsX = c.modelToView(offs).getLocation().x;
+ int offsXNext = c.modelToView(offs+1).getLocation().x;
+
+ // Chose the one which is nearer to us and return its offset.
+ if (Math.abs(offsX-x) <= Math.abs(offsXNext-x))
+ return offs;
+ else
+ return offs+1;
+ }
+
+ /**
+ * Returns the document position that is closest below to the specified x
+ * coordinate in the row containing <code>offset</code>.
+ *
+ * @param c the text component
+ * @param offset the offset
+ * @param x the x coordinate
+ *
+ * @return the document position that is closest above to the specified x
+ * coordinate in the row containing <code>offset</code>
+ *
+ * @throws BadLocationException if <code>offset</code> is not a valid offset
+ */
+ public static final int getPositionBelow(JTextComponent c, int offset, int x)
+ throws BadLocationException
+ {
+ int offs = getRowEnd(c, offset);
+
+ if(offs == -1)
+ return -1;
+
+ Point pt = null;
+
+ // Note: Some views represent the position after the last
+ // typed character others do not. Converting offset 3 in "a\nb"
+ // in a PlainView will return a valid rectangle while in a
+ // WrappedPlainView this will throw a BadLocationException.
+ // This behavior has been observed in the RI.
+ try
+ {
+ // Effectively calculates the y value of the next line.
+ pt = c.modelToView(offs+1).getLocation();
+ }
+ catch(BadLocationException ble)
+ {
+ return offset;
+ }
+
+ pt.x = x;
+
+ // Calculate a simple fitting offset.
+ offs = c.viewToModel(pt);
+
+ if (offs == c.getDocument().getLength())
+ return offs;
+
+ // Find out the real x positions of the calculated character and its
+ // neighbour.
+ int offsX = c.modelToView(offs).getLocation().x;
+ int offsXNext = c.modelToView(offs+1).getLocation().x;
+
+ // Chose the one which is nearer to us and return its offset.
+ if (Math.abs(offsX-x) <= Math.abs(offsXNext-x))
+ return offs;
+ else
+ return offs+1;
+ }
+
+ /** This is an internal helper method which is used by the
+ * <code>javax.swing.text</code> package. It simply delegates the
+ * call to a method with the same name on the <code>NavigationFilter</code>
+ * of the provided <code>JTextComponent</code> (if it has one) or its UI.
+ *
+ * If the underlying method throws a <code>BadLocationException</code> it
+ * will be swallowed and the initial offset is returned.
+ */
+ static int getNextVisualPositionFrom(JTextComponent t, int offset, int direction)
+ {
+ NavigationFilter nf = t.getNavigationFilter();
+
+ try
+ {
+ return (nf != null)
+ ? nf.getNextVisualPositionFrom(t,
+ offset,
+ Bias.Forward,
+ direction,
+ new Position.Bias[1])
+ : t.getUI().getNextVisualPositionFrom(t,
+ offset,
+ Bias.Forward,
+ direction,
+ new Position.Bias[1]);
+ }
+ catch (BadLocationException ble)
+ {
+ return offset;
+ }
+
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java
new file mode 100644
index 000000000..e3c795735
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/View.java
@@ -0,0 +1,881 @@
+/* View.java --
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Container;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+
+public abstract class View implements SwingConstants
+{
+ public static final int BadBreakWeight = 0;
+ public static final int ExcellentBreakWeight = 2000;
+ public static final int ForcedBreakWeight = 3000;
+ public static final int GoodBreakWeight = 1000;
+
+ public static final int X_AXIS = 0;
+ public static final int Y_AXIS = 1;
+
+ private Element elt;
+ private View parent;
+
+ /**
+ * Creates a new <code>View</code> instance.
+ *
+ * @param elem an <code>Element</code> value
+ */
+ public View(Element elem)
+ {
+ elt = elem;
+ }
+
+ public abstract void paint(Graphics g, Shape s);
+
+ /**
+ * Sets the parent for this view. This is the first method that is beeing
+ * called on a view to setup the view hierarchy. This is also the last method
+ * beeing called when the view is disconnected from the view hierarchy, in
+ * this case <code>parent</code> is null.
+ *
+ * If <code>parent</code> is <code>null</code>, a call to this method also
+ * calls <code>setParent</code> on the children, thus disconnecting them from
+ * the view hierarchy. That means that super must be called when this method
+ * is overridden.
+ *
+ * @param parent the parent to set, <code>null</code> when this view is
+ * beeing disconnected from the view hierarchy
+ */
+ public void setParent(View parent)
+ {
+ if (parent == null)
+ {
+ int numChildren = getViewCount();
+ for (int i = 0; i < numChildren; i++)
+ {
+ View child = getView(i);
+ // It is important that we only reset the parent on views that
+ // actually belong to us. In FlowView the child may already be
+ // reparented.
+ if (child.getParent() == this)
+ child.setParent(null);
+ }
+ }
+
+ this.parent = parent;
+ }
+
+ public View getParent()
+ {
+ return parent;
+ }
+
+ public Container getContainer()
+ {
+ View parent = getParent();
+ if (parent == null)
+ return null;
+ else
+ return parent.getContainer();
+ }
+
+ public Document getDocument()
+ {
+ return getElement().getDocument();
+ }
+
+ public Element getElement()
+ {
+ return elt;
+ }
+
+ /**
+ * Returns the preferred span along the specified axis. Normally the view is
+ * rendered with the span returned here if that is possible.
+ *
+ * @param axis the axis
+ *
+ * @return the preferred span along the specified axis
+ */
+ public abstract float getPreferredSpan(int axis);
+
+ /**
+ * Returns the resize weight of this view. A value of <code>0</code> or less
+ * means this view is not resizeable. Positive values make the view
+ * resizeable. The default implementation returns <code>0</code>
+ * unconditionally.
+ *
+ * @param axis the axis
+ *
+ * @return the resizability of this view along the specified axis
+ */
+ public int getResizeWeight(int axis)
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the maximum span along the specified axis. The default
+ * implementation will forward to
+ * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)}
+ * returns a value > 0, in which case this returns {@link Integer#MIN_VALUE}.
+ *
+ * @param axis the axis
+ *
+ * @return the maximum span along the specified axis
+ */
+ public float getMaximumSpan(int axis)
+ {
+ float max = Integer.MAX_VALUE;
+ if (getResizeWeight(axis) <= 0)
+ max = getPreferredSpan(axis);
+ return max;
+ }
+
+ /**
+ * Returns the minimum span along the specified axis. The default
+ * implementation will forward to
+ * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)}
+ * returns a value > 0, in which case this returns <code>0</code>.
+ *
+ * @param axis the axis
+ *
+ * @return the minimum span along the specified axis
+ */
+ public float getMinimumSpan(int axis)
+ {
+ float min = 0;
+ if (getResizeWeight(axis) <= 0)
+ min = getPreferredSpan(axis);
+ return min;
+ }
+
+ public void setSize(float width, float height)
+ {
+ // The default implementation does nothing.
+ }
+
+ /**
+ * Returns the alignment of this view along the baseline of the parent view.
+ * An alignment of <code>0.0</code> will align this view with the left edge
+ * along the baseline, an alignment of <code>0.5</code> will align it
+ * centered to the baseline, an alignment of <code>1.0</code> will align
+ * the right edge along the baseline.
+ *
+ * The default implementation returns 0.5 unconditionally.
+ *
+ * @param axis the axis
+ *
+ * @return the alignment of this view along the parents baseline for the
+ * specified axis
+ */
+ public float getAlignment(int axis)
+ {
+ return 0.5f;
+ }
+
+ public AttributeSet getAttributes()
+ {
+ return getElement().getAttributes();
+ }
+
+ public boolean isVisible()
+ {
+ return true;
+ }
+
+ public int getViewCount()
+ {
+ return 0;
+ }
+
+ public View getView(int index)
+ {
+ return null;
+ }
+
+ public ViewFactory getViewFactory()
+ {
+ View parent = getParent();
+ return parent != null ? parent.getViewFactory() : null;
+ }
+
+ /**
+ * Replaces a couple of child views with new child views. If
+ * <code>length == 0</code> then this is a simple insertion, if
+ * <code>views == null</code> this only removes some child views.
+ *
+ * @param offset the offset at which to replace
+ * @param length the number of child views to be removed
+ * @param views the new views to be inserted, may be <code>null</code>
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ // Default implementation does nothing.
+ }
+
+ public void insert(int offset, View view)
+ {
+ View[] array = { view };
+ replace(offset, 1, array);
+ }
+
+ public void append(View view)
+ {
+ View[] array = { view };
+ int offset = getViewCount();
+ replace(offset, 0, array);
+ }
+
+ public void removeAll()
+ {
+ replace(0, getViewCount(), null);
+ }
+
+ public void remove(int index)
+ {
+ replace(index, 1, null);
+ }
+
+ public View createFragment(int p0, int p1)
+ {
+ // The default implementation doesn't support fragmentation.
+ return this;
+ }
+
+ public int getStartOffset()
+ {
+ return getElement().getStartOffset();
+ }
+
+ public int getEndOffset()
+ {
+ return getElement().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);
+
+ String text = null;
+ if (index >= 0)
+ {
+ allocation = getChildAllocation(index, allocation);
+ Rectangle r = allocation instanceof Rectangle ? (Rectangle) allocation
+ : allocation.getBounds();
+ if (r.contains(x, y))
+ text = getView(index).getToolTipText(x, y, allocation);
+ }
+ return text;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public Graphics getGraphics()
+ {
+ return getContainer().getGraphics();
+ }
+
+ public void preferenceChanged(View child, boolean width, boolean height)
+ {
+ View p = getParent();
+ if (p != null)
+ p.preferenceChanged(this, width, height);
+ }
+
+ public int getBreakWeight(int axis, float pos, float len)
+ {
+ int weight = BadBreakWeight;
+ if (len > getPreferredSpan(axis))
+ weight = GoodBreakWeight;
+ return weight;
+ }
+
+ public View breakView(int axis, int offset, float pos, float len)
+ {
+ return this;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public int getViewIndex(int pos, Position.Bias b)
+ {
+ return -1;
+ }
+
+ /**
+ * Receive notification about an insert update to the text model.
+ *
+ * The default implementation of this method does the following:
+ * <ul>
+ * <li>Call {@link #updateChildren} if the element that this view is
+ * responsible for has changed. This makes sure that the children can
+ * correctly represent the model.<li>
+ * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
+ * the child views.<li>
+ * <li>Call {@link #updateLayout}. Gives the view a chance to either
+ * repair its layout, reschedule layout or do nothing at all.</li>
+ * </ul>
+ *
+ * @param ev the DocumentEvent that describes the change
+ * @param shape the shape of the view
+ * @param vf the ViewFactory for creating child views
+ */
+ public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ if (getViewCount() > 0)
+ {
+ Element el = getElement();
+ DocumentEvent.ElementChange ec = ev.getChange(el);
+ if (ec != null)
+ {
+ if (! updateChildren(ec, ev, vf))
+ ec = null;
+ }
+ forwardUpdate(ec, ev, shape, vf);
+ updateLayout(ec, ev, shape);
+ }
+ }
+
+ /**
+ * Receive notification about a remove update to the text model.
+ *
+ * The default implementation of this method does the following:
+ * <ul>
+ * <li>Call {@link #updateChildren} if the element that this view is
+ * responsible for has changed. This makes sure that the children can
+ * correctly represent the model.<li>
+ * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
+ * the child views.<li>
+ * <li>Call {@link #updateLayout}. Gives the view a chance to either
+ * repair its layout, reschedule layout or do nothing at all.</li>
+ * </ul>
+ *
+ * @param ev the DocumentEvent that describes the change
+ * @param shape the shape of the view
+ * @param vf the ViewFactory for creating child views
+ */
+ public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ Element el = getElement();
+ DocumentEvent.ElementChange ec = ev.getChange(el);
+ if (ec != null)
+ {
+ if (! updateChildren(ec, ev, vf))
+ ec = null;
+ }
+ forwardUpdate(ec, ev, shape, vf);
+ updateLayout(ec, ev, shape);
+ }
+
+ /**
+ * Receive notification about a change update to the text model.
+ *
+ * The default implementation of this method does the following:
+ * <ul>
+ * <li>Call {@link #updateChildren} if the element that this view is
+ * responsible for has changed. This makes sure that the children can
+ * correctly represent the model.<li>
+ * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to
+ * the child views.<li>
+ * <li>Call {@link #updateLayout}. Gives the view a chance to either
+ * repair its layout, reschedule layout or do nothing at all.</li>
+ * </ul>
+ *
+ * @param ev the DocumentEvent that describes the change
+ * @param shape the shape of the view
+ * @param vf the ViewFactory for creating child views
+ */
+ public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ if (getViewCount() > 0)
+ {
+ Element el = getElement();
+ DocumentEvent.ElementChange ec = ev.getChange(el);
+ if (ec != null)
+ {
+ if (! updateChildren(ec, ev, vf))
+ ec = null;
+ }
+ forwardUpdate(ec, ev, shape, vf);
+ updateLayout(ec, ev, shape);
+ }
+ }
+
+ /**
+ * Updates the list of children that is returned by {@link #getView}
+ * and {@link #getViewCount}.
+ *
+ * Element that are specified as beeing added in the ElementChange record are
+ * assigned a view for using the ViewFactory. Views of Elements that
+ * are specified as beeing removed are removed from the list.
+ *
+ * @param ec the ElementChange record that describes the change of the
+ * element
+ * @param ev the DocumentEvent describing the change of the document model
+ * @param vf the ViewFactory to use for creating new views
+ *
+ * @return whether or not the child views represent the child elements of
+ * the element that this view is responsible for. Some views may
+ * create views that are responsible only for parts of the element
+ * that they are responsible for and should then return false.
+ *
+ * @since 1.3
+ */
+ protected boolean updateChildren(DocumentEvent.ElementChange ec,
+ DocumentEvent ev,
+ ViewFactory vf)
+ {
+ Element[] added = ec.getChildrenAdded();
+ Element[] removed = ec.getChildrenRemoved();
+ int index = ec.getIndex();
+
+ View[] newChildren = null;
+ if (added != null)
+ {
+ newChildren = new View[added.length];
+ for (int i = 0; i < added.length; ++i)
+ newChildren[i] = vf.create(added[i]);
+ }
+ int numRemoved = removed != null ? removed.length : 0;
+ replace(index, numRemoved, newChildren);
+
+ return true;
+ }
+
+ /**
+ * Forwards the DocumentEvent to child views that need to get notified
+ * of the change to the model. This calles {@link #forwardUpdateToView}
+ * for each View that must be forwarded to.
+ *
+ * If <code>ec</code> is not <code>null</code> (this means there have been
+ * structural changes to the element that this view is responsible for) this
+ * method should recognize this and don't notify newly added child views.
+ *
+ * @param ec the ElementChange describing the element changes (may be
+ * <code>null</code> if there were no changes)
+ * @param ev the DocumentEvent describing the changes to the model
+ * @param shape the current allocation of the view
+ * @param vf the ViewFactory used to create new Views
+ *
+ * @since 1.3
+ */
+ protected void forwardUpdate(DocumentEvent.ElementChange ec,
+ DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ int count = getViewCount();
+ if (count > 0)
+ {
+ // Determine start index.
+ int startOffset = ev.getOffset();
+ int startIndex = getViewIndex(startOffset, Position.Bias.Backward);
+
+ // For REMOVE events we have to forward the event to the last element,
+ // for the case that an Element has been removed that represente
+ // the offset.
+ if (startIndex == -1 && ev.getType() == DocumentEvent.EventType.REMOVE
+ && startOffset >= getEndOffset())
+ {
+ startIndex = getViewCount() - 1;
+ }
+
+ // When startIndex is on a view boundary, forward event to the
+ // previous view too.
+ if (startIndex >= 0)
+ {
+ View v = getView(startIndex);
+ if (v != null)
+ {
+ if (v.getStartOffset() == startOffset && startOffset > 0)
+ startIndex = Math.max(0, startIndex - 1);
+ }
+ }
+ startIndex = Math.max(0, startIndex);
+
+ // Determine end index.
+ int endIndex = startIndex;
+ if (ev.getType() != DocumentEvent.EventType.REMOVE)
+ {
+ endIndex = getViewIndex(startOffset + ev.getLength(),
+ Position.Bias.Forward);
+ if (endIndex < 0)
+ endIndex = getViewCount() - 1;
+ }
+
+ // Determine hole that comes from added elements (we don't forward
+ // the event to newly added views.
+ int startAdded = endIndex + 1;
+ int endAdded = startAdded;
+ Element[] added = (ec != null) ? ec.getChildrenAdded() : null;
+ if (added != null && added.length > 0)
+ {
+ startAdded = ec.getIndex();
+ endAdded = startAdded + added.length - 1;
+ }
+
+ // Forward event to all views between startIndex and endIndex,
+ // and leave out all views in the hole.
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ // Skip newly added child views.
+ if (! (i >= startAdded && i <= endAdded))
+ {
+ View child = getView(i);
+ if (child != null)
+ {
+ Shape childAlloc = getChildAllocation(i, shape);
+ forwardUpdateToView(child, ev, childAlloc, vf);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Forwards an update event to the given child view. This calls
+ * {@link #insertUpdate}, {@link #removeUpdate} or {@link #changedUpdate},
+ * depending on the type of document event.
+ *
+ * @param view the View to forward the event to
+ * @param ev the DocumentEvent to forward
+ * @param shape the current allocation of the View
+ * @param vf the ViewFactory used to create new Views
+ *
+ * @since 1.3
+ */
+ protected void forwardUpdateToView(View view, DocumentEvent ev, Shape shape,
+ ViewFactory vf)
+ {
+ DocumentEvent.EventType type = ev.getType();
+ if (type == DocumentEvent.EventType.INSERT)
+ view.insertUpdate(ev, shape, vf);
+ else if (type == DocumentEvent.EventType.REMOVE)
+ view.removeUpdate(ev, shape, vf);
+ else if (type == DocumentEvent.EventType.CHANGE)
+ view.changedUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Updates the layout.
+ *
+ * @param ec the ElementChange that describes the changes to the element
+ * @param ev the DocumentEvent that describes the changes to the model
+ * @param shape the current allocation for this view
+ *
+ * @since 1.3
+ */
+ protected void updateLayout(DocumentEvent.ElementChange ec,
+ DocumentEvent ev, Shape shape)
+ {
+ if (ec != null && shape != null)
+ {
+ preferenceChanged(null, true, true);
+ Container c = getContainer();
+ if (c != null)
+ c.repaint();
+ }
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * @param pos the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param b either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public abstract Shape modelToView(int pos, Shape a, Position.Bias b)
+ throws BadLocationException;
+
+ /**
+ * Maps a region in the document into the coordinate space of the View.
+ *
+ * @param p1 the beginning position inside the document
+ * @param b1 the direction bias for the beginning position
+ * @param p2 the end position inside the document
+ * @param b2 the direction bias for the end position
+ * @param a the area that is occupied by the view
+ *
+ * @return a rectangle that gives the span of the document region
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>p1</code> or <code>p2</code> are
+ * invalid
+ * @throws IllegalArgumentException if b1 or b2 is not one of the above
+ * listed valid values
+ */
+ public Shape modelToView(int p1, Position.Bias b1,
+ int p2, Position.Bias b2, Shape a)
+ throws BadLocationException
+ {
+ if (b1 != Position.Bias.Forward && b1 != Position.Bias.Backward)
+ throw new IllegalArgumentException
+ ("b1 must be either Position.Bias.Forward or Position.Bias.Backward");
+ if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward)
+ throw new IllegalArgumentException
+ ("b2 must be either Position.Bias.Forward or Position.Bias.Backward");
+
+ Shape s1 = modelToView(p1, a, b1);
+ // Special case for p2 == end index.
+ Shape s2;
+ if (p2 != getEndOffset())
+ {
+ s2 = modelToView(p2, a, b2);
+ }
+ else
+ {
+ try
+ {
+ s2 = modelToView(p2, a, b2);
+ }
+ catch (BadLocationException ex)
+ {
+ // Assume the end rectangle to be at the right edge of the
+ // view.
+ Rectangle aRect = a instanceof Rectangle ? (Rectangle) a
+ : a.getBounds();
+ s2 = new Rectangle(aRect.x + aRect.width - 1, aRect.y, 1,
+ aRect.height);
+ }
+ }
+
+ // Need to modify the rectangle, so we create a copy in all cases.
+ Rectangle r1 = s1.getBounds();
+ Rectangle r2 = s2 instanceof Rectangle ? (Rectangle) s2
+ : s2.getBounds();
+
+ // For multiline view, let the resulting rectangle span the whole view.
+ if (r1.y != r2.y)
+ {
+ Rectangle aRect = a instanceof Rectangle ? (Rectangle) a
+ : a.getBounds();
+ r1.x = aRect.x;
+ r1.width = aRect.width;
+ }
+
+ return SwingUtilities.computeUnion(r2.x, r2.y, r2.width, r2.height, r1);
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * This method is deprecated and calls
+ * {@link #modelToView(int, Position.Bias, int, Position.Bias, Shape)} with
+ * a bias of {@link Position.Bias#Forward}.
+ *
+ * @param pos the position of the character in the model
+ * @param a the area that is occupied by the view
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ *
+ * @deprecated Use {@link #modelToView(int, Shape, Position.Bias)} instead.
+ */
+ public Shape modelToView(int pos, Shape a) throws BadLocationException
+ {
+ return modelToView(pos, a, Position.Bias.Forward);
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b);
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model. This method is deprecated and only there for
+ * compatibility.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ *
+ * @deprecated Use {@link #viewToModel(float, float, Shape, Position.Bias[])}
+ * instead.
+ */
+ public int viewToModel(float x, float y, Shape a)
+ {
+ Position.Bias[] biasRet = new Position.Bias[1];
+ biasRet[0] = Position.Bias.Forward;
+ return viewToModel(x, y, a, biasRet);
+ }
+
+ /**
+ * Dumps the complete View hierarchy. This method can be used for debugging
+ * purposes.
+ */
+ protected void dump()
+ {
+ // Climb up the hierarchy to the parent.
+ View parent = getParent();
+ if (parent != null)
+ parent.dump();
+ else
+ dump(0);
+ }
+
+ /**
+ * Dumps the view hierarchy below this View with the specified indentation
+ * level.
+ *
+ * @param indent the indentation level to be used for this view
+ */
+ void dump(int indent)
+ {
+ for (int i = 0; i < indent; ++i)
+ System.out.print('.');
+ System.out.println(this + "(" + getStartOffset() + "," + getEndOffset() + ": " + getElement());
+
+ int count = getViewCount();
+ for (int i = 0; i < count; ++i)
+ getView(i).dump(indent + 1);
+ }
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param a the allocation for this view
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ * @throws IllegalArgumentException if <code>d</code> is not a valid direction
+ */
+ public int getNextVisualPositionFrom(int pos, Position.Bias b,
+ Shape a, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ int ret = pos;
+ Rectangle r;
+ View parent;
+
+ switch (d)
+ {
+ case EAST:
+ // TODO: take component orientation into account?
+ // Note: If pos is below zero the implementation will return
+ // pos + 1 regardless of whether that value is a correct offset
+ // in the document model. However this is what the RI does.
+ ret = Math.min(pos + 1, getEndOffset());
+ break;
+ case WEST:
+ // TODO: take component orientation into account?
+ ret = Math.max(pos - 1, getStartOffset());
+ break;
+ case NORTH:
+ // Try to find a suitable offset by examining the area above.
+ parent = getParent();
+ r = parent.modelToView(pos, a, b).getBounds();
+ ret = parent.viewToModel(r.x, r.y - 1, a, biasRet);
+ break;
+ case SOUTH:
+ // Try to find a suitable offset by examining the area below.
+ parent = getParent();
+ r = parent.modelToView(pos, a, b).getBounds();
+ ret = parent.viewToModel(r.x + r.width, r.y + r.height, a, biasRet);
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal value for d");
+ }
+
+ return ret;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/ViewFactory.java b/libjava/classpath/javax/swing/text/ViewFactory.java
new file mode 100644
index 000000000..cb57bd801
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/ViewFactory.java
@@ -0,0 +1,50 @@
+/* ViewFactory.java --
+ Copyright (C) 2002, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+public interface ViewFactory
+{
+ /**
+ * Creates a view for a given element.
+ *
+ * @param elem them element to create view for
+ *
+ * @return a new created view
+ */
+ View create(Element elem);
+}
diff --git a/libjava/classpath/javax/swing/text/WrappedPlainView.java b/libjava/classpath/javax/swing/text/WrappedPlainView.java
new file mode 100644
index 000000000..f2a6c92d8
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/WrappedPlainView.java
@@ -0,0 +1,795 @@
+/* WrappedPlainView.java --
+ Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Container;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.Position.Bias;
+
+/**
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ */
+public class WrappedPlainView extends BoxView implements TabExpander
+{
+ /** The color for selected text **/
+ Color selectedColor;
+
+ /** The color for unselected text **/
+ Color unselectedColor;
+
+ /** The color for disabled components **/
+ Color disabledColor;
+
+ /**
+ * Stores the font metrics. This is package private to avoid synthetic
+ * accessor method.
+ */
+ FontMetrics metrics;
+
+ /** Whether or not to wrap on word boundaries **/
+ boolean wordWrap;
+
+ /** A ViewFactory that creates WrappedLines **/
+ ViewFactory viewFactory = new WrappedLineCreator();
+
+ /** The start of the selected text **/
+ int selectionStart;
+
+ /** The end of the selected text **/
+ int selectionEnd;
+
+ /** The height of the line (used while painting) **/
+ int lineHeight;
+
+ /**
+ * The base offset for tab calculations.
+ */
+ private int tabBase;
+
+ /**
+ * The tab size.
+ */
+ private int tabSize;
+
+ /**
+ * The instance returned by {@link #getLineBuffer()}.
+ */
+ private transient Segment lineBuffer;
+
+ public WrappedPlainView (Element elem)
+ {
+ this (elem, false);
+ }
+
+ public WrappedPlainView (Element elem, boolean wordWrap)
+ {
+ super (elem, Y_AXIS);
+ this.wordWrap = wordWrap;
+ }
+
+ /**
+ * Provides access to the Segment used for retrievals from the Document.
+ * @return the Segment.
+ */
+ protected final Segment getLineBuffer()
+ {
+ if (lineBuffer == null)
+ lineBuffer = new Segment();
+ return lineBuffer;
+ }
+
+ /**
+ * 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)
+ {
+ int next = (int) x;
+ if (tabSize != 0)
+ {
+ int numTabs = ((int) x - tabBase) / tabSize;
+ next = tabBase + (numTabs + 1) * tabSize;
+ }
+ return next;
+ }
+
+ /**
+ * Returns the tab size for the Document based on
+ * PlainDocument.tabSizeAttribute, defaulting to 8 if this property is
+ * not defined
+ *
+ * @return the tab size.
+ */
+ protected int getTabSize()
+ {
+ Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute);
+ if (tabSize == null)
+ return 8;
+ return ((Integer)tabSize).intValue();
+ }
+
+ /**
+ * Draws a line of text, suppressing white space at the end and expanding
+ * tabs. Calls drawSelectedText and drawUnselectedText.
+ * @param p0 starting document position to use
+ * @param p1 ending document position to use
+ * @param g graphics context
+ * @param x starting x position
+ * @param y starting y position
+ */
+ protected void drawLine(int p0, int p1, Graphics g, int x, int y)
+ {
+ try
+ {
+ // We have to draw both selected and unselected text. There are
+ // several cases:
+ // - entire range is unselected
+ // - entire range is selected
+ // - start of range is selected, end of range is unselected
+ // - start of range is unselected, end of range is selected
+ // - middle of range is selected, start and end of range is unselected
+
+ // entire range unselected:
+ if ((selectionStart == selectionEnd) ||
+ (p0 > selectionEnd || p1 < selectionStart))
+ drawUnselectedText(g, x, y, p0, p1);
+
+ // entire range selected
+ else if (p0 >= selectionStart && p1 <= selectionEnd)
+ drawSelectedText(g, x, y, p0, p1);
+
+ // start of range selected, end of range unselected
+ else if (p0 >= selectionStart)
+ {
+ x = drawSelectedText(g, x, y, p0, selectionEnd);
+ drawUnselectedText(g, x, y, selectionEnd, p1);
+ }
+
+ // start of range unselected, end of range selected
+ else if (selectionStart > p0 && selectionEnd > p1)
+ {
+ x = drawUnselectedText(g, x, y, p0, selectionStart);
+ drawSelectedText(g, x, y, selectionStart, p1);
+ }
+
+ // middle of range selected
+ else if (selectionStart > p0)
+ {
+ x = drawUnselectedText(g, x, y, p0, selectionStart);
+ x = drawSelectedText(g, x, y, selectionStart, selectionEnd);
+ drawUnselectedText(g, x, y, selectionEnd, p1);
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // shouldn't happen
+ }
+ }
+
+ /**
+ * Renders the range of text as selected text. Just paints the text
+ * in the color specified by the host component. Assumes the highlighter
+ * will render the selected background.
+ * @param g the graphics context
+ * @param x the starting X coordinate
+ * @param y the starting Y coordinate
+ * @param p0 the starting model location
+ * @param p1 the ending model location
+ * @return the X coordinate of the end of the text
+ * @throws BadLocationException if the given range is invalid
+ */
+ protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
+ throws BadLocationException
+ {
+ g.setColor(selectedColor);
+ Segment segment = getLineBuffer();
+ getDocument().getText(p0, p1 - p0, segment);
+ return Utilities.drawTabbedText(segment, x, y, g, this, p0);
+ }
+
+ /**
+ * Renders the range of text as normal unhighlighted text.
+ * @param g the graphics context
+ * @param x the starting X coordinate
+ * @param y the starting Y coordinate
+ * @param p0 the starting model location
+ * @param p1 the end model location
+ * @return the X location of the end off the range
+ * @throws BadLocationException if the range given is invalid
+ */
+ protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
+ throws BadLocationException
+ {
+ JTextComponent textComponent = (JTextComponent) getContainer();
+ if (textComponent.isEnabled())
+ g.setColor(unselectedColor);
+ else
+ g.setColor(disabledColor);
+
+ Segment segment = getLineBuffer();
+ getDocument().getText(p0, p1 - p0, segment);
+ return Utilities.drawTabbedText(segment, x, y, g, this, p0);
+ }
+
+ /**
+ * Loads the children to initiate the view. Called by setParent.
+ * Creates a WrappedLine for each child Element.
+ */
+ protected void loadChildren (ViewFactory f)
+ {
+ Element root = getElement();
+ int numChildren = root.getElementCount();
+ if (numChildren == 0)
+ return;
+
+ View[] children = new View[numChildren];
+ for (int i = 0; i < numChildren; i++)
+ children[i] = new WrappedLine(root.getElement(i));
+ replace(0, 0, children);
+ }
+
+ /**
+ * Calculates the break position for the text between model positions
+ * p0 and p1. Will break on word boundaries or character boundaries
+ * depending on the break argument given in construction of this
+ * WrappedPlainView. Used by the nested WrappedLine class to determine
+ * when to start the next logical line.
+ * @param p0 the start model position
+ * @param p1 the end model position
+ * @return the model position at which to break the text
+ */
+ protected int calculateBreakPosition(int p0, int p1)
+ {
+ Segment s = new Segment();
+ try
+ {
+ getDocument().getText(p0, p1 - p0, s);
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "Couldn't load text";
+ }
+ int width = getWidth();
+ int pos;
+ if (wordWrap)
+ pos = p0 + Utilities.getBreakLocation(s, metrics, tabBase,
+ tabBase + width, this, p0);
+ else
+ pos = p0 + Utilities.getTabbedTextOffset(s, metrics, tabBase,
+ tabBase + width, this, p0,
+ false);
+ return pos;
+ }
+
+ void updateMetrics()
+ {
+ Container component = getContainer();
+ metrics = component.getFontMetrics(component.getFont());
+ tabSize = getTabSize()* metrics.charWidth('m');
+ }
+
+ /**
+ * Determines the preferred span along the given axis. Implemented to
+ * cache the font metrics and then call the super classes method.
+ */
+ public float getPreferredSpan (int axis)
+ {
+ updateMetrics();
+ return super.getPreferredSpan(axis);
+ }
+
+ /**
+ * Determines the minimum span along the given axis. Implemented to
+ * cache the font metrics and then call the super classes method.
+ */
+ public float getMinimumSpan (int axis)
+ {
+ updateMetrics();
+ return super.getMinimumSpan(axis);
+ }
+
+ /**
+ * Determines the maximum span along the given axis. Implemented to
+ * cache the font metrics and then call the super classes method.
+ */
+ public float getMaximumSpan (int axis)
+ {
+ updateMetrics();
+ return super.getMaximumSpan(axis);
+ }
+
+ /**
+ * Called when something was inserted. Overridden so that
+ * the view factory creates WrappedLine views.
+ */
+ public void insertUpdate (DocumentEvent e, Shape a, ViewFactory f)
+ {
+ // Update children efficiently.
+ updateChildren(e, a);
+
+ // Notify children.
+ Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a)
+ : null;
+ View v = getViewAtPosition(e.getOffset(), r);
+ if (v != null)
+ v.insertUpdate(e, r, f);
+ }
+
+ /**
+ * Called when something is removed. Overridden so that
+ * the view factory creates WrappedLine views.
+ */
+ public void removeUpdate (DocumentEvent e, Shape a, ViewFactory f)
+ {
+ // Update children efficiently.
+ updateChildren(e, a);
+
+ // Notify children.
+ Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a)
+ : null;
+ View v = getViewAtPosition(e.getOffset(), r);
+ if (v != null)
+ v.removeUpdate(e, r, f);
+ }
+
+ /**
+ * Called when the portion of the Document that this View is responsible
+ * for changes. Overridden so that the view factory creates
+ * WrappedLine views.
+ */
+ public void changedUpdate (DocumentEvent e, Shape a, ViewFactory f)
+ {
+ // Update children efficiently.
+ updateChildren(e, a);
+ }
+
+ /**
+ * Helper method. Updates the child views in response to
+ * insert/remove/change updates. This is here to be a little more efficient
+ * than the BoxView implementation.
+ *
+ * @param ev the document event
+ * @param a the shape
+ */
+ private void updateChildren(DocumentEvent ev, Shape a)
+ {
+ Element el = getElement();
+ DocumentEvent.ElementChange ec = ev.getChange(el);
+ if (ec != null)
+ {
+ Element[] removed = ec.getChildrenRemoved();
+ Element[] added = ec.getChildrenAdded();
+ View[] addedViews = new View[added.length];
+ for (int i = 0; i < added.length; i++)
+ addedViews[i] = new WrappedLine(added[i]);
+ replace(ec.getIndex(), removed.length, addedViews);
+ if (a != null)
+ {
+ preferenceChanged(null, true, true);
+ getContainer().repaint();
+ }
+ }
+ updateMetrics();
+ }
+
+ class WrappedLineCreator implements ViewFactory
+ {
+ // Creates a new WrappedLine
+ public View create(Element elem)
+ {
+ return new WrappedLine(elem);
+ }
+ }
+
+ /**
+ * Renders the <code>Element</code> that is associated with this
+ * <code>View</code>. Caches the metrics and then calls
+ * super.paint to paint all the child views.
+ *
+ * @param g the <code>Graphics</code> context to render to
+ * @param a the allocated region for the <code>Element</code>
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ tabBase = r.x;
+
+ JTextComponent comp = (JTextComponent)getContainer();
+ // Ensure metrics are up-to-date.
+ updateMetrics();
+
+ selectionStart = comp.getSelectionStart();
+ selectionEnd = comp.getSelectionEnd();
+
+ selectedColor = comp.getSelectedTextColor();
+ unselectedColor = comp.getForeground();
+ disabledColor = comp.getDisabledTextColor();
+ selectedColor = comp.getSelectedTextColor();
+ lineHeight = metrics.getHeight();
+ g.setFont(comp.getFont());
+
+ super.paint(g, a);
+ }
+
+ /**
+ * Sets the size of the View. Implemented to update the metrics
+ * and then call super method.
+ */
+ public void setSize (float width, float height)
+ {
+ updateMetrics();
+ if (width != getWidth())
+ preferenceChanged(null, true, true);
+ super.setSize(width, height);
+ }
+
+ class WrappedLine extends View
+ {
+ /** Used to cache the number of lines for this View **/
+ int numLines = 1;
+
+ public WrappedLine(Element elem)
+ {
+ super(elem);
+ }
+
+ /**
+ * Renders this (possibly wrapped) line using the given Graphics object
+ * and on the given rendering surface.
+ */
+ public void paint(Graphics g, Shape s)
+ {
+ Rectangle rect = s.getBounds();
+
+ int end = getEndOffset();
+ int currStart = getStartOffset();
+ int currEnd;
+ int count = 0;
+
+ // Determine layered highlights.
+ Container c = getContainer();
+ LayeredHighlighter lh = null;
+ JTextComponent tc = null;
+ if (c instanceof JTextComponent)
+ {
+ tc = (JTextComponent) c;
+ Highlighter h = tc.getHighlighter();
+ if (h instanceof LayeredHighlighter)
+ lh = (LayeredHighlighter) h;
+ }
+
+ while (currStart < end)
+ {
+ currEnd = calculateBreakPosition(currStart, end);
+
+ // Paint layered highlights, if any.
+ if (lh != null)
+ {
+ // Exclude trailing newline in last line.
+ if (currEnd == end)
+ lh.paintLayeredHighlights(g, currStart, currEnd - 1, s, tc,
+ this);
+ else
+ lh.paintLayeredHighlights(g, currStart, currEnd, s, tc, this);
+
+ }
+ drawLine(currStart, currEnd, g, rect.x, rect.y + metrics.getAscent());
+
+ rect.y += lineHeight;
+ if (currEnd == currStart)
+ currStart ++;
+ else
+ currStart = currEnd;
+
+ count++;
+
+ }
+
+ if (count != numLines)
+ {
+ numLines = count;
+ preferenceChanged(this, false, true);
+ }
+
+ }
+
+ /**
+ * Calculates the number of logical lines that the Element
+ * needs to be displayed and updates the variable numLines
+ * accordingly.
+ */
+ private int determineNumLines()
+ {
+ int nLines = 0;
+ int end = getEndOffset();
+ for (int i = getStartOffset(); i < end;)
+ {
+ nLines++;
+ // careful: check that there's no off-by-one problem here
+ // depending on which position calculateBreakPosition returns
+ int breakPoint = calculateBreakPosition(i, end);
+
+ if (breakPoint == i)
+ i = breakPoint + 1;
+ else
+ i = breakPoint;
+ }
+ return nLines;
+ }
+
+ /**
+ * Determines the preferred span for this view along the given axis.
+ *
+ * @param axis the axis (either X_AXIS or Y_AXIS)
+ *
+ * @return the preferred span along the given axis.
+ * @throws IllegalArgumentException if axis is not X_AXIS or Y_AXIS
+ */
+ public float getPreferredSpan(int axis)
+ {
+ if (axis == X_AXIS)
+ return getWidth();
+ else if (axis == Y_AXIS)
+ {
+ if (metrics == null)
+ updateMetrics();
+ return numLines * metrics.getHeight();
+ }
+
+ throw new IllegalArgumentException("Invalid axis for getPreferredSpan: "
+ + axis);
+ }
+
+ /**
+ * Provides a mapping from model space to view space.
+ *
+ * @param pos the position in the model
+ * @param a the region into which the view is rendered
+ * @param b the position bias (forward or backward)
+ *
+ * @return a box in view space that represents the given position
+ * in model space
+ * @throws BadLocationException if the given model position is invalid
+ */
+ public Shape modelToView(int pos, Shape a, Bias b)
+ throws BadLocationException
+ {
+ Rectangle rect = a.getBounds();
+
+ // Throwing a BadLocationException is an observed behavior of the RI.
+ if (rect.isEmpty())
+ throw new BadLocationException("Unable to calculate view coordinates "
+ + "when allocation area is empty.", pos);
+
+ Segment s = getLineBuffer();
+ int lineHeight = metrics.getHeight();
+
+ // Return a rectangle with width 1 and height equal to the height
+ // of the text
+ rect.height = lineHeight;
+ rect.width = 1;
+
+ int currLineStart = getStartOffset();
+ int end = getEndOffset();
+
+ if (pos < currLineStart || pos >= end)
+ throw new BadLocationException("invalid offset", pos);
+
+ while (true)
+ {
+ int currLineEnd = calculateBreakPosition(currLineStart, end);
+ // If pos is between currLineStart and currLineEnd then just find
+ // the width of the text from currLineStart to pos and add that
+ // to rect.x
+ if (pos >= currLineStart && pos < currLineEnd)
+ {
+ try
+ {
+ getDocument().getText(currLineStart, pos - currLineStart, s);
+ }
+ catch (BadLocationException ble)
+ {
+ // Shouldn't happen
+ }
+ rect.x += Utilities.getTabbedTextWidth(s, metrics, rect.x,
+ WrappedPlainView.this,
+ currLineStart);
+ return rect;
+ }
+ // Increment rect.y so we're checking the next logical line
+ rect.y += lineHeight;
+
+ // Increment currLineStart to the model position of the start
+ // of the next logical line
+ if (currLineEnd == currLineStart)
+ currLineStart = end;
+ else
+ currLineStart = currLineEnd;
+ }
+
+ }
+
+ /**
+ * Provides a mapping from view space to model space.
+ *
+ * @param x the x coordinate in view space
+ * @param y the y coordinate in view space
+ * @param a the region into which the view is rendered
+ * @param b the position bias (forward or backward)
+ *
+ * @return the location in the model that best represents the
+ * given point in view space
+ */
+ public int viewToModel(float x, float y, Shape a, Bias[] b)
+ {
+ Segment s = getLineBuffer();
+ Rectangle rect = a.getBounds();
+ int currLineStart = getStartOffset();
+
+ // Although calling modelToView with the last possible offset will
+ // cause a BadLocationException in CompositeView it is allowed
+ // to return that offset in viewToModel.
+ int end = getEndOffset();
+
+ int lineHeight = metrics.getHeight();
+ if (y < rect.y)
+ return currLineStart;
+
+ if (y > rect.y + rect.height)
+ return end - 1;
+
+ // Note: rect.x and rect.width do not represent the width of painted
+ // text but the area where text *may* be painted. This means the width
+ // is most of the time identical to the component's width.
+
+ while (currLineStart != end)
+ {
+ int currLineEnd = calculateBreakPosition(currLineStart, end);
+
+ // If we're at the right y-position that means we're on the right
+ // logical line and we should look for the character
+ if (y >= rect.y && y < rect.y + lineHeight)
+ {
+ try
+ {
+ getDocument().getText(currLineStart, currLineEnd - currLineStart, s);
+ }
+ catch (BadLocationException ble)
+ {
+ // Shouldn't happen
+ }
+
+ int offset = Utilities.getTabbedTextOffset(s, metrics, rect.x,
+ (int) x,
+ WrappedPlainView.this,
+ currLineStart);
+ // If the calculated offset is the end of the line (in the
+ // document (= start of the next line) return the preceding
+ // offset instead. This makes sure that clicking right besides
+ // the last character in a line positions the cursor after the
+ // last character and not in the beginning of the next line.
+ return (offset == currLineEnd) ? offset - 1 : offset;
+ }
+ // Increment rect.y so we're checking the next logical line
+ rect.y += lineHeight;
+
+ // Increment currLineStart to the model position of the start
+ // of the next logical line.
+ currLineStart = currLineEnd;
+
+ }
+
+ return end;
+ }
+
+ /**
+ * <p>This method is called from insertUpdate and removeUpdate.</p>
+ *
+ * <p>If the number of lines in the document has changed, just repaint
+ * the whole thing (note, could improve performance by not repainting
+ * anything above the changes). If the number of lines hasn't changed,
+ * just repaint the given Rectangle.</p>
+ *
+ * <p>Note that the <code>Rectangle</code> argument may be <code>null</code>
+ * when the allocation area is empty.</code>
+ *
+ * @param a the Rectangle to repaint if the number of lines hasn't changed
+ */
+ void updateDamage (Rectangle a)
+ {
+ int nLines = determineNumLines();
+ if (numLines != nLines)
+ {
+ numLines = nLines;
+ preferenceChanged(this, false, true);
+ getContainer().repaint();
+ }
+ else if (a != null)
+ getContainer().repaint(a.x, a.y, a.width, a.height);
+ }
+
+ /**
+ * This method is called when something is inserted into the Document
+ * that this View is displaying.
+ *
+ * @param changes the DocumentEvent for the changes.
+ * @param a the allocation of the View
+ * @param f the ViewFactory used to rebuild
+ */
+ public void insertUpdate (DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ updateDamage(r);
+ }
+
+ /**
+ * This method is called when something is removed from the Document
+ * that this View is displaying.
+ *
+ * @param changes the DocumentEvent for the changes.
+ * @param a the allocation of the View
+ * @param f the ViewFactory used to rebuild
+ */
+ public void removeUpdate (DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ // Note: This method is not called when characters from the
+ // end of the document are removed. The reason for this
+ // can be found in the implementation of View.forwardUpdate:
+ // The document event will denote offsets which do not exist
+ // any more, getViewIndex() will therefore return -1 and this
+ // makes View.forwardUpdate() skip this method call.
+ // However this seems to cause no trouble and as it reduces the
+ // number of method calls it can stay this way.
+
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ updateDamage(r);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/ZoneView.java b/libjava/classpath/javax/swing/text/ZoneView.java
new file mode 100644
index 000000000..6cabc6c20
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/ZoneView.java
@@ -0,0 +1,442 @@
+/* ZoneView.java -- An effective BoxView subclass
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.Shape;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A View implementation that delays loading of sub views until they are
+ * needed for display or internal transformations. This can be used for
+ * editors that need to handle large documents more effectivly than the
+ * standard {@link BoxView}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.3
+ */
+public class ZoneView
+ extends BoxView
+{
+
+ /**
+ * The default zone view implementation. The specs suggest that this is
+ * a subclass of AsyncBoxView, so do we.
+ */
+ static class Zone
+ extends AsyncBoxView
+ {
+ /**
+ * The start position for this zone.
+ */
+ private Position p0;
+
+ /**
+ * The end position for this zone.
+ */
+ private Position p1;
+
+ /**
+ * Creates a new Zone for the specified element, start and end positions.
+ *
+ * @param el the element
+ * @param pos0 the start position
+ * @param pos1 the end position
+ * @param axis the major axis
+ */
+ Zone(Element el, Position pos0, Position pos1, int axis)
+ {
+ super(el, axis);
+ p0 = pos0;
+ p1 = pos1;
+ }
+
+ /**
+ * Returns the start offset of the zone.
+ *
+ * @return the start offset of the zone
+ */
+ public int getStartOffset()
+ {
+ return p0.getOffset();
+ }
+
+ /**
+ * Returns the end offset of the zone.
+ *
+ * @return the end offset of the zone
+ */
+ public int getEndOffset()
+ {
+ return p1.getOffset();
+ }
+ }
+
+ /**
+ * The maximumZoneSize.
+ */
+ private int maximumZoneSize;
+
+ /**
+ * The maximum number of loaded zones.
+ */
+ private int maxZonesLoaded;
+
+ /**
+ * A queue of loaded zones. When the number of loaded zones exceeds the
+ * maximum number of zones, the oldest zone(s) get unloaded.
+ */
+ private LinkedList loadedZones;
+
+ /**
+ * Creates a new ZoneView for the specified element and axis.
+ *
+ * @param element the element for which to create a ZoneView
+ * @param axis the major layout axis for the box
+ */
+ public ZoneView(Element element, int axis)
+ {
+ super(element, axis);
+ maximumZoneSize = 8192;
+ maxZonesLoaded = 3;
+ loadedZones = new LinkedList();
+ }
+
+ /**
+ * Sets the maximum zone size. Note that zones might still become larger
+ * then the size specified when a singe child view is larger for itself,
+ * because zones are formed on child view boundaries.
+ *
+ * @param size the maximum zone size to set
+ *
+ * @see #getMaximumZoneSize()
+ */
+ public void setMaximumZoneSize(int size)
+ {
+ maximumZoneSize = size;
+ }
+
+ /**
+ * Returns the maximum zone size. Note that zones might still become larger
+ * then the size specified when a singe child view is larger for itself,
+ * because zones are formed on child view boundaries.
+ *
+ * @return the maximum zone size
+ *
+ * @see #setMaximumZoneSize(int)
+ */
+ public int getMaximumZoneSize()
+ {
+ return maximumZoneSize;
+ }
+
+ /**
+ * Sets the maximum number of zones that are allowed to be loaded at the
+ * same time. If the new number of allowed zones is smaller then the
+ * previous settings, this unloads all zones the aren't allowed to be
+ * loaded anymore.
+ *
+ * @param num the number of zones allowed to be loaded at the same time
+ *
+ * @throws IllegalArgumentException if <code>num &lt;= 0</code>
+ *
+ * @see #getMaxZonesLoaded()
+ */
+ public void setMaxZonesLoaded(int num)
+ {
+ if (num < 1)
+ throw new IllegalArgumentException("Illegal number of zones");
+ maxZonesLoaded = num;
+ unloadOldestZones();
+ }
+
+ /**
+ * Returns the number of zones that are allowed to be loaded.
+ *
+ * @return the number of zones that are allowed to be loaded
+ *
+ * @see #setMaxZonesLoaded(int)
+ */
+ public int getMaxZonesLoaded()
+ {
+ return maxZonesLoaded;
+ }
+
+ /**
+ * Gets called after a zone has been loaded. This unloads the oldest zone(s)
+ * when the maximum number of zones is reached.
+ *
+ * @param zone the zone that has been loaded
+ */
+ protected void zoneWasLoaded(View zone)
+ {
+ loadedZones.addLast(zone);
+ unloadOldestZones();
+ }
+
+ /**
+ * This unloads the specified zone. This is implemented to simply remove
+ * all child views from that zone.
+ *
+ * @param zone the zone to be unloaded
+ */
+ protected void unloadZone(View zone)
+ {
+ zone.removeAll();
+ }
+
+ /**
+ * Returns <code>true</code> when the specified zone is loaded,
+ * <code>false</code> otherwise. The default implementation checks if
+ * the zone view has child elements.
+ *
+ * @param zone the zone view to check
+ *
+ * @return <code>true</code> when the specified zone is loaded,
+ * <code>false</code> otherwise
+ */
+ protected boolean isZoneLoaded(View zone)
+ {
+ return zone.getViewCount() > 0;
+ }
+
+ /**
+ * Creates a zone for the specified range. Subclasses can override this
+ * to provide a custom implementation for the zones.
+ *
+ * @param p0 the start of the range
+ * @param p1 the end of the range
+ *
+ * @return the zone
+ */
+ protected View createZone(int p0, int p1)
+ {
+ Document doc = getDocument();
+ Position pos0 = null;
+ Position pos1 = null;
+ try
+ {
+ pos0 = doc.createPosition(p0);
+ pos1 = doc.createPosition(p1);
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "Must not happen";
+ }
+ Zone zone = new Zone(getElement(), pos0, pos1, getAxis());
+ return zone;
+ }
+
+ // --------------------------------------------------------------------------
+ // CompositeView methods.
+ // --------------------------------------------------------------------------
+
+ /**
+ * Overridden to not load all the child views. This methods creates
+ * initial zones without actually loading them.
+ *
+ * @param vf not used
+ */
+ protected void loadChildren(ViewFactory vf)
+ {
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+ append(createZone(p0, p1));
+ checkZoneAt(p0);
+ }
+
+ /**
+ * Returns the index of the child view at the document position
+ * <code>pos</code>.
+ *
+ * This overrides the CompositeView implementation because the ZoneView does
+ * not provide a one to one mapping from Elements to Views.
+ *
+ * @param pos the document position
+ *
+ * @return the index of the child view at the document position
+ * <code>pos</code>
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ int index = -1;
+ boolean found = false;
+ if (pos >= getStartOffset() && pos <= getEndOffset())
+ {
+ int upper = getViewCount() - 1;
+ int lower = 0;
+ index = (upper - lower) / 2 + lower;
+ int bias = 0;
+ do
+ {
+ View child = getView(index);
+ int childStart = child.getStartOffset();
+ int childEnd = child.getEndOffset();
+ if (pos >= childStart && pos < childEnd)
+ found = true;
+ else if (pos < childStart)
+ {
+ upper = index;
+ bias = -1;
+ }
+ else if (pos >= childEnd)
+ {
+ lower = index;
+ bias = 1;
+ }
+ if (! found)
+ {
+ int newIndex = (upper - lower) / 2 + lower;
+ if (newIndex == index)
+ index = newIndex + bias;
+ else
+ index = newIndex;
+ }
+ } while (upper != lower && ! found);
+ }
+ // If no child view actually covers the specified offset, reset index to
+ // -1.
+ if (! found)
+ index = -1;
+ return index;
+ }
+
+ // --------------------------------------------------------------------------
+ // View methods.
+ // --------------------------------------------------------------------------
+
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ }
+
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ }
+
+ protected boolean updateChildren(DocumentEvent.ElementChange ec,
+ DocumentEvent e, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ return false;
+ }
+
+ // --------------------------------------------------------------------------
+ // Internal helper methods.
+ // --------------------------------------------------------------------------
+
+ /**
+ * A helper method to unload the oldest zones when there are more loaded
+ * zones then allowed.
+ */
+ private void unloadOldestZones()
+ {
+ int maxZones = getMaxZonesLoaded();
+ while (loadedZones.size() > maxZones)
+ {
+ View zone = (View) loadedZones.removeFirst();
+ unloadZone(zone);
+ }
+ }
+
+ /**
+ * Checks if the zone view at position <code>pos</code> should be split
+ * (its size is greater than maximumZoneSize) and tries to split it.
+ *
+ * @param pos the document position to check
+ */
+ private void checkZoneAt(int pos)
+ {
+ int viewIndex = getViewIndexAtPosition(pos); //, Position.Bias.Forward);
+ View view = getView(viewIndex);
+ int p0 = view.getStartOffset();
+ int p1 = view.getEndOffset();
+ if (p1 - p0 > maximumZoneSize)
+ splitZone(viewIndex, p0, p1);
+ }
+
+ /**
+ * Tries to break the view at the specified index and inside the specified
+ * range into pieces that are acceptable with respect to the maximum zone
+ * size.
+ *
+ * @param index the index of the view to split
+ * @param p0 the start offset
+ * @param p1 the end offset
+ */
+ private void splitZone(int index, int p0, int p1)
+ {
+ ArrayList newZones = new ArrayList();
+ int p = p0;
+ do
+ {
+ p0 = p;
+ p = Math.min(getPreferredZoneEnd(p0), p1);
+ newZones.add(createZone(p0, p));
+ } while (p < p1);
+ View[] newViews = new View[newZones.size()];
+ newViews = (View[]) newZones.toArray(newViews);
+ replace(index, 1, newViews);
+ }
+
+ /**
+ * Calculates the positions at which a zone split is performed. This
+ * tries to create zones sized close to half the maximum zone size.
+ *
+ * @param start the start offset
+ *
+ * @return the preferred end offset
+ */
+ private int getPreferredZoneEnd(int start)
+ {
+ Element el = getElement();
+ int index = el.getElementIndex(start + (maximumZoneSize / 2));
+ Element child = el.getElement(index);
+ int p0 = child.getStartOffset();
+ int p1 = child.getEndOffset();
+ int end = p1;
+ if (p0 - start > maximumZoneSize && p0 > start)
+ end = p0;
+ return end;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/BRView.java b/libjava/classpath/javax/swing/text/html/BRView.java
new file mode 100644
index 000000000..6f465c9d1
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/BRView.java
@@ -0,0 +1,70 @@
+/* BRView.java -- HTML BR tag view
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import javax.swing.text.Element;
+
+/**
+ * Handled the HTML BR tag.
+ */
+class BRView
+ extends InlineView
+{
+ /**
+ * Creates the new BR view.
+ *
+ * @param elem the HTML element, representing the view.
+ */
+ public BRView(Element elem)
+ {
+ super(elem);
+ }
+
+ /**
+ * Always return ForcedBreakWeight for the X_AXIS, BadBreakWeight for the
+ * Y_AXIS.
+ */
+ public int getBreakWeight(int axis, float pos, float len)
+ {
+ if (axis == X_AXIS)
+ return ForcedBreakWeight;
+ else
+ return super.getBreakWeight(axis, pos, len);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/BlockView.java b/libjava/classpath/javax/swing/text/html/BlockView.java
new file mode 100644
index 000000000..1c3397126
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/BlockView.java
@@ -0,0 +1,721 @@
+/* BlockView.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import gnu.javax.swing.text.html.css.Length;
+
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.util.HashMap;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BoxView;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * @author Lillian Angel <langel@redhat.com>
+ */
+public class BlockView extends BoxView
+{
+
+ /**
+ * Stores information about child positioning according to the
+ * CSS attributes position, left, right, top and bottom.
+ */
+ private static class PositionInfo
+ {
+ // TODO: Use enums when available.
+
+ /**
+ * Static positioning. This is the default and is thus rarely really
+ * used.
+ */
+ static final int STATIC = 0;
+
+ /**
+ * Relative positioning. The box is teaked relative to its static
+ * computed bounds.
+ */
+ static final int RELATIVE = 1;
+
+ /**
+ * Absolute positioning. The box is moved relative to the parent's box.
+ */
+ static final int ABSOLUTE = 2;
+
+ /**
+ * Like ABSOLUTE, with some fixation against the viewport (not yet
+ * implemented).
+ */
+ static final int FIXED = 3;
+
+ /**
+ * The type according to the constants of this class.
+ */
+ int type;
+
+ /**
+ * The left constraint, null if not set.
+ */
+ Length left;
+
+ /**
+ * The right constraint, null if not set.
+ */
+ Length right;
+
+ /**
+ * The top constraint, null if not set.
+ */
+ Length top;
+
+ /**
+ * The bottom constraint, null if not set.
+ */
+ Length bottom;
+
+ /**
+ * Creates a new PositionInfo object.
+ *
+ * @param typ the type to set
+ * @param l the left constraint
+ * @param r the right constraint
+ * @param t the top constraint
+ * @param b the bottom constraint
+ */
+ PositionInfo(int typ, Length l, Length r, Length t, Length b)
+ {
+ type = typ;
+ left = l;
+ right = r;
+ top = t;
+ bottom = b;
+ }
+ }
+
+ /**
+ * The attributes for this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The box painter for this view.
+ *
+ * This is package private because the TableView needs access to it.
+ */
+ StyleSheet.BoxPainter painter;
+
+ /**
+ * The width and height as specified in the stylesheet, null if not
+ * specified. The first value is the X_AXIS, the second the Y_AXIS. You
+ * can index this directly by the X_AXIS and Y_AXIS constants.
+ */
+ private Length[] cssSpans;
+
+ /**
+ * Stores additional CSS layout information.
+ */
+ private HashMap positionInfo;
+
+ /**
+ * Creates a new view that represents an html box.
+ * This can be used for a number of elements.
+ *
+ * @param elem - the element to create a view for
+ * @param axis - either View.X_AXIS or View.Y_AXIS
+ */
+ public BlockView(Element elem, int axis)
+ {
+ super(elem, axis);
+ cssSpans = new Length[2];
+ positionInfo = new HashMap();
+ }
+
+ /**
+ * Creates the parent view for this. It is called before
+ * any other methods, if the parent view is working properly.
+ * Implemented to forward to the superclass and call
+ * setPropertiesFromAttributes to set the paragraph
+ * properties.
+ *
+ * @param parent - the new parent, or null if the view
+ * is being removed from a parent it was added to.
+ */
+ public void setParent(View parent)
+ {
+ super.setParent(parent);
+
+ if (parent != null)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Calculates the requirements along the major axis.
+ * This is implemented to call the superclass and then
+ * adjust it if the CSS width or height attribute is specified
+ * and applicable.
+ *
+ * @param axis - the axis to check the requirements for.
+ * @param r - the SizeRequirements. If null, one is created.
+ * @return the new SizeRequirements object.
+ */
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ if (r == null)
+ r = new SizeRequirements();
+
+ if (setCSSSpan(r, axis))
+ {
+ // If we have set the span from CSS, then we need to adjust
+ // the margins.
+ SizeRequirements parent = super.calculateMajorAxisRequirements(axis,
+ null);
+ int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
+ : getTopInset() + getBottomInset();
+ r.minimum -= margin;
+ r.preferred -= margin;
+ r.maximum -= margin;
+ constrainSize(axis, r, parent);
+ }
+ else
+ r = super.calculateMajorAxisRequirements(axis, r);
+ return r;
+ }
+
+ /**
+ * Calculates the requirements along the minor axis.
+ * This is implemented to call the superclass and then
+ * adjust it if the CSS width or height attribute is specified
+ * and applicable.
+ *
+ * @param axis - the axis to check the requirements for.
+ * @param r - the SizeRequirements. If null, one is created.
+ * @return the new SizeRequirements object.
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ if (r == null)
+ r = new SizeRequirements();
+
+ if (setCSSSpan(r, axis))
+ {
+ // If we have set the span from CSS, then we need to adjust
+ // the margins.
+ SizeRequirements parent = super.calculateMinorAxisRequirements(axis,
+ null);
+ int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
+ : getTopInset() + getBottomInset();
+ r.minimum -= margin;
+ r.preferred -= margin;
+ r.maximum -= margin;
+ constrainSize(axis, r, parent);
+ }
+ else
+ r = super.calculateMinorAxisRequirements(axis, r);
+
+ // Apply text alignment if appropriate.
+ if (axis == X_AXIS)
+ {
+ Object o = getAttributes().getAttribute(CSS.Attribute.TEXT_ALIGN);
+ if (o != null)
+ {
+ String al = o.toString().trim();
+ if (al.equals("center"))
+ r.alignment = 0.5f;
+ else if (al.equals("right"))
+ r.alignment = 1.0f;
+ else
+ r.alignment = 0.0f;
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Sets the span on the SizeRequirements object according to the
+ * according CSS span value, when it is set.
+ *
+ * @param r the size requirements
+ * @param axis the axis
+ *
+ * @return <code>true</code> when the CSS span has been set,
+ * <code>false</code> otherwise
+ */
+ private boolean setCSSSpan(SizeRequirements r, int axis)
+ {
+ boolean ret = false;
+ Length span = cssSpans[axis];
+ // We can't set relative CSS spans here because we don't know
+ // yet about the allocated span. Instead we use the view's
+ // normal requirements.
+ if (span != null && ! span.isPercentage())
+ {
+ r.minimum = (int) span.getValue();
+ r.preferred = (int) span.getValue();
+ r.maximum = (int) span.getValue();
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Constrains the <code>r</code> requirements according to
+ * <code>min</code>.
+ *
+ * @param axis the axis
+ * @param r the requirements to constrain
+ * @param min the constraining requirements
+ */
+ private void constrainSize(int axis, SizeRequirements r,
+ SizeRequirements min)
+ {
+ if (min.minimum > r.minimum)
+ {
+ r.minimum = min.minimum;
+ r.preferred = min.minimum;
+ r.maximum = Math.max(r.maximum, min.maximum);
+ }
+ }
+
+ /**
+ * Lays out the box along the minor axis (the axis that is
+ * perpendicular to the axis that it represents). The results
+ * of the layout are placed in the given arrays which are
+ * the allocations to the children along the minor axis.
+ *
+ * @param targetSpan - the total span given to the view, also
+ * used to layout the children.
+ * @param axis - the minor axis
+ * @param offsets - the offsets from the origin of the view for
+ * all the child views. This is a return value and is filled in by this
+ * function.
+ * @param spans - the span of each child view. This is a return value and is
+ * filled in by this function.
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis,
+ int[] offsets, int[] spans)
+ {
+ int viewCount = getViewCount();
+ for (int i = 0; i < viewCount; i++)
+ {
+ View view = getView(i);
+ int min = (int) view.getMinimumSpan(axis);
+ int max;
+ // Handle CSS span value of child.
+ Length length = cssSpans[axis];
+ if (length != null)
+ {
+ min = Math.max((int) length.getValue(targetSpan), min);
+ max = min;
+ }
+ else
+ max = (int) view.getMaximumSpan(axis);
+
+ if (max < targetSpan)
+ {
+ // Align child.
+ float align = view.getAlignment(axis);
+ offsets[i] = (int) ((targetSpan - max) * align);
+ spans[i] = max;
+ }
+ else
+ {
+ offsets[i] = 0;
+ spans[i] = Math.max(min, targetSpan);
+ }
+
+ // Adjust according to CSS position info.
+ positionView(targetSpan, axis, i, offsets, spans);
+ }
+ }
+
+ /**
+ * Overridden to perform additional CSS layout (absolute/relative
+ * positioning).
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis,
+ int[] offsets, int[] spans)
+ {
+ super.layoutMajorAxis(targetSpan, axis, offsets, spans);
+
+ // Adjust according to CSS position info.
+ int viewCount = getViewCount();
+ for (int i = 0; i < viewCount; i++)
+ {
+ positionView(targetSpan, axis, i, offsets, spans);
+ }
+ }
+
+ /**
+ * Positions a view according to any additional CSS constraints.
+ *
+ * @param targetSpan the target span
+ * @param axis the axis
+ * @param i the index of the view
+ * @param offsets the offsets get placed here
+ * @param spans the spans get placed here
+ */
+ private void positionView(int targetSpan, int axis, int i, int[] offsets,
+ int[] spans)
+ {
+ View view = getView(i);
+ PositionInfo pos = (PositionInfo) positionInfo.get(view);
+ if (pos != null)
+ {
+ int p0 = -1;
+ int p1 = -1;
+ if (axis == X_AXIS)
+ {
+ Length l = pos.left;
+ if (l != null)
+ p0 = (int) l.getValue(targetSpan);
+ l = pos.right;
+ if (l != null)
+ p1 = (int) l.getValue(targetSpan);
+ }
+ else
+ {
+ Length l = pos.top;
+ if (l != null)
+ p0 = (int) l.getValue(targetSpan);
+ l = pos.bottom;
+ if (l != null)
+ p1 = (int) l.getValue(targetSpan);
+ }
+ if (pos.type == PositionInfo.ABSOLUTE
+ || pos.type == PositionInfo.FIXED)
+ {
+ if (p0 != -1)
+ {
+ offsets[i] = p0;
+ if (p1 != -1)
+ {
+ // Overrides computed width. (Possibly overconstrained
+ // when the width attribute was set too.)
+ spans[i] = targetSpan - p1 - offsets[i];
+ }
+ }
+ else if (p1 != -1)
+ {
+ // Preserve any computed width.
+ offsets[i] = targetSpan - p1 - spans[i];
+ }
+ }
+ else if (pos.type == PositionInfo.RELATIVE)
+ {
+ if (p0 != -1)
+ {
+ offsets[i] += p0;
+ if (p1 != -1)
+ {
+ // Overrides computed width. (Possibly overconstrained
+ // when the width attribute was set too.)
+ spans[i] = spans[i] - p0 - p1 - offsets[i];
+ }
+ }
+ else if (p1 != -1)
+ {
+ // Preserve any computed width.
+ offsets[i] -= p1;
+ }
+ }
+ }
+ }
+
+ /**
+ * Paints using the given graphics configuration and shape.
+ * This delegates to the css box painter to paint the
+ * border and background prior to the interior.
+ *
+ * @param g - Graphics configuration
+ * @param a - the Shape to render into.
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
+ // Debug output. Shows blocks in green rectangles.
+ // g.setColor(Color.GREEN);
+ // g.drawRect(rect.x, rect.y, rect.width, rect.height);
+
+ painter.paint(g, rect.x, rect.y, rect.width, rect.height, this);
+ super.paint(g, a);
+ }
+
+ /**
+ * Fetches the attributes to use when painting.
+ *
+ * @return the attributes of this model.
+ */
+ public AttributeSet getAttributes()
+ {
+ if (attributes == null)
+ attributes = getStyleSheet().getViewAttributes(this);
+ return attributes;
+ }
+
+ /**
+ * Gets the resize weight.
+ *
+ * @param axis - the axis to get the resize weight for.
+ * @return the resize weight.
+ * @throws IllegalArgumentException - for an invalid axis
+ */
+ public int getResizeWeight(int axis) throws IllegalArgumentException
+ {
+ // Can't resize the Y_AXIS
+ if (axis == Y_AXIS)
+ return 0;
+ if (axis == X_AXIS)
+ return 1;
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Gets the alignment.
+ *
+ * @param axis - the axis to get the alignment for.
+ * @return the alignment.
+ */
+ public float getAlignment(int axis)
+ {
+ if (axis == X_AXIS)
+ return super.getAlignment(axis);
+ if (axis == Y_AXIS)
+ {
+ if (getViewCount() == 0)
+ return 0.0F;
+ float prefHeight = getPreferredSpan(Y_AXIS);
+ View first = getView(0);
+ float firstRowHeight = first.getPreferredSpan(Y_AXIS);
+ return prefHeight != 0 ? (firstRowHeight * first.getAlignment(Y_AXIS))
+ / prefHeight
+ : 0;
+ }
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Gives notification from the document that attributes were
+ * changed in a location that this view is responsible for.
+ *
+ * @param ev - the change information
+ * @param a - the current shape of the view
+ * @param f - the factory to use to rebuild if the view has children.
+ */
+ public void changedUpdate(DocumentEvent ev,
+ Shape a, ViewFactory f)
+ {
+ super.changedUpdate(ev, a, f);
+
+ // If more elements were added, then need to set the properties for them
+ int currPos = ev.getOffset();
+ if (currPos <= getStartOffset()
+ && (currPos + ev.getLength()) >= getEndOffset())
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Determines the preferred span along the axis.
+ *
+ * @param axis - the view to get the preferred span for.
+ * @return the span the view would like to be painted into >=0/
+ * The view is usually told to paint into the span that is returned,
+ * although the parent may choose to resize or break the view.
+ * @throws IllegalArgumentException - for an invalid axis
+ */
+ public float getPreferredSpan(int axis) throws IllegalArgumentException
+ {
+ if (axis == X_AXIS || axis == Y_AXIS)
+ return super.getPreferredSpan(axis);
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Determines the minimum span along the axis.
+ *
+ * @param axis - the axis to get the minimum span for.
+ * @return the span the view would like to be painted into >=0/
+ * The view is usually told to paint into the span that is returned,
+ * although the parent may choose to resize or break the view.
+ * @throws IllegalArgumentException - for an invalid axis
+ */
+ public float getMinimumSpan(int axis) throws IllegalArgumentException
+ {
+ if (axis == X_AXIS || axis == Y_AXIS)
+ return super.getMinimumSpan(axis);
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Determines the maximum span along the axis.
+ *
+ * @param axis - the axis to get the maximum span for.
+ * @return the span the view would like to be painted into >=0/
+ * The view is usually told to paint into the span that is returned,
+ * although the parent may choose to resize or break the view.
+ * @throws IllegalArgumentException - for an invalid axis
+ */
+ public float getMaximumSpan(int axis) throws IllegalArgumentException
+ {
+ if (axis == X_AXIS || axis == Y_AXIS)
+ return super.getMaximumSpan(axis);
+ throw new IllegalArgumentException("Invalid Axis");
+ }
+
+ /**
+ * Updates any cached values that come from attributes.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ // Fetch attributes.
+ StyleSheet ss = getStyleSheet();
+ attributes = ss.getViewAttributes(this);
+
+ // Fetch painter.
+ painter = ss.getBoxPainter(attributes);
+
+ // Update insets.
+ if (attributes != null)
+ {
+ setInsets((short) painter.getInset(TOP, this),
+ (short) painter.getInset(LEFT, this),
+ (short) painter.getInset(BOTTOM, this),
+ (short) painter.getInset(RIGHT, this));
+ }
+
+ // Fetch width and height.
+ float emBase = ss.getEMBase(attributes);
+ float exBase = ss.getEXBase(attributes);
+ cssSpans[X_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.WIDTH);
+ if (cssSpans[X_AXIS] != null)
+ cssSpans[X_AXIS].setFontBases(emBase, exBase);
+ cssSpans[Y_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.HEIGHT);
+ if (cssSpans[Y_AXIS] != null)
+ cssSpans[Y_AXIS].setFontBases(emBase, exBase);
+ }
+
+ /**
+ * Gets the default style sheet.
+ *
+ * @return the style sheet
+ */
+ protected StyleSheet getStyleSheet()
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ return doc.getStyleSheet();
+ }
+
+ /**
+ * Overridden to fetch additional CSS layout information.
+ */
+ public void replace(int offset, int length, View[] views)
+ {
+ // First remove unneeded stuff.
+ for (int i = 0; i < length; i++)
+ {
+ View child = getView(i + offset);
+ positionInfo.remove(child);
+ }
+
+ // Call super to actually replace the views.
+ super.replace(offset, length, views);
+
+ // Now fetch the position infos for the new views.
+ for (int i = 0; i < views.length; i++)
+ {
+ fetchLayoutInfo(views[i]);
+ }
+ }
+
+ /**
+ * Fetches and stores the layout info for the specified view.
+ *
+ * @param view the view for which the layout info is stored
+ */
+ private void fetchLayoutInfo(View view)
+ {
+ AttributeSet atts = view.getAttributes();
+ Object o = atts.getAttribute(CSS.Attribute.POSITION);
+ if (o != null && o instanceof String && ! o.equals("static"))
+ {
+ int type;
+ if (o.equals("relative"))
+ type = PositionInfo.RELATIVE;
+ else if (o.equals("absolute"))
+ type = PositionInfo.ABSOLUTE;
+ else if (o.equals("fixed"))
+ type = PositionInfo.FIXED;
+ else
+ type = PositionInfo.STATIC;
+
+ if (type != PositionInfo.STATIC)
+ {
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(atts);
+ float exBase = ss.getEXBase(atts);
+ Length left = (Length) atts.getAttribute(CSS.Attribute.LEFT);
+ if (left != null)
+ left.setFontBases(emBase, exBase);
+ Length right = (Length) atts.getAttribute(CSS.Attribute.RIGHT);
+ if (right != null)
+ right.setFontBases(emBase, exBase);
+ Length top = (Length) atts.getAttribute(CSS.Attribute.TOP);
+ if (top != null)
+ top.setFontBases(emBase, exBase);
+ Length bottom = (Length) atts.getAttribute(CSS.Attribute.BOTTOM);
+ if (bottom != null)
+ bottom.setFontBases(emBase, exBase);
+ if (left != null || right != null || top != null || bottom != null)
+ {
+ PositionInfo pos = new PositionInfo(type, left, right, top,
+ bottom);
+ positionInfo.put(view, pos);
+ }
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/CSS.java b/libjava/classpath/javax/swing/text/html/CSS.java
new file mode 100644
index 000000000..0a77bdff9
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/CSS.java
@@ -0,0 +1,736 @@
+/* CSS.java -- Provides CSS attributes
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import gnu.javax.swing.text.html.css.BorderStyle;
+import gnu.javax.swing.text.html.css.BorderWidth;
+import gnu.javax.swing.text.html.css.CSSColor;
+import gnu.javax.swing.text.html.css.FontSize;
+import gnu.javax.swing.text.html.css.FontStyle;
+import gnu.javax.swing.text.html.css.FontWeight;
+import gnu.javax.swing.text.html.css.Length;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import javax.swing.text.MutableAttributeSet;
+
+/**
+ * Provides CSS attributes to be used by the HTML view classes. The constants
+ * defined here are used as keys for text attributes for use in
+ * {@link javax.swing.text.AttributeSet}s of {@link javax.swing.text.Element}s.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class CSS implements Serializable
+{
+ /**
+ * Returns an array of all CSS attributes.
+ *
+ * @return All available CSS.Attribute objects.
+ */
+ public static CSS.Attribute[] getAllAttributeKeys()
+ {
+ Object[] src = Attribute.attributeMap.values().toArray();
+ CSS.Attribute[] dst = new CSS.Attribute[ src.length ];
+ System.arraycopy(src, 0, dst, 0, src.length);
+ return dst;
+ }
+
+ /**
+ * Returns an a given CSS attribute.
+ *
+ * @param name - The name of the attribute.
+ * @return The CSS attribute with the given name, or <code>null</code> if
+ * no attribute with that name exists.
+ */
+ public static CSS.Attribute getAttribute(String name)
+ {
+ return (CSS.Attribute)Attribute.attributeMap.get( name );
+ }
+
+ public static final class Attribute
+ {
+ /**
+ * The CSS attribute 'background'.
+ */
+ public static final Attribute BACKGROUND =
+ new Attribute("background", false, null);
+
+ /**
+ * The CSS attribute 'background-attachment'.
+ */
+ public static final Attribute BACKGROUND_ATTACHMENT =
+ new Attribute("background-attachment", false, "scroll");
+
+ /**
+ * The CSS attribute 'background-color'.
+ */
+ public static final Attribute BACKGROUND_COLOR =
+ new Attribute("background-color", false, "transparent");
+
+ /**
+ * The CSS attribute 'background-image'.
+ */
+ public static final Attribute BACKGROUND_IMAGE =
+ new Attribute("background-image", false, "none");
+
+ /**
+ * The CSS attribute 'background-position'.
+ */
+ public static final Attribute BACKGROUND_POSITION =
+ new Attribute("background-position", false, null);
+
+ /**
+ * The CSS attribute 'background-repeat'.
+ */
+ public static final Attribute BACKGROUND_REPEAT =
+ new Attribute("background-repeat", false, "repeat");
+
+ /**
+ * The CSS attribute 'border'.
+ */
+ public static final Attribute BORDER = new Attribute("border", false, null);
+
+ /**
+ * The CSS attribute 'border-bottom'.
+ */
+ public static final Attribute BORDER_BOTTOM =
+ new Attribute("border-bottom", false, null);
+
+ /**
+ * The CSS attribute 'border-bottom-width'.
+ */
+ public static final Attribute BORDER_BOTTOM_WIDTH =
+ new Attribute("border-bottom-width", false, "medium");
+
+ /**
+ * The CSS attribute 'border-color'.
+ */
+ public static final Attribute BORDER_COLOR =
+ new Attribute("border-color", false, "black");
+
+ /**
+ * The CSS attribute 'border-left'.
+ */
+ public static final Attribute BORDER_LEFT =
+ new Attribute("border-left", false, null);
+
+ /**
+ * The CSS attribute 'border-left-width'.
+ */
+ public static final Attribute BORDER_LEFT_WIDTH =
+ new Attribute("border-left-width", false, "medium");
+
+ /**
+ * The CSS attribute 'border-right'.
+ */
+ public static final Attribute BORDER_RIGHT =
+ new Attribute("border-right", false, null);
+
+ /**
+ * The CSS attribute 'border-right-width'.
+ */
+ public static final Attribute BORDER_RIGHT_WIDTH =
+ new Attribute("border-right-width", false, "medium");
+
+ /**
+ * The CSS attribute 'border-style'.
+ */
+ public static final Attribute BORDER_STYLE =
+ new Attribute("border-style", false, "none");
+
+ /**
+ * The CSS attribute 'border-top'.
+ */
+ public static final Attribute BORDER_TOP =
+ new Attribute("border-top", false, null);
+
+ /**
+ * The CSS attribute 'border-top-width'.
+ */
+ public static final Attribute BORDER_TOP_WIDTH =
+ new Attribute("border-top-width", false, "medium");
+
+ /**
+ * The CSS attribute 'border-width'.
+ */
+ public static final Attribute BORDER_WIDTH =
+ new Attribute("border-width", false, "medium");
+
+ /**
+ * The CSS attribute 'clear'.
+ */
+ public static final Attribute CLEAR = new Attribute("clear", false, "none");
+
+ /**
+ * The CSS attribute 'color'.
+ */
+ public static final Attribute COLOR = new Attribute("color", true, "black");
+
+ /**
+ * The CSS attribute 'display'.
+ */
+ public static final Attribute DISPLAY =
+ new Attribute("display", false, "block");
+
+ /**
+ * The CSS attribute 'float'.
+ */
+ public static final Attribute FLOAT = new Attribute("float", false, "none");
+
+ /**
+ * The CSS attribute 'font'.
+ */
+ public static final Attribute FONT = new Attribute("font", true, null);
+
+ /**
+ * The CSS attribute 'font-family'.
+ */
+ public static final Attribute FONT_FAMILY =
+ new Attribute("font-family", true, null);
+
+ /**
+ * The CSS attribute 'font-size'.
+ */
+ public static final Attribute FONT_SIZE =
+ new Attribute("font-size", true, "medium");
+
+ /**
+ * The CSS attribute 'font-style'.
+ */
+ public static final Attribute FONT_STYLE =
+ new Attribute("font-style", true, "normal");
+
+ /**
+ * The CSS attribute 'font-variant'.
+ */
+ public static final Attribute FONT_VARIANT =
+ new Attribute("font-variant", true, "normal");
+
+ /**
+ * The CSS attribute 'font-weight'.
+ */
+ public static final Attribute FONT_WEIGHT =
+ new Attribute("font-weight", true, "normal");
+
+ /**
+ * The CSS attribute 'height'.
+ */
+ public static final Attribute HEIGHT =
+ new Attribute("height", false, "auto");
+
+ /**
+ * The CSS attribute 'letter-spacing'.
+ */
+ public static final Attribute LETTER_SPACING =
+ new Attribute("letter-spacing", true, "normal");
+
+ /**
+ * The CSS attribute 'line-height'.
+ */
+ public static final Attribute LINE_HEIGHT =
+ new Attribute("line-height", true, "normal");
+
+ /**
+ * The CSS attribute 'list-style'.
+ */
+ public static final Attribute LIST_STYLE =
+ new Attribute("list-style", true, null);
+
+ /**
+ * The CSS attribute 'list-style-image'.
+ */
+ public static final Attribute LIST_STYLE_IMAGE =
+ new Attribute("list-style-image", true, "none");
+
+ /**
+ * The CSS attribute 'list-style-position'.
+ */
+ public static final Attribute LIST_STYLE_POSITION =
+ new Attribute("list-style-position", true, "outside");
+
+ /**
+ * The CSS attribute 'list-style-type'.
+ */
+ public static final Attribute LIST_STYLE_TYPE =
+ new Attribute("list-style-type", true, "disc");
+
+ /**
+ * The CSS attribute 'margin'.
+ */
+ public static final Attribute MARGIN = new Attribute("margin", false, null);
+
+ /**
+ * The CSS attribute 'margin-bottom'.
+ */
+ public static final Attribute MARGIN_BOTTOM =
+ new Attribute("margin-bottom", false, "0");
+
+ /**
+ * The CSS attribute 'margin-left'.
+ */
+ public static final Attribute MARGIN_LEFT =
+ new Attribute("margin-left", false, "0");
+
+ /**
+ * The CSS attribute 'margin-right'.
+ */
+ public static final Attribute MARGIN_RIGHT =
+ new Attribute("margin-right", false, "0");
+
+ /**
+ * The CSS attribute 'margin-top'.
+ */
+ public static final Attribute MARGIN_TOP =
+ new Attribute("margin-top", false, "0");
+
+ /**
+ * The CSS attribute 'padding'.
+ */
+ public static final Attribute PADDING =
+ new Attribute("padding", false, null);
+
+ /**
+ * The CSS attribute 'padding-bottom'.
+ */
+ public static final Attribute PADDING_BOTTOM =
+ new Attribute("padding-bottom", false, "0");
+
+ /**
+ * The CSS attribute 'padding-left'.
+ */
+ public static final Attribute PADDING_LEFT =
+ new Attribute("padding-left", false, "0");
+
+ /**
+ * The CSS attribute 'padding-right'.
+ */
+ public static final Attribute PADDING_RIGHT =
+ new Attribute("padding-right", false, "0");
+
+ /**
+ * The CSS attribute 'padding-top'.
+ */
+ public static final Attribute PADDING_TOP =
+ new Attribute("padding-top", false, "0");
+
+ /**
+ * The CSS attribute 'text-align'.
+ */
+ public static final Attribute TEXT_ALIGN =
+ new Attribute("text-align", true, null);
+
+ /**
+ * The CSS attribute 'text-decoration'.
+ */
+ public static final Attribute TEXT_DECORATION =
+ new Attribute("text-decoration", true, "none");
+
+ /**
+ * The CSS attribute 'text-indent'.
+ */
+ public static final Attribute TEXT_INDENT =
+ new Attribute("text-indent", true, "0");
+
+ /**
+ * The CSS attribute 'text-transform'.
+ */
+ public static final Attribute TEXT_TRANSFORM =
+ new Attribute("text-transform", true, "none");
+
+ /**
+ * The CSS attribute 'vertical-align'.
+ */
+ public static final Attribute VERTICAL_ALIGN =
+ new Attribute("vertical-align", false, "baseline");
+
+ /**
+ * The CSS attribute 'white-space'.
+ */
+ public static final Attribute WHITE_SPACE =
+ new Attribute("white-space", true, "normal");
+
+ /**
+ * The CSS attribute 'width'.
+ */
+ public static final Attribute WIDTH =
+ new Attribute("width", false, "auto");
+
+ /**
+ * The CSS attribute 'word-spacing'.
+ */
+ public static final Attribute WORD_SPACING =
+ new Attribute("word-spacing", true, "normal");
+
+ // Some GNU Classpath specific extensions.
+ static final Attribute BORDER_TOP_STYLE =
+ new Attribute("border-top-style", false, null);
+ static final Attribute BORDER_BOTTOM_STYLE =
+ new Attribute("border-bottom-style", false, null);
+ static final Attribute BORDER_LEFT_STYLE =
+ new Attribute("border-left-style", false, null);
+ static final Attribute BORDER_RIGHT_STYLE =
+ new Attribute("border-right-style", false, null);
+ static final Attribute BORDER_TOP_COLOR =
+ new Attribute("border-top-color", false, null);
+ static final Attribute BORDER_BOTTOM_COLOR =
+ new Attribute("border-bottom-color", false, null);
+ static final Attribute BORDER_LEFT_COLOR =
+ new Attribute("border-left-color", false, null);
+ static final Attribute BORDER_RIGHT_COLOR =
+ new Attribute("border-right-color", false, null);
+ static final Attribute BORDER_SPACING =
+ new Attribute("border-spacing", false, null);
+ static final Attribute POSITION =
+ new Attribute("position", false, null);
+ static final Attribute LEFT =
+ new Attribute("left", false, null);
+ static final Attribute RIGHT =
+ new Attribute("right", false, null);
+ static final Attribute TOP =
+ new Attribute("top", false, null);
+ static final Attribute BOTTOM =
+ new Attribute("bottom", false, null);
+
+ /**
+ * The attribute string.
+ */
+ String attStr;
+
+ /**
+ * Indicates if this attribute should be inherited from it's parent or
+ * not.
+ */
+ boolean isInherited;
+
+ /**
+ * A default value for this attribute if one exists, otherwise null.
+ */
+ String defaultValue;
+
+ /**
+ * A HashMap of all attributes.
+ */
+ static HashMap attributeMap;
+
+ /**
+ * Creates a new Attribute instance with the specified values.
+ *
+ * @param attr the attribute string
+ * @param inherited if the attribute should be inherited or not
+ * @param def a default value; may be <code>null</code>
+ */
+ Attribute(String attr, boolean inherited, String def)
+ {
+ attStr = attr;
+ isInherited = inherited;
+ defaultValue = def;
+ if( attributeMap == null)
+ attributeMap = new HashMap();
+ attributeMap.put( attr, this );
+ }
+
+ /**
+ * Returns the string representation of this attribute as specified
+ * in the CSS specification.
+ */
+ public String toString()
+ {
+ return attStr;
+ }
+
+ /**
+ * Returns <code>true</code> if the attribute should be inherited from
+ * the parent, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the attribute should be inherited from
+ * the parent, <code>false</code> otherwise
+ */
+ public boolean isInherited()
+ {
+ return isInherited;
+ }
+
+ /**
+ * Returns the default value of this attribute if one exists,
+ * <code>null</code> otherwise.
+ *
+ * @return the default value of this attribute if one exists,
+ * <code>null</code> otherwise
+ */
+ public String getDefaultValue()
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Maps attribute values (String) to some converter class, based on the
+ * key.
+ *
+ * @param att the key
+ * @param v the value
+ *
+ * @return the wrapped value
+ */
+ static Object getValue(Attribute att, String v)
+ {
+ Object o;
+ if (att == Attribute.FONT_SIZE)
+ o = new FontSize(v);
+ else if (att == Attribute.FONT_WEIGHT)
+ o = new FontWeight(v);
+ else if (att == Attribute.FONT_STYLE)
+ o = new FontStyle(v);
+ else if (att == Attribute.COLOR || att == Attribute.BACKGROUND_COLOR
+ || att == Attribute.BORDER_COLOR
+ || att == Attribute.BORDER_TOP_COLOR
+ || att == Attribute.BORDER_BOTTOM_COLOR
+ || att == Attribute.BORDER_LEFT_COLOR
+ || att == Attribute.BORDER_RIGHT_COLOR)
+ o = new CSSColor(v);
+ else if (att == Attribute.MARGIN || att == Attribute.MARGIN_BOTTOM
+ || att == Attribute.MARGIN_LEFT || att == Attribute.MARGIN_RIGHT
+ || att == Attribute.MARGIN_TOP || att == Attribute.WIDTH
+ || att == Attribute.HEIGHT
+ || att == Attribute.PADDING || att == Attribute.PADDING_BOTTOM
+ || att == Attribute.PADDING_LEFT || att == Attribute.PADDING_RIGHT
+ || att == Attribute.PADDING_TOP
+ || att == Attribute.LEFT || att == Attribute.RIGHT
+ || att == Attribute.TOP || att == Attribute.BOTTOM)
+ o = new Length(v);
+ else if (att == Attribute.BORDER_WIDTH || att == Attribute.BORDER_TOP_WIDTH
+ || att == Attribute.BORDER_LEFT_WIDTH
+ || att == Attribute.BORDER_RIGHT_WIDTH
+ || att == Attribute.BORDER_BOTTOM_WIDTH)
+ o = new BorderWidth(v);
+ else
+ o = v;
+ return o;
+ }
+
+ static void addInternal(MutableAttributeSet atts, Attribute a, String v)
+ {
+ if (a == Attribute.BACKGROUND)
+ parseBackgroundShorthand(atts, v);
+ else if (a == Attribute.PADDING)
+ parsePaddingShorthand(atts, v);
+ else if (a == Attribute.MARGIN)
+ parseMarginShorthand(atts, v);
+ else if (a == Attribute.BORDER || a == Attribute.BORDER_LEFT
+ || a == Attribute.BORDER_RIGHT || a == Attribute.BORDER_TOP
+ || a == Attribute.BORDER_BOTTOM)
+ parseBorderShorthand(atts, v, a);
+ }
+
+ /**
+ * Parses the background shorthand and translates it to more specific
+ * background attributes.
+ *
+ * @param atts the attributes
+ * @param v the value
+ */
+ private static void parseBackgroundShorthand(MutableAttributeSet atts,
+ String v)
+ {
+ StringTokenizer tokens = new StringTokenizer(v, " ");
+ while (tokens.hasMoreElements())
+ {
+ String token = tokens.nextToken();
+ if (CSSColor.isValidColor(token))
+ atts.addAttribute(Attribute.BACKGROUND_COLOR,
+ new CSSColor(token));
+ }
+ }
+
+ /**
+ * Parses the padding shorthand and translates to the specific padding
+ * values.
+ *
+ * @param atts the attributes
+ * @param v the actual value
+ */
+ private static void parsePaddingShorthand(MutableAttributeSet atts, String v)
+ {
+ StringTokenizer tokens = new StringTokenizer(v, " ");
+ int numTokens = tokens.countTokens();
+ if (numTokens == 1)
+ {
+ Length l = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.PADDING_BOTTOM, l);
+ atts.addAttribute(Attribute.PADDING_LEFT, l);
+ atts.addAttribute(Attribute.PADDING_RIGHT, l);
+ atts.addAttribute(Attribute.PADDING_TOP, l);
+ }
+ else if (numTokens == 2)
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.PADDING_BOTTOM, l1);
+ atts.addAttribute(Attribute.PADDING_TOP, l1);
+ atts.addAttribute(Attribute.PADDING_LEFT, l2);
+ atts.addAttribute(Attribute.PADDING_RIGHT, l2);
+ }
+ else if (numTokens == 3)
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ Length l3 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.PADDING_TOP, l1);
+ atts.addAttribute(Attribute.PADDING_LEFT, l2);
+ atts.addAttribute(Attribute.PADDING_RIGHT, l2);
+ atts.addAttribute(Attribute.PADDING_BOTTOM, l3);
+ }
+ else
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ Length l3 = new Length(tokens.nextToken());
+ Length l4 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.PADDING_TOP, l1);
+ atts.addAttribute(Attribute.PADDING_RIGHT, l2);
+ atts.addAttribute(Attribute.PADDING_BOTTOM, l3);
+ atts.addAttribute(Attribute.PADDING_LEFT, l4);
+ }
+ }
+
+ /**
+ * Parses the margin shorthand and translates to the specific margin
+ * values.
+ *
+ * @param atts the attributes
+ * @param v the actual value
+ */
+ private static void parseMarginShorthand(MutableAttributeSet atts, String v)
+ {
+ StringTokenizer tokens = new StringTokenizer(v, " ");
+ int numTokens = tokens.countTokens();
+ if (numTokens == 1)
+ {
+ Length l = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.MARGIN_BOTTOM, l);
+ atts.addAttribute(Attribute.MARGIN_LEFT, l);
+ atts.addAttribute(Attribute.MARGIN_RIGHT, l);
+ atts.addAttribute(Attribute.MARGIN_TOP, l);
+ }
+ else if (numTokens == 2)
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.MARGIN_BOTTOM, l1);
+ atts.addAttribute(Attribute.MARGIN_TOP, l1);
+ atts.addAttribute(Attribute.MARGIN_LEFT, l2);
+ atts.addAttribute(Attribute.MARGIN_RIGHT, l2);
+ }
+ else if (numTokens == 3)
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ Length l3 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.MARGIN_TOP, l1);
+ atts.addAttribute(Attribute.MARGIN_LEFT, l2);
+ atts.addAttribute(Attribute.MARGIN_RIGHT, l2);
+ atts.addAttribute(Attribute.MARGIN_BOTTOM, l3);
+ }
+ else
+ {
+ Length l1 = new Length(tokens.nextToken());
+ Length l2 = new Length(tokens.nextToken());
+ Length l3 = new Length(tokens.nextToken());
+ Length l4 = new Length(tokens.nextToken());
+ atts.addAttribute(Attribute.MARGIN_TOP, l1);
+ atts.addAttribute(Attribute.MARGIN_RIGHT, l2);
+ atts.addAttribute(Attribute.MARGIN_BOTTOM, l3);
+ atts.addAttribute(Attribute.MARGIN_LEFT, l4);
+ }
+ }
+
+ /**
+ * Parses the CSS border shorthand attribute and translates it to the
+ * more specific border attributes.
+ *
+ * @param atts the attribute
+ * @param value the value
+ */
+ private static void parseBorderShorthand(MutableAttributeSet atts,
+ String value, Attribute cssAtt)
+ {
+ StringTokenizer tokens = new StringTokenizer(value, " ");
+ while (tokens.hasMoreTokens())
+ {
+ String token = tokens.nextToken();
+ if (BorderStyle.isValidStyle(token))
+ {
+ if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_LEFT_STYLE, token);
+ if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_RIGHT_STYLE, token);
+ if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_BOTTOM_STYLE, token);
+ if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_TOP_STYLE, token);
+ }
+ else if (BorderWidth.isValid(token))
+ {
+ BorderWidth w = new BorderWidth(token);
+ if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_LEFT_WIDTH, w);
+ if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_RIGHT_WIDTH, w);
+ if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_BOTTOM_WIDTH, w);
+ if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_TOP_WIDTH, w);
+ }
+ else if (CSSColor.isValidColor(token))
+ {
+ CSSColor c = new CSSColor(token);
+ if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_LEFT_COLOR, c);
+ if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_RIGHT_COLOR, c);
+ if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_BOTTOM_COLOR, c);
+ if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER)
+ atts.addAttribute(Attribute.BORDER_TOP_COLOR, c);
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/CSSBorder.java b/libjava/classpath/javax/swing/text/html/CSSBorder.java
new file mode 100644
index 000000000..23fcdccc6
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/CSSBorder.java
@@ -0,0 +1,421 @@
+/* CSSBorder.java -- A border for rendering CSS border styles
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import gnu.javax.swing.text.html.css.BorderWidth;
+import gnu.javax.swing.text.html.css.CSSColor;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+
+import javax.swing.border.Border;
+import javax.swing.text.AttributeSet;
+
+/**
+ * A border implementation to render CSS border styles.
+ */
+class CSSBorder
+ implements Border
+{
+
+ /**
+ * The CSS border styles.
+ */
+
+ private static final int STYLE_NOT_SET = -1;
+ private static final int STYLE_NONE = 0;
+ private static final int STYLE_HIDDEN = 1;
+ private static final int STYLE_DOTTED = 2;
+ private static final int STYLE_DASHED = 3;
+ private static final int STYLE_SOLID = 4;
+ private static final int STYLE_DOUBLE = 5;
+ private static final int STYLE_GROOVE = 6;
+ private static final int STYLE_RIDGE = 7;
+ private static final int STYLE_INSET = 8;
+ private static final int STYLE_OUTSET = 9;
+
+ /**
+ * The left insets.
+ */
+ private int left;
+
+ /**
+ * The right insets.
+ */
+ private int right;
+
+ /**
+ * The top insets.
+ */
+ private int top;
+
+ /**
+ * The bottom insets.
+ */
+ private int bottom;
+
+ /**
+ * The border style on the left.
+ */
+ private int leftStyle;
+
+ /**
+ * The border style on the right.
+ */
+ private int rightStyle;
+
+ /**
+ * The border style on the top.
+ */
+ private int topStyle;
+
+ /**
+ * The color for the top border.
+ */
+ private Color topColor;
+
+ /**
+ * The color for the bottom border.
+ */
+ private Color bottomColor;
+
+ /**
+ * The color for the left border.
+ */
+ private Color leftColor;
+
+ /**
+ * The color for the right border.
+ */
+ private Color rightColor;
+
+ /**
+ * The border style on the bottom.
+ */
+ private int bottomStyle;
+
+ /**
+ * Creates a new CSS border and fetches its attributes from the specified
+ * attribute set.
+ *
+ * @param atts the attribute set that contains the border spec
+ */
+ CSSBorder(AttributeSet atts, StyleSheet ss)
+ {
+ // Determine the border styles.
+ int style = getBorderStyle(atts, CSS.Attribute.BORDER_STYLE);
+ if (style == STYLE_NOT_SET)
+ style = STYLE_NONE; // Default to none.
+ topStyle = bottomStyle = leftStyle = rightStyle = style;
+ style = getBorderStyle(atts, CSS.Attribute.BORDER_TOP_STYLE);
+ if (style != STYLE_NOT_SET)
+ topStyle = style;
+ style = getBorderStyle(atts, CSS.Attribute.BORDER_BOTTOM_STYLE);
+ if (style != STYLE_NOT_SET)
+ bottomStyle = style;
+ style = getBorderStyle(atts, CSS.Attribute.BORDER_LEFT_STYLE);
+ if (style != STYLE_NOT_SET)
+ leftStyle = style;
+ style = getBorderStyle(atts, CSS.Attribute.BORDER_RIGHT_STYLE);
+ if (style != STYLE_NOT_SET)
+ rightStyle = style;
+
+ // Determine the border colors.
+ Color color = getBorderColor(atts, CSS.Attribute.BORDER_COLOR);
+ if (color == null)
+ color = Color.BLACK;
+ topColor = bottomColor = leftColor = rightColor = color;
+ color = getBorderColor(atts, CSS.Attribute.BORDER_TOP_COLOR);
+ if (color != null)
+ topColor = color;
+ color = getBorderColor(atts, CSS.Attribute.BORDER_BOTTOM_COLOR);
+ if (color != null)
+ bottomColor = color;
+ color = getBorderColor(atts, CSS.Attribute.BORDER_LEFT_COLOR);
+ if (color != null)
+ leftColor = color;
+ color = getBorderColor(atts, CSS.Attribute.BORDER_RIGHT_COLOR);
+ if (color != null)
+ rightColor = color;
+
+ // Determine the border widths.
+ int width = getBorderWidth(atts, CSS.Attribute.BORDER_WIDTH, ss);
+ if (width == -1)
+ width = 0;
+ top = bottom = left = right = width;
+ width = getBorderWidth(atts, CSS.Attribute.BORDER_TOP_WIDTH, ss);
+ if (width >= 0)
+ top = width;
+ width = getBorderWidth(atts, CSS.Attribute.BORDER_BOTTOM_WIDTH, ss);
+ if (width >= 0)
+ bottom = width;
+ width = getBorderWidth(atts, CSS.Attribute.BORDER_LEFT_WIDTH, ss);
+ if (width >= 0)
+ left = width;
+ width = getBorderWidth(atts, CSS.Attribute.BORDER_RIGHT_WIDTH, ss);
+ if (width >= 0)
+ right = width;
+ }
+
+ /**
+ * Determines the border style for a given CSS attribute.
+ *
+ * @param atts the attribute set
+ * @param key the CSS key
+ *
+ * @return the border style according to the constants defined in this class
+ */
+ private int getBorderStyle(AttributeSet atts, CSS.Attribute key)
+ {
+ int style = STYLE_NOT_SET;
+ Object o = atts.getAttribute(key);
+ if (o != null)
+ {
+ String cssStyle = o.toString();
+ if (cssStyle.equals("none"))
+ style = STYLE_NONE;
+ else if (cssStyle.equals("hidden"))
+ style = STYLE_HIDDEN;
+ else if (cssStyle.equals("dotted"))
+ style = STYLE_DOTTED;
+ else if (cssStyle.equals("dashed"))
+ style = STYLE_DASHED;
+ else if (cssStyle.equals("solid"))
+ style = STYLE_SOLID;
+ else if (cssStyle.equals("double"))
+ style = STYLE_DOUBLE;
+ else if (cssStyle.equals("groove"))
+ style = STYLE_GROOVE;
+ else if (cssStyle.equals("ridge"))
+ style = STYLE_RIDGE;
+ else if (cssStyle.equals("inset"))
+ style = STYLE_INSET;
+ else if (cssStyle.equals("outset"))
+ style = STYLE_OUTSET;
+ }
+ return style;
+ }
+
+ /**
+ * Determines the border color for the specified key.
+ *
+ * @param atts the attribute set from which to fetch the color
+ * @param key the CSS key
+ *
+ * @return the border color
+ */
+ private Color getBorderColor(AttributeSet atts, CSS.Attribute key)
+ {
+ Object o = atts.getAttribute(key);
+ Color color = null;
+ if (o instanceof CSSColor)
+ {
+ CSSColor cssColor = (CSSColor) o;
+ color = cssColor.getValue();
+ }
+ return color;
+ }
+
+ /**
+ * Returns the width for the specified key.
+ *
+ * @param atts the attributes to fetch the width from
+ * @param key the CSS key
+ *
+ * @return the width, or -1 of none has been set
+ */
+ private int getBorderWidth(AttributeSet atts, CSS.Attribute key,
+ StyleSheet ss)
+ {
+ int width = -1;
+ Object o = atts.getAttribute(key);
+ if (o instanceof BorderWidth)
+ {
+ BorderWidth w = (BorderWidth) o;
+ w.setFontBases(ss.getEMBase(atts), ss.getEXBase(atts));
+ width = (int) ((BorderWidth) o).getValue();
+ }
+ return width;
+ }
+
+ /**
+ * Returns the border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(top, left, bottom, right);
+ }
+
+ /**
+ * CSS borders are generally opaque so return true here.
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ // Top border.
+ paintBorderLine(g, x, y + top / 2, x + width, y + top / 2, topStyle, top,
+ topColor, false);
+ // Left border.
+ paintBorderLine(g, x + left / 2, y, x + left / 2, y + height, leftStyle,
+ left, leftColor, true);
+ // Bottom border.
+ paintBorderLine(g, x, y + height - bottom / 2, x + width,
+ y + height - bottom / 2, topStyle, bottom, bottomColor,
+ false);
+ // Right border.
+ paintBorderLine(g, x + width - right / 2, y, x + width - right / 2,
+ y + height, topStyle, right, rightColor, true);
+
+ }
+
+ private void paintBorderLine(Graphics g, int x1, int y1, int x2, int y2,
+ int style, int width, Color color,
+ boolean vertical)
+ {
+ switch (style)
+ {
+ case STYLE_DOTTED:
+ paintDottedLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_DASHED:
+ paintDashedLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_SOLID:
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_DOUBLE:
+ paintDoubleLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_GROOVE:
+ paintGrooveLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_RIDGE:
+ paintRidgeLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_OUTSET:
+ paintOutsetLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_INSET:
+ paintInsetLine(g, x1, y1, x2, y2, width, color, vertical);
+ break;
+ case STYLE_NONE:
+ case STYLE_HIDDEN:
+ default:
+ // Nothing to do in these cases.
+ }
+ }
+
+ private void paintDottedLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintDashedLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintSolidLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ int x = Math.min(x1, x2);
+ int y = Math.min(y1, y1);
+ int w = Math.abs(x2 - x1);
+ int h = Math.abs(y2 - y1);
+ if (vertical)
+ {
+ w = width;
+ x -= width / 2;
+ }
+ else
+ {
+ h = width;
+ y -= width / 2;
+ }
+ g.setColor(color);
+ g.fillRect(x, y, w, h);
+ }
+
+ private void paintDoubleLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintGrooveLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintRidgeLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintOutsetLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+ private void paintInsetLine(Graphics g, int x1, int y1, int x2, int y2,
+ int width, Color color, boolean vertical)
+ {
+ // FIXME: Implement this.
+ paintSolidLine(g, x1, y1, x2, y2, width, color, vertical);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/CSSParser.java b/libjava/classpath/javax/swing/text/html/CSSParser.java
new file mode 100644
index 000000000..5024c7b59
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/CSSParser.java
@@ -0,0 +1,561 @@
+/* CSSParser.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.io.*;
+
+/**
+ * Parses a CSS document. This works by way of a delegate that implements the
+ * CSSParserCallback interface. The delegate is notified of the following
+ * events:
+ * - Import statement: handleImport
+ * - Selectors handleSelector. This is invoked for each string. For example if
+ * the Reader contained p, bar , a {}, the delegate would be notified 4 times,
+ * for 'p,' 'bar' ',' and 'a'.
+ * - When a rule starts, startRule
+ * - Properties in the rule via the handleProperty. This
+ * is invoked one per property/value key, eg font size: foo;, would cause the
+ * delegate to be notified once with a value of 'font size'.
+ * - Values in the rule via the handleValue, this is notified for the total value.
+ * - When a rule ends, endRule
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+class CSSParser
+{
+
+ /**
+ * Receives all information about the CSS document structure while parsing it.
+ * The methods are invoked by parser.
+ */
+ static interface CSSParserCallback
+ {
+ /**
+ * Handles the import statment in the document.
+ *
+ * @param imp - the import string
+ */
+ public abstract void handleImport(String imp);
+
+ /**
+ * Called when the start of a rule is encountered.
+ */
+ public abstract void startRule();
+
+ /**
+ * Called when the end of a rule is encountered.
+ */
+ public abstract void endRule();
+
+ /**
+ * Handles the selector of a rule.
+ *
+ * @param selector - the selector in the rule
+ */
+ public abstract void handleSelector(String selector);
+
+ /**
+ * Handles the properties in the document.
+ *
+ * @param property - the property in the document.
+ */
+ public abstract void handleProperty(String property);
+
+ /**
+ * Handles the values in the document.
+ *
+ * @param value - the value to handle.
+ */
+ public abstract void handleValue(String value);
+
+ }
+
+ /**
+ * The identifier of the rule.
+ */
+ private static final int IDENTIFIER = 1;
+
+ /**
+ * The open bracket.
+ */
+ private static final int BRACKET_OPEN = 2;
+
+ /**
+ * The close bracket.
+ */
+ private static final int BRACKET_CLOSE = 3;
+
+ /**
+ * The open brace.
+ */
+ private static final int BRACE_OPEN = 4;
+
+ /**
+ * The close brace.
+ */
+ private static final int BRACE_CLOSE = 5;
+
+ /**
+ * The open parentheses.
+ */
+ private static final int PAREN_OPEN = 6;
+
+ /**
+ * The close parentheses.
+ */
+ private static final int PAREN_CLOSE = 7;
+
+ /**
+ * The end of the document.
+ */
+ private static final int END = -1;
+
+ /**
+ * The character mapping in the document.
+ */
+ // FIXME: What is this used for?
+ private static final char[] charMapping = null;
+
+ /**
+ * Set to true if one character has been read ahead.
+ */
+ private boolean didPushChar;
+
+ /**
+ * The read ahead character.
+ */
+ private int pushedChar;
+
+ /**
+ * Used to indicate blocks.
+ */
+ private int[] unitStack;
+
+ /**
+ * Number of valid blocks.
+ */
+ private int stackCount;
+
+ /**
+ * Holds the incoming CSS rules.
+ */
+ private Reader reader;
+
+ /**
+ * Set to true when the first non @ rule is encountered.
+ */
+ private boolean encounteredRuleSet;
+
+ /**
+ * The call back used to parse.
+ */
+ private CSSParser.CSSParserCallback callback;
+
+ /**
+ * nextToken() inserts the string here.
+ */
+ private char[] tokenBuffer;
+
+ /**
+ * Current number of chars in tokenBufferLength.
+ */
+ private int tokenBufferLength;
+
+ /**
+ * Set to true if any whitespace is read.
+ */
+ private boolean readWS;
+
+ /**
+ * Constructor
+ */
+ CSSParser()
+ {
+ tokenBuffer = new char[10];
+ }
+
+ /**
+ * Appends a character to the token buffer.
+ *
+ * @param c - the character to append
+ */
+ private void append(char c)
+ {
+ if (tokenBuffer.length >= tokenBufferLength)
+ {
+ char[] temp = new char[tokenBufferLength * 2];
+ if (tokenBuffer != null)
+ System.arraycopy(tokenBuffer, 0, temp, 0, tokenBufferLength);
+
+ temp[tokenBufferLength] = c;
+ tokenBuffer = temp;
+ }
+ else
+ tokenBuffer[tokenBufferLength] = c;
+ tokenBufferLength++;
+ }
+
+ /**
+ * Fetches the next token.
+ *
+ * @param c - the character to fetch.
+ * @return the location
+ * @throws IOException - any i/o error encountered while reading
+ */
+ private int nextToken(char c) throws IOException
+ {
+ readWS = false;
+ int next = readWS();
+
+ switch (next)
+ {
+ case '\"':
+ if (tokenBufferLength > 0)
+ tokenBufferLength--;
+ return IDENTIFIER;
+ case '\'':
+ if (tokenBufferLength > 0)
+ tokenBufferLength--;
+ return IDENTIFIER;
+ case '(':
+ return PAREN_OPEN;
+ case ')':
+ return PAREN_CLOSE;
+ case '{':
+ return BRACE_OPEN;
+ case '}':
+ return BRACE_CLOSE;
+ case '[':
+ return BRACKET_OPEN;
+ case ']':
+ return BRACKET_CLOSE;
+ case -1:
+ return END;
+ default:
+ pushChar(next);
+ getIdentifier(c);
+ return IDENTIFIER;
+ }
+ }
+
+ /**
+ * Reads a character from the stream.
+ *
+ * @return the number of characters read or -1 if end of stream is reached.
+ * @throws IOException - any i/o encountered while reading
+ */
+ private int readChar() throws IOException
+ {
+ if (didPushChar)
+ {
+ didPushChar = false;
+ return pushedChar;
+ }
+ return reader.read();
+ }
+
+ /**
+ * Parses the the contents of the reader using the
+ * callback.
+ *
+ * @param reader - the reader to read from
+ * @param callback - the callback instance
+ * @param parsingDeclaration - true if parsing a declaration
+ * @throws IOException - any i/o error from the reader
+ */
+ void parse(Reader reader, CSSParser.CSSParserCallback callback,
+ boolean parsingDeclaration)
+ throws IOException
+ {
+ this.reader = reader;
+ this.callback = callback;
+
+ try
+ {
+ if (!parsingDeclaration)
+ while(getNextStatement())
+ ;
+ else
+ parseDeclarationBlock();
+ }
+ catch (IOException ioe)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * Skips any white space, returning the character after the white space.
+ *
+ * @return the character after the whitespace
+ * @throws IOException - any i/o error from the reader
+ */
+ private int readWS() throws IOException
+ {
+ int next = readChar();
+ while (Character.isWhitespace((char) next))
+ {
+ readWS = true;
+ int tempNext = readChar();
+ if (tempNext == END)
+ return next;
+ next = tempNext;
+ }
+
+ // Its all whitespace
+ return END;
+ }
+
+ /**
+ * Gets the next statement, returning false if the end is reached.
+ * A statement is either an At-rule, or a ruleset.
+ *
+ * @return false if the end is reached
+ * @throws IOException - any i/o error from the reader
+ */
+ private boolean getNextStatement() throws IOException
+ {
+ int c = nextToken((char) 0);
+ switch (c)
+ {
+ case PAREN_OPEN:
+ case BRACE_OPEN:
+ case BRACKET_OPEN:
+ parseTillClosed(c);
+ break;
+ case BRACKET_CLOSE:
+ case BRACE_CLOSE:
+ case PAREN_CLOSE:
+ throw new IOException("Not a proper statement.");
+ case IDENTIFIER:
+ if (tokenBuffer[0] == ('@'))
+ parseAtRule();
+ else
+ parseRuleSet();
+ break;
+ case END:
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Parses an @ rule, stopping at a matching brace pair, or ;.
+ *
+ * @throws IOException - any i/o error from the reader
+ */
+ private void parseAtRule() throws IOException
+ {
+ // An At-Rule begins with the "@" character followed immediately by a keyword.
+ // Following the keyword separated by a space is an At-rule statement appropriate
+ // to the At-keyword used. If the At-Rule is a simple declarative statement
+ // (charset, import, fontdef), it is terminated by a semi-colon (";".)
+ // If the At-Rule is a conditional or informative statement (media, page, font-face),
+ // it is followed by optional arguments and then a style declaration block inside matching
+ // curly braces ("{", "}".) At-Rules are sometimes nestable, depending on the context.
+ // If any part of an At-Rule is not understood, it should be ignored.
+
+ // FIXME: Not Implemented
+ // call handleimport
+ }
+
+ /**
+ * Parses the next rule set, which is a selector followed by a declaration
+ * block.
+ *
+ * @throws IOException - any i/o error from the reader
+ */
+ private void parseRuleSet() throws IOException
+ {
+ // call parseDeclarationBlock
+ // call parse selectors
+ // call parse identifiers
+ // call startrule/endrule
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Parses a set of selectors, returning false if the end of the stream is
+ * reached.
+ *
+ * @return false if the end of stream is reached
+ * @throws IOException - any i/o error from the reader
+ */
+ private boolean parseSelectors() throws IOException
+ {
+ // FIXME: Not Implemented
+ // call handleselector
+ return false;
+ }
+
+ /**
+ * Parses a declaration block. Which a number of declarations followed by a
+ * })].
+ *
+ * @throws IOException - any i/o error from the reader
+ */
+ private void parseDeclarationBlock() throws IOException
+ {
+ // call parseDeclaration
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Parses a single declaration, which is an identifier a : and another identifier.
+ * This returns the last token seen.
+ *
+ * @returns the last token
+ * @throws IOException - any i/o error from the reader
+ */
+ private int parseDeclaration() throws IOException
+ {
+ // call handleValue
+ // FIXME: Not Implemented
+ return 0;
+ }
+
+ /**
+ * Parses identifiers until c is encountered, returning the ending token,
+ * which will be IDENTIFIER if c is found.
+ *
+ * @param c - the stop character
+ * @param wantsBlocks - true if blocks are wanted
+ * @return the ending token
+ * @throws IOException - any i/o error from the reader
+ */
+ private int parseIdentifiers(char c, boolean wantsBlocks) throws IOException
+ {
+ // FIXME: Not implemented
+ // call handleproperty?
+ return 0;
+ }
+
+ /**
+ * Parses till a matching block close is encountered. This is only appropriate
+ * to be called at the top level (no nesting).
+ *
+ * @param i - FIXME
+ * @throws IOException - any i/o error from the reader
+ */
+ private void parseTillClosed(int i) throws IOException
+ {
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Gets an identifier, returning true if the length of the string is greater
+ * than 0, stopping when c, whitespace, or one of {}()[] is hit.
+ *
+ * @param c - the stop character
+ * @return returns true if the length of the string > 0
+ * @throws IOException - any i/o error from the reader
+ */
+ private boolean getIdentifier(char c) throws IOException
+ {
+ // FIXME: Not Implemented
+ return false;
+ }
+
+ /**
+ * Reads till c is encountered, escaping characters as necessary.
+ *
+ * @param c - the stop character
+ * @throws IOException - any i/o error from the reader
+ */
+ private void readTill(char c) throws IOException
+ {
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Parses a comment block.
+ *
+ * @throws IOException - any i/o error from the reader
+ */
+ private void readComment() throws IOException
+ {
+ // Should ignore comments. Read until end of comment.
+ // FIXME: Not implemented
+ }
+
+ /**
+ * Called when a block start is encountered ({[.
+ *
+ * @param start of block
+ */
+ private void startBlock(int start)
+ {
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Called when an end block is encountered )]}
+ *
+ * @param end of block
+ */
+ private void endBlock(int end)
+ {
+ // FIXME: Not Implemented
+ }
+
+ /**
+ * Checks if currently in a block.
+ *
+ * @return true if currently in a block.
+ */
+ private boolean inBlock()
+ {
+ // FIXME: Not Implemented
+ return false;
+ }
+
+ /**
+ * Supports one character look ahead, this will throw if called twice in a row.
+ *
+ * @param c - the character to push.
+ * @throws IOException - if called twice in a row
+ */
+ private void pushChar(int c) throws IOException
+ {
+ if (didPushChar)
+ throw new IOException("pushChar called twice.");
+ didPushChar = true;
+ pushedChar = c;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java b/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java
new file mode 100644
index 000000000..bc7c36f4b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java
@@ -0,0 +1,123 @@
+/* FormSubmitEvent.java -- Event fired on form submit
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.net.URL;
+
+import javax.swing.text.Element;
+
+/**
+ * The event fired on form submit.
+ *
+ * @since 1.5
+ */
+public class FormSubmitEvent
+ extends HTMLFrameHyperlinkEvent
+{
+
+ // FIXME: Use enums when available.
+ /**
+ * The submit method.
+ */
+ public static class MethodType
+ {
+ /**
+ * Indicates a form submit with HTTP method POST.
+ */
+ public static final MethodType POST = new MethodType();
+
+ /**
+ * Indicates a form submit with HTTP method GET.
+ */
+ public static final MethodType GET = new MethodType();
+
+ private MethodType()
+ {
+ }
+ }
+
+ /**
+ * The submit method.
+ */
+ private MethodType method;
+
+ /**
+ * The actual submit data.
+ */
+ private String data;
+
+ /**
+ * Creates a new FormSubmitEvent.
+ *
+ * @param source the source
+ * @param type the type of hyperlink update
+ * @param url the action url
+ * @param el the associated element
+ * @param target the target attribute
+ * @param m the submit method
+ * @param d the submit data
+ */
+ FormSubmitEvent(Object source, EventType type, URL url, Element el,
+ String target, MethodType m, String d)
+ {
+ super(source, type, url, el, target);
+ method = m;
+ data = d;
+ }
+
+ /**
+ * Returns the submit data.
+ *
+ * @return the submit data
+ */
+ public String getData()
+ {
+ return data;
+ }
+
+ /**
+ * Returns the HTTP submit method.
+ *
+ * @return the HTTP submit method
+ */
+ public MethodType getMethod()
+ {
+ return method;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/FormView.java b/libjava/classpath/javax/swing/text/html/FormView.java
new file mode 100644
index 000000000..61c568f02
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/FormView.java
@@ -0,0 +1,870 @@
+/* FormView.java -- A view for a variety of HTML form elements
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.awt.Component;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+
+import javax.swing.ButtonModel;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JEditorPane;
+import javax.swing.JList;
+import javax.swing.JPasswordField;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.ComponentView;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.ElementIterator;
+import javax.swing.text.StyleConstants;
+
+/**
+ * A View that renders HTML form elements like buttons and input fields.
+ * This is implemented as a {@link ComponentView} that creates different Swing
+ * component depending on the type and setting of the different form elements.
+ *
+ * Namely, this view creates the following components:
+ * <table>
+ * <tr><th>Element type</th><th>Swing component</th></tr>
+ * <tr><td>input, button</td><td>JButton</td></tr>
+ * <tr><td>input, checkbox</td><td>JButton</td></tr>
+ * <tr><td>input, image</td><td>JButton</td></tr>
+ * <tr><td>input, password</td><td>JButton</td></tr>
+ * <tr><td>input, radio</td><td>JButton</td></tr>
+ * <tr><td>input, reset</td><td>JButton</td></tr>
+ * <tr><td>input, submit</td><td>JButton</td></tr>
+ * <tr><td>input, text</td><td>JButton</td></tr>
+ * <tr><td>select, size > 1 or with multiple attribute</td>
+ * <td>JList in JScrollPane</td></tr>
+ * <tr><td>select, size unspecified or == 1</td><td>JComboBox</td></tr>
+ * <tr><td>textarea, text</td><td>JTextArea in JScrollPane</td></tr>
+ * <tr><td>input, file</td><td>JTextField</td></tr>
+ * </table>
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class FormView
+ extends ComponentView
+ implements ActionListener
+{
+
+ protected class MouseEventListener
+ extends MouseAdapter
+ {
+ /**
+ * Creates a new <code>MouseEventListener</code>.
+ */
+ protected MouseEventListener()
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseReleased(MouseEvent ev)
+ {
+ String data = getImageData(ev.getPoint());
+ imageSubmit(data);
+ }
+ }
+
+ /**
+ * Actually submits the form data.
+ */
+ private class SubmitThread
+ extends Thread
+ {
+ /**
+ * The submit data.
+ */
+ private String data;
+
+ /**
+ * Creates a new SubmitThread.
+ *
+ * @param d the submit data
+ */
+ SubmitThread(String d)
+ {
+ data = d;
+ }
+
+ /**
+ * Actually performs the submit.
+ */
+ public void run()
+ {
+ if (data.length() > 0)
+ {
+ final String method = getMethod();
+ final URL actionURL = getActionURL();
+ final String target = getTarget();
+ URLConnection conn;
+ final JEditorPane editor = (JEditorPane) getContainer();
+ final HTMLDocument doc = (HTMLDocument) editor.getDocument();
+ HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
+ if (kit.isAutoFormSubmission())
+ {
+ try
+ {
+ final URL url;
+ if (method != null && method.equals("post"))
+ {
+ // Perform POST.
+ url = actionURL;
+ conn = url.openConnection();
+ postData(conn, data);
+ }
+ else
+ {
+ // Default to GET.
+ url = new URL(actionURL + "?" + data);
+ }
+ Runnable loadDoc = new Runnable()
+ {
+ public void run()
+ {
+ if (doc.isFrameDocument())
+ {
+ editor.fireHyperlinkUpdate(createSubmitEvent(method,
+ actionURL,
+ target));
+ }
+ else
+ {
+ try
+ {
+ editor.setPage(url);
+ }
+ catch (IOException ex)
+ {
+ // Oh well.
+ ex.printStackTrace();
+ }
+ }
+ }
+ };
+ SwingUtilities.invokeLater(loadDoc);
+ }
+ catch (MalformedURLException ex)
+ {
+ ex.printStackTrace();
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ else
+ {
+ editor.fireHyperlinkUpdate(createSubmitEvent(method,actionURL,
+ target));
+ }
+ }
+ }
+
+ /**
+ * Determines the submit method.
+ *
+ * @return the submit method
+ */
+ private String getMethod()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ String method = null;
+ if (formAtts != null)
+ {
+ method = (String) formAtts.getAttribute(HTML.Attribute.METHOD);
+ }
+ return method;
+ }
+
+ /**
+ * Determines the action URL.
+ *
+ * @return the action URL
+ */
+ private URL getActionURL()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ HTMLDocument doc = (HTMLDocument) getElement().getDocument();
+ URL url = doc.getBase();
+ if (formAtts != null)
+ {
+ String action =
+ (String) formAtts.getAttribute(HTML.Attribute.ACTION);
+ if (action != null)
+ {
+ try
+ {
+ url = new URL(url, action);
+ }
+ catch (MalformedURLException ex)
+ {
+ url = null;
+ }
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Fetches the target attribute.
+ *
+ * @return the target attribute or _self if none is present
+ */
+ private String getTarget()
+ {
+ AttributeSet formAtts = getFormAttributes();
+ String target = null;
+ if (formAtts != null)
+ {
+ target = (String) formAtts.getAttribute(HTML.Attribute.TARGET);
+ if (target != null)
+ target = target.toLowerCase();
+ }
+ if (target == null)
+ target = "_self";
+ return target;
+ }
+
+ /**
+ * Posts the form data over the specified connection.
+ *
+ * @param conn the connection
+ */
+ private void postData(URLConnection conn, String data)
+ {
+ conn.setDoOutput(true);
+ PrintWriter out = null;
+ try
+ {
+ out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream()));
+ out.print(data);
+ out.flush();
+ }
+ catch (IOException ex)
+ {
+ // Deal with this!
+ ex.printStackTrace();
+ }
+ finally
+ {
+ if (out != null)
+ out.close();
+ }
+ }
+
+ /**
+ * Determines the attributes from the relevant form tag.
+ *
+ * @return the attributes from the relevant form tag, <code>null</code>
+ * when there is no form tag
+ */
+ private AttributeSet getFormAttributes()
+ {
+ AttributeSet atts = null;
+ Element form = getFormElement();
+ if (form != null)
+ atts = form.getAttributes();
+ return atts;
+ }
+
+ /**
+ * Creates the submit event that should be fired.
+ *
+ * This is package private to avoid accessor methods.
+ *
+ * @param method the submit method
+ * @param actionURL the action URL
+ * @param target the target
+ *
+ * @return the submit event
+ */
+ FormSubmitEvent createSubmitEvent(String method, URL actionURL,
+ String target)
+ {
+ FormSubmitEvent.MethodType m = "post".equals(method)
+ ? FormSubmitEvent.MethodType.POST
+ : FormSubmitEvent.MethodType.GET;
+ return new FormSubmitEvent(FormView.this,
+ HyperlinkEvent.EventType.ACTIVATED,
+ actionURL, getElement(), target, m, data);
+ }
+ }
+
+ /**
+ * If the value attribute of an <code>&lt;input type=&quot;submit&quot;&gt>
+ * tag is not specified, then this string is used.
+ *
+ * @deprecated As of JDK1.3 the value is fetched from the UIManager property
+ * <code>FormView.submitButtonText</code>.
+ */
+ public static final String SUBMIT =
+ UIManager.getString("FormView.submitButtonText");
+
+ /**
+ * If the value attribute of an <code>&lt;input type=&quot;reset&quot;&gt>
+ * tag is not specified, then this string is used.
+ *
+ * @deprecated As of JDK1.3 the value is fetched from the UIManager property
+ * <code>FormView.resetButtonText</code>.
+ */
+ public static final String RESET =
+ UIManager.getString("FormView.resetButtonText");
+
+ /**
+ * If this is true, the maximum size is set to the preferred size.
+ */
+ private boolean maxIsPreferred;
+
+ /**
+ * Creates a new <code>FormView</code>.
+ *
+ * @param el the element that is displayed by this view.
+ */
+ public FormView(Element el)
+ {
+ super(el);
+ }
+
+ /**
+ * Creates the correct AWT component for rendering the form element.
+ */
+ protected Component createComponent()
+ {
+ Component comp = null;
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ Object tag = atts.getAttribute(StyleConstants.NameAttribute);
+ Object model = atts.getAttribute(StyleConstants.ModelAttribute);
+ if (tag.equals(HTML.Tag.INPUT))
+ {
+ String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+ if (type.equals("button"))
+ {
+ String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ JButton b = new JButton(value);
+ if (model != null)
+ {
+ b.setModel((ButtonModel) model);
+ b.addActionListener(this);
+ }
+ comp = b;
+ maxIsPreferred = true;
+ }
+ else if (type.equals("checkbox"))
+ {
+ if (model instanceof ResetableToggleButtonModel)
+ {
+ ResetableToggleButtonModel m =
+ (ResetableToggleButtonModel) model;
+ JCheckBox c = new JCheckBox();
+ c.setModel(m);
+ comp = c;
+ maxIsPreferred = true;
+ }
+ }
+ else if (type.equals("image"))
+ {
+ String src = (String) atts.getAttribute(HTML.Attribute.SRC);
+ JButton b;
+ try
+ {
+ URL base = ((HTMLDocument) el.getDocument()).getBase();
+ URL srcURL = new URL(base, src);
+ ImageIcon icon = new ImageIcon(srcURL);
+ b = new JButton(icon);
+ }
+ catch (MalformedURLException ex)
+ {
+ b = new JButton(src);
+ }
+ if (model != null)
+ {
+ b.setModel((ButtonModel) model);
+ b.addActionListener(this);
+ }
+ comp = b;
+ maxIsPreferred = true;
+ }
+ else if (type.equals("password"))
+ {
+ int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
+ -1);
+ JTextField tf = new JPasswordField();
+ if (size > 0)
+ tf.setColumns(size);
+ else
+ tf.setColumns(20);
+ if (model != null)
+ tf.setDocument((Document) model);
+ tf.addActionListener(this);
+ comp = tf;
+ maxIsPreferred = true;
+ }
+ else if (type.equals("radio"))
+ {
+ if (model instanceof ResetableToggleButtonModel)
+ {
+ ResetableToggleButtonModel m =
+ (ResetableToggleButtonModel) model;
+ JRadioButton c = new JRadioButton();
+ c.setModel(m);
+ comp = c;
+ maxIsPreferred = true;
+ }
+ }
+ else if (type.equals("reset"))
+ {
+ String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = UIManager.getString("FormView.resetButtonText");
+ JButton b = new JButton(value);
+ if (model != null)
+ {
+ b.setModel((ButtonModel) model);
+ b.addActionListener(this);
+ }
+ comp = b;
+ maxIsPreferred = true;
+ }
+ else if (type.equals("submit"))
+ {
+ String value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = UIManager.getString("FormView.submitButtonText");
+ JButton b = new JButton(value);
+ if (model != null)
+ {
+ b.setModel((ButtonModel) model);
+ b.addActionListener(this);
+ }
+ comp = b;
+ maxIsPreferred = true;
+ }
+ else if (type.equals("text"))
+ {
+ int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
+ -1);
+ JTextField tf = new JTextField();
+ if (size > 0)
+ tf.setColumns(size);
+ else
+ tf.setColumns(20);
+ if (model != null)
+ tf.setDocument((Document) model);
+ tf.addActionListener(this);
+ comp = tf;
+ maxIsPreferred = true;
+ }
+ }
+ else if (tag == HTML.Tag.TEXTAREA)
+ {
+ JTextArea textArea = new JTextArea((Document) model);
+ int rows = HTML.getIntegerAttributeValue(atts, HTML.Attribute.ROWS, 1);
+ textArea.setRows(rows);
+ int cols = HTML.getIntegerAttributeValue(atts, HTML.Attribute.COLS, 20);
+ textArea.setColumns(cols);
+ maxIsPreferred = true;
+ comp = new JScrollPane(textArea,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+ }
+ else if (tag == HTML.Tag.SELECT)
+ {
+ if (model instanceof SelectListModel)
+ {
+ SelectListModel slModel = (SelectListModel) model;
+ JList list = new JList(slModel);
+ int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE,
+ 1);
+ list.setVisibleRowCount(size);
+ list.setSelectionModel(slModel.getSelectionModel());
+ comp = new JScrollPane(list);
+ }
+ else if (model instanceof SelectComboBoxModel)
+ {
+ SelectComboBoxModel scbModel = (SelectComboBoxModel) model;
+ comp = new JComboBox(scbModel);
+ }
+ maxIsPreferred = true;
+ }
+ return comp;
+ }
+
+ /**
+ * Determines the maximum span for this view on the specified axis.
+ *
+ * @param axis the axis along which to determine the span
+ *
+ * @return the maximum span for this view on the specified axis
+ *
+ * @throws IllegalArgumentException if the axis is invalid
+ */
+ public float getMaximumSpan(int axis)
+ {
+ float span;
+ if (maxIsPreferred)
+ span = getPreferredSpan(axis);
+ else
+ span = super.getMaximumSpan(axis);
+ return span;
+ }
+
+ /**
+ * Processes an action from the Swing component.
+ *
+ * If the action comes from a submit button, the form is submitted by calling
+ * {@link #submitData}. In the case of a reset button, the form is reset to
+ * the original state. If the action comes from a password or text field,
+ * then the input focus is transferred to the next input element in the form,
+ * unless this text/password field is the last one, in which case the form
+ * is submitted.
+ *
+ * @param ev the action event
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ Element el = getElement();
+ Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute);
+ if (tag.equals(HTML.Tag.INPUT))
+ {
+ AttributeSet atts = el.getAttributes();
+ String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+ if (type.equals("submit"))
+ submitData(getFormData());
+ else if (type.equals("reset"))
+ resetForm();
+ }
+ // FIXME: Implement the remaining actions.
+ }
+
+ /**
+ * Submits the form data. A separate thread is created to do the
+ * transmission.
+ *
+ * @param data the form data
+ */
+ protected void submitData(String data)
+ {
+ SubmitThread submitThread = new SubmitThread(data);
+ submitThread.start();
+ }
+
+ /**
+ * Submits the form data in response to a click on a
+ * <code>&lt;input type=&quot;image&quot;&gt;</code> element.
+ *
+ * @param imageData the mouse click coordinates
+ */
+ protected void imageSubmit(String imageData)
+ {
+ // FIXME: Implement this.
+ }
+
+ /**
+ * Determines the image data that should be submitted in response to a
+ * mouse click on a image. This is either 'x=<p.x>&y=<p.y>' if the name
+ * attribute of the element is null or '' or
+ * <name>.x=<p.x>&<name>.y=<p.y>' when the name attribute is not empty.
+ *
+ * @param p the coordinates of the mouseclick
+ */
+ String getImageData(Point p)
+ {
+ String name = (String) getElement().getAttributes()
+ .getAttribute(HTML.Attribute.NAME);
+ String data;
+ if (name == null || name.equals(""))
+ {
+ data = "x=" + p.x + "&y=" + p.y;
+ }
+ else
+ {
+ data = name + ".x=" + p.x + "&" + name + ".y=" + p.y;
+ }
+ return data;
+ }
+
+ /**
+ * Determines and returns the enclosing form element if there is any.
+ *
+ * This is package private to avoid accessor methods.
+ *
+ * @return the enclosing form element, or <code>null</code> if there is no
+ * enclosing form element
+ */
+ Element getFormElement()
+ {
+ Element form = null;
+ Element el = getElement();
+ while (el != null && form == null)
+ {
+ AttributeSet atts = el.getAttributes();
+ if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FORM)
+ form = el;
+ else
+ el = el.getParentElement();
+ }
+ return form;
+ }
+
+ /**
+ * Determines the form data that is about to be submitted.
+ *
+ * @return the form data
+ */
+ private String getFormData()
+ {
+ Element form = getFormElement();
+ StringBuilder b = new StringBuilder();
+ if (form != null)
+ {
+ ElementIterator i = new ElementIterator(form);
+ Element next;
+ while ((next = i.next()) != null)
+ {
+ if (next.isLeaf())
+ {
+ AttributeSet atts = next.getAttributes();
+ String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+ if (type != null && type.equals("submit")
+ && next != getElement())
+ {
+ // Skip this. This is not the actual submit trigger.
+ }
+ else if (type == null || ! type.equals("image"))
+ {
+ getElementFormData(next, b);
+ }
+ }
+ }
+ }
+ return b.toString();
+ }
+
+ /**
+ * Fetches the form data from the specified element and appends it to
+ * the data string.
+ *
+ * @param el the element from which to fetch form data
+ * @param b the data string
+ */
+ private void getElementFormData(Element el, StringBuilder b)
+ {
+ AttributeSet atts = el.getAttributes();
+ String name = (String) atts.getAttribute(HTML.Attribute.NAME);
+ if (name != null)
+ {
+ String value = null;
+ HTML.Tag tag = (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
+ if (tag == HTML.Tag.SELECT)
+ {
+ getSelectData(atts, b);
+ }
+ else
+ {
+ if (tag == HTML.Tag.INPUT)
+ value = getInputFormData(atts);
+ else if (tag == HTML.Tag.TEXTAREA)
+ value = getTextAreaData(atts);
+ if (name != null && value != null)
+ {
+ addData(b, name, value);
+ }
+ }
+ }
+ }
+
+ /**
+ * Fetches form data from select boxes.
+ *
+ * @param atts the attributes of the element
+ *
+ * @param b the form data string to append to
+ */
+ private void getSelectData(AttributeSet atts, StringBuilder b)
+ {
+ String name = (String) atts.getAttribute(HTML.Attribute.NAME);
+ if (name != null)
+ {
+ Object m = atts.getAttribute(StyleConstants.ModelAttribute);
+ if (m instanceof SelectListModel)
+ {
+ SelectListModel sl = (SelectListModel) m;
+ ListSelectionModel lsm = sl.getSelectionModel();
+ for (int i = 0; i < sl.getSize(); i++)
+ {
+ if (lsm.isSelectedIndex(i))
+ {
+ Option o = (Option) sl.getElementAt(i);
+ addData(b, name, o.getValue());
+ }
+ }
+ }
+ else if (m instanceof SelectComboBoxModel)
+ {
+ SelectComboBoxModel scb = (SelectComboBoxModel) m;
+ Option o = (Option) scb.getSelectedItem();
+ if (o != null)
+ addData(b, name, o.getValue());
+ }
+ }
+ }
+
+ /**
+ * Fetches form data from a textarea.
+ *
+ * @param atts the attributes
+ *
+ * @return the form data
+ */
+ private String getTextAreaData(AttributeSet atts)
+ {
+ Document doc = (Document) atts.getAttribute(StyleConstants.ModelAttribute);
+ String data;
+ try
+ {
+ data = doc.getText(0, doc.getLength());
+ }
+ catch (BadLocationException ex)
+ {
+ data = null;
+ }
+ return data;
+ }
+
+ /**
+ * Fetches form data from an input tag.
+ *
+ * @param atts the attributes from which to fetch the data
+ *
+ * @return the field value
+ */
+ private String getInputFormData(AttributeSet atts)
+ {
+ String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+ Object model = atts.getAttribute(StyleConstants.ModelAttribute);
+ String value = null;
+ if (type.equals("text") || type.equals("password"))
+ {
+ Document doc = (Document) model;
+ try
+ {
+ value = doc.getText(0, doc.getLength());
+ }
+ catch (BadLocationException ex)
+ {
+ // Sigh.
+ assert false;
+ }
+ }
+ else if (type.equals("hidden") || type.equals("submit"))
+ {
+ value = (String) atts.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = "";
+ }
+ // TODO: Implement the others. radio, checkbox and file.
+ return value;
+ }
+
+ /**
+ * Actually adds the specified data to the string. It URL encodes
+ * the name and value and handles separation of the fields.
+ *
+ * @param b the string at which the form data to be added
+ * @param name the name of the field
+ * @param value the value
+ */
+ private void addData(StringBuilder b, String name, String value)
+ {
+ if (b.length() > 0)
+ b.append('&');
+ String encName = URLEncoder.encode(name);
+ b.append(encName);
+ b.append('=');
+ String encValue = URLEncoder.encode(value);
+ b.append(encValue);
+ }
+
+ /**
+ * Resets the form data to their initial state.
+ */
+ private void resetForm()
+ {
+ Element form = getFormElement();
+ if (form != null)
+ {
+ ElementIterator iter = new ElementIterator(form);
+ Element next;
+ while ((next = iter.next()) != null)
+ {
+ if (next.isLeaf())
+ {
+ AttributeSet atts = next.getAttributes();
+ Object m = atts.getAttribute(StyleConstants.ModelAttribute);
+ if (m instanceof ResetableModel)
+ ((ResetableModel) m).reset();
+ }
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/FrameSetView.java b/libjava/classpath/javax/swing/text/html/FrameSetView.java
new file mode 100644
index 000000000..e3252d79c
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/FrameSetView.java
@@ -0,0 +1,274 @@
+/* FrameSetView.java -- Implements HTML frameset
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.util.StringTokenizer;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BoxView;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * Implements HTML framesets. This is implemented as a vertical box that
+ * holds the rows of the frameset. Each row is again a horizontal box that
+ * holds the actual columns.
+ */
+public class FrameSetView
+ extends BoxView
+{
+
+ /**
+ * A row of a frameset.
+ */
+ private class FrameSetRow
+ extends BoxView
+ {
+ private int row;
+ FrameSetRow(Element el, int r)
+ {
+ super(el, X_AXIS);
+ row = r;
+ }
+
+ protected void loadChildren(ViewFactory f)
+ {
+ // Load the columns here.
+ Element el = getElement();
+ View[] columns = new View[numViews[X_AXIS]];
+ int offset = row * numViews[X_AXIS];
+ for (int c = 0; c < numViews[X_AXIS]; c++)
+ {
+ Element child = el.getElement(offset + c);
+ columns[c] = f.create(child);
+ }
+ replace(0, 0, columns);
+ }
+
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ int numRows = numViews[X_AXIS];
+ int[] abs = absolute[X_AXIS];
+ int[] rel = relative[X_AXIS];
+ int[] perc = percent[X_AXIS];
+ layoutViews(targetSpan, axis, offsets, spans, numRows, abs, rel, perc);
+ }
+ }
+
+ /**
+ * Holds the absolute layout information for the views along one axis. The
+ * indices are absolute[axis][index], where axis is either X_AXIS (columns)
+ * or Y_AXIS (rows). Rows or columns that don't have absolute layout have
+ * a -1 in this array.
+ */
+ int[][] absolute;
+
+ /**
+ * Holds the relative (*) layout information for the views along one axis.
+ * The indices are relative[axis][index], where axis is either X_AXIS
+ * (columns) or Y_AXIS (rows). Rows or columns that don't have relative
+ * layout have a Float.NaN in this array.
+ */
+ int[][] relative;
+
+ /**
+ * Holds the relative (%) layout information for the views along one axis.
+ * The indices are relative[axis][index], where axis is either X_AXIS
+ * (columns) or Y_AXIS (rows). Rows or columns that don't have relative
+ * layout have a Float.NaN in this array.
+ *
+ * The percentage is divided by 100 so that we hold the actual fraction here.
+ */
+ int[][] percent;
+
+ /**
+ * The number of children in each direction.
+ */
+ int[] numViews;
+
+ FrameSetView(Element el)
+ {
+ super(el, Y_AXIS);
+ numViews = new int[2];
+ absolute = new int[2][];
+ relative = new int[2][];
+ percent = new int[2][];
+ }
+
+ /**
+ * Loads the children and places them inside the grid.
+ */
+ protected void loadChildren(ViewFactory f)
+ {
+ parseRowsCols();
+ // Set up the rows.
+ View[] rows = new View[numViews[Y_AXIS]];
+ for (int r = 0; r < numViews[Y_AXIS]; r++)
+ {
+ rows[r] = new FrameSetRow(getElement(), r);
+ }
+ replace(0, 0, rows);
+ }
+
+ /**
+ * Parses the rows and cols attributes and sets up the layout info.
+ */
+ private void parseRowsCols()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ String cols = (String) atts.getAttribute(HTML.Attribute.COLS);
+ if (cols == null) // Defaults to '100%' when not specified.
+ cols = "100%";
+ parseLayout(cols, X_AXIS);
+ String rows = (String) atts.getAttribute(HTML.Attribute.ROWS);
+ if (rows == null) // Defaults to '100%' when not specified.
+ rows = "100%";
+ parseLayout(rows, Y_AXIS);
+ }
+
+ /**
+ * Parses the cols or rows attribute and places the layout info in the
+ * appropriate arrays.
+ *
+ * @param att the attributes to parse
+ * @param axis the axis
+ */
+ private void parseLayout(String att, int axis)
+ {
+ StringTokenizer tokens = new StringTokenizer(att, ",");
+ numViews[axis] = tokens.countTokens();
+ absolute[axis] = new int[numViews[axis]];
+ relative[axis] = new int[numViews[axis]];
+ percent[axis] = new int[numViews[axis]];
+ for (int index = 0; tokens.hasMoreTokens(); index++)
+ {
+ String token = tokens.nextToken();
+ int p = token.indexOf('%');
+ int s = token.indexOf('*');
+ if (p != -1)
+ {
+ // Percent value.
+ String number = token.substring(0, p);
+ try
+ {
+ percent[axis][index] = Integer.parseInt(number);
+ }
+ catch (NumberFormatException ex)
+ {
+ // Leave value as 0 then.
+ }
+ }
+ else if (s != -1)
+ {
+ // Star relative value.
+ String number = token.substring(0, s);
+ try
+ {
+ relative[axis][index] = Integer.parseInt(number);
+ }
+ catch (NumberFormatException ex)
+ {
+ // Leave value as 0 then.
+ }
+ }
+ else
+ {
+ // Absolute value.
+ try
+ {
+ absolute[axis][index] = Integer.parseInt(token);
+ }
+ catch (NumberFormatException ex)
+ {
+ // Leave value as 0 then.
+ }
+ }
+ }
+ }
+
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ int numRows = numViews[Y_AXIS];
+ int[] abs = absolute[Y_AXIS];
+ int[] rel = relative[Y_AXIS];
+ int[] perc = percent[Y_AXIS];
+ layoutViews(targetSpan, axis, offsets, spans, numRows, abs, rel, perc);
+ }
+
+ void layoutViews(int targetSpan, int axis, int[] offsets, int[] spans,
+ int numViews, int[] abs, int[] rel, int[] perc)
+ {
+ // We need two passes. In the first pass we layout the absolute and
+ // percent values and accumulate the needed space. In the second pass
+ // the relative values are distributed and the offsets are set.
+ int total = 0;
+ int relTotal = 0;
+ for (int i = 0; i < numViews; i++)
+ {
+ if (abs[i] > 0)
+ {
+ spans[i] = abs[i];
+ total += spans[i];
+ }
+ else if (perc[i] > 0)
+ {
+ spans[i] = (targetSpan * perc[i]) / 100;
+ total += spans[i];
+ }
+ else if (rel[i] > 0)
+ {
+ relTotal += rel[i];
+ }
+ }
+ int offs = 0;
+ for (int i = 0; i < numViews; i++)
+ {
+ if (relTotal > 0 && rel[i] > 0)
+ {
+ spans[i] = targetSpan * (rel[i] / relTotal);
+ }
+ offsets[i] = offs;
+ offs += spans[i];
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/FrameView.java b/libjava/classpath/javax/swing/text/html/FrameView.java
new file mode 100644
index 000000000..cd4e44a98
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/FrameView.java
@@ -0,0 +1,233 @@
+/* FrameView.java -- Renders HTML frame tags
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.awt.Component;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.JEditorPane;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.ComponentView;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+
+/**
+ * A view that is responsible for rendering HTML frame tags.
+ * This is accomplished by a specialized {@link ComponentView}
+ * that embeds a JEditorPane with an own document.
+ */
+class FrameView
+ extends ComponentView
+ implements HyperlinkListener
+{
+
+ /**
+ * Creates a new FrameView for the specified element.
+ *
+ * @param el the element for the view
+ */
+ FrameView(Element el)
+ {
+ super(el);
+ }
+
+ /**
+ * Creates the element that will be embedded in the view.
+ * This will be a JEditorPane with the appropriate content set.
+ *
+ * @return the element that will be embedded in the view
+ */
+ protected Component createComponent()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ JEditorPane html = new JEditorPane();
+ html.addHyperlinkListener(this);
+ URL base = ((HTMLDocument) el.getDocument()).getBase();
+ String srcAtt = (String) atts.getAttribute(HTML.Attribute.SRC);
+ if (srcAtt != null && ! srcAtt.equals(""))
+ {
+ try
+ {
+ URL page = new URL(base, srcAtt);
+ html.setPage(page);
+ ((HTMLDocument) html.getDocument()).setFrameDocument(true);
+ }
+ catch (MalformedURLException ex)
+ {
+ // Leave page empty.
+ }
+ catch (IOException ex)
+ {
+ // Leave page empty.
+ }
+ }
+ return html;
+ }
+
+ /**
+ * Catches hyperlink events on that frame's editor and forwards it to
+ * the outermost editorpane.
+ */
+ public void hyperlinkUpdate(HyperlinkEvent event)
+ {
+ JEditorPane outer = getTopEditorPane();
+ if (outer != null)
+ {
+ if (event instanceof HTMLFrameHyperlinkEvent)
+ {
+ HTMLFrameHyperlinkEvent hfhe = (HTMLFrameHyperlinkEvent) event;
+ if (hfhe.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
+ {
+ String target = hfhe.getTarget();
+ if (event instanceof FormSubmitEvent)
+ {
+ handleFormSubmitEvent(hfhe, outer, target);
+ }
+ else // No FormSubmitEvent.
+ {
+ handleHyperlinkEvent(hfhe, outer, target);
+ }
+ }
+ }
+ else
+ {
+ // Simply forward this event.
+ outer.fireHyperlinkUpdate(event);
+ }
+ }
+ }
+
+ /**
+ * Handles normal hyperlink events.
+ *
+ * @param event the event
+ * @param outer the top editor
+ * @param target the target
+ */
+ private void handleHyperlinkEvent(HyperlinkEvent event,
+ JEditorPane outer, String target)
+ {
+ if (target.equals("_top"))
+ {
+ try
+ {
+ outer.setPage(event.getURL());
+ }
+ catch (IOException ex)
+ {
+ // Well...
+ ex.printStackTrace();
+ }
+ }
+ if (! outer.isEditable())
+ {
+ outer.fireHyperlinkUpdate
+ (new HTMLFrameHyperlinkEvent(outer,
+ event.getEventType(),
+ event.getURL(),
+ event.getDescription(),
+ getElement(),
+ target));
+ }
+ }
+
+ /**
+ * Handles form submit events.
+ *
+ * @param event the event
+ * @param outer the top editor
+ * @param target the target
+ */
+ private void handleFormSubmitEvent(HTMLFrameHyperlinkEvent event,
+ JEditorPane outer,
+ String target)
+ {
+ HTMLEditorKit kit = (HTMLEditorKit) outer.getEditorKit();
+ if (kit != null && kit.isAutoFormSubmission())
+ {
+ if (target.equals("_top"))
+ {
+ try
+ {
+ outer.setPage(event.getURL());
+ }
+ catch (IOException ex)
+ {
+ // Well...
+ ex.printStackTrace();
+ }
+ }
+ else
+ {
+ HTMLDocument doc =
+ (HTMLDocument) outer.getDocument();
+ doc.processHTMLFrameHyperlinkEvent(event);
+ }
+ }
+ else
+ {
+ outer.fireHyperlinkUpdate(event);
+ }
+ }
+
+ /**
+ * Determines the topmost editor in a nested frameset.
+ *
+ * @return the topmost editor in a nested frameset
+ */
+ private JEditorPane getTopEditorPane()
+ {
+ View parent = getParent();
+ View top = null;
+ while (parent != null)
+ {
+ if (parent instanceof FrameSetView)
+ top = parent;
+ }
+ JEditorPane editor = null;
+ if (top != null)
+ editor = (JEditorPane) top.getContainer();
+ return editor;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HRuleView.java b/libjava/classpath/javax/swing/text/html/HRuleView.java
new file mode 100644
index 000000000..9be1efff3
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HRuleView.java
@@ -0,0 +1,189 @@
+/* HRuleView.java -- Horizontal dash in HTML documents.
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.text.Element;
+import javax.swing.text.View;
+
+/**
+ * Represents the long horizontal separating dash that can be inserted into the
+ * HTML documents with HR tag.
+ */
+class HRuleView extends InlineView
+{
+ /**
+ * The null view, indicating, that nothing should be painted ahead the
+ * breaking point.
+ */
+ View nullView;
+
+ /**
+ * The height of the horizontal dash area.
+ */
+ static int HEIGHT = 4;
+
+ /**
+ * The imaginary invisible view that stays after end of line after the
+ * breaking procedure. It occupies on character.
+ */
+ class Beginning extends NullView
+ {
+ /**
+ * The break offset that becomes the views start offset.
+ */
+ int breakOffset;
+
+ /**
+ * Return the end offset that is always one char after the break offset.
+ */
+ public int getEndOffset()
+ {
+ return breakOffset + 1;
+ }
+
+ /**
+ * Return the start offset that has been passed in a constructor.
+ */
+ public int getStartOffset()
+ {
+ return breakOffset;
+ }
+
+ /**
+ * Create the new instance of this view.
+ *
+ * @param element the element (inherited from the HR view)
+ * @param offset the position where the HR view has been broken
+ */
+ public Beginning(Element element, int offset)
+ {
+ super(element);
+ breakOffset = offset;
+ }
+ }
+
+ /**
+ * Creates the new HR view.
+ */
+ public HRuleView(Element element)
+ {
+ super(element);
+ }
+
+ /**
+ * Returns the ForcedBreakWeight for the vertical axis, indicating, the the
+ * view must be broken to be displayed correctly. The horizontal dash is
+ * not breakeable along the Y axis.
+ */
+ public int getBreakWeight(int axis, float pos, float len)
+ {
+ if (axis == X_AXIS && ((getEndOffset() - getStartOffset()) > 1))
+ return ForcedBreakWeight;
+ else
+ return BadBreakWeight;
+ }
+
+ /**
+ * Draws the double line, upped black and the lower light gray.
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ Rectangle bounds = a.getBounds();
+
+ int x = bounds.x;
+ int y = bounds.y;
+
+ int w = bounds.x + bounds.width;
+
+ // We move "half pixel up" from the actual horizontal position -
+ // this will be rounded to the closest actual int co-ordinate.
+ int h = bounds.y + (int) Math.round(bounds.height * 0.5 - 0.5);
+
+ g.setColor(Color.black);
+ g.drawLine(x, y++, w, h++);
+ g.setColor(Color.lightGray);
+ g.drawLine(x, y, w, h);
+ }
+
+ /**
+ * Break the view into this view and the invisible imaginary view that
+ * stays on the end of line that is broken by HR dash. The view is broken
+ * only if its length is longer than one (the two characters are expected
+ * in the initial length).
+ */
+ public View breakView(int axis, int offset, float pos, float len)
+ {
+ if (getEndOffset() - getStartOffset() > 1)
+ return new Beginning(getElement(), offset);
+ else
+ return this;
+ }
+
+ /**
+ * Returns the width of the container for the horizontal axis and the
+ * thickness of the dash area for the vertical axis.
+ */
+ public float getMaximumSpan(int axis)
+ {
+ if (axis == X_AXIS)
+ {
+ Component container = getContainer();
+ if (container != null)
+ return getContainer().getWidth();
+ else
+ return 640;
+ }
+ else
+ return HEIGHT;
+ }
+
+ /**
+ * Returns the same values as {@link #getMaximumSpan(int)}
+ */
+ public float getPreferredSpan(int axis)
+ {
+ return getMaximumSpan(axis);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTML.java b/libjava/classpath/javax/swing/text/html/HTML.java
new file mode 100644
index 000000000..93c05daa2
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTML.java
@@ -0,0 +1,1253 @@
+/* HTML.java -- HTML document tag constants
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.io.Serializable;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.swing.text.AttributeSet;
+
+/**
+ * HTML attribute and tag definitions.
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+public class HTML
+{
+ /**
+ * Represents a HTML attribute.
+ */
+ public static final class Attribute
+ {
+ /**
+ * The action attribute
+ */
+ public static final Attribute ACTION = new Attribute("action");
+
+ /**
+ * The align attribute
+ */
+ public static final Attribute ALIGN = new Attribute("align");
+
+ /**
+ * The alink attribute
+ */
+ public static final Attribute ALINK = new Attribute("alink");
+
+ /**
+ * The alt attribute
+ */
+ public static final Attribute ALT = new Attribute("alt");
+
+ /**
+ * The archive attribute
+ */
+ public static final Attribute ARCHIVE = new Attribute("archive");
+
+ /**
+ * The background attribute
+ */
+ public static final Attribute BACKGROUND = new Attribute("background");
+
+ /**
+ * The bgcolor attribute
+ */
+ public static final Attribute BGCOLOR = new Attribute("bgcolor");
+
+ /**
+ * The border attribute
+ */
+ public static final Attribute BORDER = new Attribute("border");
+
+ /**
+ * The cellpadding attribute
+ */
+ public static final Attribute CELLPADDING = new Attribute("cellpadding");
+
+ /**
+ * The cellspacing attribute
+ */
+ public static final Attribute CELLSPACING = new Attribute("cellspacing");
+
+ /**
+ * The checked attribute
+ */
+ public static final Attribute CHECKED = new Attribute("checked");
+
+ /**
+ * The class attribute
+ */
+ public static final Attribute CLASS = new Attribute("class");
+
+ /**
+ * The classid attribute
+ */
+ public static final Attribute CLASSID = new Attribute("classid");
+
+ /**
+ * The clear attribute
+ */
+ public static final Attribute CLEAR = new Attribute("clear");
+
+ /**
+ * The code attribute
+ */
+ public static final Attribute CODE = new Attribute("code");
+
+ /**
+ * The codebase attribute
+ */
+ public static final Attribute CODEBASE = new Attribute("codebase");
+
+ /**
+ * The codetype attribute
+ */
+ public static final Attribute CODETYPE = new Attribute("codetype");
+
+ /**
+ * The color attribute
+ */
+ public static final Attribute COLOR = new Attribute("color");
+
+ /**
+ * The cols attribute
+ */
+ public static final Attribute COLS = new Attribute("cols");
+
+ /**
+ * The colspan attribute
+ */
+ public static final Attribute COLSPAN = new Attribute("colspan");
+
+ /**
+ * The comment attribute
+ */
+ public static final Attribute COMMENT = new Attribute("comment");
+
+ /**
+ * The compact attribute
+ */
+ public static final Attribute COMPACT = new Attribute("compact");
+
+ /**
+ * The content attribute
+ */
+ public static final Attribute CONTENT = new Attribute("content");
+
+ /**
+ * The coords attribute
+ */
+ public static final Attribute COORDS = new Attribute("coords");
+
+ /**
+ * The data attribute
+ */
+ public static final Attribute DATA = new Attribute("data");
+
+ /**
+ * The declare attribute
+ */
+ public static final Attribute DECLARE = new Attribute("declare");
+
+ /**
+ * The dir attribute
+ */
+ public static final Attribute DIR = new Attribute("dir");
+
+ /**
+ * The dummy attribute
+ */
+ public static final Attribute DUMMY = new Attribute("dummy");
+
+ /**
+ * The enctype attribute
+ */
+ public static final Attribute ENCTYPE = new Attribute("enctype");
+
+ /**
+ * The endtag attribute
+ */
+ public static final Attribute ENDTAG = new Attribute("endtag");
+
+ /**
+ * The face attribute
+ */
+ public static final Attribute FACE = new Attribute("face");
+
+ /**
+ * The frameborder attribute
+ */
+ public static final Attribute FRAMEBORDER = new Attribute("frameborder");
+
+ /**
+ * The halign attribute
+ */
+ public static final Attribute HALIGN = new Attribute("halign");
+
+ /**
+ * The height attribute
+ */
+ public static final Attribute HEIGHT = new Attribute("height");
+
+ /**
+ * The href attribute
+ */
+ public static final Attribute HREF = new Attribute("href");
+
+ /**
+ * The hspace attribute
+ */
+ public static final Attribute HSPACE = new Attribute("hspace");
+
+ /**
+ * The http-equiv attribute
+ */
+ public static final Attribute HTTPEQUIV = new Attribute("http-equiv");
+
+ /**
+ * The id attribute
+ */
+ public static final Attribute ID = new Attribute("id");
+
+ /**
+ * The ismap attribute
+ */
+ public static final Attribute ISMAP = new Attribute("ismap");
+
+ /**
+ * The lang attribute
+ */
+ public static final Attribute LANG = new Attribute("lang");
+
+ /**
+ * The language attribute
+ */
+ public static final Attribute LANGUAGE = new Attribute("language");
+
+ /**
+ * The link attribute
+ */
+ public static final Attribute LINK = new Attribute("link");
+
+ /**
+ * The lowsrc attribute
+ */
+ public static final Attribute LOWSRC = new Attribute("lowsrc");
+
+ /**
+ * The marginheight attribute
+ */
+ public static final Attribute MARGINHEIGHT = new Attribute("marginheight");
+
+ /**
+ * The marginwidth attribute
+ */
+ public static final Attribute MARGINWIDTH = new Attribute("marginwidth");
+
+ /**
+ * The maxlength attribute
+ */
+ public static final Attribute MAXLENGTH = new Attribute("maxlength");
+
+ /**
+ * The media attribute
+ */
+ static final Attribute MEDIA = new Attribute("media");
+
+ /**
+ * The method attribute
+ */
+ public static final Attribute METHOD = new Attribute("method");
+
+ /**
+ * The multiple attribute
+ */
+ public static final Attribute MULTIPLE = new Attribute("multiple");
+
+ /**
+ * The n attribute
+ */
+ public static final Attribute N = new Attribute("n");
+
+ /**
+ * The name attribute
+ */
+ public static final Attribute NAME = new Attribute("name");
+
+ /**
+ * The nohref attribute
+ */
+ public static final Attribute NOHREF = new Attribute("nohref");
+
+ /**
+ * The noresize attribute
+ */
+ public static final Attribute NORESIZE = new Attribute("noresize");
+
+ /**
+ * The noshade attribute
+ */
+ public static final Attribute NOSHADE = new Attribute("noshade");
+
+ /**
+ * The nowrap attribute
+ */
+ public static final Attribute NOWRAP = new Attribute("nowrap");
+
+ /**
+ * The prompt attribute
+ */
+ public static final Attribute PROMPT = new Attribute("prompt");
+
+ /**
+ * The rel attribute
+ */
+ public static final Attribute REL = new Attribute("rel");
+
+ /**
+ * The rev attribute
+ */
+ public static final Attribute REV = new Attribute("rev");
+
+ /**
+ * The rows attribute
+ */
+ public static final Attribute ROWS = new Attribute("rows");
+
+ /**
+ * The rowspan attribute
+ */
+ public static final Attribute ROWSPAN = new Attribute("rowspan");
+
+ /**
+ * The scrolling attribute
+ */
+ public static final Attribute SCROLLING = new Attribute("scrolling");
+
+ /**
+ * The selected attribute
+ */
+ public static final Attribute SELECTED = new Attribute("selected");
+
+ /**
+ * The shape attribute
+ */
+ public static final Attribute SHAPE = new Attribute("shape");
+
+ /**
+ * The shapes attribute
+ */
+ public static final Attribute SHAPES = new Attribute("shapes");
+
+ /**
+ * The size attribute
+ */
+ public static final Attribute SIZE = new Attribute("size");
+
+ /**
+ * The src attribute
+ */
+ public static final Attribute SRC = new Attribute("src");
+
+ /**
+ * The standby attribute
+ */
+ public static final Attribute STANDBY = new Attribute("standby");
+
+ /**
+ * The start attribute
+ */
+ public static final Attribute START = new Attribute("start");
+
+ /**
+ * The style attribute
+ */
+ public static final Attribute STYLE = new Attribute("style");
+
+ /**
+ * The target attribute
+ */
+ public static final Attribute TARGET = new Attribute("target");
+
+ /**
+ * The text attribute
+ */
+ public static final Attribute TEXT = new Attribute("text");
+
+ /**
+ * The title attribute
+ */
+ public static final Attribute TITLE = new Attribute("title");
+
+ /**
+ * The type attribute
+ */
+ public static final Attribute TYPE = new Attribute("type");
+
+ /**
+ * The usemap attribute
+ */
+ public static final Attribute USEMAP = new Attribute("usemap");
+
+ /**
+ * The valign attribute
+ */
+ public static final Attribute VALIGN = new Attribute("valign");
+
+ /**
+ * The value attribute
+ */
+ public static final Attribute VALUE = new Attribute("value");
+
+ /**
+ * The valuetype attribute
+ */
+ public static final Attribute VALUETYPE = new Attribute("valuetype");
+
+ /**
+ * The version attribute
+ */
+ public static final Attribute VERSION = new Attribute("version");
+
+ /**
+ * The vlink attribute
+ */
+ public static final Attribute VLINK = new Attribute("vlink");
+
+ /**
+ * The vspace attribute
+ */
+ public static final Attribute VSPACE = new Attribute("vspace");
+
+ /**
+ * The width attribute
+ */
+ public static final Attribute WIDTH = new Attribute("width");
+
+ /**
+ * This is used to reflect the pseudo class for the a tag.
+ */
+ static final Attribute PSEUDO_CLASS = new Attribute("_pseudo");
+
+ /**
+ * This is used to reflect the dynamic class for the a tag.
+ */
+ static final Attribute DYNAMIC_CLASS = new Attribute("_dynamic");
+
+ /**
+ * The attribute name.
+ */
+ private final String name;
+
+ /**
+ * Creates the attribute with the given name.
+ */
+ private Attribute(String a_name)
+ {
+ name = a_name;
+ }
+
+ /**
+ * Returns the attribute name. The names of the built-in attributes
+ * are always returned in lowercase.
+ */
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Return an array of all attributes, declared in the HTML.Attribute
+ * class. WARNING: attributes are the only public fields,
+ * expected in this class.
+ */
+ static Attribute[] getAllAttributes()
+ {
+ Field[] f = Attribute.class.getFields();
+ Attribute[] attrs = new Attribute[ f.length ];
+ Field x;
+ int p = 0;
+ Attribute a;
+
+ for (int i = 0; i < f.length; i++)
+ {
+ x = f [ i ];
+
+ if ((x.getModifiers() & Modifier.STATIC) != 0)
+ {
+ if (x.getType().equals(Attribute.class))
+ {
+ try
+ {
+ a = (Attribute) x.get(null);
+ attrs [ p++ ] = a;
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace(System.err);
+ throw new Error("This should never happen, report a bug");
+ }
+ }
+ }
+ }
+
+ return attrs;
+ }
+ }
+
+ /**
+ * Represents a HTML tag.
+ */
+ public static class Tag
+ {
+ /**
+ * The &lt;a&gt; tag
+ */
+ public static final Tag A = new Tag("a");
+
+ /**
+ * The &lt;address&gt; tag
+ */
+ public static final Tag ADDRESS = new Tag("address");
+
+ /**
+ * The &lt;applet&gt; tag
+ */
+ public static final Tag APPLET = new Tag("applet");
+
+ /**
+ * The &lt;area&gt; tag
+ */
+ public static final Tag AREA = new Tag("area");
+
+ /**
+ * The &lt;b&gt; tag
+ */
+ public static final Tag B = new Tag("b");
+
+ /**
+ * The &lt;base&gt; tag
+ */
+ public static final Tag BASE = new Tag("base");
+
+ /**
+ * The &lt;basefont&gt; tag
+ */
+ public static final Tag BASEFONT = new Tag("basefont");
+
+ /**
+ * The &lt;big&gt; tag
+ */
+ public static final Tag BIG = new Tag("big");
+
+ /**
+ * The &lt;blockquote&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag BLOCKQUOTE = new Tag("blockquote", BREAKS | BLOCK);
+
+ /**
+ * The &lt;body&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag BODY = new Tag("body", BREAKS | BLOCK);
+
+ /**
+ * The &lt;br&gt; tag , breaks flow.
+ */
+ public static final Tag BR = new Tag("br", BREAKS);
+
+ /**
+ * The &lt;caption&gt; tag
+ */
+ public static final Tag CAPTION = new Tag("caption");
+
+ /**
+ * The &lt;center&gt; tag , breaks flow.
+ */
+ public static final Tag CENTER = new Tag("center", BREAKS);
+
+ /**
+ * The &lt;cite&gt; tag
+ */
+ public static final Tag CITE = new Tag("cite");
+
+ /**
+ * The &lt;code&gt; tag
+ */
+ public static final Tag CODE = new Tag("code");
+
+ /**
+ * The &lt;dd&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag DD = new Tag("dd", BREAKS | BLOCK);
+
+ /**
+ * The &lt;dfn&gt; tag
+ */
+ public static final Tag DFN = new Tag("dfn");
+
+ /**
+ * The &lt;dir&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag DIR = new Tag("dir", BREAKS | BLOCK);
+
+ /**
+ * The &lt;div&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag DIV = new Tag("div", BREAKS | BLOCK);
+
+ /**
+ * The &lt;dl&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag DL = new Tag("dl", BREAKS | BLOCK);
+
+ /**
+ * The &lt;dt&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag DT = new Tag("dt", BREAKS | BLOCK);
+
+ /**
+ * The &lt;em&gt; tag
+ */
+ public static final Tag EM = new Tag("em");
+
+ /**
+ * The &lt;font&gt; tag
+ */
+ public static final Tag FONT = new Tag("font");
+
+ /**
+ * The &lt;form&gt; tag , breaks flow.
+ */
+ public static final Tag FORM = new Tag("form", BREAKS);
+
+ /**
+ * The &lt;frame&gt; tag
+ */
+ public static final Tag FRAME = new Tag("frame");
+
+ /**
+ * The &lt;frameset&gt; tag
+ */
+ public static final Tag FRAMESET = new Tag("frameset");
+
+ /**
+ * The &lt;h1&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag H1 = new Tag("h1", BREAKS | BLOCK);
+
+ /**
+ * The &lt;h2&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag H2 = new Tag("h2", BREAKS | BLOCK);
+
+ /**
+ * The &lt;h3&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag H3 = new Tag("h3", BREAKS | BLOCK);
+
+ /**
+ * The &lt;h4&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag H4 = new Tag("h4", BREAKS | BLOCK);
+
+ /**
+ * The &lt;h5&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag H5 = new Tag("h5", BREAKS | BLOCK);
+
+ /**
+ * The &lt;h6&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag H6 = new Tag("h6", BREAKS | BLOCK);
+
+ /**
+ * The &lt;head&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag HEAD = new Tag("head", BREAKS | BLOCK);
+
+ /**
+ * The &lt;hr&gt; tag , breaks flow.
+ */
+ public static final Tag HR = new Tag("hr", BREAKS);
+
+ /**
+ * The &lt;html&gt; tag , breaks flow.
+ */
+ public static final Tag HTML = new Tag("html", BREAKS);
+
+ /**
+ * The &lt;i&gt; tag
+ */
+ public static final Tag I = new Tag("i");
+
+ /**
+ * The &lt;img&gt; tag
+ */
+ public static final Tag IMG = new Tag("img");
+
+ /**
+ * The &lt;input&gt; tag
+ */
+ public static final Tag INPUT = new Tag("input");
+
+ /**
+ * The &lt;isindex&gt; tag , breaks flow.
+ */
+ public static final Tag ISINDEX = new Tag("isindex", BREAKS);
+
+ /**
+ * The &lt;kbd&gt; tag
+ */
+ public static final Tag KBD = new Tag("kbd");
+
+ /**
+ * The &lt;li&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag LI = new Tag("li", BREAKS | BLOCK);
+
+ /**
+ * The &lt;link&gt; tag
+ */
+ public static final Tag LINK = new Tag("link");
+
+ /**
+ * The &lt;map&gt; tag
+ */
+ public static final Tag MAP = new Tag("map");
+
+ /**
+ * The &lt;menu&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag MENU = new Tag("menu", BREAKS | BLOCK);
+
+ /**
+ * The &lt;meta&gt; tag
+ */
+ public static final Tag META = new Tag("meta");
+
+ /**
+ * The &lt;nobr&gt; tag
+ */
+ static final Tag NOBR = new Tag("nobr");
+
+ /**
+ * The &lt;noframes&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag NOFRAMES = new Tag("noframes", BREAKS | BLOCK);
+
+ /**
+ * The &lt;object&gt; tag
+ */
+ public static final Tag OBJECT = new Tag("object");
+
+ /**
+ * The &lt;ol&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag OL = new Tag("ol", BREAKS | BLOCK);
+
+ /**
+ * The &lt;option&gt; tag
+ */
+ public static final Tag OPTION = new Tag("option");
+
+ /**
+ * The &lt;p&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag P = new Tag("p", BREAKS | BLOCK);
+
+ /**
+ * The &lt;param&gt; tag
+ */
+ public static final Tag PARAM = new Tag("param");
+
+ /**
+ * The &lt;pre&gt; tag , breaks flow, block tag, preformatted.
+ */
+ public static final Tag PRE = new Tag("pre", BREAKS | BLOCK | PREFORMATTED);
+
+ /**
+ * The &lt;s&gt; tag
+ */
+ public static final Tag S = new Tag("s");
+
+ /**
+ * The &lt;samp&gt; tag
+ */
+ public static final Tag SAMP = new Tag("samp");
+
+ /**
+ * The &lt;script&gt; tag
+ */
+ public static final Tag SCRIPT = new Tag("script");
+
+ /**
+ * The &lt;select&gt; tag
+ */
+ public static final Tag SELECT = new Tag("select");
+
+ /**
+ * The &lt;small&gt; tag
+ */
+ public static final Tag SMALL = new Tag("small");
+
+ /**
+ * The &lt;span&gt; tag
+ */
+ public static final Tag SPAN = new Tag("span");
+
+ /**
+ * The &lt;strike&gt; tag
+ */
+ public static final Tag STRIKE = new Tag("strike");
+
+ /**
+ * The &lt;strong&gt; tag
+ */
+ public static final Tag STRONG = new Tag("strong");
+
+ /**
+ * The &lt;style&gt; tag
+ */
+ public static final Tag STYLE = new Tag("style");
+
+ /**
+ * The &lt;sub&gt; tag
+ */
+ public static final Tag SUB = new Tag("sub");
+
+ /**
+ * The &lt;sup&gt; tag
+ */
+ public static final Tag SUP = new Tag("sup");
+
+ /**
+ * The &lt;table&gt; tag , block tag.
+ */
+ public static final Tag TABLE = new Tag("table", BLOCK);
+
+ /**
+ * The &lt;td&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag TD = new Tag("td", BREAKS | BLOCK);
+
+ /**
+ * The &lt;textarea&gt; tag , preformatted.
+ */
+ public static final Tag TEXTAREA = new Tag("textarea", PREFORMATTED);
+
+ /**
+ * The &lt;th&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag TH = new Tag("th", BREAKS | BLOCK);
+
+ /**
+ * The &lt;title&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag TITLE = new Tag("title", BREAKS | BLOCK);
+
+ /**
+ * The &lt;tr&gt; tag , block tag.
+ */
+ public static final Tag TR = new Tag("tr", BLOCK);
+
+ /**
+ * The &lt;tt&gt; tag
+ */
+ public static final Tag TT = new Tag("tt");
+
+ /**
+ * The &lt;u&gt; tag
+ */
+ public static final Tag U = new Tag("u");
+
+ /**
+ * The &lt;ul&gt; tag , breaks flow, block tag.
+ */
+ public static final Tag UL = new Tag("ul", BREAKS | BLOCK);
+
+ /**
+ * The &lt;var&gt; tag
+ */
+ public static final Tag VAR = new Tag("var");
+
+ /* Special tags */
+
+ /**
+ * Total number of syntetic tags, delared in the Tag class.
+ * This must be adjusted if the new synthetic tags are declared.
+ * Otherwise the HTML.getAllTags() will not work as expected.
+ */
+ private static final int TOTAL_SYNTHETIC_TAGS = 3;
+
+ /**
+ * All comments are labeled with this tag.
+ * This tag is not included into the array, returned by getAllTags().
+ * toString() returns 'comment'. HTML reader synthesizes this tag.
+ */
+ public static final Tag COMMENT = new Tag("comment", SYNTHETIC);
+
+ /**
+ * All text content is labeled with this tag.
+ * This tag is not included into the array, returned by getAllTags().
+ * toString() returns 'content'. HTML reader synthesizes this tag.
+ */
+ public static final Tag CONTENT = new Tag("content", SYNTHETIC);
+
+ /**
+ * All text content must be in a paragraph element.
+ * If a paragraph didn't exist when content was encountered,
+ * a paragraph is manufactured.
+ * toString() returns 'p-implied'. HTML reader synthesizes this tag.
+ */
+ public static final Tag IMPLIED = new Tag("p-implied", SYNTHETIC);
+ final String name;
+ final int flags;
+
+ /**
+ * Create the unitialised instance of HTML.Tag.
+ *
+ * The {@link #breaksFlow()}, {@link #isBlock()}
+ * and {@link #isPreformatted()} will always return false.
+ * The {@link #toString()} will return <code>null</code>.
+ *
+ * @since 1.3
+ */
+ public Tag()
+ {
+ name = null;
+ flags = 0;
+ }
+
+ /**
+ * Creates a new Tag with the specified id, and with causesBreak
+ * and isBlock set to false.
+ */
+ protected Tag(String id)
+ {
+ name = id;
+ flags = 0;
+ }
+
+ /**
+ * Creates a new Tag with the specified tag name and
+ * causesBreak and isBlock properties.
+ */
+ protected Tag(String id, boolean causesBreak, boolean isBlock)
+ {
+ int f = 0;
+
+ if (causesBreak)
+ {
+ f |= BREAKS;
+ }
+
+ if (isBlock)
+ {
+ f |= BLOCK;
+ }
+
+ flags = f;
+ name = id;
+ }
+
+ /**
+ * Create a tag taking flags.
+ */
+ Tag(String id, int a_flags)
+ {
+ name = id;
+ flags = a_flags;
+ }
+
+ /**
+ * Returns true if this tag is a block tag, which is a tag used to
+ * add structure to a document.
+ */
+ public boolean isBlock()
+ {
+ return (flags & BLOCK) != 0;
+ }
+
+ /**
+ * Returns true if this tag is pre-formatted, which is true if
+ * the tag is either PRE or TEXTAREA
+ */
+ public boolean isPreformatted()
+ {
+ return (flags & PREFORMATTED) != 0;
+ }
+
+ /**
+ * Returns true if this tag causes a line break to the flow of text
+ */
+ public boolean breaksFlow()
+ {
+ return (flags & BREAKS) != 0;
+ }
+
+ /**
+ * Returns the tag name. The names of the built-in tags are always
+ * returned in lowercase.
+ */
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Return an array of HTML tags, declared in HTML.Tag class.
+ * WARNING: This method expects that the Tags are the only
+ * public fields declared in the Tag class.
+ */
+ static Tag[] getAllTags()
+ {
+ Field[] f = Tag.class.getFields();
+ Field x;
+
+ // The syntetic tags are not included.
+ Tag[] tags = new Tag[ f.length - TOTAL_SYNTHETIC_TAGS ];
+ int p = 0;
+ Tag t;
+
+ for (int i = 0; i < f.length; i++)
+ {
+ x = f [ i ];
+
+ if ((x.getModifiers() & Modifier.STATIC) != 0)
+ {
+ if (x.getType().equals(Tag.class))
+ {
+ try
+ {
+ t = (Tag) x.get(null);
+
+ if (!t.isSyntetic())
+ {
+ tags [ p++ ] = t;
+ }
+ }
+ catch (IllegalAccessException ex)
+ {
+ unexpected(ex);
+ }
+ catch (IllegalArgumentException ex)
+ {
+ unexpected(ex);
+ }
+ }
+ }
+ }
+
+ return tags;
+ }
+
+ /**
+ * Returns true for tags, generated by the html reader
+ * (COMMENT, CONTENT and IMPLIED).
+ */
+ boolean isSyntetic()
+ {
+ return (flags & SYNTHETIC) != 0;
+ }
+
+ private static void unexpected(Exception ex)
+ throws Error
+ {
+ throw new Error("This should never happen, report a bug", ex);
+ }
+ }
+
+ /**
+ * Represents an unknown HTML tag.
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+ public static class UnknownTag
+ extends Tag
+ implements Serializable
+ {
+ private static final long serialVersionUID = -1534369342247250625L;
+
+ /**
+ * Creates a new UnknownTag with the specified name
+ * @param name The tag name.
+ *
+ */
+ public UnknownTag(String name)
+ {
+ super(name);
+ }
+ }
+
+ /**
+ * This value is returned for attributes without value that have no
+ * default value defined in the DTD.
+ */
+ public static final String NULL_ATTRIBUTE_VALUE = "#DEFAULT";
+
+ /* Package level html tag flags */
+ static final int BREAKS = 1;
+ static final int BLOCK = 2;
+ static final int PREFORMATTED = 4;
+ static final int SYNTHETIC = 8;
+ private static Map<String,Tag> tagMap;
+ private static Map<String,Attribute> attrMap;
+
+ /**
+ * The public constructor (does nothing). It it seldom required to have
+ * an instance of this class, because all public fields and methods
+ * are static.
+ */
+ public HTML()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the set of the recognized HTML attributes.
+ */
+ public static HTML.Attribute[] getAllAttributeKeys()
+ {
+ return Attribute.getAllAttributes();
+ }
+
+ /**
+ * Returns the set of actual HTML tags that are recognized by
+ * the default HTML reader. The returned array does not include the
+ * COMMENT, CONTENT and IMPLIED tags.
+ */
+ public static HTML.Tag[] getAllTags()
+ {
+ return Tag.getAllTags();
+ }
+
+ /**
+ * Returns an htl attribute constant for the given attribute name.
+ * @param attName the attribute name, case insensitive
+ */
+ public static Attribute getAttributeKey(String attName)
+ {
+ if (attrMap == null)
+ {
+ // Create the map on demand.
+ attrMap = new TreeMap<String,Attribute>();
+
+ Attribute[] attrs = getAllAttributeKeys();
+
+ for (int i = 0; i < attrs.length; i++)
+ {
+ attrMap.put(attrs [ i ].toString(), attrs [ i ]);
+ }
+ }
+
+ return attrMap.get(attName.toLowerCase());
+ }
+
+ /**
+ * Searches the value of given attribute in the provided set.
+ * If the value is found (String type expected), tries to parse it as
+ * an integer value. If succeded, returns the obtained integer value.
+ *
+ * For example:<p><code>
+ * SimpleAttributeSet ase = new SimpleAttributeSet();
+ * ase.addAttribute(HTML.getAttributeKey("size"),"222");
+ * System.out.println(
+ * HTML.getIntegerAttributeValue
+ * (ase, HTML.getAttributeKey("size"), 333)); // prints "222"
+ * System.out.println(
+ * HTML.getIntegerAttributeValue
+ * (ase, HTML.getAttributeKey("width"), 333)); // prints "333".
+ * </code></p>
+ *
+ *
+ * @param set The attribute set to search in. If the set contains the
+ * given attribute, it must by a type of String.
+ * @param attribute The html attribute to search in
+ * @param defaultValue The value that is returned if the attribute is not
+ * found in the given set or if the NumberFormatException was thrown
+ * during the parsing.
+ */
+ public static int getIntegerAttributeValue(AttributeSet set,
+ HTML.Attribute attribute,
+ int defaultValue
+ )
+ {
+ Object v = set.getAttribute(attribute);
+
+ if (v == null)
+ {
+ return defaultValue;
+ }
+
+ try
+ {
+ return Integer.parseInt(v.toString().trim());
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns a HTML tag constant for the given HTML attribute name.
+ * If the tag is unknown, the null is returned.
+ * @param tagName the tag name, case insensitive
+ */
+ public static Tag getTag(String tagName)
+ {
+ if (tagMap == null)
+ {
+ // Create the mao on demand.
+ tagMap = new TreeMap<String,Tag>();
+
+ Tag[] tags = getAllTags();
+
+ for (int i = 0; i < tags.length; i++)
+ {
+ tagMap.put(tags [ i ].toString(), tags [ i ]);
+ }
+ }
+
+ return tagMap.get(tagName.toLowerCase());
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java
new file mode 100644
index 000000000..9545be4e8
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java
@@ -0,0 +1,2298 @@
+/* HTMLDocument.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import gnu.classpath.NotImplementedException;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Stack;
+import java.util.Vector;
+
+import javax.swing.ButtonGroup;
+import javax.swing.DefaultButtonModel;
+import javax.swing.JEditorPane;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.Element;
+import javax.swing.text.ElementIterator;
+import javax.swing.text.GapContent;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.PlainDocument;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.html.HTML.Tag;
+
+/**
+ * Represents the HTML document that is constructed by defining the text and
+ * other components (images, buttons, etc) in HTML language. This class can
+ * becomes the default document for {@link JEditorPane} after setting its
+ * content type to "text/html". HTML document also serves as an intermediate
+ * data structure when it is needed to parse HTML and then obtain the content of
+ * the certain types of tags. This class also has methods for modifying the HTML
+ * content.
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ * @author Anthony Balkissoon (abalkiss@redhat.com)
+ * @author Lillian Angel (langel@redhat.com)
+ */
+public class HTMLDocument extends DefaultStyledDocument
+{
+ /** A key for document properies. The value for the key is
+ * a Vector of Strings of comments not found in the body.
+ */
+ public static final String AdditionalComments = "AdditionalComments";
+ URL baseURL = null;
+ boolean preservesUnknownTags = true;
+ int tokenThreshold = Integer.MAX_VALUE;
+ HTMLEditorKit.Parser parser;
+
+ /**
+ * Indicates whether this document is inside a frame or not.
+ */
+ private boolean frameDocument;
+
+ /**
+ * Package private to avoid accessor methods.
+ */
+ String baseTarget;
+
+ /**
+ * Constructs an HTML document using the default buffer size and a default
+ * StyleSheet.
+ */
+ public HTMLDocument()
+ {
+ this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet());
+ }
+
+ /**
+ * Constructs an HTML document with the default content storage
+ * implementation and the specified style/attribute storage mechanism.
+ *
+ * @param styles - the style sheet
+ */
+ public HTMLDocument(StyleSheet styles)
+ {
+ this(new GapContent(BUFFER_SIZE_DEFAULT), styles);
+ }
+
+ /**
+ * Constructs an HTML document with the given content storage implementation
+ * and the given style/attribute storage mechanism.
+ *
+ * @param c - the document's content
+ * @param styles - the style sheet
+ */
+ public HTMLDocument(AbstractDocument.Content c, StyleSheet styles)
+ {
+ super(c, styles);
+ }
+
+ /**
+ * Gets the style sheet with the document display rules (CSS) that were specified
+ * in the HTML document.
+ *
+ * @return - the style sheet
+ */
+ public StyleSheet getStyleSheet()
+ {
+ return (StyleSheet) getAttributeContext();
+ }
+
+ /**
+ * This method creates a root element for the new document.
+ *
+ * @return the new default root
+ */
+ protected AbstractElement createDefaultRoot()
+ {
+ AbstractDocument.AttributeContext ctx = getAttributeContext();
+
+ // Create html element.
+ AttributeSet atts = ctx.getEmptySet();
+ atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.HTML);
+ BranchElement html = (BranchElement) createBranchElement(null, atts);
+
+ // Create body element.
+ atts = ctx.getEmptySet();
+ atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.BODY);
+ BranchElement body = (BranchElement) createBranchElement(html, atts);
+ html.replace(0, 0, new Element[] { body });
+
+ // Create p element.
+ atts = ctx.getEmptySet();
+ atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.P);
+ BranchElement p = (BranchElement) createBranchElement(body, atts);
+ body.replace(0, 0, new Element[] { p });
+
+ // Create an empty leaf element.
+ atts = ctx.getEmptySet();
+ atts = ctx.addAttribute(atts, StyleConstants.NameAttribute,
+ HTML.Tag.CONTENT);
+ Element leaf = createLeafElement(p, atts, 0, 1);
+ p.replace(0, 0, new Element[]{ leaf });
+
+ return html;
+ }
+
+ /**
+ * This method returns an HTMLDocument.RunElement object attached to
+ * parent representing a run of text from p0 to p1. The run has
+ * attributes described by a.
+ *
+ * @param parent - the parent element
+ * @param a - the attributes for the element
+ * @param p0 - the beginning of the range >= 0
+ * @param p1 - the end of the range >= p0
+ *
+ * @return the new element
+ */
+ protected Element createLeafElement(Element parent, AttributeSet a, int p0,
+ int p1)
+ {
+ return new RunElement(parent, a, p0, p1);
+ }
+
+ /**
+ * This method returns an HTMLDocument.BlockElement object representing the
+ * attribute set a and attached to parent.
+ *
+ * @param parent - the parent element
+ * @param a - the attributes for the element
+ *
+ * @return the new element
+ */
+ protected Element createBranchElement(Element parent, AttributeSet a)
+ {
+ return new BlockElement(parent, a);
+ }
+
+ /**
+ * Returns the parser used by this HTMLDocument to insert HTML.
+ *
+ * @return the parser used by this HTMLDocument to insert HTML.
+ */
+ public HTMLEditorKit.Parser getParser()
+ {
+ return parser;
+ }
+
+ /**
+ * Sets the parser used by this HTMLDocument to insert HTML.
+ *
+ * @param p the parser to use
+ */
+ public void setParser (HTMLEditorKit.Parser p)
+ {
+ parser = p;
+ }
+ /**
+ * Sets the number of tokens to buffer before trying to display the
+ * Document.
+ *
+ * @param n the number of tokens to buffer
+ */
+ public void setTokenThreshold (int n)
+ {
+ tokenThreshold = n;
+ }
+
+ /**
+ * Returns the number of tokens that are buffered before the document
+ * is rendered.
+ *
+ * @return the number of tokens buffered
+ */
+ public int getTokenThreshold ()
+ {
+ return tokenThreshold;
+ }
+
+ /**
+ * Returns the location against which to resolve relative URLs.
+ * This is the document's URL if the document was loaded from a URL.
+ * If a <code>base</code> tag is found, it will be used.
+ * @return the base URL
+ */
+ public URL getBase()
+ {
+ return baseURL;
+ }
+
+ /**
+ * Sets the location against which to resolve relative URLs.
+ * @param u the new base URL
+ */
+ public void setBase(URL u)
+ {
+ baseURL = u;
+ getStyleSheet().setBase(u);
+ }
+
+ /**
+ * Returns whether or not the parser preserves unknown HTML tags.
+ * @return true if the parser preserves unknown tags
+ */
+ public boolean getPreservesUnknownTags()
+ {
+ return preservesUnknownTags;
+ }
+
+ /**
+ * Sets the behaviour of the parser when it encounters unknown HTML tags.
+ * @param preservesTags true if the parser should preserve unknown tags.
+ */
+ public void setPreservesUnknownTags(boolean preservesTags)
+ {
+ preservesUnknownTags = preservesTags;
+ }
+
+ /**
+ * An iterator to iterate through LeafElements in the document.
+ */
+ class LeafIterator extends Iterator
+ {
+ HTML.Tag tag;
+ HTMLDocument doc;
+ ElementIterator it;
+
+ public LeafIterator (HTML.Tag t, HTMLDocument d)
+ {
+ doc = d;
+ tag = t;
+ it = new ElementIterator(doc);
+ }
+
+ /**
+ * Return the attributes for the tag associated with this iteartor
+ * @return the AttributeSet
+ */
+ public AttributeSet getAttributes()
+ {
+ if (it.current() != null)
+ return it.current().getAttributes();
+ return null;
+ }
+
+ /**
+ * Get the end of the range for the current occurrence of the tag
+ * being defined and having the same attributes.
+ * @return the end of the range
+ */
+ public int getEndOffset()
+ {
+ if (it.current() != null)
+ return it.current().getEndOffset();
+ return -1;
+ }
+
+ /**
+ * Get the start of the range for the current occurrence of the tag
+ * being defined and having the same attributes.
+ * @return the start of the range (-1 if it can't be found).
+ */
+
+ public int getStartOffset()
+ {
+ if (it.current() != null)
+ return it.current().getStartOffset();
+ return -1;
+ }
+
+ /**
+ * Advance the iterator to the next LeafElement .
+ */
+ public void next()
+ {
+ it.next();
+ while (it.current()!= null && !it.current().isLeaf())
+ it.next();
+ }
+
+ /**
+ * Indicates whether or not the iterator currently represents an occurrence
+ * of the tag.
+ * @return true if the iterator currently represents an occurrence of the
+ * tag.
+ */
+ public boolean isValid()
+ {
+ return it.current() != null;
+ }
+
+ /**
+ * Type of tag for this iterator.
+ */
+ public Tag getTag()
+ {
+ return tag;
+ }
+
+ }
+
+ public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event)
+ {
+ String target = event.getTarget();
+ Element el = event.getSourceElement();
+ URL url = event.getURL();
+ if (target.equals("_self"))
+ {
+ updateFrame(el, url);
+ }
+ else if (target.equals("_parent"))
+ {
+ updateFrameSet(el.getParentElement(), url);
+ }
+ else
+ {
+ Element targetFrame = findFrame(target);
+ if (targetFrame != null)
+ updateFrame(targetFrame, url);
+ }
+ }
+
+ /**
+ * Finds the named frame inside this document.
+ *
+ * @param target the name to look for
+ *
+ * @return the frame if there is a matching frame, <code>null</code>
+ * otherwise
+ */
+ private Element findFrame(String target)
+ {
+ ElementIterator i = new ElementIterator(this);
+ Element next = null;
+ while ((next = i.next()) != null)
+ {
+ AttributeSet atts = next.getAttributes();
+ if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FRAME)
+ {
+ String name = (String) atts.getAttribute(HTML.Attribute.NAME);
+ if (name != null && name.equals(target))
+ break;
+ }
+ }
+ return next;
+ }
+
+ /**
+ * Updates the frame that is represented by the specified element to
+ * refer to the specified URL.
+ *
+ * @param el the element
+ * @param url the new url
+ */
+ private void updateFrame(Element el, URL url)
+ {
+ try
+ {
+ writeLock();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(el.getStartOffset(), 1,
+ DocumentEvent.EventType.CHANGE);
+ AttributeSet elAtts = el.getAttributes();
+ AttributeSet copy = elAtts.copyAttributes();
+ MutableAttributeSet matts = (MutableAttributeSet) elAtts;
+ ev.addEdit(new AttributeUndoableEdit(el, copy, false));
+ matts.removeAttribute(HTML.Attribute.SRC);
+ matts.addAttribute(HTML.Attribute.SRC, url.toString());
+ ev.end();
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+ /**
+ * Updates the frameset that is represented by the specified element
+ * to create a frame that refers to the specified URL.
+ *
+ * @param el the element
+ * @param url the url
+ */
+ private void updateFrameSet(Element el, URL url)
+ {
+ int start = el.getStartOffset();
+ int end = el.getEndOffset();
+
+ StringBuilder html = new StringBuilder();
+ html.append("<frame");
+ if (url != null)
+ {
+ html.append(" src=\"");
+ html.append(url.toString());
+ html.append("\"");
+ }
+ html.append('>');
+ if (getParser() == null)
+ setParser(new HTMLEditorKit().getParser());
+ try
+ {
+ setOuterHTML(el, html.toString());
+ }
+ catch (BadLocationException ex)
+ {
+ ex.printStackTrace();
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Gets an iterator for the given HTML.Tag.
+ * @param t the requested HTML.Tag
+ * @return the Iterator
+ */
+ public HTMLDocument.Iterator getIterator (HTML.Tag t)
+ {
+ return new HTMLDocument.LeafIterator(t, this);
+ }
+
+ /**
+ * An iterator over a particular type of tag.
+ */
+ public abstract static class Iterator
+ {
+ /**
+ * Return the attribute set for this tag.
+ * @return the <code>AttributeSet</code> (null if none found).
+ */
+ public abstract AttributeSet getAttributes();
+
+ /**
+ * Get the end of the range for the current occurrence of the tag
+ * being defined and having the same attributes.
+ * @return the end of the range
+ */
+ public abstract int getEndOffset();
+
+ /**
+ * Get the start of the range for the current occurrence of the tag
+ * being defined and having the same attributes.
+ * @return the start of the range (-1 if it can't be found).
+ */
+ public abstract int getStartOffset();
+
+ /**
+ * Move the iterator forward.
+ */
+ public abstract void next();
+
+ /**
+ * Indicates whether or not the iterator currently represents an occurrence
+ * of the tag.
+ * @return true if the iterator currently represents an occurrence of the
+ * tag.
+ */
+ public abstract boolean isValid();
+
+ /**
+ * Type of tag this iterator represents.
+ * @return the tag.
+ */
+ public abstract HTML.Tag getTag();
+ }
+
+ public class BlockElement extends AbstractDocument.BranchElement
+ {
+ public BlockElement (Element parent, AttributeSet a)
+ {
+ super(parent, a);
+ }
+
+ /**
+ * Gets the resolving parent. Since HTML attributes are not
+ * inherited at the model level, this returns null.
+ */
+ public AttributeSet getResolveParent()
+ {
+ return null;
+ }
+
+ /**
+ * Gets the name of the element.
+ *
+ * @return the name of the element if it exists, null otherwise.
+ */
+ public String getName()
+ {
+ Object tag = getAttribute(StyleConstants.NameAttribute);
+ String name = null;
+ if (tag != null)
+ name = tag.toString();
+ if (name == null)
+ name = super.getName();
+ return name;
+ }
+ }
+
+ /**
+ * RunElement represents a section of text that has a set of
+ * HTML character level attributes assigned to it.
+ */
+ public class RunElement extends AbstractDocument.LeafElement
+ {
+
+ /**
+ * Constructs an element that has no children. It represents content
+ * within the document.
+ *
+ * @param parent - parent of this
+ * @param a - elements attributes
+ * @param start - the start offset >= 0
+ * @param end - the end offset
+ */
+ public RunElement(Element parent, AttributeSet a, int start, int end)
+ {
+ super(parent, a, start, end);
+ }
+
+ /**
+ * Gets the name of the element.
+ *
+ * @return the name of the element if it exists, null otherwise.
+ */
+ public String getName()
+ {
+ Object tag = getAttribute(StyleConstants.NameAttribute);
+ String name = null;
+ if (tag != null)
+ name = tag.toString();
+ if (name == null)
+ name = super.getName();
+ return name;
+ }
+
+ /**
+ * Gets the resolving parent. HTML attributes do not inherit at the
+ * model level, so this method returns null.
+ *
+ * @return null
+ */
+ public AttributeSet getResolveParent()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * A reader to load an HTMLDocument with HTML structure.
+ *
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ */
+ public class HTMLReader extends HTMLEditorKit.ParserCallback
+ {
+ /**
+ * The maximum token threshold. We don't grow it larger than this.
+ */
+ private static final int MAX_THRESHOLD = 10000;
+
+ /**
+ * The threshold growth factor.
+ */
+ private static final int GROW_THRESHOLD = 5;
+
+ /**
+ * Holds the current character attribute set *
+ */
+ protected MutableAttributeSet charAttr = new SimpleAttributeSet();
+
+ protected Vector<ElementSpec> parseBuffer = new Vector<ElementSpec>();
+
+ /**
+ * The parse stack. It holds the current element tree path.
+ */
+ private Stack<HTML.Tag> parseStack = new Stack<HTML.Tag>();
+
+ /**
+ * A stack for character attribute sets *
+ */
+ Stack charAttrStack = new Stack();
+
+ /** A mapping between HTML.Tag objects and the actions that handle them **/
+ HashMap tagToAction;
+
+ /** Tells us whether we've received the '</html>' tag yet **/
+ boolean endHTMLEncountered = false;
+
+ /**
+ * Related to the constructor with explicit insertTag
+ */
+ int popDepth;
+
+ /**
+ * Related to the constructor with explicit insertTag
+ */
+ int pushDepth;
+
+ /**
+ * Related to the constructor with explicit insertTag
+ */
+ int offset;
+
+ /**
+ * The tag (inclusve), after that the insertion should start.
+ */
+ HTML.Tag insertTag;
+
+ /**
+ * This variable becomes true after the insert tag has been encountered.
+ */
+ boolean insertTagEncountered;
+
+
+ /** A temporary variable that helps with the printing out of debug information **/
+ boolean debug = false;
+
+ /**
+ * This is true when we are inside a pre tag.
+ */
+ boolean inPreTag = false;
+
+ /**
+ * This is true when we are inside a style tag. This will add text
+ * content inside this style tag beeing parsed as CSS.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ boolean inStyleTag = false;
+
+ /**
+ * This is true when we are inside a &lt;textarea&gt; tag. Any text
+ * content will then be added to the text area.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ boolean inTextArea = false;
+
+ /**
+ * This contains all stylesheets that are somehow read, either
+ * via embedded style tags, or via linked stylesheets. The
+ * elements will be String objects containing a stylesheet each.
+ */
+ ArrayList styles;
+
+ /**
+ * The document model for a textarea.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ ResetablePlainDocument textAreaDocument;
+
+ /**
+ * The current model of a select tag. Can be a ComboBoxModel or a
+ * ListModel depending on the type of the select box.
+ */
+ Object selectModel;
+
+ /**
+ * The current option beeing read.
+ */
+ Option option;
+
+ /**
+ * The current number of options in the current select model.
+ */
+ int numOptions;
+
+ /**
+ * The current button groups mappings.
+ */
+ HashMap buttonGroups;
+
+ /**
+ * The token threshold. This gets increased while loading.
+ */
+ private int threshold;
+
+ public class TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action. By default this does nothing.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action. By default does nothing.
+ */
+ public void end(HTML.Tag t)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ public class BlockAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // Tell the parse buffer to open a new block for this tag.
+ blockOpen(t, a);
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // Tell the parse buffer to close this block.
+ blockClose(t);
+ }
+ }
+
+ public class CharacterAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ // Put the old attribute set on the stack.
+ pushCharacterStyle();
+
+ // Initialize with link pseudo class.
+ if (t == HTML.Tag.A)
+ a.addAttribute(HTML.Attribute.PSEUDO_CLASS, "link");
+
+ // Just add the attributes in <code>a</code>.
+ charAttr.addAttribute(t, a.copyAttributes());
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ popCharacterStyle();
+ }
+ }
+
+ /**
+ * Processes elements that make up forms: &lt;input&gt;, &lt;textarea&gt;,
+ * &lt;select&gt; and &lt;option&gt;.
+ */
+ public class FormAction extends SpecialAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ if (t == HTML.Tag.INPUT)
+ {
+ String type = (String) a.getAttribute(HTML.Attribute.TYPE);
+ if (type == null)
+ {
+ type = "text"; // Default to 'text' when nothing was specified.
+ a.addAttribute(HTML.Attribute.TYPE, type);
+ }
+ setModel(type, a);
+ }
+ else if (t == HTML.Tag.TEXTAREA)
+ {
+ inTextArea = true;
+ textAreaDocument = new ResetablePlainDocument();
+ a.addAttribute(StyleConstants.ModelAttribute, textAreaDocument);
+ }
+ else if (t == HTML.Tag.SELECT)
+ {
+ int size = HTML.getIntegerAttributeValue(a, HTML.Attribute.SIZE,
+ 1);
+ boolean multi = a.getAttribute(HTML.Attribute.MULTIPLE) != null;
+ if (size > 1 || multi)
+ {
+ SelectListModel m = new SelectListModel();
+ if (multi)
+ m.getSelectionModel().setSelectionMode(ListSelectionModel
+ .MULTIPLE_INTERVAL_SELECTION);
+ selectModel = m;
+ }
+ else
+ {
+ selectModel = new SelectComboBoxModel();
+ }
+ a.addAttribute(StyleConstants.ModelAttribute, selectModel);
+ }
+ if (t == HTML.Tag.OPTION)
+ {
+ option = new Option(a);
+ if (selectModel instanceof SelectListModel)
+ {
+ SelectListModel m = (SelectListModel) selectModel;
+ m.addElement(option);
+ if (option.isSelected())
+ {
+ m.getSelectionModel().addSelectionInterval(numOptions,
+ numOptions);
+ m.addInitialSelection(numOptions);
+ }
+ }
+ else if (selectModel instanceof SelectComboBoxModel)
+ {
+ SelectComboBoxModel m = (SelectComboBoxModel) selectModel;
+ m.addElement(option);
+ if (option.isSelected())
+ {
+ m.setSelectedItem(option);
+ m.setInitialSelection(option);
+ }
+ }
+ numOptions++;
+ }
+ else
+ {
+ // Build the element.
+ super.start(t, a);
+ }
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ if (t == HTML.Tag.OPTION)
+ {
+ option = null;
+ }
+ else
+ {
+ if (t == HTML.Tag.TEXTAREA)
+ {
+ inTextArea = false;
+ }
+ else if (t == HTML.Tag.SELECT)
+ {
+ selectModel = null;
+ numOptions = 0;
+ }
+ // Finish the element.
+ super.end(t);
+ }
+ }
+
+ private void setModel(String type, MutableAttributeSet attrs)
+ {
+ if (type.equals("submit") || type.equals("reset")
+ || type.equals("image"))
+ {
+ // Create button.
+ attrs.addAttribute(StyleConstants.ModelAttribute,
+ new DefaultButtonModel());
+ }
+ else if (type.equals("text") || type.equals("password"))
+ {
+ String text = (String) attrs.getAttribute(HTML.Attribute.VALUE);
+ ResetablePlainDocument doc = new ResetablePlainDocument();
+ if (text != null)
+ {
+ doc.setInitialText(text);
+ try
+ {
+ doc.insertString(0, text, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // Shouldn't happen.
+ assert false;
+ }
+ }
+ attrs.addAttribute(StyleConstants.ModelAttribute, doc);
+ }
+ else if (type.equals("file"))
+ {
+ attrs.addAttribute(StyleConstants.ModelAttribute,
+ new PlainDocument());
+ }
+ else if (type.equals("checkbox") || type.equals("radio"))
+ {
+ ResetableToggleButtonModel model =
+ new ResetableToggleButtonModel();
+ if (attrs.getAttribute(HTML.Attribute.SELECTED) != null)
+ {
+ model.setSelected(true);
+ model.setInitial(true);
+ }
+ if (type.equals("radio"))
+ {
+ String name = (String) attrs.getAttribute(HTML.Attribute.NAME);
+ if (name != null)
+ {
+ if (buttonGroups == null)
+ buttonGroups = new HashMap();
+ ButtonGroup group = (ButtonGroup) buttonGroups.get(name);
+ if (group == null)
+ {
+ group = new ButtonGroup();
+ buttonGroups.put(name, group);
+ }
+ model.setGroup(group);
+ }
+ }
+ attrs.addAttribute(StyleConstants.ModelAttribute, model);
+ }
+ }
+ }
+
+ /**
+ * Called for form tags.
+ */
+ class FormTagAction
+ extends BlockAction
+ {
+ /**
+ * Clears the button group mapping.
+ */
+ public void end(HTML.Tag t)
+ {
+ super.end(t);
+ buttonGroups = null;
+ }
+ }
+
+ /**
+ * This action indicates that the content between starting and closing HTML
+ * elements (like script - /script) should not be visible. The content is
+ * still inserted and can be accessed when iterating the HTML document. The
+ * parser will only fire
+ * {@link javax.swing.text.html.HTMLEditorKit.ParserCallback#handleText} for
+ * the hidden tags, regardless from that html tags the hidden section may
+ * contain.
+ */
+ public class HiddenAction
+ extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ blockOpen(t, a);
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ blockClose(t);
+ }
+ }
+
+ /**
+ * Handles &lt;isindex&gt; tags.
+ */
+ public class IsindexAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
+ addSpecialElement(t, a);
+ blockClose(HTML.Tag.IMPLIED);
+ }
+ }
+
+ public class ParagraphAction extends BlockAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ super.start(t, a);
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ super.end(t);
+ }
+ }
+
+ /**
+ * This action is performed when a &lt;pre&gt; tag is parsed.
+ */
+ public class PreAction extends BlockAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ inPreTag = true;
+ blockOpen(t, a);
+ a.addAttribute(CSS.Attribute.WHITE_SPACE, "pre");
+ blockOpen(HTML.Tag.IMPLIED, a);
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ blockClose(HTML.Tag.IMPLIED);
+ inPreTag = false;
+ blockClose(t);
+ }
+ }
+
+ /**
+ * Inserts the elements that are represented by ths single tag with
+ * attributes (only). The closing tag, even if present, mut follow
+ * immediately after the starting tag without providing any additional
+ * information. Hence the {@link TagAction#end} method need not be
+ * overridden and still does nothing.
+ */
+ public class SpecialAction extends TagAction
+ {
+ /**
+ * The functionality is delegated to {@link HTMLReader#addSpecialElement}
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ addSpecialElement(t, a);
+ }
+ }
+
+ class AreaAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ }
+ }
+
+ /**
+ * Converts HTML tags to CSS attributes.
+ */
+ class ConvertAction
+ extends TagAction
+ {
+
+ public void start(HTML.Tag tag, MutableAttributeSet atts)
+ {
+ pushCharacterStyle();
+ charAttr.addAttribute(tag, atts.copyAttributes());
+ StyleSheet styleSheet = getStyleSheet();
+ // TODO: Add other tags here.
+ if (tag == HTML.Tag.FONT)
+ {
+ String color = (String) atts.getAttribute(HTML.Attribute.COLOR);
+ if (color != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.COLOR, color);
+ String face = (String) atts.getAttribute(HTML.Attribute.FACE);
+ if (face != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_FAMILY,
+ face);
+ String size = (String) atts.getAttribute(HTML.Attribute.SIZE);
+ if (size != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_SIZE,
+ size);
+ }
+ }
+
+ public void end(HTML.Tag tag)
+ {
+ popCharacterStyle();
+ }
+ }
+
+ class BaseAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ baseTarget = (String) a.getAttribute(HTML.Attribute.TARGET);
+ }
+ }
+
+ class HeadAction extends BlockAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ super.start(t, a);
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ // We read in all the stylesheets that are embedded or referenced
+ // inside the header.
+ if (styles != null)
+ {
+ int numStyles = styles.size();
+ for (int i = 0; i < numStyles; i++)
+ {
+ String style = (String) styles.get(i);
+ getStyleSheet().addRule(style);
+ }
+ }
+ super.end(t);
+ }
+ }
+
+ class LinkAction extends HiddenAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ super.start(t, a);
+ String type = (String) a.getAttribute(HTML.Attribute.TYPE);
+ if (type == null)
+ type = "text/css";
+ if (type.equals("text/css"))
+ {
+ String rel = (String) a.getAttribute(HTML.Attribute.REL);
+ String media = (String) a.getAttribute(HTML.Attribute.MEDIA);
+ String title = (String) a.getAttribute(HTML.Attribute.TITLE);
+ if (media == null)
+ media = "all";
+ else
+ media = media.toLowerCase();
+ if (rel != null)
+ {
+ rel = rel.toLowerCase();
+ if ((media.indexOf("all") != -1
+ || media.indexOf("screen") != -1)
+ && (rel.equals("stylesheet")))
+ {
+ String href = (String) a.getAttribute(HTML.Attribute.HREF);
+ URL url = null;
+ try
+ {
+ url = new URL(baseURL, href);
+ }
+ catch (MalformedURLException ex)
+ {
+ try
+ {
+ url = new URL(href);
+ }
+ catch (MalformedURLException ex2)
+ {
+ url = null;
+ }
+ }
+ if (url != null)
+ {
+ try
+ {
+ getStyleSheet().importStyleSheet(url);
+ }
+ catch (Exception ex)
+ {
+ // Don't let exceptions and runtime exceptions
+ // in CSS parsing disprupt the HTML parsing
+ // process. But inform the user/developer
+ // on the console about it.
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ class MapAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ }
+ }
+
+ class MetaAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ }
+ }
+
+ class StyleAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ {
+ inStyleTag = true;
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ {
+ inStyleTag = false;
+ }
+ }
+
+ class TitleAction extends TagAction
+ {
+ /**
+ * This method is called when a start tag is seen for one of the types
+ * of tags associated with this Action.
+ */
+ public void start(HTML.Tag t, MutableAttributeSet a)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * Called when an end tag is seen for one of the types of tags associated
+ * with this Action.
+ */
+ public void end(HTML.Tag t)
+ throws NotImplementedException
+ {
+ // FIXME: Implement.
+ }
+ }
+
+ public HTMLReader(int offset)
+ {
+ this (offset, 0, 0, null);
+ }
+
+ public HTMLReader(int offset, int popDepth, int pushDepth,
+ HTML.Tag insertTag)
+ {
+ this.insertTag = insertTag;
+ this.offset = offset;
+ this.popDepth = popDepth;
+ this.pushDepth = pushDepth;
+ threshold = getTokenThreshold();
+ initTags();
+ }
+
+ void initTags()
+ {
+ tagToAction = new HashMap(72);
+ CharacterAction characterAction = new CharacterAction();
+ HiddenAction hiddenAction = new HiddenAction();
+ AreaAction areaAction = new AreaAction();
+ BaseAction baseAction = new BaseAction();
+ BlockAction blockAction = new BlockAction();
+ SpecialAction specialAction = new SpecialAction();
+ ParagraphAction paragraphAction = new ParagraphAction();
+ HeadAction headAction = new HeadAction();
+ FormAction formAction = new FormAction();
+ IsindexAction isindexAction = new IsindexAction();
+ LinkAction linkAction = new LinkAction();
+ MapAction mapAction = new MapAction();
+ PreAction preAction = new PreAction();
+ MetaAction metaAction = new MetaAction();
+ StyleAction styleAction = new StyleAction();
+ TitleAction titleAction = new TitleAction();
+
+ ConvertAction convertAction = new ConvertAction();
+ tagToAction.put(HTML.Tag.A, characterAction);
+ tagToAction.put(HTML.Tag.ADDRESS, characterAction);
+ tagToAction.put(HTML.Tag.APPLET, hiddenAction);
+ tagToAction.put(HTML.Tag.AREA, areaAction);
+ tagToAction.put(HTML.Tag.B, characterAction);
+ tagToAction.put(HTML.Tag.BASE, baseAction);
+ tagToAction.put(HTML.Tag.BASEFONT, characterAction);
+ tagToAction.put(HTML.Tag.BIG, characterAction);
+ tagToAction.put(HTML.Tag.BLOCKQUOTE, blockAction);
+ tagToAction.put(HTML.Tag.BODY, blockAction);
+ tagToAction.put(HTML.Tag.BR, specialAction);
+ tagToAction.put(HTML.Tag.CAPTION, blockAction);
+ tagToAction.put(HTML.Tag.CENTER, blockAction);
+ tagToAction.put(HTML.Tag.CITE, characterAction);
+ tagToAction.put(HTML.Tag.CODE, characterAction);
+ tagToAction.put(HTML.Tag.DD, blockAction);
+ tagToAction.put(HTML.Tag.DFN, characterAction);
+ tagToAction.put(HTML.Tag.DIR, blockAction);
+ tagToAction.put(HTML.Tag.DIV, blockAction);
+ tagToAction.put(HTML.Tag.DL, blockAction);
+ tagToAction.put(HTML.Tag.DT, paragraphAction);
+ tagToAction.put(HTML.Tag.EM, characterAction);
+ tagToAction.put(HTML.Tag.FONT, convertAction);
+ tagToAction.put(HTML.Tag.FORM, new FormTagAction());
+ tagToAction.put(HTML.Tag.FRAME, specialAction);
+ tagToAction.put(HTML.Tag.FRAMESET, blockAction);
+ tagToAction.put(HTML.Tag.H1, paragraphAction);
+ tagToAction.put(HTML.Tag.H2, paragraphAction);
+ tagToAction.put(HTML.Tag.H3, paragraphAction);
+ tagToAction.put(HTML.Tag.H4, paragraphAction);
+ tagToAction.put(HTML.Tag.H5, paragraphAction);
+ tagToAction.put(HTML.Tag.H6, paragraphAction);
+ tagToAction.put(HTML.Tag.HEAD, headAction);
+ tagToAction.put(HTML.Tag.HR, specialAction);
+ tagToAction.put(HTML.Tag.HTML, blockAction);
+ tagToAction.put(HTML.Tag.I, characterAction);
+ tagToAction.put(HTML.Tag.IMG, specialAction);
+ tagToAction.put(HTML.Tag.INPUT, formAction);
+ tagToAction.put(HTML.Tag.ISINDEX, isindexAction);
+ tagToAction.put(HTML.Tag.KBD, characterAction);
+ tagToAction.put(HTML.Tag.LI, blockAction);
+ tagToAction.put(HTML.Tag.LINK, linkAction);
+ tagToAction.put(HTML.Tag.MAP, mapAction);
+ tagToAction.put(HTML.Tag.MENU, blockAction);
+ tagToAction.put(HTML.Tag.META, metaAction);
+ tagToAction.put(HTML.Tag.NOFRAMES, blockAction);
+ tagToAction.put(HTML.Tag.OBJECT, specialAction);
+ tagToAction.put(HTML.Tag.OL, blockAction);
+ tagToAction.put(HTML.Tag.OPTION, formAction);
+ tagToAction.put(HTML.Tag.P, paragraphAction);
+ tagToAction.put(HTML.Tag.PARAM, hiddenAction);
+ tagToAction.put(HTML.Tag.PRE, preAction);
+ tagToAction.put(HTML.Tag.SAMP, characterAction);
+ tagToAction.put(HTML.Tag.SCRIPT, hiddenAction);
+ tagToAction.put(HTML.Tag.SELECT, formAction);
+ tagToAction.put(HTML.Tag.SMALL, characterAction);
+ tagToAction.put(HTML.Tag.STRIKE, characterAction);
+ tagToAction.put(HTML.Tag.S, characterAction);
+ tagToAction.put(HTML.Tag.STRONG, characterAction);
+ tagToAction.put(HTML.Tag.STYLE, styleAction);
+ tagToAction.put(HTML.Tag.SUB, characterAction);
+ tagToAction.put(HTML.Tag.SUP, characterAction);
+ tagToAction.put(HTML.Tag.TABLE, blockAction);
+ tagToAction.put(HTML.Tag.TD, blockAction);
+ tagToAction.put(HTML.Tag.TEXTAREA, formAction);
+ tagToAction.put(HTML.Tag.TH, blockAction);
+ tagToAction.put(HTML.Tag.TITLE, titleAction);
+ tagToAction.put(HTML.Tag.TR, blockAction);
+ tagToAction.put(HTML.Tag.TT, characterAction);
+ tagToAction.put(HTML.Tag.U, characterAction);
+ tagToAction.put(HTML.Tag.UL, blockAction);
+ tagToAction.put(HTML.Tag.VAR, characterAction);
+ }
+
+ /**
+ * Pushes the current character style onto the stack.
+ *
+ */
+ protected void pushCharacterStyle()
+ {
+ charAttrStack.push(charAttr.copyAttributes());
+ }
+
+ /**
+ * Pops a character style off of the stack and uses it as the
+ * current character style.
+ *
+ */
+ protected void popCharacterStyle()
+ {
+ if (!charAttrStack.isEmpty())
+ charAttr = (MutableAttributeSet) charAttrStack.pop();
+ }
+
+ /**
+ * Registers a given tag with a given Action. All of the well-known tags
+ * are registered by default, but this method can change their behaviour
+ * or add support for custom or currently unsupported tags.
+ *
+ * @param t the Tag to register
+ * @param a the Action for the Tag
+ */
+ protected void registerTag(HTML.Tag t, HTMLDocument.HTMLReader.TagAction a)
+ {
+ tagToAction.put (t, a);
+ }
+
+ /**
+ * This is the last method called on the HTMLReader, allowing any pending
+ * changes to be flushed to the HTMLDocument.
+ */
+ public void flush() throws BadLocationException
+ {
+ flushImpl();
+ }
+
+ /**
+ * Flushes the buffer and handle partial inserts.
+ *
+ */
+ private void flushImpl()
+ throws BadLocationException
+ {
+ int oldLen = getLength();
+ int size = parseBuffer.size();
+ ElementSpec[] elems = new ElementSpec[size];
+ parseBuffer.copyInto(elems);
+ if (oldLen == 0)
+ create(elems);
+ else
+ insert(offset, elems);
+ parseBuffer.removeAllElements();
+ offset += getLength() - oldLen;
+ }
+
+ /**
+ * This method is called by the parser to indicate a block of
+ * text was encountered. Should insert the text appropriately.
+ *
+ * @param data the text that was inserted
+ * @param pos the position at which the text was inserted
+ */
+ public void handleText(char[] data, int pos)
+ {
+ if (shouldInsert() && data != null && data.length > 0)
+ {
+ if (inTextArea)
+ textAreaContent(data);
+ else if (inPreTag)
+ preContent(data);
+ else if (option != null)
+ option.setLabel(new String(data));
+ else if (inStyleTag)
+ {
+ if (styles == null)
+ styles = new ArrayList();
+ styles.add(new String(data));
+ }
+ else
+ addContent(data, 0, data.length);
+
+ }
+ }
+
+ /**
+ * Checks if the HTML tag should be inserted. The tags before insert tag (if
+ * specified) are not inserted. Also, the tags after the end of the html are
+ * not inserted.
+ *
+ * @return true if the tag should be inserted, false otherwise.
+ */
+ private boolean shouldInsert()
+ {
+ return ! endHTMLEncountered
+ && (insertTagEncountered || insertTag == null);
+ }
+
+ /**
+ * This method is called by the parser and should route the call to the
+ * proper handler for the tag.
+ *
+ * @param t the HTML.Tag
+ * @param a the attribute set
+ * @param pos the position at which the tag was encountered
+ */
+ public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos)
+ {
+ if (t == insertTag)
+ insertTagEncountered = true;
+
+ if (shouldInsert())
+ {
+ TagAction action = (TagAction) tagToAction.get(t);
+ if (action != null)
+ action.start(t, a);
+ }
+ }
+
+ /**
+ * This method called by parser to handle a comment block.
+ *
+ * @param data the comment
+ * @param pos the position at which the comment was encountered
+ */
+ public void handleComment(char[] data, int pos)
+ {
+ if (shouldInsert())
+ {
+ TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT);
+ if (action != null)
+ {
+ action.start(HTML.Tag.COMMENT, new SimpleAttributeSet());
+ action.end(HTML.Tag.COMMENT);
+ }
+ }
+ }
+
+ /**
+ * This method is called by the parser and should route the call to the
+ * proper handler for the tag.
+ *
+ * @param t the HTML.Tag
+ * @param pos the position at which the tag was encountered
+ */
+ public void handleEndTag(HTML.Tag t, int pos)
+ {
+ if (shouldInsert())
+ {
+ // If this is the </html> tag we need to stop calling the Actions
+ if (t == HTML.Tag.HTML)
+ endHTMLEncountered = true;
+
+ TagAction action = (TagAction) tagToAction.get(t);
+ if (action != null)
+ action.end(t);
+ }
+ }
+
+ /**
+ * This is a callback from the parser that should be routed to the
+ * appropriate handler for the tag.
+ *
+ * @param t the HTML.Tag that was encountered
+ * @param a the attribute set
+ * @param pos the position at which the tag was encountered
+ */
+ public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos)
+ {
+ if (t == insertTag)
+ insertTagEncountered = true;
+
+ if (shouldInsert())
+ {
+ TagAction action = (TagAction) tagToAction.get(t);
+ if (action != null)
+ {
+ action.start(t, a);
+ action.end(t);
+ }
+ }
+ }
+
+ /**
+ * This is invoked after the stream has been parsed but before it has been
+ * flushed.
+ *
+ * @param eol one of \n, \r, or \r\n, whichever was encountered the most in
+ * parsing the stream
+ * @since 1.3
+ */
+ public void handleEndOfLineString(String eol)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * Adds the given text to the textarea document. Called only when we are
+ * within a textarea.
+ *
+ * @param data the text to add to the textarea
+ */
+ protected void textAreaContent(char[] data)
+ {
+ try
+ {
+ int offset = textAreaDocument.getLength();
+ String text = new String(data);
+ textAreaDocument.setInitialText(text);
+ textAreaDocument.insertString(offset, text, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // Must not happen as we insert at a model location that we
+ // got from the document itself.
+ assert false;
+ }
+ }
+
+ /**
+ * Adds the given text that was encountered in a <PRE> element.
+ * This adds synthesized lines to hold the text runs.
+ *
+ * @param data the text
+ */
+ protected void preContent(char[] data)
+ {
+ int start = 0;
+ for (int i = 0; i < data.length; i++)
+ {
+ if (data[i] == '\n')
+ {
+ addContent(data, start, i - start + 1);
+ blockClose(HTML.Tag.IMPLIED);
+ MutableAttributeSet atts = new SimpleAttributeSet();
+ atts.addAttribute(CSS.Attribute.WHITE_SPACE, "pre");
+ blockOpen(HTML.Tag.IMPLIED, atts);
+ start = i + 1;
+ }
+ }
+ if (start < data.length)
+ {
+ // Add remaining last line.
+ addContent(data, start, data.length - start);
+ }
+ }
+
+ /**
+ * Instructs the parse buffer to create a block element with the given
+ * attributes.
+ *
+ * @param t the tag that requires opening a new block
+ * @param attr the attribute set for the new block
+ */
+ protected void blockOpen(HTML.Tag t, MutableAttributeSet attr)
+ {
+ if (inImpliedParagraph())
+ blockClose(HTML.Tag.IMPLIED);
+
+ // Push the new tag on top of the stack.
+ parseStack.push(t);
+
+ DefaultStyledDocument.ElementSpec element;
+
+ AbstractDocument.AttributeContext ctx = getAttributeContext();
+ AttributeSet copy = attr.copyAttributes();
+ copy = ctx.addAttribute(copy, StyleConstants.NameAttribute, t);
+ element = new DefaultStyledDocument.ElementSpec(copy,
+ DefaultStyledDocument.ElementSpec.StartTagType);
+ parseBuffer.addElement(element);
+ }
+
+ /**
+ * Returns true when we are currently inside a paragraph, either
+ * a real one or an implied, false otherwise.
+ *
+ * @return
+ */
+ private boolean inParagraph()
+ {
+ boolean inParagraph = false;
+ if (! parseStack.isEmpty())
+ {
+ HTML.Tag top = parseStack.peek();
+ inParagraph = top == HTML.Tag.P || top == HTML.Tag.IMPLIED;
+ }
+ return inParagraph;
+ }
+
+ private boolean inImpliedParagraph()
+ {
+ boolean inParagraph = false;
+ if (! parseStack.isEmpty())
+ {
+ HTML.Tag top = parseStack.peek();
+ inParagraph = top == HTML.Tag.IMPLIED;
+ }
+ return inParagraph;
+ }
+
+ /**
+ * Instructs the parse buffer to close the block element associated with
+ * the given HTML.Tag
+ *
+ * @param t the HTML.Tag that is closing its block
+ */
+ protected void blockClose(HTML.Tag t)
+ {
+ DefaultStyledDocument.ElementSpec element;
+
+ if (inImpliedParagraph() && t != HTML.Tag.IMPLIED)
+ blockClose(HTML.Tag.IMPLIED);
+
+ // Pull the token from the stack.
+ if (! parseStack.isEmpty()) // Just to be sure.
+ parseStack.pop();
+
+ // If the previous tag is a start tag then we insert a synthetic
+ // content tag.
+ DefaultStyledDocument.ElementSpec prev;
+ prev = parseBuffer.size() > 0 ? (DefaultStyledDocument.ElementSpec)
+ parseBuffer.get(parseBuffer.size() - 1) : null;
+ if (prev != null &&
+ prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType)
+ {
+ addContent(new char[]{' '}, 0, 1);
+ }
+
+ element = new DefaultStyledDocument.ElementSpec(null,
+ DefaultStyledDocument.ElementSpec.EndTagType);
+ parseBuffer.addElement(element);
+ }
+
+ /**
+ * Adds text to the appropriate context using the current character
+ * attribute set.
+ *
+ * @param data the text to add
+ * @param offs the offset at which to add it
+ * @param length the length of the text to add
+ */
+ protected void addContent(char[] data, int offs, int length)
+ {
+ addContent(data, offs, length, true);
+ }
+
+ /**
+ * Adds text to the appropriate context using the current character
+ * attribute set, and possibly generating an IMPLIED Tag if necessary.
+ *
+ * @param data the text to add
+ * @param offs the offset at which to add it
+ * @param length the length of the text to add
+ * @param generateImpliedPIfNecessary whether or not we should generate
+ * an HTML.Tag.IMPLIED tag if necessary
+ */
+ protected void addContent(char[] data, int offs, int length,
+ boolean generateImpliedPIfNecessary)
+ {
+ if (generateImpliedPIfNecessary && ! inParagraph())
+ {
+ blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
+ }
+
+ AbstractDocument.AttributeContext ctx = getAttributeContext();
+ DefaultStyledDocument.ElementSpec element;
+ AttributeSet attributes = null;
+
+ // Copy the attribute set, don't use the same object because
+ // it may change
+ if (charAttr != null)
+ attributes = charAttr.copyAttributes();
+ else
+ attributes = ctx.getEmptySet();
+ attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute,
+ HTML.Tag.CONTENT);
+ element = new DefaultStyledDocument.ElementSpec(attributes,
+ DefaultStyledDocument.ElementSpec.ContentType,
+ data, offs, length);
+
+ // Add the element to the buffer
+ parseBuffer.addElement(element);
+
+ if (parseBuffer.size() > threshold)
+ {
+ if (threshold <= MAX_THRESHOLD)
+ threshold *= GROW_THRESHOLD;
+ try
+ {
+ flushImpl();
+ }
+ catch (BadLocationException ble)
+ {
+ // TODO: what to do here?
+ }
+ }
+ }
+
+ /**
+ * Adds content that is specified in the attribute set.
+ *
+ * @param t the HTML.Tag
+ * @param a the attribute set specifying the special content
+ */
+ protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a)
+ {
+ if (t != HTML.Tag.FRAME && ! inParagraph())
+ {
+ blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
+ }
+
+ a.addAttribute(StyleConstants.NameAttribute, t);
+
+ // The two spaces are required because some special elements like HR
+ // must be broken. At least two characters are needed to break into the
+ // two parts.
+ DefaultStyledDocument.ElementSpec spec =
+ new DefaultStyledDocument.ElementSpec(a.copyAttributes(),
+ DefaultStyledDocument.ElementSpec.ContentType,
+ new char[] {' '}, 0, 1 );
+ parseBuffer.add(spec);
+ }
+
+ }
+
+ /**
+ * Gets the reader for the parser to use when loading the document with HTML.
+ *
+ * @param pos - the starting position
+ * @return - the reader
+ */
+ public HTMLEditorKit.ParserCallback getReader(int pos)
+ {
+ return new HTMLReader(pos);
+ }
+
+ /**
+ * Gets the reader for the parser to use when loading the document with HTML.
+ *
+ * @param pos - the starting position
+ * @param popDepth - the number of EndTagTypes to generate before inserting
+ * @param pushDepth - the number of StartTagTypes with a direction
+ * of JoinNextDirection that should be generated before inserting,
+ * but after the end tags have been generated.
+ * @param insertTag - the first tag to start inserting into document
+ * @return - the reader
+ */
+ public HTMLEditorKit.ParserCallback getReader(int pos,
+ int popDepth,
+ int pushDepth,
+ HTML.Tag insertTag)
+ {
+ return new HTMLReader(pos, popDepth, pushDepth, insertTag);
+ }
+
+ /**
+ * Gets the reader for the parser to use when inserting the HTML fragment into
+ * the document. Checks if the parser is present, sets the parent in the
+ * element stack and removes any actions for BODY (it can be only one body in
+ * a HTMLDocument).
+ *
+ * @param pos - the starting position
+ * @param popDepth - the number of EndTagTypes to generate before inserting
+ * @param pushDepth - the number of StartTagTypes with a direction of
+ * JoinNextDirection that should be generated before inserting, but
+ * after the end tags have been generated.
+ * @param insertTag - the first tag to start inserting into document
+ * @param parent the element that will be the parent in the document. HTML
+ * parsing includes checks for the parent, so it must be available.
+ * @return - the reader
+ * @throws IllegalStateException if the parsert is not set.
+ */
+ public HTMLEditorKit.ParserCallback getInsertingReader(int pos, int popDepth,
+ int pushDepth,
+ HTML.Tag insertTag,
+ final Element parent)
+ throws IllegalStateException
+ {
+ if (parser == null)
+ throw new IllegalStateException("Parser has not been set");
+
+ HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth, insertTag)
+ {
+ /**
+ * Ignore BODY.
+ */
+ public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos)
+ {
+ if (t != HTML.Tag.BODY)
+ super.handleStartTag(t, a, pos);
+ }
+
+ /**
+ * Ignore BODY.
+ */
+ public void handleEndTag(HTML.Tag t, int pos)
+ {
+ if (t != HTML.Tag.BODY)
+ super.handleEndTag(t, pos);
+ }
+ };
+
+ return reader;
+ }
+
+ /**
+ * Gets the child element that contains the attribute with the value or null.
+ * Not thread-safe.
+ *
+ * @param e - the element to begin search at
+ * @param attribute - the desired attribute
+ * @param value - the desired value
+ * @return the element found with the attribute and value specified or null if
+ * it is not found.
+ */
+ public Element getElement(Element e, Object attribute, Object value)
+ {
+ if (e != null)
+ {
+ if (e.getAttributes().containsAttribute(attribute, value))
+ return e;
+
+ int count = e.getElementCount();
+ for (int j = 0; j < count; j++)
+ {
+ Element child = e.getElement(j);
+ if (child.getAttributes().containsAttribute(attribute, value))
+ return child;
+
+ Element grandChild = getElement(child, attribute, value);
+ if (grandChild != null)
+ return grandChild;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the element that has the given id Attribute (for instance, &lt;p id
+ * ='my paragraph &gt;'). If it is not found, null is returned. The HTML tag,
+ * having this attribute, is not checked by this method and can be any. The
+ * method is not thread-safe.
+ *
+ * @param attrId - the value of the attribute id to look for
+ * @return the element that has the given id.
+ */
+ public Element getElement(String attrId)
+ {
+ return getElement(getDefaultRootElement(), HTML.Attribute.ID,
+ attrId);
+ }
+
+ /**
+ * Replaces the children of the given element with the contents of
+ * the string. The document must have an HTMLEditorKit.Parser set.
+ * This will be seen as at least two events, n inserts followed by a remove.
+ *
+ * @param elem - the brance element whose children will be replaced
+ * @param htmlText - the string to be parsed and assigned to element.
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalArgumentException - if elem is a leaf
+ * @throws IllegalStateException - if an HTMLEditorKit.Parser has not been set
+ */
+ public void setInnerHTML(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ if (elem.isLeaf())
+ throw new IllegalArgumentException("Element is a leaf");
+
+ int start = elem.getStartOffset();
+ int end = elem.getEndOffset();
+
+ HTMLEditorKit.ParserCallback reader = getInsertingReader(
+ end, 0, 0, HTML.Tag.BODY, elem);
+
+ // TODO charset
+ getParser().parse(new StringReader(htmlText), reader, true);
+
+ // Remove the previous content
+ remove(start, end - start);
+ }
+
+ /**
+ * Replaces the given element in the parent with the string. When replacing a
+ * leaf, this will attempt to make sure there is a newline present if one is
+ * needed. This may result in an additional element being inserted. This will
+ * be seen as at least two events, n inserts followed by a remove. The
+ * HTMLEditorKit.Parser must be set.
+ *
+ * @param elem - the branch element whose parent will be replaced
+ * @param htmlText - the string to be parsed and assigned to elem
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser is not set
+ */
+public void setOuterHTML(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ // Remove the current element:
+ int start = elem.getStartOffset();
+ int end = elem.getEndOffset();
+
+ remove(start, end-start);
+
+ HTMLEditorKit.ParserCallback reader = getInsertingReader(
+ start, 0, 0, HTML.Tag.BODY, elem);
+
+ // TODO charset
+ getParser().parse(new StringReader(htmlText), reader, true);
+ }
+
+ /**
+ * Inserts the string before the start of the given element. The parser must
+ * be set.
+ *
+ * @param elem - the element to be the root for the new text.
+ * @param htmlText - the string to be parsed and assigned to elem
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser has not been set
+ */
+ public void insertBeforeStart(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ HTMLEditorKit.ParserCallback reader = getInsertingReader(
+ elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem);
+
+ // TODO charset
+ getParser().parse(new StringReader(htmlText), reader, true);
+ }
+
+ /**
+ * Inserts the string at the end of the element. If elem's children are
+ * leaves, and the character at elem.getEndOffset() - 1 is a newline, then it
+ * will be inserted before the newline. The parser must be set.
+ *
+ * @param elem - the element to be the root for the new text
+ * @param htmlText - the text to insert
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser is not set
+ */
+ public void insertBeforeEnd(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ HTMLEditorKit.ParserCallback reader = getInsertingReader(
+ elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem);
+
+ // TODO charset
+ getParser().parse(new StringReader(htmlText), reader, true);
+
+ }
+
+ /**
+ * Inserts the string after the end of the given element.
+ * The parser must be set.
+ *
+ * @param elem - the element to be the root for the new text
+ * @param htmlText - the text to insert
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser is not set
+ */
+ public void insertAfterEnd(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ HTMLEditorKit.ParserCallback reader = getInsertingReader(
+ elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem);
+
+ // TODO charset
+ getParser().parse(new StringReader(htmlText), reader, true);
+ }
+
+ /**
+ * Inserts the string at the start of the element.
+ * The parser must be set.
+ *
+ * @param elem - the element to be the root for the new text
+ * @param htmlText - the text to insert
+ * @throws BadLocationException
+ * @throws IOException
+ * @throws IllegalStateException - if parser is not set
+ */
+ public void insertAfterStart(Element elem, String htmlText)
+ throws BadLocationException, IOException
+ {
+ HTMLEditorKit.ParserCallback reader = getInsertingReader(
+ elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem);
+
+ // TODO charset
+ getParser().parse(new StringReader(htmlText), reader, true);
+ }
+
+ /**
+ * Overridden to tag content with the synthetic HTML.Tag.CONTENT
+ * tag.
+ */
+ protected void insertUpdate(DefaultDocumentEvent evt, AttributeSet att)
+ {
+ if (att == null)
+ {
+ SimpleAttributeSet sas = new SimpleAttributeSet();
+ sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
+ att = sas;
+ }
+ super.insertUpdate(evt, att);
+ }
+
+ /**
+ * Returns <code>true</code> when this document is inside a frame,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> when this document is inside a frame,
+ * <code>false</code> otherwise
+ */
+ boolean isFrameDocument()
+ {
+ return frameDocument;
+ }
+
+ /**
+ * Set <code>true</code> when this document is inside a frame,
+ * <code>false</code> otherwise.
+ *
+ * @param frameDoc <code>true</code> when this document is inside a frame,
+ * <code>false</code> otherwise
+ */
+ void setFrameDocument(boolean frameDoc)
+ {
+ frameDocument = frameDoc;
+ }
+
+ /**
+ * Returns the target that is specified in the base tag, if this is the case.
+ *
+ * @return the target that is specified in the base tag, if this is the case
+ */
+ String getBaseTarget()
+ {
+ return baseTarget;
+ }
+
+ /**
+ * Updates the A tag's pseudo class value in response to a hyperlink
+ * action.
+ *
+ * @param el the corresponding element
+ * @param value the new value
+ */
+ void updateSpecialClass(Element el, HTML.Attribute cl, String value)
+ {
+ try
+ {
+ writeLock();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(el.getStartOffset(), 1,
+ DocumentEvent.EventType.CHANGE);
+ AttributeSet elAtts = el.getAttributes();
+ AttributeSet anchorAtts = (AttributeSet) elAtts.getAttribute(HTML.Tag.A);
+ if (anchorAtts != null)
+ {
+ AttributeSet copy = elAtts.copyAttributes();
+ StyleSheet ss = getStyleSheet();
+ if (value != null)
+ {
+ anchorAtts = ss.addAttribute(anchorAtts, cl, value);
+ }
+ else
+ {
+ anchorAtts = ss.removeAttribute(anchorAtts, cl);
+ }
+ MutableAttributeSet matts = (MutableAttributeSet) elAtts;
+ ev.addEdit(new AttributeUndoableEdit(el, copy, false));
+ matts.removeAttribute(HTML.Tag.A);
+ matts.addAttribute(HTML.Tag.A, anchorAtts);
+ ev.end();
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ }
+ finally
+ {
+ writeUnlock();
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
new file mode 100644
index 000000000..e3505d3c2
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
@@ -0,0 +1,1520 @@
+/* HTMLEditorKit.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.awt.Cursor;
+import java.awt.Point;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+import javax.swing.Action;
+import javax.swing.JEditorPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.TextAction;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+import javax.swing.text.html.parser.ParserDelegator;
+
+/* Move these imports here after javax.swing.text.html to make it compile
+ with jikes. */
+import gnu.javax.swing.text.html.parser.GnuParserDelegator;
+import gnu.javax.swing.text.html.parser.HTML_401F;
+
+/**
+ * @author Lillian Angel (langel at redhat dot com)
+ */
+public class HTMLEditorKit
+ extends StyledEditorKit
+ implements Serializable, Cloneable, Accessible
+{
+
+ /**
+ * Fires the hyperlink events on the associated component
+ * when needed.
+ */
+ public static class LinkController
+ extends MouseAdapter
+ implements MouseMotionListener, Serializable
+ {
+
+ /**
+ * The element of the last anchor tag.
+ */
+ private Element lastAnchorElement;
+
+ /**
+ * Constructor
+ */
+ public LinkController()
+ {
+ super();
+ }
+
+ /**
+ * Dispatched when the mouse is clicked. If the component
+ * is read-only, then the clicked event is used to drive an
+ * attempt to follow the reference specified by a link
+ *
+ * @param e - the mouse event
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ JEditorPane editor = (JEditorPane) e.getSource();
+ if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e))
+ {
+ Point loc = e.getPoint();
+ int pos = editor.viewToModel(loc);
+ if (pos >= 0)
+ activateLink(pos, editor, e.getX(), e.getY());
+ }
+ }
+
+ /**
+ * Dispatched when the mouse is dragged on a component.
+ *
+ * @param e - the mouse event.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Dispatched when the mouse cursor has moved into the component.
+ *
+ * @param e - the mouse event.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ JEditorPane editor = (JEditorPane) e.getSource();
+ HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
+ if (! editor.isEditable())
+ {
+ Document doc = editor.getDocument();
+ if (doc instanceof HTMLDocument)
+ {
+ Cursor newCursor = kit.getDefaultCursor();
+ HTMLDocument htmlDoc = (HTMLDocument) doc;
+ Point loc = e.getPoint();
+ int pos = editor.viewToModel(loc);
+ Element el = htmlDoc.getCharacterElement(pos);
+ if (pos < el.getStartOffset() || pos >= el.getEndOffset())
+ el = null;
+ if (el != null)
+ {
+ AttributeSet aAtts = (AttributeSet)
+ el.getAttributes().getAttribute(HTML.Tag.A);
+ if (aAtts != null)
+ {
+ if (el != lastAnchorElement)
+ {
+ if (lastAnchorElement != null)
+ htmlDoc.updateSpecialClass(lastAnchorElement,
+ HTML.Attribute.DYNAMIC_CLASS,
+ null);
+ lastAnchorElement = el;
+ htmlDoc.updateSpecialClass(el,
+ HTML.Attribute.DYNAMIC_CLASS,
+ "hover");
+ }
+ newCursor = kit.getLinkCursor();
+ }
+ else
+ {
+ if (lastAnchorElement != null)
+ htmlDoc.updateSpecialClass(lastAnchorElement,
+ HTML.Attribute.DYNAMIC_CLASS,
+ null);
+ lastAnchorElement = null;
+ }
+ }
+ else
+ {
+ if (lastAnchorElement != null)
+ htmlDoc.updateSpecialClass(lastAnchorElement,
+ HTML.Attribute.DYNAMIC_CLASS,
+ null);
+ lastAnchorElement = null;
+ }
+ if (editor.getCursor() != newCursor)
+ {
+ editor.setCursor(newCursor);
+ }
+ }
+ }
+ }
+
+ /**
+ * If the given position represents a link, then linkActivated is called
+ * on the JEditorPane.
+ *
+ * @param pos the position
+ * @param editor the editor pane
+ */
+ protected void activateLink(int pos, JEditorPane editor)
+ {
+ activateLink(pos, editor);
+ }
+
+ private void activateLink(int pos, JEditorPane editor, int x, int y)
+ {
+ // TODO: This is here for future extension for mapped links support.
+ // For the time beeing we implement simple hyperlinks.
+ Document doc = editor.getDocument();
+ if (doc instanceof HTMLDocument)
+ {
+ HTMLDocument htmlDoc = (HTMLDocument) doc;
+ Element el = htmlDoc.getCharacterElement(pos);
+ AttributeSet atts = el.getAttributes();
+ AttributeSet anchorAtts =
+ (AttributeSet) atts.getAttribute(HTML.Tag.A);
+ String href = null;
+ if (anchorAtts != null)
+ {
+ href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF);
+ htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS,
+ "visited");
+ }
+ else
+ {
+ // TODO: Implement link maps here.
+ }
+ HyperlinkEvent event = null;
+ if (href != null)
+ event = createHyperlinkEvent(editor, htmlDoc, href,
+ anchorAtts, el);
+ if (event != null)
+ editor.fireHyperlinkUpdate(event);
+ }
+
+ }
+
+ /**
+ * Creates a HyperlinkEvent for the specified href and anchor if
+ * possible. If for some reason this won't work, return null.
+ *
+ * @param editor the editor
+ * @param doc the document
+ * @param href the href link
+ * @param anchor the anchor
+ * @param el the element
+ *
+ * @return the hyperlink event, or <code>null</code> if we couldn't
+ * create one
+ */
+ private HyperlinkEvent createHyperlinkEvent(JEditorPane editor,
+ HTMLDocument doc,
+ String href,
+ AttributeSet anchor,
+ Element el)
+ {
+ URL url;
+ try
+ {
+ URL base = doc.getBase();
+ url = new URL(base, href);
+
+ }
+ catch (MalformedURLException ex)
+ {
+ url = null;
+ }
+ HyperlinkEvent ev;
+ if (doc.isFrameDocument())
+ {
+ String target = null;
+ if (anchor != null)
+ target = (String) anchor.getAttribute(HTML.Attribute.TARGET);
+ if (target == null || target.equals(""))
+ target = doc.getBaseTarget();
+ if (target == null || target.equals(""))
+ target = "_self";
+ ev = new HTMLFrameHyperlinkEvent(editor,
+ HyperlinkEvent.EventType.ACTIVATED,
+ url, href, el, target);
+ }
+ else
+ {
+ ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED,
+ url, href, el);
+ }
+ return ev;
+ }
+ }
+
+ /**
+ * This class is used to insert a string of HTML into an existing
+ * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
+ * identifies the parent in the document to add the elements to. The second, (addTag),
+ * identifies that the first tag should be added to the document as seen in the string.
+ * The parser will generate all appropriate (opening/closing tags_ even if they are not
+ * in the HTML string passed in.
+ */
+ public static class InsertHTMLTextAction
+ extends HTMLTextAction
+ {
+
+ /**
+ * Tag in HTML to start adding tags from.
+ */
+ protected HTML.Tag addTag;
+
+ /**
+ * Alternate tag in HTML to start adding tags from if parentTag is
+ * not found and alternateParentTag is not found.
+ */
+ protected HTML.Tag alternateAddTag;
+
+ /**
+ * Alternate tag to check if parentTag is not found.
+ */
+ protected HTML.Tag alternateParentTag;
+
+ /**
+ * HTML to insert.
+ */
+ protected String html;
+
+ /**
+ * Tag to check for in the document.
+ */
+ protected HTML.Tag parentTag;
+
+ /**
+ * Initializes all fields.
+ *
+ * @param name - the name of the document.
+ * @param html - the html to insert
+ * @param parentTag - the parent tag to check for
+ * @param addTag - the tag to start adding from
+ */
+ public InsertHTMLTextAction(String name, String html,
+ HTML.Tag parentTag, HTML.Tag addTag)
+ {
+ this(name, html, parentTag, addTag, null, null);
+ }
+
+ /**
+ * Initializes all fields and calls super
+ *
+ * @param name - the name of the document.
+ * @param html - the html to insert
+ * @param parentTag - the parent tag to check for
+ * @param addTag - the tag to start adding from
+ * @param alternateParentTag - the alternate parent tag
+ * @param alternateAddTag - the alternate add tag
+ */
+ public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag,
+ HTML.Tag addTag, HTML.Tag alternateParentTag,
+ HTML.Tag alternateAddTag)
+ {
+ super(name);
+ // Fields are for easy access when the action is applied to an actual
+ // document.
+ this.html = html;
+ this.parentTag = parentTag;
+ this.addTag = addTag;
+ this.alternateParentTag = alternateParentTag;
+ this.alternateAddTag = alternateAddTag;
+ }
+
+ /**
+ * HTMLEditorKit.insertHTML is called. If an exception is
+ * thrown, it is wrapped in a RuntimeException and thrown.
+ *
+ * @param editor - the editor to use to get the editorkit
+ * @param doc -
+ * the Document to insert the HTML into.
+ * @param offset -
+ * where to begin inserting the HTML.
+ * @param html -
+ * the String to insert
+ * @param popDepth -
+ * the number of ElementSpec.EndTagTypes to generate before
+ * inserting
+ * @param pushDepth -
+ * the number of ElementSpec.StartTagTypes with a direction of
+ * ElementSpec.JoinNextDirection that should be generated before
+ * @param addTag -
+ * the first tag to start inserting into document
+ */
+ protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset,
+ String html, int popDepth, int pushDepth,
+ HTML.Tag addTag)
+ {
+ try
+ {
+ super.getHTMLEditorKit(editor).insertHTML(doc, offset, html,
+ popDepth, pushDepth, addTag);
+ }
+ catch (IOException e)
+ {
+ throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e);
+ }
+ catch (BadLocationException ex)
+ {
+ throw (RuntimeException) new RuntimeException("BadLocationException: "
+ + offset).initCause(ex);
+ }
+ }
+
+ /**
+ * Invoked when inserting at a boundary. Determines the number of pops,
+ * and then the number of pushes that need to be performed. The it calls
+ * insertHTML.
+ *
+ * @param editor -
+ * the editor to use to get the editorkit
+ * @param doc -
+ * the Document to insert the HTML into.
+ * @param offset -
+ * where to begin inserting the HTML.
+ * @param insertElement -
+ * the element to insert
+ * @param html -
+ * the html to insert
+ * @param parentTag -
+ * the parent tag
+ * @param addTag -
+ * the first tag
+ */
+ protected void insertAtBoundary(JEditorPane editor,
+ HTMLDocument doc, int offset,
+ Element insertElement,
+ String html, HTML.Tag parentTag,
+ HTML.Tag addTag)
+ {
+ insertAtBoundry(editor, doc, offset, insertElement,
+ html, parentTag, addTag);
+ }
+
+ /**
+ * Invoked when inserting at a boundary. Determines the number of pops,
+ * and then the number of pushes that need to be performed. The it calls
+ * insertHTML.
+ *
+ * @param editor - the editor to use to get the editorkit
+ * @param doc -
+ * the Document to insert the HTML into.
+ * @param offset -
+ * where to begin inserting the HTML.
+ * @param insertElement - the element to insert
+ * @param html - the html to insert
+ * @param parentTag - the parent tag
+ * @param addTag - the first tag
+ *
+ * @deprecated as of v1.3, use insertAtBoundary
+ */
+ protected void insertAtBoundry(JEditorPane editor,
+ HTMLDocument doc,
+ int offset, Element insertElement,
+ String html, HTML.Tag parentTag,
+ HTML.Tag addTag)
+ {
+ Element parent = insertElement;
+ Element el;
+ // Find common parent element.
+ if (offset > 0 || insertElement == null)
+ {
+ el = doc.getDefaultRootElement();
+ while (el != null && el.getStartOffset() != offset
+ && ! el.isLeaf())
+ el = el.getElement(el.getElementIndex(offset));
+ parent = el != null ? el.getParentElement() : null;
+ }
+ if (parent != null)
+ {
+ int pops = 0;
+ int pushes = 0;
+ if (offset == 0 && insertElement != null)
+ {
+ el = parent;
+ while (el != null && ! el.isLeaf())
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pops++;
+ }
+ }
+ else
+ {
+ el = parent;
+ offset--;
+ while (el != null && ! el.isLeaf())
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pops++;
+ }
+ el = parent;
+ offset++;
+ while (el != null && el != insertElement)
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pushes++;
+ }
+ }
+ pops = Math.max(0, pops - 1);
+ insertHTML(editor, doc, offset, html, pops, pushes, addTag);
+ }
+ }
+
+ /**
+ * Inserts the HTML.
+ *
+ * @param ae - the action performed
+ */
+ public void actionPerformed(ActionEvent ae)
+ {
+ JEditorPane source = getEditor(ae);
+ if (source != null)
+ {
+ HTMLDocument d = getHTMLDocument(source);
+ int offset = source.getSelectionStart();
+ int length = d.getLength();
+ boolean inserted = true;
+ if (! tryInsert(source, d, offset, parentTag, addTag))
+ {
+ inserted = tryInsert(source, d, offset, alternateParentTag,
+ alternateAddTag);
+ }
+ if (inserted)
+ adjustSelection(source, d, offset, length);
+ }
+ }
+
+ /**
+ * Tries to insert the html chunk to the specified <code>addTag</code>.
+ *
+ * @param pane the editor
+ * @param doc the document
+ * @param offset the offset at which to insert
+ * @param tag the tag at which to insert
+ * @param addTag the add tag
+ *
+ * @return <code>true</code> when the html has been inserted successfully,
+ * <code>false</code> otherwise
+ */
+ private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset,
+ HTML.Tag tag, HTML.Tag addTag)
+ {
+ boolean inserted = false;
+ Element el = findElementMatchingTag(doc, offset, tag);
+ if (el != null && el.getStartOffset() == offset)
+ {
+ insertAtBoundary(pane, doc, offset, el, html, tag, addTag);
+ inserted = true;
+ }
+ else if (offset > 0)
+ {
+ int depth = elementCountToTag(doc, offset - 1, tag);
+ if (depth != -1)
+ {
+ insertHTML(pane, doc, offset, html, depth, 0, addTag);
+ inserted = true;
+ }
+ }
+ return inserted;
+ }
+
+ /**
+ * Adjusts the selection after an insertion has been performed.
+ *
+ * @param pane the editor pane
+ * @param doc the document
+ * @param offset the insert offset
+ * @param oldLen the old document length
+ */
+ private void adjustSelection(JEditorPane pane, HTMLDocument doc,
+ int offset, int oldLen)
+ {
+ int newLen = doc.getLength();
+ if (newLen != oldLen && offset < newLen)
+ {
+ if (offset > 0)
+ {
+ String text;
+ try
+ {
+ text = doc.getText(offset - 1, 1);
+ }
+ catch (BadLocationException ex)
+ {
+ text = null;
+ }
+ if (text != null && text.length() > 0
+ && text.charAt(0) == '\n')
+ {
+ pane.select(offset, offset);
+ }
+ else
+ {
+ pane.select(offset + 1, offset + 1);
+ }
+ }
+ else
+ {
+ pane.select(1, 1);
+ }
+ }
+ }
+ }
+
+ /**
+ * Abstract Action class that helps inserting HTML into an existing document.
+ */
+ public abstract static class HTMLTextAction
+ extends StyledEditorKit.StyledTextAction
+ {
+
+ /**
+ * Constructor
+ */
+ public HTMLTextAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Gets the HTMLDocument from the JEditorPane.
+ *
+ * @param e - the editor pane
+ * @return the html document.
+ */
+ protected HTMLDocument getHTMLDocument(JEditorPane e)
+ {
+ Document d = e.getDocument();
+ if (d instanceof HTMLDocument)
+ return (HTMLDocument) d;
+ throw new IllegalArgumentException("Document is not a HTMLDocument.");
+ }
+
+ /**
+ * Gets the HTMLEditorKit
+ *
+ * @param e - the JEditorPane to get the HTMLEditorKit from.
+ * @return the HTMLEditorKit
+ */
+ protected HTMLEditorKit getHTMLEditorKit(JEditorPane e)
+ {
+ EditorKit d = e.getEditorKit();
+ if (d instanceof HTMLEditorKit)
+ return (HTMLEditorKit) d;
+ throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit.");
+ }
+
+ /**
+ * Returns an array of Elements that contain the offset.
+ * The first elements corresponds to the roots of the doc.
+ *
+ * @param doc - the document to get the Elements from.
+ * @param offset - the offset the Elements must contain
+ * @return an array of all the elements containing the offset.
+ */
+ protected Element[] getElementsAt(HTMLDocument doc,
+ int offset)
+ {
+ return getElementsAt(doc.getDefaultRootElement(), offset, 0);
+ }
+
+ /**
+ * Helper function to get all elements using recursion.
+ */
+ private Element[] getElementsAt(Element root, int offset, int depth)
+ {
+ Element[] elements = null;
+ if (root != null)
+ {
+ if (root.isLeaf())
+ {
+ elements = new Element[depth + 1];
+ elements[depth] = root;
+ return elements;
+ }
+ elements = getElementsAt(root.getElement(root.getElementIndex(offset)),
+ offset, depth + 1);
+ elements[depth] = root;
+ }
+ return elements;
+ }
+
+ /**
+ * Returns the number of elements, starting at the deepest point, needed
+ * to get an element representing tag. -1 if no elements are found, 0 if
+ * the parent of the leaf at offset represents the tag.
+ *
+ * @param doc -
+ * the document to search
+ * @param offset -
+ * the offset to check
+ * @param tag -
+ * the tag to look for
+ * @return - the number of elements needed to get an element representing
+ * tag.
+ */
+ protected int elementCountToTag(HTMLDocument doc,
+ int offset, HTML.Tag tag)
+ {
+ Element root = doc.getDefaultRootElement();
+ int num = -1;
+ Element next = root.getElement(root.getElementIndex(offset));
+
+ while (!next.isLeaf())
+ {
+ num++;
+ if (next.getAttributes().
+ getAttribute(StyleConstants.NameAttribute).equals(tag))
+ return num;
+ next = next.getElement(next.getElementIndex(offset));
+ }
+ return num;
+ }
+
+ /**
+ * Gets the deepest element at offset with the
+ * matching tag.
+ *
+ * @param doc - the document to search
+ * @param offset - the offset to check for
+ * @param tag - the tag to match
+ * @return - the element that is found, null if not found.
+ */
+ protected Element findElementMatchingTag(HTMLDocument doc,
+ int offset, HTML.Tag tag)
+ {
+ Element element = doc.getDefaultRootElement();
+ Element tagElement = null;
+
+ while (element != null)
+ {
+ Object otag = element.getAttributes().getAttribute(
+ StyleConstants.NameAttribute);
+ if (otag instanceof HTML.Tag && otag.equals(tag))
+ tagElement = element;
+ element = element.getElement(element.getElementIndex(offset));
+ }
+
+ return tagElement;
+ }
+ }
+
+ /**
+ * A {@link ViewFactory} that is able to create {@link View}s for
+ * the <code>Element</code>s that are supported.
+ */
+ public static class HTMLFactory
+ implements ViewFactory
+ {
+
+ /**
+ * Constructor
+ */
+ public HTMLFactory()
+ {
+ // Do Nothing here.
+ }
+
+ /**
+ * Creates a {@link View} for the specified <code>Element</code>.
+ *
+ * @param element the <code>Element</code> to create a <code>View</code>
+ * for
+ * @return the <code>View</code> for the specified <code>Element</code>
+ * or <code>null</code> if the type of <code>element</code> is
+ * not supported
+ */
+ public View create(Element element)
+ {
+ View view = null;
+ Object attr =
+ element.getAttributes().getAttribute(StyleConstants.NameAttribute);
+ if (attr instanceof HTML.Tag)
+ {
+ HTML.Tag tag = (HTML.Tag) attr;
+
+ if (tag == HTML.Tag.IMPLIED || tag == HTML.Tag.P
+ || tag == HTML.Tag.H1 || tag == HTML.Tag.H2
+ || tag == HTML.Tag.H3 || tag == HTML.Tag.H4
+ || tag == HTML.Tag.H5 || tag == HTML.Tag.H6
+ || tag == HTML.Tag.DT)
+ view = new ParagraphView(element);
+ else if (tag == HTML.Tag.LI || tag == HTML.Tag.DL
+ || tag == HTML.Tag.DD || tag == HTML.Tag.BODY
+ || tag == HTML.Tag.HTML || tag == HTML.Tag.CENTER
+ || tag == HTML.Tag.DIV
+ || tag == HTML.Tag.BLOCKQUOTE
+ || tag == HTML.Tag.PRE
+ || tag == HTML.Tag.FORM
+ // Misplaced TD and TH tags get mapped as vertical block.
+ // Note that correctly placed tags get mapped in TableView.
+ || tag == HTML.Tag.TD || tag == HTML.Tag.TH)
+ view = new BlockView(element, View.Y_AXIS);
+ else if (tag == HTML.Tag.TR)
+ // Misplaced TR tags get mapped as horizontal blocks.
+ // Note that correctly placed tags get mapped in TableView.
+ view = new BlockView(element, View.X_AXIS);
+ else if (tag == HTML.Tag.IMG)
+ view = new ImageView(element);
+
+ else if (tag == HTML.Tag.CONTENT)
+ view = new InlineView(element);
+ else if (tag == HTML.Tag.HEAD)
+ view = new NullView(element);
+ else if (tag == HTML.Tag.TABLE)
+ view = new javax.swing.text.html.TableView(element);
+ else if (tag == HTML.Tag.HR)
+ view = new HRuleView(element);
+ else if (tag == HTML.Tag.BR)
+ view = new BRView(element);
+ else if (tag == HTML.Tag.INPUT || tag == HTML.Tag.SELECT
+ || tag == HTML.Tag.TEXTAREA)
+ view = new FormView(element);
+
+ else if (tag == HTML.Tag.MENU || tag == HTML.Tag.DIR
+ || tag == HTML.Tag.UL || tag == HTML.Tag.OL)
+ view = new ListView(element);
+ else if (tag == HTML.Tag.FRAMESET)
+ view = new FrameSetView(element);
+ else if (tag == HTML.Tag.FRAME)
+ view = new FrameView(element);
+ else if (tag == HTML.Tag.OBJECT)
+ view = new ObjectView(element);
+ }
+ if (view == null)
+ {
+ view = new NullView(element);
+ }
+ return view;
+ }
+ }
+
+ /**
+ * The abstract HTML parser declaration.
+ */
+ public abstract static class Parser
+ {
+ /**
+ * Parse the HTML text, calling various methods of the provided callback
+ * in response to the occurence of the corresponding HTML constructions.
+ * @param reader The reader to read the source HTML from.
+ * @param callback The callback to receive information about the parsed
+ * HTML structures
+ * @param ignoreCharSet If true, the parser ignores all charset information
+ * that may be present in HTML documents.
+ * @throws IOException, normally if the reader throws one.
+ */
+ public abstract void parse(Reader reader, ParserCallback callback,
+ boolean ignoreCharSet) throws IOException;
+ }
+
+ /**
+ * The "hook" that receives all information about the HTML document
+ * structure while parsing it. The methods are invoked by parser
+ * and should be normally overridden.
+ */
+ public static class ParserCallback
+ {
+ /**
+ * If the tag does not occurs in the html stream directly, but
+ * is supposed by parser, the tag attribute set contains this additional
+ * attribute, having value Boolean.True.
+ */
+ public static final Object IMPLIED = "_implied_";
+
+ /**
+ * Constructor
+ */
+ public ParserCallback()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * The parser calls this method after it finishes parsing the document.
+ */
+ public void flush() throws BadLocationException
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Handle HTML comment, present in the given position.
+ * @param comment the comment
+ * @position the position of the comment in the text being parsed.
+ */
+ public void handleComment(char[] comment, int position)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notifies about the character sequences, used to separate lines in
+ * this document. The parser calls this method after it finishes
+ * parsing the document, but before flush().
+ * @param end_of_line The "end of line sequence", one of: \r or \n or \r\n.
+ */
+ public void handleEndOfLineString(String end_of_line)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * The method is called when the HTML closing tag ((like &lt;/table&gt;)
+ * is found or if the parser concludes that the one should be present
+ * in the current position.
+ * @param tag The tag being handled
+ * @param position the tag position in the text being parsed.
+ */
+ public void handleEndTag(HTML.Tag tag, int position)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Handle the error.
+ * @param message The message, explaining the error.
+ * @param position The starting position of the fragment that has caused
+ * the error in the html document being parsed.
+ */
+ public void handleError(String message, int position)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Handle the tag with no content, like &lt;br&gt;. The method is
+ * called for the elements that, in accordance with the current DTD,
+ * has an empty content.
+ * @param tag The tag being handled.
+ * @param position The tag position in the text being parsed.
+ */
+ public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes,
+ int position)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * The method is called when the HTML opening tag ((like &lt;table&gt;)
+ * is found or if the parser concludes that the one should be present
+ * in the current position.
+ * @param tag The tag being handled
+ * @param position The tag position in the text being parsed
+ */
+ public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes,
+ int position)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Handle the text section.
+ * @param text A section text.
+ * @param position The text position in the HTML document text being parsed.
+ */
+ public void handleText(char[] text, int position)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * Use serialVersionUID (v1.4) for interoperability.
+ */
+ private static final long serialVersionUID = 8751997116710384592L;
+
+ /**
+ * Default cascading stylesheed file ("default.css").
+ */
+ public static final String DEFAULT_CSS = "default.css";
+
+ /**
+ * The <b>bold</b> action identifier.
+ */
+ public static final String BOLD_ACTION = "html-bold-action";
+
+ /**
+ * The <i>italic</i> action identifier.
+ */
+ public static final String ITALIC_ACTION = "html-italic-action";
+
+ /**
+ * The <font color="#FF0000">color</font> action indentifier
+ * (passing the color as an argument).
+ */
+ public static final String COLOR_ACTION = "html-color-action";
+
+ /**
+ * The <font size="+1">increase</font> font action identifier.
+ */
+ public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
+
+ /**
+ * The <font size="-1">decrease</font> font action identifier.
+ */
+ public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
+
+ /**
+ * Align images at the bottom.
+ */
+ public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
+
+ /**
+ * Align images at the middle.
+ */
+ public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
+
+ /**
+ * Align images at the top.
+ */
+ public static final String IMG_ALIGN_TOP = "html-image-align-top";
+
+ /**
+ * Align images at the border.
+ */
+ public static final String IMG_BORDER = "html-image-border";
+
+ /**
+ * The "logical style" action identifier, passing that style as parameter.
+ */
+ public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
+
+ /**
+ * The "ident paragraph left" action.
+ */
+ public static final String PARA_INDENT_LEFT = "html-para-indent-left";
+
+ /**
+ * The "ident paragraph right" action.
+ */
+ public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
+
+ /**
+ * Actions for HTML
+ */
+ private static final Action[] defaultActions =
+ {
+ new InsertHTMLTextAction("InsertTable",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertTableRow",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.TABLE, HTML.Tag.TR,
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertTableCell",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.TR, HTML.Tag.TD,
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertUnorderedList",
+ "<ul><li></li></ul>",
+ HTML.Tag.BODY, HTML.Tag.UL),
+ new InsertHTMLTextAction("InsertUnorderedListItem",
+ "<ul><li></li></ul>",
+ HTML.Tag.UL, HTML.Tag.LI,
+ HTML.Tag.BODY, HTML.Tag.UL),
+ new InsertHTMLTextAction("InsertOrderedList",
+ "<ol><li></li></ol>",
+ HTML.Tag.BODY, HTML.Tag.OL),
+ new InsertHTMLTextAction("InsertOrderedListItem",
+ "<ol><li></li></ol>",
+ HTML.Tag.OL, HTML.Tag.LI,
+ HTML.Tag.BODY, HTML.Tag.OL),
+ new InsertHTMLTextAction("InsertPre",
+ "<pre></pre>", HTML.Tag.BODY, HTML.Tag.PRE)
+ // TODO: The reference impl has an InsertHRAction too.
+ };
+
+ /**
+ * The current style sheet.
+ */
+ private StyleSheet styleSheet;
+
+ /**
+ * The ViewFactory for HTMLFactory.
+ */
+ HTMLFactory viewFactory;
+
+ /**
+ * The Cursor for links.
+ */
+ Cursor linkCursor;
+
+ /**
+ * The default cursor.
+ */
+ Cursor defaultCursor;
+
+ /**
+ * The parser.
+ */
+ Parser parser;
+
+ /**
+ * The mouse listener used for links.
+ */
+ private LinkController linkController;
+
+ /** The content type */
+ String contentType = "text/html";
+
+ /** The input attributes defined by default.css */
+ MutableAttributeSet inputAttributes;
+
+ /** The editor pane used. */
+ JEditorPane editorPane;
+
+ /**
+ * Whether or not the editor kit handles form submissions.
+ *
+ * @see #isAutoFormSubmission()
+ * @see #setAutoFormSubmission(boolean)
+ */
+ private boolean autoFormSubmission;
+
+ /**
+ * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
+ */
+ public HTMLEditorKit()
+ {
+ linkController = new LinkController();
+ autoFormSubmission = true;
+ }
+
+ /**
+ * Gets a factory suitable for producing views of any
+ * models that are produced by this kit.
+ *
+ * @return the view factory suitable for producing views.
+ */
+ public ViewFactory getViewFactory()
+ {
+ if (viewFactory == null)
+ viewFactory = new HTMLFactory();
+ return viewFactory;
+ }
+
+ /**
+ * Create a text storage model for this type of editor.
+ *
+ * @return the model
+ */
+ public Document createDefaultDocument()
+ {
+ // Protect the shared stylesheet.
+ StyleSheet styleSheet = getStyleSheet();
+ StyleSheet ss = new StyleSheet();
+ ss.addStyleSheet(styleSheet);
+
+ HTMLDocument document = new HTMLDocument(ss);
+ document.setParser(getParser());
+ document.setAsynchronousLoadPriority(4);
+ document.setTokenThreshold(100);
+ return document;
+ }
+
+ /**
+ * Get the parser that this editor kit uses for reading HTML streams. This
+ * method can be overridden to use the alternative parser.
+ *
+ * @return the HTML parser (by default, {@link ParserDelegator}).
+ */
+ protected Parser getParser()
+ {
+ if (parser == null)
+ {
+ parser = new GnuParserDelegator(HTML_401F.getInstance());
+ }
+ return parser;
+ }
+
+ /**
+ * Inserts HTML into an existing document.
+ *
+ * @param doc - the Document to insert the HTML into.
+ * @param offset - where to begin inserting the HTML.
+ * @param html - the String to insert
+ * @param popDepth - the number of ElementSpec.EndTagTypes
+ * to generate before inserting
+ * @param pushDepth - the number of ElementSpec.StartTagTypes
+ * with a direction of ElementSpec.JoinNextDirection that
+ * should be generated before
+ * @param insertTag - the first tag to start inserting into document
+ * @throws IOException - on any I/O error
+ * @throws BadLocationException - if pos represents an invalid location
+ * within the document
+ */
+ public void insertHTML(HTMLDocument doc, int offset, String html,
+ int popDepth, int pushDepth, HTML.Tag insertTag)
+ throws BadLocationException, IOException
+ {
+ Parser parser = getParser();
+ if (offset < 0 || offset > doc.getLength())
+ throw new BadLocationException("Bad location", offset);
+ if (parser == null)
+ throw new IOException("Parser is null.");
+
+ ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag);
+
+ // FIXME: What should ignoreCharSet be set to?
+
+ // parser.parse inserts html into the buffer
+ parser.parse(new StringReader(html), pc, false);
+ pc.flush();
+ }
+
+ /**
+ * Inserts content from the given stream. Inserting HTML into a non-empty
+ * document must be inside the body Element, if you do not insert into
+ * the body an exception will be thrown. When inserting into a non-empty
+ * document all tags outside of the body (head, title) will be dropped.
+ *
+ * @param in - the stream to read from
+ * @param doc - the destination for the insertion
+ * @param pos - the location in the document to place the content
+ * @throws IOException - on any I/O error
+ * @throws BadLocationException - if pos represents an invalid location
+ * within the document
+ */
+ public void read(Reader in, Document doc, int pos) throws IOException,
+ BadLocationException
+ {
+ if (doc instanceof HTMLDocument)
+ {
+ Parser parser = getParser();
+ if (pos < 0 || pos > doc.getLength())
+ throw new BadLocationException("Bad location", pos);
+ if (parser == null)
+ throw new IOException("Parser is null.");
+
+ HTMLDocument hd = ((HTMLDocument) doc);
+ if (editorPane != null)
+ hd.setBase(editorPane.getPage());
+ ParserCallback pc = hd.getReader(pos);
+
+ // FIXME: What should ignoreCharSet be set to?
+
+ // parser.parse inserts html into the buffer
+ parser.parse(in, pc, false);
+ pc.flush();
+ }
+ else
+ // read in DefaultEditorKit is called.
+ // the string is inserted in the document as usual.
+ super.read(in, doc, pos);
+ }
+
+ /**
+ * Writes content from a document to the given stream in
+ * an appropriate format.
+ *
+ * @param out - the stream to write to
+ * @param doc - the source for the write
+ * @param pos - the location in the document to get the content.
+ * @param len - the amount to write out
+ * @throws IOException - on any I/O error
+ * @throws BadLocationException - if pos represents an invalid location
+ * within the document
+ */
+ public void write(Writer out, Document doc, int pos, int len)
+ throws IOException, BadLocationException
+ {
+ if (doc instanceof HTMLDocument)
+ {
+ HTMLWriter writer = new HTMLWriter(out, (HTMLDocument) doc, pos, len);
+ writer.write();
+ }
+ else if (doc instanceof StyledDocument)
+ {
+ MinimalHTMLWriter writer = new MinimalHTMLWriter(out,
+ (StyledDocument) doc,
+ pos, len);
+ writer.write();
+ }
+ else
+ super.write(out, doc, pos, len);
+ }
+
+ /**
+ * Gets the content type that the kit supports.
+ * This kit supports the type text/html.
+ *
+ * @returns the content type supported.
+ */
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+ /**
+ * Creates a copy of the editor kit.
+ *
+ * @return a copy of this.
+ */
+ public Object clone()
+ {
+ // FIXME: Need to clone all fields
+ HTMLEditorKit copy = (HTMLEditorKit) super.clone();
+ copy.linkController = new LinkController();
+ return copy;
+ }
+
+ /**
+ * Copies the key/values in elements AttributeSet into set.
+ * This does not copy component, icon, or element names attributes.
+ * This is called anytime the caret moves over a different location.
+ *
+ * @param element - the element to create the input attributes for.
+ * @param set - the set to copy the values into.
+ */
+ protected void createInputAttributes(Element element,
+ MutableAttributeSet set)
+ {
+ set.removeAttributes(set);
+ set.addAttributes(element.getAttributes());
+ // FIXME: Not fully implemented.
+ }
+
+ /**
+ * Called when this is installed into the JEditorPane.
+ *
+ * @param c - the JEditorPane installed into.
+ */
+ public void install(JEditorPane c)
+ {
+ super.install(c);
+ c.addMouseListener(linkController);
+ c.addMouseMotionListener(linkController);
+ editorPane = c;
+ }
+
+ /**
+ * Called when the this is removed from the JEditorPane.
+ * It unregisters any listeners.
+ *
+ * @param c - the JEditorPane being removed from.
+ */
+ public void deinstall(JEditorPane c)
+ {
+ super.deinstall(c);
+ c.removeMouseListener(linkController);
+ c.removeMouseMotionListener(linkController);
+ editorPane = null;
+ }
+
+ /**
+ * Gets the AccessibleContext associated with this.
+ *
+ * @return the AccessibleContext for this.
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ // FIXME: Should return an instance of
+ // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext
+ // Not implemented yet.
+ return null;
+ }
+
+ /**
+ * Gets the action list. This list is supported by the superclass
+ * augmented by the collection of actions defined locally for style
+ * operations.
+ *
+ * @return an array of all the actions
+ */
+ public Action[] getActions()
+ {
+ return TextAction.augmentList(super.getActions(), defaultActions);
+ }
+
+ /**
+ * Returns the default cursor.
+ *
+ * @return the default cursor
+ */
+ public Cursor getDefaultCursor()
+ {
+ if (defaultCursor == null)
+ defaultCursor = Cursor.getDefaultCursor();
+ return defaultCursor;
+ }
+
+ /**
+ * Returns the cursor for links.
+ *
+ * @return the cursor for links.
+ */
+ public Cursor getLinkCursor()
+ {
+ if (linkCursor == null)
+ linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+ return linkCursor;
+ }
+
+ /**
+ * Sets the Cursor for links.
+ *
+ * @param cursor - the new cursor for links.
+ */
+ public void setLinkCursor(Cursor cursor)
+ {
+ linkCursor = cursor;
+ }
+
+ /**
+ * Sets the default cursor.
+ *
+ * @param cursor - the new default cursor.
+ */
+ public void setDefaultCursor(Cursor cursor)
+ {
+ defaultCursor = cursor;
+ }
+
+ /**
+ * Gets the input attributes used for the styled editing actions.
+ *
+ * @return the attribute set
+ */
+ public MutableAttributeSet getInputAttributes()
+ {
+ return inputAttributes;
+ }
+
+ /**
+ * Get the set of styles currently being used to render the HTML elements.
+ * By default the resource specified by DEFAULT_CSS gets loaded, and is
+ * shared by all HTMLEditorKit instances.
+ *
+ * @return the style sheet.
+ */
+ public StyleSheet getStyleSheet()
+ {
+ if (styleSheet == null)
+ {
+ try
+ {
+ styleSheet = new StyleSheet();
+ Class<?> c = HTMLEditorKit.class;
+ InputStream in = c.getResourceAsStream(DEFAULT_CSS);
+ InputStreamReader r = new InputStreamReader(in);
+ styleSheet.loadRules(r, null);
+ r.close();
+ }
+ catch (IOException ex)
+ {
+ throw new RuntimeException("No style available.", ex);
+ }
+ }
+ return styleSheet;
+ }
+
+ /**
+ * Set the set of styles to be used to render the various HTML elements.
+ * These styles are specified in terms of CSS specifications. Each document
+ * produced by the kit will have a copy of the sheet which it can add the
+ * document specific styles to. By default, the StyleSheet specified is shared
+ * by all HTMLEditorKit instances.
+ *
+ * @param s - the new style sheet
+ */
+ public void setStyleSheet(StyleSheet s)
+ {
+ styleSheet = s;
+ }
+
+ /**
+ * Returns <code>true</code> when forms should be automatically submitted
+ * by the editor kit. Set this to <code>false</code> when you want to
+ * intercept form submission. In this case you'd want to listen for
+ * hyperlink events on the document and handle FormSubmitEvents specially.
+ *
+ * The default is <code>true</code>.
+ *
+ * @return <code>true</code> when forms should be automatically submitted
+ * by the editor kit, <code>false</code> otherwise
+ *
+ * @since 1.5
+ *
+ * @see #setAutoFormSubmission(boolean)
+ * @see FormSubmitEvent
+ */
+ public boolean isAutoFormSubmission()
+ {
+ return autoFormSubmission;
+ }
+
+ /**
+ * Sets whether or not the editor kit should automatically submit forms.
+ *
+ * @param auto <code>true</code> when the editor kit should handle form
+ * submission, <code>false</code> otherwise
+ *
+ * @since 1.5
+ *
+ * @see #isAutoFormSubmission()
+ */
+ public void setAutoFormSubmission(boolean auto)
+ {
+ autoFormSubmission = auto;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java
new file mode 100644
index 000000000..e146965d7
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java
@@ -0,0 +1,130 @@
+/* HTMLFrameHyperlinkEvent.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.net.URL;
+
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.Element;
+
+/**
+ * HTMLFrameHyperlinkEvent transfers information about the link that was
+ * activated in a frame.
+ *
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public class HTMLFrameHyperlinkEvent extends HyperlinkEvent
+{
+ private final String target_frame;
+
+ /**
+ * Creates a new hypertext link event.
+ *
+ * @param source The object this link is associated to.
+ * @param type The type of event.
+ * @param url The URL this link pointing too.
+ * @param element The element in the document representing the anchor.
+ * @param frame - the Frame to display the document in.
+ */
+ public HTMLFrameHyperlinkEvent(Object source, EventType type, URL url,
+ Element element, String frame)
+ {
+ super(source, type, url, frame, element);
+ target_frame = frame;
+ }
+
+ /**
+ * Creates a new hypertext link event.
+ *
+ * @param source The object this link is associated to.
+ * @param type The type of event.
+ * @param url The URL this link pointing too.
+ * @param frame - the Frame to display the document in.
+ */
+ public HTMLFrameHyperlinkEvent(Object source, EventType type, URL url,
+ String frame)
+ {
+ super(source, type, url, frame);
+ target_frame = frame;
+ }
+
+ /**
+ * Creates a new hypertext link event.
+ *
+ * @param source The object this link is associated to.
+ * @param type The type of event.
+ * @param url The URL this link pointing too.
+ * @param description The description for this link.
+ * @param element The element in the document representing the anchor.
+ * @param frame - the Frame to display the document in.
+ */
+ public HTMLFrameHyperlinkEvent(Object source, EventType type, URL url,
+ String description, Element element,
+ String frame)
+ {
+ super(source, type, url, description, element);
+ target_frame = frame;
+ }
+
+ /**
+ * Creates a new hypertext link event.
+ *
+ * @param source The object this link is associated to.
+ * @param type The type of event.
+ * @param url The URL this link pointing too.
+ * @param description The description for this link.
+ * @param frame - the Frame to display the document in.
+ */
+ public HTMLFrameHyperlinkEvent(Object source, EventType type, URL url,
+ String description, String frame)
+ {
+ super(source, type, url, description);
+ target_frame = frame;
+ }
+
+ /**
+ * Gets the string, passed as the target frame identifier.
+ *
+ * @return the target for the link.
+ */
+ public String getTarget()
+ {
+ return target_frame;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLWriter.java b/libjava/classpath/javax/swing/text/html/HTMLWriter.java
new file mode 100644
index 000000000..55d25ccbb
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTMLWriter.java
@@ -0,0 +1,1088 @@
+/* HTMLWriter.java --
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.Enumeration;
+import java.util.HashSet;
+
+import javax.swing.ComboBoxModel;
+
+import javax.swing.text.AbstractWriter;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.Option;
+
+/**
+ * HTMLWriter,
+ * A Writer for HTMLDocuments.
+ *
+ * @author David Fu (fchoong at netbeans.jp)
+ */
+
+public class HTMLWriter
+ extends AbstractWriter
+{
+ /**
+ * We keep a reference of the writer passed by the construct.
+ */
+ private Writer outWriter = null;
+
+ /**
+ * We keep a reference of the HTMLDocument passed by the construct.
+ */
+ private HTMLDocument htmlDoc = null;
+
+ /**
+ * Used to keep track of which embedded has been written out.
+ */
+ private HashSet<HTML.Tag> openEmbeddedTagHashSet = null;
+
+ private String new_line_str = "" + NEWLINE;
+
+ private char[] html_entity_char_arr = {'<', '>', '&', '"'};
+
+ private String[] html_entity_escape_str_arr = {"&lt;", "&gt;", "&amp;",
+ "&quot;"};
+
+ // variables used to output Html Fragment
+ private int doc_pos = -1;
+ private int doc_len = -1;
+ private int doc_offset_remaining = -1;
+ private int doc_len_remaining = -1;
+ private HashSet<Element> htmlFragmentParentHashSet = null;
+ private Element startElem = null;
+ private Element endElem = null;
+ private boolean fg_pass_start_elem = false;
+ private boolean fg_pass_end_elem = false;
+
+ /**
+ * Constructs a HTMLWriter.
+ *
+ * @param writer writer to write output to
+ * @param doc the HTMLDocument to output
+ */
+ public HTMLWriter(Writer writer, HTMLDocument doc)
+ {
+ super(writer, doc);
+ outWriter = writer;
+ htmlDoc = doc;
+ openEmbeddedTagHashSet = new HashSet<HTML.Tag>();
+ } // public HTMLWriter(Writer writer, HTMLDocument doc)
+
+ /**
+ * Constructs a HTMLWriter which outputs a Html Fragment.
+ *
+ * @param writer <code>Writer</code> to write output to
+ * @param doc the <code>javax.swing.text.html.HTMLDocument</code>
+ * to output
+ * @param pos position to start outputing the document
+ * @param len amount to output the document
+ */
+ public HTMLWriter(Writer writer, HTMLDocument doc, int pos, int len)
+ {
+ super(writer, doc, pos, len);
+ outWriter = writer;
+ htmlDoc = doc;
+ openEmbeddedTagHashSet = new HashSet<HTML.Tag>();
+
+ doc_pos = pos;
+ doc_offset_remaining = pos;
+ doc_len = len;
+ doc_len_remaining = len;
+ htmlFragmentParentHashSet = new HashSet<Element>();
+ } // public HTMLWriter(Writer writer, HTMLDocument doc, int pos, int len)
+
+ /**
+ * Call this method to start outputing HTML.
+ *
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ public void write()
+ throws IOException, BadLocationException
+ {
+ Element rootElem = htmlDoc.getDefaultRootElement();
+
+ if (doc_pos == -1 && doc_len == -1)
+ {
+ // Normal traversal.
+ traverse(rootElem);
+ } // if(doc_pos == -1 && doc_len == -1)
+ else
+ {
+ // Html fragment traversal.
+ if (doc_pos == -1 || doc_len == -1)
+ throw new BadLocationException("Bad Location("
+ + doc_pos + ", " + doc_len + ")", doc_pos);
+
+ startElem = htmlDoc.getCharacterElement(doc_pos);
+
+ int start_offset = startElem.getStartOffset();
+
+ // Positions before start_offset will not be traversed, and thus
+ // will not be counted.
+ if (start_offset > 0)
+ doc_offset_remaining = doc_offset_remaining - start_offset;
+
+ Element tempParentElem = startElem;
+
+ while ((tempParentElem = tempParentElem.getParentElement()) != null)
+ {
+ if (!htmlFragmentParentHashSet.contains(tempParentElem))
+ htmlFragmentParentHashSet.add(tempParentElem);
+ } // while((tempParentElem = tempParentElem.getParentElement())
+ // != null)
+
+ // NOTE: 20061030 - fchoong - the last index should not be included.
+ endElem = htmlDoc.getCharacterElement(doc_pos + doc_len - 1);
+
+ tempParentElem = endElem;
+
+ while ((tempParentElem = tempParentElem.getParentElement()) != null)
+ {
+ if (!htmlFragmentParentHashSet.contains(tempParentElem))
+ htmlFragmentParentHashSet.add(tempParentElem);
+ } // while((tempParentElem = tempParentElem.getParentElement())
+ // != null)
+
+ traverseHtmlFragment(rootElem);
+
+ } // else
+
+ // NOTE: close out remaining open embeded tags.
+ HTML.Tag[] tag_arr =
+ openEmbeddedTagHashSet.toArray(new HTML.Tag[openEmbeddedTagHashSet.size()]);
+
+ for (int i = 0; i < tag_arr.length; i++)
+ {
+ writeRaw("</" + tag_arr[i].toString() + ">");
+ } // for(int i = 0; i < tag_arr.length; i++)
+
+ } // public void write() throws IOException, BadLocationException
+
+ /**
+ * Writes all the attributes in the attrSet, except for attrbutes with
+ * keys of <code>javax.swing.text.html.HTML.Tag</code>,
+ * <code>javax.swing.text.StyleConstants</code> or
+ * <code>javax.swing.text.html.HTML.Attribute.ENDTAG</code>.
+ *
+ * @param attrSet attrSet to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void writeAttributes(AttributeSet attrSet)
+ throws IOException
+ {
+ Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ // HTML.Attribute.ENDTAG is an instance, not a class.
+ if (!((key instanceof HTML.Tag) || (key instanceof StyleConstants)
+ || (key == HTML.Attribute.ENDTAG)))
+ {
+ if (key == HTML.Attribute.SELECTED)
+ writeRaw(" selected");
+ else if (key == HTML.Attribute.CHECKED)
+ writeRaw(" checked");
+ else
+ writeRaw(" " + key + "=\"" + value + "\"");
+ } // if(!((key instanceof HTML.Tag) || (key instanceof
+ // StyleConstants) || (key == HTML.Attribute.ENDTAG)))
+ } // while(attrNameEnum.hasMoreElements())
+
+ } // protected void writeAttributes(AttributeSet attrSet) throws IOException
+
+ /**
+ * Writes out an empty tag. i.e. a tag without any child elements.
+ *
+ * @param paramElem the element to output as an empty tag
+ *
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ protected void emptyTag(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ String elem_name = paramElem.getName();
+ AttributeSet attrSet = paramElem.getAttributes();
+
+ writeRaw("<" + elem_name);
+ writeAttributes(attrSet);
+ writeRaw(">");
+
+ if (isBlockTag(attrSet))
+ {
+ writeRaw("</" + elem_name + ">");
+ } // if(isBlockTag(attrSet))
+
+ } // protected void emptyTag(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Determines if it is a block tag or not.
+ *
+ * @param attrSet the attrSet of the element
+ *
+ * @return <code>true</code> if it is a block tag
+ * <code>false</code> if it is a not block tag
+ */
+ protected boolean isBlockTag(AttributeSet attrSet)
+ {
+ return ((HTML.Tag)
+ attrSet.getAttribute(StyleConstants.NameAttribute)).isBlock();
+ } // protected boolean isBlockTag(AttributeSet attrSet)
+
+ /**
+ * Writes out a start tag. Synthesized elements are skipped.
+ *
+ * @param paramElem the element to output as a start tag
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ protected void startTag(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ // NOTE: Sysnthesized elements do no call this method at all.
+ String elem_name = paramElem.getName();
+ AttributeSet attrSet = paramElem.getAttributes();
+
+ indent();
+ writeRaw("<" + elem_name);
+ writeAttributes(attrSet);
+ writeRaw(">");
+ writeLineSeparator(); // Extra formatting to look more like the RI.
+ incrIndent();
+
+ } // protected void startTag(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Writes out the contents of a textarea.
+ *
+ * @param attrSet the attrSet of the element to output as a text area
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ protected void textAreaContent(AttributeSet attrSet)
+ throws IOException, BadLocationException
+ {
+ writeLineSeparator(); // Extra formatting to look more like the RI.
+ indent();
+ writeRaw("<textarea");
+ writeAttributes(attrSet);
+ writeRaw(">");
+
+ Document tempDocument =
+ (Document) attrSet.getAttribute(StyleConstants.ModelAttribute);
+
+ writeRaw(tempDocument.getText(0, tempDocument.getLength()));
+ indent();
+ writeRaw("</textarea>");
+
+ } // protected void textAreaContent(AttributeSet attrSet)
+ // throws IOException, BadLocationException
+
+ /**
+ * Writes out text, within the appropriate range if it is specified.
+ *
+ * @param paramElem the element to output as a text
+ * @throws IOException on any I/O exceptions
+ * @throws BadLocationException if a pos is not a valid position in the
+ * html doc element
+ */
+ protected void text(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ int offset = paramElem.getStartOffset();
+ int len = paramElem.getEndOffset() - paramElem.getStartOffset();
+ String txt_value = htmlDoc.getText(offset, len);
+
+ writeContent(txt_value);
+
+ } // protected void text(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Writes out the contents of a select element.
+ *
+ * @param attrSet the attrSet of the element to output as a select box
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void selectContent(AttributeSet attrSet)
+ throws IOException
+ {
+ writeLineSeparator(); // Extra formatting to look more like the RI.
+ indent();
+ writeRaw("<select");
+ writeAttributes(attrSet);
+ writeRaw(">");
+ incrIndent();
+ writeLineSeparator(); // extra formatting to look more like the RI.
+
+ ComboBoxModel comboBoxModel =
+ (ComboBoxModel) attrSet.getAttribute(StyleConstants.ModelAttribute);
+
+ for (int i = 0; i < comboBoxModel.getSize(); i++)
+ {
+ writeOption((Option) comboBoxModel.getElementAt(i));
+ } // for(int i = 0; i < comboBoxModel.getSize(); i++)
+
+ decrIndent();
+ indent();
+ writeRaw("</select>");
+
+ } // protected void selectContent(AttributeSet attrSet) throws IOException
+
+ /**
+ * Writes out the contents of an option element.
+ *
+ * @param option the option object to output as a select option
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void writeOption(Option option)
+ throws IOException
+ {
+ indent();
+ writeRaw("<option");
+ writeAttributes(option.getAttributes());
+ writeRaw(">");
+
+ writeContent(option.getLabel());
+
+ writeRaw("</option>");
+ writeLineSeparator(); // extra formatting to look more like the RI.
+
+ } // protected void writeOption(Option option) throws IOException
+
+ /**
+ * Writes out an end tag.
+ *
+ * @param paramElem the element to output as an end tag
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void endTag(Element paramElem)
+ throws IOException
+ {
+ String elem_name = paramElem.getName();
+
+ //writeLineSeparator(); // Extra formatting to look more like the RI.
+ decrIndent();
+ indent();
+ writeRaw("</" + elem_name + ">");
+ writeLineSeparator(); // Extra formatting to look more like the RI.
+
+ } // protected void endTag(Element paramElem) throws IOException
+
+ /**
+ * Writes out the comment.
+ *
+ * @param paramElem the element to output as a comment
+ */
+ protected void comment(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ AttributeSet attrSet = paramElem.getAttributes();
+
+ String comment_str = (String) attrSet.getAttribute(HTML.Attribute.COMMENT);
+
+ writeRaw("<!--" + comment_str + "-->");
+
+ } // protected void comment(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Determines if element is a synthesized
+ * <code>javax.swing.text.Element</code> or not.
+ *
+ * @param element the element to test
+ *
+ * @return <code>true</code> if it is a synthesized element,
+ * <code>false</code> if it is a not synthesized element
+ */
+ protected boolean synthesizedElement(Element element)
+ {
+ AttributeSet attrSet = element.getAttributes();
+ Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute);
+
+ if (tagType == HTML.Tag.CONTENT || tagType == HTML.Tag.COMMENT
+ || tagType == HTML.Tag.IMPLIED)
+ return true;
+ else
+ return false;
+ } // protected boolean synthesizedElement(Element element)
+
+ /**
+ * Determines if
+ * <code>javax.swing.text.StyleConstants.NameAttribute</code>
+ * matches tag or not.
+ *
+ * @param attrSet the <code>javax.swing.text.AttributeSet</code> of
+ * element to be matched
+ * @param tag the HTML.Tag to match
+ *
+ * @return <code>true</code> if it matches,
+ * <code>false</code> if it does not match
+ */
+ protected boolean matchNameAttribute(AttributeSet attrSet, HTML.Tag tag)
+ {
+ Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute);
+
+ if (tagType == tag)
+ return true;
+ else
+ return false;
+ } // protected boolean matchNameAttribute(AttributeSet attrSet,
+ // HTML.Tag tag)
+
+ /**
+ * Writes out an embedded tag. The tags not already in
+ * openEmbededTagHashSet will written out.
+ *
+ * @param attrSet the <code>javax.swing.text.AttributeSet</code> of
+ * the element to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void writeEmbeddedTags(AttributeSet attrSet)
+ throws IOException
+ {
+ Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ if (key instanceof HTML.Tag)
+ {
+ if (!openEmbeddedTagHashSet.contains(key))
+ {
+ writeRaw("<" + key);
+ writeAttributes((AttributeSet) value);
+ writeRaw(">");
+ openEmbeddedTagHashSet.add((HTML.Tag) key);
+ } // if(!openEmbededTagHashSet.contains(key))
+ } // if(key instanceof HTML.Tag)
+ } // while(attrNameEnum.hasMoreElements())
+
+ } // protected void writeEmbeddedTags(AttributeSet attrSet)
+ // throws IOException
+
+ /**
+ * Closes out an unwanted embedded tag. The tags from the
+ * openEmbededTagHashSet not found in attrSet will be written out.
+ *
+ * @param attrSet the AttributeSet of the element to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet)
+ throws IOException
+ {
+ HTML.Tag[] tag_arr =
+ openEmbeddedTagHashSet.toArray(new HTML.Tag[openEmbeddedTagHashSet.size()]);
+
+ for (int i = 0; i < tag_arr.length; i++)
+ {
+ HTML.Tag key = tag_arr[i];
+
+ if (!attrSet.isDefined(key))
+ {
+ writeRaw("</" + key.toString() + ">");
+ openEmbeddedTagHashSet.remove(key);
+ } // if(!attrSet.isDefined(key))
+ } // for(int i = 0; i < tag_arr.length; i++)
+
+ } // protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet)
+ // throws IOException
+
+ /**
+ * Writes out a line separator. Overwrites the parent to write out a new
+ * line.
+ *
+ * @throws IOException on any I/O exceptions.
+ */
+ protected void writeLineSeparator()
+ throws IOException
+ {
+ writeRaw(new_line_str);
+ } // protected void writeLineSeparator() throws IOException
+
+ /**
+ * Write to the writer. Character entites such as &lt;, &gt;
+ * are escaped appropriately.
+ *
+ * @param chars char array to write out
+ * @param off offset
+ * @param len length
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ protected void output(char[] chars, int off, int len)
+ throws IOException
+ {
+ CPStringBuilder strBuffer = new CPStringBuilder();
+
+ for (int i = 0; i < chars.length; i++)
+ {
+ if (isCharHtmlEntity(chars[i]))
+ strBuffer.append(escapeCharHtmlEntity(chars[i]));
+ else
+ strBuffer.append(chars[i]);
+ } // for(int i = 0; i < chars.length; i++)
+
+ writeRaw(strBuffer.toString());
+
+ } // protected void output(char[] chars, int off, int len)
+ // throws IOException
+
+ //-------------------------------------------------------------------------
+ // private methods
+
+ /**
+ * The main method used to traverse through the elements.
+ *
+ * @param paramElem element to traverse
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void traverse(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ Element currElem = paramElem;
+
+ AttributeSet attrSet = currElem.getAttributes();
+
+ closeOutUnwantedEmbeddedTags(attrSet);
+
+ // handle the tag
+ if (synthesizedElement(paramElem))
+ {
+ if (matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+ {
+ writeEmbeddedTags(attrSet);
+ text(currElem);
+ } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+ {
+ comment(currElem);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+ {
+ int child_elem_count = currElem.getElementCount();
+
+ if (child_elem_count > 0)
+ {
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverse(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+ } // if(child_elem_count > 0)
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+ } // if(synthesizedElement(paramElem))
+ else
+ {
+ // NOTE: 20061030 - fchoong - title is treated specially here.
+ // based on RI behavior.
+ if (matchNameAttribute(attrSet, HTML.Tag.TITLE))
+ {
+ boolean fg_is_end_tag = false;
+ Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ if (key == HTML.Attribute.ENDTAG && value.equals("true"))
+ fg_is_end_tag = true;
+ } // while(attrNameEnum.hasMoreElements())
+
+ if (fg_is_end_tag)
+ writeRaw("</title>");
+ else
+ {
+ indent();
+ writeRaw("<title>");
+
+ String title_str =
+ (String) htmlDoc.getProperty(HTMLDocument.TitleProperty);
+
+ if (title_str != null)
+ writeContent(title_str);
+
+ } // else
+ } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE))
+ else if (matchNameAttribute(attrSet, HTML.Tag.PRE))
+ {
+ // We pursue more stringent formating here.
+ attrSet = paramElem.getAttributes();
+
+ indent();
+ writeRaw("<pre");
+ writeAttributes(attrSet);
+ writeRaw(">");
+
+ int child_elem_count = currElem.getElementCount();
+
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverse(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+
+ writeRaw("</pre>");
+
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE))
+ else if (matchNameAttribute(attrSet, HTML.Tag.SELECT))
+ {
+ selectContent(attrSet);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+ {
+ textAreaContent(attrSet);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+ else
+ {
+ int child_elem_count = currElem.getElementCount();
+
+ if (child_elem_count > 0)
+ {
+ startTag(currElem);
+
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverse(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+
+ endTag(currElem);
+
+ } // if(child_elem_count > 0)
+ else
+ {
+ emptyTag(currElem);
+ } // else
+ } // else
+ } // else
+
+ } // private void traverse(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * The method used to traverse through a html fragment.
+ *
+ * @param paramElem element to traverse
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void traverseHtmlFragment(Element paramElem)
+ throws IOException, BadLocationException
+ {
+ // NOTE: This method is similar to traverse(Element paramElem)
+ Element currElem = paramElem;
+
+ boolean fg_is_fragment_parent_elem = false;
+ boolean fg_is_start_and_end_elem = false;
+
+ if (htmlFragmentParentHashSet.contains(paramElem))
+ fg_is_fragment_parent_elem = true;
+
+ if (paramElem == startElem)
+ fg_pass_start_elem = true;
+
+ if (paramElem == startElem && paramElem == endElem)
+ fg_is_start_and_end_elem = true;
+
+ AttributeSet attrSet = currElem.getAttributes();
+
+ closeOutUnwantedEmbeddedTags(attrSet);
+
+ if (fg_is_fragment_parent_elem || (fg_pass_start_elem
+ && fg_pass_end_elem == false) || fg_is_start_and_end_elem)
+ {
+ // handle the tag
+ if (synthesizedElement(paramElem))
+ {
+ if (matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+ {
+ writeEmbeddedTags(attrSet);
+
+ int content_offset = paramElem.getStartOffset();
+ int content_length = currElem.getEndOffset() - content_offset;
+
+ if (doc_offset_remaining > 0)
+ {
+ if (content_length > doc_offset_remaining)
+ {
+ int split_len = content_length;
+
+ split_len = split_len - doc_offset_remaining;
+
+ if (split_len > doc_len_remaining)
+ split_len = doc_len_remaining;
+
+ // we need to split it.
+ String txt_value = htmlDoc.getText(content_offset
+ + doc_offset_remaining, split_len);
+
+ writeContent(txt_value);
+
+ doc_offset_remaining = 0; // the offset is used up.
+ doc_len_remaining = doc_len_remaining - split_len;
+ } // if(content_length > doc_offset_remaining)
+ else
+ {
+ // doc_offset_remaining is greater than the entire
+ // length of content
+ doc_offset_remaining = doc_offset_remaining
+ - content_length;
+ } // else
+ } // if(doc_offset_remaining > 0)
+ else if (content_length <= doc_len_remaining)
+ {
+ // we can fit the entire content.
+ text(currElem);
+ doc_len_remaining = doc_len_remaining - content_length;
+ } // else if(content_length <= doc_len_remaining)
+ else
+ {
+ // we need to split it.
+ String txt_value = htmlDoc.getText(content_offset,
+ doc_len_remaining);
+
+ writeContent(txt_value);
+
+ doc_len_remaining = 0;
+ } // else
+
+ } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+ {
+ comment(currElem);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+ {
+ int child_elem_count = currElem.getElementCount();
+
+ if (child_elem_count > 0)
+ {
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverseHtmlFragment(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+ } // if(child_elem_count > 0)
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+ } // if(synthesizedElement(paramElem))
+ else
+ {
+ // NOTE: 20061030 - fchoong - the isLeaf() condition seems to
+ // generate the closest behavior to the RI.
+ if (paramElem.isLeaf())
+ {
+ if (doc_offset_remaining > 0)
+ {
+ doc_offset_remaining--;
+ } // if(doc_offset_remaining > 0)
+ else if (doc_len_remaining > 0)
+ {
+ doc_len_remaining--;
+ } // else if(doc_len_remaining > 0)
+ } // if(paramElem.isLeaf())
+
+ // NOTE: 20061030 - fchoong - title is treated specially here.
+ // based on RI behavior.
+ if (matchNameAttribute(attrSet, HTML.Tag.TITLE))
+ {
+ boolean fg_is_end_tag = false;
+ Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ if (key == HTML.Attribute.ENDTAG && value.equals("true"))
+ fg_is_end_tag = true;
+ } // while(attrNameEnum.hasMoreElements())
+
+ if (fg_is_end_tag)
+ writeRaw("</title>");
+ else
+ {
+ indent();
+ writeRaw("<title>");
+
+ String title_str =
+ (String) htmlDoc.getProperty(HTMLDocument.TitleProperty);
+
+ if (title_str != null)
+ writeContent(title_str);
+
+ } // else
+ } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE))
+ else if (matchNameAttribute(attrSet, HTML.Tag.PRE))
+ {
+ // We pursue more stringent formating here.
+ attrSet = paramElem.getAttributes();
+
+ indent();
+ writeRaw("<pre");
+ writeAttributes(attrSet);
+ writeRaw(">");
+
+ int child_elem_count = currElem.getElementCount();
+
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverseHtmlFragment(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+
+ writeRaw("</pre>");
+
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE))
+ else if (matchNameAttribute(attrSet, HTML.Tag.SELECT))
+ {
+ selectContent(attrSet);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT))
+ else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+ {
+ textAreaContent(attrSet);
+ } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+ else
+ {
+ int child_elem_count = currElem.getElementCount();
+
+ if (child_elem_count > 0)
+ {
+ startTag(currElem);
+
+ for (int i = 0; i < child_elem_count; i++)
+ {
+ Element childElem = paramElem.getElement(i);
+
+ traverseHtmlFragment(childElem);
+
+ } // for(int i = 0; i < child_elem_count; i++)
+
+ endTag(currElem);
+
+ } // if(child_elem_count > 0)
+ else
+ {
+ emptyTag(currElem);
+ } // else
+ } // else
+ } // else
+
+ } // if(fg_is_fragment_parent_elem || (fg_pass_start_elem
+ // && fg_pass_end_elem == false) || fg_is_start_and_end_elem)
+
+ if (paramElem == endElem)
+ fg_pass_end_elem = true;
+
+ } // private void traverseHtmlFragment(Element paramElem)
+ // throws IOException, BadLocationException
+
+ /**
+ * Write to the writer without any modifications.
+ *
+ * @param param_str the str to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void writeRaw(String param_str)
+ throws IOException
+ {
+ super.output(param_str.toCharArray(), 0, param_str.length());
+ } // private void writeRaw(char[] chars, int off, int len)
+ // throws IOException
+
+ /**
+ * Write to the writer, escaping HTML character entitie where neccessary.
+ *
+ * @param param_str the str to write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void writeContent(String param_str)
+ throws IOException
+ {
+ char[] str_char_arr = param_str.toCharArray();
+
+ if (hasHtmlEntity(param_str))
+ output(str_char_arr, 0, str_char_arr.length);
+ else
+ super.output(str_char_arr, 0, str_char_arr.length);
+
+ } // private void writeContent(String param_str) throws IOException
+
+ /**
+ * Use this for debugging. Writes out all attributes regardless of type.
+ *
+ * @param attrSet the <code>javax.swing.text.AttributeSet</code> to
+ * write out
+ *
+ * @throws IOException on any I/O exceptions
+ */
+ private void writeAllAttributes(AttributeSet attrSet)
+ throws IOException
+ {
+ Enumeration<?> attrNameEnum = attrSet.getAttributeNames();
+
+ while (attrNameEnum.hasMoreElements())
+ {
+ Object key = attrNameEnum.nextElement();
+ Object value = attrSet.getAttribute(key);
+
+ writeRaw(" " + key + "=\"" + value + "\"");
+ writeRaw(" " + key.getClass().toString() + "=\""
+ + value.getClass().toString() + "\"");
+ } // while(attrNameEnum.hasMoreElements())
+
+ } // private void writeAllAttributes(AttributeSet attrSet)
+ // throws IOException
+
+ /**
+ * Tests if the str contains any html entities.
+ *
+ * @param param_str the str to test
+ *
+ * @return <code>true</code> if it has a html entity
+ * <code>false</code> if it does not have a html entity
+ */
+ private boolean hasHtmlEntity(String param_str)
+ {
+ boolean ret_bool = false;
+
+ for (int i = 0; i < html_entity_char_arr.length; i++)
+ {
+ if (param_str.indexOf(html_entity_char_arr[i]) != -1)
+ {
+ ret_bool = true;
+ break;
+ } // if(param_str.indexOf(html_entity_char_arr[i]) != -1)
+ } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+ return ret_bool;
+ } // private boolean hasHtmlEntity(String param_str)
+
+ /**
+ * Tests if the char is a html entities.
+ *
+ * @param param_char the char to test
+ *
+ * @return <code>true</code> if it is a html entity
+ * <code>false</code> if it is not a html entity.
+ */
+ private boolean isCharHtmlEntity(char param_char)
+ {
+ boolean ret_bool = false;
+
+ for (int i = 0; i < html_entity_char_arr.length; i++)
+ {
+ if (param_char == html_entity_char_arr[i])
+ {
+ ret_bool = true;
+ break;
+ } // if(param_char == html_entity_char_arr[i])
+ } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+ return ret_bool;
+ } // private boolean hasHtmlEntity(String param_str)
+
+ /**
+ * Escape html entities.
+ *
+ * @param param_char the char to escape
+ *
+ * @return escaped html entity. Original char is returned as a str if is
+ * is not a html entity
+ */
+ private String escapeCharHtmlEntity(char param_char)
+ {
+ String ret_str = "" + param_char;
+
+ for (int i = 0; i < html_entity_char_arr.length; i++)
+ {
+ if (param_char == html_entity_char_arr[i])
+ {
+ ret_str = html_entity_escape_str_arr[i];
+ break;
+ } // if(param_char == html_entity_char_arr[i])
+ } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+ return ret_str;
+ } // private String escapeCharHtmlEntity(char param_char)
+
+} // public class HTMLWriter extends AbstractWriter
diff --git a/libjava/classpath/javax/swing/text/html/ImageView.java b/libjava/classpath/javax/swing/text/html/ImageView.java
new file mode 100644
index 000000000..bdbe9b3bb
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ImageView.java
@@ -0,0 +1,591 @@
+package javax.swing.text.html;
+
+import gnu.javax.swing.text.html.ImageViewIconFactory;
+import gnu.javax.swing.text.html.css.Length;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.image.ImageObserver;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.Icon;
+import javax.swing.SwingUtilities;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.Position.Bias;
+import javax.swing.text.html.HTML.Attribute;
+
+/**
+ * A view, representing a single image, represented by the HTML IMG tag.
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+public class ImageView extends View
+{
+ /**
+ * Tracks image loading state and performs the necessary layout updates.
+ */
+ class Observer
+ implements ImageObserver
+ {
+
+ public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height)
+ {
+ boolean widthChanged = false;
+ if ((flags & ImageObserver.WIDTH) != 0 && spans[X_AXIS] == null)
+ widthChanged = true;
+ boolean heightChanged = false;
+ if ((flags & ImageObserver.HEIGHT) != 0 && spans[Y_AXIS] == null)
+ heightChanged = true;
+ if (widthChanged || heightChanged)
+ safePreferenceChanged(ImageView.this, widthChanged, heightChanged);
+ boolean ret = (flags & ALLBITS) != 0;
+ return ret;
+ }
+
+ }
+
+ /**
+ * True if the image loads synchronuosly (on demand). By default, the image
+ * loads asynchronuosly.
+ */
+ boolean loadOnDemand;
+
+ /**
+ * The image icon, wrapping the image,
+ */
+ Image image;
+
+ /**
+ * The image state.
+ */
+ byte imageState = MediaTracker.LOADING;
+
+ /**
+ * True when the image needs re-loading, false otherwise.
+ */
+ private boolean reloadImage;
+
+ /**
+ * True when the image properties need re-loading, false otherwise.
+ */
+ private boolean reloadProperties;
+
+ /**
+ * True when the width is set as CSS/HTML attribute.
+ */
+ private boolean haveWidth;
+
+ /**
+ * True when the height is set as CSS/HTML attribute.
+ */
+ private boolean haveHeight;
+
+ /**
+ * True when the image is currently loading.
+ */
+ private boolean loading;
+
+ /**
+ * The current width of the image.
+ */
+ private int width;
+
+ /**
+ * The current height of the image.
+ */
+ private int height;
+
+ /**
+ * Our ImageObserver for tracking the loading state.
+ */
+ private ImageObserver observer;
+
+ /**
+ * The CSS width and height.
+ *
+ * Package private to avoid synthetic accessor methods.
+ */
+ Length[] spans;
+
+ /**
+ * The cached attributes.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * Creates the image view that represents the given element.
+ *
+ * @param element the element, represented by this image view.
+ */
+ public ImageView(Element element)
+ {
+ super(element);
+ spans = new Length[2];
+ observer = new Observer();
+ reloadProperties = true;
+ reloadImage = true;
+ loadOnDemand = false;
+ }
+
+ /**
+ * Load or reload the image. This method initiates the image reloading. After
+ * the image is ready, the repaint event will be scheduled. The current image,
+ * if it already exists, will be discarded.
+ */
+ private void reloadImage()
+ {
+ loading = true;
+ reloadImage = false;
+ haveWidth = false;
+ haveHeight = false;
+ image = null;
+ width = 0;
+ height = 0;
+ try
+ {
+ loadImage();
+ updateSize();
+ }
+ finally
+ {
+ loading = false;
+ }
+ }
+
+ /**
+ * Get the image alignment. This method works handling standart alignment
+ * attributes in the HTML IMG tag (align = top bottom middle left right).
+ * Depending from the parameter, either horizontal or vertical alingment
+ * information is returned.
+ *
+ * @param axis -
+ * either X_AXIS or Y_AXIS
+ */
+ public float getAlignment(int axis)
+ {
+ AttributeSet attrs = getAttributes();
+ Object al = attrs.getAttribute(Attribute.ALIGN);
+
+ // Default is top left aligned.
+ if (al == null)
+ return 0.0f;
+
+ String align = al.toString();
+
+ if (axis == View.X_AXIS)
+ {
+ if (align.equals("middle"))
+ return 0.5f;
+ else if (align.equals("left"))
+ return 0.0f;
+ else if (align.equals("right"))
+ return 1.0f;
+ else
+ return 0.0f;
+ }
+ else if (axis == View.Y_AXIS)
+ {
+ if (align.equals("middle"))
+ return 0.5f;
+ else if (align.equals("top"))
+ return 0.0f;
+ else if (align.equals("bottom"))
+ return 1.0f;
+ else
+ return 0.0f;
+ }
+ else
+ throw new IllegalArgumentException("axis " + axis);
+ }
+
+ /**
+ * Get the text that should be shown as the image replacement and also as the
+ * image tool tip text. The method returns the value of the attribute, having
+ * the name {@link Attribute#ALT}. If there is no such attribute, the image
+ * name from the url is returned. If the URL is not available, the empty
+ * string is returned.
+ */
+ public String getAltText()
+ {
+ Object rt = getAttributes().getAttribute(Attribute.ALT);
+ if (rt != null)
+ return rt.toString();
+ else
+ {
+ URL u = getImageURL();
+ if (u == null)
+ return "";
+ else
+ return u.getFile();
+ }
+ }
+
+ /**
+ * Returns the combination of the document and the style sheet attributes.
+ */
+ public AttributeSet getAttributes()
+ {
+ if (attributes == null)
+ attributes = getStyleSheet().getViewAttributes(this);
+ return attributes;
+ }
+
+ /**
+ * Get the image to render. May return null if the image is not yet loaded.
+ */
+ public Image getImage()
+ {
+ updateState();
+ return image;
+ }
+
+ /**
+ * Get the URL location of the image to render. If this method returns null,
+ * the "no image" icon is rendered instead. By defaul, url must be present as
+ * the "src" property of the IMG tag. If it is missing, null is returned and
+ * the "no image" icon is rendered.
+ *
+ * @return the URL location of the image to render.
+ */
+ public URL getImageURL()
+ {
+ Element el = getElement();
+ String src = (String) el.getAttributes().getAttribute(Attribute.SRC);
+ URL url = null;
+ if (src != null)
+ {
+ URL base = ((HTMLDocument) getDocument()).getBase();
+ try
+ {
+ url = new URL(base, src);
+ }
+ catch (MalformedURLException ex)
+ {
+ // Return null.
+ }
+ }
+ return url;
+ }
+
+ /**
+ * Get the icon that should be displayed while the image is loading and hence
+ * not yet available.
+ *
+ * @return an icon, showing a non broken sheet of paper with image.
+ */
+ public Icon getLoadingImageIcon()
+ {
+ return ImageViewIconFactory.getLoadingImageIcon();
+ }
+
+ /**
+ * Get the image loading strategy.
+ *
+ * @return false (default) if the image is loaded when the view is
+ * constructed, true if the image is only loaded on demand when
+ * rendering.
+ */
+ public boolean getLoadsSynchronously()
+ {
+ return loadOnDemand;
+ }
+
+ /**
+ * Get the icon that should be displayed when the image is not available.
+ *
+ * @return an icon, showing a broken sheet of paper with image.
+ */
+ public Icon getNoImageIcon()
+ {
+ return ImageViewIconFactory.getNoImageIcon();
+ }
+
+ /**
+ * Get the preferred span of the image along the axis. The image size is first
+ * requested to the attributes {@link Attribute#WIDTH} and
+ * {@link Attribute#HEIGHT}. If they are missing, and the image is already
+ * loaded, the image size is returned. If there are no attributes, and the
+ * image is not loaded, zero is returned.
+ *
+ * @param axis -
+ * either X_AXIS or Y_AXIS
+ * @return either width of height of the image, depending on the axis.
+ */
+ public float getPreferredSpan(int axis)
+ {
+ Image image = getImage();
+
+ if (axis == View.X_AXIS)
+ {
+ if (spans[axis] != null)
+ return spans[axis].getValue();
+ else if (image != null)
+ return image.getWidth(getContainer());
+ else
+ return getNoImageIcon().getIconWidth();
+ }
+ else if (axis == View.Y_AXIS)
+ {
+ if (spans[axis] != null)
+ return spans[axis].getValue();
+ else if (image != null)
+ return image.getHeight(getContainer());
+ else
+ return getNoImageIcon().getIconHeight();
+ }
+ else
+ throw new IllegalArgumentException("axis " + axis);
+ }
+
+ /**
+ * Get the associated style sheet from the document.
+ *
+ * @return the associated style sheet.
+ */
+ protected StyleSheet getStyleSheet()
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ return doc.getStyleSheet();
+ }
+
+ /**
+ * Get the tool tip text. This is overridden to return the value of the
+ * {@link #getAltText()}. The parameters are ignored.
+ *
+ * @return that is returned by getAltText().
+ */
+ public String getToolTipText(float x, float y, Shape shape)
+ {
+ return getAltText();
+ }
+
+ /**
+ * Paints the image or one of the two image state icons. The image is resized
+ * to the shape bounds. If there is no image available, the alternative text
+ * is displayed besides the image state icon.
+ *
+ * @param g
+ * the Graphics, used for painting.
+ * @param bounds
+ * the bounds of the region where the image or replacing icon must be
+ * painted.
+ */
+ public void paint(Graphics g, Shape bounds)
+ {
+ updateState();
+ Rectangle r = bounds instanceof Rectangle ? (Rectangle) bounds
+ : bounds.getBounds();
+ Image image = getImage();
+ if (image != null)
+ {
+ g.drawImage(image, r.x, r.y, r.width, r.height, observer);
+ }
+ else
+ {
+ Icon icon = getNoImageIcon();
+ if (icon != null)
+ icon.paintIcon(getContainer(), g, r.x, r.y);
+ }
+ }
+
+ /**
+ * Set if the image should be loaded only when needed (synchronuosly). By
+ * default, the image loads asynchronuosly. If the image is not yet ready, the
+ * icon, returned by the {@link #getLoadingImageIcon()}, is displayed.
+ */
+ public void setLoadsSynchronously(boolean load_on_demand)
+ {
+ loadOnDemand = load_on_demand;
+ }
+
+ /**
+ * Update all cached properties from the attribute set, returned by the
+ * {@link #getAttributes}.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ AttributeSet atts = getAttributes();
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(atts);
+ float exBase = ss.getEXBase(atts);
+ spans[X_AXIS] = (Length) atts.getAttribute(CSS.Attribute.WIDTH);
+ if (spans[X_AXIS] != null)
+ {
+ spans[X_AXIS].setFontBases(emBase, exBase);
+ }
+ spans[Y_AXIS] = (Length) atts.getAttribute(CSS.Attribute.HEIGHT);
+ if (spans[Y_AXIS] != null)
+ {
+ spans[Y_AXIS].setFontBases(emBase, exBase);
+ }
+ }
+
+ /**
+ * Maps the picture co-ordinates into the image position in the model. As the
+ * image is not divideable, this is currently implemented always to return the
+ * start offset.
+ */
+ public int viewToModel(float x, float y, Shape shape, Bias[] bias)
+ {
+ return getStartOffset();
+ }
+
+ /**
+ * This is currently implemented always to return the area of the image view,
+ * as the image is not divideable by character positions.
+ *
+ * @param pos character position
+ * @param area of the image view
+ * @param bias bias
+ *
+ * @return the shape, where the given character position should be mapped.
+ */
+ public Shape modelToView(int pos, Shape area, Bias bias)
+ throws BadLocationException
+ {
+ return area;
+ }
+
+ /**
+ * Starts loading the image asynchronuosly. If the image must be loaded
+ * synchronuosly instead, the {@link #setLoadsSynchronously} must be
+ * called before calling this method. The passed parameters are not used.
+ */
+ public void setSize(float width, float height)
+ {
+ updateState();
+ // TODO: Implement this when we have an alt view for the alt=... attribute.
+ }
+
+ /**
+ * This makes sure that the image and properties have been loaded.
+ */
+ private void updateState()
+ {
+ if (reloadImage)
+ reloadImage();
+ if (reloadProperties)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Actually loads the image.
+ */
+ private void loadImage()
+ {
+ URL src = getImageURL();
+ Image newImage = null;
+ if (src != null)
+ {
+ // Call getImage(URL) to allow the toolkit caching of that image URL.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ newImage = tk.getImage(src);
+ tk.prepareImage(newImage, -1, -1, observer);
+ if (newImage != null && getLoadsSynchronously())
+ {
+ // Load image synchronously.
+ MediaTracker tracker = new MediaTracker(getContainer());
+ tracker.addImage(newImage, 0);
+ try
+ {
+ tracker.waitForID(0);
+ }
+ catch (InterruptedException ex)
+ {
+ Thread.interrupted();
+ }
+
+ }
+ }
+ image = newImage;
+ }
+
+ /**
+ * Updates the size parameters of the image.
+ */
+ private void updateSize()
+ {
+ int newW = 0;
+ int newH = 0;
+ Image newIm = getImage();
+ if (newIm != null)
+ {
+ // Fetch width.
+ Length l = spans[X_AXIS];
+ if (l != null)
+ {
+ newW = (int) l.getValue();
+ haveWidth = true;
+ }
+ else
+ {
+ newW = newIm.getWidth(observer);
+ }
+ // Fetch height.
+ l = spans[Y_AXIS];
+ if (l != null)
+ {
+ newH = (int) l.getValue();
+ haveHeight = true;
+ }
+ else
+ {
+ newW = newIm.getWidth(observer);
+ }
+ // Go and trigger loading.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ if (haveWidth || haveHeight)
+ tk.prepareImage(newIm, width, height, observer);
+ else
+ tk.prepareImage(newIm, -1, -1, observer);
+ }
+ }
+
+ /**
+ * Calls preferenceChanged from the event dispatch thread and within
+ * a read lock to protect us from threading issues.
+ *
+ * @param v the view
+ * @param width true when the width changed
+ * @param height true when the height changed
+ */
+ void safePreferenceChanged(final View v, final boolean width,
+ final boolean height)
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ Document doc = getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ preferenceChanged(v, width, height);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ }
+ else
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ safePreferenceChanged(v, width, height);
+ }
+ });
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/InlineView.java b/libjava/classpath/javax/swing/text/html/InlineView.java
new file mode 100644
index 000000000..5376c6b9d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/InlineView.java
@@ -0,0 +1,307 @@
+/* InlineView.java -- Renders HTML content
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.awt.FontMetrics;
+import java.awt.Shape;
+import java.text.BreakIterator;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.LabelView;
+import javax.swing.text.Segment;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * Renders HTML content (identified by {@link HTML.Tag#CONTENT}). This is
+ * basically a {@link LabelView} that is adjusted to understand styles defined
+ * by stylesheets.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class InlineView
+ extends LabelView
+{
+
+ /**
+ * The attributes used by this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The span of the longest word in this view.
+ *
+ * @see #getLongestWord()
+ */
+ private float longestWord;
+
+ /**
+ * Indicates if we may wrap or not.
+ */
+ private boolean nowrap;
+
+ /**
+ * Creates a new <code>InlineView</code> that renders the specified element.
+ *
+ * @param element the element for this view
+ */
+ public InlineView(Element element)
+ {
+ super(element);
+ }
+
+ /**
+ * Receives notification that something was inserted into the document in
+ * a location that this view is responsible for.
+ *
+ * @param e the document event
+ * @param a the current allocation of this view
+ * @param f the view factory for creating new views
+ *
+ * @since 1.5
+ */
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ // FIXME: What to do here?
+ super.insertUpdate(e, a, f);
+ }
+
+ /**
+ * Receives notification that something was removed from the document in
+ * a location that this view is responsible for.
+ *
+ * @param e the document event
+ * @param a the current allocation of this view
+ * @param f the view factory for creating new views
+ *
+ * @since 1.5
+ */
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ // FIXME: What to do here?
+ super.removeUpdate(e, a, f);
+ }
+
+ /**
+ * Receives notification that attributes have changed in the document in
+ * a location that this view is responsible for. This calls
+ * {@link #setPropertiesFromAttributes}.
+ *
+ * @param e the document event
+ * @param a the current allocation of this view
+ * @param f the view factory for creating new views
+ *
+ * @since 1.5
+ */
+ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.changedUpdate(e, a, f);
+ StyleSheet ss = getStyleSheet();
+ attributes = ss.getViewAttributes(this);
+ preferenceChanged(null, true, true);
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Returns the attributes that are used for rendering. This is implemented
+ * to multiplex the attributes specified in the model with a stylesheet.
+ *
+ * @return the attributes that are used for rendering
+ */
+ public AttributeSet getAttributes()
+ {
+ if (attributes == null)
+ {
+ StyleSheet ss = getStyleSheet();
+ attributes = ss.getViewAttributes(this);
+ }
+ return attributes;
+ }
+
+
+ public int getBreakWeight(int axis, float pos, float len)
+ {
+ int weight;
+ if (nowrap)
+ weight = BadBreakWeight;
+ else
+ weight = super.getBreakWeight(axis, pos, len);
+ return weight;
+ }
+
+ public View breakView(int axis, int offset, float pos, float len)
+ {
+ // FIXME: Implement this.
+ return super.breakView(axis, offset, pos, len);
+ }
+
+ /**
+ * Loads the character style properties from the stylesheet.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ super.setPropertiesFromAttributes();
+ AttributeSet atts = getAttributes();
+ Object o = atts.getAttribute(CSS.Attribute.TEXT_DECORATION);
+
+ // Check for underline.
+ boolean b = false;
+ if (o != null && o.toString().contains("underline"))
+ b = true;
+ setUnderline(b);
+
+ // Check for line-through.
+ b = false;
+ if (o != null && o.toString().contains("line-through"))
+ b = true;
+ setStrikeThrough(b);
+
+ // Check for vertical alignment (subscript/superscript).
+ o = atts.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+
+ // Subscript.
+ b = false;
+ if (o != null && o.toString().contains("sub"))
+ b = true;
+ setSubscript(b);
+
+ // Superscript.
+ b = false;
+ if (o != null && o.toString().contains("sup"))
+ b = true;
+ setSuperscript(b);
+
+ // Fetch nowrap setting.
+ o = atts.getAttribute(CSS.Attribute.WHITE_SPACE);
+ if (o != null && o.equals("nowrap"))
+ nowrap = true;
+ else
+ nowrap = false;
+ }
+
+ /**
+ * Returns the stylesheet used by this view. This returns the stylesheet
+ * of the <code>HTMLDocument</code> that is rendered by this view.
+ *
+ * @return the stylesheet used by this view
+ */
+ protected StyleSheet getStyleSheet()
+ {
+ Document doc = getDocument();
+ StyleSheet styleSheet = null;
+ if (doc instanceof HTMLDocument)
+ styleSheet = ((HTMLDocument) doc).getStyleSheet();
+ return styleSheet;
+ }
+
+ /**
+ * Returns the minimum span for the specified axis. This returns the
+ * width of the longest word for the X axis and the super behaviour for
+ * the Y axis. This is a slight deviation from the reference implementation.
+ * IMO this should improve rendering behaviour so that an InlineView never
+ * gets smaller than the longest word in it.
+ */
+ public float getMinimumSpan(int axis)
+ {
+ float min = super.getMinimumSpan(axis);
+ if (axis == X_AXIS)
+ min = Math.max(getLongestWord(), min);
+ return min;
+ }
+
+ /**
+ * Returns the span of the longest word in this view.
+ *
+ * @return the span of the longest word in this view
+ */
+ private float getLongestWord()
+ {
+ if (longestWord == -1)
+ longestWord = calculateLongestWord();
+ return longestWord;
+ }
+
+ /**
+ * Calculates the span of the longest word in this view.
+ *
+ * @return the span of the longest word in this view
+ */
+ private float calculateLongestWord()
+ {
+ float span = 0;
+ try
+ {
+ Document doc = getDocument();
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+ Segment s = new Segment();
+ doc.getText(p0, p1 - p0, s);
+ BreakIterator iter = BreakIterator.getWordInstance();
+ iter.setText(s);
+ int wordStart = p0;
+ int wordEnd = p0;
+ int start = iter.first();
+ for (int end = iter.next(); end != BreakIterator.DONE;
+ start = end, end = iter.next())
+ {
+ if ((end - start) > (wordEnd - wordStart))
+ {
+ wordStart = start;
+ wordEnd = end;
+ }
+ }
+ if (wordEnd - wordStart > 0)
+ {
+ FontMetrics fm = getFontMetrics();
+ int offset = s.offset + wordStart - s.getBeginIndex();
+ span = fm.charsWidth(s.array, offset, wordEnd - wordStart);
+ }
+ }
+ catch (BadLocationException ex)
+ {
+ // Return 0.
+ }
+ return span;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ListView.java b/libjava/classpath/javax/swing/text/html/ListView.java
new file mode 100644
index 000000000..f05051a4d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ListView.java
@@ -0,0 +1,128 @@
+/* ListView.java --
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.text.Element;
+
+/**
+ * A View to render HTML lists, like the <code>&lt;ul&gt;</code> and
+ * <code>&lt;ol&gt;</code> tags.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class ListView
+ extends BlockView
+{
+
+ /**
+ * The painter used to paint the list items.
+ */
+ private StyleSheet.ListPainter painter;
+
+ /**
+ * Creates a new <code>ListView</code> for the specified element.
+ *
+ * @param el the element to create a list view for
+ */
+ public ListView(Element el)
+ {
+ super(el, Y_AXIS);
+ }
+
+ /**
+ * Returns the alignment of this view along the specified axis.
+ *
+ * This returns <code>0.5</code> unconditionally.
+ *
+ * @param axis the axis
+ *
+ * @return the alignment of this view along the specified axis
+ */
+ public float getAlignment(int axis)
+ {
+ if (axis != X_AXIS && axis != Y_AXIS)
+ throw new IllegalArgumentException("Illegal axis parameter: " + axis);
+
+ return 0.5F;
+ }
+
+ /**
+ * Paints the <code>ListView</code>.
+ *
+ * @param g the graphics context to use for painting
+ * @param allocation the allocation given to this view
+ */
+ public void paint(Graphics g, Shape allocation)
+ {
+ super.paint(g, allocation);
+ }
+
+ /**
+ * Paints the child with the specified index into the specified allocation.
+ *
+ * This implementation forwards to the list painter fetched from the
+ * {@link StyleSheet} and then calls
+ * <code>super.paintChild(g, a, index)</code>.
+ *
+ * @param g the graphics context to use
+ * @param a the allocation for the child
+ * @param index the child index
+ */
+ protected void paintChild(Graphics g, Rectangle a, int index)
+ {
+ painter.paint(g, a.x, a.y, a.width, a.height, this, index);
+ super.paintChild(g, a, index);
+ }
+
+ /**
+ * Fetches this view's properties from the style attributes of this view's
+ * element.
+ *
+ * This forwards to super and then fetches a {@link StyleSheet.ListPainter}
+ * from the stylesheet suitable for painting the list.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ super.setPropertiesFromAttributes();
+ painter = getStyleSheet().getListPainter(getAttributes());
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java b/libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java
new file mode 100644
index 000000000..3dddfc3de
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java
@@ -0,0 +1,453 @@
+/* MinimalHTMLWriter.java --
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.AbstractWriter;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.Element;
+import javax.swing.text.ElementIterator;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.Style;
+import javax.swing.text.StyledDocument;
+import java.io.Writer;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.awt.Color;
+
+/**
+ * MinimalHTMLWriter,
+ * A minimal AbstractWriter implementation for HTML.
+ *
+ * @author Sven de Marothy
+ */
+public class MinimalHTMLWriter extends AbstractWriter
+{
+ private StyledDocument doc;
+ private Deque<String> tagStack;
+ private boolean inFontTag = false;
+
+ /**
+ * Constructs a MinimalHTMLWriter.
+ * @param w - a Writer, for output.
+ * @param doc - the document
+ */
+ public MinimalHTMLWriter(Writer w, StyledDocument doc)
+ {
+ super(w, doc);
+ this.doc = doc;
+ tagStack = new ArrayDeque<String>();
+ }
+
+ /**
+ * Constructs a MinimalHTMLWriter.
+ * @param w - a Writer, for output.
+ * @param doc - the document
+ * @param pos - start position
+ * @param len - length
+ */
+ public MinimalHTMLWriter(Writer w, StyledDocument doc, int pos, int len)
+ {
+ super(w, doc, pos, len);
+ this.doc = doc;
+ tagStack = new ArrayDeque<String>();
+ }
+
+ /**
+ * Starts a span tag.
+ */
+ protected void startFontTag(String style) throws IOException
+ {
+ if( inFontTag() )
+ endOpenTags();
+ writeStartTag("<span style=\""+style+"\">");
+ inFontTag = true;
+ }
+
+ /**
+ * Returns whether the writer is within two span tags.
+ */
+ protected boolean inFontTag()
+ {
+ return inFontTag;
+ }
+
+ /**
+ * Ends a span tag.
+ */
+ protected void endFontTag() throws IOException
+ {
+ writeEndTag("</span>");
+ inFontTag = false;
+ }
+
+ /**
+ * Write the entire HTML document.
+ */
+ public synchronized void write() throws IOException, BadLocationException
+ {
+ writeStartTag("<html>");
+ writeHeader();
+ writeBody();
+ writeEndTag("</html>");
+ }
+
+ /**
+ * Write a start tag and increment the indent.
+ */
+ protected void writeStartTag(String tag) throws IOException
+ {
+ indent();
+ write(tag+NEWLINE);
+ incrIndent();
+ }
+
+ /**
+ * Write an ending tag and decrement the indent.
+ */
+ protected void writeEndTag(String endTag) throws IOException
+ {
+ decrIndent();
+ indent();
+ write(endTag+NEWLINE);
+ }
+
+ /**
+ * Write the HTML header.
+ */
+ protected void writeHeader() throws IOException
+ {
+ writeStartTag("<head>");
+ writeStartTag("<style>");
+ writeStartTag("<!--");
+ writeStyles();
+ writeEndTag("-->");
+ writeEndTag("</style>");
+ writeEndTag("</head>");
+ }
+
+ /**
+ * Write a paragraph start tag.
+ */
+ protected void writeStartParagraph(Element elem) throws IOException
+ {
+ indent();
+ write("<p class=default>"+NEWLINE); // FIXME: Class value = ?
+ incrIndent();
+ }
+
+ /**
+ * Write a paragraph end tag, closes any other open tags.
+ */
+ protected void writeEndParagraph() throws IOException
+ {
+ endOpenTags();
+ writeEndTag("</p>");
+ }
+
+ /**
+ * Writes the body of the HTML document.
+ */
+ protected void writeBody() throws IOException, BadLocationException
+ {
+ writeStartTag("<body>");
+
+ ElementIterator ei = getElementIterator();
+ Element e = ei.first();
+ boolean inParagraph = false;
+ do
+ {
+ if( e.isLeaf() )
+ {
+ boolean hasNL = (getText(e).indexOf(NEWLINE) != -1);
+ if( !inParagraph && hasText( e ) )
+ {
+ writeStartParagraph(e);
+ inParagraph = true;
+ }
+
+ if( hasText( e ) )
+ writeContent(e, true);
+
+ if( hasNL && inParagraph )
+ {
+ writeEndParagraph();
+ inParagraph = false;
+ }
+ else
+ endOpenTags();
+ }
+ }
+ while((e = ei.next()) != null);
+
+ writeEndTag("</body>");
+ }
+
+ protected void text(Element elem) throws IOException, BadLocationException
+ {
+ write( getText(elem).trim() );
+ }
+
+ /**
+ * Write bold, indent and underline tags.
+ */
+ protected void writeHTMLTags(AttributeSet attr) throws IOException
+ {
+ if(attr.getAttribute(StyleConstants.Bold) != null)
+ if(((Boolean)attr.getAttribute(StyleConstants.Bold)).booleanValue())
+ {
+ write("<b>");
+ tagStack.push("</b>");
+ }
+ if(attr.getAttribute(StyleConstants.Italic) != null)
+ if(((Boolean)attr.getAttribute(StyleConstants.Italic)).booleanValue())
+ {
+ write("<i>");
+ tagStack.push("</i>");
+ }
+ if(attr.getAttribute(StyleConstants.Underline) != null)
+ if(((Boolean)attr.getAttribute(StyleConstants.Underline)).booleanValue())
+ {
+ write("<u>");
+ tagStack.push("</u>");
+ }
+ }
+
+ /**
+ * Returns whether the element contains text or not.
+ */
+ protected boolean isText(Element elem)
+ {
+ return (elem.getEndOffset() != elem.getStartOffset());
+ }
+
+ /**
+ * Writes the content of an element.
+ */
+ protected void writeContent(Element elem, boolean needsIndenting)
+ throws IOException, BadLocationException
+ {
+ writeNonHTMLAttributes(elem.getAttributes());
+ if(needsIndenting)
+ indent();
+ writeHTMLTags(elem.getAttributes());
+ if( isText(elem) )
+ text(elem);
+ else
+ writeLeaf(elem);
+
+ endOpenTags();
+ }
+
+ /**
+ * Writes a non-text leaf element.
+ */
+ protected void writeLeaf(Element e) throws IOException
+ {
+ // NOTE: Haven't tested if this is correct.
+ if(e.getName().equals(StyleConstants.IconElementName))
+ writeImage(e);
+ else
+ writeComponent(e);
+ }
+
+ /**
+ * Write the HTML attributes which do not have tag equivalents,
+ * e.g. attributes other than bold/italic/underlined.
+ */
+ protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException
+ {
+ String style = "";
+
+ // Alignment? Background?
+
+ if( StyleConstants.getForeground(attr) != null )
+ style = style + "color: " +
+ getColor(StyleConstants.getForeground(attr)) + "; ";
+
+ style = style + "font-size: "+StyleConstants.getFontSize(attr)+"pt; ";
+ style = style + "font-family: "+StyleConstants.getFontFamily(attr);
+
+ startFontTag(style);
+ }
+
+ /**
+ * Write the styles used.
+ */
+ protected void writeStyles() throws IOException
+ {
+ if(doc instanceof DefaultStyledDocument)
+ {
+ Enumeration<?> styles = ((DefaultStyledDocument)doc).getStyleNames();
+ while(styles.hasMoreElements())
+ writeStyle(doc.getStyle((String)styles.nextElement()));
+ }
+ else
+ { // What else to do here?
+ Style s = doc.getStyle("default");
+ if(s != null)
+ writeStyle( s );
+ }
+ }
+
+ /**
+ * Write a set of attributes.
+ */
+ protected void writeAttributes(AttributeSet attr) throws IOException
+ {
+ Enumeration<?> attribs = attr.getAttributeNames();
+ while(attribs.hasMoreElements())
+ {
+ Object attribName = attribs.nextElement();
+ String name = attribName.toString();
+ String output = getAttribute(name, attr.getAttribute(attribName));
+ if( output != null )
+ {
+ indent();
+ write( output + NEWLINE );
+ }
+ }
+ }
+
+ /**
+ * Deliberately unimplemented, handles component elements.
+ */
+ protected void writeComponent(Element elem) throws IOException
+ {
+ }
+
+ /**
+ * Deliberately unimplemented.
+ * Writes StyleConstants.IconElementName elements.
+ */
+ protected void writeImage(Element elem) throws IOException
+ {
+ }
+
+ // -------------------- Private methods. --------------------------------
+
+ /**
+ * Write a single style attribute
+ */
+ private String getAttribute(String name, Object a) throws IOException
+ {
+ if(name.equals("foreground"))
+ return "foreground:"+getColor((Color)a)+";";
+ if(name.equals("background"))
+ return "background:"+getColor((Color)a)+";";
+ if(name.equals("italic"))
+ return "italic:"+(((Boolean)a).booleanValue() ? "italic;" : ";");
+ if(name.equals("bold"))
+ return "bold:"+(((Boolean)a).booleanValue() ? "bold;" : "normal;");
+ if(name.equals("family"))
+ return "family:" + a + ";";
+ if(name.equals("size"))
+ {
+ int size = ((Integer)a).intValue();
+ int htmlSize;
+ if( size > 24 )
+ htmlSize = 7;
+ else if( size > 18 )
+ htmlSize = 6;
+ else if( size > 14 )
+ htmlSize = 5;
+ else if( size > 12 )
+ htmlSize = 4;
+ else if( size > 10 )
+ htmlSize = 3;
+ else if( size > 8 )
+ htmlSize = 2;
+ else
+ htmlSize = 1;
+
+ return "size:" + htmlSize + ";";
+ }
+
+ return null;
+ }
+
+ /**
+ * Stupid that Color doesn't have a method for this.
+ */
+ private String getColor(Color c)
+ {
+ String r = "00" + Integer.toHexString(c.getRed());
+ r = r.substring(r.length() - 2);
+ String g = "00" + Integer.toHexString(c.getGreen());
+ g = g.substring(g.length() - 2);
+ String b = "00" + Integer.toHexString(c.getBlue());
+ b = b.substring(b.length() - 2);
+ return "#" + r + g + b;
+ }
+
+ /**
+ * Empty the stack of open tags
+ */
+ private void endOpenTags() throws IOException
+ {
+ while(tagStack.size() > 0)
+ write(tagStack.pop());
+
+ if( inFontTag() )
+ {
+ write(""+NEWLINE);
+ endFontTag();
+ }
+ }
+
+ /**
+ * Output a single style
+ */
+ private void writeStyle(Style s) throws IOException
+ {
+ if( s == null )
+ return;
+
+ writeStartTag("p."+s.getName()+" {");
+ writeAttributes(s);
+ writeEndTag("}");
+ }
+
+ private boolean hasText(Element e) throws BadLocationException
+ {
+ return (getText(e).trim().length() > 0);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java b/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java
new file mode 100644
index 000000000..e7fa9f373
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java
@@ -0,0 +1,213 @@
+/* MultiAttributeSet.java -- Multiplexes between a set of AttributeSets
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+/**
+ * An AttributeSet impl that multiplexes between a set of other AttributeSets.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class MultiAttributeSet
+ implements AttributeSet
+{
+
+ /**
+ * The Enumeration for the multiplexed names.
+ */
+ private class MultiNameEnumeration
+ implements Enumeration<Object>
+ {
+ /**
+ * The index of the current AttributeSet.
+ */
+ private int index;
+
+ /**
+ * The names Enumeration of the current AttributeSet.
+ */
+ private Enumeration<?> current;
+
+ /**
+ * Creates a new instance.
+ */
+ MultiNameEnumeration()
+ {
+ index = 0;
+ current = multi[0].getAttributeNames();
+ }
+
+ public boolean hasMoreElements()
+ {
+ return current.hasMoreElements() || index < multi.length - 1;
+ }
+
+ public Object nextElement()
+ {
+ if (! current.hasMoreElements())
+ {
+ if (index < multi.length - 1)
+ {
+ index++;
+ current = multi[index].getAttributeNames();
+ }
+ else
+ throw new NoSuchElementException();
+ }
+ return current.nextElement();
+ }
+
+ }
+
+ /**
+ * The AttributeSets to multiplex.
+ */
+ AttributeSet[] multi;
+
+ /**
+ * Provided for subclasses that need to initialize via {@link #init}.
+ */
+ MultiAttributeSet()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param m the AttributeSets to multiplex
+ */
+ MultiAttributeSet(AttributeSet[] m)
+ {
+ init(m);
+ }
+
+ /**
+ * Provided for subclasses to initialize the attribute set.
+ *
+ * @param m the attributes to multiplex
+ */
+ void init(AttributeSet[] m)
+ {
+ multi = m;
+ }
+
+ public boolean containsAttribute(Object name, Object value)
+ {
+ boolean ret = false;
+ for (int i = 0; i < multi.length && ret == false; i++)
+ {
+ if (multi[i].containsAttribute(name, value))
+ ret = true;
+ }
+ return ret;
+ }
+
+ public boolean containsAttributes(AttributeSet attributes)
+ {
+ boolean ret = true;
+ Enumeration<?> e = attributes.getAttributeNames();
+ while (ret && e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ ret = attributes.getAttribute(key).equals(getAttribute(key));
+ }
+ return ret;
+ }
+
+ public AttributeSet copyAttributes()
+ {
+ SimpleAttributeSet copy = new SimpleAttributeSet();
+ for (int i = 0; i < multi.length; i++)
+ {
+ copy.addAttributes(multi[i]);
+ }
+ return copy;
+ }
+
+ public Object getAttribute(Object key)
+ {
+ Object ret = null;
+ for (int i = 0; i < multi.length && ret == null; i++)
+ {
+ ret = multi[i].getAttribute(key);
+ }
+ return ret;
+ }
+
+ public int getAttributeCount()
+ {
+ int n = 0;
+ for (int i = 0; i < multi.length; i++)
+ {
+ n += multi[i].getAttributeCount();
+ }
+ return n;
+ }
+
+ public Enumeration<?> getAttributeNames()
+ {
+ return new MultiNameEnumeration();
+ }
+
+ public AttributeSet getResolveParent()
+ {
+ return null;
+ }
+
+ public boolean isDefined(Object attrName)
+ {
+ boolean ret = false;
+ for (int i = 0; i < multi.length && ! ret; i++)
+ ret = multi[i].isDefined(attrName);
+ return ret;
+ }
+
+ public boolean isEqual(AttributeSet attr)
+ {
+ return getAttributeCount() == attr.getAttributeCount()
+ && containsAttributes(attr);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/MultiStyle.java b/libjava/classpath/javax/swing/text/html/MultiStyle.java
new file mode 100644
index 000000000..eb6215035
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/MultiStyle.java
@@ -0,0 +1,136 @@
+/* MultiStyle.java -- Multiplexes between several Styles
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.util.Enumeration;
+
+import javax.swing.event.ChangeListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+
+/**
+ * A Style implementation that is able to multiplex between several other
+ * Styles. This is used for CSS style resolving.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class MultiStyle
+ extends MultiAttributeSet
+ implements Style
+{
+
+ // FIXME: Fix the implementation to also return attributes that
+ // are added to this style, etc. However, this is not really needed
+ // now for CSS, but would be nice for correctness.
+
+ /**
+ * The name of the style.
+ */
+ private String name;
+
+ /**
+ * The attributes added to this style.
+ */
+ private SimpleAttributeSet attributes;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param n the name
+ * @param m the styles to multiplex
+ */
+ public MultiStyle(String n, AttributeSet[] m)
+ {
+ super(m);
+ name = n;
+ attributes = new SimpleAttributeSet();
+ }
+
+ /**
+ * Returns the name of the style.
+ *
+ * @return the name of the style
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement.
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement.
+ }
+
+ public void addAttribute(Object name, Object value)
+ {
+ attributes.addAttribute(name, value);
+ }
+
+ public void addAttributes(AttributeSet atts)
+ {
+ attributes.addAttributes(atts);
+ }
+
+ public void removeAttribute(Object name)
+ {
+ attributes.removeAttribute(name);
+ }
+
+ public void removeAttributes(Enumeration<?> names)
+ {
+ attributes.removeAttribute(names);
+ }
+
+ public void removeAttributes(AttributeSet atts)
+ {
+ attributes.removeAttribute(atts);
+ }
+
+ public void setResolveParent(AttributeSet parent)
+ {
+ // TODO: Implement.
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/NullView.java b/libjava/classpath/javax/swing/text/html/NullView.java
new file mode 100644
index 000000000..86ce0c10f
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/NullView.java
@@ -0,0 +1,102 @@
+/* NullView.java -- A dummy view that renders nothing
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.awt.Graphics;
+import java.awt.Shape;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.Position.Bias;
+
+/**
+ * A dummy view that renders nothing. This is used for invisible HTML elements
+ * like <head>.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class NullView
+ extends View
+{
+
+ /**
+ * Creates a new NullView.
+ *
+ * @param elem the element
+ */
+ public NullView(Element elem)
+ {
+ super(elem);
+ }
+
+ /**
+ * Does nothing.
+ */
+ public void paint(Graphics g, Shape s)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns zero for both directions.
+ */
+ public float getPreferredSpan(int axis)
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the allocation of this view, which should be empty anyway.
+ */
+ public Shape modelToView(int pos, Shape a, Bias b)
+ throws BadLocationException
+ {
+ return a;
+ }
+
+ /**
+ * Returns the start offset of the element.
+ */
+ public int viewToModel(float x, float y, Shape a, Bias[] b)
+ {
+ return getElement().getStartOffset();
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ObjectView.java b/libjava/classpath/javax/swing/text/html/ObjectView.java
new file mode 100644
index 000000000..9d900441b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ObjectView.java
@@ -0,0 +1,110 @@
+/* ObjectView.java -- A view for HTML object tags
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.awt.Component;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.ComponentView;
+import javax.swing.text.Element;
+
+/**
+ * A view for HTML <code>&lt;object&gt;</code> tags.
+ *
+ * This is a {@link ComponentView} that creates special components depending
+ * on the object specification. If the object tag has a classid attribute, then
+ * this view will try to load the class specified by this attribute using the
+ * classloader that loaded the associated document. If the class could be
+ * loaded, an instance is created and the type narrowed to {@link Component}.
+ *
+ * It is also possible to set bean properties on the created component using
+ * nested <code>&lt;param&gt;</code> tags. For example:
+ * <pre>
+ * <object classid="javax.swing.JLabel">
+ * <param name="text" value="sample text">
+ * </object>
+ * </pre>
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class ObjectView extends ComponentView
+{
+
+ /**
+ * Creates a new <code>ObjectView</code>.
+ *
+ * @param el the element for which to create a view
+ */
+ public ObjectView(Element el)
+ {
+ super(el);
+ }
+
+ /**
+ * Creates a component based on the specification in the element of this
+ * view. See the class description for details.
+ */
+ protected Component createComponent()
+ {
+ Component comp = null;
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ String classId = (String) atts.getAttribute("classid");
+ try
+ {
+ Class<?> objectClass = Class.forName(classId);
+ Object instance = objectClass.newInstance();
+ comp = (Component) instance;
+ }
+ catch (ClassNotFoundException ex)
+ {
+ // Ignored.
+ }
+ catch (IllegalAccessException ex)
+ {
+ // Ignored.
+ }
+ catch (InstantiationException ex)
+ {
+ // Ignored.
+ }
+ // FIXME: Handle param tags and set bean properties accordingly.
+ return comp;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/Option.java b/libjava/classpath/javax/swing/text/html/Option.java
new file mode 100644
index 000000000..18d5c2bd8
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/Option.java
@@ -0,0 +1,159 @@
+/* Option.java -- Value class for <option> list model elements
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import javax.swing.text.AttributeSet;
+
+/**
+ * Value class for the combobox model that renders <code>&lt;option&gt;</code>
+ * elements.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class Option
+{
+
+ /**
+ * The attributes of the <option> tag.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The label.
+ */
+ private String label;
+
+ /**
+ * The selected state of this label.
+ */
+ private boolean selected;
+
+ /**
+ * Creates a new <code>Option</code> instance that uses the specified
+ * tag attributes.
+ *
+ * @param attr the attributes to use
+ */
+ public Option(AttributeSet attr)
+ {
+ // Protect the attribute set.
+ attributes = attr.copyAttributes();
+ label = null;
+ selected = attr.getAttribute(HTML.Attribute.SELECTED) != null;
+ }
+
+ /**
+ * Sets the label to use for this <code>&lt;option&gt;</code> tag.
+ *
+ * @param l the label to set
+ */
+ public void setLabel(String l)
+ {
+ label = l;
+ }
+
+ /**
+ * Returns the label of this <code>&lt;option&gt;</code> tag.
+ *
+ * @return the label of this <code>&lt;option&gt;</code> tag
+ */
+ public String getLabel()
+ {
+ return label;
+ }
+
+ /**
+ * Returns the attributes used to render this <code>&lt;option&gt;</code>
+ * tag.
+ *
+ * @return the attributes used to render this <code>&lt;option&gt;</code> tag
+ */
+ public AttributeSet getAttributes()
+ {
+ return attributes;
+ }
+
+ /**
+ * Returns a string representation of this <code>&lt;option&gt;</code> tag.
+ * This returns the <code>label</code> property.
+ *
+ * @return a string representation of this <code>&lt;option&gt;</code> tag
+ */
+ public String toString()
+ {
+ return label;
+ }
+
+ /**
+ * Sets the selected state of this <code>&lt;option&gt;</code> tag.
+ *
+ * @param s the selected state to set
+ */
+ protected void setSelection(boolean s)
+ {
+ selected = s;
+ }
+
+ /**
+ * Returns <code>true</code> when this option is selected, <code>false</code>
+ * otherwise.
+ *
+ * @return <code>true</code> when this option is selected, <code>false</code>
+ * otherwise
+ */
+ public boolean isSelected()
+ {
+ return selected;
+ }
+
+ /**
+ * Returns the string associated with the <code>value</code> attribute or
+ * the label, if no such attribute is specified.
+ *
+ * @return the string associated with the <code>value</code> attribute or
+ * the label, if no such attribute is specified
+ */
+ public String getValue()
+ {
+ String value = (String) attributes.getAttribute(HTML.Attribute.VALUE);
+ if (value == null)
+ value = label;
+ return value;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/ParagraphView.java b/libjava/classpath/javax/swing/text/html/ParagraphView.java
new file mode 100644
index 000000000..cab8edb28
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ParagraphView.java
@@ -0,0 +1,322 @@
+/* ParagraphView.java -- Renders a paragraph in HTML
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import gnu.javax.swing.text.html.css.Length;
+
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SizeRequirements;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
+
+/**
+ * Renders a paragraph in HTML. This is a subclass of
+ * {@link javax.swing.text.ParagraphView} with some adjustments for
+ * understanding stylesheets.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class ParagraphView
+ extends javax.swing.text.ParagraphView
+{
+
+ /**
+ * The attributes used by this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The stylesheet's box painter.
+ */
+ private StyleSheet.BoxPainter painter;
+
+ /**
+ * The width as specified in the stylesheet or null if not specified.
+ */
+ private Length cssWidth;
+
+ /**
+ * The height as specified in the stylesheet or null if not specified.
+ */
+ private Length cssHeight;
+
+ /**
+ * Creates a new ParagraphView for the specified element.
+ *
+ * @param element the element
+ */
+ public ParagraphView(Element element)
+ {
+ super(element);
+ }
+
+ /**
+ * Sets the parent of this view. This is implemented to call the parent
+ * functionality and then trigger {@link #setPropertiesFromAttributes} in
+ * order to load the stylesheet attributes.
+ *
+ * @param parent the parent view to set
+ */
+ public void setParent(View parent)
+ {
+ super.setParent(parent);
+ if (parent != null)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Returns the attributes used by this view. This is implemented to multiplex
+ * the attributes of the model with the attributes of the stylesheet.
+ */
+ public AttributeSet getAttributes()
+ {
+ if (attributes == null)
+ {
+ attributes = getStyleSheet().getViewAttributes(this);
+ }
+ return attributes;
+ }
+
+ /**
+ * Loads the visual properties of the ParagraphView from the element's
+ * attributes and the stylesheet of the HTML document.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ super.setPropertiesFromAttributes();
+
+ // Fetch CSS attributes.
+ attributes = getAttributes();
+ if (attributes != null)
+ {
+ super.setPropertiesFromAttributes();
+ Object o = attributes.getAttribute(CSS.Attribute.TEXT_ALIGN);
+ if (o != null)
+ {
+ String align = o.toString();
+ if (align.equals("left"))
+ setJustification(StyleConstants.ALIGN_LEFT);
+ else if (align.equals("right"))
+ setJustification(StyleConstants.ALIGN_RIGHT);
+ else if (align.equals("center"))
+ setJustification(StyleConstants.ALIGN_CENTER);
+ else if (align.equals("justify"))
+ setJustification(StyleConstants.ALIGN_JUSTIFIED);
+ }
+
+ // Fetch StyleSheet's box painter.
+ painter = getStyleSheet().getBoxPainter(attributes);
+ setInsets((short) painter.getInset(TOP, this),
+ (short) painter.getInset(LEFT, this),
+ (short) painter.getInset(BOTTOM, this),
+ (short) painter.getInset(RIGHT, this));
+
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(attributes);
+ float exBase = ss.getEXBase(attributes);
+ cssWidth = (Length) attributes.getAttribute(CSS.Attribute.WIDTH);
+ if (cssWidth != null)
+ cssWidth.setFontBases(emBase, exBase);
+ cssHeight = (Length) attributes.getAttribute(CSS.Attribute.WIDTH);
+ if (cssHeight != null)
+ cssHeight.setFontBases(emBase, exBase);
+ }
+ }
+
+ /**
+ * Returns the stylesheet used by this view.
+ *
+ * @return the stylesheet used by this view
+ */
+ protected StyleSheet getStyleSheet()
+ {
+ Document doc = getDocument();
+ StyleSheet styleSheet = null;
+ if (doc instanceof HTMLDocument)
+ styleSheet = ((HTMLDocument) doc).getStyleSheet();
+ return styleSheet;
+ }
+
+ /**
+ * Calculates the minor axis requirements of this view. This is implemented
+ * to return the super class'es requirements and modifies the minimumSpan
+ * slightly so that it is not smaller than the length of the longest word.
+ *
+ * @param axis the axis
+ * @param r the SizeRequirements object to be used as return parameter;
+ * if <code>null</code> a new one will be created
+ *
+ * @return the requirements along the minor layout axis
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ r = super.calculateMinorAxisRequirements(axis, r);
+ if (! setCSSSpan(r, axis))
+ {
+ int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
+ : getTopInset() + getBottomInset();
+ r.minimum -= margin;
+ r.preferred -= margin;
+ r.maximum -= margin;
+ }
+ return r;
+ }
+
+ /**
+ * Sets the span on the SizeRequirements object according to the
+ * according CSS span value, when it is set.
+ *
+ * @param r the size requirements
+ * @param axis the axis
+ *
+ * @return <code>true</code> when the CSS span has been set,
+ * <code>false</code> otherwise
+ */
+ private boolean setCSSSpan(SizeRequirements r, int axis)
+ {
+ boolean ret = false;
+ if (axis == X_AXIS)
+ {
+ if (cssWidth != null && ! cssWidth.isPercentage())
+ {
+ r.minimum = (int) cssWidth.getValue();
+ r.preferred = (int) cssWidth.getValue();
+ r.maximum = (int) cssWidth.getValue();
+ ret = true;
+ }
+ }
+ else
+ {
+ if (cssHeight != null && ! cssWidth.isPercentage())
+ {
+ r.minimum = (int) cssHeight.getValue();
+ r.preferred = (int) cssHeight.getValue();
+ r.maximum = (int) cssHeight.getValue();
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Determines if this view is visible or not. If none of the children is
+ * visible and the only visible child is the break that ends the paragraph,
+ * this paragraph is not considered to be visible.
+ *
+ * @return the visibility of this paragraph
+ */
+ public boolean isVisible()
+ {
+ // FIXME: Implement the above specified behaviour.
+ return super.isVisible();
+ }
+
+ /**
+ * Paints this view. This paints the box using the stylesheet's
+ * box painter for this view and delegates to the super class paint()
+ * afterwards.
+ *
+ * @param g the graphics object
+ * @param a the current allocation of this view
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ if (a != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ painter.paint(g, r.x, r.y, r.width, r.height, this);
+ }
+ super.paint(g, a);
+ }
+
+ /**
+ * Returns the preferred span of this view. If this view is not visible,
+ * we return <code>0</code>, otherwise the super class is called.
+ *
+ * @param axis the axis
+ *
+ * @return the preferred span of this view
+ */
+ public float getPreferredSpan(int axis)
+ {
+ float span = 0;
+ if (isVisible())
+ span = super.getPreferredSpan(axis);
+ return span;
+ }
+
+ /**
+ * Returns the minimum span of this view. If this view is not visible,
+ * we return <code>0</code>, otherwise the super class is called.
+ *
+ * @param axis the axis
+ *
+ * @return the minimum span of this view
+ */
+ public float getMinimumSpan(int axis)
+ {
+ float span = 0;
+ if (isVisible())
+ span = super.getMinimumSpan(axis);
+ return span;
+ }
+
+ /**
+ * Returns the maximum span of this view. If this view is not visible,
+ * we return <code>0</code>, otherwise the super class is called.
+ *
+ * @param axis the axis
+ *
+ * @return the maximum span of this view
+ */
+ public float getMaximumSpan(int axis)
+ {
+ float span = 0;
+ if (isVisible())
+ span = super.getMaximumSpan(axis);
+ return span;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/ResetableModel.java b/libjava/classpath/javax/swing/text/html/ResetableModel.java
new file mode 100644
index 000000000..17f65b97d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetableModel.java
@@ -0,0 +1,50 @@
+/* ResetableModel.java -- Form models that can be resetted
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+/**
+ * Form models that can be resetted implement this.
+ */
+interface ResetableModel
+{
+ /**
+ * Resets the model.
+ */
+ void reset();
+}
diff --git a/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java b/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java
new file mode 100644
index 000000000..6177f9b86
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java
@@ -0,0 +1,82 @@
+/* ResetablePlainDocument.java -- A plain document for use in the HTML renderer
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.PlainDocument;
+
+/**
+ * A PlainDocument that can be resetted.
+ */
+class ResetablePlainDocument
+ extends PlainDocument
+ implements ResetableModel
+{
+ /**
+ * The initial text.
+ */
+ private String initial;
+
+ /**
+ * Stores the initial text.
+ *
+ * @param text the initial text
+ */
+ void setInitialText(String text)
+ {
+ initial = text;
+ }
+
+ /**
+ * Resets the model.
+ */
+ public void reset()
+ {
+ try
+ {
+ replace(0, getLength(), initial, null);
+ }
+ catch (BadLocationException ex)
+ {
+ // Shouldn't happen.
+ assert false;
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java b/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java
new file mode 100644
index 000000000..637ece151
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java
@@ -0,0 +1,70 @@
+/* ResetableToggleButtonModel.java -- A toggle button model with reset support
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import javax.swing.JToggleButton.ToggleButtonModel;
+
+class ResetableToggleButtonModel
+ extends ToggleButtonModel
+ implements ResetableModel
+{
+
+ /**
+ * The initial state.
+ */
+ private boolean initial;
+
+ /**
+ * Sets the initial selection value.
+ *
+ * @param state the initial value
+ */
+ public void setInitial(boolean state)
+ {
+ initial = state;
+ }
+
+ /**
+ * Resets the model.
+ */
+ public void reset()
+ {
+ setSelected(initial);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java b/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java
new file mode 100644
index 000000000..999746413
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java
@@ -0,0 +1,84 @@
+/* SelectComboBoxModel.java -- A special ComboBoxModel for use in HTML renderer
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import javax.swing.DefaultComboBoxModel;
+
+/**
+ * A special ComboBoxModel that supports storing the initial value so that
+ * the combobox can be resetted later.
+ */
+class SelectComboBoxModel
+ extends DefaultComboBoxModel
+ implements ResetableModel
+{
+
+ /**
+ * The initial selection.
+ */
+ private Option initial;
+
+ /**
+ * Sets the initial selection.
+ *
+ * @param option the initial selection
+ */
+ void setInitialSelection(Option option)
+ {
+ initial = option;
+ }
+
+ /**
+ * Returns the initial selection.
+ *
+ * @return the initial selection
+ */
+ Option getInitialSelection()
+ {
+ return initial;
+ }
+
+ /**
+ * Resets the model.
+ */
+ public void reset()
+ {
+ setSelectedItem(initial);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/SelectListModel.java b/libjava/classpath/javax/swing/text/html/SelectListModel.java
new file mode 100644
index 000000000..23bfaa11b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/SelectListModel.java
@@ -0,0 +1,106 @@
+/* OptionListModel.java -- A special ListModel for use in the HTML renderer
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.util.BitSet;
+
+import javax.swing.DefaultListModel;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.ListSelectionModel;
+
+/**
+ * A special list model that encapsulates its selection model and supports
+ * storing of the initial value so that it can be resetted.
+ */
+class SelectListModel
+ extends DefaultListModel
+ implements ResetableModel
+{
+ /**
+ * The selection model.
+ */
+ private DefaultListSelectionModel selectionModel;
+
+ /**
+ * The initial selection.
+ */
+ private BitSet initialSelection;
+
+ /**
+ * Creates a new SelectListModel.
+ */
+ SelectListModel()
+ {
+ selectionModel = new DefaultListSelectionModel();
+ initialSelection = new BitSet();
+ }
+
+ /**
+ * Sets the initial selection.
+ *
+ * @param init the initial selection
+ */
+ void addInitialSelection(int init)
+ {
+ initialSelection.set(init);
+ }
+
+ /**
+ * Resets the model.
+ */
+ public void reset()
+ {
+ selectionModel.clearSelection();
+ for (int i = initialSelection.size(); i >= 0; i--)
+ {
+ if (initialSelection.get(i))
+ selectionModel.addSelectionInterval(i, i);
+ }
+ }
+
+ /**
+ * Returns the associated selection model.
+ *
+ * @return the associated selection model
+ */
+ ListSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/StyleSheet.java b/libjava/classpath/javax/swing/text/html/StyleSheet.java
new file mode 100644
index 000000000..5cf015bc5
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/StyleSheet.java
@@ -0,0 +1,1455 @@
+/* StyleSheet.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import gnu.javax.swing.text.html.css.BorderWidth;
+import gnu.javax.swing.text.html.css.CSSColor;
+import gnu.javax.swing.text.html.css.CSSParser;
+import gnu.javax.swing.text.html.css.CSSParserCallback;
+import gnu.javax.swing.text.html.css.FontSize;
+import gnu.javax.swing.text.html.css.FontStyle;
+import gnu.javax.swing.text.html.css.FontWeight;
+import gnu.javax.swing.text.html.css.Length;
+import gnu.javax.swing.text.html.css.Selector;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.Rectangle2D;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.border.Border;
+import javax.swing.event.ChangeListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+import javax.swing.text.View;
+
+
+/**
+ * This class adds support for defining the visual characteristics of HTML views
+ * being rendered. This enables views to be customized by a look-and-feel, mulitple
+ * views over the same model can be rendered differently. Each EditorPane has its
+ * own StyleSheet, but by default one sheet will be shared by all of the HTMLEditorKit
+ * instances. An HTMLDocument can also have a StyleSheet, which holds specific CSS
+ * specs.
+ *
+ * In order for Views to store less state and therefore be more lightweight,
+ * the StyleSheet can act as a factory for painters that handle some of the
+ * rendering tasks. Since the StyleSheet may be used by views over multiple
+ * documents the HTML attributes don't effect the selector being used.
+ *
+ * The rules are stored as named styles, and other information is stored to
+ * translate the context of an element to a rule.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+public class StyleSheet extends StyleContext
+{
+
+ /**
+ * Parses CSS stylesheets using the parser in gnu/javax/swing/html/css.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ class CSSStyleSheetParserCallback
+ implements CSSParserCallback
+ {
+ /**
+ * The current styles.
+ */
+ private CSSStyle[] styles;
+
+ /**
+ * The precedence of the stylesheet to be parsed.
+ */
+ private int precedence;
+
+ /**
+ * Creates a new CSS parser. This parser parses a CSS stylesheet with
+ * the specified precedence.
+ *
+ * @param prec the precedence, according to the constants defined in
+ * CSSStyle
+ */
+ CSSStyleSheetParserCallback(int prec)
+ {
+ precedence = prec;
+ }
+
+ /**
+ * Called at the beginning of a statement.
+ *
+ * @param sel the selector
+ */
+ public void startStatement(Selector[] sel)
+ {
+ styles = new CSSStyle[sel.length];
+ for (int i = 0; i < sel.length; i++)
+ styles[i] = new CSSStyle(precedence, sel[i]);
+ }
+
+ /**
+ * Called at the end of a statement.
+ */
+ public void endStatement()
+ {
+ for (int i = 0; i < styles.length; i++)
+ css.add(styles[i]);
+ styles = null;
+ }
+
+ /**
+ * Called when a declaration is parsed.
+ *
+ * @param property the property
+ * @param value the value
+ */
+ public void declaration(String property, String value)
+ {
+ CSS.Attribute cssAtt = CSS.getAttribute(property);
+ Object val = CSS.getValue(cssAtt, value);
+ for (int i = 0; i < styles.length; i++)
+ {
+ CSSStyle style = styles[i];
+ CSS.addInternal(style, cssAtt, value);
+ if (cssAtt != null)
+ style.addAttribute(cssAtt, val);
+ }
+ }
+
+ }
+
+ /**
+ * Represents a style that is defined by a CSS rule.
+ */
+ private class CSSStyle
+ extends SimpleAttributeSet
+ implements Style, Comparable<CSSStyle>
+ {
+
+ static final int PREC_UA = 0;
+ static final int PREC_NORM = 100000;
+ static final int PREC_AUTHOR_NORMAL = 200000;
+ static final int PREC_AUTHOR_IMPORTANT = 300000;
+ static final int PREC_USER_IMPORTANT = 400000;
+
+ /**
+ * The priority of this style when matching CSS selectors.
+ */
+ private int precedence;
+
+ /**
+ * The selector for this rule.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Selector selector;
+
+ CSSStyle(int prec, Selector sel)
+ {
+ precedence = prec;
+ selector = sel;
+ }
+
+ public String getName()
+ {
+ // TODO: Implement this for correctness.
+ return null;
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement this for correctness.
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement this for correctness.
+ }
+
+ /**
+ * Sorts the rule according to the style's precedence and the
+ * selectors specificity.
+ */
+ public int compareTo(CSSStyle other)
+ {
+ return other.precedence + other.selector.getSpecificity()
+ - precedence - selector.getSpecificity();
+ }
+
+ }
+
+ /** The base URL */
+ URL base;
+
+ /** Base font size (int) */
+ int baseFontSize;
+
+ /**
+ * The linked style sheets stored.
+ */
+ private ArrayList<StyleSheet> linked;
+
+ /**
+ * Maps element names (selectors) to AttributSet (the corresponding style
+ * information).
+ */
+ ArrayList<CSSStyle> css = new ArrayList<CSSStyle>();
+
+ /**
+ * Maps selectors to their resolved styles.
+ */
+ private HashMap<String,Style> resolvedStyles;
+
+ /**
+ * Constructs a StyleSheet.
+ */
+ public StyleSheet()
+ {
+ super();
+ baseFontSize = 4; // Default font size from CSS
+ resolvedStyles = new HashMap<String,Style>();
+ }
+
+ /**
+ * Gets the style used to render the given tag. The element represents the tag
+ * and can be used to determine the nesting, where the attributes will differ
+ * if there is nesting inside of elements.
+ *
+ * @param t - the tag to translate to visual attributes
+ * @param e - the element representing the tag
+ * @return the set of CSS attributes to use to render the tag.
+ */
+ public Style getRule(HTML.Tag t, Element e)
+ {
+ // Create list of the element and all of its parents, starting
+ // with the bottommost element.
+ ArrayList<Element> path = new ArrayList<Element>();
+ Element el;
+ AttributeSet atts;
+ for (el = e; el != null; el = el.getParentElement())
+ path.add(el);
+
+ // Create fully qualified selector.
+ StringBuilder selector = new StringBuilder();
+ int count = path.size();
+ // We append the actual element after this loop.
+ for (int i = count - 1; i > 0; i--)
+ {
+ el = path.get(i);
+ atts = el.getAttributes();
+ Object name = atts.getAttribute(StyleConstants.NameAttribute);
+ selector.append(name.toString());
+ if (atts.isDefined(HTML.Attribute.ID))
+ {
+ selector.append('#');
+ selector.append(atts.getAttribute(HTML.Attribute.ID));
+ }
+ if (atts.isDefined(HTML.Attribute.CLASS))
+ {
+ selector.append('.');
+ selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
+ }
+ selector.append(' ');
+ }
+ selector.append(t.toString());
+ el = path.get(0);
+ atts = el.getAttributes();
+ // For leaf elements, we have to fetch the tag specific attributes.
+ if (el.isLeaf())
+ {
+ Object o = atts.getAttribute(t);
+ if (o instanceof AttributeSet)
+ atts = (AttributeSet) o;
+ else
+ atts = null;
+ }
+ if (atts != null)
+ {
+ if (atts.isDefined(HTML.Attribute.ID))
+ {
+ selector.append('#');
+ selector.append(atts.getAttribute(HTML.Attribute.ID));
+ }
+ if (atts.isDefined(HTML.Attribute.CLASS))
+ {
+ selector.append('.');
+ selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
+ }
+ }
+ return getResolvedStyle(selector.toString(), path, t);
+ }
+
+ /**
+ * Fetches a resolved style. If there is no resolved style for the
+ * specified selector, the resolve the style using
+ * {@link #resolveStyle(String, List, HTML.Tag)}.
+ *
+ * @param selector the selector for which to resolve the style
+ * @param path the Element path, used in the resolving algorithm
+ * @param tag the tag for which to resolve
+ *
+ * @return the resolved style
+ */
+ private Style getResolvedStyle(String selector, List<Element> path, HTML.Tag tag)
+ {
+ Style style = resolvedStyles.get(selector);
+ if (style == null)
+ style = resolveStyle(selector, path, tag);
+ return style;
+ }
+
+ /**
+ * Resolves a style. This creates arrays that hold the tag names,
+ * class and id attributes and delegates the work to
+ * {@link #resolveStyle(String, String[], List<Map<String,String>>)}.
+ *
+ * @param selector the selector
+ * @param path the Element path
+ * @param tag the tag
+ *
+ * @return the resolved style
+ */
+ private Style resolveStyle(String selector, List<Element> path, HTML.Tag tag)
+ {
+ int count = path.size();
+ String[] tags = new String[count];
+ List<Map<String,String>> attributes =
+ new ArrayList<Map<String,String>>(count);
+ for (int i = 0; i < count; i++)
+ {
+ Element el = path.get(i);
+ AttributeSet atts = el.getAttributes();
+ if (i == 0 && el.isLeaf())
+ {
+ Object o = atts.getAttribute(tag);
+ if (o instanceof AttributeSet)
+ atts = (AttributeSet) o;
+ else
+ atts = null;
+ }
+ if (atts != null)
+ {
+ HTML.Tag t =
+ (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
+ if (t != null)
+ tags[i] = t.toString();
+ else
+ tags[i] = null;
+ attributes.set(i, attributeSetToMap(atts));
+ }
+ else
+ {
+ tags[i] = null;
+ }
+ }
+ tags[0] = tag.toString();
+ return resolveStyle(selector, tags, attributes);
+ }
+
+ /**
+ * Performs style resolving.
+ *
+ * @param selector the selector
+ * @param tags the tags
+ * @param attributes the attributes of the tags
+ *
+ * @return the resolved style
+ */
+ private Style resolveStyle(String selector, String[] tags,
+ List<Map<String,String>> attributes)
+ {
+ // FIXME: This style resolver is not correct. But it works good enough for
+ // the default.css.
+ ArrayList<CSSStyle> styles = new ArrayList<CSSStyle>();
+ for (CSSStyle style : css)
+ {
+ if (style.selector.matches(tags, attributes))
+ styles.add(style);
+ }
+
+ // Add styles from linked stylesheets.
+ if (linked != null)
+ {
+ for (int i = linked.size() - 1; i >= 0; i--)
+ {
+ StyleSheet ss = linked.get(i);
+ for (int j = ss.css.size() - 1; j >= 0; j--)
+ {
+ CSSStyle style = ss.css.get(j);
+ if (style.selector.matches(tags, attributes))
+ styles.add(style);
+ }
+ }
+ }
+
+ // Sort selectors.
+ Collections.sort(styles);
+ Style[] styleArray = styles.toArray(new Style[styles.size()]);
+ Style resolved = new MultiStyle(selector, styleArray);
+ resolvedStyles.put(selector, resolved);
+ return resolved;
+ }
+
+ /**
+ * Gets the rule that best matches the selector. selector is a space
+ * separated String of element names. The attributes of the returned
+ * Style will change as rules are added and removed.
+ *
+ * @param selector - the element names separated by spaces
+ * @return the set of CSS attributes to use to render
+ */
+ public Style getRule(String selector)
+ {
+ CSSStyle best = null;
+ for (Iterator<CSSStyle> i = css.iterator(); i.hasNext();)
+ {
+ CSSStyle style = i.next();
+ if (style.compareTo(best) < 0)
+ best = style;
+ }
+ return best;
+ }
+
+ /**
+ * Adds a set of rules to the sheet. The rules are expected to be in valid
+ * CSS format. This is called as a result of parsing a <style> tag
+ *
+ * @param rule - the rule to add to the sheet
+ */
+ public void addRule(String rule)
+ {
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
+ // FIXME: Handle ref.
+ StringReader in = new StringReader(rule);
+ CSSParser parser = new CSSParser(in, cb);
+ try
+ {
+ parser.parse();
+ }
+ catch (IOException ex)
+ {
+ // Shouldn't happen. And if, then don't let it bork the outside code.
+ }
+ // Clean up resolved styles cache so that the new styles are recognized
+ // on next stylesheet request.
+ resolvedStyles.clear();
+ }
+
+ /**
+ * Translates a CSS declaration into an AttributeSet. This is called
+ * as a result of encountering an HTML style attribute.
+ *
+ * @param decl - the declaration to get
+ * @return the AttributeSet representing the declaration
+ */
+ public AttributeSet getDeclaration(String decl)
+ {
+ if (decl == null)
+ return SimpleAttributeSet.EMPTY;
+ // FIXME: Not implemented.
+ return null;
+ }
+
+ /**
+ * Loads a set of rules that have been specified in terms of CSS grammar.
+ * If there are any conflicts with existing rules, the new rule is added.
+ *
+ * @param in - the stream to read the CSS grammar from.
+ * @param ref - the reference URL. It is the location of the stream, it may
+ * be null. All relative URLs specified in the stream will be based upon this
+ * parameter.
+ * @throws IOException - For any IO error while reading
+ */
+ public void loadRules(Reader in, URL ref)
+ throws IOException
+ {
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_UA);
+ // FIXME: Handle ref.
+ CSSParser parser = new CSSParser(in, cb);
+ parser.parse();
+ }
+
+ /**
+ * Gets a set of attributes to use in the view. This is a set of
+ * attributes that can be used for View.getAttributes
+ *
+ * @param v - the view to get the set for
+ * @return the AttributeSet to use in the view.
+ */
+ public AttributeSet getViewAttributes(View v)
+ {
+ return new ViewAttributeSet(v, this);
+ }
+
+ /**
+ * Removes a style previously added.
+ *
+ * @param nm - the name of the style to remove
+ */
+ public void removeStyle(String nm)
+ {
+ // FIXME: Not implemented.
+ super.removeStyle(nm);
+ }
+
+ /**
+ * Adds the rules from ss to those of the receiver. ss's rules will
+ * override the old rules. An added StyleSheet will never override the rules
+ * of the receiving style sheet.
+ *
+ * @param ss - the new StyleSheet.
+ */
+ public void addStyleSheet(StyleSheet ss)
+ {
+ if (linked == null)
+ linked = new ArrayList<StyleSheet>();
+ linked.add(ss);
+ }
+
+ /**
+ * Removes ss from those of the receiver
+ *
+ * @param ss - the StyleSheet to remove.
+ */
+ public void removeStyleSheet(StyleSheet ss)
+ {
+ if (linked != null)
+ {
+ linked.remove(ss);
+ }
+ }
+
+ /**
+ * Returns an array of the linked StyleSheets. May return null.
+ *
+ * @return - An array of the linked StyleSheets.
+ */
+ public StyleSheet[] getStyleSheets()
+ {
+ StyleSheet[] linkedSS;
+ if (linked != null)
+ {
+ linkedSS = new StyleSheet[linked.size()];
+ linkedSS = linked.toArray(linkedSS);
+ }
+ else
+ {
+ linkedSS = null;
+ }
+ return linkedSS;
+ }
+
+ /**
+ * Imports a style sheet from the url. The rules are directly added to the
+ * receiver. This is usually called when a <link> tag is resolved in an
+ * HTML document.
+ *
+ * @param url the URL to import the StyleSheet from
+ */
+ public void importStyleSheet(URL url)
+ {
+ try
+ {
+ InputStream in = url.openStream();
+ Reader r = new BufferedReader(new InputStreamReader(in));
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
+ CSSParser parser = new CSSParser(r, cb);
+ parser.parse();
+ }
+ catch (IOException ex)
+ {
+ // We can't do anything about it I guess.
+ }
+ }
+
+ /**
+ * Sets the base url. All import statements that are relative, will be
+ * relative to base.
+ *
+ * @param base -
+ * the base URL.
+ */
+ public void setBase(URL base)
+ {
+ this.base = base;
+ }
+
+ /**
+ * Gets the base url.
+ *
+ * @return - the base
+ */
+ public URL getBase()
+ {
+ return base;
+ }
+
+ /**
+ * Adds a CSS attribute to the given set.
+ *
+ * @param attr - the attribute set
+ * @param key - the attribute to add
+ * @param value - the value of the key
+ */
+ public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key,
+ String value)
+ {
+ Object val = CSS.getValue(key, value);
+ CSS.addInternal(attr, key, value);
+ attr.addAttribute(key, val);
+ }
+
+ /**
+ * Adds a CSS attribute to the given set.
+ * This method parses the value argument from HTML based on key.
+ * Returns true if it finds a valid value for the given key,
+ * and false otherwise.
+ *
+ * @param attr - the attribute set
+ * @param key - the attribute to add
+ * @param value - the value of the key
+ * @return true if a valid value was found.
+ */
+ public boolean addCSSAttributeFromHTML(MutableAttributeSet attr, CSS.Attribute key,
+ String value)
+ {
+ // FIXME: Need to parse value from HTML based on key.
+ attr.addAttribute(key, value);
+ return attr.containsAttribute(key, value);
+ }
+
+ /**
+ * Converts a set of HTML attributes to an equivalent set of CSS attributes.
+ *
+ * @param htmlAttrSet - the set containing the HTML attributes.
+ * @return the set of CSS attributes
+ */
+ public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet)
+ {
+ AttributeSet cssAttr = htmlAttrSet.copyAttributes();
+
+ // The HTML align attribute maps directly to the CSS text-align attribute.
+ Object o = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.TEXT_ALIGN, o);
+
+ // The HTML width attribute maps directly to CSS width.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.WIDTH);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.WIDTH,
+ new Length(o.toString()));
+
+ // The HTML height attribute maps directly to CSS height.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.HEIGHT);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.HEIGHT,
+ new Length(o.toString()));
+
+ o = htmlAttrSet.getAttribute(HTML.Attribute.NOWRAP);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.WHITE_SPACE, "nowrap");
+
+ // Map cellspacing attr of tables to CSS border-spacing.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.CELLSPACING);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_SPACING,
+ new Length(o.toString()));
+
+ // For table cells and headers, fetch the cellpadding value from the
+ // parent table and set it as CSS padding attribute.
+ HTML.Tag tag = (HTML.Tag)
+ htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
+ if ((tag == HTML.Tag.TD || tag == HTML.Tag.TH)
+ && htmlAttrSet instanceof Element)
+ {
+ Element el = (Element) htmlAttrSet;
+ AttributeSet tableAttrs = el.getParentElement().getParentElement()
+ .getAttributes();
+ o = tableAttrs.getAttribute(HTML.Attribute.CELLPADDING);
+ if (o != null)
+ {
+ Length l = new Length(o.toString());
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_BOTTOM, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_LEFT, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_RIGHT, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_TOP, l);
+ }
+ o = tableAttrs.getAttribute(HTML.Attribute.BORDER);
+ cssAttr = translateBorder(cssAttr, o);
+ }
+
+ // Translate border attribute.
+ o = cssAttr.getAttribute(HTML.Attribute.BORDER);
+ cssAttr = translateBorder(cssAttr, o);
+
+ // TODO: Add more mappings.
+ return cssAttr;
+ }
+
+ /**
+ * Translates a HTML border attribute to a corresponding set of CSS
+ * attributes.
+ *
+ * @param cssAttr the original set of CSS attributes to add to
+ * @param o the value of the border attribute
+ *
+ * @return the new set of CSS attributes
+ */
+ private AttributeSet translateBorder(AttributeSet cssAttr, Object o)
+ {
+ if (o != null)
+ {
+ BorderWidth l = new BorderWidth(o.toString());
+ if (l.getValue() > 0)
+ {
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_WIDTH, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_STYLE,
+ "solid");
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_COLOR,
+ new CSSColor("black"));
+ }
+ }
+ return cssAttr;
+ }
+
+ /**
+ * Adds an attribute to the given set and returns a new set. This is implemented
+ * to convert StyleConstants attributes to CSS before forwarding them to the superclass.
+ * The StyleConstants attribute do not have corresponding CSS entry, the attribute
+ * is stored (but will likely not be used).
+ *
+ * @param old - the old set
+ * @param key - the non-null attribute key
+ * @param value - the attribute value
+ * @return the updated set
+ */
+ public AttributeSet addAttribute(AttributeSet old, Object key,
+ Object value)
+ {
+ // FIXME: Not implemented.
+ return super.addAttribute(old, key, value);
+ }
+
+ /**
+ * Adds a set of attributes to the element. If any of these attributes are
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param attr - the attributes to add
+ * @return the updated attribute set
+ */
+ public AttributeSet addAttributes(AttributeSet old, AttributeSet attr)
+ {
+ // FIXME: Not implemented.
+ return super.addAttributes(old, attr);
+ }
+
+ /**
+ * Removes an attribute from the set. If the attribute is a
+ * StyleConstants, it will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param key - the non-null attribute key
+ * @return the updated set
+ */
+ public AttributeSet removeAttribute(AttributeSet old, Object key)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttribute(old, key);
+ }
+
+ /**
+ * Removes an attribute from the set. If any of the attributes are
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param attrs - the attributes to remove
+ * @return the updated set
+ */
+ public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttributes(old, attrs);
+ }
+
+ /**
+ * Removes a set of attributes for the element. If any of the attributes is a
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old attribute set
+ * @param names - the attribute names
+ * @return the update attribute set
+ */
+ public AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttributes(old, names);
+ }
+
+ /**
+ * Creates a compact set of attributes that might be shared. This is a hook
+ * for subclasses that want to change the behaviour of SmallAttributeSet.
+ *
+ * @param a - the set of attributes to be represented in the compact form.
+ * @return the set of attributes created
+ */
+ protected StyleContext.SmallAttributeSet createSmallAttributeSet(AttributeSet a)
+ {
+ return super.createSmallAttributeSet(a);
+ }
+
+ /**
+ * Creates a large set of attributes. This set is not shared. This is a hook
+ * for subclasses that want to change the behaviour of the larger attribute
+ * storage format.
+ *
+ * @param a - the set of attributes to be represented in the larger form.
+ * @return the large set of attributes.
+ */
+ protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
+ {
+ return super.createLargeAttributeSet(a);
+ }
+
+ /**
+ * Gets the font to use for the given set.
+ *
+ * @param a - the set to get the font for.
+ * @return the font for the set
+ */
+ public Font getFont(AttributeSet a)
+ {
+ int realSize = getFontSize(a);
+
+ // Decrement size for subscript and superscript.
+ Object valign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+ if (valign != null)
+ {
+ String v = valign.toString();
+ if (v.contains("sup") || v.contains("sub"))
+ realSize -= 2;
+ }
+
+ // TODO: Convert font family.
+ String family = "SansSerif";
+
+ int style = Font.PLAIN;
+ FontWeight weight = (FontWeight) a.getAttribute(CSS.Attribute.FONT_WEIGHT);
+ if (weight != null)
+ style |= weight.getValue();
+ FontStyle fStyle = (FontStyle) a.getAttribute(CSS.Attribute.FONT_STYLE);
+ if (fStyle != null)
+ style |= fStyle.getValue();
+ return new Font(family, style, realSize);
+ }
+
+ /**
+ * Determines the EM base value based on the specified attributes.
+ *
+ * @param atts the attibutes
+ *
+ * @return the EM base value
+ */
+ float getEMBase(AttributeSet atts)
+ {
+ Font font = getFont(atts);
+ FontRenderContext ctx = new FontRenderContext(null, false, false);
+ Rectangle2D bounds = font.getStringBounds("M", ctx);
+ return (float) bounds.getWidth();
+ }
+
+ /**
+ * Determines the EX base value based on the specified attributes.
+ *
+ * @param atts the attibutes
+ *
+ * @return the EX base value
+ */
+ float getEXBase(AttributeSet atts)
+ {
+ Font font = getFont(atts);
+ FontRenderContext ctx = new FontRenderContext(null, false, false);
+ Rectangle2D bounds = font.getStringBounds("x", ctx);
+ return (float) bounds.getHeight();
+ }
+
+ /**
+ * Resolves the fontsize for a given set of attributes.
+ *
+ * @param atts the attributes
+ *
+ * @return the resolved font size
+ */
+ private int getFontSize(AttributeSet atts)
+ {
+ int size = 12;
+ if (atts.isDefined(CSS.Attribute.FONT_SIZE))
+ {
+ FontSize fs = (FontSize) atts.getAttribute(CSS.Attribute.FONT_SIZE);
+ if (fs.isRelative())
+ {
+ int parSize = 12;
+ AttributeSet resolver = atts.getResolveParent();
+ if (resolver != null)
+ parSize = getFontSize(resolver);
+ size = fs.getValue(parSize);
+ }
+ else
+ {
+ size = fs.getValue();
+ }
+ }
+ else
+ {
+ AttributeSet resolver = atts.getResolveParent();
+ if (resolver != null)
+ size = getFontSize(resolver);
+ }
+ return size;
+ }
+
+ /**
+ * Takes a set of attributes and turns it into a foreground
+ * color specification. This is used to specify things like, brigher, more hue
+ * etc.
+ *
+ * @param a - the set to get the foreground color for
+ * @return the foreground color for the set
+ */
+ public Color getForeground(AttributeSet a)
+ {
+ CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.COLOR);
+ Color color = null;
+ if (c != null)
+ color = c.getValue();
+ return color;
+ }
+
+ /**
+ * Takes a set of attributes and turns it into a background
+ * color specification. This is used to specify things like, brigher, more hue
+ * etc.
+ *
+ * @param a - the set to get the background color for
+ * @return the background color for the set
+ */
+ public Color getBackground(AttributeSet a)
+ {
+ CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.BACKGROUND_COLOR);
+ Color color = null;
+ if (c != null)
+ color = c.getValue();
+ return color;
+ }
+
+ /**
+ * Gets the box formatter to use for the given set of CSS attributes.
+ *
+ * @param a - the given set
+ * @return the box formatter
+ */
+ public BoxPainter getBoxPainter(AttributeSet a)
+ {
+ return new BoxPainter(a, this);
+ }
+
+ /**
+ * Gets the list formatter to use for the given set of CSS attributes.
+ *
+ * @param a - the given set
+ * @return the list formatter
+ */
+ public ListPainter getListPainter(AttributeSet a)
+ {
+ return new ListPainter(a, this);
+ }
+
+ /**
+ * Sets the base font size between 1 and 7.
+ *
+ * @param sz - the new font size for the base.
+ */
+ public void setBaseFontSize(int sz)
+ {
+ if (sz <= 7 && sz >= 1)
+ baseFontSize = sz;
+ }
+
+ /**
+ * Sets the base font size from the String. It can either identify
+ * a specific font size (between 1 and 7) or identify a relative
+ * font size such as +1 or -2.
+ *
+ * @param size - the new font size as a String.
+ */
+ public void setBaseFontSize(String size)
+ {
+ size = size.trim();
+ int temp = 0;
+ try
+ {
+ if (size.length() == 2)
+ {
+ int i = new Integer(size.substring(1)).intValue();
+ if (size.startsWith("+"))
+ temp = baseFontSize + i;
+ else if (size.startsWith("-"))
+ temp = baseFontSize - i;
+ }
+ else if (size.length() == 1)
+ temp = new Integer(size.substring(0)).intValue();
+
+ if (temp <= 7 && temp >= 1)
+ baseFontSize = temp;
+ }
+ catch (NumberFormatException nfe)
+ {
+ // Do nothing here
+ }
+ }
+
+ /**
+ * TODO
+ *
+ * @param pt - TODO
+ * @return TODO
+ */
+ public static int getIndexOfSize(float pt)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Gets the point size, given a size index.
+ *
+ * @param index - the size index
+ * @return the point size.
+ */
+ public float getPointSize(int index)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Given the string of the size, returns the point size value.
+ *
+ * @param size - the string representation of the size.
+ * @return - the point size value.
+ */
+ public float getPointSize(String size)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Convert the color string represenation into java.awt.Color. The valid
+ * values are like "aqua" , "#00FFFF" or "rgb(1,6,44)".
+ *
+ * @param colorName the color to convert.
+ * @return the matching java.awt.color
+ */
+ public Color stringToColor(String colorName)
+ {
+ return CSSColor.convertValue(colorName);
+ }
+
+ /**
+ * This class carries out some of the duties of CSS formatting. This enables views
+ * to present the CSS formatting while not knowing how the CSS values are cached.
+ *
+ * This object is reponsible for the insets of a View and making sure
+ * the background is maintained according to the CSS attributes.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+ public static class BoxPainter extends Object implements Serializable
+ {
+
+ /**
+ * The left inset.
+ */
+ private float leftInset;
+
+ /**
+ * The right inset.
+ */
+ private float rightInset;
+
+ /**
+ * The top inset.
+ */
+ private float topInset;
+
+ /**
+ * The bottom inset.
+ */
+ private float bottomInset;
+
+ /**
+ * The border of the box.
+ */
+ private Border border;
+
+ private float leftPadding;
+ private float rightPadding;
+ private float topPadding;
+ private float bottomPadding;
+
+ /**
+ * The background color.
+ */
+ private Color background;
+
+ /**
+ * Package-private constructor.
+ *
+ * @param as - AttributeSet for painter
+ */
+ BoxPainter(AttributeSet as, StyleSheet ss)
+ {
+ float emBase = ss.getEMBase(as);
+ float exBase = ss.getEXBase(as);
+ // Fetch margins.
+ Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ leftInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ rightInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_TOP);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ topInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_BOTTOM);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ bottomInset = l.getValue();
+ }
+
+ // Fetch padding.
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_LEFT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ leftPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_RIGHT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ rightPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_TOP);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ topPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_BOTTOM);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ bottomPadding = l.getValue();
+ }
+
+ // Determine border.
+ border = new CSSBorder(as, ss);
+
+ // Determine background.
+ background = ss.getBackground(as);
+
+ }
+
+
+ /**
+ * Gets the inset needed on a given side to account for the margin, border
+ * and padding.
+ *
+ * @param size - the size of the box to get the inset for. View.TOP, View.LEFT,
+ * View.BOTTOM or View.RIGHT.
+ * @param v - the view making the request. This is used to get the AttributeSet,
+ * amd may be used to resolve percentage arguments.
+ * @return the inset
+ * @throws IllegalArgumentException - for an invalid direction.
+ */
+ public float getInset(int size, View v)
+ {
+ float inset;
+ switch (size)
+ {
+ case View.TOP:
+ inset = topInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).top;
+ inset += topPadding;
+ break;
+ case View.BOTTOM:
+ inset = bottomInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).bottom;
+ inset += bottomPadding;
+ break;
+ case View.LEFT:
+ inset = leftInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).left;
+ inset += leftPadding;
+ break;
+ case View.RIGHT:
+ inset = rightInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).right;
+ inset += rightPadding;
+ break;
+ default:
+ inset = 0.0F;
+ }
+ return inset;
+ }
+
+ /**
+ * Paints the CSS box according to the attributes given. This should
+ * paint the border, padding and background.
+ *
+ * @param g - the graphics configuration
+ * @param x - the x coordinate
+ * @param y - the y coordinate
+ * @param w - the width of the allocated area
+ * @param h - the height of the allocated area
+ * @param v - the view making the request
+ */
+ public void paint(Graphics g, float x, float y, float w, float h, View v)
+ {
+ int inX = (int) (x + leftInset);
+ int inY = (int) (y + topInset);
+ int inW = (int) (w - leftInset - rightInset);
+ int inH = (int) (h - topInset - bottomInset);
+ if (background != null)
+ {
+ g.setColor(background);
+ g.fillRect(inX, inY, inW, inH);
+ }
+ if (border != null)
+ {
+ border.paintBorder(null, g, inX, inY, inW, inH);
+ }
+ }
+ }
+
+ /**
+ * This class carries out some of the CSS list formatting duties. Implementations
+ * of this class enable views to present the CSS formatting while not knowing anything
+ * about how the CSS values are being cached.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+ public static class ListPainter implements Serializable
+ {
+
+ /**
+ * Attribute set for painter
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The associated style sheet.
+ */
+ private StyleSheet styleSheet;
+
+ /**
+ * The bullet type.
+ */
+ private String type;
+
+ /**
+ * Package-private constructor.
+ *
+ * @param as - AttributeSet for painter
+ */
+ ListPainter(AttributeSet as, StyleSheet ss)
+ {
+ attributes = as;
+ styleSheet = ss;
+ type = (String) as.getAttribute(CSS.Attribute.LIST_STYLE_TYPE);
+ }
+
+ /**
+ * Cached rectangle re-used in the paint method below.
+ */
+ private final Rectangle tmpRect = new Rectangle();
+
+ /**
+ * Paints the CSS list decoration according to the attributes given.
+ *
+ * @param g - the graphics configuration
+ * @param x - the x coordinate
+ * @param y - the y coordinate
+ * @param w - the width of the allocated area
+ * @param h - the height of the allocated area
+ * @param v - the view making the request
+ * @param item - the list item to be painted >=0.
+ */
+ public void paint(Graphics g, float x, float y, float w, float h, View v,
+ int item)
+ {
+ // FIXME: This is a very simplistic list rendering. We still need
+ // to implement different bullet types (see type field) and custom
+ // bullets via images.
+ View itemView = v.getView(item);
+ AttributeSet viewAtts = itemView.getAttributes();
+ Object tag = viewAtts.getAttribute(StyleConstants.NameAttribute);
+ // Only paint something here when the child view is an LI tag
+ // and the calling view is some of the list tags then).
+ if (tag != null && tag == HTML.Tag.LI)
+ {
+ g.setColor(Color.BLACK);
+ int centerX = (int) (x - 12);
+ int centerY = -1;
+ // For paragraphs (almost all cases) center bullet vertically
+ // in the middle of the first line.
+ tmpRect.setBounds((int) x, (int) y, (int) w, (int) h);
+ if (itemView.getViewCount() > 0)
+ {
+ View v1 = itemView.getView(0);
+ if (v1 instanceof ParagraphView && v1.getViewCount() > 0)
+ {
+ Shape a1 = itemView.getChildAllocation(0, tmpRect);
+ Rectangle r1 = a1 instanceof Rectangle ? (Rectangle) a1
+ : a1.getBounds();
+ ParagraphView par = (ParagraphView) v1;
+ Shape a = par.getChildAllocation(0, r1);
+ if (a != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a
+ : a.getBounds();
+ centerY = (int) (r.height / 2 + r.y);
+ }
+ }
+ }
+ if (centerY == -1)
+ {
+ centerY =(int) (h / 2 + y);
+ }
+ g.fillOval(centerX - 3, centerY - 3, 6, 6);
+ }
+ }
+ }
+
+ /**
+ * Converts an AttributeSet to a Map. This is used for CSS resolving.
+ *
+ * @param atts the attributes to convert
+ *
+ * @return the converted map
+ */
+ private Map<String,String> attributeSetToMap(AttributeSet atts)
+ {
+ HashMap<String,String> map = new HashMap<String,String>();
+ Enumeration<?> keys = atts.getAttributeNames();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ Object value = atts.getAttribute(key);
+ map.put(key.toString(), value.toString());
+ }
+ return map;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/TableView.java b/libjava/classpath/javax/swing/text/html/TableView.java
new file mode 100644
index 000000000..2ad1b6d38
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/TableView.java
@@ -0,0 +1,974 @@
+/* TableView.java -- A table view for HTML tables
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import gnu.javax.swing.text.html.css.Length;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * A view implementation that renders HTML tables.
+ *
+ * This is basically a vertical BoxView that contains the rows of the table
+ * and the rows are horizontal BoxViews that contain the actual columns.
+ */
+class TableView
+ extends BlockView
+ implements ViewFactory
+{
+
+ /**
+ * Represents a single table row.
+ */
+ class RowView
+ extends BlockView
+ {
+ /**
+ * Has true at column positions where an above row's cell overlaps into
+ * this row.
+ */
+ boolean[] overlap;
+
+ /**
+ * Stores the row index of this row.
+ */
+ int rowIndex;
+
+ /**
+ * Creates a new RowView.
+ *
+ * @param el the element for the row view
+ */
+ RowView(Element el)
+ {
+ super(el, X_AXIS);
+ }
+
+ public void replace(int offset, int len, View[] views)
+ {
+ gridValid = false;
+ super.replace(offset, len, views);
+ }
+
+ /**
+ * Overridden to make rows not resizable along the Y axis.
+ */
+ public float getMaximumSpan(int axis)
+ {
+ float span;
+ if (axis == Y_AXIS)
+ span = super.getPreferredSpan(axis);
+ else
+ span = Integer.MAX_VALUE;
+ return span;
+ }
+
+ public float getMinimumSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = totalColumnRequirements.minimum;
+ else
+ span = super.getMinimumSpan(axis);
+ return span;
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = totalColumnRequirements.preferred;
+ else
+ span = super.getPreferredSpan(axis);
+ return span;
+ }
+
+ /**
+ * Calculates the overall size requirements for the row along the
+ * major axis. This will be the sum of the column requirements.
+ */
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ if (r == null)
+ r = new SizeRequirements();
+ int adjust = (columnRequirements.length + 1) * cellSpacing;
+ r.minimum = totalColumnRequirements.minimum + adjust;
+ r.preferred = totalColumnRequirements.preferred + adjust;
+ r.maximum = totalColumnRequirements.maximum + adjust;
+ r.alignment = 0.0F;
+ return r;
+ }
+
+ /**
+ * Lays out the columns in this row.
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int spans[])
+ {
+ super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+
+ // Adjust columns that have rowSpan > 1.
+ int numCols = getViewCount();
+ for (int i = 0; i < numCols; i++)
+ {
+ View v = getView(i);
+ if (v instanceof CellView)
+ {
+ CellView cell = (CellView) v;
+ if (cell.rowSpan > 1)
+ {
+ for (int r = 1; r < cell.rowSpan; r++)
+ {
+ spans[i] += TableView.this.getSpan(axis, rowIndex + r);
+ spans[i] += cellSpacing;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Lays out the columns in this row.
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int spans[])
+ {
+ updateGrid();
+ int realColumn = 0;
+ int colCount = getViewCount();
+ for (int i = 0; i < numColumns;)
+ {
+ if (! overlap[i] && realColumn < colCount)
+ {
+ View v = getView(realColumn);
+ if (v instanceof CellView)
+ {
+ CellView cv = (CellView) v;
+ offsets[realColumn] = columnOffsets[i];
+ spans[realColumn] = 0;
+ for (int j = 0; j < cv.colSpan; j++, i++)
+ {
+ spans[realColumn] += columnSpans[i];
+ if (j < cv.colSpan - 1)
+ spans[realColumn] += cellSpacing;
+ }
+ }
+ realColumn++;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+ }
+
+ /**
+ * A view that renders HTML table cells (TD and TH tags).
+ */
+ class CellView
+ extends BlockView
+ {
+
+ /**
+ * The number of columns that this view spans.
+ */
+ int colSpan;
+
+ /**
+ * The number of rows that this cell spans.
+ */
+ int rowSpan;
+
+ /**
+ * Creates a new CellView for the specified element.
+ *
+ * @param el the element for which to create the colspan
+ */
+ CellView(Element el)
+ {
+ super(el, Y_AXIS);
+ }
+
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ r = super.calculateMajorAxisRequirements(axis, r);
+ r.maximum = Integer.MAX_VALUE;
+ return r;
+ }
+
+ /**
+ * Overridden to fetch the columnSpan attibute.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ super.setPropertiesFromAttributes();
+ colSpan = 1;
+ AttributeSet atts = getAttributes();
+ Object o = atts.getAttribute(HTML.Attribute.COLSPAN);
+ if (o != null)
+ {
+ try
+ {
+ colSpan = Integer.parseInt(o.toString());
+ }
+ catch (NumberFormatException ex)
+ {
+ // Couldn't parse the colspan, assume 1.
+ colSpan = 1;
+ }
+ }
+ rowSpan = 1;
+ o = atts.getAttribute(HTML.Attribute.ROWSPAN);
+ if (o != null)
+ {
+ try
+ {
+ rowSpan = Integer.parseInt(o.toString());
+ }
+ catch (NumberFormatException ex)
+ {
+ // Couldn't parse the colspan, assume 1.
+ rowSpan = 1;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * The attributes of this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The column requirements.
+ *
+ * Package private to avoid accessor methods.
+ */
+ SizeRequirements[] columnRequirements;
+
+ /**
+ * The overall requirements across all columns.
+ *
+ * Package private to avoid accessor methods.
+ */
+ SizeRequirements totalColumnRequirements;
+
+ /**
+ * The column layout, offsets.
+ *
+ * Package private to avoid accessor methods.
+ */
+ int[] columnOffsets;
+
+ /**
+ * The column layout, spans.
+ *
+ * Package private to avoid accessor methods.
+ */
+ int[] columnSpans;
+
+ /**
+ * The widths of the columns that have been explicitly specified.
+ */
+ Length[] columnWidths;
+
+ /**
+ * The total number of columns.
+ */
+ int numColumns;
+
+ /**
+ * The table width.
+ */
+ private Length width;
+
+ /**
+ * Indicates if the grid setup is ok.
+ */
+ boolean gridValid = false;
+
+ /**
+ * Additional space that is added _between_ table cells.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ int cellSpacing;
+
+ /**
+ * A cached Rectangle object for reuse in paint().
+ */
+ private Rectangle tmpRect;
+
+ /**
+ * Creates a new HTML table view for the specified element.
+ *
+ * @param el the element for the table view
+ */
+ public TableView(Element el)
+ {
+ super(el, Y_AXIS);
+ totalColumnRequirements = new SizeRequirements();
+ tmpRect = new Rectangle();
+ }
+
+ /**
+ * Implementation of the ViewFactory interface for creating the
+ * child views correctly.
+ */
+ public View create(Element elem)
+ {
+ View view = null;
+ AttributeSet atts = elem.getAttributes();
+ Object name = atts.getAttribute(StyleConstants.NameAttribute);
+ AttributeSet pAtts = elem.getParentElement().getAttributes();
+ Object pName = pAtts.getAttribute(StyleConstants.NameAttribute);
+
+ if (name == HTML.Tag.TR && pName == HTML.Tag.TABLE)
+ view = new RowView(elem);
+ else if ((name == HTML.Tag.TD || name == HTML.Tag.TH)
+ && pName == HTML.Tag.TR)
+ view = new CellView(elem);
+ else if (name == HTML.Tag.CAPTION)
+ view = new ParagraphView(elem);
+ else
+ {
+ // If we haven't mapped the element, then fall back to the standard
+ // view factory.
+ View parent = getParent();
+ if (parent != null)
+ {
+ ViewFactory vf = parent.getViewFactory();
+ if (vf != null)
+ view = vf.create(elem);
+ }
+ }
+ return view;
+ }
+
+ /**
+ * Returns this object as view factory so that we get our TR, TD, TH
+ * and CAPTION subelements created correctly.
+ */
+ public ViewFactory getViewFactory()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the attributes of this view. This is overridden to provide
+ * the attributes merged with the CSS stuff.
+ */
+ public AttributeSet getAttributes()
+ {
+ if (attributes == null)
+ attributes = getStyleSheet().getViewAttributes(this);
+ return attributes;
+ }
+
+ /**
+ * Returns the stylesheet associated with this view.
+ *
+ * @return the stylesheet associated with this view
+ */
+ protected StyleSheet getStyleSheet()
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ return doc.getStyleSheet();
+ }
+
+ /**
+ * Overridden to calculate the size requirements according to the
+ * columns distribution.
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ updateGrid();
+ calculateColumnRequirements();
+
+ // Calculate the horizontal requirements according to the superclass.
+ // This will return the maximum of the row's widths.
+ r = super.calculateMinorAxisRequirements(axis, r);
+
+ // Try to set the CSS width if it fits.
+ if (width != null)
+ {
+ int w = (int) width.getValue();
+ if (r.minimum < w)
+ r.minimum = w;
+ }
+
+ // Adjust requirements when we have cell spacing.
+ int adjust = (columnRequirements.length + 1) * cellSpacing;
+ r.minimum += adjust;
+ r.preferred += adjust;
+
+ // Apply the alignment.
+ AttributeSet atts = getAttributes();
+ Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN);
+ r.alignment = 0.0F;
+ if (o != null)
+ {
+ String al = o.toString();
+ if (al.equals("left"))
+ r.alignment = 0.0F;
+ else if (al.equals("center"))
+ r.alignment = 0.5F;
+ else if (al.equals("right"))
+ r.alignment = 1.0F;
+ }
+
+ // Make it not resize in the horizontal direction.
+ r.maximum = r.preferred;
+ return r;
+ }
+
+ /**
+ * Overridden to perform the table layout before calling the super
+ * implementation.
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ updateGrid();
+
+ // Mark all rows as invalid along their minor axis to force correct
+ // layout of multi-row cells.
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View row = getView(i);
+ if (row instanceof RowView)
+ ((RowView) row).layoutChanged(axis);
+ }
+
+ layoutColumns(targetSpan);
+ super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Calculates the size requirements for the columns.
+ */
+ private void calculateColumnRequirements()
+ {
+ int numRows = getViewCount();
+ totalColumnRequirements.minimum = 0;
+ totalColumnRequirements.preferred = 0;
+ totalColumnRequirements.maximum = 0;
+
+ // In this first pass we find out a suitable total width to fit in
+ // all columns of all rows.
+ for (int r = 0; r < numRows; r++)
+ {
+ View rowView = getView(r);
+ int numCols;
+ if (rowView instanceof RowView)
+ numCols = ((RowView) rowView).getViewCount();
+ else
+ numCols = 0;
+
+ // We collect the normal (non-relative) column requirements in the
+ // total variable and the relative requirements in the relTotal
+ // variable. In the end we create the maximum of both to get the
+ // real requirements.
+ SizeRequirements total = new SizeRequirements();
+ SizeRequirements relTotal = new SizeRequirements();
+ float totalPercent = 0.F;
+ int realCol = 0;
+ for (int c = 0; c < numCols; c++)
+ {
+ View v = rowView.getView(c);
+ if (v instanceof CellView)
+ {
+ CellView cellView = (CellView) v;
+ int colSpan = cellView.colSpan;
+ if (colSpan > 1)
+ {
+ int cellMin = (int) cellView.getMinimumSpan(X_AXIS);
+ int cellPref = (int) cellView.getPreferredSpan(X_AXIS);
+ int cellMax = (int) cellView.getMaximumSpan(X_AXIS);
+ int currentMin = 0;
+ int currentPref = 0;
+ long currentMax = 0;
+ for (int i = 0; i < colSpan; i++)
+ {
+ SizeRequirements req = columnRequirements[realCol];
+ currentMin += req.minimum;
+ currentPref += req.preferred;
+ currentMax += req.maximum;
+ }
+ int deltaMin = cellMin - currentMin;
+ int deltaPref = cellPref - currentPref;
+ int deltaMax = (int) (cellMax - currentMax);
+ // Distribute delta.
+ for (int i = 0; i < colSpan; i++)
+ {
+ SizeRequirements req = columnRequirements[realCol];
+ if (deltaMin > 0)
+ req.minimum += deltaMin / colSpan;
+ if (deltaPref > 0)
+ req.preferred += deltaPref / colSpan;
+ if (deltaMax > 0)
+ req.maximum += deltaMax / colSpan;
+ if (columnWidths[realCol] == null
+ || ! columnWidths[realCol].isPercentage())
+ {
+ total.minimum += req.minimum;
+ total.preferred += req.preferred;
+ total.maximum += req.maximum;
+ }
+ else
+ {
+ relTotal.minimum =
+ Math.max(relTotal.minimum,
+ (int) (req.minimum
+ * columnWidths[realCol].getValue()));
+ relTotal.preferred =
+ Math.max(relTotal.preferred,
+ (int) (req.preferred
+ * columnWidths[realCol].getValue()));
+ relTotal.maximum =
+ Math.max(relTotal.maximum,
+ (int) (req.maximum
+ * columnWidths[realCol].getValue()));
+ totalPercent += columnWidths[realCol].getValue();
+ }
+ }
+ realCol += colSpan;
+ }
+ else
+ {
+ // Shortcut for colSpan == 1.
+ SizeRequirements req = columnRequirements[realCol];
+ req.minimum = Math.max(req.minimum,
+ (int) cellView.getMinimumSpan(X_AXIS));
+ req.preferred = Math.max(req.preferred,
+ (int) cellView.getPreferredSpan(X_AXIS));
+ req.maximum = Math.max(req.maximum,
+ (int) cellView.getMaximumSpan(X_AXIS));
+ if (columnWidths[realCol] == null
+ || ! columnWidths[realCol].isPercentage())
+ {
+ total.minimum += columnRequirements[realCol].minimum;
+ total.preferred +=
+ columnRequirements[realCol].preferred;
+ total.maximum += columnRequirements[realCol].maximum;
+ }
+ else
+ {
+ relTotal.minimum =
+ Math.max(relTotal.minimum,
+ (int) (req.minimum
+ / columnWidths[c].getValue()));
+ relTotal.preferred =
+ Math.max(relTotal.preferred,
+ (int) (req.preferred
+ / columnWidths[c].getValue()));
+ relTotal.maximum =
+ Math.max(relTotal.maximum,
+ (int) (req.maximum
+ / columnWidths[c].getValue()));
+ totalPercent += columnWidths[c].getValue();
+ }
+ realCol += 1;
+ }
+ }
+ }
+
+ // Update the total requirements as follows:
+ // 1. Multiply the absolute requirements with 1 - totalPercent. This
+ // gives the total requirements based on the wishes of the absolute
+ // cells.
+ // 2. Take the maximum of this value and the total relative
+ // requirements. Now we should have enough space for whatever cell
+ // in this column.
+ // 3. Take the maximum of this value and the previous maximum value.
+ total.minimum *= 1.F / (1.F - totalPercent);
+ total.preferred *= 1.F / (1.F - totalPercent);
+ total.maximum *= 1.F / (1.F - totalPercent);
+
+ int rowTotalMin = Math.max(total.minimum, relTotal.minimum);
+ int rowTotalPref = Math.max(total.preferred, relTotal.preferred);
+ int rowTotalMax = Math.max(total.maximum, relTotal.maximum);
+ totalColumnRequirements.minimum =
+ Math.max(totalColumnRequirements.minimum, rowTotalMin);
+ totalColumnRequirements.preferred =
+ Math.max(totalColumnRequirements.preferred, rowTotalPref);
+ totalColumnRequirements.maximum =
+ Math.max(totalColumnRequirements.maximum, rowTotalMax);
+ }
+
+ // Now we know what we want and can fix up the actual relative
+ // column requirements.
+ int numCols = columnRequirements.length;
+ for (int i = 0; i < numCols; i++)
+ {
+ if (columnWidths[i] != null)
+ {
+ columnRequirements[i].minimum = (int)
+ columnWidths[i].getValue(totalColumnRequirements.minimum);
+ columnRequirements[i].preferred = (int)
+ columnWidths[i].getValue(totalColumnRequirements.preferred);
+ columnRequirements[i].maximum = (int)
+ columnWidths[i].getValue(totalColumnRequirements.maximum);
+ }
+ }
+ }
+
+ /**
+ * Lays out the columns.
+ *
+ * @param targetSpan the target span into which the table is laid out
+ */
+ private void layoutColumns(int targetSpan)
+ {
+ // Set the spans to the preferred sizes. Determine the space
+ // that we have to adjust the sizes afterwards.
+ long sumPref = 0;
+ int n = columnRequirements.length;
+ for (int i = 0; i < n; i++)
+ {
+ SizeRequirements col = columnRequirements[i];
+ if (columnWidths[i] != null)
+ columnSpans[i] = (int) columnWidths[i].getValue(targetSpan);
+ else
+ columnSpans[i] = col.preferred;
+ sumPref += columnSpans[i];
+ }
+
+ // Try to adjust the spans so that we fill the targetSpan.
+ // For adjustments we have to use the targetSpan minus the cumulated
+ // cell spacings.
+ long diff = targetSpan - (n + 1) * cellSpacing - sumPref;
+ float factor = 0.0F;
+ int[] diffs = null;
+ if (diff != 0)
+ {
+ long total = 0;
+ diffs = new int[n];
+ for (int i = 0; i < n; i++)
+ {
+ // Only adjust the width if we haven't set a column width here.
+ if (columnWidths[i] == null)
+ {
+ SizeRequirements col = columnRequirements[i];
+ int span;
+ if (diff < 0)
+ {
+ span = col.minimum;
+ diffs[i] = columnSpans[i] - span;
+ }
+ else
+ {
+ span = col.maximum;
+ diffs[i] = span - columnSpans[i];
+ }
+ total += span;
+ }
+ else
+ total += columnSpans[i];
+ }
+
+ float maxAdjust = Math.abs(total - sumPref);
+ factor = diff / maxAdjust;
+ factor = Math.min(factor, 1.0F);
+ factor = Math.max(factor, -1.0F);
+ }
+
+ // Actually perform adjustments.
+ int totalOffs = cellSpacing;
+ for (int i = 0; i < n; i++)
+ {
+ columnOffsets[i] = totalOffs;
+ if (diff != 0)
+ {
+ float adjust = factor * diffs[i];
+ columnSpans[i] += Math.round(adjust);
+ }
+ // Avoid overflow here.
+ totalOffs = (int) Math.min((long) totalOffs + (long) columnSpans[i]
+ + (long) cellSpacing, Integer.MAX_VALUE);
+ }
+ }
+
+ /**
+ * Updates the arrays that contain the row and column data in response
+ * to a change to the table structure.
+ *
+ * Package private to avoid accessor methods.
+ */
+ void updateGrid()
+ {
+ if (! gridValid)
+ {
+ AttributeSet atts = getAttributes();
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(atts);
+ float exBase = ss.getEXBase(atts);
+ int maxColumns = 0;
+ int numRows = getViewCount();
+ for (int r = 0; r < numRows; r++)
+ {
+ View rowView = getView(r);
+ int numCols = 0;
+ if (rowView instanceof RowView)
+ {
+ int numCells = ((RowView) rowView).getViewCount();
+ for (int i = 0; i < numCells; i++)
+ {
+ View v = rowView.getView(i);
+ if (v instanceof CellView)
+ numCols += ((CellView) v).colSpan;
+ }
+ }
+ maxColumns = Math.max(numCols, maxColumns);
+ }
+ numColumns = maxColumns;
+ columnWidths = new Length[maxColumns];
+ int[] rowSpans = new int[maxColumns];
+ for (int r = 0; r < numRows; r++)
+ {
+ View view = getView(r);
+ if (view instanceof RowView)
+ {
+ RowView rowView = (RowView) view;
+ rowView.rowIndex = r;
+ rowView.overlap = new boolean[maxColumns];
+ int colIndex = 0;
+ int colCount = rowView.getViewCount();
+ for (int c = 0; c < maxColumns;)
+ {
+ if (rowSpans[c] > 0)
+ {
+ rowSpans[c]--;
+ rowView.overlap[c] = true;
+ c++;
+ }
+ else if (colIndex < colCount)
+ {
+ View v = rowView.getView(colIndex);
+ colIndex++;
+ if (v instanceof CellView)
+ {
+ CellView cv = (CellView) v;
+ Object o =
+ cv.getAttributes().getAttribute(CSS.Attribute.WIDTH);
+ if (o != null && columnWidths[c] == null
+ && o instanceof Length)
+ {
+ columnWidths[c]= (Length) o;
+ columnWidths[c].setFontBases(emBase, exBase);
+ }
+ int rs = cv.rowSpan - 1;
+ for (int col = cv.colSpan - 1; col >= 0; col--)
+ {
+ rowSpans[c] = rs;
+ c++;
+ }
+ }
+ }
+ else
+ {
+ c++;
+ }
+ }
+ }
+ }
+ columnRequirements = new SizeRequirements[maxColumns];
+ for (int i = 0; i < maxColumns; i++)
+ columnRequirements[i] = new SizeRequirements();
+ columnOffsets = new int[maxColumns];
+ columnSpans = new int[maxColumns];
+
+ gridValid = true;
+ }
+ }
+
+ /**
+ * Overridden to restrict the table width to the preferred size.
+ */
+ public float getMaximumSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = super.getPreferredSpan(axis);
+ else
+ span = super.getMaximumSpan(axis);
+ return span;
+ }
+
+ /**
+ * Overridden to fetch the CSS attributes when view gets connected.
+ */
+ public void setParent(View parent)
+ {
+ super.setParent(parent);
+ if (parent != null)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Fetches CSS and HTML layout attributes.
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ super.setPropertiesFromAttributes();
+
+ // Fetch and parse cell spacing.
+ AttributeSet atts = getAttributes();
+ StyleSheet ss = getStyleSheet();
+ float emBase = ss.getEMBase(atts);
+ float exBase = ss.getEXBase(atts);
+ Object o = atts.getAttribute(CSS.Attribute.BORDER_SPACING);
+ if (o != null && o instanceof Length)
+ {
+ Length l = (Length) o;
+ l.setFontBases(emBase, exBase);
+ cellSpacing = (int) l.getValue();
+ }
+ o = atts.getAttribute(CSS.Attribute.WIDTH);
+ if (o != null && o instanceof Length)
+ {
+ width = (Length) o;
+ width.setFontBases(emBase, exBase);
+ }
+ }
+
+ /**
+ * Overridden to adjust for cellSpacing.
+ */
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ r = super.calculateMajorAxisRequirements(axis, r);
+ int adjust = (getViewCount() + 1) * cellSpacing;
+ r.minimum += adjust;
+ r.preferred += adjust;
+ r.maximum += adjust;
+ return r;
+ }
+
+ /**
+ * Overridden to adjust for cellSpacing.
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int spans[])
+ {
+ // Mark all rows as invalid along their minor axis to force correct
+ // layout of multi-row cells.
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View row = getView(i);
+ if (row instanceof RowView)
+ ((RowView) row).layoutChanged(axis);
+ }
+
+ int adjust = (getViewCount() + 1) * cellSpacing;
+ super.layoutMajorAxis(targetSpan - adjust, axis, offsets, spans);
+ for (int i = 0; i < offsets.length; i++)
+ {
+ offsets[i] += (i + 1) * cellSpacing;
+ }
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
+ */
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.insertUpdate(e, a, this);
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
+ */
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.removeUpdate(e, a, this);
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
+ */
+ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.changedUpdate(e, a, this);
+ }
+
+ public void replace(int offset, int len, View[] views)
+ {
+ gridValid = false;
+ super.replace(offset, len, views);
+ }
+
+ /**
+ * We can't use the super class's paint() method because it might cut
+ * off multi-row children. Instead we trigger painting for all rows
+ * and let the rows sort out what to paint and what not.
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ painter.paint(g, rect.x, rect.y, rect.width, rect.height, this);
+ int nRows = getViewCount();
+ Rectangle inside = getInsideAllocation(a);
+ for (int r = 0; r < nRows; r++)
+ {
+ tmpRect.setBounds(inside);
+ childAllocation(r, tmpRect);
+ paintChild(g, tmpRect, r);
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java b/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java
new file mode 100644
index 000000000..fb57872ce
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java
@@ -0,0 +1,163 @@
+/* ViewAttributeSet.java -- The AttributeSet used by HTML views
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
+
+/**
+ * An AttributeSet implemenation that is used by the HTML views. This
+ * AttributeSet is created by StyleSheet.getViewAttributes() and combines
+ * the following attributes:
+ * - The original attributes of the View's element.
+ * - Any translated (HTML->CSS) attributes, as returned by
+ * StyleSheet.translateHTMLToCS().
+ * - CSS Styles as resolved by the CSS stylesheet.
+ *
+ * In addition to that, it resolves attributes to the parent views, if
+ * a CSS attribute is requested that is inheritable.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class ViewAttributeSet
+ extends MultiAttributeSet
+{
+
+ /**
+ * The view for which we are the AttributeSet.
+ */
+ private View view;
+
+ /**
+ * The stylesheet to use.
+ */
+ private StyleSheet styleSheet;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param v the view for which to do the AttributeSet
+ */
+ ViewAttributeSet(View v, StyleSheet ss)
+ {
+ styleSheet = ss;
+ view = v;
+ ArrayList<AttributeSet> atts = new ArrayList<AttributeSet>();
+
+ Element el = v.getElement();
+ AttributeSet elAtts = el.getAttributes();
+ AttributeSet htmlAtts = styleSheet.translateHTMLToCSS(elAtts);
+ if (htmlAtts.getAttributeCount() > 0)
+ atts.add(htmlAtts);
+
+ if (el.isLeaf())
+ {
+ Enumeration<?> n = elAtts.getAttributeNames();
+ while (n.hasMoreElements())
+ {
+ Object key = n.nextElement();
+ if (key instanceof HTML.Tag)
+ {
+ AttributeSet rule = styleSheet.getRule((HTML.Tag) key, el);
+ if (rule != null)
+ atts.add(rule);
+ }
+ }
+ }
+ else
+ {
+ HTML.Tag tag =
+ (HTML.Tag) elAtts.getAttribute(StyleConstants.NameAttribute);
+ AttributeSet rule = styleSheet.getRule(tag, el);
+ if (rule != null)
+ atts.add(rule);
+ }
+
+ AttributeSet[] atts1 = new AttributeSet[atts.size()];
+ atts1 = atts.toArray(atts1);
+ init(atts1);
+ }
+
+ /**
+ * Fetches the attribute for the specific ckey. If the attribute
+ * can't be found and the key is a CSS.Attribute that is inherited,
+ * then the attribute is looked up in the resolve parent.
+ */
+ public Object getAttribute(Object key)
+ {
+ Object val = super.getAttribute(key);
+ if (val == null)
+ {
+ // Didn't find value. If the key is a CSS.Attribute, and is
+ // inherited, then ask the resolve parent.
+ if (key instanceof CSS.Attribute)
+ {
+ CSS.Attribute cssKey = (CSS.Attribute) key;
+ if (cssKey.isInherited())
+ {
+ AttributeSet resolveParent = getResolveParent();
+ if (resolveParent != null)
+ val = resolveParent.getAttribute(cssKey);
+ }
+ }
+ }
+ return val;
+ }
+
+ /**
+ * Returns the resolve parent of this AttributeSet. This is the AttributeSet
+ * returned by the parent view if available.
+ */
+ public AttributeSet getResolveParent()
+ {
+ AttributeSet parent = null;
+ if (view != null)
+ {
+ View parentView = view.getParent();
+ if (parentView != null)
+ parent = parentView.getAttributes();
+ }
+ return parent;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/package.html b/libjava/classpath/javax/swing/text/html/package.html
new file mode 100644
index 000000000..c7e774428
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/package.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.text.html package.
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.text.html</title></head>
+
+<body>
+<p> Provides supporting classes for web browsers,
+ web robots, web page content analysers, web editors and
+ other applications applications working with Hypertext
+ Markup Language (HTML).
+</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/text/html/parser/AttributeList.java b/libjava/classpath/javax/swing/text/html/parser/AttributeList.java
new file mode 100644
index 000000000..a943f056d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/AttributeList.java
@@ -0,0 +1,294 @@
+/* AttributeList.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import gnu.javax.swing.text.html.parser.support.gnuStringIntMapper;
+
+import java.io.Serializable;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * <p>
+ * Stores the attribute information, obtained by parsing SGML (DTD) tag
+ * <code>&lt;!ATTLIST .. &gt;</code></p>
+ * <p>
+ * Elements can have a associated named properties (attributes) having the
+ * assigned values. The element start tag can have any number of attribute
+ * value pairs, separated by spaces. They can appear in any order.
+ * SGML requires you to delimit the attribute values using either double (")
+ * or single (') quotation marks. In HTML, it is possible
+ * (but not recommended) to specify the value of an attribute without
+ * quotation marks. Such attribute value may only contain
+ * letters, digits, hyphens (-) and periods (.) .
+ * </p>
+ * <p>
+ * The <code>AttributeList</code> defines a single attribute that additionally
+ * has a pointer referencing the possible subsequent attribute.
+ * The whole structure is just a simple linked list, storing all attributes of
+ * some <code>Element</code>.
+ * Use the <code>getNext()</code> method repeatedly to see all attributes in
+ * the list.
+ * </p>
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public final class AttributeList
+ implements DTDConstants, Serializable
+{
+ /** Maps between type names and they string values. */
+ private static final gnuStringIntMapper mapper =
+ new gnuStringIntMapper()
+ {
+ protected void create()
+ {
+ add("CDATA", DTDConstants.CDATA);
+ add("ENTITY", DTDConstants.ENTITY);
+ add("ENTITIES", DTDConstants.ENTITIES);
+ add("ID", DTDConstants.ID);
+ add("IDREF", DTDConstants.IDREF);
+ add("IDREFS", DTDConstants.IDREFS);
+ add("NAME", DTDConstants.NAME);
+ add("NAMES", DTDConstants.NAMES);
+ add("NMTOKEN", DTDConstants.NMTOKEN);
+ add("NMTOKENS", DTDConstants.NMTOKENS);
+ add("NOTATION", DTDConstants.NOTATION);
+ add("NUMBER", DTDConstants.NUMBER);
+ add("NUMBERS", DTDConstants.NUMBERS);
+ add("NUTOKEN", DTDConstants.NUTOKEN);
+ add("NUTOKENS", DTDConstants.NUTOKENS);
+ }
+ };
+
+ /** Use serialVersionUID for interoperability. */
+ private static final long serialVersionUID = -1361214058742015233L;
+
+ /**
+ * The value of ( = pointer to ) the next attribute in the linked list,
+ * storing all attributes of some Element. Contains null for the
+ * last attribute.
+ */
+ public AttributeList next;
+
+ /**
+ * The name of the attribute. The attribute names are case insensitive.
+ */
+ public String name;
+
+ /**
+ * The default value of this attribute. Equals to null if no default value
+ * is specified.
+ */
+ public String value;
+
+ /**
+ * The explicit set of the allowed values of this attribute. Equals to
+ * null, if this parameter was not specified.
+ * Values, defined in DTD, are case insensitive.
+ */
+ public Vector<?> values;
+
+ /**
+ * The modifier of this attribute. This field contains one of the
+ * following DTD constants:
+ * <ul>
+ * <li> REQUIRED if the attribute value is always required,</li>
+ * <li> IMPLIED if the user agent must supply the default value itself,</li>
+ * <li> FIXED if the attribute value is fixed to some value and cannot
+ * be changed.</li>
+ * <li> DEFAULT if the attribute default value has been supplied.</li>
+ * <li> CURRENT the value that at any point in the document is
+ * the last value supplied for that element. A value is required to be
+ * supplied for the first* occurrence of an element</li>
+ * <li> CONREF specifies the IDREF value of
+ * the reference to content in another location of the document.
+ * The element with this attribute is empty, the content from
+ * that another location must be used instead.</li>
+ * </ul>
+ */
+ public int modifier;
+
+ /**
+ * The type of the attribute. The possible values of this field
+ * (NUMBER, NAME, ID, CDATA and so on) are defined in DTDConstants.
+ */
+ public int type;
+
+ /**
+ * Creates the attribute with the given name, initializing other fields
+ * to the default values ( 0 and null ).
+ *
+ * @param a_name The name of the attribute.
+ */
+ public AttributeList(String a_name)
+ {
+ name = a_name;
+ }
+
+ /**
+ * Creates the attribute with the given properties.
+ * @param a_name The name of the attribute
+ * @param a_type The type of the attribute. The possible values are defined
+ * in <code> DTDConstants</code>.
+ * @param a_modifier The modifier of this attribute. The possible values
+ * are defined in <code> DTDConstants</code>.
+ * @param a_default The default value of this attribute
+ * @param allowed_values The explicit set of the allowed values of
+ * this attribute
+ * @param a_next The value of the subsequent instance of the AttributeList,
+ * representing the next attribute definition for the same element.
+ * Equals to null for the last attribute definition.
+ */
+ public AttributeList(String a_name, int a_type, int a_modifier,
+ String a_default, Vector<?> allowed_values,
+ AttributeList a_next
+ )
+ {
+ this(a_name);
+ type = a_type;
+ modifier = a_modifier;
+ value = a_default;
+ values = allowed_values;
+ next = a_next;
+ }
+
+ /**
+ * Get the modifier of this attribute. This field contains one of the
+ * following DTD constants:
+ * <ul>
+ * <li> REQUIRED if the attribute value is always required,</li>
+ * <li> IMPLIED if the user agent must supply the default value itself,</li>
+ * <li> FIXED if the attribute value is fixed to some value and cannot
+ * be changed.</li>
+ * <li> DEFAULT if the attribute default value has been supplied.</li>
+ * <li> CURRENT the value that at any point in the document is
+ * the last value supplied for that element. A value is required to be
+ * supplied for the first* occurrence of an element</li>
+ * <li> CONREF specifies the IDREF value of
+ * the reference to content in another location of the document.
+ * The element with this attribute is empty, the content from
+ * that another location must be used instead.</li>
+ * </ul>
+ */
+ public int getModifier()
+ {
+ return modifier;
+ }
+
+ /**
+ * Get the name of the attribute.
+ * The value is returned as it was supplied to a
+ * constructor, preserving the character case.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Get the value of ( = pointer to ) the next attribute in the linked list,
+ * storing all attributes of some Element. Contains null for the
+ * last attribute.
+ */
+ public AttributeList getNext()
+ {
+ return next;
+ }
+
+ /**
+ * Get the type of the attribute. The possible values of this field
+ * (NUMBER, NAME, ID, CDATA and so on) are defined in DTDConstants.
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+ /**
+ * Get the default value of this attribute.
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Get the allowed values of this attribute.
+ */
+ public Enumeration<?> getValues()
+ {
+ return (values != null) ? values.elements() : null;
+ }
+
+ /**
+ * Converts a string value, representing a valid SGLM attribute type,
+ * into the corresponding value, defined in DTDConstants.
+ * @param typeName the name of the type (character case is ignored).
+ * @return a value from DTDConstants or DTDConstants.ANY if the
+ * string is not representing a known type. The known attribute types
+ * in this implementation are CDATA, ENTITY, ENTITIES, ID, IDREF, IDREFS,
+ * NAME, NAMES, NMTOKEN, NMTOKENS, NOTATION, NUMBER, NUMBERS, NUTOKEN and
+ * NUTOKENS.
+ * @throws NullPointerException if the passed parameter is null.
+ */
+ public static int name2type(String typeName)
+ {
+ return mapper.get(typeName.toUpperCase());
+ }
+
+ /**
+ * Returns the attribute name.
+ */
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Converts a value from DTDConstants into the string representation.
+ * @param type - an integer value of the public static integer field,
+ * defined in the DTDConstants class.
+ * @return a corresponding SGML DTD keyword (UPPERCASE) or null if there
+ * are no attribute type constant having the given value.
+ */
+ public static String type2name(int type)
+ {
+ return mapper.get(type);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java
new file mode 100644
index 000000000..d5c4418de
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java
@@ -0,0 +1,223 @@
+/* ContentModel.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import gnu.javax.swing.text.html.parser.models.transformer;
+
+import java.io.Serializable;
+
+import java.util.Vector;
+
+/**
+ * A representation of the element content. The instances of this class
+ * can be arranged into the linked list, representing a BNF expression.
+ * The content model is constructed as a branched tree structure in the
+ * following way:
+ * <pre>
+ * a = new ContentModel('+', A, null); // a reprensents A+
+ * b = new ContentModel('&amp;', B, a); // b represents B &amp; A+
+ * c = new ContentModel('*', b, null); // c represents ( B &amp; A+) *
+ * d = new ContentModel('|', new ContentModel('*', A, null),
+ * new ContentModel('?', B, null)); // d represents ( A* | B? )
+ * </pre>
+ * where the valid operations are:
+ * <ul>
+ * <li><code>E* </code> E occurs zero or more times</li>
+ * <li><code>E+ </code> E occurs one or more times</li>
+ * <li><code>E? </code> E occurs once or not atl all</li>
+ * <li><code>A,B</code> A occurs before B</li>
+ * <li><code>A|B</code> both A and B are permitted in any order.
+ * The '|' alone does not permit the repetetive occurence of A or B
+ * (use <code>(A|B)*</code>.</li>
+ * <li><code>A&amp;B</code> both A and B must occur once (in any order)</li>
+ * </ul>
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public final class ContentModel
+ implements Serializable
+{
+ /** Use serialVersionUID for interoperability. */
+ private static final long serialVersionUID = -1130825523866321257L;
+
+ /**
+ * The next content model model ( = pointer to the next element of
+ * the linked list) for the binary expression (',','&amp;' or '|'). Null
+ * for the last element in the list.
+ */
+ public ContentModel next;
+
+ /**
+ * The document content, containing either Element or the enclosed
+ * content model (that would be in the parentheses in BNF expression).
+ */
+ public Object content;
+
+ /**
+ * Specifies the BNF operation between this node and the node,
+ * stored in the field <code>next</code> (or for this node, if it is
+ * an unary operation.
+ */
+ public int type;
+
+ /**
+ * Create a content model initializing all fields to default values.
+ */
+ public ContentModel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Create a content model, consisting of the single element.
+ * Examples:
+ *<code>
+ * a = new ContentModel('+', A, null); // a reprensents A+
+ * b = new ContentModel('&amp;', B, a); // b represents B &amp; A+
+ * c = new ContentModel('*', b, null); // c represents ( B &amp; A+) *
+ * d = new ContentModel('|', A,
+ * new ContentModel('?',b, null);
+ * // d represents
+ * </code>
+ */
+ public ContentModel(Element a_content)
+ {
+ content = a_content;
+ }
+
+ /**
+ * Create a content model, involving expression of the given type.
+ * @param a_type The expression operation type ('*','?' or '+'
+ * @param a_content The content for that the expression is applied.
+ */
+ public ContentModel(int a_type, ContentModel a_content)
+ {
+ content = a_content;
+ type = a_type;
+ }
+
+ /**
+ * Create a content model, involving binary expression of the given type.
+ * @param a_type The expression operation type ( ',', '|' or '&amp;').
+ * @param a_content The content of the left part of the expression.
+ * @param a_next The content model, representing the right part of the
+ * expression.
+ */
+ public ContentModel(int a_type, Object a_content, ContentModel a_next)
+ {
+ content = a_content;
+ type = a_type;
+ next = a_next;
+ }
+
+ /**
+ * Adds all list elements to the given vector, ignoring the
+ * operations between the elements. The old vector values are not
+ * discarded.
+ * @param elements - a vector to add the values to.
+ */
+ public void getElements(Vector<Element> elements)
+ {
+ ContentModel c = this;
+
+ while (c != null)
+ {
+ // FIXME: correct?
+ if (c.content instanceof Element)
+ elements.add((Element) c.content);
+ c = c.next;
+ }
+ }
+
+ /**
+ * Checks if the content model matches an empty input stream.
+ * The empty content is created using SGML DTD keyword EMPTY.
+ * The empty model is a model with the content field equal to null.
+ *
+ * @return true if the content field is equal to null.
+ */
+ public boolean empty()
+ {
+ return content == null;
+ }
+
+ /**
+ * Get the element, stored in the <code>next.content</code>.
+ * The method is programmed as the part of the standard API, but not
+ * used in this implementation.
+ * @return the value of the field <code>next</code>.
+ */
+ public Element first()
+ {
+ return (Element) next.content;
+ }
+
+ /**
+ * Checks if this object can potentially be the first token in the
+ * ContenModel list. The method is programmed as the part of the
+ * standard API, but not used in this implementation.
+ */
+ public boolean first(Object token)
+ {
+ ContentModel c = this;
+ while (c.next != null)
+ {
+ if (c.content != null && c.content.toString().equals(token.toString()) &&
+ c.type != ','
+ )
+
+ // Agree if the operation with the preceeding element
+ // is not the comma operation.
+ return true;
+ c = c.next;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a string representation (an expression) of this content model.
+ * The expression has BNF-like syntax, except the absence of the
+ * unary operator is additionally indicated by " ' ". It is
+ * advisable to check the created models for correctness using this
+ * method.
+ */
+ public String toString()
+ {
+ return transformer.transform(this).toString();
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/DTD.java b/libjava/classpath/javax/swing/text/html/parser/DTD.java
new file mode 100644
index 000000000..09b50fee7
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/DTD.java
@@ -0,0 +1,609 @@
+/* DTD.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.BitSet;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * <p>Representation or the SGML DTD document.
+ * Provides basis for describing a syntax of the
+ * HTML documents. The fields of this class are NOT initialized in
+ * constructor. You need to do this separately before passing this data
+ * structure to the HTML parser. The subclasses with the fields, pre-
+ * initialized, for example, for HTML 4.01, can be available only between
+ * the implementation specific classes
+ * ( for example, {@link gnu.javax.swing.text.html.parser.HTML_401F }
+ * in this implementation).</p>
+ * <p>
+ * If you need more information about SGML DTD documents,
+ * the author suggests to read SGML tutorial on
+ * <a href="http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html"
+ * >http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html</a>.
+ * We also recommend Goldfarb C.F (1991) <i>The SGML Handbook</i>,
+ * Oxford University Press, 688 p, ISBN: 0198537379.
+ * </p>
+ * <p>
+ * Warning: the html, head and other tag fields will only be automatically
+ * assigned if the VM has the correctly implemented reflection mechanism.
+ * As these fields are not used anywhere in the implementation, not
+ * exception will be thrown in the opposite case.
+ * </p>
+ *
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public class DTD
+ implements DTDConstants
+{
+ /**
+ * The version of the persistent data format.
+ * @specnote This was made <code>final</code> in 1.5.
+ */
+ public static final int FILE_VERSION = 1;
+
+ /**
+ * The table of existing available DTDs.
+ */
+ static Hashtable<String,DTD> dtdHash = new Hashtable<String,DTD>();
+
+ /**
+ * The applet element for this DTD.
+ */
+ public Element applet;
+
+ /**
+ * The base element for this DTD.
+ */
+ public Element base;
+
+ /**
+ * The body element for this DTD.
+ */
+ public Element body;
+
+ /**
+ * The head element for this DTD.
+ */
+ public Element head;
+
+ /**
+ * The html element for this DTD.
+ */
+ public Element html;
+
+ /**
+ * The isindex element of for this DTD.
+ */
+ public Element isindex;
+
+ /**
+ * The meta element for this DTD.
+ */
+ public Element meta;
+
+ /**
+ * The p element for this DTD.
+ */
+ public Element p;
+
+ /**
+ * The param element for this DTD.
+ */
+ public Element param;
+
+ /**
+ * The pcdata for this DTD.
+ */
+ public Element pcdata;
+
+ /**
+ * The title element for this DTD.
+ */
+ public Element title;
+
+ /**
+ * The element for accessing all DTD elements by name.
+ */
+ public Hashtable<String,Element> elementHash =
+ new Hashtable<String,Element>();
+
+ /**
+ * The entity table for accessing all DTD entities by name.
+ */
+ public Hashtable<Object, Entity> entityHash = new Hashtable<Object, Entity>();
+
+ /**
+ * The name of this DTD.
+ */
+ public String name;
+
+ /**
+ * Contains all elements in this DTD. The
+ * javax.swing.text.html.parser.Element#index field of all elements
+ * in this vector is set to the element position in this vector.
+ */
+ public Vector<Element> elements = new Vector<Element>();
+
+ /** Create a new DTD with the specified name. */
+ protected DTD(String a_name)
+ {
+ name = a_name;
+ }
+
+ /** Get this DTD by name. The current implementation
+ * only looks in the internal table of DTD documents. If no corresponding
+ * entry is found, the new entry is created, placed into
+ * the table and returned. */
+ public static DTD getDTD(String name)
+ throws IOException
+ {
+ DTD d = dtdHash.get(name);
+
+ if (d == null)
+ {
+ d = new DTD(name);
+ dtdHash.put(d.name, d);
+ }
+
+ return d;
+ }
+
+ /**
+ * Get the element by the element name. If the element is not yet
+ * defined, it is newly created and placed into the element table.
+ * If the element name matches (ingoring case) a public non static
+ * element field in this class, this field is assigned to the value
+ * of the newly created element.
+ */
+ public Element getElement(String element_name)
+ {
+ return newElement(element_name);
+ }
+
+ /**
+ * Get the element by the value of its
+ * {@link javax.swing.text.html.parser.Element#index} field.
+ */
+ public Element getElement(int index)
+ {
+ return elements.get(index);
+ }
+
+ /**
+ * Get the entity with the given identifier.
+ * @param id that can be returned by
+ * {@link javax.swing.text.html.parser.Entity#name2type(String an_entity)}
+ * @return The entity from this DTD or null if there is no entity with
+ * such id or such entity is not present in the table of this instance.
+ */
+ public Entity getEntity(int id)
+ {
+ String name = Entity.mapper.get(id);
+
+ if (name != null)
+ return entityHash.get(name);
+ else
+ return null;
+ }
+
+ /**
+ * Get the named entity by its name.
+ */
+ public Entity getEntity(String entity_name)
+ {
+ return entityHash.get(entity_name);
+ }
+
+ /**
+ * Get the name of this instance of DTD
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Creates, adds into the entity table and returns the
+ * character entity like <code>&amp;lt;</code>
+ * (means '<code>&lt;</code>' );
+ * @param name The entity name (without heading &amp; and closing ;)
+ * @param type The entity type
+ * @param character The entity value (single character)
+ * @return The created entity
+ */
+ public Entity defEntity(String name, int type, int character)
+ {
+ Entity e = newEntity(name, type);
+ e.data = new char[] { (char) character };
+ return e;
+ }
+
+ /**
+ * Define the attributes for the element with the given name.
+ * If the element is not exist, it is created.
+ * @param forElement
+ * @param attributes
+ */
+ public void defineAttributes(String forElement, AttributeList attributes)
+ {
+ Element e = elementHash.get(forElement.toLowerCase());
+
+ if (e == null)
+ e = newElement(forElement);
+
+ e.atts = attributes;
+ }
+
+ /**
+ * Defines the element and adds it to the element table. Sets the
+ * <code>Element.index</code> field to the value, unique for this
+ * instance of DTD. If the element with the given name already exists,
+ * replaces all other its settings by the method argument values.
+ * @param name the name of the element
+ * @param type the type of the element
+ * @param headless true if the element needs no starting tag
+ * (should not occur in HTML).
+ * @param tailless true if the element needs no ending tag (like
+ * <code>&lt;hr&gt;</code>
+ * @param content the element content
+ * @param exclusions the set of elements that must not occur inside
+ * this element. The <code>Element.index</code> value defines which
+ * bit in this bitset corresponds to that element.
+ * @param inclusions the set of elements that can occur inside this
+ * element. the <code>Element.index</code> value defines which
+ * bit in this bitset corresponds to that element.
+ * @param attributes the element attributes.
+ * @return the newly defined element.
+ */
+ public Element defineElement(String name, int type, boolean headless,
+ boolean tailless, ContentModel content,
+ BitSet exclusions, BitSet inclusions,
+ AttributeList attributes
+ )
+ {
+ Element e = newElement(name);
+ e.type = type;
+ e.oStart = headless;
+ e.oEnd = tailless;
+ e.content = content;
+ e.exclusions = exclusions;
+ e.inclusions = inclusions;
+ e.atts = attributes;
+
+ return e;
+ }
+
+ /**
+ * Creates, intializes and adds to the entity table the new
+ * entity.
+ * @param name the name of the entity
+ * @param type the type of the entity
+ * @param data the data section of the entity
+ * @return the created entity
+ */
+ public Entity defineEntity(String name, int type, char[] data)
+ {
+ Entity e = newEntity(name, type);
+ e.data = data;
+
+ return e;
+ }
+
+ /** Place this DTD into the DTD table. */
+ public static void putDTDHash(String name, DTD dtd)
+ {
+ dtdHash.put(name, dtd);
+ }
+
+ /**
+ * <p>Reads DTD from an archived format. This format is not standardized
+ * and differs between implementations.</p><p> This implementation
+ * reads and defines all entities and elements using
+ * ObjectInputStream. The elements and entities can be written into the
+ * stream in any order. The objects other than elements and entities
+ * are ignored.</p>
+ * @param stream A data stream to read from.
+ * @throws java.io.IOException If one is thrown by the input stream
+ */
+ public void read(DataInputStream stream)
+ throws java.io.IOException
+ {
+ ObjectInputStream oi = new ObjectInputStream(stream);
+ Object def;
+ try
+ {
+ while (true)
+ {
+ def = oi.readObject();
+ if (def instanceof Element)
+ {
+ Element e = (Element) def;
+ elementHash.put(e.name.toLowerCase(), e);
+ assignField(e);
+ }
+ else if (def instanceof Entity)
+ {
+ Entity e = (Entity) def;
+ entityHash.put(e.name, e);
+ }
+ }
+ }
+ catch (ClassNotFoundException ex)
+ {
+ throw new IOException(ex.getMessage());
+ }
+ catch (EOFException ex)
+ {
+ // ok EOF
+ }
+ }
+
+ /**
+ * Returns the name of this instance of DTD.
+ */
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Creates and returns new attribute (not an attribute list).
+ * @param name the name of this attribute
+ * @param type the type of this attribute (FIXED, IMPLIED or
+ * REQUIRED from <code>DTDConstants</code>).
+ * @param modifier the modifier of this attribute
+ * @param default_value the default value of this attribute
+ * @param allowed_values the allowed values of this attribute. The multiple
+ * possible values in this parameter are supposed to be separated by
+ * '|', same as in SGML DTD <code>&lt;!ATTLIST </code>tag. This parameter
+ * can be null if no list of allowed values is specified.
+ * @param atts the previous attribute of this element. This is
+ * placed to the field
+ * {@link javax.swing.text.html.parser.AttributeList#next },
+ * creating a linked list.
+ * @return The attributes.
+ */
+ protected AttributeList defAttributeList(String name, int type, int modifier,
+ String default_value,
+ String allowed_values,
+ AttributeList atts
+ )
+ {
+ AttributeList al = new AttributeList(name);
+ al.modifier = modifier;
+ al.value = default_value;
+ al.next = atts;
+
+ if (allowed_values != null)
+ {
+ StringTokenizer st = new StringTokenizer(allowed_values, " \t|");
+ Vector<String> v = new Vector<String>(st.countTokens());
+
+ while (st.hasMoreTokens())
+ v.add(st.nextToken());
+
+ al.values = v;
+ }
+
+ return al;
+ }
+
+ /**
+ * Creates a new content model.
+ * @param type specifies the BNF operation for this content model.
+ * The valid operations are documented in the
+ * {@link javax.swing.text.html.parser.ContentModel#type }.
+ * @param content the content of this content model
+ * @param next if the content model is specified by BNF-like
+ * expression, contains the rest of this expression.
+ * @return The newly created content model.
+ */
+ protected ContentModel defContentModel(int type, Object content,
+ ContentModel next
+ )
+ {
+ ContentModel model = new ContentModel();
+ model.type = type;
+ model.next = next;
+ model.content = content;
+
+ return model;
+ }
+
+ /**
+ * Defines a new element and adds it to the element table.
+ * If the element alredy exists,
+ * overrides it settings with the specified values.
+ * @param name the name of the new element
+ * @param type the type of the element
+ * @param headless true if the element needs no starting tag
+ * @param tailless true if the element needs no closing tag
+ * @param content the element content.
+ * @param exclusions the elements that must be excluded from the
+ * content of this element, in all levels of the hierarchy.
+ * @param inclusions the elements that can be included as the
+ * content of this element.
+ * @param attributes the element attributes.
+ * @return the created or updated element.
+ */
+ protected Element defElement(String name, int type, boolean headless,
+ boolean tailless, ContentModel content,
+ String[] exclusions, String[] inclusions,
+ AttributeList attributes
+ )
+ {
+ // compute the bit sets
+ BitSet exclude = bitSet(exclusions);
+ BitSet include = bitSet(inclusions);
+
+ Element e =
+ defineElement(name, type, headless, tailless, content, exclude, include,
+ attributes
+ );
+
+ return e;
+ }
+
+ /**
+ * Creates, intializes and adds to the entity table the new
+ * entity.
+ * @param name the name of the entity
+ * @param type the type of the entity
+ * @param data the data section of the entity
+ * @return the created entity
+ */
+ protected Entity defEntity(String name, int type, String data)
+ {
+ Entity e = newEntity(name, type);
+ e.data = data.toCharArray();
+
+ return e;
+ }
+
+ private void assignField(Element e)
+ {
+ String element_name = e.name;
+ try
+ {
+ // Assign the field via reflection.
+ Field f = getClass().getField(element_name.toLowerCase());
+ if ((f.getModifiers() & Modifier.PUBLIC) != 0)
+ if ((f.getModifiers() & Modifier.STATIC) == 0)
+ if (f.getType().isAssignableFrom(e.getClass()))
+ f.set(this, e);
+ }
+ catch (IllegalAccessException ex)
+ {
+ unexpected(ex);
+ }
+ catch (NoSuchFieldException ex)
+ {
+ // This is ok.
+ }
+
+ // Some virtual machines may still lack the proper
+ // implementation of reflection. As the tag fields
+ // are not used anywhere in this implementation,
+ // (and this class is also rarely used by the end user),
+ // it may be better not to crash everything by throwing an error
+ // for each case when the HTML parsing is required.
+ catch (Throwable t)
+ {
+ // This VM has no reflection mechanism implemented!
+ if (t instanceof OutOfMemoryError)
+ throw (Error) t;
+ }
+ }
+
+ /**
+ * Create the bit set for this array of elements.
+ * The unknown elements are automatically defined and added
+ * to the element table.
+ * @param elements
+ * @return The bit set.
+ */
+ private BitSet bitSet(String[] elements)
+ {
+ BitSet b = new BitSet();
+
+ for (int i = 0; i < elements.length; i++)
+ {
+ Element e = getElement(elements [ i ]);
+
+ if (e == null)
+ e = newElement(elements [ i ]);
+
+ b.set(e.index);
+ }
+
+ return b;
+ }
+
+ /**
+ * Find the element with the given name in the element table.
+ * If not find, create a new element with this name and add to the
+ * table.
+ * @param name the name of the element
+ * @return the found or created element.
+ */
+ private Element newElement(String name)
+ {
+ Element e = elementHash.get(name.toLowerCase());
+
+ if (e == null)
+ {
+ e = new Element();
+ e.name = name;
+ e.index = elements.size();
+ elements.add(e);
+ elementHash.put(e.name.toLowerCase(), e);
+ assignField(e);
+ }
+ return e;
+ }
+
+ /**
+ * Creates and adds to the element table the entity with an
+ * unitialized data section. Used internally.
+ * @param name the name of the entity
+ * @param type the type of the entity, a bitwise combination
+ * of GENERAL, PARAMETER, SYSTEM and PUBLIC.
+ *
+ * @return the created entity
+ */
+ private Entity newEntity(String name, int type)
+ {
+ Entity e = new Entity(name, type, null);
+ entityHash.put(e.name, e);
+ return e;
+ }
+
+ private void unexpected(Exception ex)
+ {
+ throw new Error("This should never happen, report a bug", ex);
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/DTDConstants.java b/libjava/classpath/javax/swing/text/html/parser/DTDConstants.java
new file mode 100644
index 000000000..75e7afb4d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/DTDConstants.java
@@ -0,0 +1,292 @@
+/* DTDConstants.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+/**
+ * <p>This class defines the SGML basic types, used for describing HTML 4.01
+ * at <a href="http://www.w3.org/TR/html4/types.html"
+ * >http://www.w3.org/TR/html4/types.html</a>. Not all constants,
+ * defined here, are actually used in HTML 4.01 SGML specification. Some others
+ * are defined just as part of the required implementation.
+ * </p>
+ * <p>
+ * If you need more information about SGML DTD documents,
+ * the author suggests to read SGML tutorial on
+ * <a href="http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html"
+ * >http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html</a>.
+ * We also recommend Goldfarb C.F (1991) <i>The SGML Handbook</i>,
+ * Oxford University Press, 688 p, ISBN: 0198537379.
+ * </p>
+ *
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public interface DTDConstants
+{
+ /* ----- The data types, used in HTML 4.01 SGML definition: ---- */
+
+ /**
+ * The CDATA (Character data) constant, specifes the content model,
+ * consisting of characters only. In SGML for HTML 4.01, the character
+ * entities must be replaced by characters, the line feeds must be
+ * ignored and any number of the subsequent carriage returns or tabs
+ * must be replaced by a single space.
+ */
+ int CDATA = 1;
+
+ /**
+ * The EMPTY constant, means the element with no content.
+ */
+ int EMPTY = 17;
+
+ /**
+ * The ID constant, means that the token is the unique identifier.
+ * This identifier can be referenced by attribute with value of IDREF.
+ * The identifier must begin with letter, followed by any number of
+ * letters, digits, hyphens, underscores, colons and periods.
+ */
+ int ID = 4;
+
+ /**
+ * The IDREF constant, specifies reference to a valid ID within
+ * the document.
+ */
+ int IDREF = 5;
+
+ /**
+ * The IDREFS constant, a space separated list of IDREFs
+ */
+ int IDREFS = 6;
+
+ /**
+ * The NAME constant, means the token that
+ * must begin with letter, followed by any number of
+ * letters, digits, hyphens, underscores, colons and periods.
+ */
+ int NAME = 7;
+
+ /**
+ * The NAMES constant, specifies a space separated of NAMEs.
+ */
+ int NAMES = 8;
+
+ /**
+ * The NMTOKEN constant, specifies the attribute, consisting of
+ * characters that can be either digits or alphabetic characters).
+ */
+ int NMTOKEN = 9;
+
+ /**
+ * The NMTOKENS constant, specifies a list of NMTOKENs.
+ */
+ int NMTOKENS = 10;
+
+ /**
+ * The NOTATION constant, a previously defined data type.
+ */
+ int NOTATION = 11;
+
+ /**
+ * The NUMBER constant (means that the attribute consists of at least
+ * one decimal digit).
+ */
+ int NUMBER = 12;
+
+ /**
+ * The NUMBERS constant, specifies a space separated list of NUMBERs.
+ */
+ int NUMBERS = 13;
+
+ /**
+ * The NUTOKEN constant.
+ */
+ int NUTOKEN = 14;
+
+ /**
+ * The NUTOKENS constant.
+ */
+ int NUTOKENS = 15;
+
+ /* -------
+ The entity scope constants.
+ As these four constants are combined with the bitwise OR,
+ they are defined in the hexadecimal notation.
+ The reason of setting the two bits at once (for PUBLIC and SYSTEM)
+ is probably historical. ----- */
+
+ /**
+ * The PUBLIC constant, specifies the public entity. The PUBLIC entities
+ * are assumed to be known to many systems so that a full declaration
+ * need not be transmitted. For example,
+ * &lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"&gt;
+ */
+ int PUBLIC = 0xA;
+
+ /**
+ * The SYSTEM constant, specifies the system entitiy. The system entities
+ * are assumed to be known but require the clear identifer
+ * (like the file path), where they can be found in the system.
+ * For example, <code>
+ * &lt;DOCTYPE html SYSTEM "/path/to/file.dtd"&gt; </code>.
+ */
+ int SYSTEM = 0x11;
+
+ /**
+ * The PARAMETER constant, specifies that entity is only valid
+ * inside SGML DTD scope.
+ */
+ int PARAMETER = 0x40000;
+
+ /**
+ * The GENERAL constant, specifies theat the entity is valid in the
+ * whole HTML document scope.
+ */
+ int GENERAL = 0x10000;
+
+ /* ---- The constants, defining if the element attribute is required,
+ fixed or implied. ---- */
+
+ /**
+ * The attribute modifier #REQUIRED constant, indicates that the
+ * value must be supplied.
+ */
+ int REQUIRED = 2;
+
+ /**
+ * The attribute modifier #FIXED constant, means that the attribute has
+ * the fixed value that cannot be changed.
+ */
+ int FIXED = 1;
+
+ /**
+ * The attribute modifier #IMPLIED constant,
+ * indicating that for this attribute the user agent must provide
+ * the value itself.
+ */
+ int IMPLIED = 5;
+
+ /**
+ * The attribute modifier #CURRENT constant, specifies the value
+ * that at any point in the document is the last value supplied for
+ * that element. A value is required to be supplied for the first
+ * occurrence of an element
+ */
+ int CURRENT = 3;
+
+ /**
+ * The attribute modifier #CONREF constant, specifies the IDREF value of
+ * the reference to content in another location of the document.
+ * The element with this attribute is empty, the content from
+ * that another location must be used instead.
+ */
+ int CONREF = 4;
+
+ /* ----- Constants, defining if the element
+ start and end tags are required. ---- */
+
+ /**
+ * The STARTTAG, meaning that the element needs a starting tag.
+ */
+ int STARTTAG = 13;
+
+ /**
+ * The ENDTAG constant, meaning that the element needs a closing tag.
+ */
+ int ENDTAG = 14;
+
+ /* ----- Other constants: ----- */
+
+ /**
+ * The ANY constant, specifies
+ * an attribute, consisting from arbitrary characters.
+ */
+ int ANY = 19;
+
+ /**
+ * The DEFAULT constant, specifies the default value.
+ */
+ int DEFAULT = 131072;
+
+ /**
+ * The ENTITIES constant (list of ENTITYes)
+ */
+ int ENTITIES = 3;
+
+ /**
+ * The ENTITY constant, meaning the numeric or symbolic name of some
+ * HTML data.
+ */
+ int ENTITY = 2;
+
+ /**
+ * The MD constant.
+ */
+ int MD = 16;
+
+ /**
+ * The MODEL constant.
+ */
+ int MODEL = 18;
+
+ /**
+ * The MS constant.
+ */
+ int MS = 15;
+
+ /**
+ * The PI (Processing Instruction) constant, specifies a processing
+ * instruction. Processing instructions are used to embed information
+ * intended for specific applications.
+ */
+ int PI = 12;
+
+ /**
+ * The RCDATA constant (Entity References and Character Data), specifies
+ * the content model, consisting of characters AND entities. The
+ * "&lt;" is threated as an ordinary character, but
+ * "<code>&amp;name;</code>" still means the general entity with
+ * the given name.
+ */
+ int RCDATA = 16;
+
+ /**
+ * The SDATA constant. Means that the value contains the entity name
+ * and the replacement value of a character entity reference.
+ */
+ int SDATA = 11;
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java
new file mode 100644
index 000000000..f717d69cb
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java
@@ -0,0 +1,268 @@
+/* DocumentParser.java -- A parser for HTML documents.
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import javax.swing.text.html.parser.Parser;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.html.HTMLEditorKit;
+
+/**
+ * <p>A simple error-tolerant HTML parser that uses a DTD document
+ * to access data on the possible tokens, arguments and syntax.</p>
+ * <p> The parser reads an HTML content from a Reader and calls various
+ * notifying methods (which should be overridden in a subclass)
+ * when tags or data are encountered.</p>
+ * <p>Some HTML elements need no opening or closing tags. The
+ * task of this parser is to invoke the tag handling methods also when
+ * the tags are not explicitly specified and must be supposed using
+ * information, stored in the DTD.
+ * For example, parsing the document
+ * <p>&lt;table&gt;&lt;tr&gt;&lt;td&gt;a&lt;td&gt;b&lt;td&gt;c&lt;/tr&gt; <br>
+ * will invoke exactly the handling methods exactly in the same order
+ * (and with the same parameters) as if parsing the document: <br>
+ * <em>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;table&gt;&lt;
+ * tbody&gt;</em>&lt;tr&gt;&lt;td&gt;a<em>&lt;/td&gt;</em>&lt;td&gt;b<em>
+ * &lt;/td&gt;</em>&lt;td&gt;c<em>&lt;/td&gt;&lt;/tr&gt;</em>&lt;
+ * <em>/tbody&gt;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;</em></p>
+ * (supposed tags are given in italics). The parser also supports
+ * obsolete elements of HTML syntax.<p>
+ * </p>
+ * In this implementation, DocumentParser is directly derived from its
+ * ancestor without changes of functionality.
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public class DocumentParser
+ extends Parser
+ implements DTDConstants
+{
+ /**
+ * The enclosed working parser class.
+ */
+ private class gnuParser
+ extends gnu.javax.swing.text.html.parser.support.Parser
+ {
+ private gnuParser(DTD d)
+ {
+ super(d);
+ }
+
+ protected final void handleComment(char[] comment)
+ {
+ parser.handleComment(comment);
+ callBack.handleComment(comment, hTag.where.startPosition);
+ }
+
+ protected final void handleEmptyTag(TagElement tag)
+ throws javax.swing.text.ChangedCharSetException
+ {
+ parser.handleEmptyTag(tag);
+ callBack.handleSimpleTag(tag.getHTMLTag(), getAttributes(),
+ hTag.where.startPosition
+ );
+ }
+
+ protected final void handleEndTag(TagElement tag)
+ {
+ parser.handleEndTag(tag);
+ callBack.handleEndTag(tag.getHTMLTag(), hTag.where.startPosition);
+ }
+
+ protected final void handleError(int line, String message)
+ {
+ parser.handleError(line, message);
+ callBack.handleError(message, hTag.where.startPosition);
+ }
+
+ protected final void handleStartTag(TagElement tag)
+ {
+ parser.handleStartTag(tag);
+ SimpleAttributeSet attributes = gnu.getAttributes();
+
+ if (tag.fictional())
+ attributes.addAttribute(HTMLEditorKit.ParserCallback.IMPLIED,
+ Boolean.TRUE
+ );
+
+ callBack.handleStartTag(tag.getHTMLTag(), attributes,
+ hTag.where.startPosition
+ );
+ }
+
+ protected final void handleText(char[] text)
+ {
+ parser.handleText(text);
+ callBack.handleText(text, hTag.where.startPosition);
+ }
+
+ DTD getDTD()
+ {
+ return dtd;
+ }
+ }
+
+ /**
+ * This field is used to access the identically named
+ * methods of the outer class.
+ * This is package-private to avoid an accessor method.
+ */
+ DocumentParser parser = this;
+
+ /**
+ * The callback.
+ * This is package-private to avoid an accessor method.
+ */
+ HTMLEditorKit.ParserCallback callBack;
+
+ /**
+ * The reference to the working class of HTML parser that is
+ * actually used to parse the document.
+ * This is package-private to avoid an accessor method.
+ */
+ gnuParser gnu;
+
+ /**
+ * Creates a new parser that uses the given DTD to access data on the
+ * possible tokens, arguments and syntax. There is no single - step way
+ * to get a default DTD; you must either refer to the implementation -
+ * specific packages, write your own DTD or obtain the working instance
+ * of parser in other way, for example, by calling
+ * {@link javax.swing.text.html.HTMLEditorKit#getParser()}.
+ *
+ * @param a_dtd a DTD to use.
+ */
+ public DocumentParser(DTD a_dtd)
+ {
+ super(a_dtd);
+ gnu = new gnuParser(a_dtd);
+ }
+
+ /**
+ * Parses the HTML document, calling methods of the provided
+ * callback. This method must be multithread - safe.
+ * @param reader The reader to read the HTML document from
+ * @param aCallback The callback that is notifyed about the presence
+ * of HTML elements in the document.
+ * @param ignoreCharSet If thrue, any charset changes during parsing
+ * are ignored.
+ * @throws java.io.IOException
+ */
+ public void parse(Reader reader, HTMLEditorKit.ParserCallback aCallback,
+ boolean ignoreCharSet
+ )
+ throws IOException
+ {
+ callBack = aCallback;
+ gnu.parse(reader);
+
+ callBack.handleEndOfLineString(gnu.getEndOfLineSequence());
+ try
+ {
+ callBack.flush();
+ }
+ catch (BadLocationException ex)
+ {
+ // Convert this into the supported type of exception.
+ throw new IOException(ex.getMessage());
+ }
+ }
+
+ /**
+ * Handle HTML comment. The default method returns without action.
+ * @param comment the comment being handled
+ */
+ protected void handleComment(char[] comment)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * Handle the tag with no content, like &lt;br&gt;. The method is
+ * called for the elements that, in accordance with the current DTD,
+ * has an empty content.
+ * @param tag the tag being handled.
+ * @throws javax.swing.text.ChangedCharSetException
+ */
+ protected void handleEmptyTag(TagElement tag)
+ throws javax.swing.text.ChangedCharSetException
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * The method is called when the HTML closing tag ((like &lt;/table&gt;)
+ * is found or if the parser concludes that the one should be present
+ * in the current position.
+ * @param tag The tag being handled
+ */
+ protected void handleEndTag(TagElement tag)
+ {
+ // This default implementation does nothing.
+ }
+
+ /* Handle error that has occured in the given line. */
+ protected void handleError(int line, String message)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * The method is called when the HTML opening tag ((like &lt;table&gt;)
+ * is found or if the parser concludes that the one should be present
+ * in the current position.
+ * @param tag The tag being handled
+ */
+ protected void handleStartTag(TagElement tag)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * Handle the text section.
+ * @param text a section text.
+ */
+ protected void handleText(char[] text)
+ {
+ // This default implementation does nothing.
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/Element.java b/libjava/classpath/javax/swing/text/html/parser/Element.java
new file mode 100644
index 000000000..c07c07f54
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/Element.java
@@ -0,0 +1,317 @@
+/* Element.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import gnu.javax.swing.text.html.parser.support.gnuStringIntMapper;
+
+import java.io.Serializable;
+
+import java.util.BitSet;
+
+/**
+ * <p>
+ * Stores the element information, obtained by parsing SGML DTD
+ * tag <code>&lt;!ELEMENT .. &gt;</code>. This class has no public
+ * constructor and can only be instantiated using the
+ * {@link javax.swing.text.html.parser.DTD } methods</p>
+ *
+ * <p>SGML defines elements that represent structures or
+ * behavior. An element typically consists of a start tag, content, and an
+ * end tag. Hence the elements are not tags. The HTML 4.0 definition specifies
+ * that some elements are not required to have the end tags. Also, some
+ * HTML elements (like <code>&lt;hr&gt;</code>) have no content. Element names
+ * are case sensitive.</p>
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public final class Element
+ implements DTDConstants, Serializable
+{
+ /**
+ * Package level mapper between type names and they string values.
+ */
+ static final gnuStringIntMapper mapper =
+ new gnuStringIntMapper()
+ {
+ protected void create()
+ {
+ add("CDATA", DTDConstants.CDATA);
+ add("RCDATA", DTDConstants.RCDATA);
+ add("EMPTY", DTDConstants.EMPTY);
+ add("ANY", DTDConstants.ANY);
+ }
+ };
+
+ /** Use serialVersionUID for interoperability. */
+ private static final long serialVersionUID = -6717939384601675586L;
+
+ /**
+ * The element attributes.
+ */
+ public AttributeList atts;
+
+ /**
+ * Contains refernces to elements that must NOT occur inside this element,
+ * at any level of hierarchy.
+ */
+ public BitSet exclusions;
+
+ /**
+ * Contains refernces to elements that must CAN occur inside this element,
+ * at any level of hierarchy.
+ */
+ public BitSet inclusions;
+
+ /**
+ * The content model, defining elements, entities and DTD text
+ * that may/may not occur inside this element.
+ */
+ public ContentModel content;
+
+ /**
+ * A field to store additional user data for this Element.
+ */
+ public Object data;
+
+ /**
+ * The element name.
+ */
+ public String name;
+
+ /**
+ * True is this element need not to have the closing tag, false
+ * otherwise. The HTML 4.0 definition specifies
+ * that some elements (like <code>&lt;hr&gt;</code>are
+ * not required to have the end tags.
+ */
+ public boolean oEnd;
+
+ /**
+ * True is this element need not to have the starting tag, false
+ * otherwise. The HTML 4.0 definition specifies
+ * that some elements (like <code>&lt;head&gt;</code> or
+ * <code>&lt;body&gt;</code>) are
+ * not required to have the start tags.
+
+ */
+ public boolean oStart;
+
+ /**
+ * This field contains the unique integer identifier of this Element,
+ * used to refer the element (more exactly, the element flag)
+ * in <code>inclusions</code> and <code>exclusions</code> bit set.
+ */
+ public int index;
+
+ /**
+ * The element type, containing value, defined in DTDConstants.
+ * In this implementation, the element type can be
+ * CDATA, RCDATA, EMPTY or ANY.
+ */
+ public int type;
+
+ /**
+ * The default constructor must have package level access in this
+ * class. Use DTD.defineElement(..) to create an element when required.
+ */
+ Element()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Converts the string representation of the element type
+ * into its unique integer identifier, defined in DTDConstants.
+ * @param a_type A name of the type
+ * @return DTDConstants.CDATA, DTDConstants.RCDATA, DTDConstants.EMPTY,
+ * DTDConstants.ANY or null if the type name is not
+ * "CDATA", "RCDATA", "EMPTY" or "ANY". This function is case sensitive.
+ * @throws NullPointerException if <code>a_type</code> is null.
+ */
+ public static int name2type(String a_type)
+ {
+ return mapper.get(a_type);
+ }
+
+ /**
+ * Get the element attribute by name.
+ * @param attribute the attribute name, case insensitive.
+ * @return the correspoding attribute of this element. The class,
+ * for storing as attribute list, as a single attribute, is used to
+ * store a single attribute in this case.
+ * @throws NullPointerException if the attribute name is null.
+ */
+ public AttributeList getAttribute(String attribute)
+ {
+ AttributeList a = atts;
+
+ while (a != null && !attribute.equalsIgnoreCase(a.name))
+ a = a.next;
+
+ return a;
+ }
+
+ /**
+ * Get the element attribute by its value.
+ * @param a_value the attribute value, case insensitive.
+ * @return the correspoding attribute of this element. The class,
+ * for storing as attribute list, as a single attribute, is used to
+ * store a single attribute in this case. If there are several
+ * attributes with the same value, there is no garranty, which one
+ * is returned.
+ */
+ public AttributeList getAttributeByValue(String a_value)
+ {
+ AttributeList a = atts;
+
+ if (a_value == null)
+ {
+ while (a != null)
+ {
+ if (a.value == null)
+ return a;
+
+ a = a.next;
+ }
+ }
+ else
+ {
+ while (a != null)
+ {
+ if (a.value != null && a_value.equalsIgnoreCase(a.value))
+ return a;
+
+ a = a.next;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get all attributes of this document as an attribute list.
+ * @return The attribute list.
+ */
+ public AttributeList getAttributes()
+ {
+ return atts;
+ }
+
+ /**
+ * Get the content model, defining elements, entities and DTD text
+ * that may/may not occur inside this element.
+ */
+ public ContentModel getContent()
+ {
+ return content;
+ }
+
+ /**
+ * Returns true for the element with no content.
+ * Empty elements are defined with the SGML DTD keyword "EMPTY".
+ * @return true if content model field (content) method is equal to
+ * null or its method empty() returns true.
+ */
+ public boolean isEmpty()
+ {
+ return content == null || content.empty();
+ }
+
+ /**
+ * Get the unique integer identifier of this Element,
+ * used to refer the element (more exactly, the element flag)
+ * in <code>inclusions</code> and <code>exclusions</code> bit set.
+ * WARNING: This value may not be the same between different
+ * implementations.
+ */
+ public int getIndex()
+ {
+ return index;
+ }
+
+ /**
+ * Get the element name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Get the element type.
+ * @return one of the values, defined DTDConstants.
+ * In this implementation, the element type can be
+ * CDATA, RCDATA, EMPTY or ANY.
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+ /**
+ * True is this element need not to have the starting tag, false
+ * otherwise.s element need not to have the closing tag, false
+ * otherwise. The HTML 4.0 definition specifies
+ * that some elements (like <code>&lt;hr&gt;</code>are
+ * not required to have the end tags.
+ */
+ public boolean omitEnd()
+ {
+ return oEnd;
+ }
+
+ /**
+ * True is this element need not to have the closing tag, false
+ * otherwise. The HTML 4.0 definition specifies
+ * that some elements (like <code>&lt;head&gt;</code> or
+ * <code>&lt;body&gt;</code>) are
+ * not required to have the start tags.
+ */
+ public boolean omitStart()
+ {
+ return oStart;
+ }
+
+ /**
+ * Returns the name of this element.
+ */
+ public String toString()
+ {
+ return name;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/Entity.java b/libjava/classpath/javax/swing/text/html/parser/Entity.java
new file mode 100644
index 000000000..d40fb94f3
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/Entity.java
@@ -0,0 +1,183 @@
+/* Entity.java -- Stores information, obtained by parsing SGML DTL
+ * &lt;!ENTITY % .. &gt; tag
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import gnu.javax.swing.text.html.parser.support.gnuStringIntMapper;
+
+/**
+ * <p>Stores information, obtained by parsing SGML DTL
+ * &lt;!ENTITY % .. &gt; tag.</p>
+ * <p>
+ * The entity defines some kind of macro that can be used elsewhere in
+ * the document.
+ * When the macro is referred to by the name in the DTD, it is expanded into
+ * a string
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public final class Entity
+ implements DTDConstants
+{
+ /**
+ * Package level mapper between type names and they string values.
+ */
+ final static gnuStringIntMapper mapper =
+ new gnuStringIntMapper()
+ {
+ protected void create()
+ {
+ add("ANY", DTDConstants.ANY);
+ add("CDATA", DTDConstants.CDATA);
+ add("PUBLIC", DTDConstants.PUBLIC);
+ add("SDATA", DTDConstants.SDATA);
+ add("PI", DTDConstants.PI);
+ add("STARTTAG", DTDConstants.STARTTAG);
+ add("ENDTAG", DTDConstants.ENDTAG);
+ add("MS", DTDConstants.MS);
+ add("MD", DTDConstants.MD);
+ add("SYSTEM", DTDConstants.SYSTEM);
+ }
+ };
+
+ /**
+ * The entity name.
+ */
+ public String name;
+
+ /**
+ * The entity data
+ */
+ public char[] data;
+
+ /**
+ * The entity type.
+ */
+ public int type;
+
+ /**
+ * String representation of the entity data.
+ */
+ private String sdata;
+
+ /**
+ * Create a new entity
+ * @param a_name the entity name
+ * @param a_type the entity type
+ * @param a_data the data replacing the entity reference
+ */
+ public Entity(String a_name, int a_type, char[] a_data)
+ {
+ name = a_name;
+ type = a_type;
+ data = a_data;
+ }
+
+ /**
+ * Converts a given string to the corresponding entity type.
+ * @return a value, defined in DTDConstants (one of
+ * PUBLIC, CDATA, SDATA, PI, STARTTAG, ENDTAG, MS, MD, SYSTEM)
+ * or CDATA if the parameter is not a valid entity type.
+ */
+ public static int name2type(String an_entity)
+ {
+ int r = mapper.get(an_entity);
+ return (r == 0) ? DTDConstants.CDATA : r;
+ }
+
+ /**
+ * Get the entity data.
+ */
+ public char[] getData()
+ {
+ return data;
+ }
+
+ /**
+ * Returns true for general entities. Each general entity can be
+ * referenced as <code>&entity-name;</code>. Such entities are
+ * defined by the SGML DTD tag
+ * <code>&lt;!ENTITY <i>name</i> "<i>value</i>"></code>. The general
+ * entities can be used anywhere in the document.
+ */
+ public boolean isGeneral()
+ {
+ return (type & DTDConstants.GENERAL) != 0;
+ }
+
+ /**
+ * Get the entity name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Returns true for parameter entities. Each parameter entity can be
+ * referenced as <code>&entity-name;</code>. Such entities are
+ * defined by the SGML DTD tag
+ * <code>&lt;!ENTITY % <i>name</i> "<i>value</i>"></code>. The parameter
+ * entities can be used only in SGML context.
+ */
+ public boolean isParameter()
+ {
+ return (type & DTDConstants.PARAMETER) != 0;
+ }
+
+ /**
+ * Returns a data as String
+ */
+ public String getString()
+ {
+ if (sdata == null)
+ sdata = new String(data);
+
+ return sdata;
+ }
+
+ /**
+ * Get the entity type.
+ * @return the value of the {@link #type}.
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/Parser.java b/libjava/classpath/javax/swing/text/html/parser/Parser.java
new file mode 100644
index 000000000..f3faa2524
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/Parser.java
@@ -0,0 +1,446 @@
+/* Parser.java -- HTML parser
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import javax.swing.text.ChangedCharSetException;
+import javax.swing.text.SimpleAttributeSet;
+
+/*
+ * FOR DEVELOPERS: To avoid regression, please run the package test
+ * textsuite/javax.swing.text.html.parser/AllParserTests after your
+ * modifications.
+ */
+
+/**
+ * <p>A simple error-tolerant HTML parser that uses a DTD document
+ * to access data on the possible tokens, arguments and syntax.</p>
+ * <p> The parser reads an HTML content from a Reader and calls various
+ * notifying methods (which should be overridden in a subclass)
+ * when tags or data are encountered.</p>
+ * <p>Some HTML elements need no opening or closing tags. The
+ * task of this parser is to invoke the tag handling methods also when
+ * the tags are not explicitly specified and must be supposed using
+ * information, stored in the DTD.
+ * For example, parsing the document
+ * <p>&lt;table&gt;&lt;tr&gt;&lt;td&gt;a&lt;td&gt;b&lt;td&gt;c&lt;/tr&gt; <br>
+ * will invoke exactly the handling methods exactly in the same order
+ * (and with the same parameters) as if parsing the document: <br>
+ * <em>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;table&gt;&lt;
+ * tbody&gt;</em>&lt;tr&gt;&lt;td&gt;a<em>&lt;/td&gt;</em>&lt;td&gt;b<em>
+ * &lt;/td&gt;</em>&lt;td&gt;c<em>&lt;/td&gt;&lt;/tr&gt;</em>&lt;
+ * <em>/tbody&gt;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;</em></p>
+ * (supposed tags are given in italics). The parser also supports
+ * obsolete elements of HTML syntax.<p>
+ * </p>
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public class Parser
+ implements DTDConstants
+{
+ /**
+ * The document template description that will be used to parse the documents.
+ */
+ protected DTD dtd;
+
+ /**
+ * The value of this field determines whether or not the Parser will be
+ * strict in enforcing SGML compatibility. The default value is false,
+ * stating that the parser should do everything to parse and get at least
+ * some information even from the incorrectly written HTML input.
+ */
+ protected boolean strict;
+
+ /**
+ * The package level reference to the working HTML parser in this
+ * implementation.
+ */
+ final gnu.javax.swing.text.html.parser.support.Parser gnu;
+
+ /**
+ * Creates a new parser that uses the given DTD to access data on the
+ * possible tokens, arguments and syntax. There is no single - step way
+ * to get a default DTD; you must either refer to the implementation -
+ * specific packages, write your own DTD or obtain the working instance
+ * of parser in other way, for example, by calling
+ * {@link javax.swing.text.html.HTMLEditorKit#getParser() }.
+ * @param a_dtd A DTD to use.
+ */
+ public Parser(DTD a_dtd)
+ {
+ dtd = a_dtd;
+
+ final Parser j = this;
+
+ gnu =
+ new gnu.javax.swing.text.html.parser.support.Parser(dtd)
+ {
+ protected final void handleComment(char[] comment)
+ {
+ j.handleComment(comment);
+ }
+
+ protected final void handleEOFInComment()
+ {
+ j.handleEOFInComment();
+ }
+
+ protected final void handleEmptyTag(TagElement tag)
+ throws javax.swing.text.ChangedCharSetException
+ {
+ j.handleEmptyTag(tag);
+ }
+
+ protected final void handleStartTag(TagElement tag)
+ {
+ j.handleStartTag(tag);
+ }
+
+ protected final void handleEndTag(TagElement tag)
+ {
+ j.handleEndTag(tag);
+ }
+
+ protected final void handleError(int line, String message)
+ {
+ j.handleError(line, message);
+ }
+
+ protected final void handleText(char[] text)
+ {
+ j.handleText(text);
+ }
+
+ protected final void handleTitle(char[] title)
+ {
+ j.handleTitle(title);
+ }
+
+ protected final void markFirstTime(Element element)
+ {
+ j.markFirstTime(element);
+ }
+
+ protected final void startTag(TagElement tag)
+ throws ChangedCharSetException
+ {
+ j.startTag(tag);
+ }
+
+ protected final void endTag(boolean omitted)
+ {
+ j.endTag(omitted);
+ }
+
+ protected TagElement makeTag(Element element)
+ {
+ return j.makeTag(element);
+ }
+
+ protected TagElement makeTag(Element element, boolean isSupposed)
+ {
+ return j.makeTag(element, isSupposed);
+ }
+ };
+ }
+
+ /**
+ * Parse the HTML text, calling various methods in response to the
+ * occurence of the corresponding HTML constructions.
+ * @param reader The reader to read the source HTML from.
+ * @throws IOException If the reader throws one.
+ */
+ public synchronized void parse(Reader reader)
+ throws IOException
+ {
+ gnu.parse(reader);
+ }
+
+ /**
+ * Parses DTD markup declaration. Currently returns without action.
+ * @return null.
+ * @throws java.io.IOException
+ */
+ public String parseDTDMarkup()
+ throws IOException
+ {
+ return gnu.parseDTDMarkup();
+ }
+
+ /**
+ * Parse DTD document declarations. Currently only parses the document
+ * type declaration markup.
+ * @param strBuff
+ * @return true if this is a valid DTD markup declaration.
+ * @throws IOException
+ */
+ protected boolean parseMarkupDeclarations(StringBuffer strBuff)
+ throws IOException
+ {
+ return gnu.parseMarkupDeclarations(strBuff);
+ }
+
+ /**
+ * Get the attributes of the current tag.
+ * @return The attribute set, representing the attributes of the current tag.
+ */
+ protected SimpleAttributeSet getAttributes()
+ {
+ return gnu.getAttributes();
+ }
+
+ /**
+ * Get the number of the document line being parsed.
+ * @return The current line.
+ */
+ protected int getCurrentLine()
+ {
+ return gnu.hTag.where.beginLine;
+ }
+
+ /**
+ * Get the current position in the document being parsed.
+ * @return The current position.
+ */
+ protected int getCurrentPos()
+ {
+ return gnu.hTag.where.startPosition;
+ }
+
+ /**
+ * The method is called when the HTML end (closing) tag is found or if
+ * the parser concludes that the one should be present in the
+ * current position. The method is called immediatly
+ * before calling the handleEndTag().
+ * @param omitted True if the tag is no actually present in the document,
+ * but is supposed by the parser (like &lt;/html&gt; at the end of the
+ * document).
+ */
+ protected void endTag(boolean omitted)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * Invokes the error handler. The default method in this implementation
+ * finally delegates the call to handleError, also providing the number of the
+ * current line.
+ */
+ protected void error(String msg)
+ {
+ gnu.error(msg);
+ }
+
+ /**
+ * Invokes the error handler. The default method in this implementation
+ * finally delegates the call to error (msg+": '"+invalid+"'").
+ */
+ protected void error(String msg, String invalid)
+ {
+ gnu.error(msg, invalid);
+ }
+
+ /**
+ * Invokes the error handler. The default method in this implementation
+ * finally delegates the call to error (parm1+" "+ parm2+" "+ parm3).
+ */
+ protected void error(String parm1, String parm2, String parm3)
+ {
+ gnu.error(parm1, parm2, parm3);
+ }
+
+ /**
+ * Invokes the error handler. The default method in this implementation
+ * finally delegates the call to error
+ * (parm1+" "+ parm2+" "+ parm3+" "+ parm4).
+ */
+ protected void error(String parm1, String parm2, String parm3, String parm4)
+ {
+ gnu.error(parm1, parm2, parm3, parm4);
+ }
+
+ /**
+ * In this implementation, this is never called and returns without action.
+ */
+ protected void flushAttributes()
+ {
+ gnu.flushAttributes();
+ }
+
+ /**
+ * Handle HTML comment. The default method returns without action.
+ * @param comment The comment being handled
+ */
+ protected void handleComment(char[] comment)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * This is additionally called in when the HTML content terminates
+ * without closing the HTML comment. This can only happen if the
+ * HTML document contains errors (for example, the closing --;gt is
+ * missing. The default method calls the error handler.
+ */
+ protected void handleEOFInComment()
+ {
+ gnu.error("Unclosed comment");
+ }
+
+ /**
+ * Handle the tag with no content, like &lt;br&gt;. The method is
+ * called for the elements that, in accordance with the current DTD,
+ * has an empty content.
+ * @param tag The tag being handled.
+ * @throws javax.swing.text.ChangedCharSetException
+ */
+ protected void handleEmptyTag(TagElement tag)
+ throws ChangedCharSetException
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * The method is called when the HTML closing tag ((like &lt;/table&gt;)
+ * is found or if the parser concludes that the one should be present
+ * in the current position.
+ * @param tag The tag being handled
+ */
+ protected void handleEndTag(TagElement tag)
+ {
+ // This default implementation does nothing.
+ }
+
+ /* Handle error that has occured in the given line. */
+ protected void handleError(int line, String message)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * The method is called when the HTML opening tag ((like &lt;table&gt;)
+ * is found or if the parser concludes that the one should be present
+ * in the current position.
+ * @param tag The tag being handled
+ */
+ protected void handleStartTag(TagElement tag)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * Handle the text section.
+ * <p> For non-preformatted section, the parser replaces
+ * \t, \r and \n by spaces and then multiple spaces
+ * by a single space. Additionaly, all whitespace around
+ * tags is discarded.
+ * </p>
+ * <p> For pre-formatted text (inside TEXAREA and PRE), the parser preserves
+ * all tabs and spaces, but removes <b>one</b> bounding \r, \n or \r\n,
+ * if it is present. Additionally, it replaces each occurence of \r or \r\n
+ * by a single \n.</p>
+ *
+ * @param text A section text.
+ */
+ protected void handleText(char[] text)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * Handle HTML &lt;title&gt; tag. This method is invoked when
+ * both title starting and closing tags are already behind.
+ * The passed argument contains the concatenation of all
+ * title text sections.
+ * @param title The title text.
+ */
+ protected void handleTitle(char[] title)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * Constructs the tag from the given element. In this implementation,
+ * this is defined, but never called.
+ * @param element the base element of the tag.
+ * @return the tag
+ */
+ protected TagElement makeTag(Element element)
+ {
+ return makeTag(element, false);
+ }
+
+ /**
+ * Constructs the tag from the given element.
+ * @param element the tag base {@link javax.swing.text.html.parser.Element}
+ * @param isSupposed true if the tag is not actually present in the
+ * html input, but the parser supposes that it should to occur in
+ * the current location.
+ * @return the tag
+ */
+ protected TagElement makeTag(Element element, boolean isSupposed)
+ {
+ return new TagElement(element, isSupposed);
+ }
+
+ /**
+ * This is called when the tag, representing the given element,
+ * occurs first time in the document.
+ * @param element
+ */
+ protected void markFirstTime(Element element)
+ {
+ // This default implementation does nothing.
+ }
+
+ /**
+ * The method is called when the HTML opening tag ((like &lt;table&gt;)
+ * is found or if the parser concludes that the one should be present
+ * in the current position. The method is called immediately before
+ * calling the handleStartTag.
+ * @param tag The tag
+ */
+ protected void startTag(TagElement tag)
+ throws ChangedCharSetException
+ {
+ // This default implementation does nothing.
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java
new file mode 100644
index 000000000..cdd339b8f
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java
@@ -0,0 +1,207 @@
+/* ParserDelegator.java -- Delegator for ParserDocument.
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import gnu.javax.swing.text.html.parser.HTML_401F;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Serializable;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.HTMLEditorKit.ParserCallback;
+
+/**
+ * This class instantiates and starts the working instance of
+ * html parser, being responsible for providing the default DTD.
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+public class ParserDelegator
+ extends javax.swing.text.html.HTMLEditorKit.Parser
+ implements Serializable
+{
+ private class gnuParser
+ extends gnu.javax.swing.text.html.parser.support.Parser
+ {
+ private static final long serialVersionUID = 1;
+
+ private gnuParser(DTD d)
+ {
+ super(d);
+ }
+
+ protected final void handleComment(char[] comment)
+ {
+ callBack.handleComment(comment, hTag.where.startPosition);
+ }
+
+ protected final void handleEmptyTag(TagElement tag)
+ throws javax.swing.text.ChangedCharSetException
+ {
+ callBack.handleSimpleTag(tag.getHTMLTag(), getAttributes(),
+ hTag.where.startPosition
+ );
+ }
+
+ protected final void handleEndTag(TagElement tag)
+ {
+ callBack.handleEndTag(tag.getHTMLTag(), hTag.where.startPosition);
+ }
+
+ protected final void handleError(int line, String message)
+ {
+ callBack.handleError(message, hTag.where.startPosition);
+ }
+
+ protected final void handleStartTag(TagElement tag)
+ {
+ SimpleAttributeSet attributes = gnu.getAttributes();
+
+ if (tag.fictional())
+ attributes.addAttribute(ParserCallback.IMPLIED, Boolean.TRUE);
+
+ callBack.handleStartTag(tag.getHTMLTag(), attributes,
+ hTag.where.startPosition
+ );
+ }
+
+ protected final void handleText(char[] text)
+ {
+ callBack.handleText(text, hTag.where.startPosition);
+ }
+
+ DTD getDTD()
+ {
+ // Accessing the inherited gnu.javax.swing.text.html.parser.support.Parser
+ // field. super. is a workaround, required to support JDK1.3's javac.
+ return super.dtd;
+ }
+ }
+
+ /**
+ * Use serialVersionUID for interoperability.
+ */
+ private static final long serialVersionUID = -1276686502624777206L;
+
+ private static DTD dtd = HTML_401F.getInstance();
+
+ /**
+ * The callback.
+ * This is package-private to avoid an accessor method.
+ */
+ HTMLEditorKit.ParserCallback callBack;
+
+ /**
+ * The reference to the working class of HTML parser that is
+ * actually used to parse the document.
+ * This is package-private to avoid an accessor method.
+ */
+ gnuParser gnu;
+
+ /**
+ * Parses the HTML document, calling methods of the provided
+ * callback. This method must be multithread - safe.
+ * @param reader The reader to read the HTML document from
+ * @param a_callback The callback that is notifyed about the presence
+ * of HTML elements in the document.
+ * @param ignoreCharSet If thrue, any charset changes during parsing
+ * are ignored.
+ * @throws java.io.IOException
+ */
+ public void parse(Reader reader, HTMLEditorKit.ParserCallback a_callback,
+ boolean ignoreCharSet
+ )
+ throws IOException
+ {
+ callBack = a_callback;
+
+ if (gnu == null || !dtd.equals(gnu.getDTD()))
+ {
+ gnu = new gnuParser(dtd);
+ }
+
+ gnu.parse(reader);
+
+ callBack.handleEndOfLineString(gnu.getEndOfLineSequence());
+ try
+ {
+ callBack.flush();
+ }
+ catch (BadLocationException ex)
+ {
+ // Convert this into the supported type of exception.
+ throw new IOException(ex.getMessage());
+ }
+ }
+
+ /**
+ * Calling this method instructs that, if not specified directly,
+ * the documents will be parsed using the default
+ * DTD of the implementation.
+ */
+ protected static void setDefaultDTD()
+ {
+ dtd = HTML_401F.getInstance();
+ }
+
+ /**
+ * Registers the user - written DTD under the given name, also
+ * making it default for the subsequent parsings. This has effect on
+ * all subsequent calls to the parse(...) . If you need to specify
+ * your DTD locally, simply {@link javax.swing.text.html.parser.Parser}
+ * instead.
+ * @param a_dtd The DTD that will be used to parse documents by this class.
+ * @param name The name of this DTD.
+ * @return No standard is specified on which instance of DTD must be
+ * returned by this method, and it is recommended to leave the returned
+ * value without consideration. This implementation returns the DTD
+ * that was previously set as the default DTD, or the implementations
+ * default DTD if none was set.
+ */
+ protected static DTD createDTD(DTD a_dtd, String name)
+ {
+ DTD.putDTDHash(name, a_dtd);
+
+ DTD dtd_prev = dtd;
+ dtd = a_dtd;
+ return dtd_prev;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/TagElement.java b/libjava/classpath/javax/swing/text/html/parser/TagElement.java
new file mode 100644
index 000000000..4558b15eb
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/TagElement.java
@@ -0,0 +1,142 @@
+/* TagElement.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.html.parser;
+
+import javax.swing.text.html.HTML;
+
+/**
+ * The SGML element, defining a single html tag.
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public class TagElement
+{
+ /**
+ * The Element the tag was constructed from.
+ */
+ private final Element element;
+
+ /**
+ * The coresponding HTML tag, assigned once in constructor.
+ */
+ private final HTML.Tag tag;
+
+ /**
+ * The 'fictional' flag.
+ */
+ private final boolean fictional;
+
+ /**
+ * Creates the html tag element from the defintion, stored in the
+ * given element. Sets the flag 'fictional' to false.
+ * @param an_element
+ */
+ public TagElement(Element an_element)
+ {
+ this(an_element, false);
+ }
+
+ /**
+ * Creates the html tag element from the defintion, stored in the
+ * given element, setting the flag 'fictional' to the given value.
+ */
+ public TagElement(Element an_element, boolean is_fictional)
+ {
+ element = an_element;
+ fictional = is_fictional;
+
+ HTML.Tag t = HTML.getTag(element.getName());
+
+ if (t != null)
+ tag = t;
+ else
+ tag = new HTML.UnknownTag(element.getName());
+ }
+
+ /**
+ * Get the element from that the tag was constructed.
+ */
+ public Element getElement()
+ {
+ return element;
+ }
+
+ /**
+ * Get the corresponding HTML tag. This is either one of the
+ * pre-defined HTML tags or the instance of the UnknownTag with the
+ * element name.
+ */
+ public HTML.Tag getHTMLTag()
+ {
+ return tag;
+ }
+
+ /**
+ * Calls isPreformatted() for the corresponding html tag and returns
+ * the obtained value.
+ */
+ public boolean isPreformatted()
+ {
+ return tag.isPreformatted();
+ }
+
+ /**
+ * Calls breaksFlow() for the corresponding html tag and returns
+ * the obtained value.
+ */
+ public boolean breaksFlow()
+ {
+ return tag.breaksFlow();
+ }
+
+ /**
+ * Get the value of the flag 'fictional'.
+ */
+ public boolean fictional()
+ {
+ return fictional;
+ }
+
+ /**
+ * Returns string representation of this object.
+ */
+ public String toString()
+ {
+ return getElement() + (fictional ? "?" : "");
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/parser/package.html b/libjava/classpath/javax/swing/text/html/parser/package.html
new file mode 100644
index 000000000..5d5157fb2
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/parser/package.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.text.html package.
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.text.html.parser</title></head>
+
+<body>
+<p> Provides the DTD driven for web browsers,
+ web robots, web page content analysers, web editors and
+ other applications applications working with Hypertext
+ Markup Language (HTML).
+</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/text/package.html b/libjava/classpath/javax/swing/text/package.html
new file mode 100644
index 000000000..5db555d88
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.text package.
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.text</title></head>
+
+<body>
+<p>Provides core text classes and interfaces representing models and views
+used by the text components for display and editing of text.</p>
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/text/rtf/ControlWordToken.java b/libjava/classpath/javax/swing/text/rtf/ControlWordToken.java
new file mode 100644
index 000000000..7008f0fd4
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/rtf/ControlWordToken.java
@@ -0,0 +1,86 @@
+/* ControlWordToken.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.rtf;
+
+/**
+ * A special {@link Token} that represents a control word in RTF like
+ * '\deff0' where 'deff' is the name of the control word and '0' is an
+ * optional parameter.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+class ControlWordToken extends Token
+{
+
+ /**
+ * The name of the control word.
+ */
+ public String name;
+
+ /**
+ * The optional parameter of the control word. Absence of a parameter is
+ * expressed through Integer.MIN_VALUE.
+ */
+ public int param;
+
+ /**
+ * Constructs a new ControlWordToken with the specified name and without
+ * a parameter.
+ *
+ * @param name the name of the control word
+ */
+ public ControlWordToken(String name)
+ {
+ this(name, Integer.MIN_VALUE);
+ }
+
+
+ /**
+ * Constructs a new ControlWordToken with the specified name and parameter.
+ *
+ * @param name the name of the control word
+ */
+ public ControlWordToken(String name, int param)
+ {
+ super(Token.CONTROL_WORD);
+ this.name = name;
+ this.param = param;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/rtf/RTFEditorKit.java b/libjava/classpath/javax/swing/text/rtf/RTFEditorKit.java
new file mode 100644
index 000000000..b2ebe3dd1
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/rtf/RTFEditorKit.java
@@ -0,0 +1,114 @@
+/* RTFEditorKit.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.rtf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.StyledEditorKit;
+
+/**
+ * Provides support for RTF data for use in
+ * {@link javax.swing.JEditorPane}s.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+public class RTFEditorKit
+ extends StyledEditorKit
+{
+
+ /**
+ * Constructs a new RTFEditorKit.
+ */
+ public RTFEditorKit()
+ {
+ super();
+ }
+
+ /**
+ * Returns the MIME content type. In the case of RTFEditorKit this is
+ * &apos;text/rtf&apos;
+ *
+ * @return the MIME content type for RTFEditorKit
+ */
+ public String getContentType()
+ {
+ return "text/rtf";
+ }
+
+ /**
+ * Reads RTF data from <code>stream</code> into <code>doc</code> at the
+ * specified position <code>pos</code>.
+ *
+ * @param stream the {@link InputStream} from where we read RTF data
+ * @param doc the {@link Document} into which we read the RTF data
+ * @param pos the position where to start
+ *
+ * @throws IOException if an IO error occurs
+ * @throws BadLocationException if the position is not valid
+ */
+ public void read(InputStream stream, Document doc, int pos)
+ throws IOException, BadLocationException
+ {
+ RTFParser parser = new RTFParser(stream, doc, pos);
+ parser.parse();
+ }
+
+
+ /**
+ * Reads RTF data from <code>reader</code> into <code>doc</code> at the
+ * specified position <code>pos</code>.
+ *
+ * @param reader the {@link Reader} from where we read RTF data
+ * @param doc the {@link Document} into which we read the RTF data
+ * @param pos the position where to start
+ *
+ * @throws IOException if an IO error occurs
+ * @throws BadLocationException if the position is not valid
+ */
+ public void read(Reader reader, Document doc, int pos)
+ throws IOException, BadLocationException
+ {
+ RTFParser parser = new RTFParser(reader, doc, pos);
+ parser.parse();
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/rtf/RTFParseException.java b/libjava/classpath/javax/swing/text/rtf/RTFParseException.java
new file mode 100644
index 000000000..2a9c64f05
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/rtf/RTFParseException.java
@@ -0,0 +1,65 @@
+/* RTFParseException.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.rtf;
+
+/**
+ * Indicates a parsing error during RTF processing.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+class RTFParseException
+ extends RuntimeException
+{
+ /**
+ * Constructs a new RTFParseException without message.
+ */
+ public RTFParseException()
+ {
+ super();
+ }
+
+ /**
+ * Constructs a new RTFParseException with the specified message.
+ */
+ public RTFParseException(String message)
+ {
+ super(message);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/rtf/RTFParser.java b/libjava/classpath/javax/swing/text/rtf/RTFParser.java
new file mode 100644
index 000000000..3306056ff
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/rtf/RTFParser.java
@@ -0,0 +1,203 @@
+/* RTFParser.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.rtf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+/**
+ * Parses an RTF file into a {@link Document}. The parser utilizes
+ * {@link RTFScanner}.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+class RTFParser
+{
+
+ /**
+ * Our scanner.
+ */
+ private RTFScanner scanner;
+
+ /**
+ * The document into which we parse.
+ */
+ private Document doc;
+
+ /**
+ * The current position.
+ */
+ private int pos;
+
+ /**
+ * Constructs a new RTFParser for the specified document and position,
+ * without initializing the scanner. This is only used internally.
+ *
+ * @param doc the {@link Document} into which we should parse
+ * @param pos the position to start
+ */
+ private RTFParser(Document doc, int pos)
+ {
+ this.doc = doc;
+ this.pos = pos;
+ }
+
+ /**
+ * Constructs a new RTFParser for the specified <code>stream</code>.
+ *
+ * @param stream the stream from which we parse
+ * @param doc the {@link Document} into which we should parse
+ * @param pos the position to start
+ */
+ public RTFParser(InputStream stream, Document doc, int pos)
+ {
+ this(doc, pos);
+ scanner = new RTFScanner(stream);
+ }
+
+ /**
+ * Constructs a new RTFParser for the specified <code>reader</code>.
+ *
+ * @param reader the reader from which we parse
+ * @param doc the {@link Document} into which we should parse
+ * @param pos the position to start
+ */
+ public RTFParser(Reader reader, Document doc, int pos)
+ {
+ this(doc, pos);
+ scanner = new RTFScanner(reader);
+ }
+
+ /**
+ * Returns the {@link Document} in which we parsed the RTF data.
+ *
+ * @return the {@link Document} in which we parsed the RTF data
+ */
+ public Document getDocument()
+ {
+ return doc;
+ }
+
+ /**
+ * Starts the parsing process.
+ */
+ public void parse()
+ throws IOException, BadLocationException
+ {
+ parseFile();
+ }
+
+ /**
+ * The parse rules for &lt;file&gt;.
+ */
+ private void parseFile()
+ throws IOException, BadLocationException
+ {
+ Token t1 = scanner.readToken();
+ if (t1.type != Token.LCURLY)
+ throw new RTFParseException("expected left curly braces");
+
+ parseHeader();
+ parseDocument();
+
+ Token t2 = scanner.peekToken();
+ if (t2.type == Token.RCURLY)
+ {
+ // Eat the token.
+ scanner.readToken();
+ }
+ else
+ {
+ // Ignore this for maximum robustness when file is broken.
+ System.err.println("RTF warning: expected right curly braces");
+ }
+
+ }
+
+ /**
+ * The parse rules for &lt;header&gt;.
+ *
+ * TODO: implement this properly
+ */
+ private void parseHeader()
+ //throws IOException, BadLocationException
+ {
+ // TODO add parse rules here
+ }
+
+
+ /**
+ * The parse rules for &lt;document&gt;.
+ *
+ * TODO: implement this properly
+ */
+ private void parseDocument()
+ throws IOException, BadLocationException
+ {
+ // !!! TODO !!!
+ // This simply emits every TEXT Token as text to the document
+ // which is plain stupid
+
+ boolean eof = false;
+
+ do {
+ Token token = scanner.readToken();
+ switch (token.type)
+ {
+ case Token.TEXT:
+ TextToken textToken = (TextToken) token;
+ doc.insertString(pos, textToken.text, null);
+ pos += textToken.text.length();
+ break;
+ case Token.EOF:
+ eof = true;
+ break;
+ default:
+ // FIXME
+ break;
+ }
+ } while (!eof);
+
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/rtf/RTFScanner.java b/libjava/classpath/javax/swing/text/rtf/RTFScanner.java
new file mode 100644
index 000000000..2eca159c4
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/rtf/RTFScanner.java
@@ -0,0 +1,294 @@
+/* RTFScanner.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.rtf;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+/**
+ * Provides a scanner that scans an {@link InputStream} for tokens of the
+ * RTF syntax.
+ *
+ * This scanner is based upon the RTF specification 1.6
+ * available at:
+ *
+ * <a
+ * href="http://msdn.microsoft.com/library/en-us/dnrtfspec/html/rtfspec.asp">
+ * RTF specification at MSDN</a>
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+class RTFScanner
+{
+
+ /**
+ * The reader from which we read the RTF data.
+ */
+ private Reader in;
+
+ /**
+ * This is used to constuct strings from the read in chars.
+ */
+ private StringBuffer buffer;
+
+ /**
+ * Lookahead token.
+ */
+ private Token lastToken;
+
+ /**
+ * Constructs a new RTFScanner without initializing the {@link Reader}.
+ */
+ private RTFScanner()
+ {
+ buffer = new StringBuffer();
+ }
+
+ /**
+ * Constructs a new RTFScanner for the given {@link InputStream}.
+ * The stream is wrapped into an {@link InputStreamReader} and if it's
+ * not yet buffered then the Reader is wrapped in a {@link BufferedReader}
+ *
+ * @param stream the {@link InputStream} to read RTF data from
+ */
+ public RTFScanner(InputStream stream)
+ {
+ this();
+ InputStreamReader reader = new InputStreamReader(stream);
+ in = new BufferedReader(reader);
+ }
+
+ /**
+ * Constructs a new RTFScanner for the given {@link Reader}.
+ *
+ * If the reader is not an instance of {@link BufferedReader} then it
+ * is wrapped into a BufferedReader.
+ *
+ * @param reader the {@link BufferedReader} to read RTF data from
+ */
+ public RTFScanner(Reader reader)
+ {
+ this();
+ if (reader instanceof BufferedReader)
+ {
+ in = reader;
+ }
+ else
+ {
+ in = new BufferedReader(reader);
+ }
+ }
+
+ /**
+ * Reads in the next {@link Token} from the stream.
+ *
+ * @return the read {@link Token}
+ *
+ * @throws IOException if the underlying stream has problems
+ */
+ private Token readTokenImpl()
+ throws IOException
+ {
+ Token token = null;
+
+ int c = in.read();
+ switch(c)
+ {
+ case -1:
+ token = new Token(Token.EOF);
+ break;
+
+ case '{':
+ token = new Token(Token.LCURLY);
+ break;
+
+ case '}':
+ token = new Token(Token.RCURLY);
+ break;
+
+ case '\\':
+ buffer.delete(0, buffer.length());
+ buffer.append((char) c);
+ token = readControlWord();
+ break;
+
+ default:
+ buffer.delete(0, buffer.length());
+ buffer.append((char) c);
+ token = readText();
+ break;
+ }
+
+ return token;
+ }
+
+ Token peekToken()
+ throws IOException
+ {
+ lastToken = readTokenImpl();
+ return lastToken;
+ }
+
+ Token readToken()
+ throws IOException
+ {
+ Token token;
+ if (lastToken != null)
+ {
+ token = lastToken;
+ lastToken = null;
+ }
+ else
+ token = readTokenImpl();
+ return token;
+ }
+
+ /**
+ * Reads in a control word and optional parameter.
+ *
+ * @return the read in control word as {@link ControlWordToken}
+ *
+ * @throws IOException if the underlying stream has problems
+ */
+ private Token readControlWord()
+ throws IOException
+ {
+ // this flag indicates if we are still reading the name or are already
+ // in the parameter
+ boolean readingName = true;
+ String name = null;
+ String param = null;
+
+ while (true)
+ {
+ in.mark(1);
+ int c = in.read();
+
+ // check for 'a'..'z'
+ if (readingName && (c >= 'a') && (c <= 'z'))
+ {
+ buffer.append((char) c);
+ }
+ else if ((c >= '0') && (c <= '9'))
+ {
+ // if the last char was in the name, then finish reading the name
+ if (readingName)
+ {
+ name = buffer.toString();
+ buffer.delete(0, buffer.length());
+ readingName = false;
+ }
+ buffer.append((char) c);
+ }
+ else
+ {
+ // if we were in the name, then finish this
+ if (readingName)
+ {
+ name = buffer.toString();
+ }
+ // otherwise finish the parameter
+ else
+ {
+ param = buffer.toString();
+ }
+
+ // clear up
+ buffer.delete(0, buffer.length());
+ // reset input buffer to last char
+ in.reset();
+ // break while loop
+ break;
+ }
+ }
+
+ ControlWordToken token = null;
+
+ if (param == null)
+ token = new ControlWordToken(name);
+ else
+ token =new ControlWordToken(name, Integer.parseInt(param));
+
+ return token;
+
+ }
+
+ /**
+ * Reads in a block of text.
+ *
+ * @return the token for the text
+ */
+ private Token readText()
+ throws IOException
+ {
+
+ boolean readingText = true;
+ while (readingText)
+ {
+ in.mark(1);
+ int c = in.read();
+ switch(c)
+ {
+ case '\\':
+ case '{':
+ case '}':
+ case -1:
+ readingText = false;
+ in.reset();
+ break;
+
+ default:
+ buffer.append((char) c);
+ break;
+ }
+
+ }
+
+ String text = buffer.toString();
+ Token token = new TextToken(text);
+
+ buffer.delete(0, buffer.length());
+
+ return token;
+
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/rtf/TextToken.java b/libjava/classpath/javax/swing/text/rtf/TextToken.java
new file mode 100644
index 000000000..2d6d527d1
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/rtf/TextToken.java
@@ -0,0 +1,65 @@
+/* TextToken.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.rtf;
+
+/**
+ * A special {@link Token} that represents a piece of text in RTF.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+class TextToken extends Token
+{
+
+ /**
+ * The text.
+ */
+ public String text;
+
+ /**
+ * Constructs a new TextToken with the specified textual data.
+ *
+ * @param text the text for this token
+ */
+ public TextToken(String text)
+ {
+ super(Token.TEXT);
+ this.text = text;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/text/rtf/Token.java b/libjava/classpath/javax/swing/text/rtf/Token.java
new file mode 100644
index 000000000..7d5adaaf0
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/rtf/Token.java
@@ -0,0 +1,91 @@
+/* Token.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.rtf;
+
+/**
+ * Represents a simple token that the RTFScanner can read. A simple
+ * only has a type (like LCURLY or RCURLY). More complex tokens may
+ * attach data to the token.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+class Token
+{
+
+ /**
+ * This special type inidicates the end of the input stream.
+ */
+ public static final int EOF = -1;
+
+ /**
+ * A left curly brace '{'.
+ */
+ public static final int LCURLY = 1;
+
+ /**
+ * A right curly brace '}'.
+ */
+ public static final int RCURLY = 2;
+
+ /**
+ * A control word like '\rtf1'. Tokens with this type are represented
+ * through the subclass {@link ControlWordToken}.
+ */
+ public static final int CONTROL_WORD = 3;
+
+ /**
+ * A token that contains text. This is represented through the subclass
+ * {@link TextToken}.
+ */
+ public static final int TEXT = 4;
+
+
+ /** The token type. */
+ public int type;
+
+ /**
+ * Constructs a new Token with the specified type.
+ *
+ * @param type the Token type
+ */
+ public Token(int type)
+ {
+ this.type = type;
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/rtf/package.html b/libjava/classpath/javax/swing/text/rtf/package.html
new file mode 100644
index 000000000..c695aef6c
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/rtf/package.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.text.rtf package.
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.text.rtf</title></head>
+
+<body>
+<p>Provides support for Rich Text Format (RTF) data to be used with the
+{@link JEditorPane} component.
+</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java b/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java
new file mode 100644
index 000000000..29ce165a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/AbstractLayoutCache.java
@@ -0,0 +1,456 @@
+/* AbstractLayoutCache.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import java.awt.Rectangle;
+import java.util.Enumeration;
+
+import javax.swing.event.TreeModelEvent;
+
+/**
+ * class AbstractLayoutCache
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class AbstractLayoutCache
+ implements RowMapper
+{
+ /**
+ * class NodeDimensions
+ */
+ public abstract static class NodeDimensions
+ {
+ /**
+ * Creates <code>NodeDimensions</code> object.
+ */
+ public NodeDimensions()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Get the node dimensions. The NodeDimensions property must be set (unless
+ * the method is overridden, like if {@link FixedHeightLayoutCache}. If the
+ * method is not overridden and the property is not set, the InternalError is
+ * thrown.
+ *
+ * @param value the last node in the path
+ * @param row the node row
+ * @param depth the indentation depth
+ * @param expanded true if this node is expanded, false otherwise
+ * @param bounds the area where the tree is displayed
+ */
+ public abstract Rectangle getNodeDimensions(Object value, int row,
+ int depth, boolean expanded,
+ Rectangle bounds);
+ }
+
+ /**
+ * nodeDimensions
+ */
+ protected NodeDimensions nodeDimensions;
+
+ /**
+ * treeModel
+ */
+ protected TreeModel treeModel;
+
+ /**
+ * treeSelectionModel
+ */
+ protected TreeSelectionModel treeSelectionModel;
+
+ /**
+ * rootVisible
+ */
+ protected boolean rootVisible;
+
+ /**
+ * rowHeight
+ */
+ protected int rowHeight;
+
+ /**
+ * Constructor AbstractLayoutCache
+ */
+ public AbstractLayoutCache()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * setNodeDimensions
+ *
+ * @param dimensions TODO
+ */
+ public void setNodeDimensions(NodeDimensions dimensions)
+ {
+ nodeDimensions = dimensions;
+ }
+
+ /**
+ * getNodeDimensions
+ *
+ * @return NodeDimensions
+ */
+ public NodeDimensions getNodeDimensions()
+ {
+ return nodeDimensions;
+ }
+
+ /**
+ * Get the node dimensions. The NodeDimensions property must be set
+ * (unless the method is overridden, like if
+ * {@link FixedHeightLayoutCache}. If the method is not overridden and
+ * the property is not set, the InternalError is thrown.
+ *
+ * @param value the last node in the path
+ * @param row the node row
+ * @param depth the indentation depth
+ * @param expanded true if this node is expanded, false otherwise
+ * @param bounds the area where the tree is displayed
+ */
+ protected Rectangle getNodeDimensions(Object value, int row, int depth,
+ boolean expanded, Rectangle bounds)
+ {
+ Rectangle d = null;
+ if (nodeDimensions != null)
+ d = nodeDimensions.getNodeDimensions(value, row, depth, expanded,
+ bounds);
+ return d;
+ }
+
+ /**
+ * Sets the model that provides the tree data.
+ *
+ * @param model the model
+ */
+ public void setModel(TreeModel model)
+ {
+ treeModel = model;
+ }
+
+ /**
+ * Returns the model that provides the tree data.
+ *
+ * @return the model
+ */
+ public TreeModel getModel()
+ {
+ return treeModel;
+ }
+
+ /**
+ * setRootVisible
+ *
+ * @param visible <code>true</code> if root should be visible,
+ * <code>false</code> otherwise
+ */
+ public void setRootVisible(boolean visible)
+ {
+ rootVisible = visible;
+ }
+
+ /**
+ * isRootVisible
+ *
+ * @return <code>true</code> if root is visible,
+ * <code>false</code> otherwise
+ */
+ public boolean isRootVisible()
+ {
+ return rootVisible;
+ }
+
+ /**
+ * setRowHeight
+ *
+ * @param height the row height
+ */
+ public void setRowHeight(int height)
+ {
+ rowHeight = height;
+ invalidateSizes();
+ }
+
+ /**
+ * getRowHeight
+ *
+ * @return the row height
+ */
+ public int getRowHeight()
+ {
+ return rowHeight;
+ }
+
+ /**
+ * setSelectionModel
+ *
+ * @param model the model
+ */
+ public void setSelectionModel(TreeSelectionModel model)
+ {
+ if (treeSelectionModel != null)
+ treeSelectionModel.setRowMapper(null);
+ treeSelectionModel = model;
+ if (treeSelectionModel != null)
+ treeSelectionModel.setRowMapper(this);
+
+ }
+
+ /**
+ * getSelectionModel
+ *
+ * @return the model
+ */
+ public TreeSelectionModel getSelectionModel()
+ {
+ return treeSelectionModel;
+ }
+
+ /**
+ * Get the sum of heights for all rows. This class provides a general not
+ * optimized implementation that is overridded in derived classes
+ * ({@link VariableHeightLayoutCache}, {@link FixedHeightLayoutCache}) for
+ * the better performance.
+ */
+ public int getPreferredHeight()
+ {
+ int height = 0;
+ int n = getRowCount();
+ Rectangle r = new Rectangle();
+ for (int i = 0; i < n; i++)
+ {
+ TreePath path = getPathForRow(i);
+ height += getBounds(path, r).height;
+ }
+ return height;
+ }
+
+ /**
+ * Get the maximal width. This class provides a general not
+ * optimized implementation that is overridded in derived classes
+ * ({@link VariableHeightLayoutCache}, {@link FixedHeightLayoutCache}) for
+ * the better performance.
+ *
+ * @param rect the rectangle that is used during the method work
+ */
+ public int getPreferredWidth(Rectangle rect)
+ {
+ int maximalWidth = 0;
+ Rectangle r = new Rectangle();
+ int n = getRowCount();
+ for (int i = 0; i < n; i++)
+ {
+ TreePath path = getPathForRow(i);
+ r.setBounds(0, 0, 0, 0);
+ r = getBounds(path, r);
+ if (r.x + r.width > maximalWidth)
+ maximalWidth = r.x + r.width;
+ // Invalidate the cached value as this may be the very early call
+ // before the heigth is properly set (the vertical coordinate may
+ // not be correct).
+ invalidatePathBounds(path);
+ }
+ return maximalWidth;
+ }
+ /**
+ * isExpanded
+ *
+ * @param value0 TODO
+ *
+ * @return boolean
+ */
+ public abstract boolean isExpanded(TreePath value0);
+
+ /**
+ * getBounds
+ *
+ * @param value0 TODO
+ * @param value1 TODO
+ *
+ * @return Rectangle
+ */
+ public abstract Rectangle getBounds(TreePath value0, Rectangle value1);
+
+ /**
+ * getPathForRow
+ *
+ * @param row the row
+ *
+ * @return the tree path
+ */
+ public abstract TreePath getPathForRow(int row);
+
+ /**
+ * getRowForPath
+ *
+ * @param path the tree path
+ *
+ * @return the row
+ */
+ public abstract int getRowForPath(TreePath path);
+
+ /**
+ * getPathClosestTo
+ *
+ * @param value0 TODO
+ * @param value1 TODO
+ *
+ * @return the tree path
+ */
+ public abstract TreePath getPathClosestTo(int value0, int value1);
+
+ /**
+ * getVisiblePathsFrom
+ *
+ * @param path the tree path
+ *
+ * @return Enumeration
+ */
+ public abstract Enumeration<TreePath> getVisiblePathsFrom(TreePath path);
+
+ /**
+ * getVisibleChildCount
+ *
+ * @param path the tree path
+ *
+ * @return int
+ */
+ public abstract int getVisibleChildCount(TreePath path);
+
+ /**
+ * setExpandedState
+ *
+ * @param value0 TODO
+ *
+ * @param value1 TODO
+ */
+ public abstract void setExpandedState(TreePath value0, boolean value1);
+
+ /**
+ * getExpandedState
+ *
+ * @param path the tree path
+ *
+ * @return boolean
+ */
+ public abstract boolean getExpandedState(TreePath path);
+
+ /**
+ * getRowCount
+ *
+ * @return the number of rows
+ */
+ public abstract int getRowCount();
+
+ /**
+ * invalidateSizes
+ */
+ public abstract void invalidateSizes();
+
+ /**
+ * invalidatePathBounds
+ *
+ * @param path the tree path
+ */
+ public abstract void invalidatePathBounds(TreePath path);
+
+ /**
+ * treeNodesChanged
+ *
+ * @param event the event to send
+ */
+ public abstract void treeNodesChanged(TreeModelEvent event);
+
+ /**
+ * treeNodesInserted
+ *
+ * @param event the event to send
+ */
+ public abstract void treeNodesInserted(TreeModelEvent event);
+
+ /**
+ * treeNodesRemoved
+ *
+ * @param event the event to send
+ */
+ public abstract void treeNodesRemoved(TreeModelEvent event);
+
+ /**
+ * treeStructureChanged
+ *
+ * @param event the event to send
+ */
+ public abstract void treeStructureChanged(TreeModelEvent event);
+
+ /**
+ * Get the tree row numbers for the given pathes. This method performs
+ * the "bulk" conversion that may be faster than mapping pathes one by
+ * one. To have the benefit from the bulk conversion, the method must be
+ * overridden in the derived classes. The default method delegates work
+ * to the {@link #getRowForPath(TreePath)}.
+ *
+ * @param paths the tree paths the array of the tree pathes.
+ * @return the array of the matching tree rows.
+ */
+ public int[] getRowsForPaths(TreePath[] paths)
+ {
+ int[] rows = null;
+ if (paths != null)
+ {
+ rows = new int[paths.length];
+ for (int i = 0; i < rows.length; i++)
+ rows[i] = getRowForPath(paths[i]);
+ }
+ return rows;
+ }
+
+ /**
+ * Returns true if this layout supposes that all rows have the fixed
+ * height.
+ *
+ * @return boolean true if all rows in the tree must have the fixed
+ * height (false by default).
+ */
+ protected boolean isFixedRowHeight()
+ {
+ return rowHeight > 0;
+ }
+}
diff --git a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
new file mode 100644
index 000000000..260c385aa
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java
@@ -0,0 +1,1217 @@
+/* DefaultMutableTreeNode.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import gnu.java.util.EmptyEnumeration;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+import java.util.Vector;
+
+
+/**
+ * A default implementation of the {@link MutableTreeNode} interface.
+ *
+ * @author Andrew Selkirk
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+public class DefaultMutableTreeNode
+ implements Cloneable, MutableTreeNode, Serializable
+{
+ private static final long serialVersionUID = -4298474751201349152L;
+
+ /**
+ * An empty enumeration, returned by {@link #children()} if a node has no
+ * children.
+ */
+ public static final Enumeration<TreeNode> EMPTY_ENUMERATION =
+ new EmptyEnumeration<TreeNode>();
+
+ /**
+ * The parent of this node (possibly <code>null</code>).
+ */
+ protected MutableTreeNode parent;
+
+ /**
+ * The child nodes for this node (may be empty).
+ */
+ protected Vector<MutableTreeNode> children = new Vector<MutableTreeNode>();
+
+ /**
+ * userObject
+ */
+ protected transient Object userObject;
+
+ /**
+ * allowsChildren
+ */
+ protected boolean allowsChildren;
+
+ /**
+ * Creates a <code>DefaultMutableTreeNode</code> object.
+ * This is equivalent to <code>DefaultMutableTreeNode(null, true)</code>.
+ */
+ public DefaultMutableTreeNode()
+ {
+ this(null, true);
+ }
+
+ /**
+ * Creates a <code>DefaultMutableTreeNode</code> object with the given
+ * user object attached to it. This is equivalent to
+ * <code>DefaultMutableTreeNode(userObject, true)</code>.
+ *
+ * @param userObject the user object (<code>null</code> permitted).
+ */
+ public DefaultMutableTreeNode(Object userObject)
+ {
+ this(userObject, true);
+ }
+
+ /**
+ * Creates a <code>DefaultMutableTreeNode</code> object with the given
+ * user object attached to it.
+ *
+ * @param userObject the user object (<code>null</code> permitted).
+ * @param allowsChildren <code>true</code> if the code allows to add child
+ * nodes, <code>false</code> otherwise
+ */
+ public DefaultMutableTreeNode(Object userObject, boolean allowsChildren)
+ {
+ this.userObject = userObject;
+ this.allowsChildren = allowsChildren;
+ }
+
+ /**
+ * Returns a clone of the node. The clone contains a shallow copy of the
+ * user object, and does not copy the parent node or the child nodes.
+ *
+ * @return A clone of the node.
+ */
+ public Object clone()
+ {
+ return new DefaultMutableTreeNode(this.userObject, this.allowsChildren);
+ }
+
+ /**
+ * Returns a string representation of the node. This implementation returns
+ * <code>getUserObject().toString()</code>, or <code>null</code> if there
+ * is no user object.
+ *
+ * @return A string representation of the node (possibly <code>null</code>).
+ */
+ public String toString()
+ {
+ if (userObject == null)
+ return null;
+
+ return userObject.toString();
+ }
+
+ /**
+ * Adds a new child node to this node and sets this node as the parent of
+ * the child node. The child node must not be an ancestor of this node.
+ * If the tree uses the {@link DefaultTreeModel}, you must subsequently
+ * call {@link DefaultTreeModel#reload(TreeNode)}.
+ *
+ * @param child the child node (<code>null</code> not permitted).
+ *
+ * @throws IllegalStateException if {@link #getAllowsChildren()} returns
+ * <code>false</code>.
+ * @throws IllegalArgumentException if {@link #isNodeAncestor} returns
+ * <code>true</code>.
+ * @throws IllegalArgumentException if <code>child</code> is
+ * <code>null</code>.
+ */
+ public void add(MutableTreeNode child)
+ {
+ if (! allowsChildren)
+ throw new IllegalStateException();
+
+ if (child == null)
+ throw new IllegalArgumentException();
+
+ if (isNodeAncestor(child))
+ throw new IllegalArgumentException("Cannot add ancestor node.");
+
+ children.add(child);
+ child.setParent(this);
+ }
+
+ /**
+ * Returns the parent node of this node.
+ *
+ * @return The parent node (possibly <code>null</code>).
+ */
+ public TreeNode getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Removes the child with the given index from this node.
+ *
+ * @param index the index (in the range <code>0</code> to
+ * <code>getChildCount() - 1</code>).
+ *
+ * @throws ArrayIndexOutOfBoundsException if <code>index</code> is outside
+ * the valid range.
+ */
+ public void remove(int index)
+ {
+ MutableTreeNode child = children.remove(index);
+ child.setParent(null);
+ }
+
+ /**
+ * Removes the given child from this node and sets its parent to
+ * <code>null</code>.
+ *
+ * @param node the child node (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>node</code> is not a child of
+ * this node.
+ * @throws IllegalArgumentException if <code>node</code> is null.
+ */
+ public void remove(MutableTreeNode node)
+ {
+ if (node == null)
+ throw new IllegalArgumentException("Null 'node' argument.");
+ if (node.getParent() != this)
+ throw new IllegalArgumentException(
+ "The given 'node' is not a child of this node.");
+ children.remove(node);
+ node.setParent(null);
+ }
+
+ /**
+ * writeObject
+ *
+ * @param stream the output stream
+ *
+ * @exception IOException If an error occurs
+ */
+ private void writeObject(ObjectOutputStream stream)
+ throws IOException
+ {
+ // TODO: Implement me.
+ }
+
+ /**
+ * readObject
+ *
+ * @param stream the input stream
+ *
+ * @exception IOException If an error occurs
+ * @exception ClassNotFoundException TODO
+ */
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ // TODO: Implement me.
+ }
+
+ /**
+ * Inserts given child node at the given index.
+ *
+ * @param node the child node (<code>null</code> not permitted).
+ * @param index the index.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is
+ * </code>null</code>.
+ */
+ public void insert(MutableTreeNode node, int index)
+ {
+ if (! allowsChildren)
+ throw new IllegalStateException();
+
+ if (node == null)
+ throw new IllegalArgumentException("Null 'node' argument.");
+
+ if (isNodeAncestor(node))
+ throw new IllegalArgumentException("Cannot insert ancestor node.");
+
+ children.insertElementAt(node, index);
+ }
+
+ /**
+ * Returns a path to this node from the root.
+ *
+ * @return an array of tree nodes
+ */
+ public TreeNode[] getPath()
+ {
+ return getPathToRoot(this, 0);
+ }
+
+ /**
+ * Returns an enumeration containing all children of this node.
+ * <code>EMPTY_ENUMERATION</code> is returned if this node has no children.
+ *
+ * @return an enumeration of tree nodes
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration children()
+ {
+ if (children.size() == 0)
+ return EMPTY_ENUMERATION;
+
+ return children.elements();
+ }
+
+ /**
+ * Set the parent node for this node.
+ *
+ * @param node the parent node
+ */
+ public void setParent(MutableTreeNode node)
+ {
+ parent = node;
+ }
+
+ /**
+ * Returns the child node at a given index.
+ *
+ * @param index the index
+ *
+ * @return the child node
+ */
+ public TreeNode getChildAt(int index)
+ {
+ return children.elementAt(index);
+ }
+
+ /**
+ * Returns the number of children of this node.
+ *
+ * @return the number of children
+ */
+ public int getChildCount()
+ {
+ return children.size();
+ }
+
+ /**
+ * Returns the index of the specified child node, or -1 if the node is not
+ * in fact a child of this node.
+ *
+ * @param node the node (<code>null</code> not permitted).
+ *
+ * @return The index of the specified child node, or -1.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is <code>null</code>.
+ */
+ public int getIndex(TreeNode node)
+ {
+ if (node == null)
+ throw new IllegalArgumentException("Null 'node' argument.");
+ return children.indexOf(node);
+ }
+
+ /**
+ * Sets the flag that controls whether or not this node allows the addition /
+ * insertion of child nodes. If the flag is set to <code>false</code>, any
+ * existing children are removed.
+ *
+ * @param allowsChildren the flag.
+ */
+ public void setAllowsChildren(boolean allowsChildren)
+ {
+ if (!allowsChildren)
+ removeAllChildren();
+ this.allowsChildren = allowsChildren;
+ }
+
+ /**
+ * getAllowsChildren
+ *
+ * @return boolean
+ */
+ public boolean getAllowsChildren()
+ {
+ return allowsChildren;
+ }
+
+ /**
+ * Sets the user object for this node
+ *
+ * @param userObject the user object
+ */
+ public void setUserObject(Object userObject)
+ {
+ this.userObject = userObject;
+ }
+
+ /**
+ * Returns the user object attached to this node. <code>null</code> is
+ * returned when no user object is set.
+ *
+ * @return the user object
+ */
+ public Object getUserObject()
+ {
+ return userObject;
+ }
+
+ /**
+ * Removes this node from its parent.
+ */
+ public void removeFromParent()
+ {
+ parent.remove(this);
+ parent = null;
+ }
+
+ /**
+ * Removes all child nodes from this node.
+ */
+ public void removeAllChildren()
+ {
+ for (int i = getChildCount() - 1; i >= 0; i--)
+ remove(i);
+ }
+
+ /**
+ * Returns <code>true</code> if <code>node</code> is an ancestor of this
+ * tree node, and <code>false</code> otherwise. An ancestor node is any of:
+ * <ul>
+ * <li>this tree node;</li>
+ * <li>the parent node (if there is one);</li>
+ * <li>any ancestor of the parent node;</li>
+ * </ul>
+ * If <code>node</code> is <code>null</code>, this method returns
+ * <code>false</code>.
+ *
+ * @param node the node (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean isNodeAncestor(TreeNode node)
+ {
+ if (node == null)
+ return false;
+
+ TreeNode current = this;
+
+ while (current != null && current != node)
+ current = current.getParent();
+
+ return current == node;
+ }
+
+ /**
+ * Returns <code>true</code> if <code>node</code> is a descendant of this
+ * tree node, and <code>false</code> otherwise. A descendant node is any of:
+ * <ul>
+ * <li>this tree node;</li>
+ * <li>the child nodes belonging to this tree node, if there are any;</li>
+ * <li>any descendants of the child nodes;</li>
+ * </ul>
+ * If <code>node</code> is <code>null</code>, this method returns
+ * <code>false</code>.
+ *
+ * @param node the node (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean isNodeDescendant(DefaultMutableTreeNode node)
+ {
+ if (node == null)
+ return false;
+
+ TreeNode current = node;
+
+ while (current != null
+ && current != this)
+ current = current.getParent();
+
+ return current == this;
+ }
+
+ /**
+ * getSharedAncestor
+ *
+ * @param node TODO
+ *
+ * @return TreeNode
+ */
+ public TreeNode getSharedAncestor(DefaultMutableTreeNode node)
+ {
+ TreeNode current = this;
+ ArrayList<TreeNode> list = new ArrayList<TreeNode>();
+
+ while (current != null)
+ {
+ list.add(current);
+ current = current.getParent();
+ }
+
+ current = node;
+
+ while (current != null)
+ {
+ if (list.contains(current))
+ return current;
+
+ current = current.getParent();
+ }
+
+ return null;
+ }
+
+ /**
+ * isNodeRelated
+ *
+ * @param node TODO
+ *
+ * @return boolean
+ */
+ public boolean isNodeRelated(DefaultMutableTreeNode node)
+ {
+ if (node == null)
+ return false;
+
+ return node.getRoot() == getRoot();
+ }
+
+ /**
+ * getDepth
+ *
+ * @return int
+ */
+ public int getDepth()
+ {
+ if ((! allowsChildren)
+ || children.size() == 0)
+ return 0;
+
+ Stack<Integer> stack = new Stack<Integer>();
+ stack.push(new Integer(0));
+ TreeNode node = getChildAt(0);
+ int depth = 0;
+ int current = 1;
+
+ while (! stack.empty())
+ {
+ if (node.getChildCount() != 0)
+ {
+ node = node.getChildAt(0);
+ stack.push(new Integer(0));
+ current++;
+ }
+ else
+ {
+ if (current > depth)
+ depth = current;
+
+ int size;
+ int index;
+
+ do
+ {
+ node = node.getParent();
+ size = node.getChildCount();
+ index = stack.pop().intValue() + 1;
+ current--;
+ }
+ while (index >= size
+ && node != this);
+
+ if (index < size)
+ {
+ node = node.getChildAt(index);
+ stack.push(new Integer(index));
+ current++;
+ }
+ }
+ }
+
+ return depth;
+ }
+
+ /**
+ * getLevel
+ *
+ * @return int
+ */
+ public int getLevel()
+ {
+ int count = -1;
+ TreeNode current = this;
+
+ do
+ {
+ current = current.getParent();
+ count++;
+ }
+ while (current != null);
+
+ return count;
+ }
+
+ /**
+ * getPathToRoot
+ *
+ * @param node TODO
+ * @param depth TODO
+ *
+ * @return TreeNode[]
+ */
+ protected TreeNode[] getPathToRoot(TreeNode node, int depth)
+ {
+ if (node == null)
+ {
+ if (depth == 0)
+ return null;
+
+ return new TreeNode[depth];
+ }
+
+ TreeNode[] path = getPathToRoot(node.getParent(), depth + 1);
+ path[path.length - depth - 1] = node;
+ return path;
+ }
+
+ /**
+ * getUserObjectPath
+ *
+ * @return Object[]
+ */
+ public Object[] getUserObjectPath()
+ {
+ TreeNode[] path = getPathToRoot(this, 0);
+ Object[] object = new Object[path.length];
+
+ for (int index = 0; index < path.length; ++index)
+ object[index] = ((DefaultMutableTreeNode) path[index]).getUserObject();
+
+ return object;
+ }
+
+ /**
+ * Returns the root node by iterating the parents of this node.
+ *
+ * @return the root node
+ */
+ public TreeNode getRoot()
+ {
+ TreeNode current = this;
+ TreeNode check = current.getParent();
+
+ while (check != null)
+ {
+ current = check;
+ check = current.getParent();
+ }
+
+ return current;
+ }
+
+ /**
+ * Tells whether this node is the root node or not.
+ *
+ * @return <code>true</code> if this is the root node,
+ * <code>false</code>otherwise
+ */
+ public boolean isRoot()
+ {
+ return parent == null;
+ }
+
+ /**
+ * getNextNode
+ *
+ * @return DefaultMutableTreeNode
+ */
+ public DefaultMutableTreeNode getNextNode()
+ {
+ // Return first child.
+ if (getChildCount() != 0)
+ return (DefaultMutableTreeNode) getChildAt(0);
+
+ // Return next sibling (if needed the sibling of some parent).
+ DefaultMutableTreeNode node = this;
+ DefaultMutableTreeNode sibling;
+
+ do
+ {
+ sibling = node.getNextSibling();
+ node = (DefaultMutableTreeNode) node.getParent();
+ }
+ while (sibling == null &&
+ node != null);
+
+ // Return sibling.
+ return sibling;
+ }
+
+ /**
+ * getPreviousNode
+ *
+ * @return DefaultMutableTreeNode
+ */
+ public DefaultMutableTreeNode getPreviousNode()
+ {
+ // Return null if no parent.
+ if (parent == null)
+ return null;
+
+ DefaultMutableTreeNode sibling = getPreviousSibling();
+
+ // Return parent if no sibling.
+ if (sibling == null)
+ return (DefaultMutableTreeNode) parent;
+
+ // Return last leaf of sibling.
+ if (sibling.getChildCount() != 0)
+ return sibling.getLastLeaf();
+
+ // Return sibling.
+ return sibling;
+ }
+
+ /**
+ * preorderEnumeration
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration preorderEnumeration()
+ {
+ return new PreorderEnumeration(this);
+ }
+
+ /**
+ * postorderEnumeration
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration postorderEnumeration()
+ {
+ return new PostorderEnumeration(this);
+ }
+
+ /**
+ * breadthFirstEnumeration
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration breadthFirstEnumeration()
+ {
+ return new BreadthFirstEnumeration(this);
+ }
+
+ /**
+ * depthFirstEnumeration
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration depthFirstEnumeration()
+ {
+ return postorderEnumeration();
+ }
+
+ /**
+ * pathFromAncestorEnumeration
+ *
+ * @param node TODO
+ *
+ * @return Enumeration
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ public Enumeration pathFromAncestorEnumeration(TreeNode node)
+ {
+ if (node == null)
+ throw new IllegalArgumentException();
+
+ TreeNode parent = this;
+ Vector<TreeNode> nodes = new Vector<TreeNode>();
+ nodes.add(this);
+
+ while (parent != node && parent != null)
+ {
+ parent = parent.getParent();
+ nodes.add(0, parent);
+ }
+
+ if (parent != node)
+ throw new IllegalArgumentException();
+
+ return nodes.elements();
+ }
+
+ /**
+ * Returns <code>true</code> if <code>node</code> is a child of this tree
+ * node, and <code>false</code> otherwise. If <code>node</code> is
+ * <code>null</code>, this method returns <code>false</code>.
+ *
+ * @param node the node (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean isNodeChild(TreeNode node)
+ {
+ if (node == null)
+ return false;
+
+ return node.getParent() == this;
+ }
+
+ /**
+ * Returns the first child node belonging to this tree node.
+ *
+ * @return The first child node.
+ *
+ * @throws NoSuchElementException if this tree node has no children.
+ */
+ public TreeNode getFirstChild()
+ {
+ return children.firstElement();
+ }
+
+ /**
+ * Returns the last child node belonging to this tree node.
+ *
+ * @return The last child node.
+ *
+ * @throws NoSuchElementException if this tree node has no children.
+ */
+ public TreeNode getLastChild()
+ {
+ return children.lastElement();
+ }
+
+ /**
+ * Returns the next child after the specified <code>node</code>, or
+ * <code>null</code> if there is no child after the specified
+ * <code>node</code>.
+ *
+ * @param node a child of this node (<code>null</code> not permitted).
+ *
+ * @return The next child, or <code>null</code>.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is not a child of
+ * this node, or is <code>null</code>.
+ */
+ public TreeNode getChildAfter(TreeNode node)
+ {
+ if (node == null || node.getParent() != this)
+ throw new IllegalArgumentException();
+
+ int index = getIndex(node) + 1;
+
+ if (index == getChildCount())
+ return null;
+
+ return getChildAt(index);
+ }
+
+ /**
+ * Returns the previous child before the specified <code>node</code>, or
+ * <code>null</code> if there is no child before the specified
+ * <code>node</code>.
+ *
+ * @param node a child of this node (<code>null</code> not permitted).
+ *
+ * @return The previous child, or <code>null</code>.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is not a child of
+ * this node, or is <code>null</code>.
+ */
+ public TreeNode getChildBefore(TreeNode node)
+ {
+ if (node == null || node.getParent() != this)
+ throw new IllegalArgumentException();
+
+ int index = getIndex(node) - 1;
+
+ if (index < 0)
+ return null;
+
+ return getChildAt(index);
+ }
+
+ /**
+ * Returns <code>true</code> if this tree node and <code>node</code> share
+ * the same parent. If <code>node</code> is this tree node, the method
+ * returns <code>true</code> and if <code>node</code> is <code>null</code>
+ * this method returns <code>false</code>.
+ *
+ * @param node the node (<code>null</code> permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean isNodeSibling(TreeNode node)
+ {
+ if (node == null)
+ return false;
+ if (node == this)
+ return true;
+ return node.getParent() == getParent() && getParent() != null;
+ }
+
+ /**
+ * Returns the number of siblings for this tree node. If the tree node has
+ * a parent, this method returns the child count for the parent, otherwise
+ * it returns <code>1</code>.
+ *
+ * @return The sibling count.
+ */
+ public int getSiblingCount()
+ {
+ if (parent == null)
+ return 1;
+
+ return parent.getChildCount();
+ }
+
+ /**
+ * Returns the next sibling for this tree node. If this node has no parent,
+ * or this node is the last child of its parent, this method returns
+ * <code>null</code>.
+ *
+ * @return The next sibling, or <code>null</code>.
+ */
+ public DefaultMutableTreeNode getNextSibling()
+ {
+ if (parent == null)
+ return null;
+
+ int index = parent.getIndex(this) + 1;
+
+ if (index == parent.getChildCount())
+ return null;
+
+ return (DefaultMutableTreeNode) parent.getChildAt(index);
+ }
+
+ /**
+ * Returns the previous sibling for this tree node. If this node has no
+ * parent, or this node is the first child of its parent, this method returns
+ * <code>null</code>.
+ *
+ * @return The previous sibling, or <code>null</code>.
+ */
+ public DefaultMutableTreeNode getPreviousSibling()
+ {
+ if (parent == null)
+ return null;
+
+ int index = parent.getIndex(this) - 1;
+
+ if (index < 0)
+ return null;
+
+ return (DefaultMutableTreeNode) parent.getChildAt(index);
+ }
+
+ /**
+ * Returns <code>true</code> if this tree node is a lead node (that is, it
+ * has no children), and <code>false</otherwise>.
+ *
+ * @return A boolean.
+ */
+ public boolean isLeaf()
+ {
+ return children.size() == 0;
+ }
+
+ /**
+ * Returns the first leaf node that is a descendant of this node. Recall
+ * that a node is its own descendant, so if this node has no children then
+ * it is returned as the first leaf.
+ *
+ * @return The first leaf node.
+ */
+ public DefaultMutableTreeNode getFirstLeaf()
+ {
+ TreeNode current = this;
+
+ while (current.getChildCount() > 0)
+ current = current.getChildAt(0);
+
+ return (DefaultMutableTreeNode) current;
+ }
+
+ /**
+ * Returns the last leaf node that is a descendant of this node. Recall
+ * that a node is its own descendant, so if this node has no children then
+ * it is returned as the last leaf.
+ *
+ * @return The first leaf node.
+ */
+ public DefaultMutableTreeNode getLastLeaf()
+ {
+ TreeNode current = this;
+ int size = current.getChildCount();
+
+ while (size > 0)
+ {
+ current = current.getChildAt(size - 1);
+ size = current.getChildCount();
+ }
+
+ return (DefaultMutableTreeNode) current;
+ }
+
+ /**
+ * Returns the next leaf node after this tree node.
+ *
+ * @return The next leaf node, or <code>null</code>.
+ */
+ public DefaultMutableTreeNode getNextLeaf()
+ {
+ // if there is a next sibling, return its first leaf
+ DefaultMutableTreeNode sibling = getNextSibling();
+ if (sibling != null)
+ return sibling.getFirstLeaf();
+ // otherwise move up one level and try again...
+ if (parent != null)
+ return ((DefaultMutableTreeNode) parent).getNextLeaf();
+ return null;
+ }
+
+ /**
+ * Returns the previous leaf node before this tree node.
+ *
+ * @return The previous leaf node, or <code>null</code>.
+ */
+ public DefaultMutableTreeNode getPreviousLeaf()
+ {
+ // if there is a previous sibling, return its last leaf
+ DefaultMutableTreeNode sibling = getPreviousSibling();
+ if (sibling != null)
+ return sibling.getLastLeaf();
+ // otherwise move up one level and try again...
+ if (parent != null)
+ return ((DefaultMutableTreeNode) parent).getPreviousLeaf();
+ return null;
+ }
+
+ /**
+ * getLeafCount
+ *
+ * @return int
+ */
+ public int getLeafCount()
+ {
+ int count = 0;
+ Enumeration<?> e = depthFirstEnumeration();
+
+ while (e.hasMoreElements())
+ {
+ TreeNode current = (TreeNode) e.nextElement();
+
+ if (current.isLeaf())
+ count++;
+ }
+
+ return count;
+ }
+
+ /** Provides an enumeration of a tree in breadth-first traversal
+ * order.
+ */
+ static class BreadthFirstEnumeration implements Enumeration<TreeNode>
+ {
+
+ LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
+
+ BreadthFirstEnumeration(TreeNode node)
+ {
+ queue.add(node);
+ }
+
+ public boolean hasMoreElements()
+ {
+ return !queue.isEmpty();
+ }
+
+ @SuppressWarnings("unchecked")
+ public TreeNode nextElement()
+ {
+ if (queue.isEmpty())
+ throw new NoSuchElementException("No more elements left.");
+
+ TreeNode node = queue.removeFirst();
+
+ Enumeration<TreeNode> children =
+ (Enumeration<TreeNode>) node.children();
+ while (children.hasMoreElements())
+ queue.add(children.nextElement());
+
+ return node;
+ }
+ }
+
+ /** Provides an enumeration of a tree traversing it
+ * preordered.
+ */
+ static class PreorderEnumeration implements Enumeration<TreeNode>
+ {
+ TreeNode next;
+
+ Stack<Enumeration<TreeNode>> childrenEnums =
+ new Stack<Enumeration<TreeNode>>();
+
+ @SuppressWarnings("unchecked")
+ PreorderEnumeration(TreeNode node)
+ {
+ next = node;
+ childrenEnums.push((Enumeration<TreeNode>) node.children());
+ }
+
+ public boolean hasMoreElements()
+ {
+ return next != null;
+ }
+
+ public TreeNode nextElement()
+ {
+ if (next == null)
+ throw new NoSuchElementException("No more elements left.");
+
+ TreeNode current = next;
+
+ Enumeration<TreeNode> children = childrenEnums.peek();
+
+ // Retrieves the next element.
+ next = traverse(children);
+
+ return current;
+ }
+
+ @SuppressWarnings("unchecked")
+ private TreeNode traverse(Enumeration<TreeNode> children)
+ {
+ // If more children are available step down.
+ if (children.hasMoreElements())
+ {
+ TreeNode child = children.nextElement();
+ childrenEnums.push((Enumeration<TreeNode>) child.children());
+
+ return child;
+ }
+
+ // If no children are left, we return to a higher level.
+ childrenEnums.pop();
+
+ // If there are no more levels left, there is no next
+ // element to return.
+ if (childrenEnums.isEmpty())
+ return null;
+ else
+ {
+ return traverse(childrenEnums.peek());
+ }
+ }
+ }
+
+ /** Provides an enumeration of a tree traversing it
+ * postordered (= depth-first).
+ */
+ static class PostorderEnumeration implements Enumeration<TreeNode>
+ {
+
+ Stack<TreeNode> nodes = new Stack<TreeNode>();
+ Stack<Enumeration<TreeNode>> childrenEnums =
+ new Stack<Enumeration<TreeNode>>();
+
+ @SuppressWarnings("unchecked")
+ PostorderEnumeration(TreeNode node)
+ {
+ nodes.push(node);
+ childrenEnums.push((Enumeration<TreeNode>) node.children());
+ }
+
+ public boolean hasMoreElements()
+ {
+ return !nodes.isEmpty();
+ }
+
+ public TreeNode nextElement()
+ {
+ if (nodes.isEmpty())
+ throw new NoSuchElementException("No more elements left!");
+
+ Enumeration<TreeNode> children = childrenEnums.peek();
+
+ return traverse(children);
+ }
+
+ @SuppressWarnings("unchecked")
+ private TreeNode traverse(Enumeration<TreeNode> children)
+ {
+ if (children.hasMoreElements())
+ {
+ TreeNode node = children.nextElement();
+ nodes.push(node);
+
+ Enumeration<TreeNode> newChildren =
+ (Enumeration<TreeNode>) node.children();
+ childrenEnums.push(newChildren);
+
+ return traverse(newChildren);
+ }
+ else
+ {
+ childrenEnums.pop();
+
+ // Returns the node whose children
+ // have all been visited. (= postorder)
+ TreeNode next = nodes.peek();
+ nodes.pop();
+
+ return next;
+ }
+ }
+
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java
new file mode 100644
index 000000000..9ee0a14ba
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java
@@ -0,0 +1,792 @@
+/* DefaultTreeCellEditor.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.EventObject;
+
+import javax.swing.DefaultCellEditor;
+import javax.swing.Icon;
+import javax.swing.JTextField;
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.EventListenerList;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+
+/**
+ * Participates in the tree cell editing.
+ *
+ * @author Andrew Selkirk
+ * @author Audrius Meskauskas
+ */
+public class DefaultTreeCellEditor
+ implements ActionListener, TreeCellEditor, TreeSelectionListener
+{
+ /**
+ * This container that appears on the tree during editing session.
+ * It contains the editing component displays various other editor -
+ * specific parts like editing icon.
+ */
+ public class EditorContainer extends Container
+ {
+ /**
+ * Use v 1.5 serial version UID for interoperability.
+ */
+ static final long serialVersionUID = 6470339600449699810L;
+
+ /**
+ * Creates an <code>EditorContainer</code> object.
+ */
+ public EditorContainer()
+ {
+ setLayout(null);
+ }
+
+ /**
+ * This method only exists for API compatibility and is useless as it does
+ * nothing. It got probably introduced by accident.
+ */
+ public void EditorContainer()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Overrides Container.paint to paint the node's icon and use the selection
+ * color for the background.
+ *
+ * @param g -
+ * the specified Graphics window
+ */
+ public void paint(Graphics g)
+ {
+ // Paint editing icon.
+ if (editingIcon != null)
+ {
+ // From the previous version, the left margin is taken as half
+ // of the icon width.
+ int y = Math.max(0, (getHeight() - editingIcon.getIconHeight()) / 2);
+ editingIcon.paintIcon(this, g, 0, y);
+ }
+ // Paint border.
+ Color c = getBorderSelectionColor();
+ if (c != null)
+ {
+ g.setColor(c);
+ g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
+ }
+ super.paint(g);
+ }
+
+ /**
+ * Lays out this Container, moving the editor component to the left
+ * (leaving place for the icon).
+ */
+ public void doLayout()
+ {
+ if (editingComponent != null)
+ {
+ editingComponent.getPreferredSize();
+ editingComponent.setBounds(offset, 0, getWidth() - offset,
+ getHeight());
+ }
+ }
+
+ public Dimension getPreferredSize()
+ {
+ Dimension dim;
+ if (editingComponent != null)
+ {
+ dim = editingComponent.getPreferredSize();
+ dim.width += offset + 5;
+ if (renderer != null)
+ {
+ Dimension r = renderer.getPreferredSize();
+ dim.height = Math.max(dim.height, r.height);
+ }
+ if (editingIcon != null)
+ dim.height = Math.max(dim.height, editingIcon.getIconHeight());
+ dim.width = Math.max(100, dim.width);
+ }
+ else
+ dim = new Dimension(0, 0);
+ return dim;
+ }
+ }
+
+ /**
+ * The default text field, used in the editing sessions.
+ */
+ public class DefaultTextField extends JTextField
+ {
+ /**
+ * Use v 1.5 serial version UID for interoperability.
+ */
+ static final long serialVersionUID = -6629304544265300143L;
+
+ /**
+ * The border of the text field.
+ */
+ protected Border border;
+
+ /**
+ * Creates a <code>DefaultTextField</code> object.
+ *
+ * @param aBorder the border to use
+ */
+ public DefaultTextField(Border aBorder)
+ {
+ border = aBorder;
+ }
+
+ /**
+ * Gets the font of this component.
+ * @return this component's font; if a font has not been set for
+ * this component, the font of its parent is returned (if the parent
+ * is not null, otherwise null is returned).
+ */
+ public Font getFont()
+ {
+ Font font = super.getFont();
+ if (font == null)
+ {
+ Component parent = getParent();
+ if (parent != null)
+ return parent.getFont();
+ return null;
+ }
+ return font;
+ }
+
+ /**
+ * Returns the border of the text field.
+ *
+ * @return the border
+ */
+ public Border getBorder()
+ {
+ return border;
+ }
+
+ /**
+ * Overrides JTextField.getPreferredSize to return the preferred size
+ * based on current font, if set, or else use renderer's font.
+ *
+ * @return the Dimension of this textfield.
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension size = super.getPreferredSize();
+ if (renderer != null && DefaultTreeCellEditor.this.getFont() == null)
+ {
+ size.height = renderer.getPreferredSize().height;
+ }
+ return renderer.getPreferredSize();
+ }
+ }
+
+ private EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Editor handling the editing.
+ */
+ protected TreeCellEditor realEditor;
+
+ /**
+ * Renderer, used to get border and offsets from.
+ */
+ protected DefaultTreeCellRenderer renderer;
+
+ /**
+ * Editing container, will contain the editorComponent.
+ */
+ protected Container editingContainer;
+
+ /**
+ * Component used in editing, obtained from the editingContainer.
+ */
+ protected transient Component editingComponent;
+
+ /**
+ * As of Java 2 platform v1.4 this field should no longer be used.
+ * If you wish to provide similar behavior you should directly
+ * override isCellEditable.
+ */
+ protected boolean canEdit;
+
+ /**
+ * Used in editing. Indicates x position to place editingComponent.
+ */
+ protected transient int offset;
+
+ /**
+ * JTree instance listening too.
+ */
+ protected transient JTree tree;
+
+ /**
+ * Last path that was selected.
+ */
+ protected transient TreePath lastPath;
+
+ /**
+ * Used before starting the editing session.
+ */
+ protected transient javax.swing.Timer timer;
+
+ /**
+ * Row that was last passed into getTreeCellEditorComponent.
+ */
+ protected transient int lastRow;
+
+ /**
+ * True if the border selection color should be drawn.
+ */
+ protected Color borderSelectionColor;
+
+ /**
+ * Icon to use when editing.
+ */
+ protected transient Icon editingIcon;
+
+ /**
+ * Font to paint with, null indicates font of renderer is to be used.
+ */
+ protected Font font;
+
+ /**
+ * Constructs a DefaultTreeCellEditor object for a JTree using the
+ * specified renderer and a default editor. (Use this constructor
+ * for normal editing.)
+ *
+ * @param tree - a JTree object
+ * @param renderer - a DefaultTreeCellRenderer object
+ */
+ public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer)
+ {
+ this(tree, renderer, null);
+ }
+
+ /**
+ * Constructs a DefaultTreeCellEditor object for a JTree using the specified
+ * renderer and the specified editor. (Use this constructor
+ * for specialized editing.)
+ *
+ * @param tree - a JTree object
+ * @param renderer - a DefaultTreeCellRenderer object
+ * @param editor - a TreeCellEditor object
+ */
+ public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer,
+ TreeCellEditor editor)
+ {
+ this.renderer = renderer;
+ realEditor = editor;
+ if (realEditor == null)
+ realEditor = createTreeCellEditor();
+ editingContainer = createContainer();
+ setTree(tree);
+ Color c = UIManager.getColor("Tree.editorBorderSelectionColor");
+ setBorderSelectionColor(c);
+ }
+
+ /**
+ * writeObject
+ *
+ * @param value0
+ * TODO
+ * @exception IOException
+ * TODO
+ */
+ private void writeObject(ObjectOutputStream value0) throws IOException
+ {
+ // TODO
+ }
+
+ /**
+ * readObject
+ * @param value0 TODO
+ * @exception IOException TODO
+ * @exception ClassNotFoundException TODO
+ */
+ private void readObject(ObjectInputStream value0)
+ throws IOException, ClassNotFoundException
+ {
+ // TODO
+ }
+
+ /**
+ * Sets the color to use for the border.
+ * @param newColor - the new border color
+ */
+ public void setBorderSelectionColor(Color newColor)
+ {
+ this.borderSelectionColor = newColor;
+ }
+
+ /**
+ * Returns the color the border is drawn.
+ * @return Color
+ */
+ public Color getBorderSelectionColor()
+ {
+ return borderSelectionColor;
+ }
+
+ /**
+ * Sets the font to edit with. null indicates the renderers
+ * font should be used. This will NOT override any font you have
+ * set in the editor the receiver was instantied with. If null for
+ * an editor was passed in, a default editor will be created that
+ * will pick up this font.
+ *
+ * @param font - the editing Font
+ */
+ public void setFont(Font font)
+ {
+ if (font != null)
+ this.font = font;
+ else
+ this.font = renderer.getFont();
+ }
+
+ /**
+ * Gets the font used for editing.
+ *
+ * @return the editing font
+ */
+ public Font getFont()
+ {
+ return font;
+ }
+
+ /**
+ * Configures the editor. Passed onto the realEditor.
+ * Sets an initial value for the editor. This will cause
+ * the editor to stopEditing and lose any partially edited value
+ * if the editor is editing when this method is called.
+ * Returns the component that should be added to the client's Component
+ * hierarchy. Once installed in the client's hierarchy this component will
+ * then be able to draw and receive user input.
+ *
+ * @param tree - the JTree that is asking the editor to edit; this parameter can be null
+ * @param value - the value of the cell to be edited
+ * @param isSelected - true is the cell is to be rendered with selection highlighting
+ * @param expanded - true if the node is expanded
+ * @param leaf - true if the node is a leaf node
+ * @param row - the row index of the node being edited
+ *
+ * @return the component for editing
+ */
+ public Component getTreeCellEditorComponent(JTree tree, Object value,
+ boolean isSelected,
+ boolean expanded,
+ boolean leaf, int row)
+ {
+ setTree(tree);
+ lastRow = row;
+ determineOffset(tree, value, isSelected, expanded, leaf, row);
+ if (editingComponent != null)
+ editingContainer.remove(editingComponent);
+
+ editingComponent = realEditor.getTreeCellEditorComponent(tree, value,
+ isSelected,
+ expanded, leaf,
+ row);
+ Font f = getFont();
+ if (f == null)
+ {
+ if (renderer != null)
+ f = renderer.getFont();
+ if (f == null)
+ f = tree.getFont();
+ }
+ editingContainer.setFont(f);
+ prepareForEditing();
+ return editingContainer;
+ }
+
+ /**
+ * Returns the value currently being edited (requests it from the
+ * {@link #realEditor}.
+ *
+ * @return the value currently being edited
+ */
+ public Object getCellEditorValue()
+ {
+ return realEditor.getCellEditorValue();
+ }
+
+ /**
+ * If the realEditor returns true to this message, prepareForEditing
+ * is messaged and true is returned.
+ *
+ * @param event - the event the editor should use to consider whether to
+ * begin editing or not
+ * @return true if editing can be started
+ */
+ public boolean isCellEditable(EventObject event)
+ {
+ boolean ret = false;
+ boolean ed = false;
+ if (event != null)
+ {
+ if (event.getSource() instanceof JTree)
+ {
+ setTree((JTree) event.getSource());
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) event;
+ TreePath path = tree.getPathForLocation(me.getX(), me.getY());
+ ed = lastPath != null && path != null && lastPath.equals(path);
+ if (path != null)
+ {
+ lastRow = tree.getRowForPath(path);
+ Object val = path.getLastPathComponent();
+ boolean isSelected = tree.isRowSelected(lastRow);
+ boolean isExpanded = tree.isExpanded(path);
+ TreeModel m = tree.getModel();
+ boolean isLeaf = m.isLeaf(val);
+ determineOffset(tree, val, isSelected, isExpanded, isLeaf,
+ lastRow);
+ }
+ }
+ }
+ }
+ if (! realEditor.isCellEditable(event))
+ ret = false;
+ else
+ {
+ if (canEditImmediately(event))
+ ret = true;
+ else if (ed && shouldStartEditingTimer(event))
+ startEditingTimer();
+ else if (timer != null && timer.isRunning())
+ timer.stop();
+ }
+ if (ret)
+ prepareForEditing();
+ return ret;
+
+ }
+
+ /**
+ * Messages the realEditor for the return value.
+ *
+ * @param event -
+ * the event the editor should use to start editing
+ * @return true if the editor would like the editing cell to be selected;
+ * otherwise returns false
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ return true;
+ }
+
+ /**
+ * If the realEditor will allow editing to stop, the realEditor
+ * is removed and true is returned, otherwise false is returned.
+ * @return true if editing was stopped; false otherwise
+ */
+ public boolean stopCellEditing()
+ {
+ boolean ret = false;
+ if (realEditor.stopCellEditing())
+ {
+ finish();
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Messages cancelCellEditing to the realEditor and removes it
+ * from this instance.
+ */
+ public void cancelCellEditing()
+ {
+ realEditor.cancelCellEditing();
+ finish();
+ }
+
+ private void finish()
+ {
+ if (editingComponent != null)
+ editingContainer.remove(editingComponent);
+ editingComponent = null;
+ }
+
+ /**
+ * Adds a <code>CellEditorListener</code> object to this editor.
+ *
+ * @param listener
+ * the listener to add
+ */
+ public void addCellEditorListener(CellEditorListener listener)
+ {
+ realEditor.addCellEditorListener(listener);
+ }
+
+ /**
+ * Removes a <code>CellEditorListener</code> object.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeCellEditorListener(CellEditorListener listener)
+ {
+ realEditor.removeCellEditorListener(listener);
+ }
+
+ /**
+ * Returns all added <code>CellEditorListener</code> objects to this editor.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public CellEditorListener[] getCellEditorListeners()
+ {
+ return (CellEditorListener[]) listenerList.getListeners(CellEditorListener.class);
+ }
+
+ /**
+ * Resets lastPath.
+ *
+ * @param e - the event that characterizes the change.
+ */
+ public void valueChanged(TreeSelectionEvent e)
+ {
+ if (tree != null)
+ {
+ if (tree.getSelectionCount() == 1)
+ lastPath = tree.getSelectionPath();
+ else
+ lastPath = null;
+ }
+ // TODO: We really should do the following here, but can't due
+ // to buggy DefaultTreeSelectionModel. This selection model
+ // should only fire if the selection actually changes.
+// if (timer != null)
+// timer.stop();
+ }
+
+ /**
+ * Messaged when the timer fires.
+ *
+ * @param e the event that characterizes the action.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (tree != null && lastPath != null)
+ tree.startEditingAtPath(lastPath);
+ }
+
+ /**
+ * Sets the tree currently editing for. This is needed to add a selection
+ * listener.
+ *
+ * @param newTree -
+ * the new tree to be edited
+ */
+ protected void setTree(JTree newTree)
+ {
+ if (tree != newTree)
+ {
+ if (tree != null)
+ tree.removeTreeSelectionListener(this);
+ tree = newTree;
+ if (tree != null)
+ tree.addTreeSelectionListener(this);
+
+ if (timer != null)
+ timer.stop();
+ }
+ }
+
+ /**
+ * Returns true if event is a MouseEvent and the click count is 1.
+ *
+ * @param event - the event being studied
+ * @return true if editing should start
+ */
+ protected boolean shouldStartEditingTimer(EventObject event)
+ {
+ boolean ret = false;
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) event;
+ ret = SwingUtilities.isLeftMouseButton(me) && me.getClickCount() == 1
+ && inHitRegion(me.getX(), me.getY());
+ }
+ return ret;
+ }
+
+ /**
+ * Starts the editing timer (if one installed).
+ */
+ protected void startEditingTimer()
+ {
+ if (timer == null)
+ {
+ timer = new Timer(1200, this);
+ timer.setRepeats(false);
+ }
+ timer.start();
+ }
+
+ /**
+ * Returns true if event is null, or it is a MouseEvent with
+ * a click count > 2 and inHitRegion returns true.
+ *
+ * @param event - the event being studied
+ * @return true if event is null, or it is a MouseEvent with
+ * a click count > 2 and inHitRegion returns true
+ */
+ protected boolean canEditImmediately(EventObject event)
+ {
+ if (event == null || !(event instanceof MouseEvent) || (((MouseEvent) event).
+ getClickCount() > 2 && inHitRegion(((MouseEvent) event).getX(),
+ ((MouseEvent) event).getY())))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns true if the passed in location is a valid mouse location
+ * to start editing from. This is implemented to return false if x is
+ * less than or equal to the width of the icon and icon
+ * gap displayed by the renderer. In other words this returns true if
+ * the user clicks over the text part displayed by the renderer, and
+ * false otherwise.
+ *
+ * @param x - the x-coordinate of the point
+ * @param y - the y-coordinate of the point
+ *
+ * @return true if the passed in location is a valid mouse location
+ */
+ protected boolean inHitRegion(int x, int y)
+ {
+ Rectangle bounds = tree.getPathBounds(lastPath);
+ return bounds.contains(x, y);
+ }
+
+ /**
+ * determineOffset
+ * @param tree -
+ * @param value -
+ * @param isSelected -
+ * @param expanded -
+ * @param leaf -
+ * @param row -
+ */
+ protected void determineOffset(JTree tree, Object value, boolean isSelected,
+ boolean expanded, boolean leaf, int row)
+ {
+ if (renderer != null)
+ {
+ if (leaf)
+ editingIcon = renderer.getLeafIcon();
+ else if (expanded)
+ editingIcon = renderer.getOpenIcon();
+ else
+ editingIcon = renderer.getClosedIcon();
+ if (editingIcon != null)
+ offset = renderer.getIconTextGap() + editingIcon.getIconWidth();
+ else
+ offset = renderer.getIconTextGap();
+ }
+ else
+ {
+ editingIcon = null;
+ offset = 0;
+ }
+ }
+
+ /**
+ * Invoked just before editing is to start. Will add the
+ * editingComponent to the editingContainer.
+ */
+ protected void prepareForEditing()
+ {
+ if (editingComponent != null)
+ editingContainer.add(editingComponent);
+ }
+
+ /**
+ * Creates the container to manage placement of editingComponent.
+ *
+ * @return the container to manage the placement of the editingComponent.
+ */
+ protected Container createContainer()
+ {
+ return new DefaultTreeCellEditor.EditorContainer();
+ }
+
+ /**
+ * This is invoked if a TreeCellEditor is not supplied in the constructor.
+ * It returns a TextField editor.
+ *
+ * @return a new TextField editor
+ */
+ protected TreeCellEditor createTreeCellEditor()
+ {
+ Border border = UIManager.getBorder("Tree.editorBorder");
+ JTextField tf = new DefaultTreeCellEditor.DefaultTextField(border);
+ DefaultCellEditor editor = new DefaultCellEditor(tf);
+ editor.setClickCountToStart(1);
+ realEditor = editor;
+ return editor;
+ }
+}
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java
new file mode 100644
index 000000000..a2c8fc204
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java
@@ -0,0 +1,720 @@
+/* DefaultTreeCellRenderer.java
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 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.tree;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.JTree;
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A default implementation of the {@link TreeCellRenderer} interface.
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultTreeCellRenderer
+ extends JLabel
+ implements TreeCellRenderer
+{
+
+ /**
+ * A flag indicating the current selection status.
+ */
+ protected boolean selected;
+
+ /**
+ * A flag indicating the current focus status.
+ */
+ protected boolean hasFocus;
+
+ /**
+ * Indicates if the focus border is also drawn around the icon.
+ */
+ private boolean drawsFocusBorderAroundIcon;
+
+ /**
+ * The icon used to represent non-leaf nodes that are closed.
+ *
+ * @see #setClosedIcon(Icon)
+ */
+ protected transient Icon closedIcon;
+
+ /**
+ * The icon used to represent leaf nodes.
+ *
+ * @see #setLeafIcon(Icon)
+ */
+ protected transient Icon leafIcon;
+
+ /**
+ * The icon used to represent non-leaf nodes that are open.
+ *
+ * @see #setOpenIcon(Icon)
+ */
+ protected transient Icon openIcon;
+
+ /**
+ * The color used for text in selected cells.
+ *
+ * @see #setTextSelectionColor(Color)
+ */
+ protected Color textSelectionColor;
+
+ /**
+ * The color used for text in non-selected cells.
+ *
+ * @see #setTextNonSelectionColor(Color)
+ */
+ protected Color textNonSelectionColor;
+
+ /**
+ * The background color for selected cells.
+ *
+ * @see #setBackgroundSelectionColor(Color)
+ */
+ protected Color backgroundSelectionColor;
+
+ /**
+ * The background color for non-selected cells.
+ *
+ * @see #setBackgroundNonSelectionColor(Color)
+ */
+ protected Color backgroundNonSelectionColor;
+
+ /**
+ * The border color for selected tree cells.
+ *
+ * @see #setBorderSelectionColor(Color)
+ */
+ protected Color borderSelectionColor;
+
+ /**
+ * Creates a new tree cell renderer with defaults appropriate for the
+ * current {@link LookAndFeel}.
+ */
+ public DefaultTreeCellRenderer()
+ {
+ setLeafIcon(getDefaultLeafIcon());
+ setOpenIcon(getDefaultOpenIcon());
+ setClosedIcon(getDefaultClosedIcon());
+
+ setTextNonSelectionColor(UIManager.getColor("Tree.textForeground"));
+ setTextSelectionColor(UIManager.getColor("Tree.selectionForeground"));
+ setBackgroundNonSelectionColor(UIManager.getColor("Tree.textBackground"));
+ setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground"));
+ setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
+ Object val = UIManager.get("Tree.drawsFocusBorderAroundIcon");
+ drawsFocusBorderAroundIcon = val != null && ((Boolean) val).booleanValue();
+ }
+
+ /**
+ * Returns the default icon for non-leaf tree cells that are open (expanded).
+ * The icon is fetched from the defaults table for the current
+ * {@link LookAndFeel} using the key <code>Tree.openIcon</code>.
+ *
+ * @return The default icon.
+ */
+ public Icon getDefaultOpenIcon()
+ {
+ return UIManager.getIcon("Tree.openIcon");
+ }
+
+ /**
+ * Returns the default icon for non-leaf tree cells that are closed (not
+ * expanded). The icon is fetched from the defaults table for the current
+ * {@link LookAndFeel} using the key <code>Tree.closedIcon</code>.
+ *
+ * @return The default icon.
+ */
+ public Icon getDefaultClosedIcon()
+ {
+ return UIManager.getIcon("Tree.closedIcon");
+ }
+
+ /**
+ * Returns the default icon for leaf tree cells. The icon is fetched from
+ * the defaults table for the current {@link LookAndFeel} using the key
+ * <code>Tree.leafIcon</code>.
+ *
+ * @return The default icon.
+ */
+ public Icon getDefaultLeafIcon()
+ {
+ return UIManager.getIcon("Tree.leafIcon");
+ }
+
+ /**
+ * Sets the icon to be displayed for non-leaf nodes that are open (expanded).
+ * Set this to <code>null</code> if no icon is required.
+ *
+ * @param icon the icon (<code>null</code> permitted).
+ *
+ * @see #getOpenIcon()
+ */
+ public void setOpenIcon(Icon icon)
+ {
+ openIcon = icon;
+ }
+
+ /**
+ * Returns the icon displayed for non-leaf nodes that are open (expanded).
+ * The default value is initialised from the {@link LookAndFeel}.
+ *
+ * @return The open icon (possibly <code>null</code>).
+ *
+ * @see #setOpenIcon(Icon)
+ */
+ public Icon getOpenIcon()
+ {
+ return openIcon;
+ }
+
+ /**
+ * Sets the icon to be displayed for non-leaf nodes that are closed. Set
+ * this to <code>null</code> if no icon is required.
+ *
+ * @param icon the icon (<code>null</code> permitted).
+ *
+ * @see #getClosedIcon()
+ */
+ public void setClosedIcon(Icon icon)
+ {
+ closedIcon = icon;
+ }
+
+ /**
+ * Returns the icon displayed for non-leaf nodes that are closed. The
+ * default value is initialised from the {@link LookAndFeel}.
+ *
+ * @return The closed icon (possibly <code>null</code>).
+ *
+ * @see #setClosedIcon(Icon)
+ */
+ public Icon getClosedIcon()
+ {
+ return closedIcon;
+ }
+
+ /**
+ * Sets the icon to be displayed for leaf nodes. Set this to
+ * <code>null</code> if no icon is required.
+ *
+ * @param icon the icon (<code>null</code> permitted).
+ *
+ * @see #getLeafIcon()
+ */
+ public void setLeafIcon(Icon icon)
+ {
+ leafIcon = icon;
+ }
+
+ /**
+ * Returns the icon displayed for leaf nodes. The default value is
+ * initialised from the {@link LookAndFeel}.
+ *
+ * @return The leaf icon (possibly <code>null</code>).
+ *
+ * @see #setLeafIcon(Icon)
+ */
+ public Icon getLeafIcon()
+ {
+ return leafIcon;
+ }
+
+ /**
+ * Sets the text color for tree cells that are selected.
+ *
+ * @param c the color (<code>null</code> permitted).
+ *
+ * @see #getTextSelectionColor()
+ */
+ public void setTextSelectionColor(Color c)
+ {
+ textSelectionColor = c;
+ }
+
+ /**
+ * Returns the text color for tree cells that are selected.
+ * The default value is obtained from the {@link LookAndFeel} defaults
+ * table using the key <code>Tree.selectionForeground</code>.
+ *
+ * @return The text color for tree cells that are selected.
+ *
+ * @see #setTextSelectionColor(Color)
+ */
+ public Color getTextSelectionColor()
+ {
+ return textSelectionColor;
+ }
+
+ /**
+ * Sets the text color for tree cells that are not selected.
+ *
+ * @param c the color (<code>null</code> permitted).
+ *
+ * @see #getTextNonSelectionColor()
+ */
+ public void setTextNonSelectionColor(Color c)
+ {
+ textNonSelectionColor = c;
+ }
+
+ /**
+ * Returns the text color for tree cells that are not selected.
+ * The default value is obtained from the {@link LookAndFeel} defaults
+ * table using the key <code>Tree.selectionForeground</code>.
+ *
+ * @return The background color for tree cells that are not selected.
+ *
+ * @see #setTextgroundNonSelectionColor(Color)
+ */
+ public Color getTextNonSelectionColor()
+ {
+ return textNonSelectionColor;
+ }
+
+ /**
+ * Sets the background color for tree cells that are selected.
+ *
+ * @param c the color (<code>null</code> permitted).
+ *
+ * @see #getBackgroundSelectionColor()
+ */
+ public void setBackgroundSelectionColor(Color c)
+ {
+ backgroundSelectionColor = c;
+ }
+
+ /**
+ * Returns the background color for tree cells that are selected.
+ * The default value is obtained from the {@link LookAndFeel} defaults
+ * table using the key <code>Tree.selectionBackground</code>.
+ *
+ * @return The background color for tree cells that are selected.
+ *
+ * @see #setBackgroundSelectionColor(Color)
+ */
+ public Color getBackgroundSelectionColor()
+ {
+ return backgroundSelectionColor;
+ }
+
+ /**
+ * Sets the background color for tree cells that are not selected.
+ *
+ * @param c the color (<code>null</code> permitted).
+ *
+ * @see #getBackgroundNonSelectionColor()
+ */
+ public void setBackgroundNonSelectionColor(Color c)
+ {
+ backgroundNonSelectionColor = c;
+ }
+
+ /**
+ * Returns the background color for tree cells that are not selected.
+ * The default value is obtained from the {@link LookAndFeel} defaults
+ * table using the key <code>Tree.textBackground</code>.
+ *
+ * @return The background color for tree cells that are not selected.
+ *
+ * @see #setBackgroundNonSelectionColor(Color)
+ */
+ public Color getBackgroundNonSelectionColor()
+ {
+ return backgroundNonSelectionColor;
+ }
+
+ /**
+ * Sets the border color for tree cells that are selected.
+ *
+ * @param c the color (<code>null</code> permitted).
+ *
+ * @see #getBorderSelectionColor()
+ */
+ public void setBorderSelectionColor(Color c)
+ {
+ borderSelectionColor = c;
+ }
+
+ /**
+ * Returns the border color for tree cells that are selected.
+ * The default value is obtained from the {@link LookAndFeel} defaults
+ * table using the key <code>Tree.selectionBorderColor</code>.
+ *
+ * @return The border color for tree cells that are selected.
+ *
+ * @see #setBorderSelectionColor(Color)
+ */
+ public Color getBorderSelectionColor()
+ {
+ return borderSelectionColor;
+ }
+
+ /**
+ * Sets the font.
+ *
+ * @param f the font.
+ *
+ * @see #getFont()
+ */
+ public void setFont(Font f)
+ {
+ if (f != null && f instanceof UIResource)
+ f = null;
+ super.setFont(f);
+ }
+
+ /**
+ * Sets the background color.
+ *
+ * @param c the color.
+ */
+ public void setBackground(Color c)
+ {
+ if (c != null && c instanceof UIResource)
+ c = null;
+ super.setBackground(c);
+ }
+
+ /**
+ * Returns a component (in fact <code>this</code>) that can be used to
+ * render a tree cell with the specified state.
+ *
+ * @param tree the tree that the cell belongs to.
+ * @param val the cell value.
+ * @param selected indicates whether or not the cell is selected.
+ * @param expanded indicates whether or not the cell is expanded.
+ * @param leaf indicates whether or not the cell is a leaf in the tree.
+ * @param row the row index.
+ * @param hasFocus indicates whether or not the cell has the focus.
+ *
+ * @return <code>this</code>.
+ */
+ public Component getTreeCellRendererComponent(JTree tree, Object val,
+ boolean selected,
+ boolean expanded, boolean leaf,
+ int row, boolean hasFocus)
+ {
+ if (leaf)
+ setIcon(getLeafIcon());
+ else if (expanded)
+ setIcon(getOpenIcon());
+ else
+ setIcon(getClosedIcon());
+
+ setText(val.toString());
+ this.selected = selected;
+ this.hasFocus = hasFocus;
+ setHorizontalAlignment(LEFT);
+ setOpaque(false);
+ setVerticalAlignment(CENTER);
+ setEnabled(true);
+ super.setFont(UIManager.getFont("Tree.font"));
+
+ if (selected)
+ {
+ super.setBackground(getBackgroundSelectionColor());
+ setForeground(getTextSelectionColor());
+
+ if (hasFocus)
+ setBorderSelectionColor(UIManager.getLookAndFeelDefaults().
+ getColor("Tree.selectionBorderColor"));
+ else
+ setBorderSelectionColor(null);
+ }
+ else
+ {
+ super.setBackground(getBackgroundNonSelectionColor());
+ setForeground(getTextNonSelectionColor());
+ setBorderSelectionColor(null);
+ }
+
+ return this;
+ }
+
+ /**
+ * Returns the current font.
+ *
+ * @return The current font.
+ *
+ * @see #setFont(Font)
+ */
+ public Font getFont()
+ {
+ return super.getFont();
+ }
+
+ /**
+ * Paints the value. The background is filled based on selected.
+ *
+ * @param g the graphics device.
+ */
+ public void paint(Graphics g)
+ {
+ // Determine background color.
+ Color bgColor;
+ if (selected)
+ bgColor = getBackgroundSelectionColor();
+ else
+ {
+ bgColor = getBackgroundNonSelectionColor();
+ if (bgColor == null)
+ bgColor = getBackground();
+ }
+ // Paint background.
+ int xOffset = -1;
+ if (bgColor != null)
+ {
+ xOffset = getXOffset();
+ g.setColor(bgColor);
+ g.fillRect(xOffset, 0, getWidth() - xOffset, getHeight());
+ }
+
+ if (hasFocus)
+ {
+ if (drawsFocusBorderAroundIcon)
+ xOffset = 0;
+ else if (xOffset == -1)
+ xOffset = getXOffset();
+ paintFocus(g, xOffset, 0, getWidth() - xOffset, getHeight());
+ }
+ super.paint(g);
+ }
+
+ /**
+ * Paints the focus indicator.
+ */
+ private void paintFocus(Graphics g, int x, int y, int w, int h)
+ {
+ Color col = getBorderSelectionColor();
+ if (col != null)
+ {
+ g.setColor(col);
+ g.drawRect(x, y, w - 1, h - 1);
+ }
+ }
+
+ /**
+ * Determines the X offset of the label that is caused by
+ * the icon.
+ *
+ * @return the X offset of the label
+ */
+ private int getXOffset()
+ {
+ Icon i = getIcon();
+ int offs = 0;
+ if (i != null && getText() != null)
+ offs = i.getIconWidth() + Math.max(0, getIconTextGap() - 1);
+ return offs;
+ }
+
+ /**
+ * Returns the preferred size of the cell.
+ *
+ * @return The preferred size of the cell.
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension size = super.getPreferredSize();
+ size.width += 3;
+ return size;
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ */
+ public void validate()
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ */
+ public void revalidate()
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param tm ignored
+ * @param x coordinate of the region to mark as dirty
+ * @param y coordinate of the region to mark as dirty
+ * @param width dimension of the region to mark as dirty
+ * @param height dimension of the region to mark as dirty
+ */
+ public void repaint(long tm, int x, int y, int width, int height)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param area the area to repaint.
+ */
+ public void repaint(Rectangle area)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ protected void firePropertyChange(String name, Object oldValue,
+ Object newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ public void firePropertyChange(String name, byte oldValue, byte newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ public void firePropertyChange(String name, char oldValue, char newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ public void firePropertyChange(String name, short oldValue, short newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ public void firePropertyChange(String name, int oldValue, int newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ public void firePropertyChange(String name, long oldValue, long newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ public void firePropertyChange(String name, float oldValue, float newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ public void firePropertyChange(String name, double oldValue, double newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+ /**
+ * For performance reasons, this method is overridden to do nothing.
+ *
+ * @param name the property name.
+ * @param oldValue the old value.
+ * @param newValue the new value.
+ */
+ public void firePropertyChange(String name, boolean oldValue,
+ boolean newValue)
+ {
+ // Overridden for performance reasons.
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java
new file mode 100644
index 000000000..0fa444586
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java
@@ -0,0 +1,622 @@
+/* DefaultTreeModel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.EventListenerList;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+
+/**
+ * DefaultTreeModel
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultTreeModel
+ implements Serializable, TreeModel
+{
+ static final long serialVersionUID = -2621068368932566998L;
+
+ /**
+ * root
+ */
+ protected TreeNode root;
+
+ /**
+ * listenerList
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * asksAllowsChildren
+ */
+ protected boolean asksAllowsChildren;
+
+ /**
+ * Constructor DefaultTreeModel where any node can have children.
+ *
+ * @param root the tree root.
+ */
+ public DefaultTreeModel(TreeNode root)
+ {
+ this (root, false);
+ }
+
+ /**
+ * Create the DefaultTreeModel that may check if the nodes can have
+ * children or not.
+ *
+ * @param aRoot the tree root.
+ * @param asksAllowsChildren if true, each node is asked if it can have
+ * children. If false, the model does not care about this, supposing, that
+ * any node can have children.
+ */
+ public DefaultTreeModel(TreeNode aRoot, boolean asksAllowsChildren)
+ {
+ if (aRoot == null)
+ aRoot = new DefaultMutableTreeNode();
+ this.root = aRoot;
+ this.asksAllowsChildren = asksAllowsChildren;
+ }
+
+ /**
+ * writeObject
+ *
+ * @param obj the object.
+ * @exception IOException TODO
+ */
+ private void writeObject(ObjectOutputStream obj) throws IOException
+ {
+ // TODO
+ }
+
+ /**
+ * readObject
+ *
+ * @param value0 TODO
+ * @exception IOException TODO
+ * @exception ClassNotFoundException TODO
+ */
+ private void readObject(ObjectInputStream value0) throws IOException,
+ ClassNotFoundException
+ {
+ // TODO
+ }
+
+ /**
+ * asksAllowsChildren
+ *
+ * @return boolean
+ */
+ public boolean asksAllowsChildren()
+ {
+ return asksAllowsChildren;
+ }
+
+ /**
+ * setAsksAllowsChildren
+ *
+ * @param value TODO
+ */
+ public void setAsksAllowsChildren(boolean value)
+ {
+ asksAllowsChildren = value;
+ }
+
+ /**
+ * setRoot
+ *
+ * @param root the root node.
+ */
+ public void setRoot(TreeNode root)
+ {
+ this.root = root;
+ }
+
+ /**
+ * getRoot
+ *
+ * @return Object
+ */
+ public Object getRoot()
+ {
+ return root;
+ }
+
+ /**
+ * getIndexOfChild
+ *
+ * @param parent TODO
+ * @param child TODO
+ * @return int
+ */
+ public int getIndexOfChild(Object parent, Object child)
+ {
+ for (int i = 0; i < getChildCount(parent); i++)
+ {
+ if (getChild(parent, i).equals(child))
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * getChild
+ *
+ * @param node TODO
+ * @param idx TODO
+ * @return Object
+ */
+ public Object getChild(Object node, int idx)
+ {
+ if (node instanceof TreeNode)
+ return ((TreeNode) node).getChildAt(idx);
+ else
+ return null;
+ }
+
+ /**
+ * getChildCount
+ *
+ * @param node TODO
+ * @return int
+ */
+ public int getChildCount(Object node)
+ {
+ if (node instanceof TreeNode)
+ return ((TreeNode) node).getChildCount();
+ else
+ return 0;
+ }
+
+ /**
+ * Returns if the specified node is a leaf or not. When
+ * {@link #asksAllowsChildren} is true, then this checks if the TreeNode
+ * allows children, otherwise it returns the TreeNode's <code>leaf</code>
+ * property.
+ *
+ * @param node the node to check
+ *
+ * @return boolean <code>true</code> if the node is a leaf node,
+ * <code>false</code> otherwise
+ *
+ * @throws ClassCastException if the specified node is not a
+ * <code>TreeNode</code> instance
+ *
+ * @see TreeNode#getAllowsChildren()
+ * @see TreeNode#isLeaf()
+ */
+ public boolean isLeaf(Object node)
+ {
+ // The RI throws a ClassCastException when node isn't a TreeNode, so do we.
+ TreeNode treeNode = (TreeNode) node;
+ boolean leaf;
+ if (asksAllowsChildren)
+ leaf = ! treeNode.getAllowsChildren();
+ else
+ leaf = treeNode.isLeaf();
+ return leaf;
+ }
+
+ /**
+ * <p>
+ * Invoke this method if you've modified the TreeNodes upon which this model
+ * depends. The model will notify all of its listeners that the model has
+ * changed. It will fire the events, necessary to update the layout caches and
+ * repaint the tree. The tree will <i>not</i> be properly refreshed if you
+ * call the JTree.repaint instead.
+ * </p>
+ * <p>
+ * This method will refresh the information about whole tree from the root. If
+ * only part of the tree should be refreshed, it is more effective to call
+ * {@link #reload(TreeNode)}.
+ * </p>
+ */
+ public void reload()
+ {
+ // Need to duplicate the code because the root can formally be
+ // no an instance of the TreeNode.
+ int n = getChildCount(root);
+ int[] childIdx = new int[n];
+ Object[] children = new Object[n];
+
+ for (int i = 0; i < n; i++)
+ {
+ childIdx[i] = i;
+ children[i] = getChild(root, i);
+ }
+
+ fireTreeStructureChanged(this, new Object[] { root }, childIdx, children);
+ }
+
+ /**
+ * Invoke this method if you've modified the TreeNodes upon which this model
+ * depends. The model will notify all of its listeners that the model has
+ * changed. It will fire the events, necessary to update the layout caches and
+ * repaint the tree. The tree will <i>not</i> be properly refreshed if you
+ * call the JTree.repaint instead.
+ *
+ * @param node - the tree node, from which the tree nodes have changed
+ * (inclusive). If you do not know this node, call {@link #reload()}
+ * instead.
+ */
+ public void reload(TreeNode node)
+ {
+ int n = getChildCount(node);
+ int[] childIdx = new int[n];
+ Object[] children = new Object[n];
+
+ for (int i = 0; i < n; i++)
+ {
+ childIdx[i] = i;
+ children[i] = getChild(node, i);
+ }
+
+ fireTreeStructureChanged(this, getPathToRoot(node), childIdx, children);
+ }
+
+ /**
+ * Messaged when the user has altered the value for the item
+ * identified by path to newValue. If newValue signifies a truly new
+ * value the model should post a treeNodesChanged event.
+ * This sets the user object of the TreeNode identified by
+ * path and posts a node changed. If you use custom user objects
+ * in the TreeModel you're going to need to subclass this and set
+ * the user object of the changed node to something meaningful.
+ *
+ * @param path - path to the node that the user has altered
+ * @param newValue - the new value from the TreeCellEditor
+ */
+ public void valueForPathChanged(TreePath path, Object newValue)
+ {
+ Object node = path.getLastPathComponent();
+ if (node instanceof MutableTreeNode)
+ {
+ ((MutableTreeNode) node).setUserObject(newValue);
+ int[] ci = null;
+ Object[] c = null;
+ Object[] parentPath = path.getPath();
+ if (path.getPathCount() > 1)
+ {
+ Object parent = ((TreeNode) node).getParent();
+ ci = new int[1];
+ ci[0] = getIndexOfChild(parent, node);
+ node = newValue;
+ path = path.getParentPath().pathByAddingChild(node);
+ c = new Object[1];
+ c[0] = node;
+ parentPath = path.getParentPath().getPath();
+ }
+
+ fireTreeNodesChanged(this, parentPath, ci, c);
+ }
+ }
+
+ /**
+ * Invoked this to insert newChild at location index in parents children.
+ * This will then message nodesWereInserted to create the appropriate event.
+ * This is the preferred way to add children as it will create the
+ * appropriate event.
+ *
+ * @param newChild is the node to add to the parent's children
+ * @param parent is the parent of the newChild
+ * @param index is the index of the newChild
+ */
+ public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent,
+ int index)
+ {
+ newChild.setParent(parent);
+ parent.insert(newChild, index);
+ int[] childIndices = new int[1];
+ childIndices[0] = index;
+ nodesWereInserted(parent, childIndices);
+ }
+
+ /**
+ * Message this to remove node from its parent. This will message
+ * nodesWereRemoved to create the appropriate event. This is the preferred
+ * way to remove a node as it handles the event creation for you.
+ *
+ * @param node to be removed
+ */
+ public void removeNodeFromParent(MutableTreeNode node)
+ {
+ TreeNode parent = node.getParent();
+ Object[] children = new Object[1];
+ children[0] = node;
+ int[] childIndices = new int[1];
+ childIndices[0] = getIndexOfChild(parent, node);
+ node.removeFromParent();
+ nodesWereRemoved(parent, childIndices, children);
+ }
+
+ /**
+ * Invoke this method after you've changed how node is to be represented
+ * in the tree.
+ *
+ * @param node that was changed
+ */
+ public void nodeChanged(TreeNode node)
+ {
+ TreeNode parent = node.getParent();
+ int[] childIndices = new int[1];
+ childIndices[0] = getIndexOfChild(parent, node);
+ Object[] children = new Object[1];
+ children[0] = node;
+ fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
+ }
+
+ /**
+ * Invoke this method after you've inserted some TreeNodes
+ * into node. childIndices should be the index of the new elements and must
+ * be sorted in ascending order.
+ *
+ * @param parent that had a child added to
+ * @param childIndices of the children added
+ */
+ public void nodesWereInserted(TreeNode parent, int[] childIndices)
+ {
+ Object[] children = new Object[childIndices.length];
+ for (int i = 0; i < children.length; i++)
+ children[i] = getChild(parent, childIndices[i]);
+ fireTreeNodesInserted(this, getPathToRoot(parent), childIndices, children);
+ }
+
+ /**
+ * Invoke this method after you've removed some TreeNodes from node.
+ * childIndices should be the index of the removed elements and
+ * must be sorted in ascending order. And removedChildren should be the
+ * array of the children objects that were removed.
+ *
+ * @param parent that had a child added to
+ * @param childIndices of the children added
+ * @param removedChildren are all the children removed from parent.
+ */
+ public void nodesWereRemoved(TreeNode parent, int[] childIndices,
+ Object[] removedChildren)
+ {
+ fireTreeNodesRemoved(this, getPathToRoot(parent), childIndices,
+ removedChildren);
+ }
+
+ /**
+ * Invoke this method after you've changed how the children identified by
+ * childIndices are to be represented in the tree.
+ *
+ * @param node that is the parent of the children that changed in a tree.
+ * @param childIndices are the child nodes that changed.
+ */
+ public void nodesChanged(TreeNode node, int[] childIndices)
+ {
+ Object[] children = new Object[childIndices.length];
+ for (int i = 0; i < children.length; i++)
+ children[i] = getChild(node, childIndices[i]);
+ fireTreeNodesChanged(this, getPathToRoot(node), childIndices, children);
+ }
+
+ /**
+ * Invoke this method if you've totally changed the children of node and
+ * its childrens children. This will post a treeStructureChanged event.
+ *
+ * @param node that had its children and grandchildren changed.
+ */
+ public void nodeStructureChanged(TreeNode node)
+ {
+ int n = getChildCount(root);
+ int[] childIdx = new int[n];
+ Object[] children = new Object[n];
+
+ for (int i = 0; i < n; i++)
+ {
+ childIdx[i] = i;
+ children[i] = getChild(root, i);
+ }
+
+ fireTreeStructureChanged(this, new Object[] { root }, childIdx, children);
+ }
+
+ /**
+ * Builds the parents of node up to and including the root node, where
+ * the original node is the last element in the returned array. The
+ * length of the returned array gives the node's depth in the tree.
+ *
+ * @param node - the TreeNode to get the path for
+ * @return TreeNode[] - the path from node to the root
+ */
+ public TreeNode[] getPathToRoot(TreeNode node)
+ {
+ return getPathToRoot(node, 0);
+ }
+
+ /**
+ * Builds the parents of node up to and including the root node, where
+ * the original node is the last element in the returned array. The
+ * length of the returned array gives the node's depth in the tree.
+ *
+ * @param node - the TreeNode to get the path for
+ * @param depth - an int giving the number of steps already taken
+ * towards the root (on recursive calls), used to size the returned array
+ * @return an array of TreeNodes giving the path from the root to the
+ * specified node
+ */
+ protected TreeNode[] getPathToRoot(TreeNode node, int depth)
+ {
+ if (node == null)
+ {
+ if (depth == 0)
+ return null;
+
+ return new TreeNode[depth];
+ }
+
+ TreeNode[] path = getPathToRoot(node.getParent(), depth + 1);
+ path[path.length - depth - 1] = node;
+ return path;
+ }
+
+ /**
+ * Registers a listere to the model.
+ *
+ * @param listener the listener to add
+ */
+ public void addTreeModelListener(TreeModelListener listener)
+ {
+ listenerList.add(TreeModelListener.class, listener);
+ }
+
+ /**
+ * Removes a listener from the model.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeTreeModelListener(TreeModelListener listener)
+ {
+ listenerList.remove(TreeModelListener.class, listener);
+ }
+
+ /**
+ * Returns all registered <code>TreeModelListener</code> listeners.
+ *
+ * @return an array of listeners.
+ *
+ * @since 1.4
+ */
+ public TreeModelListener[] getTreeModelListeners()
+ {
+ return (TreeModelListener[]) listenerList
+ .getListeners(TreeModelListener.class);
+ }
+
+ /**
+ * Notifies all listeners that have registered interest for notification
+ * on this event type. The event instance is lazily created using the parameters
+ * passed into the fire method.
+ *
+ * @param source the node being changed
+ * @param path the path to the root node
+ * @param childIndices the indices of the changed elements
+ * @param children the changed elements
+ */
+ protected void fireTreeNodesChanged(Object source, Object[] path,
+ int[] childIndices, Object[] children)
+ {
+ TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
+ children);
+
+ TreeModelListener[] listeners = getTreeModelListeners();
+
+ for (int i = listeners.length - 1; i >= 0; --i)
+ listeners[i].treeNodesChanged(event);
+ }
+
+ /**
+ * fireTreeNodesInserted
+ *
+ * @param source the node where new nodes got inserted
+ * @param path the path to the root node
+ * @param childIndices the indices of the new elements
+ * @param children the new elements
+ */
+ protected void fireTreeNodesInserted(Object source, Object[] path,
+ int[] childIndices, Object[] children)
+ {
+ TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
+ children);
+ TreeModelListener[] listeners = getTreeModelListeners();
+
+ for (int i = listeners.length - 1; i >= 0; --i)
+ listeners[i].treeNodesInserted(event);
+ }
+
+ /**
+ * fireTreeNodesRemoved
+ *
+ * @param source the node where nodes got removed-
+ * @param path the path to the root node
+ * @param childIndices the indices of the removed elements
+ * @param children the removed elements
+ */
+ protected void fireTreeNodesRemoved(Object source, Object[] path,
+ int[] childIndices, Object[] children)
+ {
+ TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
+ children);
+ TreeModelListener[] listeners = getTreeModelListeners();
+
+ for (int i = listeners.length - 1; i >= 0; --i)
+ listeners[i].treeNodesRemoved(event);
+ }
+
+ /**
+ * fireTreeStructureChanged
+ *
+ * @param source the node where the model has changed
+ * @param path the path to the root node
+ * @param childIndices the indices of the affected elements
+ * @param children the affected elements
+ */
+ protected void fireTreeStructureChanged(Object source, Object[] path,
+ int[] childIndices, Object[] children)
+ {
+ TreeModelEvent event = new TreeModelEvent(source, path, childIndices,
+ children);
+ TreeModelListener[] listeners = getTreeModelListeners();
+
+ for (int i = listeners.length - 1; i >= 0; --i)
+ listeners[i].treeStructureChanged(event);
+ }
+
+ /**
+ * Returns the registered listeners of a given type.
+ *
+ * @param listenerType the listener type to return
+ *
+ * @return an array of listeners
+ *
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+}
diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java
new file mode 100644
index 000000000..29add0e7b
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java
@@ -0,0 +1,1202 @@
+/* DefaultTreeSelectionModel.java
+ Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.EventListener;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.event.EventListenerList;
+import javax.swing.event.SwingPropertyChangeSupport;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+
+/**
+ * The implementation of the default tree selection model. The installed
+ * listeners are notified about the path and not the row changes. If you
+ * specifically need to track the row changes, register the listener for the
+ * expansion events.
+ *
+ * @author Andrew Selkirk
+ * @author Audrius Meskauskas
+ */
+public class DefaultTreeSelectionModel
+ implements Cloneable, Serializable, TreeSelectionModel
+{
+
+ /**
+ * According to the API docs, the method
+ * {@link DefaultTreeSelectionModel#notifyPathChange} should
+ * expect instances of a class PathPlaceHolder in the Vector parameter.
+ * This seems to be a non-public class, so I can only make guesses about the
+ * use of it.
+ */
+ private static class PathPlaceHolder
+ {
+ /**
+ * The path that we wrap.
+ */
+ TreePath path;
+
+ /**
+ * Indicates if the path is new or already in the selection.
+ */
+ boolean isNew;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param p the path to wrap
+ * @param n if the path is new or already in the selection
+ */
+ PathPlaceHolder(TreePath p, boolean n)
+ {
+ path = p;
+ isNew = n;
+ }
+ }
+
+ /**
+ * Use serialVersionUID for interoperability.
+ */
+ static final long serialVersionUID = 3288129636638950196L;
+
+ /**
+ * The name of the selection mode property.
+ */
+ public static final String SELECTION_MODE_PROPERTY = "selectionMode";
+
+ /**
+ * Our Swing property change support.
+ */
+ protected SwingPropertyChangeSupport changeSupport;
+
+ /**
+ * The current selection.
+ */
+ protected TreePath[] selection;
+
+ /**
+ * Our TreeSelectionListeners.
+ */
+ protected EventListenerList listenerList;
+
+ /**
+ * The current RowMapper.
+ */
+ protected transient RowMapper rowMapper;
+
+ /**
+ * The current listSelectionModel.
+ */
+ protected DefaultListSelectionModel listSelectionModel;
+
+ /**
+ * The current selection mode.
+ */
+ protected int selectionMode;
+
+ /**
+ * The path that has been added last.
+ */
+ protected TreePath leadPath;
+
+ /**
+ * The index of the last added path.
+ */
+ protected int leadIndex;
+
+ /**
+ * The row of the last added path according to the RowMapper.
+ */
+ protected int leadRow = -1;
+
+ /**
+ * A supporting datastructure that is used in addSelectionPaths() and
+ * removeSelectionPaths(). It contains currently selected paths.
+ *
+ * @see #addSelectionPaths(TreePath[])
+ * @see #removeSelectionPaths(TreePath[])
+ * @see #setSelectionPaths(TreePath[])
+ */
+ private transient HashSet<TreePath> selectedPaths;
+
+ /**
+ * A supporting datastructure that is used in addSelectionPaths() and
+ * removeSelectionPaths(). It contains the paths that are added or removed.
+ *
+ * @see #addSelectionPaths(TreePath[])
+ * @see #removeSelectionPaths(TreePath[])
+ * @see #setSelectionPaths(TreePath[])
+ */
+ private transient HashSet<TreePath> tmpPaths;
+
+ /**
+ * Constructs a new DefaultTreeSelectionModel.
+ */
+ public DefaultTreeSelectionModel()
+ {
+ setSelectionMode(DISCONTIGUOUS_TREE_SELECTION);
+ listSelectionModel = new DefaultListSelectionModel();
+ listenerList = new EventListenerList();
+ leadIndex = -1;
+ tmpPaths = new HashSet<TreePath>();
+ selectedPaths = new HashSet<TreePath>();
+ }
+
+ /**
+ * Creates a clone of this DefaultTreeSelectionModel with the same selection.
+ * The cloned instance will have the same registered listeners, the listeners
+ * themselves will not be cloned. The selection will be cloned.
+ *
+ * @exception CloneNotSupportedException should not be thrown here
+ * @return a copy of this DefaultTreeSelectionModel
+ */
+ public Object clone() throws CloneNotSupportedException
+ {
+ DefaultTreeSelectionModel cloned =
+ (DefaultTreeSelectionModel) super.clone();
+ cloned.changeSupport = null;
+ cloned.selection = (TreePath[]) selection.clone();
+ cloned.listenerList = new EventListenerList();
+ cloned.listSelectionModel =
+ (DefaultListSelectionModel) listSelectionModel.clone();
+ cloned.selectedPaths = new HashSet<TreePath>();
+ cloned.tmpPaths = new HashSet<TreePath>();
+
+ return cloned;
+ }
+
+ /**
+ * Returns a string that shows this object's properties.
+ * The returned string lists the selected tree rows, if any.
+ *
+ * @return a string that shows this object's properties
+ */
+ public String toString()
+ {
+ if (isSelectionEmpty())
+ return "[selection empty]";
+ else
+ {
+ CPStringBuilder b = new CPStringBuilder("selected rows: [");
+ for (int i = 0; i < selection.length; i++)
+ {
+ b.append(getRow(selection[i]));
+ b.append(' ');
+ }
+ b.append(", lead " + getLeadSelectionRow());
+ return b.toString();
+ }
+ }
+
+ /**
+ * writeObject
+ *
+ * @param value0 TODO
+ * @exception IOException TODO
+ */
+ private void writeObject(ObjectOutputStream value0) throws IOException
+ {
+ // TODO
+ }
+
+ /**
+ * readObject
+ *
+ * @param value0 TODO
+ * @exception IOException TODO
+ * @exception ClassNotFoundException TODO
+ */
+ private void readObject(ObjectInputStream value0) throws IOException,
+ ClassNotFoundException
+ {
+ // TODO
+ }
+
+ /**
+ * Sets the RowMapper that should be used to map between paths and their rows.
+ *
+ * @param mapper the RowMapper to set
+ * @see RowMapper
+ */
+ public void setRowMapper(RowMapper mapper)
+ {
+ rowMapper = mapper;
+ resetRowSelection();
+ }
+
+ /**
+ * Returns the RowMapper that is currently used to map between paths and their
+ * rows.
+ *
+ * @return the current RowMapper
+ * @see RowMapper
+ */
+ public RowMapper getRowMapper()
+ {
+ return rowMapper;
+ }
+
+ /**
+ * Sets the current selection mode. Possible values are
+ * {@link #SINGLE_TREE_SELECTION}, {@link #CONTIGUOUS_TREE_SELECTION} and
+ * {@link #DISCONTIGUOUS_TREE_SELECTION}.
+ *
+ * @param mode the selection mode to be set
+ * @see #getSelectionMode
+ * @see #SINGLE_TREE_SELECTION
+ * @see #CONTIGUOUS_TREE_SELECTION
+ * @see #DISCONTIGUOUS_TREE_SELECTION
+ */
+ public void setSelectionMode(int mode)
+ {
+ int oldMode = selectionMode;
+ selectionMode = mode;
+ // Make sure we have a valid selection mode.
+ if (selectionMode != SINGLE_TREE_SELECTION
+ && selectionMode != CONTIGUOUS_TREE_SELECTION
+ && selectionMode != DISCONTIGUOUS_TREE_SELECTION)
+ selectionMode = DISCONTIGUOUS_TREE_SELECTION;
+
+ // Fire property change event.
+ if (oldMode != selectionMode && changeSupport != null)
+ changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY, oldMode,
+ selectionMode);
+ }
+
+ /**
+ * Returns the current selection mode.
+ *
+ * @return the current selection mode
+ * @see #setSelectionMode
+ * @see #SINGLE_TREE_SELECTION
+ * @see #CONTIGUOUS_TREE_SELECTION
+ * @see #DISCONTIGUOUS_TREE_SELECTION
+ */
+ public int getSelectionMode()
+ {
+ return selectionMode;
+ }
+
+ /**
+ * Sets this path as the only selection. If this changes the selection the
+ * registered TreeSelectionListeners are notified.
+ *
+ * @param path the path to set as selection
+ */
+ public void setSelectionPath(TreePath path)
+ {
+ TreePath[] paths = null;
+ if (path != null)
+ paths = new TreePath[]{ path };
+ setSelectionPaths(paths);
+ }
+
+ /**
+ * Get the number of the tree row for the given path.
+ *
+ * @param path the tree path
+ * @return the tree row for this path or -1 if the path is not visible.
+ */
+ int getRow(TreePath path)
+ {
+ RowMapper mapper = getRowMapper();
+
+ if (mapper instanceof AbstractLayoutCache)
+ {
+ // The absolute majority of cases, unless the TreeUI is very
+ // seriously rewritten
+ AbstractLayoutCache ama = (AbstractLayoutCache) mapper;
+ return ama.getRowForPath(path);
+ }
+ else if (mapper != null)
+ {
+ // Generic non optimized implementation.
+ int[] rows = mapper.getRowsForPaths(new TreePath[] { path });
+ if (rows.length == 0)
+ return - 1;
+ else
+ return rows[0];
+ }
+ return -1;
+ }
+
+ /**
+ * Sets the paths as selection. This method checks for duplicates and removes
+ * them. If this changes the selection the registered TreeSelectionListeners
+ * are notified.
+ *
+ * @param paths the paths to set as selection
+ */
+ public void setSelectionPaths(TreePath[] paths)
+ {
+ int oldLength = 0;
+ if (selection != null)
+ oldLength = selection.length;
+ int newLength = 0;
+ if (paths != null)
+ newLength = paths.length;
+ if (newLength > 0 || oldLength > 0)
+ {
+ // For SINGLE_TREE_SELECTION and for CONTIGUOUS_TREE_SELECTION with
+ // a non-contiguous path, we only allow the first path element.
+ if ((selectionMode == SINGLE_TREE_SELECTION && newLength > 1)
+ || (selectionMode == CONTIGUOUS_TREE_SELECTION && newLength > 0
+ && ! arePathsContiguous(paths)))
+ {
+ paths = new TreePath[] { paths[0] };
+ newLength = 1;
+ }
+ // Find new paths.
+ Vector<PathPlaceHolder> changedPaths = null;
+ tmpPaths.clear();
+ int validPaths = 0;
+ TreePath oldLeadPath = leadPath;
+ for (int i = 0; i < newLength; i++)
+ {
+ if (paths[i] != null && ! tmpPaths.contains(paths[i]))
+ {
+ validPaths++;
+ tmpPaths.add(paths[i]);
+ if (! selectedPaths.contains(paths[i]))
+ {
+ if (changedPaths == null)
+ changedPaths = new Vector<PathPlaceHolder>();
+ changedPaths.add(new PathPlaceHolder(paths[i], true));
+ }
+ leadPath = paths[i];
+ }
+ }
+ // Put together the new selection.
+ TreePath[] newSelection = null;
+ if (validPaths != 0)
+ {
+ if (validPaths != newLength)
+ {
+ // Some of the paths are already selected, put together
+ // the new selection carefully.
+ newSelection = new TreePath[validPaths];
+ Iterator<TreePath> newPaths = tmpPaths.iterator();
+ validPaths = 0;
+ for (int i = 0; newPaths.hasNext(); i++)
+ newSelection[i] = newPaths.next();
+ }
+ else
+ {
+ newSelection = new TreePath[paths.length];
+ System.arraycopy(paths, 0, newSelection, 0, paths.length);
+ }
+ }
+
+ // Find paths that have been selected, but are no more.
+ for (int i = 0; i < oldLength; i++)
+ {
+ if (selection[i] != null && ! tmpPaths.contains(selection[i]))
+ {
+ if (changedPaths == null)
+ changedPaths = new Vector<PathPlaceHolder>();
+ changedPaths.add(new PathPlaceHolder(selection[i], false));
+ }
+ }
+
+ // Perform changes and notification.
+ selection = newSelection;
+ HashSet<TreePath> tmp = selectedPaths;
+ selectedPaths = tmpPaths;
+ tmpPaths = tmp;
+ tmpPaths.clear();
+
+ // Not necessary, but required according to the specs and to tests.
+ if (selection != null)
+ insureUniqueness();
+ updateLeadIndex();
+ resetRowSelection();
+ if (changedPaths != null && changedPaths.size() > 0)
+ notifyPathChange(changedPaths, oldLeadPath);
+ }
+ }
+
+ /**
+ * Adds a path to the list of selected paths. This method checks if the path
+ * is already selected and doesn't add the same path twice. If this changes
+ * the selection the registered TreeSelectionListeners are notified.
+ *
+ * The lead path is changed to the added path. This also happen if the
+ * passed path was already selected before.
+ *
+ * @param path the path to add to the selection
+ */
+ public void addSelectionPath(TreePath path)
+ {
+ if (path != null)
+ {
+ TreePath[] add = new TreePath[]{ path };
+ addSelectionPaths(add);
+ }
+ }
+
+ /**
+ * Adds the paths to the list of selected paths. This method checks if the
+ * paths are already selected and doesn't add the same path twice. If this
+ * changes the selection the registered TreeSelectionListeners are notified.
+ *
+ * @param paths the paths to add to the selection
+ */
+ public void addSelectionPaths(TreePath[] paths)
+ {
+ int length = paths != null ? paths.length : 0;
+ if (length > 0)
+ {
+ if (selectionMode == SINGLE_TREE_SELECTION)
+ setSelectionPaths(paths);
+ else if (selectionMode == CONTIGUOUS_TREE_SELECTION
+ && ! canPathsBeAdded(paths))
+ {
+ if (arePathsContiguous(paths))
+ setSelectionPaths(paths);
+ else
+ setSelectionPaths(new TreePath[] { paths[0] });
+ }
+ else
+ {
+ Vector<PathPlaceHolder> changedPaths = null;
+ tmpPaths.clear();
+ int validPaths = 0;
+ TreePath oldLeadPath = leadPath;
+ int oldPaths = 0;
+ if (selection != null)
+ oldPaths = selection.length;
+ int i;
+ for (i = 0; i < length; i++)
+ {
+ if (paths[i] != null)
+ {
+ if (! selectedPaths.contains(paths[i]))
+ {
+ validPaths++;
+ if (changedPaths == null)
+ changedPaths = new Vector<PathPlaceHolder>();
+ changedPaths.add(new PathPlaceHolder(paths[i], true));
+ selectedPaths.add(paths[i]);
+ tmpPaths.add(paths[i]);
+ }
+ leadPath = paths[i];
+ }
+ }
+ if (validPaths > 0)
+ {
+ TreePath[] newSelection = new TreePath[oldPaths + validPaths];
+ if (oldPaths > 0)
+ System.arraycopy(selection, 0, newSelection, 0, oldPaths);
+ if (validPaths != paths.length)
+ {
+ // Some of the paths are already selected, put together
+ // the new selection carefully.
+ Iterator<TreePath> newPaths = tmpPaths.iterator();
+ i = oldPaths;
+ while (newPaths.hasNext())
+ {
+ newSelection[i] = newPaths.next();
+ i++;
+ }
+ }
+ else
+ System.arraycopy(paths, 0, newSelection, oldPaths,
+ validPaths);
+ selection = newSelection;
+ insureUniqueness();
+ updateLeadIndex();
+ resetRowSelection();
+ if (changedPaths != null && changedPaths.size() > 0)
+ notifyPathChange(changedPaths, oldLeadPath);
+ }
+ else
+ leadPath = oldLeadPath;
+ tmpPaths.clear();
+ }
+ }
+ }
+
+ /**
+ * Removes the path from the selection. If this changes the selection the
+ * registered TreeSelectionListeners are notified.
+ *
+ * @param path the path to remove
+ */
+ public void removeSelectionPath(TreePath path)
+ {
+ if (path != null)
+ removeSelectionPaths(new TreePath[]{ path });
+ }
+
+ /**
+ * Removes the paths from the selection. If this changes the selection the
+ * registered TreeSelectionListeners are notified.
+ *
+ * @param paths the paths to remove
+ */
+ public void removeSelectionPaths(TreePath[] paths)
+ {
+ if (paths != null && selection != null && paths.length > 0)
+ {
+ if (! canPathsBeRemoved(paths))
+ clearSelection();
+ else
+ {
+ Vector<PathPlaceHolder> pathsToRemove = null;
+ for (int i = paths.length - 1; i >= 0; i--)
+ {
+ if (paths[i] != null && selectedPaths.contains(paths[i]))
+ {
+ if (pathsToRemove == null)
+ pathsToRemove = new Vector<PathPlaceHolder>();
+ selectedPaths.remove(paths[i]);
+ pathsToRemove.add(new PathPlaceHolder(paths[i],
+ false));
+ }
+ }
+ if (pathsToRemove != null)
+ {
+ int numRemove = pathsToRemove.size();
+ TreePath oldLead = leadPath;
+ if (numRemove == selection.length)
+ selection = null;
+ else
+ {
+ selection = new TreePath[selection.length - numRemove];
+ Iterator<TreePath> keep = selectedPaths.iterator();
+ for (int valid = 0; keep.hasNext(); valid++)
+ selection[valid] = keep.next();
+ }
+ // Update lead path.
+ if (leadPath != null && ! selectedPaths.contains(leadPath))
+ {
+ if (selection != null)
+ leadPath = selection[selection.length - 1];
+ else
+ leadPath = null;
+ }
+ else if (selection != null)
+ leadPath = selection[selection.length - 1];
+ else
+ leadPath = null;
+ updateLeadIndex();
+ resetRowSelection();
+ notifyPathChange(pathsToRemove, oldLead);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the first path in the selection. This is especially useful when the
+ * selectionMode is {@link #SINGLE_TREE_SELECTION}.
+ *
+ * @return the first path in the selection
+ */
+ public TreePath getSelectionPath()
+ {
+ if ((selection == null) || (selection.length == 0))
+ return null;
+ else
+ return selection[0];
+ }
+
+ /**
+ * Returns the complete selection.
+ *
+ * @return the complete selection
+ */
+ public TreePath[] getSelectionPaths()
+ {
+ return selection;
+ }
+
+ /**
+ * Returns the number of paths in the selection.
+ *
+ * @return the number of paths in the selection
+ */
+ public int getSelectionCount()
+ {
+ if (selection == null)
+ return 0;
+ else
+ return selection.length;
+ }
+
+ /**
+ * Checks if a given path is in the selection.
+ *
+ * @param path the path to check
+ * @return <code>true</code> if the path is in the selection,
+ * <code>false</code> otherwise
+ */
+ public boolean isPathSelected(TreePath path)
+ {
+ if (selection == null)
+ return false;
+
+ for (int i = 0; i < selection.length; i++)
+ {
+ if (selection[i].equals(path))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the selection is empty.
+ *
+ * @return <code>true</code> if the selection is empty, <code>false</code>
+ * otherwise
+ */
+ public boolean isSelectionEmpty()
+ {
+ return (selection == null) || (selection.length == 0);
+ }
+
+ /**
+ * Removes all paths from the selection. Fire the unselection event.
+ */
+ public void clearSelection()
+ {
+ if (selection != null)
+ {
+ int selectionLength = selection.length;
+ boolean[] news = new boolean[selectionLength];
+ Arrays.fill(news, false);
+ TreeSelectionEvent event = new TreeSelectionEvent(this, selection,
+ news, leadPath,
+ null);
+ leadPath = null;
+ leadIndex = 0;
+ leadRow = 0;
+ selectedPaths.clear();
+ selection = null;
+ resetRowSelection();
+ fireValueChanged(event);
+ }
+ }
+
+ /**
+ * Adds a <code>TreeSelectionListener</code> object to this model.
+ *
+ * @param listener the listener to add
+ */
+ public void addTreeSelectionListener(TreeSelectionListener listener)
+ {
+ listenerList.add(TreeSelectionListener.class, listener);
+ }
+
+ /**
+ * Removes a <code>TreeSelectionListener</code> object from this model.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeTreeSelectionListener(TreeSelectionListener listener)
+ {
+ listenerList.remove(TreeSelectionListener.class, listener);
+ }
+
+ /**
+ * Returns all <code>TreeSelectionListener</code> added to this model.
+ *
+ * @return an array of listeners
+ * @since 1.4
+ */
+ public TreeSelectionListener[] getTreeSelectionListeners()
+ {
+ return (TreeSelectionListener[]) getListeners(TreeSelectionListener.class);
+ }
+
+ /**
+ * fireValueChanged
+ *
+ * @param event the event to fire.
+ */
+ protected void fireValueChanged(TreeSelectionEvent event)
+ {
+ TreeSelectionListener[] listeners = getTreeSelectionListeners();
+
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].valueChanged(event);
+ }
+
+ /**
+ * Returns all added listeners of a special type.
+ *
+ * @param listenerType the listener type
+ * @return an array of listeners
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Returns the currently selected rows.
+ *
+ * @return the currently selected rows
+ */
+ public int[] getSelectionRows()
+ {
+ int[] rows = null;
+ if (rowMapper != null && selection != null)
+ {
+ rows = rowMapper.getRowsForPaths(selection);
+ if (rows != null)
+ {
+ // Find invisible rows.
+ int invisible = 0;
+ for (int i = rows.length - 1; i >= 0; i--)
+ {
+ if (rows[i] == -1)
+ invisible++;
+
+ }
+ // Clean up invisible rows.
+ if (invisible > 0)
+ {
+ if (invisible == rows.length)
+ rows = null;
+ else
+ {
+ int[] newRows = new int[rows.length - invisible];
+ int visCount = 0;
+ for (int i = rows.length - 1; i >= 0; i--)
+ {
+ if (rows[i] != -1)
+ {
+ newRows[visCount] = rows[i];
+ visCount++;
+ }
+ }
+ rows = newRows;
+ }
+ }
+ }
+ }
+ return rows;
+ }
+
+ /**
+ * Returns the smallest row index from the selection.
+ *
+ * @return the smallest row index from the selection
+ */
+ public int getMinSelectionRow()
+ {
+ return listSelectionModel.getMinSelectionIndex();
+ }
+
+ /**
+ * Returns the largest row index from the selection.
+ *
+ * @return the largest row index from the selection
+ */
+ public int getMaxSelectionRow()
+ {
+ return listSelectionModel.getMaxSelectionIndex();
+ }
+
+ /**
+ * Checks if a particular row is selected.
+ *
+ * @param row the index of the row to check
+ * @return <code>true</code> if the row is in this selection,
+ * <code>false</code> otherwise
+ * @throws NullPointerException if the row mapper is not set (can only happen
+ * if the user has plugged in the custom incorrect TreeUI
+ * implementation.
+ */
+ public boolean isRowSelected(int row)
+ {
+ return listSelectionModel.isSelectedIndex(row);
+ }
+
+ /**
+ * Updates the mappings from TreePaths to row indices.
+ */
+ public void resetRowSelection()
+ {
+ listSelectionModel.clearSelection();
+ if (selection != null && rowMapper != null)
+ {
+ int[] rows = rowMapper.getRowsForPaths(selection);
+ // Update list selection model.
+ for (int i = 0; i < rows.length; i++)
+ {
+ int row = rows[i];
+ if (row != -1)
+ listSelectionModel.addSelectionInterval(row, row);
+ }
+ // Update lead selection.
+ if (leadIndex != -1 && rows != null)
+ leadRow = rows[leadIndex];
+ else if (leadPath != null)
+ {
+ TreePath[] tmp = new TreePath[]{ leadPath };
+ rows = rowMapper.getRowsForPaths(tmp);
+ leadRow = rows != null ? rows[0] : -1;
+ }
+ else
+ leadRow = -1;
+ insureRowContinuity();
+ }
+ else
+ leadRow = -1;
+ }
+
+ /**
+ * getLeadSelectionRow
+ *
+ * @return int
+ */
+ public int getLeadSelectionRow()
+ {
+ return leadRow;
+ }
+
+ /**
+ * getLeadSelectionPath
+ *
+ * @return TreePath
+ */
+ public TreePath getLeadSelectionPath()
+ {
+ return leadPath;
+ }
+
+ /**
+ * Adds a <code>PropertyChangeListener</code> object to this model.
+ *
+ * @param listener the listener to add.
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ if (changeSupport == null)
+ changeSupport = new SwingPropertyChangeSupport(this);
+ changeSupport.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Removes a <code>PropertyChangeListener</code> object from this model.
+ *
+ * @param listener the listener to remove.
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ if (changeSupport != null)
+ changeSupport.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Returns all added <code>PropertyChangeListener</code> objects.
+ *
+ * @return an array of listeners.
+ * @since 1.4
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners()
+ {
+ PropertyChangeListener[] listeners = null;
+ if (changeSupport != null)
+ listeners = changeSupport.getPropertyChangeListeners();
+ else
+ listeners = new PropertyChangeListener[0];
+ return listeners;
+ }
+
+ /**
+ * Makes sure the currently selected paths are valid according to the current
+ * selectionMode. If the selectionMode is set to
+ * {@link #CONTIGUOUS_TREE_SELECTION} and the selection isn't contiguous then
+ * the selection is reset to the first set of contguous paths. If the
+ * selectionMode is set to {@link #SINGLE_TREE_SELECTION} and the selection
+ * has more than one path, the selection is reset to the contain only the
+ * first path.
+ */
+ protected void insureRowContinuity()
+ {
+ if (selectionMode == CONTIGUOUS_TREE_SELECTION && selection != null
+ && rowMapper != null)
+ {
+ int min = listSelectionModel.getMinSelectionIndex();
+ if (min != -1)
+ {
+ int max = listSelectionModel.getMaxSelectionIndex();
+ for (int i = min; i <= max; i++)
+ {
+ if (! listSelectionModel.isSelectedIndex(i))
+ {
+ if (i == min)
+ clearSelection();
+ else
+ {
+ TreePath[] newSelection = new TreePath[i - min];
+ int[] rows = rowMapper.getRowsForPaths(selection);
+ for (int j = 0; j < rows.length; j++)
+ {
+ if (rows[j] < i)
+ newSelection[rows[j] - min] = selection[j];
+ }
+ setSelectionPaths(newSelection);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else if (selectionMode == SINGLE_TREE_SELECTION && selection != null
+ && selection.length > 1)
+ setSelectionPath(selection[0]);
+ }
+
+ /**
+ * Returns <code>true</code> if the paths are contiguous (take subsequent
+ * rows in the diplayed tree view. The method returns <code>true</code> if
+ * we have no RowMapper assigned.
+ *
+ * @param paths the paths to check for continuity
+ * @return <code>true</code> if the paths are contiguous or we have no
+ * RowMapper assigned
+ */
+ protected boolean arePathsContiguous(TreePath[] paths)
+ {
+ if (rowMapper == null || paths.length < 2)
+ return true;
+
+ int length = paths.length;
+ TreePath[] tmp = new TreePath[1];
+ tmp[0] = paths[0];
+ int min = rowMapper.getRowsForPaths(tmp)[0];
+ BitSet selected = new BitSet();
+ int valid = 0;
+ for (int i = 0; i < length; i++)
+ {
+ if (paths[i] != null)
+ {
+ tmp[0] = paths[i];
+ int[] rows = rowMapper.getRowsForPaths(tmp);
+ if (rows == null)
+ return false; // No row mapping yet, can't be selected.
+ int row = rows[0];
+ if (row == -1 || row < (min - length) || row > (min + length))
+ return false; // Not contiguous.
+ min = Math.min(min, row);
+ if (! selected.get(row))
+ {
+ selected.set(row);
+ valid++;
+ }
+
+ }
+ }
+ int max = valid + min;
+ for (int i = min; i < max; i++)
+ if (! selected.get(i))
+ return false; // Not contiguous.
+ return true;
+ }
+
+ /**
+ * Checks if the paths can be added. This returns <code>true</code> if:
+ * <ul>
+ * <li><code>paths</code> is <code>null</code> or empty</li>
+ * <li>we have no RowMapper assigned</li>
+ * <li>nothing is currently selected</li>
+ * <li>selectionMode is {@link #DISCONTIGUOUS_TREE_SELECTION}</li>
+ * <li>adding the paths to the selection still results in a contiguous set of
+ * paths</li>
+ *
+ * @param paths the paths to check
+ * @return <code>true</code> if the paths can be added with respect to the
+ * selectionMode
+ */
+ protected boolean canPathsBeAdded(TreePath[] paths)
+ {
+ if (paths == null || paths.length == 0 || rowMapper == null
+ || selection == null || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
+ return true;
+
+ BitSet selected = new BitSet();
+ int min = listSelectionModel.getMinSelectionIndex();
+ int max = listSelectionModel.getMaxSelectionIndex();
+ TreePath[] tmp = new TreePath[1];
+ if (min != -1)
+ {
+ // Set the bitmask of selected elements.
+ for (int i = min; i <= max; i++)
+ selected.set(i);
+ }
+ else
+ {
+ tmp[0] = paths[0];
+ min = rowMapper.getRowsForPaths(tmp)[0];
+ max = min;
+ }
+ // Mark new paths as selected.
+ for (int i = paths.length - 1; i >= 0; i--)
+ {
+ if (paths[i] != null)
+ {
+ tmp[0] = paths[i];
+ int[] rows = rowMapper.getRowsForPaths(tmp);
+ if (rows == null)
+ return false; // Now row mapping yet, can't be selected.
+ int row = rows[0];
+ if (row == -1)
+ return false; // Now row mapping yet, can't be selected.
+ min = Math.min(min, row);
+ max = Math.max(max, row);
+ selected.set(row);
+ }
+ }
+ // Now look if the new selection would be contiguous.
+ for (int i = min; i <= max; i++)
+ if (! selected.get(i))
+ return false;
+ return true;
+ }
+
+ /**
+ * Checks if the paths can be removed without breaking the continuity of the
+ * selection according to selectionMode.
+ *
+ * @param paths the paths to check
+ * @return <code>true</code> if the paths can be removed with respect to the
+ * selectionMode
+ */
+ protected boolean canPathsBeRemoved(TreePath[] paths)
+ {
+ if (rowMapper == null || isSelectionEmpty()
+ || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
+ return true;
+
+ HashSet<TreePath> set = new HashSet<TreePath>();
+ for (int i = 0; i < selection.length; i++)
+ set.add(selection[i]);
+
+ for (int i = 0; i < paths.length; i++)
+ set.remove(paths[i]);
+
+ TreePath[] remaining = new TreePath[set.size()];
+ Iterator<TreePath> iter = set.iterator();
+
+ for (int i = 0; i < remaining.length; i++)
+ remaining[i] = iter.next();
+
+ return arePathsContiguous(remaining);
+ }
+
+ /**
+ * Notify the installed listeners that the given patches have changed. This
+ * method will call listeners if invoked, but it is not called from the
+ * implementation of this class.
+ *
+ * @param vPaths the vector of the changed patches
+ * @param oldLeadSelection the old selection index
+ */
+ protected void notifyPathChange(Vector<PathPlaceHolder> vPaths,
+ TreePath oldLeadSelection)
+ {
+
+ int numChangedPaths = vPaths.size();
+ boolean[] news = new boolean[numChangedPaths];
+ TreePath[] paths = new TreePath[numChangedPaths];
+ for (int i = 0; i < numChangedPaths; i++)
+ {
+ PathPlaceHolder p = vPaths.get(i);
+ news[i] = p.isNew;
+ paths[i] = p.path;
+ }
+
+ TreeSelectionEvent event = new TreeSelectionEvent(this, paths, news,
+ oldLeadSelection,
+ leadPath);
+ fireValueChanged(event);
+ }
+
+ /**
+ * Updates the lead selection row number after changing the lead selection
+ * path.
+ */
+ protected void updateLeadIndex()
+ {
+ leadIndex = -1;
+ if (leadPath != null)
+ {
+ leadRow = -1;
+ if (selection == null)
+ leadPath = null;
+ else
+ {
+ for (int i = selection.length - 1; i >= 0 && leadIndex == -1; i--)
+ {
+ if (selection[i] == leadPath)
+ leadIndex = i;
+ }
+ }
+ }
+ }
+
+ /**
+ * This method exists due historical reasons and returns without action
+ * (unless overridden). For compatibility with the applications that override
+ * it, it is still called from the {@link #setSelectionPaths(TreePath[])} and
+ * {@link #addSelectionPaths(TreePath[])}.
+ */
+ protected void insureUniqueness()
+ {
+ // Following the API 1.4, the method should return without action.
+ }
+}
diff --git a/libjava/classpath/javax/swing/tree/ExpandVetoException.java b/libjava/classpath/javax/swing/tree/ExpandVetoException.java
new file mode 100644
index 000000000..fd6330cf7
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/ExpandVetoException.java
@@ -0,0 +1,76 @@
+/* ExpandVetoException.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import javax.swing.event.TreeExpansionEvent;
+
+/**
+ * ExpandVetoException
+ * @author Andrew Selkirk
+ */
+public class ExpandVetoException extends Exception
+{
+
+ /**
+ * event
+ */
+ protected TreeExpansionEvent event;
+
+
+ /**
+ * Constructor ExpandVetoException
+ * @param event Tree Expansion Event
+ */
+ public ExpandVetoException(TreeExpansionEvent event)
+ {
+ super();
+ this.event = event;
+ }
+
+ /**
+ * Constructor ExpandVetoException
+ * @param event Tree Expansion Event
+ * @param message Message
+ */
+ public ExpandVetoException(TreeExpansionEvent event, String message)
+ {
+ super(message);
+ this.event = event;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java b/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java
new file mode 100644
index 000000000..6ff2e3595
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java
@@ -0,0 +1,628 @@
+/* FixedHeightLayoutCache.java -- Fixed cell height tree layout cache
+Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import gnu.javax.swing.tree.GnuPath;
+
+import java.awt.Rectangle;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.swing.event.TreeModelEvent;
+
+
+/**
+ * The fixed height tree layout. This class assumes that all cells in the tree
+ * have the same fixed height. This may be not the case, for instance, if leaves
+ * and branches have different height, of if the tree rows may have arbitrary
+ * variable height. This class will also work if the NodeDimensions are not
+ * set.
+ *
+ * @author Audrius Meskauskas
+ * @author Andrew Selkirk
+ */
+public class FixedHeightLayoutCache
+ extends VariableHeightLayoutCache
+{
+ /**
+ * The cached node record.
+ */
+ class NodeRecord
+ {
+ NodeRecord(int aRow, int aDepth, Object aNode, Object aParent)
+ {
+ row = aRow;
+ depth = aDepth;
+ parent = aParent;
+ node = aNode;
+
+ isExpanded = expanded.contains(aNode);
+ }
+
+ /**
+ * The row, where the tree node is displayed.
+ */
+ final int row;
+
+ /**
+ * The nesting depth
+ */
+ final int depth;
+
+ /**
+ * The parent of the given node, null for the root node.
+ */
+ final Object parent;
+
+ /**
+ * This node.
+ */
+ final Object node;
+
+ /**
+ * True for the expanded nodes. The value is calculated in constructor.
+ * Using this field saves one hashtable access operation.
+ */
+ final boolean isExpanded;
+
+ /**
+ * The cached bounds of the tree row.
+ */
+ Rectangle bounds;
+
+ /**
+ * The path from the tree top to the given node (computed under first
+ * demand)
+ */
+ private TreePath path;
+
+ /**
+ * Get the path for this node. The derived class is returned,
+ * making check for the last child of some parent easier.
+ */
+ TreePath getPath()
+ {
+ if (path == null)
+ {
+ boolean lastChild = false;
+ if (parent != null)
+ {
+ int nc = treeModel.getChildCount(parent);
+ if (nc > 0)
+ {
+ int n = treeModel.getIndexOfChild(parent, node);
+ if (n == nc - 1)
+ lastChild = true;
+ }
+ }
+
+ LinkedList<Object> lpath = new LinkedList<Object>();
+ NodeRecord rp = this;
+ while (rp != null)
+ {
+ lpath.addFirst(rp.node);
+ if (rp.parent != null)
+ {
+ Object parent = rp.parent;
+ rp = (NodeRecord) nodes.get(parent);
+ // Add the root node, even if it is not visible.
+ if (rp == null)
+ lpath.addFirst(parent);
+ }
+ else
+ rp = null;
+ }
+ path = new GnuPath(lpath.toArray(), lastChild);
+ }
+ return path;
+ }
+
+ /**
+ * Get the rectangle bounds (compute, if required).
+ */
+ Rectangle getBounds()
+ {
+ // This method may be called in the context when the tree rectangle is
+ // not known. To work around this, it is assumed near infinitely large.
+ if (bounds == null)
+ bounds = getNodeDimensions(node, row, depth, isExpanded,
+ new Rectangle());
+ return bounds;
+ }
+ }
+
+ /**
+ * The set of all expanded tree nodes.
+ */
+ Set<Object> expanded = new HashSet<Object>();
+
+ /**
+ * Maps nodes to the row numbers.
+ */
+ Hashtable<Object,NodeRecord> nodes = new Hashtable<Object,NodeRecord>();
+
+ /**
+ * Maps row numbers to nodes.
+ */
+ Hashtable<Integer,Object> row2node = new Hashtable<Integer,Object>();
+
+ /**
+ * If true, the row map must be recomputed before using.
+ */
+ boolean dirty;
+
+ /**
+ * The cumulative height of all rows.
+ */
+ int totalHeight;
+
+ /**
+ * The maximal width.
+ */
+ int maximalWidth;
+
+ /**
+ * Creates the unitialised instance. Before using the class, the row height
+ * must be set with the {@link #setRowHeight(int)} and the model must be set
+ * with {@link #setModel(TreeModel)}. The node dimensions may not be set.
+ */
+ public FixedHeightLayoutCache()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Get the total number of rows in the tree. Every displayed node occupies the
+ * single row. The root node row is included if the root node is set as
+ * visible (false by default).
+ *
+ * @return int the number of the displayed rows.
+ */
+ public int getRowCount()
+ {
+ if (dirty) update();
+ return row2node.size();
+ }
+
+ /**
+ * Refresh the row map.
+ */
+ private final void update()
+ {
+ nodes.clear();
+ row2node.clear();
+
+ totalHeight = maximalWidth = 0;
+
+ Object root = treeModel.getRoot();
+
+ if (rootVisible)
+ {
+ countRows(root, null, 0);
+ }
+ else
+ {
+ int sc = treeModel.getChildCount(root);
+ for (int i = 0; i < sc; i++)
+ {
+ Object child = treeModel.getChild(root, i);
+ countRows(child, root, 0);
+ }
+ }
+ dirty = false;
+ }
+
+ /**
+ * Recursively counts all rows in the tree.
+ */
+ private final void countRows(Object node, Object parent, int depth)
+ {
+ Integer n = new Integer(row2node.size());
+ row2node.put(n, node);
+
+ NodeRecord nr = new NodeRecord(n.intValue(), depth, node, parent);
+ nodes.put(node, nr);
+
+ // For expanded nodes and for the root node.
+ if (expanded.contains(node))
+ {
+ int sc = treeModel.getChildCount(node);
+ int deeper = depth + 1;
+ for (int i = 0; i < sc; i++)
+ {
+ Object child = treeModel.getChild(node, i);
+ countRows(child, node, deeper);
+ }
+ }
+ }
+
+ /**
+ * Discard the bound information for the given path.
+ *
+ * @param path the path, for that the bound information must be recomputed.
+ */
+ public void invalidatePathBounds(TreePath path)
+ {
+ NodeRecord r = (NodeRecord) nodes.get(path.getLastPathComponent());
+ if (r != null)
+ r.bounds = null;
+ }
+
+ /**
+ * Mark all cached information as invalid.
+ */
+ public void invalidateSizes()
+ {
+ dirty = true;
+ }
+
+ /**
+ * Set the expanded state of the given path. The expansion states must be
+ * always updated when expanding and colapsing the tree nodes. Otherwise
+ * other methods will not work correctly after the nodes are collapsed or
+ * expanded.
+ *
+ * @param path the tree path, for that the state is being set.
+ * @param isExpanded the expanded state of the given path.
+ */
+ public void setExpandedState(TreePath path, boolean isExpanded)
+ {
+ if (isExpanded)
+ expanded.add(path.getLastPathComponent());
+ else
+ expanded.remove(path.getLastPathComponent());
+
+ dirty = true;
+ }
+
+ /**
+ * Get the expanded state for the given tree path.
+ *
+ * @return true if the given path is expanded, false otherwise.
+ */
+ public boolean isExpanded(TreePath path)
+ {
+ return expanded.contains(path.getLastPathComponent());
+ }
+
+ /**
+ * Get bounds for the given tree path.
+ *
+ * @param path the tree path
+ * @param rect the rectangle that will be reused to return the result.
+ * @return Rectangle the bounds of the last line, defined by the given path.
+ */
+ public Rectangle getBounds(TreePath path, Rectangle rect)
+ {
+ if (path == null)
+ return null;
+ if (dirty)
+ update();
+ Object last = path.getLastPathComponent();
+ NodeRecord r = nodes.get(last);
+ if (r == null)
+ // This node is not visible.
+ {
+ rect.x = rect.y = rect.width = rect.height = 0;
+ }
+ else
+ {
+ if (r.bounds == null)
+ {
+ Rectangle dim = getNodeDimensions(last, r.row, r.depth,
+ r.isExpanded, rect);
+ r.bounds = dim;
+ }
+
+ rect.setRect(r.bounds);
+ }
+ return rect;
+ }
+
+ /**
+ * Get the path, the last element of that is displayed in the given row.
+ *
+ * @param row the row
+ * @return TreePath the path
+ */
+ public TreePath getPathForRow(int row)
+ {
+ if (dirty)
+ update();
+ Object last = row2node.get(new Integer(row));
+ if (last == null)
+ return null;
+ else
+ {
+ NodeRecord r = nodes.get(last);
+ return r.getPath();
+ }
+ }
+
+ /**
+ * Get the row, displaying the last node of the given path.
+ *
+ * @param path the path
+ * @return int the row number or -1 if the end of the path is not visible.
+ */
+ public int getRowForPath(TreePath path)
+ {
+ if (path == null)
+ return -1;
+
+ if (dirty) update();
+
+ NodeRecord r = nodes.get(path.getLastPathComponent());
+ if (r == null)
+ return - 1;
+ else
+ return r.row;
+ }
+
+ /**
+ * Get the path, closest to the given point.
+ *
+ * @param x the point x coordinate
+ * @param y the point y coordinate
+ * @return the tree path, closest to the the given point
+ */
+ public TreePath getPathClosestTo(int x, int y)
+ {
+ if (dirty)
+ update();
+
+ // As the rows have arbitrary height, we need to iterate.
+ NodeRecord best = null;
+ NodeRecord r;
+ Enumeration<NodeRecord> en = nodes.elements();
+
+ int dist = Integer.MAX_VALUE;
+
+ while (en.hasMoreElements() && dist > 0)
+ {
+ r = en.nextElement();
+ if (best == null)
+ {
+ best = r;
+ dist = distance(r.getBounds(), x, y);
+ }
+ else
+ {
+ int rr = distance(r.getBounds(), x, y);
+ if (rr < dist)
+ {
+ best = r;
+ dist = rr;
+ }
+ }
+ }
+
+ if (best == null)
+ return null;
+ else
+ return best.getPath();
+ }
+
+ /**
+ * Get the closest distance from this point till the given rectangle. Only
+ * vertical distance is taken into consideration.
+ */
+ int distance(Rectangle r, int x, int y)
+ {
+ if (y < r.y)
+ return r.y - y;
+ else if (y > r.y + r.height)
+ return y - (r.y + r.height);
+ else
+ return 0;
+ }
+
+ /**
+ * Get the number of the visible childs for the given tree path. If the node
+ * is not expanded, 0 is returned. Otherwise, the number of children is
+ * obtained from the model as the number of children for the last path
+ * component.
+ *
+ * @param path the tree path
+ * @return int the number of the visible childs (for row).
+ */
+ public int getVisibleChildCount(TreePath path)
+ {
+ if (isExpanded(path))
+ return 0;
+ else
+ return treeModel.getChildCount(path.getLastPathComponent());
+ }
+
+ /**
+ * Get the enumeration over all visible paths that start from the given
+ * parent path.
+ *
+ * @param parentPath the parent path
+ * @return the enumeration over pathes
+ */
+ public Enumeration<TreePath> getVisiblePathsFrom(TreePath parentPath)
+ {
+ if (dirty)
+ update();
+ Vector<TreePath> p = new Vector<TreePath>(parentPath.getPathCount());
+ Object node;
+ NodeRecord nr;
+
+ for (int i = 0; i < parentPath.getPathCount(); i++)
+ {
+ node = parentPath.getPathComponent(i);
+ nr = nodes.get(node);
+ if (nr.row >= 0)
+ p.add((TreePath) node);
+ }
+ return p.elements();
+ }
+
+ /**
+ * Return the expansion state of the given tree path. The expansion state
+ * must be previously set with the
+ * {@link #setExpandedState(TreePath, boolean)}
+ *
+ * @param path the path being checked
+ * @return true if the last node of the path is expanded, false otherwise.
+ */
+ public boolean getExpandedState(TreePath path)
+ {
+ return expanded.contains(path.getLastPathComponent());
+ }
+
+ /**
+ * The listener method, called when the tree nodes are changed.
+ *
+ * @param event the change event
+ */
+ public void treeNodesChanged(TreeModelEvent event)
+ {
+ dirty = true;
+ }
+
+ /**
+ * The listener method, called when the tree nodes are inserted.
+ *
+ * @param event the change event
+ */
+ public void treeNodesInserted(TreeModelEvent event)
+ {
+ dirty = true;
+ }
+
+ /**
+ * The listener method, called when the tree nodes are removed.
+ *
+ * @param event the change event
+ */
+ public void treeNodesRemoved(TreeModelEvent event)
+ {
+ dirty = true;
+ }
+
+ /**
+ * Called when the tree structure has been changed.
+ *
+ * @param event the change event
+ */
+ public void treeStructureChanged(TreeModelEvent event)
+ {
+ dirty = true;
+ }
+
+ /**
+ * Set the tree model that will provide the data.
+ */
+ public void setModel(TreeModel newModel)
+ {
+ treeModel = newModel;
+ // The root node is expanded by default.
+ expanded.add(treeModel.getRoot());
+ dirty = true;
+ }
+
+ /**
+ * Inform the instance if the tree root node is visible. If this method
+ * is not called, it is assumed that the tree root node is not visible.
+ *
+ * @param visible true if the tree root node is visible, false
+ * otherwise.
+ */
+ public void setRootVisible(boolean visible)
+ {
+ rootVisible = visible;
+ dirty = true;
+ }
+
+ /**
+ * Get the sum of heights for all rows.
+ */
+ public int getPreferredHeight()
+ {
+ if (dirty)
+ update();
+ totalHeight = 0;
+ Enumeration<NodeRecord> en = nodes.elements();
+ while (en.hasMoreElements())
+ {
+ NodeRecord nr = en.nextElement();
+ Rectangle r = nr.getBounds();
+ totalHeight += r.height;
+ }
+ return totalHeight;
+ }
+
+ /**
+ * Get the maximal width.
+ */
+ public int getPreferredWidth(Rectangle value)
+ {
+ if (dirty)
+ update();
+
+ maximalWidth = 0;
+ Enumeration<NodeRecord> en = nodes.elements();
+ while (en.hasMoreElements())
+ {
+ NodeRecord nr = en.nextElement();
+ Rectangle r = nr.getBounds();
+ if (r.x + r.width > maximalWidth)
+ maximalWidth = r.x + r.width;
+ }
+ return maximalWidth;
+ }
+
+ /**
+ * Returns true if this layout supposes that all rows have the fixed
+ * height.
+ *
+ * @return boolean true if all rows in the tree must have the fixed
+ * height (true by default).
+ */
+ protected boolean isFixedRowHeight()
+ {
+ return true;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/tree/MutableTreeNode.java b/libjava/classpath/javax/swing/tree/MutableTreeNode.java
new file mode 100644
index 000000000..4a0f4b69f
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/MutableTreeNode.java
@@ -0,0 +1,104 @@
+/* MutableTreeNode.java --
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+/**
+ * MutableTreeNode public interface
+
+ * @author Andrew Selkirk
+ */
+public interface MutableTreeNode extends TreeNode
+{
+ /**
+ * Inserts a node as child at a given index.
+ *
+ * @param child the note to insert
+ * @param index the index
+ *
+ * @see #remove(int)
+ * @see #remove(MutableTreeNode)
+ * @see #setParent(MutableTreeNode)
+ */
+ void insert(MutableTreeNode child, int index);
+
+ /**
+ * Removes the child node a given index.
+ *
+ * @param index the index
+ *
+ * @see #insert(MutableTreeNode,int)
+ * @see #remove(MutableTreeNode)
+ * @see #removeFromParent()
+ */
+ void remove(int index);
+
+ /**
+ * Removes a given child node.
+ *
+ * @param node the node to remove
+ *
+ * @see #insert(MutableTreeNode,int)
+ * @see #remove(int)
+ * @see #removeFromParent()
+ */
+ void remove(MutableTreeNode node);
+
+ /**
+ * Sets a user object, the data represented by the node.
+ *
+ * @param object the data
+ */
+ void setUserObject(Object object);
+
+ /**
+ * Removes this node from its parent.
+ *
+ * @see #remove(int)
+ * @see #remove(MutableTreeNode)
+ */
+ void removeFromParent();
+
+ /**
+ * Sets the parent of the node.
+ *
+ * @param parent the parent
+ *
+ * @see #insert(MutableTreeNode,int)
+ */
+ void setParent(MutableTreeNode parent);
+}
diff --git a/libjava/classpath/javax/swing/tree/RowMapper.java b/libjava/classpath/javax/swing/tree/RowMapper.java
new file mode 100644
index 000000000..d62b703c6
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/RowMapper.java
@@ -0,0 +1,54 @@
+/* RowMapper.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+/**
+ * RowMapper public interface
+ * @author Andrew Selkirk
+ */
+public interface RowMapper
+{
+
+ /**
+ * getRowsForPaths
+ * @param path TreePath
+ * @return TODO
+ */
+ int[] getRowsForPaths(TreePath[] path);
+
+}
diff --git a/libjava/classpath/javax/swing/tree/TreeCellEditor.java b/libjava/classpath/javax/swing/tree/TreeCellEditor.java
new file mode 100644
index 000000000..614f98df8
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/TreeCellEditor.java
@@ -0,0 +1,71 @@
+/* TreeCellEditor.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import java.awt.Component;
+
+import javax.swing.CellEditor;
+import javax.swing.JTree;
+
+/**
+ * A <code>TreeCellEditor</code> is used by the {@link JTree} component to
+ * edit individual tree elements (nodes).
+ *
+ * @author Andrew Selkirk
+ */
+public interface TreeCellEditor extends CellEditor
+{
+ /**
+ * Returns a component that has been configured to edit one element (or
+ * node) in a {@link JTree} component. The arguments to this method are used
+ * to pass in the value and state of the element to be edited.
+ *
+ * @param tree the tree.
+ * @param value the value to render.
+ * @param isSelected is the tree element selected?
+ * @param expanded is the tree element expanded?
+ * @param leaf is the tree element a leaf node?
+ * @param row the row index.
+ *
+ * @return A component that is configured for editing the tree element.
+ */
+ Component getTreeCellEditorComponent(JTree tree, Object value,
+ boolean isSelected, boolean expanded,
+ boolean leaf, int row);
+}
diff --git a/libjava/classpath/javax/swing/tree/TreeCellRenderer.java b/libjava/classpath/javax/swing/tree/TreeCellRenderer.java
new file mode 100644
index 000000000..fdfc0a116
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/TreeCellRenderer.java
@@ -0,0 +1,73 @@
+/* TreeCellRenderer.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import java.awt.Component;
+
+import javax.swing.JTree;
+
+/**
+ * A <code>TreeCellRenderer</code> is used by the {@link JTree} component to
+ * paint individual tree elements (nodes).
+ *
+ * @author Andrew Selkirk
+ */
+public interface TreeCellRenderer
+{
+ /**
+ * Returns a component that has been configured to display one element (or
+ * node) in a {@link JTree} component. The arguments to this method are used
+ * to pass in the value and state of the element to be rendered.
+ *
+ * @param tree the tree.
+ * @param value the value to render.
+ * @param selected is the tree element selected?
+ * @param expanded is the tree element expanded?
+ * @param leaf is the tree element a leaf node?
+ * @param row the row index.
+ * @param hasFocus does the tree element have the focus?
+ *
+ * @return A component that is configured for rendering the tree element.
+ */
+ Component getTreeCellRendererComponent(JTree tree, Object value,
+ boolean selected, boolean expanded,
+ boolean leaf, int row,
+ boolean hasFocus);
+
+}
diff --git a/libjava/classpath/javax/swing/tree/TreeModel.java b/libjava/classpath/javax/swing/tree/TreeModel.java
new file mode 100644
index 000000000..ec1884efd
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/TreeModel.java
@@ -0,0 +1,105 @@
+/* TreeModel.java --
+ Copyright (C) 2002 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import javax.swing.event.TreeModelListener;
+
+/**
+ * TreeModel public interface
+ * @author Andrew Selkirk
+ */
+public interface TreeModel
+{
+ /**
+ * getRoot
+ * @returns Object
+ */
+ Object getRoot();
+
+ /**
+ * getChild
+ * @param parent TODO
+ * @param index TODO
+ * @returns Object
+ */
+ Object getChild(Object parent, int index);
+
+ /**
+ * getChildCount
+ * @param parent TODO
+ * @returns int
+ */
+ int getChildCount(Object parent);
+
+ /**
+ * isLeaf
+ * @param node TODO
+ * @returns boolean
+ */
+ boolean isLeaf(Object node);
+
+ /**
+ * valueForPathChanged
+ * @param path TODO
+ * @param newvalue TODO
+ */
+ void valueForPathChanged(TreePath path, Object newvalue);
+
+ /**
+ * getIndexOfChild
+ * @param parent TODO
+ * @param child TODO
+ * @returns int
+ */
+ int getIndexOfChild(Object parent, Object child);
+
+ /**
+ * addTreeModelListener
+ * @param listener TODO
+ */
+ void addTreeModelListener(TreeModelListener listener);
+
+ /**
+ * removeTreeModelListener
+ * @param listener TODO
+ */
+ void removeTreeModelListener(TreeModelListener listener);
+
+
+}
diff --git a/libjava/classpath/javax/swing/tree/TreeNode.java b/libjava/classpath/javax/swing/tree/TreeNode.java
new file mode 100644
index 000000000..b68b498a7
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/TreeNode.java
@@ -0,0 +1,113 @@
+/* TreeNode.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import java.util.Enumeration;
+
+/**
+ * A tree node.
+ *
+ * @author Andrew Selkirk
+ */
+public interface TreeNode
+{
+
+ /**
+ * Returns the parent node for this tree node, or <code>null</code> if this
+ * node has no parent.
+ *
+ * @return The parent node (possibly <code>null</code>).
+ */
+ TreeNode getParent();
+
+ /**
+ * Returns the index of the specified child node, or -1 if the node is not
+ * in fact a child of this node.
+ *
+ * @param node the node (<code>null</code> not permitted).
+ *
+ * @return The index of the specified child node, or -1.
+ *
+ * @throws IllegalArgumentException if <code>node</code> is <code>null</code>.
+ */
+ int getIndex(TreeNode node);
+
+ /**
+ * Returns the child node at the given index.
+ *
+ * @param index the index (in the range <code>0</code> to
+ * <code>getChildCount() - 1</code>).
+ *
+ * @return The child node at the given index.
+ */
+ TreeNode getChildAt(int index);
+
+ /**
+ * Returns the number of children for this node.
+ *
+ * @return The number of children for this node.
+ */
+ int getChildCount();
+
+ /**
+ * Returns <code>true</code> if this node allows children, and
+ * <code>false</code> otherwise.
+ *
+ * @return A boolean.
+ */
+ boolean getAllowsChildren();
+
+ /**
+ * Returns <code>true</code> if this node is a leaf node, and
+ * <code>false</code> otherwise.
+ *
+ * @return A boolean.
+ */
+ boolean isLeaf();
+
+ /**
+ * Returns an enumeration of the children of this node, or an empty
+ * enumeration if this node has no children.
+ *
+ * @return An enumeration of the children of this node.
+ */
+ @SuppressWarnings("unchecked") // Required for API compatibility
+ Enumeration children();
+
+}
diff --git a/libjava/classpath/javax/swing/tree/TreePath.java b/libjava/classpath/javax/swing/tree/TreePath.java
new file mode 100644
index 000000000..764970696
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/TreePath.java
@@ -0,0 +1,312 @@
+/* TreePath.java --
+ Copyright (C) 2002, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * A <code>TreePath</code> represents a sequence of tree elements that form
+ * a path starting from the root of a tree. A tree element can be represented
+ * by any {@link Object}.
+ *
+ * @author Andrew Selkirk
+ */
+public class TreePath implements Serializable
+{
+ static final long serialVersionUID = 4380036194768077479L;
+
+ /**
+ * The actual patch. The {@link DefaultTreeSelectionModel#clone()}
+ * assumes that the TreePath is immutable, so it is marked final here.
+ */
+ private final Object[] path;
+
+ /**
+ * The parent path (to be reused).
+ */
+ private transient TreePath parentPath;
+
+
+ /**
+ * Creates a path from the list of objects representing tree elements. The
+ * incoming array is copied so that subsequent changes do not affect this
+ * tree path.
+ *
+ * @param path the elements in the path (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>path</code> is <code>null</code>.
+ */
+ public TreePath(Object[] path)
+ {
+ if (path == null)
+ throw new IllegalArgumentException("Null 'path' not permitted.");
+ this.path = new Object[path.length];
+ System.arraycopy(path, 0, this.path, 0, path.length);
+ }
+
+ /**
+ * Creates a new path from a single element.
+ *
+ * @param element the element (<code>null</code> not permitted).
+ *
+ * @throws IllegalArgumentException if <code>element</code> is
+ * <code>null</code>.
+ */
+ public TreePath(Object element)
+ {
+ path = new Object[1];
+ path[0] = element;
+ }
+
+ /**
+ * Creates a new tree path by adding the specified <code>element</code> to
+ * the <code>path</code>.
+ *
+ * @param path a tree path.
+ * @param element a path element.
+ */
+ protected TreePath(TreePath path, Object element)
+ {
+ if (element == null)
+ throw new NullPointerException("Null 'element' argument.");
+ Object[] treepath = path.getPath();
+
+ // Create Tree Path
+ this.path = new Object[treepath.length + 1];
+ System.arraycopy(treepath, 0, this.path, 0, treepath.length);
+ this.path[treepath.length] = element;
+ }
+
+ /**
+ * Creates a new tree path using the first <code>length</code> elements
+ * from the given array.
+ *
+ * @param path the path elements.
+ * @param length the path length.
+ */
+ protected TreePath(Object[] path, int length)
+ {
+ // Create Path
+ this.path = new Object[length];
+ System.arraycopy(path, 0, this.path, 0, length);
+ }
+
+ /**
+ * Default constructor.
+ */
+ protected TreePath()
+ {
+ path = new Object[0];
+ }
+
+
+ /**
+ * Returns a hashcode for the path.
+ *
+ * @return A hashcode.
+ */
+ public int hashCode()
+ {
+ return getLastPathComponent().hashCode();
+ }
+
+ /**
+ * Tests this path for equality with an arbitrary object. An object is
+ * considered equal to this path if and only if:
+ * <ul>
+ * <li>the object is not <code>null</code>;</li>
+ * <li>the object is an instanceof {@link TreePath};</li>
+ * <li>the object contains the same elements in the same order as this
+ * {@link TreePath};</li>
+ * </ul>
+ *
+ * @param object the object (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if <code>obj</code> is equal to this tree path,
+ * and <code>false</code> otherwise.
+ */
+ public boolean equals(Object object)
+ {
+ Object[] treepath;
+ int index;
+
+ if (object instanceof TreePath)
+ {
+ treepath = ((TreePath) object).getPath();
+ if (treepath.length != path.length)
+ return false;
+ for (index = 0; index < path.length; index++)
+ {
+ if (!path[index].equals(treepath[index]))
+ return false;
+ }
+
+ // Tree Path's are equals
+ return true;
+ }
+
+ // Unequal
+ return false;
+ }
+
+ /**
+ * Returns a string representation of this path.
+ *
+ * @return A string representation of this path.
+ */
+ public String toString()
+ {
+ if (path.length == 1)
+ return String.valueOf(path[0]);
+ else
+ return Arrays.asList(path).toString();
+ }
+
+ /**
+ * Returns an array containing the path elements.
+ *
+ * @return An array containing the path elements.
+ */
+ public Object[] getPath()
+ {
+ return (Object[]) path.clone();
+ }
+
+ /**
+ * Returns the last object in the path.
+ *
+ * @return The last object in the path.
+ */
+ public Object getLastPathComponent()
+ {
+ return path[path.length - 1];
+ }
+
+ /**
+ * Returns the number of elements in the path.
+ *
+ * @return The number of elements in the path.
+ */
+ public int getPathCount()
+ {
+ return path.length;
+ }
+
+ /**
+ * Returns the element at the specified position in the path.
+ *
+ * @param position the element position (<code>0 &lt N - 1</code>, where
+ * <code>N</code> is the number of elements in the path).
+ *
+ * @return The element at the specified position.
+ *
+ * @throws IllegalArgumentException if <code>position</code> is outside the
+ * valid range.
+ */
+ public Object getPathComponent(int position)
+ {
+ if (position < 0 || position >= getPathCount())
+ throw new IllegalArgumentException("Invalid position: " + position);
+ return path[position];
+ }
+
+ /**
+ * Returns <code>true</code> if <code>path</code> is a descendant of this
+ * path, and <code>false</code> otherwise. If <code>path</code> is
+ * <code>null</code>, this method returns <code>false</code>.
+ *
+ * @param path the path to check (<code>null</code> permitted).
+ *
+ * @return <code>true</code> if <code>path</code> is a descendant of this
+ * path, and <code>false</code> otherwise
+ */
+ public boolean isDescendant(TreePath path)
+ {
+ if (path == null)
+ return false;
+ int count = getPathCount();
+ int otherPathLength = path.getPathCount();
+ if (otherPathLength < count)
+ return false;
+ while (otherPathLength > count)
+ {
+ otherPathLength--;
+ path = path.getParentPath();
+ }
+
+ return equals(path);
+ }
+
+ /**
+ * Creates a new path that is equivalent to this path plus the specified
+ * element.
+ *
+ * @param element the element.
+ *
+ * @return A tree path.
+ */
+ public TreePath pathByAddingChild(Object element)
+ {
+ return new TreePath(this, element);
+ }
+
+ /**
+ * Returns the parent path, which is a path containing all the same elements
+ * as this path, except for the last one. If this path contains only one
+ * element, the method returns <code>null</code>.
+ *
+ * @return The parent path, or <code>null</code> if this path has only one
+ * element.
+ */
+ public TreePath getParentPath()
+ {
+ // If this path has only one element, then we return null. That
+ // is what the JDK does.
+ if (path.length <= 1)
+ return null;
+
+ // Reuse the parent path, if possible. The parent path is requested
+ // during the tree repainting, so reusing generates a lot less garbage.
+ if (parentPath == null)
+ parentPath = new TreePath(this.getPath(), path.length - 1);
+
+ return parentPath;
+ }
+}
diff --git a/libjava/classpath/javax/swing/tree/TreeSelectionModel.java b/libjava/classpath/javax/swing/tree/TreeSelectionModel.java
new file mode 100644
index 000000000..f2ba02a17
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/TreeSelectionModel.java
@@ -0,0 +1,112 @@
+/* TreeSelectionModel.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import java.beans.PropertyChangeListener;
+
+import javax.swing.event.TreeSelectionListener;
+
+/**
+ * TreeSelectionModel public interface
+ * @author Andrew Selkirk
+ */
+public interface TreeSelectionModel
+{
+
+ int SINGLE_TREE_SELECTION = 1;
+
+ int CONTIGUOUS_TREE_SELECTION = 2;
+
+ int DISCONTIGUOUS_TREE_SELECTION = 4;
+
+ void setSelectionMode(int mode);
+
+ int getSelectionMode();
+
+ void setSelectionPath(TreePath path);
+
+ void setSelectionPaths(TreePath[] paths);
+
+ void addSelectionPath(TreePath path);
+
+ void addSelectionPaths(TreePath[] paths);
+
+ void removeSelectionPath(TreePath path);
+
+ void removeSelectionPaths(TreePath[] paths);
+
+ TreePath getSelectionPath();
+
+ TreePath[] getSelectionPaths();
+
+ int getSelectionCount();
+
+ boolean isPathSelected(TreePath path);
+
+ boolean isSelectionEmpty();
+
+ void clearSelection();
+
+ void setRowMapper(RowMapper newMapper);
+
+ RowMapper getRowMapper();
+
+ int[] getSelectionRows();
+
+ int getMinSelectionRow();
+
+ int getMaxSelectionRow();
+
+ boolean isRowSelected(int row);
+
+ void resetRowSelection();
+
+ int getLeadSelectionRow();
+
+ TreePath getLeadSelectionPath();
+
+ void addPropertyChangeListener(PropertyChangeListener listener);
+
+ void removePropertyChangeListener(PropertyChangeListener listener);
+
+ void addTreeSelectionListener(TreeSelectionListener x);
+
+ void removeTreeSelectionListener(TreeSelectionListener x);
+
+}
diff --git a/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java b/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java
new file mode 100644
index 000000000..0887061b3
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java
@@ -0,0 +1,657 @@
+/* VariableHeightLayoutCache.java --
+ Copyright (C) 2002, 2004, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.tree;
+
+import gnu.javax.swing.tree.GnuPath;
+
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.swing.event.TreeModelEvent;
+
+/**
+ * The fixed height tree layout. This class requires the NodeDimensions to be
+ * set and ignores the row height property.
+ *
+ * @specnote the methods, of this class, returning TreePath, actually returns
+ * the derived class GnuPath that provides additional information for optimized
+ * painting. See the GnuPath code for details.
+ *
+ * @author Audrius Meskauskas
+ */
+public class VariableHeightLayoutCache
+ extends AbstractLayoutCache
+{
+
+ private static final Rectangle RECT_CACHE = new Rectangle();
+
+ /**
+ * The cached node record.
+ */
+ class NodeRecord
+ {
+ NodeRecord(int aRow, int aDepth, Object aNode, Object aParent)
+ {
+ row = aRow;
+ depth = aDepth;
+ parent = aParent;
+ node = aNode;
+ isExpanded = expanded.contains(aNode);
+ bounds = new Rectangle(0, -1, 0, 0);
+ }
+
+ /**
+ * The row, where the tree node is displayed.
+ */
+ final int row;
+
+ /**
+ * The nesting depth
+ */
+ final int depth;
+
+ /**
+ * The parent of the given node, null for the root node.
+ */
+ final Object parent;
+
+ /**
+ * This node.
+ */
+ final Object node;
+
+ /**
+ * True for the expanded nodes. The value is calculated in constructor.
+ * Using this field saves one hashtable access operation.
+ */
+ final boolean isExpanded;
+
+ /**
+ * The cached bounds of the tree row.
+ */
+ Rectangle bounds;
+
+ /**
+ * The path from the tree top to the given node (computed under first
+ * demand)
+ */
+ private TreePath path;
+
+ /**
+ * Get the path for this node. The derived class is returned, making check
+ * for the last child of some parent easier.
+ */
+ TreePath getPath()
+ {
+ if (path == null)
+ {
+ boolean lastChild = false;
+ if (parent != null)
+ {
+ int nc = treeModel.getChildCount(parent);
+ if (nc > 0)
+ {
+ int n = treeModel.getIndexOfChild(parent, node);
+ if (n == nc - 1)
+ lastChild = true;
+ }
+ }
+
+ LinkedList<Object> lpath = new LinkedList<Object>();
+ NodeRecord rp = this;
+ while (rp != null)
+ {
+ lpath.addFirst(rp.node);
+ if (rp.parent != null)
+ {
+ Object parent = rp.parent;
+ rp = nodes.get(parent);
+ // Add the root node, even if it is not visible.
+ if (rp == null)
+ lpath.addFirst(parent);
+ }
+ else
+ rp = null;
+ }
+ path = new GnuPath(lpath.toArray(), lastChild);
+ }
+ return path;
+ }
+
+ /**
+ * Get the rectangle bounds (compute, if required).
+ */
+ Rectangle getBounds()
+ {
+ return bounds;
+ }
+ }
+
+ /**
+ * The set of all expanded tree nodes.
+ */
+ Set<Object> expanded = new HashSet<Object>();
+
+ /**
+ * Maps nodes to the row numbers.
+ */
+ Hashtable<Object,NodeRecord> nodes = new Hashtable<Object,NodeRecord>();
+
+ /**
+ * Maps row numbers to nodes.
+ */
+ ArrayList<Object> row2node = new ArrayList<Object>();
+
+ /**
+ * If true, the row map must be recomputed before using.
+ */
+ boolean dirty;
+
+ /**
+ * The cumulative height of all rows.
+ */
+ int totalHeight;
+
+ /**
+ * The maximal width.
+ */
+ int maximalWidth;
+
+ /**
+ * Creates the unitialised instance. Before using the class, the row height
+ * must be set with the {@link #setRowHeight(int)} and the model must be set
+ * with {@link #setModel(TreeModel)}. The node dimensions may not be set.
+ */
+ public VariableHeightLayoutCache()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Get the total number of rows in the tree. Every displayed node occupies the
+ * single row. The root node row is included if the root node is set as
+ * visible (false by default).
+ *
+ * @return int the number of the displayed rows.
+ */
+ public int getRowCount()
+ {
+ if (dirty) update();
+ return row2node.size();
+ }
+
+ /**
+ * Refresh the row map.
+ */
+ private final void update()
+ {
+ nodes.clear();
+ row2node.clear();
+
+ totalHeight = maximalWidth = 0;
+
+ if (treeModel == null)
+ return;
+
+ Object root = treeModel.getRoot();
+ countRows(root, null, 0, 0);
+ dirty = false;
+ }
+
+ /**
+ * Recursively counts all rows in the tree.
+ */
+ private final int countRows(Object node, Object parent, int depth, int y)
+ {
+ boolean visible = node != treeModel.getRoot() || rootVisible;
+ int row = row2node.size();
+ if (visible)
+ {
+ row2node.add(node);
+ }
+ NodeRecord nr = new NodeRecord(row, depth, node, parent);
+ NodeDimensions d = getNodeDimensions();
+ Rectangle r = RECT_CACHE;
+ if (d != null)
+ r = d.getNodeDimensions(node, row, depth, nr.isExpanded, r);
+ else
+ r.setBounds(0, 0, 0, 0);
+
+ if (! visible)
+ r.y = -1;
+ else
+ r.y = Math.max(0, y);
+
+ if (isFixedRowHeight())
+ r.height = getRowHeight();
+
+ nr.bounds.setBounds(r);
+ nodes.put(node, nr);
+
+ if (visible)
+ y += r.height;
+
+ int sc = treeModel.getChildCount(node);
+ int deeper = depth + 1;
+ if (expanded.contains(node))
+ {
+ for (int i = 0; i < sc; i++)
+ {
+ Object child = treeModel.getChild(node, i);
+ y = countRows(child, node, deeper, y);
+ }
+ }
+ return y;
+ }
+
+ /**
+ * Discard the bound information for the given path.
+ *
+ * @param path the path, for that the bound information must be recomputed.
+ */
+ public void invalidatePathBounds(TreePath path)
+ {
+ NodeRecord r = nodes.get(path.getLastPathComponent());
+ if (r != null)
+ r.bounds = null;
+ }
+
+ /**
+ * Mark all cached information as invalid.
+ */
+ public void invalidateSizes()
+ {
+ dirty = true;
+ }
+
+ /**
+ * Set the expanded state of the given path. The expansion states must be
+ * always updated when expanding and colapsing the tree nodes. Otherwise
+ * other methods will not work correctly after the nodes are collapsed or
+ * expanded.
+ *
+ * @param path the tree path, for that the state is being set.
+ * @param isExpanded the expanded state of the given path.
+ */
+ public void setExpandedState(TreePath path, boolean isExpanded)
+ {
+ if (isExpanded)
+ {
+ int length = path.getPathCount();
+ for (int i = 0; i < length; i++)
+ expanded.add(path.getPathComponent(i));
+ }
+ else
+ expanded.remove(path.getLastPathComponent());
+
+ dirty = true;
+ }
+
+ /**
+ * Get the expanded state for the given tree path.
+ *
+ * @return true if the given path is expanded, false otherwise.
+ */
+ public boolean isExpanded(TreePath path)
+ {
+ return expanded.contains(path.getLastPathComponent());
+ }
+
+ /**
+ * Get bounds for the given tree path.
+ *
+ * @param path the tree path
+ * @param rect the rectangle that will be reused to return the result.
+ * @return Rectangle the bounds of the last line, defined by the given path.
+ */
+ public Rectangle getBounds(TreePath path, Rectangle rect)
+ {
+ if (path == null)
+ return null;
+ if (dirty)
+ update();
+
+ Object last = path.getLastPathComponent();
+ Rectangle result = null;
+ NodeRecord r = nodes.get(last);
+ if (r != null)
+ {
+ // The RI allows null arguments for rect, in which case a new Rectangle
+ // is created.
+ result = rect;
+ if (result == null)
+ result = new Rectangle(r.bounds);
+ else
+ result.setBounds(r.bounds);
+ }
+ return result;
+ }
+
+ /**
+ * Get the path, the last element of that is displayed in the given row.
+ *
+ * @param row the row
+ * @return TreePath the path
+ */
+ public TreePath getPathForRow(int row)
+ {
+ if (dirty)
+ update();
+
+ TreePath path = null;
+ // Search row in the nodes map. TODO: This is inefficient, optimize this.
+ Enumeration<NodeRecord> nodesEnum = nodes.elements();
+ while (nodesEnum.hasMoreElements() && path == null)
+ {
+ NodeRecord record = nodesEnum.nextElement();
+ if (record.row == row)
+ path = record.getPath();
+ }
+ return path;
+ }
+
+ /**
+ * Get the row, displaying the last node of the given path.
+ *
+ * @param path the path
+ * @return int the row number or -1 if the end of the path is not visible.
+ */
+ public int getRowForPath(TreePath path)
+ {
+ if (path == null)
+ return -1;
+
+ if (dirty)
+ update();
+
+ NodeRecord r = nodes.get(path.getLastPathComponent());
+ if (r == null)
+ return - 1;
+ else
+ return r.row;
+ }
+
+ /**
+ * Get the path, closest to the given point.
+ *
+ * @param x the point x coordinate
+ * @param y the point y coordinate
+ * @return the tree path, closest to the the given point
+ */
+ public TreePath getPathClosestTo(int x, int y)
+ {
+ if (dirty)
+ update();
+
+ // As the rows have arbitrary height, we need to iterate.
+ NodeRecord best = null;
+ NodeRecord r;
+ Enumeration<NodeRecord> en = nodes.elements();
+
+ int dist = Integer.MAX_VALUE;
+
+ while (en.hasMoreElements() && dist > 0)
+ {
+ r = en.nextElement();
+ if (best == null)
+ {
+ best = r;
+ dist = distance(r.getBounds(), x, y);
+ }
+ else
+ {
+ int rr = distance(r.getBounds(), x, y);
+ if (rr < dist)
+ {
+ best = r;
+ dist = rr;
+ }
+ }
+ }
+
+ if (best == null)
+ return null;
+ else
+ return best.getPath();
+ }
+
+ /**
+ * Get the closest distance from this point till the given rectangle. Only
+ * vertical distance is taken into consideration.
+ */
+ int distance(Rectangle r, int x, int y)
+ {
+ if (y < r.y)
+ return r.y - y;
+ else if (y > r.y + r.height - 1)
+ return y - (r.y + r.height - 1);
+ else
+ return 0;
+ }
+
+ /**
+ * Get the number of the visible childs for the given tree path. If the node
+ * is not expanded, 0 is returned. Otherwise, the number of children is
+ * obtained from the model as the number of children for the last path
+ * component.
+ *
+ * @param path the tree path
+ * @return int the number of the visible childs (for row).
+ */
+ public int getVisibleChildCount(TreePath path)
+ {
+ if (! isExpanded(path) || treeModel == null)
+ return 0;
+ else
+ return treeModel.getChildCount(path.getLastPathComponent());
+ }
+
+ /**
+ * Get the enumeration over all visible paths that start from the given
+ * parent path.
+ *
+ * @param parentPath the parent path
+ * @return the enumeration over pathes
+ */
+ public Enumeration<TreePath> getVisiblePathsFrom(TreePath parentPath)
+ {
+ if (dirty)
+ update();
+ Vector<TreePath> p = new Vector<TreePath>(parentPath.getPathCount());
+ Object node;
+ NodeRecord nr;
+
+ for (int i = 0; i < parentPath.getPathCount(); i++)
+ {
+ node = parentPath.getPathComponent(i);
+ nr = nodes.get(node);
+ if (nr != null && nr.row >= 0)
+ p.add((TreePath) node);
+ }
+ return p.elements();
+ }
+
+ /**
+ * Return the expansion state of the given tree path. The expansion state
+ * must be previously set with the
+ * {@link #setExpandedState(TreePath, boolean)}
+ *
+ * @param path the path being checked
+ * @return true if the last node of the path is expanded, false otherwise.
+ */
+ public boolean getExpandedState(TreePath path)
+ {
+ return expanded.contains(path.getLastPathComponent());
+ }
+
+ /**
+ * The listener method, called when the tree nodes are changed.
+ *
+ * @param event the change event
+ */
+ public void treeNodesChanged(TreeModelEvent event)
+ {
+ dirty = true;
+ }
+
+ /**
+ * The listener method, called when the tree nodes are inserted.
+ *
+ * @param event the change event
+ */
+ public void treeNodesInserted(TreeModelEvent event)
+ {
+ dirty = true;
+ }
+
+ /**
+ * The listener method, called when the tree nodes are removed.
+ *
+ * @param event the change event
+ */
+ public void treeNodesRemoved(TreeModelEvent event)
+ {
+ dirty = true;
+ }
+
+ /**
+ * Called when the tree structure has been changed.
+ *
+ * @param event the change event
+ */
+ public void treeStructureChanged(TreeModelEvent event)
+ {
+ dirty = true;
+ }
+
+ /**
+ * Set the tree model that will provide the data.
+ */
+ public void setModel(TreeModel newModel)
+ {
+ treeModel = newModel;
+ dirty = true;
+ if (treeModel != null)
+ {
+ // The root node is expanded by default.
+ expanded.add(treeModel.getRoot());
+ }
+ }
+
+ /**
+ * Inform the instance if the tree root node is visible. If this method
+ * is not called, it is assumed that the tree root node is not visible.
+ *
+ * @param visible true if the tree root node is visible, false
+ * otherwise.
+ */
+ public void setRootVisible(boolean visible)
+ {
+ rootVisible = visible;
+ dirty = true;
+ }
+
+ /**
+ * Get the sum of heights for all rows.
+ */
+ public int getPreferredHeight()
+ {
+ if (dirty)
+ update();
+ int height = 0;
+ int rowCount = getRowCount();
+ if (rowCount > 0)
+ {
+ NodeRecord last = nodes.get(row2node.get(rowCount - 1));
+ height = last.bounds.y + last.bounds.height;
+ }
+ return height;
+ }
+
+ /**
+ * Get the maximal width.
+ */
+ public int getPreferredWidth(Rectangle value)
+ {
+ if (dirty)
+ update();
+
+ maximalWidth = 0;
+ Enumeration<NodeRecord> en = nodes.elements();
+ while (en.hasMoreElements())
+ {
+ NodeRecord nr = en.nextElement();
+ if (nr != null)
+ {
+ Rectangle r = nr.getBounds();
+ int width = r.x + r.width;
+ if (width > maximalWidth)
+ maximalWidth = width;
+ }
+ }
+ return maximalWidth;
+ }
+
+ /**
+ * Sets the node dimensions and invalidates the cached layout.
+ *
+ * @param dim the dimensions to set
+ */
+ public void setNodeDimensions(NodeDimensions dim)
+ {
+ super.setNodeDimensions(dim);
+ dirty = true;
+ }
+
+ /**
+ * Sets the row height and marks the layout as invalid.
+ *
+ * @param height the row height to set
+ */
+ public void setRowHeight(int height)
+ {
+ super.setRowHeight(height);
+ dirty = true;
+ }
+}
diff --git a/libjava/classpath/javax/swing/tree/package.html b/libjava/classpath/javax/swing/tree/package.html
new file mode 100644
index 000000000..22538e4ba
--- /dev/null
+++ b/libjava/classpath/javax/swing/tree/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.tree package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.tree</title></head>
+
+<body>
+<p>Interfaces and classes that support the {@link javax.swing.JTree}
+component.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java b/libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java
new file mode 100644
index 000000000..fc586604a
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java
@@ -0,0 +1,323 @@
+/* AbstractUndoableEdit.java --
+ Copyright (C) 2002, 2003, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+import java.io.Serializable;
+
+import javax.swing.UIManager;
+
+/**
+ * A default implementation of <code>UndoableEdit</code> that can be
+ * used as a base for implementing editing operations.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class AbstractUndoableEdit
+ implements UndoableEdit, Serializable
+{
+ /**
+ * The serialization ID. Verified using the <code>serialver</code>
+ * tool of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5, and Sun JDK
+ * 1.4.1_01 on GNU/Linux.
+ */
+ static final long serialVersionUID = 580150227676302096L;
+
+
+ /**
+ * The constant string &#x201c;Undo&#x201d;, which was returned by
+ * {@link #getUndoPresentationName()} on early versions of the
+ * platform. However, this field has become obsolete with version
+ * 1.3.1. That method now retrieves a localized string from the
+ * {@link javax.swing.UIManager}, using the key
+ * <code>&#x201c;AbstractUndoableEdit.undoText&#x201d;</code>.
+ */
+ protected static final String UndoName = "Undo";
+
+
+ /**
+ * The constant string &#x201c;Redo&#x201d;, which was returned by
+ * {@link #getRedoPresentationName()} on early versions of the
+ * platform. However, this field has become obsolete with version
+ * 1.3.1. That method now retrieves a localized string from the
+ * {@link javax.swing.UIManager}, using the key
+ * <code>&#x201c;AbstractUndoableEdit.redoText&#x201d;</code>.
+ */
+ protected static final String RedoName = "Redo";
+
+
+ /**
+ * Indicates whether this editing action has been executed. A value
+ * of <code>true</code> means that the action was performed, or that
+ * a redo operation was successful. A value of <code>false</code>
+ * means that the action has not yet performed, or that an undo
+ * operation was successful.
+ */
+ private boolean hasBeenDone;
+
+
+ /**
+ * Indicates whether this editing action is still alive. The value
+ * is set to <code>true</code> by the constructor, and to
+ * <code>false</code> by the {@link #die()} method.
+ */
+ private boolean alive;
+
+
+ /**
+ * Constructs a new <code>AbstractUndoableEdit</code>. The initial
+ * state is that the editing action is alive, and
+ * <code>hasBeenDone</code> is <code>true</code>.
+ */
+ public AbstractUndoableEdit()
+ {
+ // The API specification is not clear, but Mauve test code has
+ // determined that hasBeenDone is initially set to true.
+ alive = hasBeenDone = true;
+ }
+
+
+ /**
+ * Undoes this editing action.
+ *
+ * @throws CannotUndoException if {@link #canUndo()} returns
+ * <code>false</code>, for example because this action has already
+ * been undone.
+ *
+ * @see #canUndo()
+ * @see #redo()
+ */
+ public void undo()
+ throws CannotUndoException
+ {
+ if (!canUndo())
+ throw new CannotUndoException();
+ hasBeenDone = false;
+ }
+
+
+ /**
+ * Determines whether it would be possible to undo this editing
+ * action.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * undone, <code>false</code> otherwise.
+ *
+ * @see #undo()
+ * @see #canRedo()
+ */
+ public boolean canUndo()
+ {
+ return alive && hasBeenDone;
+ }
+
+
+ /**
+ * Redoes this editing action.
+ *
+ * @throws CannotRedoException if {@link #canRedo()} returns
+ * <code>false</code>, for example because this action has not
+ * yet been undone.
+ *
+ * @see #canRedo()
+ * @see #undo()
+ */
+ public void redo()
+ throws CannotRedoException
+ {
+ if (!canRedo())
+ throw new CannotRedoException();
+ hasBeenDone = true;
+ }
+
+
+ /**
+ * Determines whether it would be possible to redo this editing
+ * action.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * redone, <code>false</code> otherwise.
+ *
+ * @see #redo()
+ * @see #canUndo()
+ */
+ public boolean canRedo()
+ {
+ return alive && !hasBeenDone;
+ }
+
+
+ /**
+ * Informs this edit action that it will no longer be used. Some
+ * actions might use this information to release resources, for
+ * example open files. Called by {@link UndoManager} before this
+ * action is removed from the edit queue.
+ */
+ public void die()
+ {
+ alive = false;
+ }
+
+
+ /**
+ * Incorporates another editing action into this one, thus forming a
+ * combined action.
+ *
+ * <p>The default implementation always returns <code>false</code>,
+ * indicating that the editing action could not be incorporated.
+ *
+ * @param edit the editing action to be incorporated.
+ */
+ public boolean addEdit(UndoableEdit edit)
+ {
+ return false;
+ }
+
+
+ /**
+ * Incorporates another editing action into this one, thus forming a
+ * combined action that replaces the argument action.
+ *
+ * <p>The default implementation always returns <code>false</code>,
+ * indicating that the argument action should not be replaced.
+ *
+ * @param edit the editing action to be replaced.
+ */
+ public boolean replaceEdit(UndoableEdit edit)
+ {
+ return false;
+ }
+
+
+ /**
+ * Determines whether this editing action is significant enough for
+ * being seperately undoable by the user. A typical significant
+ * action would be the resizing of an object. However, changing the
+ * selection in a text document would usually not be considered
+ * significant.
+ *
+ * <p>The default implementation returns <code>true</code>.
+ *
+ * @return <code>true</code> to indicate that the action is
+ * significant enough for being separately undoable, or
+ * <code>false</code> otherwise.
+ */
+ public boolean isSignificant()
+ {
+ return true;
+ }
+
+
+ /**
+ * Returns a human-readable, localized name that describes this
+ * editing action and can be displayed to the user.
+ *
+ * <p>The default implementation returns an empty string.
+ */
+ public String getPresentationName()
+ {
+ return "";
+ }
+
+
+ /**
+ * Calculates a localized name for presenting the undo action to the
+ * user.
+ *
+ * <p>The default implementation returns the concatenation of the
+ * string &#x201c;Undo&#x201d; and the action name, which is
+ * determined by calling {@link #getPresentationName()}.
+ *
+ * <p>The string &#x201c;Undo&#x201d; is retrieved from the {@link
+ * javax.swing.UIManager}, using the key
+ * <code>&#x201c;AbstractUndoableEdit.undoText&#x201d;</code>. This
+ * allows the text to be localized.
+ */
+ public String getUndoPresentationName()
+ {
+ String msg, pres;
+
+ msg = UIManager.getString("AbstractUndoableEdit.undoText");
+ if (msg == null)
+ msg = UndoName;
+
+ pres = getPresentationName();
+ if ((pres == null) || (pres.length() == 0))
+ return msg;
+ else
+ return msg + ' ' + pres;
+ }
+
+
+ /**
+ * Calculates a localized name for presenting the redo action to the
+ * user.
+ *
+ * <p>The default implementation returns the concatenation of the
+ * string &#x201c;Redo&#x201d; and the action name, which is
+ * determined by calling {@link #getPresentationName()}.
+ *
+ * <p>The string &#x201c;Redo&#x201d; is retrieved from the {@link
+ * javax.swing.UIManager}, using the key
+ * <code>&#x201c;AbstractUndoableEdit.redoText&#x201d;</code>. This
+ * allows the text to be localized.
+ */
+ public String getRedoPresentationName()
+ {
+ String msg, pres;
+
+ msg = UIManager.getString("AbstractUndoableEdit.redoText");
+ if (msg == null)
+ msg = RedoName;
+
+ pres = getPresentationName();
+ if ((pres == null) || (pres.length() == 0))
+ return msg;
+ else
+ return msg + ' ' + pres;
+ }
+
+
+ public String toString()
+ {
+ return super.toString()
+ + " hasBeenDone: " + hasBeenDone
+ + " alive: " + alive;
+ }
+}
diff --git a/libjava/classpath/javax/swing/undo/CannotRedoException.java b/libjava/classpath/javax/swing/undo/CannotRedoException.java
new file mode 100644
index 000000000..5f2264835
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/CannotRedoException.java
@@ -0,0 +1,56 @@
+/* CannotRedoException.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+/**
+ * An exception which indicates that an editing action cannot be
+ * redone.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class CannotRedoException extends RuntimeException
+{
+ /**
+ * Constructs a new instance of a <code>CannotRedoException</code>.
+ */
+ public CannotRedoException()
+ {
+ super();
+ }
+}
diff --git a/libjava/classpath/javax/swing/undo/CannotUndoException.java b/libjava/classpath/javax/swing/undo/CannotUndoException.java
new file mode 100644
index 000000000..8d08cda68
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/CannotUndoException.java
@@ -0,0 +1,58 @@
+/* CannotUndoException.java
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+
+/**
+ * An exception which indicates that an editing action cannot be
+ * undone.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class CannotUndoException
+ extends RuntimeException
+{
+ /**
+ * Constructs a new instance of a <code>CannotUndoException</code>.
+ */
+ public CannotUndoException()
+ {
+ super();
+ }
+}
diff --git a/libjava/classpath/javax/swing/undo/CompoundEdit.java b/libjava/classpath/javax/swing/undo/CompoundEdit.java
new file mode 100644
index 000000000..64e51740d
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/CompoundEdit.java
@@ -0,0 +1,400 @@
+/* CompoundEdit.java -- Combines multiple UndoableEdits.
+ Copyright (C) 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+import java.util.Vector;
+
+/**
+ * An editing action that consists of multiple
+ * <code>UndoableEdits</code>.
+ *
+ * <p>The use of a <code>CompoundEdit</code> is divided in two separate
+ * phases.</p>
+ *
+ * <ol>
+ * <li>In the first phase, the <code>CompoundEdit</code> is
+ * initialized. After a new instance of <code>CompoundEdit</code> has
+ * been created, {@link #addEdit(UndoableEdit)} is called for each
+ * element of the compound. To terminate the initialization phase,
+ * call {@link #end()}.</li>
+ * <li>In the second phase, the the <code>CompoundEdit</code> can be
+ * used, typically by invoking {@link #undo()} and
+ * {@link #redo()}.</li>
+ * </ol>
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class CompoundEdit
+ extends AbstractUndoableEdit
+{
+ /**
+ * The identifier of this class in object serialization. Determined
+ * using the serialver tool of Sun J2SE 1.4.1_01.
+ */
+ private static final long serialVersionUID = -6512679249930119683L;
+
+
+ /**
+ * The <code>UndoableEdit</code>s being combined into a compound
+ * editing action.
+ */
+ protected Vector<UndoableEdit> edits;
+
+
+ /**
+ * Indicates whether the creation of this CompoundEdit is still in
+ * progress. Initially, the value of this flag is
+ * <code>true</code>. The {@link #end()} method changes the flag to
+ * <code>false</code>.
+ */
+ private boolean inProgress;
+
+
+ /**
+ * Constructs a new CompoundEdit.
+ */
+ public CompoundEdit()
+ {
+ edits = new Vector<UndoableEdit>();
+ inProgress = true;
+ }
+
+
+ /**
+ * Undoes all edits that are part of of this
+ * <code>CompoundEdit</code>. The compound elements will receive the
+ * <code>undo</code> message in the reverse order of addition.
+ *
+ * @throws CannotUndoException if {@link #canUndo()} returns
+ * <code>false</code>. This can happen if {@link #end()} has not
+ * been called on this <code>CompoundEdit</code>, or if this edit
+ * has already been undone.
+ *
+ * @see #canUndo()
+ * @see #redo()
+ */
+ public void undo()
+ throws CannotUndoException
+ {
+ // AbstractUndoableEdit.undo() will throw a CannotUndoException if
+ // canUndo returns false.
+ super.undo();
+
+ for (int i = edits.size() - 1; i >= 0; i--)
+ edits.elementAt(i).undo();
+ }
+
+
+ /**
+ * Redoes all edits that are part of of this
+ * <code>CompoundEdit</code>. The compound elements will receive the
+ * <code>undo</code> message in the same order as they were added.
+ *
+ * @throws CannotRedoException if {@link #canRedo()} returns
+ * <code>false</code>. This can happen if {@link #end()} has not
+ * been called on this <code>CompoundEdit</code>, or if this edit
+ * has already been redone.
+ *
+ * @see #canRedo()
+ * @see #undo()
+ */
+ public void redo()
+ throws CannotRedoException
+ {
+ // AbstractUndoableEdit.redo() will throw a CannotRedoException if
+ // canRedo returns false.
+ super.redo();
+
+ for (int i = 0; i < edits.size(); i++)
+ edits.elementAt(i).redo();
+ }
+
+
+ /**
+ * Returns the the <code>UndoableEdit</code> that was last added to
+ * this compound.
+ */
+ protected UndoableEdit lastEdit()
+ {
+ if (edits.size() == 0)
+ return null;
+ else
+ return edits.elementAt(edits.size() - 1);
+ }
+
+
+ /**
+ * Informs this edit action, and all compound edits, that they will
+ * no longer be used. Some actions might use this information to
+ * release resources such as open files. Called by {@link
+ * UndoManager} before this action is removed from the edit queue.
+ *
+ * <p>The compound elements will receive the
+ * <code>die</code> message in the reverse order of addition.
+ */
+ public void die()
+ {
+ for (int i = edits.size() - 1; i >= 0; i--)
+ edits.elementAt(i).die();
+
+ super.die();
+ }
+
+
+ /**
+ * Incorporates another editing action into this one, thus forming a
+ * combined edit.
+ *
+ * <p>If this edit&#x2019;s {@link #end()} method has been called
+ * before, <code>false</code> is returned immediately. Otherwise,
+ * the {@linkplain #lastEdit() last added edit} is given the
+ * opportunity to {@linkplain UndoableEdit#addEdit(UndoableEdit)
+ * incorporate} <code>edit</code>. If this fails, <code>edit</code>
+ * is given the opportunity to {@linkplain
+ * UndoableEdit#replaceEdit(UndoableEdit) replace} the last added
+ * edit. If this fails as well, <code>edit</code> gets added as a
+ * new compound to {@link #edits}.
+ *
+ * @param edit the editing action being added.
+ *
+ * @return <code>true</code> if <code>edit</code> could somehow be
+ * incorporated; <code>false</code> if <code>edit</code> has not
+ * been incorporated because {@link #end()} was called before.
+ */
+ public boolean addEdit(UndoableEdit edit)
+ {
+ UndoableEdit last;
+
+ // If end has been called before, do nothing.
+ if (!inProgress)
+ return false;
+
+ last = lastEdit();
+
+ // If edit is the very first edit, just add it to the list.
+ if (last == null)
+ {
+ edits.add(edit);
+ return true;
+ }
+
+ // Try to incorporate edit into last.
+ if (last.addEdit(edit))
+ return true;
+
+ // Try to replace last by edit.
+ if (edit.replaceEdit(last))
+ {
+ edits.set(edits.size() - 1, edit);
+ return true;
+ }
+
+ // If everything else has failed, add edit to the list of compound
+ // edits.
+ edits.add(edit);
+ return true;
+ }
+
+
+ /**
+ * Informs this <code>CompoundEdit</code> that its construction
+ * phase has been completed. After this method has been called,
+ * {@link #undo()} and {@link #redo()} may be called, {@link
+ * #isInProgress()} will return <code>false</code>, and all attempts
+ * to {@linkplain #addEdit(UndoableEdit) add further edits} will
+ * fail.
+ */
+ public void end()
+ {
+ inProgress = false;
+ }
+
+
+ /**
+ * Determines whether it would be possible to undo this editing
+ * action. The result will be <code>true</code> if {@link #end()}
+ * has been called on this <code>CompoundEdit</code>, {@link #die()}
+ * has not yet been called, and the edit has not been undone
+ * already.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * undone; <code>false</code> otherwise.
+ *
+ * @see #undo()
+ * @see #canRedo()
+ */
+ public boolean canUndo()
+ {
+ return !inProgress && super.canUndo();
+ }
+
+
+ /**
+ * Determines whether it would be possible to redo this editing
+ * action. The result will be <code>true</code> if {@link #end()}
+ * has been called on this <code>CompoundEdit</code>, {@link #die()}
+ * has not yet been called, and the edit has not been redone
+ * already.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * redone; <code>false</code> otherwise.
+ *
+ * @see #redo()
+ * @see #canUndo()
+ */
+ public boolean canRedo()
+ {
+ return !inProgress && super.canRedo();
+ }
+
+
+ /**
+ * Determines whether the initial construction phase of this
+ * <code>CompoundEdit</code> is still in progress. During this
+ * phase, edits {@linkplain #addEdit(UndoableEdit) may be
+ * added}. After initialization has been terminated by calling
+ * {@link #end()}, {@link #undo()} and {@link #redo()} can be used.
+ *
+ * @return <code>true</code> if the initialization phase is still in
+ * progress; <code>false</code> if {@link #end()} has been called.
+ *
+ * @see #end()
+ */
+ public boolean isInProgress()
+ {
+ return inProgress;
+ }
+
+
+ /**
+ * Determines whether this editing action is significant enough for
+ * being seperately undoable by the user. A typical significant
+ * action would be the resizing of an object. However, changing the
+ * selection in a text document would usually not be considered
+ * significant.
+ *
+ * <p>A <code>CompoundEdit</code> is significant if any of its
+ * elements are significant.
+ */
+ public boolean isSignificant()
+ {
+ for (int i = edits.size() - 1; i >= 0; i--)
+ if (edits.elementAt(i).isSignificant())
+ return true;
+
+ return false;
+ }
+
+
+ /**
+ * Returns a human-readable, localized name that describes this
+ * editing action and can be displayed to the user.
+ *
+ * <p>The implementation delegates the call to the {@linkplain
+ * #lastEdit() last added edit action}. If no edit has been added
+ * yet, the inherited implementation will be invoked, which always
+ * returns an empty string.
+ */
+ public String getPresentationName()
+ {
+ UndoableEdit last;
+
+ last = lastEdit();
+ if (last == null)
+ return super.getPresentationName();
+ else
+ return last.getPresentationName();
+ }
+
+
+ /**
+ * Calculates a localized message text for presenting the undo
+ * action to the user.
+ *
+ * <p>The implementation delegates the call to the {@linkplain
+ * #lastEdit() last added edit action}. If no edit has been added
+ * yet, the {@linkplain
+ * AbstractUndoableEdit#getUndoPresentationName() inherited
+ * implementation} will be invoked.
+ */
+ public String getUndoPresentationName()
+ {
+ UndoableEdit last;
+
+ last = lastEdit();
+ if (last == null)
+ return super.getUndoPresentationName();
+ else
+ return last.getUndoPresentationName();
+ }
+
+
+ /**
+ * Calculates a localized message text for presenting the redo
+ * action to the user.
+ *
+ * <p>The implementation delegates the call to the {@linkplain
+ * #lastEdit() last added edit action}. If no edit has been added
+ * yet, the {@linkplain
+ * AbstractUndoableEdit#getRedoPresentationName() inherited
+ * implementation} will be invoked.
+ */
+ public String getRedoPresentationName()
+ {
+ UndoableEdit last;
+
+ last = lastEdit();
+ if (last == null)
+ return super.getRedoPresentationName();
+ else
+ return last.getRedoPresentationName();
+ }
+
+
+ /**
+ * Calculates a string that may be useful for debugging.
+ */
+ public String toString()
+ {
+ return super.toString()
+ + " inProgress: " + inProgress
+ + " edits: " + edits;
+ }
+}
diff --git a/libjava/classpath/javax/swing/undo/StateEdit.java b/libjava/classpath/javax/swing/undo/StateEdit.java
new file mode 100644
index 000000000..819d052c5
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/StateEdit.java
@@ -0,0 +1,265 @@
+/* StateEdit.java -- UndoableEdit for StateEditable implementations.
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+/**
+ * A helper class, making it easy to support undo and redo.
+ *
+ * <p>The following example shows how to use this class.</p>
+ *
+ * <pre>
+ * Foo foo; // class Foo implements {@link StateEditable}
+ * StateEdit edit;
+ *
+ * edit = new StateEdit(foo, "Name Change");
+ * foo.setName("Jane Doe");
+ * edit.end();
+ * undoManager.addEdit(edit);
+ * </pre>
+ *
+ * <p>If <code>Foo</code>&#x2019;s implementation of {@link
+ * StateEditable} considers the name as part of the editable state,
+ * the user can now choose &#x201c;Undo Name Change&#x201d; or
+ * &#x201c;Redo Name Change&#x201d; from the respective menu. No
+ * further undo support is needed from the application.</p>
+ *
+ * <p>The following explains what happens in the example.</p>
+ *
+ * <ol>
+ * <li>When a <code>StateEdit</code> is created, the associated
+ * {@link StateEditable} gets asked to store its state into a hash
+ * table, {@link #preState}.</li>
+ * <li>The application will now perform some changes to the edited
+ * object. This typically happens by invoking methods on the edited
+ * object.</li>
+ * <li>The editing phase is terminated by invoking the {@link #end()}
+ * method of the <code>StateEdit</code>. The <code>end()</code> method
+ * does two things.
+ *
+ * <ul>
+ * <li>The edited object receives a second request for storing
+ * its state. This time, it will use a different hash table, {@link
+ * #postState}.</li>
+ * <li>To increase efficiency, the <code>StateEdit</code> now removes
+ * any entries from {@link #preState} and {@link #postState} that have
+ * the same key, and whose values are equal. Equality is determined
+ * by invoking the <code>equals</code> method inherited from
+ * {@link java.lang.Object}.</li>
+ * </ul></li>
+ * <li>When the user later chooses to undo the <code>StateEdit</code>,
+ * the edited object is asked to {@linkplain StateEditable#restoreState
+ * restore its state} from the {@link #preState} table. Similarly,
+ * when the user chooses to <i>redo</i> the <code>StateEdit</code>,
+ * the edited object gets asked to restore its state from the {@link
+ * #postState}.</li>
+ * </ol>
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class StateEdit
+ extends AbstractUndoableEdit
+{
+ /**
+ * The ID of the Java source file in Sun&#x2019;s Revision Control
+ * System (RCS). This certainly should not be part of the API
+ * specification. But in order to be API-compatible with
+ * Sun&#x2019;s reference implementation, GNU Classpath also has to
+ * provide this field and match its value. The value used here has
+ * been in every JDK release at least from 1.2 to 1.5.
+ */
+ protected static final String RCSID = "$" +
+ "Id: StateEdit.java,v 1.6 1997/10/01 20:05:51 sandipc Exp $";
+
+
+ /**
+ * The object which is being edited by this <code>StateEdit</code>.
+ */
+ protected StateEditable object;
+
+
+ /**
+ * The state of <code>object</code> at the time of constructing
+ * this <code>StateEdit</code>.
+ */
+ protected Hashtable<Object, Object> preState;
+
+
+ /**
+ * The state of <code>object</code> at the time when {@link #end()}
+ * was called.
+ */
+ protected Hashtable<Object, Object> postState;
+
+
+ /**
+ * A human-readable name for this edit action.
+ */
+ protected String undoRedoName;
+
+
+ /**
+ * Constructs a <code>StateEdit</code>, specifying the object whose
+ * state is being edited.
+ *
+ * @param obj the object whose state is being edited by this
+ * <code>StateEdit</code>.
+ */
+ public StateEdit(StateEditable obj)
+ {
+ init(obj, null);
+ }
+
+
+ /**
+ * Constructs a <code>StateEdit</code>, specifying the object whose
+ * state is being edited.
+ *
+ * @param obj the object whose state is being edited by this
+ * <code>StateEdit</code>.
+ *
+ * @param name the human-readable name of the editing action.
+ */
+ public StateEdit(StateEditable obj, String name)
+ {
+ init(obj, name);
+ }
+
+
+ /**
+ * Initializes this <code>StateEdit</code>. The edited object will
+ * be asked to store its current state into {@link #preState}.
+ *
+ * @param obj the object being edited.
+ *
+ * @param name the human-readable name of the editing action.
+ */
+ protected void init(StateEditable obj, String name)
+ {
+ object = obj;
+ undoRedoName = name;
+ preState = new Hashtable<Object,Object>();
+ postState = new Hashtable<Object,Object>();
+ obj.storeState(preState);
+ }
+
+
+ /**
+ * Informs this <code>StateEdit</code> that all edits are finished.
+ * The edited object will be asked to store its state into {@link
+ * #postState}, and any redundant entries will get removed from
+ * {@link #preState} and {@link #postState}.
+ */
+ public void end()
+ {
+ object.storeState(postState);
+ removeRedundantState();
+ }
+
+
+ /**
+ * Undoes this edit operation. The edited object will be asked to
+ * {@linkplain StateEditable#restoreState restore its state} from
+ * {@link #preState}.
+ *
+ * @throws CannotUndoException if {@link #canUndo()} returns
+ * <code>false</code>, for example because this action has already
+ * been undone.
+ */
+ public void undo()
+ {
+ super.undo();
+ object.restoreState(preState);
+ }
+
+
+ /**
+ * Redoes this edit operation. The edited object will be asked to
+ * {@linkplain StateEditable#restoreState restore its state} from
+ * {@link #postState}.
+ *
+ * @throws CannotRedoException if {@link #canRedo()} returns
+ * <code>false</code>, for example because this action has not yet
+ * been undone.
+ */
+ public void redo()
+ {
+ super.redo();
+ object.restoreState(postState);
+ }
+
+
+ /**
+ * Returns a human-readable, localized name that describes this
+ * editing action and can be displayed to the user.
+ *
+ * @return the name, or <code>null</code> if no presentation
+ * name is available.
+ */
+ public String getPresentationName()
+ {
+ return undoRedoName;
+ }
+
+
+ /**
+ * Removes all redundant entries from the pre- and post-edit state
+ * hash tables. An entry is considered redundant if it is present
+ * both before and after the edit, and if the two values are equal.
+ */
+ protected void removeRedundantState()
+ {
+ Iterator<Object> i = preState.keySet().iterator();
+ while (i.hasNext())
+ {
+ Object key = i.next();
+ if (postState.containsKey(key))
+ {
+ if (preState.get(key).equals(postState.get(key)))
+ {
+ i.remove();
+ postState.remove(key);
+ }
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/undo/StateEditable.java b/libjava/classpath/javax/swing/undo/StateEditable.java
new file mode 100644
index 000000000..7e6cc9785
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/StateEditable.java
@@ -0,0 +1,114 @@
+/* StateEditable.java -- Interface for collaborating with StateEdit.
+ Copyright (C) 2002, 2003, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+import java.util.Hashtable;
+
+
+/**
+ * The interface for objects whose state can be undone or redone by a
+ * {@link StateEdit} action.
+ *
+ * <p>The following example shows how to write a class that implements
+ * this interface.
+ *
+ * <pre> class Foo
+ * implements StateEditable
+ * {
+ * private String name;
+ *
+ * public void setName(String n) { name = n; }
+ *
+ * public void restoreState(Hashtable h)
+ * {
+ * if (h.containsKey("name"))
+ * setName((String) h.get("name"));
+ * }
+ *
+ * public void storeState(Hashtable s)
+ * {
+ * s.put("name", name);
+ * }
+ * }</pre>
+ *
+ * @see StateEdit
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public interface StateEditable
+{
+ /**
+ * The ID of the Java source file in Sun&#x2019;s Revision Control
+ * System (RCS). This certainly should not be part of the API
+ * specification. But in order to be API-compatible with
+ * Sun&#x2019;s reference implementation, GNU Classpath also has to
+ * provide this field and match its value. The value used here has
+ * been in every JDK release at least from 1.2 to 1.5.
+ */
+ String RCSID = "$" +
+ "Id: StateEditable.java,v 1.2 1997/09/08 19:39:08 marklin Exp $";
+
+
+ /**
+ * Performs an edit action, taking any editable state information
+ * from the specified hash table.
+ *
+ * <p><b>Note to implementors of this interface:</b> To increase
+ * efficiency, the <code>StateEdit</code> class {@link
+ * StateEdit#removeRedundantState()} removes redundant state
+ * information. Therefore, implementations of this interface must be
+ * prepared for the case where certain keys were stored into the
+ * table by {@link #storeState}, but are not present anymore
+ * when the <code>restoreState</code> method gets called.
+ *
+ * @param state a hash table containing the relevant state
+ * information.
+ */
+ void restoreState(Hashtable<?, ?> state);
+
+
+ /**
+ * Stores any editable state information into the specified hash
+ * table.
+ *
+ * @param state a hash table for storing relevant state
+ * information.
+ */
+ void storeState(Hashtable<Object, Object> state);
+}
diff --git a/libjava/classpath/javax/swing/undo/UndoManager.java b/libjava/classpath/javax/swing/undo/UndoManager.java
new file mode 100644
index 000000000..9b10cc575
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/UndoManager.java
@@ -0,0 +1,625 @@
+/* UndoManager.java --
+ Copyright (C) 2002, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+import javax.swing.UIManager;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.event.UndoableEditListener;
+
+
+/**
+ * A manager for providing an application&#x2019;s undo/redo
+ * functionality.
+ *
+ * <p>Tyipcally, an application will create only one single instance
+ * of UndoManager. When the user performs an undoable action, for
+ * instance changing the color of an object from green to blue, the
+ * application registers an {@link UndoableEdit} object with the
+ * <code>UndoManager</code>. To implement the &#x201c;undo&#x201d; and
+ * &#x201c;redo&#x201d; menu commands, the application invokes the
+ * UndoManager&#x2019;s {@link #undo} and {@link #redo} methods. The
+ * human-readable text of these menu commands is provided by {@link
+ * #getUndoPresentationName} and {@link #getRedoPresentationName},
+ * respectively. To determine whether the menu item should be
+ * selectable or greyed out, use {@link #canUndo} and {@link
+ * #canRedo}.
+ *
+ * <p>The UndoManager will only keep a specified number of editing
+ * actions, the <em>limit</em>. The value of this parameter can be
+ * retrieved by calling {@link #getLimit} and set with {@link
+ * #setLimit}. If more UndoableEdits are added to the UndoManager,
+ * the oldest actions will be discarded.
+ *
+ * <p>Some applications do not provide separate menu commands for
+ * &#x201c;undo&#x201d; and &#x201c;redo.&#x201d; Instead, they
+ * have just a single command whose text switches between the two.
+ * Such applications would use an UndoManager with a <code>limit</code>
+ * of 1. The text of this combined menu item is available via
+ * {@link #getUndoOrRedoPresentationName}, and it is implemented
+ * by calling {@link #undoOrRedo}.
+ *
+ * <p><b>Thread Safety:</b> In constrast to the other classes of the
+ * <code>javax.swing.undo</code> package, the public methods of an
+ * <code>UndoManager</code> are safe to call from concurrent threads.
+ * The caller does not need to perform external synchronization, and
+ * {@link javax.swing.event.UndoableEditEvent} sources do not need to
+ * broadcast their events from inside the Swing worker thread.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class UndoManager
+ extends CompoundEdit
+ implements UndoableEditListener
+{
+ /**
+ * The unique ID for serializing instances of this class. Determined
+ * using the <code>serialver</code> tool of Sun JDK 1.4.1_01 on
+ * GNU/Linux.
+ */
+ static final long serialVersionUID = -2077529998244066750L;
+
+
+ /**
+ * An index into the inherited {@link #edits} Vector that indicates
+ * at which position newly added editing actions would get inserted.
+ *
+ * <p>Normally, the value of <code>indexOfNextAdd</code> equals
+ * the number of UndoableEdits stored by this UndoManager, i.e.
+ * <code>edits.size()</code>. For each call to {@link #undo},
+ * <code>indexOfNextAdd</code> is decremented by one. For each
+ * call to {@link #redo}, it is incremented again.
+ */
+ int indexOfNextAdd;
+
+
+ /**
+ * The maximum number of UndoableEdits stored by this UndoManager.
+ */
+ int limit;
+
+
+ /**
+ * Constructs an UndoManager.
+ *
+ * <p>The <code>limit</code> of the freshly constructed UndoManager
+ * is 100.
+ */
+ public UndoManager()
+ {
+ limit = 100;
+ }
+
+
+ /**
+ * Returns a string representation for this UndoManager. This may be
+ * useful for debugging purposes. For the text of menu items, please
+ * refer to {@link #getUndoPresentationName}, {@link
+ * #getRedoPresentationName}, and {@link
+ * #getUndoOrRedoPresentationName}.
+ */
+ public String toString()
+ {
+ return super.toString()
+ + " limit: " + limit
+ + " indexOfNextAdd: " + indexOfNextAdd;
+ }
+
+
+ /**
+ * Puts this UndoManager into a state where it acts as a normal
+ * {@link CompoundEdit}. It is unlikely that an application would
+ * want to do this.
+ */
+ public synchronized void end()
+ {
+ super.end();
+ trimEdits(indexOfNextAdd, edits.size() - 1);
+ }
+
+
+ /**
+ * Returns how many edits this UndoManager can maximally hold.
+ *
+ * @see #setLimit
+ */
+ public synchronized int getLimit()
+ {
+ return limit;
+ }
+
+
+ /**
+ * Changes the maximal number of edits that this UndoManager can
+ * process. If there are currently more edits than the new limit
+ * allows, they will receive a {@link UndoableEdit#die() die}
+ * message in reverse order of addition.
+ *
+ * @param limit the new limit.
+ *
+ * @throws IllegalStateException if {@link #end()} has already been
+ * called on this UndoManager.
+ */
+ public synchronized void setLimit(int limit)
+ {
+ if (!isInProgress())
+ throw new IllegalStateException();
+
+ this.limit = limit;
+ trimForLimit();
+ }
+
+
+ /**
+ * Discards all editing actions that are currently registered with
+ * this UndoManager. Each {@link UndoableEdit} will receive a {@link
+ * UndoableEdit#die() die message}.
+ */
+ public synchronized void discardAllEdits()
+ {
+ int size;
+
+ size = edits.size();
+ for (int i = size - 1; i >= 0; i--)
+ edits.get(i).die();
+ indexOfNextAdd = 0;
+ edits.clear();
+ }
+
+
+ /**
+ * Called by various internal methods in order to enforce
+ * the <code>limit</code> value.
+ */
+ protected void trimForLimit()
+ {
+ int high, s;
+
+ s = edits.size();
+
+ /* The Sun J2SE1.4.1_01 implementation can be observed to do
+ * nothing (instead of throwing an exception) with a negative or
+ * zero limit. It may be debatable whether this is the best
+ * behavior, but we replicate it for sake of compatibility.
+ */
+ if (limit <= 0 || s <= limit)
+ return;
+
+ high = Math.min(indexOfNextAdd + limit/2 - 1, s - 1);
+ trimEdits(high + 1, s - 1);
+ trimEdits(0, high - limit);
+ }
+
+
+ /**
+ * Discards a range of edits. All edits in the range <code>[from
+ * .. to]</code> will receive a {@linkplain UndoableEdit#die() die
+ * message} before being removed from the edits array. If
+ * <code>from</code> is greater than <code>to</code>, nothing
+ * happens.
+ *
+ * @param from the lower bound of the range of edits to be
+ * discarded.
+ *
+ * @param to the upper bound of the range of edits to be discarded.
+ */
+ protected void trimEdits(int from, int to)
+ {
+ if (from > to)
+ return;
+
+ for (int i = to; i >= from; i--)
+ edits.get(i).die();
+
+ // Remove the range [from .. to] from edits. If from == to, which
+ // is likely to be a very common case, we can do better than
+ // creating a sub-list and clearing it.
+ if (to == from)
+ edits.remove(from);
+ else
+ edits.subList(from, to + 1).clear();
+
+ if (indexOfNextAdd > to)
+ indexOfNextAdd = indexOfNextAdd - to + from - 1;
+ else if (indexOfNextAdd >= from)
+ indexOfNextAdd = from;
+ }
+
+
+ /**
+ * Determines which significant edit would be undone if {@link
+ * #undo()} was called.
+ *
+ * @return the significant edit that would be undone, or
+ * <code>null</code> if no significant edit would be affected by
+ * calling {@link #undo()}.
+ */
+ protected UndoableEdit editToBeUndone()
+ {
+ UndoableEdit result;
+
+ for (int i = indexOfNextAdd - 1; i >= 0; i--)
+ {
+ result = edits.get(i);
+ if (result.isSignificant())
+ return result;
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Determines which significant edit would be redone if {@link
+ * #redo()} was called.
+ *
+ * @return the significant edit that would be redone, or
+ * <code>null</code> if no significant edit would be affected by
+ * calling {@link #redo()}.
+ */
+ protected UndoableEdit editToBeRedone()
+ {
+ UndoableEdit result;
+
+ for (int i = indexOfNextAdd; i < edits.size(); i++)
+ {
+ result = edits.get(i);
+ if (result.isSignificant())
+ return result;
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Undoes all editing actions in reverse order of addition,
+ * up to the specified action,
+ *
+ * @param edit the last editing action to be undone.
+ */
+ protected void undoTo(UndoableEdit edit)
+ throws CannotUndoException
+ {
+ UndoableEdit cur;
+
+ if (!edits.contains(edit))
+ throw new CannotUndoException();
+
+ while (true)
+ {
+ indexOfNextAdd -= 1;
+ cur = edits.get(indexOfNextAdd);
+ cur.undo();
+ if (cur == edit)
+ return;
+ }
+ }
+
+
+ /**
+ * Redoes all editing actions in the same order as they were
+ * added to this UndoManager, up to the specified action.
+ *
+ * @param edit the last editing action to be redone.
+ */
+ protected void redoTo(UndoableEdit edit)
+ throws CannotRedoException
+ {
+ UndoableEdit cur;
+
+ if (!edits.contains(edit))
+ throw new CannotRedoException();
+
+ while (true)
+ {
+ cur = edits.get(indexOfNextAdd);
+ indexOfNextAdd += 1;
+ cur.redo();
+ if (cur == edit)
+ return;
+ }
+ }
+
+
+ /**
+ * Undoes or redoes the last action. If the last action has already
+ * been undone, it will be re-done, and vice versa.
+ *
+ * <p>This is useful for applications that do not present a separate
+ * undo and redo facility, but just have a single menu item for
+ * undoing and redoing the very last action. Such applications will
+ * use an <code>UndoManager</code> whose <code>limit</code> is 1.
+ */
+ public synchronized void undoOrRedo()
+ throws CannotRedoException, CannotUndoException
+ {
+ if (indexOfNextAdd == edits.size())
+ undo();
+ else
+ redo();
+ }
+
+
+ /**
+ * Determines whether it would be possible to either undo or redo
+ * this editing action.
+ *
+ * <p>This is useful for applications that do not present a separate
+ * undo and redo facility, but just have a single menu item for
+ * undoing and redoing the very last action. Such applications will
+ * use an <code>UndoManager</code> whose <code>limit</code> is 1.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * undone or redone; <code>false</code> if neither is possible at
+ * the current time.
+ */
+ public synchronized boolean canUndoOrRedo()
+ {
+ return indexOfNextAdd == edits.size() ? canUndo() : canRedo();
+ }
+
+
+ /**
+ * Undoes one significant edit action. If insignificant actions have
+ * been posted after the last signficant action, the insignificant
+ * ones will be undone first.
+ *
+ * <p>However, if {@link #end()} has been called on this
+ * UndoManager, it will behave like a normal {@link
+ * CompoundEdit}. In this case, all actions will be undone in
+ * reverse order of addition. Typical applications will never call
+ * {@link #end()} on their <code>UndoManager</code>.
+ *
+ * @throws CannotUndoException if no action can be undone.
+ *
+ * @see #canUndo()
+ * @see #redo()
+ * @see #undoOrRedo()
+ */
+ public synchronized void undo()
+ throws CannotUndoException
+ {
+ if (!isInProgress())
+ {
+ super.undo();
+ return;
+ }
+
+ UndoableEdit edit = editToBeUndone();
+ if (edit == null)
+ throw new CannotUndoException();
+
+ undoTo(edit);
+ }
+
+
+ /**
+ * Determines whether it would be possible to undo this editing
+ * action.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * undone; <code>false</code> otherwise.
+ *
+ * @see #undo()
+ * @see #canRedo()
+ * @see #canUndoOrRedo()
+ */
+ public synchronized boolean canUndo()
+ {
+ UndoableEdit edit;
+
+ if (!isInProgress())
+ return super.canUndo();
+
+ edit = editToBeUndone();
+ return edit != null && edit.canUndo();
+ }
+
+
+
+ /**
+ * Redoes one significant edit action. If insignificant actions have
+ * been posted in between, the insignificant ones will be redone
+ * first.
+ *
+ * <p>However, if {@link #end()} has been called on this
+ * UndoManager, it will behave like a normal {@link
+ * CompoundEdit}. In this case, <em>all</em> actions will be redone
+ * in order of addition. Typical applications will never call {@link
+ * #end()} on their <code>UndoManager</code>.
+ *
+ * @throws CannotRedoException if no action can be redone.
+ *
+ * @see #canRedo()
+ * @see #redo()
+ * @see #undoOrRedo()
+ */
+ public synchronized void redo()
+ throws CannotRedoException
+ {
+ if (!isInProgress())
+ {
+ super.redo();
+ return;
+ }
+
+ UndoableEdit edit = editToBeRedone();
+ if (edit == null)
+ throw new CannotRedoException();
+
+ redoTo(edit);
+ }
+
+
+ /**
+ * Determines whether it would be possible to redo this editing
+ * action.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * redone; <code>false</code> otherwise.
+ *
+ * @see #redo()
+ * @see #canUndo()
+ * @see #canUndoOrRedo()
+ */
+ public synchronized boolean canRedo()
+ {
+ UndoableEdit edit;
+
+ if (!isInProgress())
+ return super.canRedo();
+
+ edit = editToBeRedone();
+ return edit != null && edit.canRedo();
+ }
+
+
+ /**
+ * Registers an undoable editing action with this UndoManager. If
+ * the capacity <code>limit</code> is reached, the oldest action
+ * will be discarded (and receives a {@linkplain UndoableEdit#die()
+ * die message}. Equally, any actions that were undone (but not re-done)
+ * will be discarded, too.
+ *
+ * @param edit the editing action that is added to this UndoManager.
+ *
+ * @return <code>true</code> if <code>edit</code> could be
+ * incorporated; <code>false</code> if <code>edit</code> has not
+ * been incorporated because {@link #end()} has already been called
+ * on this <code>UndoManager</code>.
+ */
+ public synchronized boolean addEdit(UndoableEdit edit)
+ {
+ boolean result;
+
+ // Discard any edits starting at indexOfNextAdd.
+ trimEdits(indexOfNextAdd, edits.size() - 1);
+
+ result = super.addEdit(edit);
+ indexOfNextAdd = edits.size();
+ trimForLimit();
+ return result;
+ }
+
+
+ /**
+ * Calculates a localized text for presenting the undo or redo
+ * action to the user, for example in the form of a menu command.
+ *
+ * <p>This is useful for applications that do not present a separate
+ * undo and redo facility, but just have a single menu item for
+ * undoing and redoing the very last action. Such applications will
+ * use an <code>UndoManager</code> whose <code>limit</code> is 1.
+ *
+ * @return the redo presentation name if the last action has already
+ * been undone, or the undo presentation name otherwise.
+ *
+ * @see #getUndoPresentationName()
+ * @see #getRedoPresentationName()
+ */
+ public synchronized String getUndoOrRedoPresentationName()
+ {
+ if (indexOfNextAdd == edits.size())
+ return getUndoPresentationName();
+ else
+ return getRedoPresentationName();
+ }
+
+
+ /**
+ * Calculates a localized text for presenting the undo action
+ * to the user, for example in the form of a menu command.
+ */
+ public synchronized String getUndoPresentationName()
+ {
+ UndoableEdit edit;
+
+ if (!isInProgress())
+ return super.getUndoPresentationName();
+
+ edit = editToBeUndone();
+ if (edit == null)
+ return UIManager.getString("AbstractUndoableEdit.undoText");
+ else
+ return edit.getUndoPresentationName();
+ }
+
+
+ /**
+ * Calculates a localized text for presenting the redo action
+ * to the user, for example in the form of a menu command.
+ */
+ public synchronized String getRedoPresentationName()
+ {
+ UndoableEdit edit;
+
+ if (!isInProgress())
+ return super.getRedoPresentationName();
+
+ edit = editToBeRedone();
+ if (edit == null)
+ return UIManager.getString("AbstractUndoableEdit.redoText");
+ else
+ return edit.getRedoPresentationName();
+ }
+
+
+ /**
+ * Registers the edit action of an {@link UndoableEditEvent}
+ * with this UndoManager.
+ *
+ * <p><b>Thread Safety:</b> This method may safely be invoked from
+ * concurrent threads. The caller does not need to perform external
+ * synchronization. This means that {@link
+ * javax.swing.event.UndoableEditEvent} sources do not need to broadcast
+ * their events from inside the Swing worker thread.
+ *
+ * @param event the event whose <code>edit</code> will be
+ * passed to {@link #addEdit}.
+ *
+ * @see UndoableEditEvent#getEdit()
+ * @see #addEdit
+ */
+ public void undoableEditHappened(UndoableEditEvent event)
+ {
+ // Note that this method does not need to be synchronized,
+ // because addEdit will obtain and release the mutex.
+ addEdit(event.getEdit());
+ }
+}
diff --git a/libjava/classpath/javax/swing/undo/UndoableEdit.java b/libjava/classpath/javax/swing/undo/UndoableEdit.java
new file mode 100644
index 000000000..1a08ecaa5
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/UndoableEdit.java
@@ -0,0 +1,157 @@
+/* UndoableEdit.java --
+ Copyright (C) 2002, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+/**
+ * An editing operation that supports undo/redoability.
+ *
+ * @author Andrew Selkirk
+ */
+public interface UndoableEdit
+{
+
+ /**
+ * Incorporates another editing action into this one, thus forming a
+ * combined action.
+ *
+ * @param edit the editing action to be incorporated.
+ *
+ * @return <code>true</code> if the edit was combined successfully, and
+ * <code>false</code> if it could not be combined.
+ */
+ boolean addEdit(UndoableEdit edit);
+
+ /**
+ * Determines whether it would be possible to redo this editing
+ * action.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * redone, <code>false</code> otherwise.
+ *
+ * @see #redo()
+ * @see #canUndo()
+ */
+ boolean canRedo();
+
+ /**
+ * Determines whether it would be possible to undo this editing
+ * action.
+ *
+ * @return <code>true</code> to indicate that this action can be
+ * undone, <code>false</code> otherwise.
+ *
+ * @see #undo()
+ * @see #canRedo()
+ */
+ boolean canUndo();
+
+ /**
+ * Informs this edit action that it will no longer be used. Some
+ * actions might use this information to release resources, for
+ * example open files. Called by {@link UndoManager} before this
+ * action is removed from the edit queue.
+ */
+ void die();
+
+ /**
+ * Returns a human-readable, localized name that describes this
+ * editing action and can be displayed to the user.
+ *
+ * @return The presentation name.
+ */
+ String getPresentationName();
+
+ /**
+ * Returns the redo presentation name.
+ *
+ * @return The redo presentation name.
+ */
+ String getRedoPresentationName();
+
+ /**
+ * Returns the undo presentation name.
+ *
+ * @return The undo presentation name.
+ */
+ String getUndoPresentationName();
+
+ /**
+ * Determines whether this editing action is significant enough for
+ * being seperately undoable by the user. A typical significant
+ * action would be the resizing of an object. However, changing the
+ * selection in a text document would usually not be considered
+ * significant.
+ *
+ * @return <code>true</code> to indicate that the action is
+ * significant enough for being separately undoable, or
+ * <code>false</code> otherwise.
+ */
+ boolean isSignificant();
+
+ /**
+ * Redoes this editing action.
+ *
+ * @throws CannotRedoException if the edit cannot be undone.
+ *
+ * @see #canRedo()
+ * @see #undo()
+ */
+ void redo() throws CannotRedoException;
+
+ /**
+ * Incorporates another editing action into this one, thus forming a
+ * combined action that replaces the argument action.
+ *
+ * @param edit the editing action to be replaced.
+ *
+ * @return <code>true</code> if the edit is successfully replaced, and
+ * <code>false</code> otherwise.
+ */
+ boolean replaceEdit(UndoableEdit edit);
+
+ /**
+ * Undoes this editing action.
+ *
+ * @throws CannotUndoException if the edit cannot be undone.
+ *
+ * @see #canUndo()
+ * @see #redo()
+ */
+ void undo() throws CannotUndoException;
+
+}
diff --git a/libjava/classpath/javax/swing/undo/UndoableEditSupport.java b/libjava/classpath/javax/swing/undo/UndoableEditSupport.java
new file mode 100644
index 000000000..2e5d90954
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/UndoableEditSupport.java
@@ -0,0 +1,272 @@
+/* UndoableEditSupport.java --
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.undo;
+
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.event.UndoableEditListener;
+
+/**
+ * A helper class for supporting {@link
+ * javax.swing.event.UndoableEditListener}.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class UndoableEditSupport
+{
+ /**
+ * The number of times that {@link #beginUpdate()} has been called
+ * without a matching call to {@link #endUpdate()}.
+ */
+ protected int updateLevel;
+
+
+ /**
+ * compoundEdit
+ */
+ protected CompoundEdit compoundEdit;
+
+
+ /**
+ * The currently registered listeners.
+ */
+ protected Vector<UndoableEditListener> listeners =
+ new Vector<UndoableEditListener>();
+
+
+ /**
+ * The source of the broadcast UndoableEditEvents.
+ */
+ protected Object realSource;
+
+
+ /**
+ * Constructs a new helper for broadcasting UndoableEditEvents. The
+ * events will indicate the newly constructed
+ * <code>UndoableEditSupport</code> instance as their source.
+ *
+ * @see #UndoableEditSupport(java.lang.Object)
+ */
+ public UndoableEditSupport()
+ {
+ realSource = this;
+ }
+
+
+ /**
+ * Constructs a new helper for broadcasting UndoableEditEvents.
+ *
+ * @param realSource the source of the UndoableEditEvents that will
+ * be broadcast by this helper. If <code>realSource</code> is
+ * <code>null</code>, the events will indicate the newly constructed
+ * <code>UndoableEditSupport</code> instance as their source.
+ */
+ public UndoableEditSupport(Object realSource)
+ {
+ if (realSource == null)
+ realSource = this;
+ this.realSource = realSource;
+ }
+
+
+ /**
+ * Returns a string representation of this object that may be useful
+ * for debugging.
+ */
+ public String toString()
+ {
+ // Note that often, this.realSource == this. Therefore, dumping
+ // realSource without additional checks may lead to infinite
+ // recursion. See Classpath bug #7119.
+ return super.toString() + " updateLevel: " + updateLevel
+ + " listeners: " + listeners + " compoundEdit: " + compoundEdit;
+ }
+
+
+ /**
+ * Registers a listener.
+ *
+ * @param val the listener to be added.
+ */
+ public synchronized void addUndoableEditListener(UndoableEditListener val)
+ {
+ listeners.add(val);
+ }
+
+
+ /**
+ * Unregisters a listener.
+ * @param val the listener to be removed.
+ */
+ public synchronized void removeUndoableEditListener(UndoableEditListener val)
+ {
+ listeners.removeElement(val);
+ }
+
+
+ /**
+ * Returns an array containing the currently registered listeners.
+ */
+ public synchronized UndoableEditListener[] getUndoableEditListeners()
+ {
+ UndoableEditListener[] result = new UndoableEditListener[listeners.size()];
+ return listeners.toArray(result);
+ }
+
+
+ /**
+ * Notifies all registered listeners that an {@link
+ * UndoableEditEvent} has occured.
+ *
+ * <p><b>Lack of Thread Safety:</b> It is <em>not</em> safe to call
+ * this method from concurrent threads, unless the call is protected
+ * by a synchronization on this <code>UndoableEditSupport</code>
+ * instance.
+ *
+ * @param edit the edit action to be posted.
+ */
+ protected void _postEdit(UndoableEdit edit)
+ {
+ UndoableEditEvent event;
+ Iterator<UndoableEditListener> iter;
+
+ // Do nothing if we have no listeners.
+ if (listeners.isEmpty())
+ return;
+
+ event = new UndoableEditEvent(realSource, edit);
+
+ // We clone the vector because this allows listeners to register
+ // or unregister listeners in their undoableEditHappened method.
+ // Otherwise, this would throw exceptions (in the case of
+ // Iterator, a java.util.ConcurrentModificationException; in the
+ // case of a direct loop over the Vector elements, some
+ // index-out-of-bounds exception).
+ iter = new Vector<UndoableEditListener>(listeners).iterator();
+ while (iter.hasNext())
+ iter.next().undoableEditHappened(event);
+ }
+
+
+ /**
+ * If {@link #beginUpdate} has been called (so that the current
+ * update level is greater than zero), adds the specified edit
+ * to {@link #compoundEdit}. Otherwise, notify listeners of the
+ * edit by calling {@link #_postEdit(UndoableEdit)}.
+ *
+ * <p><b>Thread Safety:</b> It is safe to call this method from any
+ * thread without external synchronization.
+ *
+ * @param edit the edit action to be posted.
+ */
+ public synchronized void postEdit(UndoableEdit edit)
+ {
+ if (compoundEdit != null)
+ compoundEdit.addEdit(edit);
+ else
+ _postEdit(edit);
+ }
+
+
+ /**
+ * Returns the current update level.
+ */
+ public int getUpdateLevel()
+ {
+ return updateLevel;
+ }
+
+
+ /**
+ * Starts a (possibly nested) update session. If the current update
+ * level is zero, {@link #compoundEdit} is set to the result of the
+ * {@link #createCompoundEdit} method. In any case, the update level
+ * is increased by one.
+ *
+ * <p><b>Thread Safety:</b> It is safe to call this method from any
+ * thread without external synchronization.
+ */
+ public synchronized void beginUpdate()
+ {
+ if (compoundEdit == null)
+ compoundEdit = createCompoundEdit();
+ ++updateLevel;
+ }
+
+
+ /**
+ * Creates a new instance of {@link CompoundEdit}. Called by {@link
+ * #beginUpdate}. If a subclass wants {@link #beginUpdate} to work
+ * on a specific {@link #compoundEdit}, it should override this
+ * method.
+ *
+ * @return a newly created instance of {@link CompoundEdit}.
+ */
+ protected CompoundEdit createCompoundEdit()
+ {
+ return new CompoundEdit();
+ }
+
+
+ /**
+ * Ends an update session. If the terminated session was the
+ * outermost session, {@link #compoundEdit} will receive an
+ * <code>end</code> message, and {@link #_postEdit} gets called in
+ * order to notify any listeners. Finally, the
+ * <code>compoundEdit</code> is discarded.
+ *
+ * <p><b>Thread Safety:</b> It is safe to call this method from any
+ * thread without external synchronization.
+ */
+ public synchronized void endUpdate()
+ {
+ if (updateLevel == 0)
+ throw new IllegalStateException();
+
+ if (--updateLevel > 0)
+ return;
+
+ compoundEdit.end();
+ _postEdit(compoundEdit);
+ compoundEdit = null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/undo/package.html b/libjava/classpath/javax/swing/undo/package.html
new file mode 100644
index 000000000..125bd4446
--- /dev/null
+++ b/libjava/classpath/javax/swing/undo/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.undo package.
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.undo</title></head>
+
+<body>
+<p>Provides a mechanism to support undo/redo operations.</p>
+
+</body>
+</html>