From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; 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. --- libjava/classpath/javax/swing/AbstractAction.java | 276 ++ libjava/classpath/javax/swing/AbstractButton.java | 2652 ++++++++++ .../classpath/javax/swing/AbstractCellEditor.java | 195 + .../classpath/javax/swing/AbstractListModel.java | 181 + .../javax/swing/AbstractSpinnerModel.java | 123 + libjava/classpath/javax/swing/Action.java | 153 + libjava/classpath/javax/swing/ActionMap.java | 195 + libjava/classpath/javax/swing/BorderFactory.java | 456 ++ .../classpath/javax/swing/BoundedRangeModel.java | 193 + libjava/classpath/javax/swing/Box.java | 290 ++ libjava/classpath/javax/swing/BoxLayout.java | 451 ++ libjava/classpath/javax/swing/ButtonGroup.java | 223 + libjava/classpath/javax/swing/ButtonModel.java | 301 ++ libjava/classpath/javax/swing/CellEditor.java | 108 + .../classpath/javax/swing/CellRendererPane.java | 251 + libjava/classpath/javax/swing/ComboBoxEditor.java | 96 + libjava/classpath/javax/swing/ComboBoxModel.java | 69 + .../swing/CompatibilityFocusTraversalPolicy.java | 164 + .../classpath/javax/swing/ComponentInputMap.java | 141 + libjava/classpath/javax/swing/DebugGraphics.java | 1125 +++++ .../javax/swing/DefaultBoundedRangeModel.java | 475 ++ .../classpath/javax/swing/DefaultButtonModel.java | 578 +++ .../classpath/javax/swing/DefaultCellEditor.java | 570 +++ .../javax/swing/DefaultComboBoxModel.java | 286 ++ .../javax/swing/DefaultDesktopManager.java | 644 +++ .../classpath/javax/swing/DefaultFocusManager.java | 169 + .../javax/swing/DefaultListCellRenderer.java | 199 + .../classpath/javax/swing/DefaultListModel.java | 521 ++ .../javax/swing/DefaultListSelectionModel.java | 855 ++++ .../javax/swing/DefaultSingleSelectionModel.java | 191 + libjava/classpath/javax/swing/DesktopManager.java | 177 + libjava/classpath/javax/swing/FocusManager.java | 524 ++ libjava/classpath/javax/swing/GrayFilter.java | 97 + libjava/classpath/javax/swing/Icon.java | 73 + libjava/classpath/javax/swing/ImageIcon.java | 471 ++ libjava/classpath/javax/swing/InputMap.java | 235 + libjava/classpath/javax/swing/InputVerifier.java | 79 + .../swing/InternalFrameFocusTraversalPolicy.java | 60 + libjava/classpath/javax/swing/JApplet.java | 224 + libjava/classpath/javax/swing/JButton.java | 276 ++ libjava/classpath/javax/swing/JCheckBox.java | 176 + .../classpath/javax/swing/JCheckBoxMenuItem.java | 275 ++ libjava/classpath/javax/swing/JColorChooser.java | 644 +++ libjava/classpath/javax/swing/JComboBox.java | 1499 ++++++ libjava/classpath/javax/swing/JComponent.java | 3801 +++++++++++++++ libjava/classpath/javax/swing/JDesktopPane.java | 386 ++ libjava/classpath/javax/swing/JDialog.java | 583 +++ libjava/classpath/javax/swing/JEditorPane.java | 1222 +++++ libjava/classpath/javax/swing/JFileChooser.java | 1626 ++++++ .../classpath/javax/swing/JFormattedTextField.java | 648 +++ libjava/classpath/javax/swing/JFrame.java | 410 ++ libjava/classpath/javax/swing/JInternalFrame.java | 1820 +++++++ libjava/classpath/javax/swing/JLabel.java | 1122 +++++ libjava/classpath/javax/swing/JLayeredPane.java | 755 +++ libjava/classpath/javax/swing/JList.java | 2499 ++++++++++ libjava/classpath/javax/swing/JMenu.java | 1290 +++++ libjava/classpath/javax/swing/JMenuBar.java | 692 +++ libjava/classpath/javax/swing/JMenuItem.java | 809 +++ libjava/classpath/javax/swing/JOptionPane.java | 1637 +++++++ libjava/classpath/javax/swing/JPanel.java | 203 + libjava/classpath/javax/swing/JPasswordField.java | 297 ++ libjava/classpath/javax/swing/JPopupMenu.java | 947 ++++ libjava/classpath/javax/swing/JProgressBar.java | 861 ++++ libjava/classpath/javax/swing/JRadioButton.java | 256 + .../javax/swing/JRadioButtonMenuItem.java | 230 + libjava/classpath/javax/swing/JRootPane.java | 700 +++ libjava/classpath/javax/swing/JScrollBar.java | 700 +++ libjava/classpath/javax/swing/JScrollPane.java | 712 +++ libjava/classpath/javax/swing/JSeparator.java | 218 + libjava/classpath/javax/swing/JSlider.java | 1144 +++++ libjava/classpath/javax/swing/JSpinner.java | 754 +++ libjava/classpath/javax/swing/JSplitPane.java | 942 ++++ libjava/classpath/javax/swing/JTabbedPane.java | 1728 +++++++ libjava/classpath/javax/swing/JTable.java | 5157 ++++++++++++++++++++ libjava/classpath/javax/swing/JTextArea.java | 607 +++ libjava/classpath/javax/swing/JTextField.java | 570 +++ libjava/classpath/javax/swing/JTextPane.java | 424 ++ libjava/classpath/javax/swing/JToggleButton.java | 350 ++ libjava/classpath/javax/swing/JToolBar.java | 798 +++ libjava/classpath/javax/swing/JToolTip.java | 244 + libjava/classpath/javax/swing/JTree.java | 3186 ++++++++++++ libjava/classpath/javax/swing/JViewport.java | 948 ++++ libjava/classpath/javax/swing/JWindow.java | 290 ++ libjava/classpath/javax/swing/KeyStroke.java | 123 + libjava/classpath/javax/swing/KeyboardManager.java | 281 ++ .../javax/swing/LayoutFocusTraversalPolicy.java | 89 + .../classpath/javax/swing/ListCellRenderer.java | 50 + libjava/classpath/javax/swing/ListModel.java | 80 + .../classpath/javax/swing/ListSelectionModel.java | 332 ++ libjava/classpath/javax/swing/LookAndFeel.java | 433 ++ libjava/classpath/javax/swing/MenuElement.java | 89 + .../javax/swing/MenuSelectionManager.java | 440 ++ .../javax/swing/MutableComboBoxModel.java | 82 + libjava/classpath/javax/swing/OverlayLayout.java | 412 ++ libjava/classpath/javax/swing/Popup.java | 301 ++ libjava/classpath/javax/swing/PopupFactory.java | 171 + libjava/classpath/javax/swing/ProgressMonitor.java | 460 ++ .../javax/swing/ProgressMonitorInputStream.java | 249 + libjava/classpath/javax/swing/Renderer.java | 68 + libjava/classpath/javax/swing/RepaintManager.java | 860 ++++ .../classpath/javax/swing/RootPaneContainer.java | 96 + .../classpath/javax/swing/ScrollPaneConstants.java | 152 + .../classpath/javax/swing/ScrollPaneLayout.java | 491 ++ libjava/classpath/javax/swing/Scrollable.java | 106 + .../javax/swing/SingleSelectionModel.java | 103 + .../classpath/javax/swing/SizeRequirements.java | 523 ++ libjava/classpath/javax/swing/SizeSequence.java | 225 + .../javax/swing/SortingFocusTraversalPolicy.java | 333 ++ .../classpath/javax/swing/SpinnerDateModel.java | 317 ++ .../classpath/javax/swing/SpinnerListModel.java | 295 ++ libjava/classpath/javax/swing/SpinnerModel.java | 111 + .../classpath/javax/swing/SpinnerNumberModel.java | 361 ++ libjava/classpath/javax/swing/Spring.java | 745 +++ libjava/classpath/javax/swing/SpringLayout.java | 832 ++++ libjava/classpath/javax/swing/SwingConstants.java | 76 + libjava/classpath/javax/swing/SwingUtilities.java | 1754 +++++++ libjava/classpath/javax/swing/Timer.java | 476 ++ libjava/classpath/javax/swing/ToolTipManager.java | 593 +++ libjava/classpath/javax/swing/TransferHandler.java | 654 +++ libjava/classpath/javax/swing/UIDefaults.java | 847 ++++ libjava/classpath/javax/swing/UIManager.java | 948 ++++ .../swing/UnsupportedLookAndFeelException.java | 57 + libjava/classpath/javax/swing/ViewportLayout.java | 202 + libjava/classpath/javax/swing/WindowConstants.java | 79 + .../javax/swing/border/AbstractBorder.java | 199 + .../classpath/javax/swing/border/BevelBorder.java | 584 +++ libjava/classpath/javax/swing/border/Border.java | 103 + .../javax/swing/border/CompoundBorder.java | 255 + .../classpath/javax/swing/border/EmptyBorder.java | 223 + .../classpath/javax/swing/border/EtchedBorder.java | 414 ++ .../classpath/javax/swing/border/LineBorder.java | 347 ++ .../classpath/javax/swing/border/MatteBorder.java | 407 ++ .../javax/swing/border/SoftBevelBorder.java | 327 ++ .../classpath/javax/swing/border/TitledBorder.java | 1078 ++++ .../javax/swing/border/doc-files/BevelBorder-1.png | Bin 0 -> 4440 bytes .../javax/swing/border/doc-files/BevelBorder-2.png | Bin 0 -> 3667 bytes .../javax/swing/border/doc-files/BevelBorder-3.png | Bin 0 -> 4981 bytes .../javax/swing/border/doc-files/EmptyBorder-1.png | Bin 0 -> 11522 bytes .../swing/border/doc-files/EtchedBorder-1.png | Bin 0 -> 4820 bytes .../swing/border/doc-files/EtchedBorder-2.png | Bin 0 -> 3850 bytes .../javax/swing/border/doc-files/LineBorder-1.png | Bin 0 -> 6133 bytes .../javax/swing/border/doc-files/MatteBorder-1.png | Bin 0 -> 5447 bytes .../javax/swing/border/doc-files/MatteBorder-2.png | Bin 0 -> 5099 bytes .../javax/swing/border/doc-files/MatteBorder-3.png | Bin 0 -> 5726 bytes .../javax/swing/border/doc-files/MatteBorder-4.png | Bin 0 -> 7220 bytes .../javax/swing/border/doc-files/MatteBorder-5.png | Bin 0 -> 7971 bytes .../javax/swing/border/doc-files/MatteBorder-6.png | Bin 0 -> 5511 bytes .../swing/border/doc-files/SoftBevelBorder-1.png | Bin 0 -> 4877 bytes .../swing/border/doc-files/SoftBevelBorder-2.png | Bin 0 -> 3860 bytes .../swing/border/doc-files/SoftBevelBorder-3.png | Bin 0 -> 5155 bytes libjava/classpath/javax/swing/border/package.html | 47 + .../colorchooser/AbstractColorChooserPanel.java | 193 + .../colorchooser/ColorChooserComponentFactory.java | 86 + .../swing/colorchooser/ColorSelectionModel.java | 86 + .../colorchooser/DefaultColorSelectionModel.java | 163 + .../swing/colorchooser/DefaultHSBChooserPanel.java | 890 ++++ .../swing/colorchooser/DefaultPreviewPanel.java | 318 ++ .../swing/colorchooser/DefaultRGBChooserPanel.java | 402 ++ .../colorchooser/DefaultSwatchChooserPanel.java | 896 ++++ .../javax/swing/colorchooser/package.html | 47 + .../classpath/javax/swing/event/AncestorEvent.java | 100 + .../javax/swing/event/AncestorListener.java | 69 + .../classpath/javax/swing/event/CaretEvent.java | 70 + .../classpath/javax/swing/event/CaretListener.java | 56 + .../javax/swing/event/CellEditorListener.java | 62 + .../classpath/javax/swing/event/ChangeEvent.java | 66 + .../javax/swing/event/ChangeListener.java | 63 + .../classpath/javax/swing/event/DocumentEvent.java | 156 + .../javax/swing/event/DocumentListener.java | 68 + .../javax/swing/event/EventListenerList.java | 359 ++ .../javax/swing/event/HyperlinkEvent.java | 162 + .../javax/swing/event/HyperlinkListener.java | 57 + .../javax/swing/event/InternalFrameAdapter.java | 126 + .../javax/swing/event/InternalFrameEvent.java | 154 + .../javax/swing/event/InternalFrameListener.java | 92 + .../classpath/javax/swing/event/ListDataEvent.java | 132 + .../javax/swing/event/ListDataListener.java | 82 + .../javax/swing/event/ListSelectionEvent.java | 135 + .../javax/swing/event/ListSelectionListener.java | 61 + .../javax/swing/event/MenuDragMouseEvent.java | 104 + .../javax/swing/event/MenuDragMouseListener.java | 74 + libjava/classpath/javax/swing/event/MenuEvent.java | 59 + .../classpath/javax/swing/event/MenuKeyEvent.java | 102 + .../javax/swing/event/MenuKeyListener.java | 68 + .../classpath/javax/swing/event/MenuListener.java | 68 + .../javax/swing/event/MouseInputAdapter.java | 119 + .../javax/swing/event/MouseInputListener.java | 53 + .../javax/swing/event/PopupMenuEvent.java | 58 + .../javax/swing/event/PopupMenuListener.java | 68 + .../swing/event/SwingPropertyChangeSupport.java | 70 + .../javax/swing/event/TableColumnModelEvent.java | 93 + .../swing/event/TableColumnModelListener.java | 94 + .../javax/swing/event/TableModelEvent.java | 220 + .../javax/swing/event/TableModelListener.java | 60 + .../javax/swing/event/TreeExpansionEvent.java | 77 + .../javax/swing/event/TreeExpansionListener.java | 62 + .../javax/swing/event/TreeModelEvent.java | 168 + .../javax/swing/event/TreeModelListener.java | 74 + .../javax/swing/event/TreeSelectionEvent.java | 257 + .../javax/swing/event/TreeSelectionListener.java | 60 + .../javax/swing/event/TreeWillExpandListener.java | 65 + .../javax/swing/event/UndoableEditEvent.java | 80 + .../javax/swing/event/UndoableEditListener.java | 56 + libjava/classpath/javax/swing/event/package.html | 47 + .../javax/swing/filechooser/FileFilter.java | 85 + .../javax/swing/filechooser/FileSystemView.java | 410 ++ .../javax/swing/filechooser/FileView.java | 128 + .../swing/filechooser/UnixFileSystemView.java | 180 + .../classpath/javax/swing/filechooser/package.html | 47 + libjava/classpath/javax/swing/package.html | 47 + .../javax/swing/plaf/ActionMapUIResource.java | 62 + .../javax/swing/plaf/BorderUIResource.java | 930 ++++ libjava/classpath/javax/swing/plaf/ButtonUI.java | 52 + .../classpath/javax/swing/plaf/ColorChooserUI.java | 58 + .../javax/swing/plaf/ColorUIResource.java | 121 + libjava/classpath/javax/swing/plaf/ComboBoxUI.java | 92 + .../swing/plaf/ComponentInputMapUIResource.java | 68 + .../classpath/javax/swing/plaf/ComponentUI.java | 331 ++ .../classpath/javax/swing/plaf/DesktopIconUI.java | 56 + .../classpath/javax/swing/plaf/DesktopPaneUI.java | 58 + .../javax/swing/plaf/DimensionUIResource.java | 66 + .../classpath/javax/swing/plaf/FileChooserUI.java | 138 + .../classpath/javax/swing/plaf/FontUIResource.java | 99 + .../classpath/javax/swing/plaf/IconUIResource.java | 124 + .../javax/swing/plaf/InputMapUIResource.java | 61 + .../javax/swing/plaf/InsetsUIResource.java | 76 + .../javax/swing/plaf/InternalFrameUI.java | 59 + libjava/classpath/javax/swing/plaf/LabelUI.java | 59 + libjava/classpath/javax/swing/plaf/ListUI.java | 114 + libjava/classpath/javax/swing/plaf/MenuBarUI.java | 59 + libjava/classpath/javax/swing/plaf/MenuItemUI.java | 59 + .../classpath/javax/swing/plaf/OptionPaneUI.java | 75 + libjava/classpath/javax/swing/plaf/PanelUI.java | 58 + .../classpath/javax/swing/plaf/PopupMenuUI.java | 117 + .../classpath/javax/swing/plaf/ProgressBarUI.java | 59 + libjava/classpath/javax/swing/plaf/RootPaneUI.java | 58 + .../classpath/javax/swing/plaf/ScrollBarUI.java | 58 + .../classpath/javax/swing/plaf/ScrollPaneUI.java | 59 + .../classpath/javax/swing/plaf/SeparatorUI.java | 59 + libjava/classpath/javax/swing/plaf/SliderUI.java | 59 + libjava/classpath/javax/swing/plaf/SpinnerUI.java | 59 + .../classpath/javax/swing/plaf/SplitPaneUI.java | 133 + .../classpath/javax/swing/plaf/TabbedPaneUI.java | 110 + .../classpath/javax/swing/plaf/TableHeaderUI.java | 59 + libjava/classpath/javax/swing/plaf/TableUI.java | 59 + libjava/classpath/javax/swing/plaf/TextUI.java | 281 ++ libjava/classpath/javax/swing/plaf/ToolBarUI.java | 59 + libjava/classpath/javax/swing/plaf/ToolTipUI.java | 59 + libjava/classpath/javax/swing/plaf/TreeUI.java | 211 + libjava/classpath/javax/swing/plaf/UIResource.java | 59 + libjava/classpath/javax/swing/plaf/ViewportUI.java | 60 + .../javax/swing/plaf/basic/BasicArrowButton.java | 422 ++ .../javax/swing/plaf/basic/BasicBorders.java | 1768 +++++++ .../swing/plaf/basic/BasicButtonListener.java | 370 ++ .../javax/swing/plaf/basic/BasicButtonUI.java | 636 +++ .../swing/plaf/basic/BasicCheckBoxMenuItemUI.java | 102 + .../javax/swing/plaf/basic/BasicCheckBoxUI.java | 75 + .../swing/plaf/basic/BasicColorChooserUI.java | 344 ++ .../swing/plaf/basic/BasicComboBoxEditor.java | 181 + .../swing/plaf/basic/BasicComboBoxRenderer.java | 151 + .../javax/swing/plaf/basic/BasicComboBoxUI.java | 1410 ++++++ .../javax/swing/plaf/basic/BasicComboPopup.java | 1104 +++++ .../javax/swing/plaf/basic/BasicDesktopIconUI.java | 592 +++ .../javax/swing/plaf/basic/BasicDesktopPaneUI.java | 470 ++ .../swing/plaf/basic/BasicDirectoryModel.java | 586 +++ .../javax/swing/plaf/basic/BasicEditorPaneUI.java | 96 + .../javax/swing/plaf/basic/BasicFileChooserUI.java | 1437 ++++++ .../plaf/basic/BasicFormattedTextFieldUI.java | 69 + .../javax/swing/plaf/basic/BasicGraphicsUtils.java | 821 ++++ .../javax/swing/plaf/basic/BasicHTML.java | 471 ++ .../javax/swing/plaf/basic/BasicIconFactory.java | 328 ++ .../plaf/basic/BasicInternalFrameTitlePane.java | 1015 ++++ .../swing/plaf/basic/BasicInternalFrameUI.java | 1786 +++++++ .../javax/swing/plaf/basic/BasicLabelUI.java | 542 ++ .../javax/swing/plaf/basic/BasicListUI.java | 1421 ++++++ .../javax/swing/plaf/basic/BasicLookAndFeel.java | 1734 +++++++ .../javax/swing/plaf/basic/BasicMenuBarUI.java | 481 ++ .../javax/swing/plaf/basic/BasicMenuItemUI.java | 1339 +++++ .../javax/swing/plaf/basic/BasicMenuUI.java | 678 +++ .../javax/swing/plaf/basic/BasicOptionPaneUI.java | 1404 ++++++ .../javax/swing/plaf/basic/BasicPanelUI.java | 132 + .../swing/plaf/basic/BasicPasswordFieldUI.java | 74 + .../plaf/basic/BasicPopupMenuSeparatorUI.java | 114 + .../javax/swing/plaf/basic/BasicPopupMenuUI.java | 1019 ++++ .../javax/swing/plaf/basic/BasicProgressBarUI.java | 962 ++++ .../plaf/basic/BasicRadioButtonMenuItemUI.java | 101 + .../javax/swing/plaf/basic/BasicRadioButtonUI.java | 301 ++ .../javax/swing/plaf/basic/BasicRootPaneUI.java | 292 ++ .../javax/swing/plaf/basic/BasicScrollBarUI.java | 1513 ++++++ .../javax/swing/plaf/basic/BasicScrollPaneUI.java | 823 ++++ .../javax/swing/plaf/basic/BasicSeparatorUI.java | 252 + .../javax/swing/plaf/basic/BasicSliderUI.java | 2339 +++++++++ .../javax/swing/plaf/basic/BasicSpinnerUI.java | 561 +++ .../swing/plaf/basic/BasicSplitPaneDivider.java | 1077 ++++ .../javax/swing/plaf/basic/BasicSplitPaneUI.java | 1623 ++++++ .../javax/swing/plaf/basic/BasicTabbedPaneUI.java | 4003 +++++++++++++++ .../javax/swing/plaf/basic/BasicTableHeaderUI.java | 566 +++ .../javax/swing/plaf/basic/BasicTableUI.java | 1410 ++++++ .../javax/swing/plaf/basic/BasicTextAreaUI.java | 116 + .../javax/swing/plaf/basic/BasicTextFieldUI.java | 118 + .../javax/swing/plaf/basic/BasicTextPaneUI.java | 92 + .../javax/swing/plaf/basic/BasicTextUI.java | 1538 ++++++ .../swing/plaf/basic/BasicToggleButtonUI.java | 134 + .../swing/plaf/basic/BasicToolBarSeparatorUI.java | 124 + .../javax/swing/plaf/basic/BasicToolBarUI.java | 1610 ++++++ .../javax/swing/plaf/basic/BasicToolTipUI.java | 292 ++ .../javax/swing/plaf/basic/BasicTreeUI.java | 3939 +++++++++++++++ .../javax/swing/plaf/basic/BasicViewportUI.java | 75 + .../javax/swing/plaf/basic/ComboPopup.java | 103 + .../javax/swing/plaf/basic/DefaultMenuLayout.java | 78 + .../javax/swing/plaf/basic/SharedUIDefaults.java | 91 + .../swing/plaf/basic/doc-files/BasicBorders-1.png | Bin 0 -> 454 bytes .../swing/plaf/basic/doc-files/BasicBorders-2.png | Bin 0 -> 857 bytes .../doc-files/BasicBorders.ButtonBorder-1.png | Bin 0 -> 1787 bytes .../basic/doc-files/BasicBorders.FieldBorder-1.png | Bin 0 -> 5267 bytes .../doc-files/BasicBorders.MarginBorder-1.png | Bin 0 -> 14735 bytes .../doc-files/BasicBorders.MenuBarBorder-1.png | Bin 0 -> 3180 bytes .../doc-files/BasicBorders.RadioButtonBorder-1.png | Bin 0 -> 2667 bytes .../doc-files/BasicBorders.SplitPaneBorder-1.png | Bin 0 -> 8803 bytes .../doc-files/BasicBorders.SplitPaneBorder-2.png | Bin 0 -> 5976 bytes .../BasicBorders.SplitPaneDividerBorder-1.png | Bin 0 -> 7169 bytes .../BasicBorders.ToggleButtonBorder-1.png | Bin 0 -> 1874 bytes .../plaf/basic/doc-files/BasicGraphicsUtils-1.png | Bin 0 -> 4844 bytes .../plaf/basic/doc-files/BasicGraphicsUtils-2.png | Bin 0 -> 3771 bytes .../plaf/basic/doc-files/BasicGraphicsUtils-3.png | Bin 0 -> 13480 bytes .../plaf/basic/doc-files/BasicGraphicsUtils-4.png | Bin 0 -> 4832 bytes .../plaf/basic/doc-files/BasicGraphicsUtils-5.png | Bin 0 -> 6884 bytes .../plaf/basic/doc-files/BasicGraphicsUtils-6.png | Bin 0 -> 6816 bytes .../plaf/basic/doc-files/BasicGraphicsUtils-7.png | Bin 0 -> 3676 bytes .../classpath/javax/swing/plaf/basic/package.html | 46 + .../javax/swing/plaf/doc-files/ComponentUI-1.dia | Bin 0 -> 3085 bytes .../javax/swing/plaf/doc-files/ComponentUI-1.png | Bin 0 -> 32683 bytes .../javax/swing/plaf/doc-files/TreeUI-1.png | Bin 0 -> 8660 bytes .../javax/swing/plaf/metal/DefaultMetalTheme.java | 305 ++ .../javax/swing/plaf/metal/MetalBorders.java | 1626 ++++++ .../swing/plaf/metal/MetalButtonListener.java | 74 + .../javax/swing/plaf/metal/MetalButtonUI.java | 298 ++ .../javax/swing/plaf/metal/MetalCheckBoxIcon.java | 142 + .../javax/swing/plaf/metal/MetalCheckBoxUI.java | 88 + .../swing/plaf/metal/MetalComboBoxButton.java | 294 ++ .../swing/plaf/metal/MetalComboBoxEditor.java | 190 + .../javax/swing/plaf/metal/MetalComboBoxIcon.java | 102 + .../javax/swing/plaf/metal/MetalComboBoxUI.java | 372 ++ .../javax/swing/plaf/metal/MetalDesktopIconUI.java | 72 + .../javax/swing/plaf/metal/MetalFileChooserUI.java | 2120 ++++++++ .../javax/swing/plaf/metal/MetalIconFactory.java | 2626 ++++++++++ .../plaf/metal/MetalInternalFrameTitlePane.java | 457 ++ .../swing/plaf/metal/MetalInternalFrameUI.java | 183 + .../javax/swing/plaf/metal/MetalLabelUI.java | 108 + .../javax/swing/plaf/metal/MetalLookAndFeel.java | 1372 ++++++ .../javax/swing/plaf/metal/MetalMenuBarUI.java | 94 + .../plaf/metal/MetalPopupMenuSeparatorUI.java | 77 + .../javax/swing/plaf/metal/MetalProgressBarUI.java | 147 + .../javax/swing/plaf/metal/MetalRadioButtonUI.java | 183 + .../javax/swing/plaf/metal/MetalRootPaneUI.java | 1075 ++++ .../javax/swing/plaf/metal/MetalScrollBarUI.java | 584 +++ .../javax/swing/plaf/metal/MetalScrollButton.java | 483 ++ .../javax/swing/plaf/metal/MetalScrollPaneUI.java | 159 + .../javax/swing/plaf/metal/MetalSeparatorUI.java | 131 + .../javax/swing/plaf/metal/MetalSliderUI.java | 475 ++ .../swing/plaf/metal/MetalSplitPaneDivider.java | 264 + .../javax/swing/plaf/metal/MetalSplitPaneUI.java | 89 + .../javax/swing/plaf/metal/MetalTabbedPaneUI.java | 1269 +++++ .../javax/swing/plaf/metal/MetalTextFieldUI.java | 82 + .../javax/swing/plaf/metal/MetalTheme.java | 576 +++ .../swing/plaf/metal/MetalToggleButtonUI.java | 230 + .../javax/swing/plaf/metal/MetalToolBarUI.java | 298 ++ .../javax/swing/plaf/metal/MetalToolTipUI.java | 276 ++ .../javax/swing/plaf/metal/MetalTreeUI.java | 317 ++ .../javax/swing/plaf/metal/MetalUtils.java | 597 +++ .../javax/swing/plaf/metal/OceanTheme.java | 318 ++ .../classpath/javax/swing/plaf/metal/package.html | 55 + .../javax/swing/plaf/multi/MultiButtonUI.java | 352 ++ .../swing/plaf/multi/MultiColorChooserUI.java | 352 ++ .../javax/swing/plaf/multi/MultiComboBoxUI.java | 430 ++ .../javax/swing/plaf/multi/MultiDesktopIconUI.java | 352 ++ .../javax/swing/plaf/multi/MultiDesktopPaneUI.java | 352 ++ .../javax/swing/plaf/multi/MultiFileChooserUI.java | 511 ++ .../swing/plaf/multi/MultiInternalFrameUI.java | 353 ++ .../javax/swing/plaf/multi/MultiLabelUI.java | 352 ++ .../javax/swing/plaf/multi/MultiListUI.java | 449 ++ .../javax/swing/plaf/multi/MultiLookAndFeel.java | 243 + .../javax/swing/plaf/multi/MultiMenuBarUI.java | 352 ++ .../javax/swing/plaf/multi/MultiMenuItemUI.java | 352 ++ .../javax/swing/plaf/multi/MultiOptionPaneUI.java | 398 ++ .../javax/swing/plaf/multi/MultiPanelUI.java | 352 ++ .../javax/swing/plaf/multi/MultiPopupMenuUI.java | 352 ++ .../javax/swing/plaf/multi/MultiProgressBarUI.java | 352 ++ .../javax/swing/plaf/multi/MultiRootPaneUI.java | 352 ++ .../javax/swing/plaf/multi/MultiScrollBarUI.java | 352 ++ .../javax/swing/plaf/multi/MultiScrollPaneUI.java | 352 ++ .../javax/swing/plaf/multi/MultiSeparatorUI.java | 352 ++ .../javax/swing/plaf/multi/MultiSliderUI.java | 352 ++ .../javax/swing/plaf/multi/MultiSpinnerUI.java | 352 ++ .../javax/swing/plaf/multi/MultiSplitPaneUI.java | 494 ++ .../javax/swing/plaf/multi/MultiTabbedPaneUI.java | 447 ++ .../javax/swing/plaf/multi/MultiTableHeaderUI.java | 352 ++ .../javax/swing/plaf/multi/MultiTableUI.java | 352 ++ .../javax/swing/plaf/multi/MultiTextUI.java | 617 +++ .../javax/swing/plaf/multi/MultiToolBarUI.java | 352 ++ .../javax/swing/plaf/multi/MultiToolTipUI.java | 352 ++ .../javax/swing/plaf/multi/MultiTreeUI.java | 628 +++ .../javax/swing/plaf/multi/MultiViewportUI.java | 352 ++ .../classpath/javax/swing/plaf/multi/package.html | 46 + libjava/classpath/javax/swing/plaf/package.html | 47 + .../javax/swing/plaf/synth/ColorType.java | 135 + .../classpath/javax/swing/plaf/synth/Region.java | 474 ++ .../javax/swing/plaf/synth/SynthConstants.java | 85 + .../javax/swing/plaf/synth/SynthContext.java | 134 + .../javax/swing/plaf/synth/SynthGraphicsUtils.java | 289 ++ .../javax/swing/plaf/synth/SynthLookAndFeel.java | 279 ++ .../javax/swing/plaf/synth/SynthPainter.java | 1999 ++++++++ .../javax/swing/plaf/synth/SynthStyle.java | 203 + .../javax/swing/plaf/synth/SynthStyleFactory.java | 64 + .../classpath/javax/swing/plaf/synth/package.html | 47 + .../javax/swing/table/AbstractTableModel.java | 303 ++ .../swing/table/DefaultTableCellRenderer.java | 276 ++ .../javax/swing/table/DefaultTableColumnModel.java | 671 +++ .../javax/swing/table/DefaultTableModel.java | 634 +++ .../classpath/javax/swing/table/JTableHeader.java | 1055 ++++ .../javax/swing/table/TableCellEditor.java | 65 + .../javax/swing/table/TableCellRenderer.java | 66 + .../classpath/javax/swing/table/TableColumn.java | 693 +++ .../javax/swing/table/TableColumnModel.java | 232 + .../classpath/javax/swing/table/TableModel.java | 134 + libjava/classpath/javax/swing/table/package.html | 47 + .../javax/swing/text/AbstractDocument.java | 2906 +++++++++++ .../classpath/javax/swing/text/AbstractWriter.java | 481 ++ .../classpath/javax/swing/text/AsyncBoxView.java | 1480 ++++++ .../classpath/javax/swing/text/AttributeSet.java | 195 + .../javax/swing/text/BadLocationException.java | 78 + libjava/classpath/javax/swing/text/BoxView.java | 1112 +++++ libjava/classpath/javax/swing/text/Caret.java | 207 + .../javax/swing/text/ChangedCharSetException.java | 100 + .../classpath/javax/swing/text/ComponentView.java | 494 ++ .../classpath/javax/swing/text/CompositeView.java | 792 +++ .../classpath/javax/swing/text/DateFormatter.java | 85 + .../classpath/javax/swing/text/DefaultCaret.java | 1287 +++++ .../javax/swing/text/DefaultEditorKit.java | 1702 +++++++ .../javax/swing/text/DefaultFormatter.java | 433 ++ .../javax/swing/text/DefaultFormatterFactory.java | 280 ++ .../javax/swing/text/DefaultHighlighter.java | 478 ++ .../javax/swing/text/DefaultStyledDocument.java | 2526 ++++++++++ .../classpath/javax/swing/text/DefaultTextUI.java | 62 + libjava/classpath/javax/swing/text/Document.java | 221 + .../classpath/javax/swing/text/DocumentFilter.java | 83 + libjava/classpath/javax/swing/text/EditorKit.java | 98 + libjava/classpath/javax/swing/text/Element.java | 54 + .../javax/swing/text/ElementIterator.java | 272 ++ .../javax/swing/text/EmptyAttributeSet.java | 153 + libjava/classpath/javax/swing/text/FieldView.java | 310 ++ libjava/classpath/javax/swing/text/FlowView.java | 841 ++++ libjava/classpath/javax/swing/text/GapContent.java | 1095 +++++ libjava/classpath/javax/swing/text/GlyphView.java | 1333 +++++ .../classpath/javax/swing/text/Highlighter.java | 78 + libjava/classpath/javax/swing/text/IconView.java | 175 + .../javax/swing/text/InternationalFormatter.java | 356 ++ .../classpath/javax/swing/text/JTextComponent.java | 2059 ++++++++ libjava/classpath/javax/swing/text/Keymap.java | 58 + libjava/classpath/javax/swing/text/LabelView.java | 322 ++ .../javax/swing/text/LayeredHighlighter.java | 57 + .../classpath/javax/swing/text/LayoutQueue.java | 116 + .../classpath/javax/swing/text/MaskFormatter.java | 558 +++ .../javax/swing/text/MutableAttributeSet.java | 117 + .../javax/swing/text/NavigationFilter.java | 98 + .../javax/swing/text/NumberFormatter.java | 86 + .../classpath/javax/swing/text/ParagraphView.java | 528 ++ .../classpath/javax/swing/text/PasswordView.java | 254 + .../classpath/javax/swing/text/PlainDocument.java | 292 ++ libjava/classpath/javax/swing/text/PlainView.java | 724 +++ libjava/classpath/javax/swing/text/Position.java | 62 + libjava/classpath/javax/swing/text/Segment.java | 297 ++ .../javax/swing/text/SimpleAttributeSet.java | 415 ++ .../classpath/javax/swing/text/StringContent.java | 569 +++ libjava/classpath/javax/swing/text/Style.java | 64 + .../classpath/javax/swing/text/StyleConstants.java | 1083 ++++ .../classpath/javax/swing/text/StyleContext.java | 990 ++++ .../classpath/javax/swing/text/StyledDocument.java | 140 + .../javax/swing/text/StyledEditorKit.java | 707 +++ .../classpath/javax/swing/text/TabExpander.java | 43 + libjava/classpath/javax/swing/text/TabSet.java | 210 + libjava/classpath/javax/swing/text/TabStop.java | 190 + .../classpath/javax/swing/text/TabableView.java | 44 + libjava/classpath/javax/swing/text/TableView.java | 491 ++ libjava/classpath/javax/swing/text/TextAction.java | 240 + libjava/classpath/javax/swing/text/Utilities.java | 730 +++ libjava/classpath/javax/swing/text/View.java | 881 ++++ .../classpath/javax/swing/text/ViewFactory.java | 50 + .../javax/swing/text/WrappedPlainView.java | 795 +++ libjava/classpath/javax/swing/text/ZoneView.java | 442 ++ .../classpath/javax/swing/text/html/BRView.java | 70 + .../classpath/javax/swing/text/html/BlockView.java | 721 +++ libjava/classpath/javax/swing/text/html/CSS.java | 736 +++ .../classpath/javax/swing/text/html/CSSBorder.java | 421 ++ .../classpath/javax/swing/text/html/CSSParser.java | 561 +++ .../javax/swing/text/html/FormSubmitEvent.java | 123 + .../classpath/javax/swing/text/html/FormView.java | 870 ++++ .../javax/swing/text/html/FrameSetView.java | 274 ++ .../classpath/javax/swing/text/html/FrameView.java | 233 + .../classpath/javax/swing/text/html/HRuleView.java | 189 + libjava/classpath/javax/swing/text/html/HTML.java | 1253 +++++ .../javax/swing/text/html/HTMLDocument.java | 2298 +++++++++ .../javax/swing/text/html/HTMLEditorKit.java | 1520 ++++++ .../swing/text/html/HTMLFrameHyperlinkEvent.java | 130 + .../javax/swing/text/html/HTMLWriter.java | 1088 +++++ .../classpath/javax/swing/text/html/ImageView.java | 591 +++ .../javax/swing/text/html/InlineView.java | 307 ++ .../classpath/javax/swing/text/html/ListView.java | 128 + .../javax/swing/text/html/MinimalHTMLWriter.java | 453 ++ .../javax/swing/text/html/MultiAttributeSet.java | 213 + .../javax/swing/text/html/MultiStyle.java | 136 + .../classpath/javax/swing/text/html/NullView.java | 102 + .../javax/swing/text/html/ObjectView.java | 110 + .../classpath/javax/swing/text/html/Option.java | 159 + .../javax/swing/text/html/ParagraphView.java | 322 ++ .../javax/swing/text/html/ResetableModel.java | 50 + .../swing/text/html/ResetablePlainDocument.java | 82 + .../text/html/ResetableToggleButtonModel.java | 70 + .../javax/swing/text/html/SelectComboBoxModel.java | 84 + .../javax/swing/text/html/SelectListModel.java | 106 + .../javax/swing/text/html/StyleSheet.java | 1455 ++++++ .../classpath/javax/swing/text/html/TableView.java | 974 ++++ .../javax/swing/text/html/ViewAttributeSet.java | 163 + .../classpath/javax/swing/text/html/package.html | 50 + .../swing/text/html/parser/AttributeList.java | 294 ++ .../javax/swing/text/html/parser/ContentModel.java | 223 + .../javax/swing/text/html/parser/DTD.java | 609 +++ .../javax/swing/text/html/parser/DTDConstants.java | 292 ++ .../swing/text/html/parser/DocumentParser.java | 268 + .../javax/swing/text/html/parser/Element.java | 317 ++ .../javax/swing/text/html/parser/Entity.java | 183 + .../javax/swing/text/html/parser/Parser.java | 446 ++ .../swing/text/html/parser/ParserDelegator.java | 207 + .../javax/swing/text/html/parser/TagElement.java | 142 + .../javax/swing/text/html/parser/package.html | 50 + libjava/classpath/javax/swing/text/package.html | 46 + .../javax/swing/text/rtf/ControlWordToken.java | 86 + .../javax/swing/text/rtf/RTFEditorKit.java | 114 + .../javax/swing/text/rtf/RTFParseException.java | 65 + .../classpath/javax/swing/text/rtf/RTFParser.java | 203 + .../classpath/javax/swing/text/rtf/RTFScanner.java | 294 ++ .../classpath/javax/swing/text/rtf/TextToken.java | 65 + libjava/classpath/javax/swing/text/rtf/Token.java | 91 + .../classpath/javax/swing/text/rtf/package.html | 48 + .../javax/swing/tree/AbstractLayoutCache.java | 456 ++ .../javax/swing/tree/DefaultMutableTreeNode.java | 1217 +++++ .../javax/swing/tree/DefaultTreeCellEditor.java | 792 +++ .../javax/swing/tree/DefaultTreeCellRenderer.java | 720 +++ .../javax/swing/tree/DefaultTreeModel.java | 622 +++ .../swing/tree/DefaultTreeSelectionModel.java | 1202 +++++ .../javax/swing/tree/ExpandVetoException.java | 76 + .../javax/swing/tree/FixedHeightLayoutCache.java | 628 +++ .../javax/swing/tree/MutableTreeNode.java | 104 + libjava/classpath/javax/swing/tree/RowMapper.java | 54 + .../classpath/javax/swing/tree/TreeCellEditor.java | 71 + .../javax/swing/tree/TreeCellRenderer.java | 73 + libjava/classpath/javax/swing/tree/TreeModel.java | 105 + libjava/classpath/javax/swing/tree/TreeNode.java | 113 + libjava/classpath/javax/swing/tree/TreePath.java | 312 ++ .../javax/swing/tree/TreeSelectionModel.java | 112 + .../swing/tree/VariableHeightLayoutCache.java | 657 +++ libjava/classpath/javax/swing/tree/package.html | 47 + .../javax/swing/undo/AbstractUndoableEdit.java | 323 ++ .../javax/swing/undo/CannotRedoException.java | 56 + .../javax/swing/undo/CannotUndoException.java | 58 + .../classpath/javax/swing/undo/CompoundEdit.java | 400 ++ libjava/classpath/javax/swing/undo/StateEdit.java | 265 + .../classpath/javax/swing/undo/StateEditable.java | 114 + .../classpath/javax/swing/undo/UndoManager.java | 625 +++ .../classpath/javax/swing/undo/UndoableEdit.java | 157 + .../javax/swing/undo/UndoableEditSupport.java | 272 ++ libjava/classpath/javax/swing/undo/package.html | 46 + 572 files changed, 238924 insertions(+) create mode 100644 libjava/classpath/javax/swing/AbstractAction.java create mode 100644 libjava/classpath/javax/swing/AbstractButton.java create mode 100644 libjava/classpath/javax/swing/AbstractCellEditor.java create mode 100644 libjava/classpath/javax/swing/AbstractListModel.java create mode 100644 libjava/classpath/javax/swing/AbstractSpinnerModel.java create mode 100644 libjava/classpath/javax/swing/Action.java create mode 100644 libjava/classpath/javax/swing/ActionMap.java create mode 100644 libjava/classpath/javax/swing/BorderFactory.java create mode 100644 libjava/classpath/javax/swing/BoundedRangeModel.java create mode 100644 libjava/classpath/javax/swing/Box.java create mode 100644 libjava/classpath/javax/swing/BoxLayout.java create mode 100644 libjava/classpath/javax/swing/ButtonGroup.java create mode 100644 libjava/classpath/javax/swing/ButtonModel.java create mode 100644 libjava/classpath/javax/swing/CellEditor.java create mode 100644 libjava/classpath/javax/swing/CellRendererPane.java create mode 100644 libjava/classpath/javax/swing/ComboBoxEditor.java create mode 100644 libjava/classpath/javax/swing/ComboBoxModel.java create mode 100644 libjava/classpath/javax/swing/CompatibilityFocusTraversalPolicy.java create mode 100644 libjava/classpath/javax/swing/ComponentInputMap.java create mode 100644 libjava/classpath/javax/swing/DebugGraphics.java create mode 100644 libjava/classpath/javax/swing/DefaultBoundedRangeModel.java create mode 100644 libjava/classpath/javax/swing/DefaultButtonModel.java create mode 100644 libjava/classpath/javax/swing/DefaultCellEditor.java create mode 100644 libjava/classpath/javax/swing/DefaultComboBoxModel.java create mode 100644 libjava/classpath/javax/swing/DefaultDesktopManager.java create mode 100644 libjava/classpath/javax/swing/DefaultFocusManager.java create mode 100644 libjava/classpath/javax/swing/DefaultListCellRenderer.java create mode 100644 libjava/classpath/javax/swing/DefaultListModel.java create mode 100644 libjava/classpath/javax/swing/DefaultListSelectionModel.java create mode 100644 libjava/classpath/javax/swing/DefaultSingleSelectionModel.java create mode 100644 libjava/classpath/javax/swing/DesktopManager.java create mode 100644 libjava/classpath/javax/swing/FocusManager.java create mode 100644 libjava/classpath/javax/swing/GrayFilter.java create mode 100644 libjava/classpath/javax/swing/Icon.java create mode 100644 libjava/classpath/javax/swing/ImageIcon.java create mode 100644 libjava/classpath/javax/swing/InputMap.java create mode 100644 libjava/classpath/javax/swing/InputVerifier.java create mode 100644 libjava/classpath/javax/swing/InternalFrameFocusTraversalPolicy.java create mode 100644 libjava/classpath/javax/swing/JApplet.java create mode 100644 libjava/classpath/javax/swing/JButton.java create mode 100644 libjava/classpath/javax/swing/JCheckBox.java create mode 100644 libjava/classpath/javax/swing/JCheckBoxMenuItem.java create mode 100644 libjava/classpath/javax/swing/JColorChooser.java create mode 100644 libjava/classpath/javax/swing/JComboBox.java create mode 100644 libjava/classpath/javax/swing/JComponent.java create mode 100644 libjava/classpath/javax/swing/JDesktopPane.java create mode 100644 libjava/classpath/javax/swing/JDialog.java create mode 100644 libjava/classpath/javax/swing/JEditorPane.java create mode 100644 libjava/classpath/javax/swing/JFileChooser.java create mode 100644 libjava/classpath/javax/swing/JFormattedTextField.java create mode 100644 libjava/classpath/javax/swing/JFrame.java create mode 100644 libjava/classpath/javax/swing/JInternalFrame.java create mode 100644 libjava/classpath/javax/swing/JLabel.java create mode 100644 libjava/classpath/javax/swing/JLayeredPane.java create mode 100644 libjava/classpath/javax/swing/JList.java create mode 100644 libjava/classpath/javax/swing/JMenu.java create mode 100644 libjava/classpath/javax/swing/JMenuBar.java create mode 100644 libjava/classpath/javax/swing/JMenuItem.java create mode 100644 libjava/classpath/javax/swing/JOptionPane.java create mode 100644 libjava/classpath/javax/swing/JPanel.java create mode 100644 libjava/classpath/javax/swing/JPasswordField.java create mode 100644 libjava/classpath/javax/swing/JPopupMenu.java create mode 100644 libjava/classpath/javax/swing/JProgressBar.java create mode 100644 libjava/classpath/javax/swing/JRadioButton.java create mode 100644 libjava/classpath/javax/swing/JRadioButtonMenuItem.java create mode 100644 libjava/classpath/javax/swing/JRootPane.java create mode 100644 libjava/classpath/javax/swing/JScrollBar.java create mode 100644 libjava/classpath/javax/swing/JScrollPane.java create mode 100644 libjava/classpath/javax/swing/JSeparator.java create mode 100644 libjava/classpath/javax/swing/JSlider.java create mode 100644 libjava/classpath/javax/swing/JSpinner.java create mode 100644 libjava/classpath/javax/swing/JSplitPane.java create mode 100644 libjava/classpath/javax/swing/JTabbedPane.java create mode 100644 libjava/classpath/javax/swing/JTable.java create mode 100644 libjava/classpath/javax/swing/JTextArea.java create mode 100644 libjava/classpath/javax/swing/JTextField.java create mode 100644 libjava/classpath/javax/swing/JTextPane.java create mode 100644 libjava/classpath/javax/swing/JToggleButton.java create mode 100644 libjava/classpath/javax/swing/JToolBar.java create mode 100644 libjava/classpath/javax/swing/JToolTip.java create mode 100644 libjava/classpath/javax/swing/JTree.java create mode 100644 libjava/classpath/javax/swing/JViewport.java create mode 100644 libjava/classpath/javax/swing/JWindow.java create mode 100644 libjava/classpath/javax/swing/KeyStroke.java create mode 100644 libjava/classpath/javax/swing/KeyboardManager.java create mode 100644 libjava/classpath/javax/swing/LayoutFocusTraversalPolicy.java create mode 100644 libjava/classpath/javax/swing/ListCellRenderer.java create mode 100644 libjava/classpath/javax/swing/ListModel.java create mode 100644 libjava/classpath/javax/swing/ListSelectionModel.java create mode 100644 libjava/classpath/javax/swing/LookAndFeel.java create mode 100644 libjava/classpath/javax/swing/MenuElement.java create mode 100644 libjava/classpath/javax/swing/MenuSelectionManager.java create mode 100644 libjava/classpath/javax/swing/MutableComboBoxModel.java create mode 100644 libjava/classpath/javax/swing/OverlayLayout.java create mode 100644 libjava/classpath/javax/swing/Popup.java create mode 100644 libjava/classpath/javax/swing/PopupFactory.java create mode 100644 libjava/classpath/javax/swing/ProgressMonitor.java create mode 100644 libjava/classpath/javax/swing/ProgressMonitorInputStream.java create mode 100644 libjava/classpath/javax/swing/Renderer.java create mode 100644 libjava/classpath/javax/swing/RepaintManager.java create mode 100644 libjava/classpath/javax/swing/RootPaneContainer.java create mode 100644 libjava/classpath/javax/swing/ScrollPaneConstants.java create mode 100644 libjava/classpath/javax/swing/ScrollPaneLayout.java create mode 100644 libjava/classpath/javax/swing/Scrollable.java create mode 100644 libjava/classpath/javax/swing/SingleSelectionModel.java create mode 100644 libjava/classpath/javax/swing/SizeRequirements.java create mode 100644 libjava/classpath/javax/swing/SizeSequence.java create mode 100644 libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java create mode 100644 libjava/classpath/javax/swing/SpinnerDateModel.java create mode 100644 libjava/classpath/javax/swing/SpinnerListModel.java create mode 100644 libjava/classpath/javax/swing/SpinnerModel.java create mode 100644 libjava/classpath/javax/swing/SpinnerNumberModel.java create mode 100644 libjava/classpath/javax/swing/Spring.java create mode 100644 libjava/classpath/javax/swing/SpringLayout.java create mode 100644 libjava/classpath/javax/swing/SwingConstants.java create mode 100644 libjava/classpath/javax/swing/SwingUtilities.java create mode 100644 libjava/classpath/javax/swing/Timer.java create mode 100644 libjava/classpath/javax/swing/ToolTipManager.java create mode 100644 libjava/classpath/javax/swing/TransferHandler.java create mode 100644 libjava/classpath/javax/swing/UIDefaults.java create mode 100644 libjava/classpath/javax/swing/UIManager.java create mode 100644 libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java create mode 100644 libjava/classpath/javax/swing/ViewportLayout.java create mode 100644 libjava/classpath/javax/swing/WindowConstants.java create mode 100644 libjava/classpath/javax/swing/border/AbstractBorder.java create mode 100644 libjava/classpath/javax/swing/border/BevelBorder.java create mode 100644 libjava/classpath/javax/swing/border/Border.java create mode 100644 libjava/classpath/javax/swing/border/CompoundBorder.java create mode 100644 libjava/classpath/javax/swing/border/EmptyBorder.java create mode 100644 libjava/classpath/javax/swing/border/EtchedBorder.java create mode 100644 libjava/classpath/javax/swing/border/LineBorder.java create mode 100644 libjava/classpath/javax/swing/border/MatteBorder.java create mode 100644 libjava/classpath/javax/swing/border/SoftBevelBorder.java create mode 100644 libjava/classpath/javax/swing/border/TitledBorder.java create mode 100644 libjava/classpath/javax/swing/border/doc-files/BevelBorder-1.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/BevelBorder-2.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/BevelBorder-3.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/EmptyBorder-1.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/EtchedBorder-1.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/EtchedBorder-2.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/LineBorder-1.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/MatteBorder-1.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/MatteBorder-2.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/MatteBorder-3.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/MatteBorder-4.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/MatteBorder-5.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/MatteBorder-6.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-1.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-2.png create mode 100644 libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-3.png create mode 100644 libjava/classpath/javax/swing/border/package.html create mode 100644 libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java create mode 100644 libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java create mode 100644 libjava/classpath/javax/swing/colorchooser/ColorSelectionModel.java create mode 100644 libjava/classpath/javax/swing/colorchooser/DefaultColorSelectionModel.java create mode 100644 libjava/classpath/javax/swing/colorchooser/DefaultHSBChooserPanel.java create mode 100644 libjava/classpath/javax/swing/colorchooser/DefaultPreviewPanel.java create mode 100644 libjava/classpath/javax/swing/colorchooser/DefaultRGBChooserPanel.java create mode 100644 libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java create mode 100644 libjava/classpath/javax/swing/colorchooser/package.html create mode 100644 libjava/classpath/javax/swing/event/AncestorEvent.java create mode 100644 libjava/classpath/javax/swing/event/AncestorListener.java create mode 100644 libjava/classpath/javax/swing/event/CaretEvent.java create mode 100644 libjava/classpath/javax/swing/event/CaretListener.java create mode 100644 libjava/classpath/javax/swing/event/CellEditorListener.java create mode 100644 libjava/classpath/javax/swing/event/ChangeEvent.java create mode 100644 libjava/classpath/javax/swing/event/ChangeListener.java create mode 100644 libjava/classpath/javax/swing/event/DocumentEvent.java create mode 100644 libjava/classpath/javax/swing/event/DocumentListener.java create mode 100644 libjava/classpath/javax/swing/event/EventListenerList.java create mode 100644 libjava/classpath/javax/swing/event/HyperlinkEvent.java create mode 100644 libjava/classpath/javax/swing/event/HyperlinkListener.java create mode 100644 libjava/classpath/javax/swing/event/InternalFrameAdapter.java create mode 100644 libjava/classpath/javax/swing/event/InternalFrameEvent.java create mode 100644 libjava/classpath/javax/swing/event/InternalFrameListener.java create mode 100644 libjava/classpath/javax/swing/event/ListDataEvent.java create mode 100644 libjava/classpath/javax/swing/event/ListDataListener.java create mode 100644 libjava/classpath/javax/swing/event/ListSelectionEvent.java create mode 100644 libjava/classpath/javax/swing/event/ListSelectionListener.java create mode 100644 libjava/classpath/javax/swing/event/MenuDragMouseEvent.java create mode 100644 libjava/classpath/javax/swing/event/MenuDragMouseListener.java create mode 100644 libjava/classpath/javax/swing/event/MenuEvent.java create mode 100644 libjava/classpath/javax/swing/event/MenuKeyEvent.java create mode 100644 libjava/classpath/javax/swing/event/MenuKeyListener.java create mode 100644 libjava/classpath/javax/swing/event/MenuListener.java create mode 100644 libjava/classpath/javax/swing/event/MouseInputAdapter.java create mode 100644 libjava/classpath/javax/swing/event/MouseInputListener.java create mode 100644 libjava/classpath/javax/swing/event/PopupMenuEvent.java create mode 100644 libjava/classpath/javax/swing/event/PopupMenuListener.java create mode 100644 libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java create mode 100644 libjava/classpath/javax/swing/event/TableColumnModelEvent.java create mode 100644 libjava/classpath/javax/swing/event/TableColumnModelListener.java create mode 100644 libjava/classpath/javax/swing/event/TableModelEvent.java create mode 100644 libjava/classpath/javax/swing/event/TableModelListener.java create mode 100644 libjava/classpath/javax/swing/event/TreeExpansionEvent.java create mode 100644 libjava/classpath/javax/swing/event/TreeExpansionListener.java create mode 100644 libjava/classpath/javax/swing/event/TreeModelEvent.java create mode 100644 libjava/classpath/javax/swing/event/TreeModelListener.java create mode 100644 libjava/classpath/javax/swing/event/TreeSelectionEvent.java create mode 100644 libjava/classpath/javax/swing/event/TreeSelectionListener.java create mode 100644 libjava/classpath/javax/swing/event/TreeWillExpandListener.java create mode 100644 libjava/classpath/javax/swing/event/UndoableEditEvent.java create mode 100644 libjava/classpath/javax/swing/event/UndoableEditListener.java create mode 100644 libjava/classpath/javax/swing/event/package.html create mode 100644 libjava/classpath/javax/swing/filechooser/FileFilter.java create mode 100644 libjava/classpath/javax/swing/filechooser/FileSystemView.java create mode 100644 libjava/classpath/javax/swing/filechooser/FileView.java create mode 100644 libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java create mode 100644 libjava/classpath/javax/swing/filechooser/package.html create mode 100644 libjava/classpath/javax/swing/package.html create mode 100644 libjava/classpath/javax/swing/plaf/ActionMapUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/BorderUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/ButtonUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ColorChooserUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ColorUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/ComboBoxUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/ComponentUI.java create mode 100644 libjava/classpath/javax/swing/plaf/DesktopIconUI.java create mode 100644 libjava/classpath/javax/swing/plaf/DesktopPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/DimensionUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/FileChooserUI.java create mode 100644 libjava/classpath/javax/swing/plaf/FontUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/IconUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/InputMapUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/InsetsUIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/InternalFrameUI.java create mode 100644 libjava/classpath/javax/swing/plaf/LabelUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ListUI.java create mode 100644 libjava/classpath/javax/swing/plaf/MenuBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/MenuItemUI.java create mode 100644 libjava/classpath/javax/swing/plaf/OptionPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/PanelUI.java create mode 100644 libjava/classpath/javax/swing/plaf/PopupMenuUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ProgressBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/RootPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ScrollBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ScrollPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/SeparatorUI.java create mode 100644 libjava/classpath/javax/swing/plaf/SliderUI.java create mode 100644 libjava/classpath/javax/swing/plaf/SpinnerUI.java create mode 100644 libjava/classpath/javax/swing/plaf/SplitPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/TabbedPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/TableHeaderUI.java create mode 100644 libjava/classpath/javax/swing/plaf/TableUI.java create mode 100644 libjava/classpath/javax/swing/plaf/TextUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ToolBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/ToolTipUI.java create mode 100644 libjava/classpath/javax/swing/plaf/TreeUI.java create mode 100644 libjava/classpath/javax/swing/plaf/UIResource.java create mode 100644 libjava/classpath/javax/swing/plaf/ViewportUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicBorders.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicHTML.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicListUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/ComboPopup.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.png create mode 100644 libjava/classpath/javax/swing/plaf/basic/package.html create mode 100644 libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.dia create mode 100644 libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.png create mode 100644 libjava/classpath/javax/swing/plaf/doc-files/TreeUI-1.png create mode 100644 libjava/classpath/javax/swing/plaf/metal/DefaultMetalTheme.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalBorders.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalTheme.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/MetalUtils.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/OceanTheme.java create mode 100644 libjava/classpath/javax/swing/plaf/metal/package.html create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiButtonUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiColorChooserUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiComboBoxUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiDesktopIconUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiDesktopPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiFileChooserUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiInternalFrameUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiLabelUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiListUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiMenuBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiMenuItemUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiOptionPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiPanelUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiPopupMenuUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiProgressBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiRootPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiScrollBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiScrollPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiSeparatorUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiSliderUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiSpinnerUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiSplitPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiTabbedPaneUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiTableHeaderUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiTableUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiTextUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiToolBarUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiToolTipUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiTreeUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/MultiViewportUI.java create mode 100644 libjava/classpath/javax/swing/plaf/multi/package.html create mode 100644 libjava/classpath/javax/swing/plaf/package.html create mode 100644 libjava/classpath/javax/swing/plaf/synth/ColorType.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/Region.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/SynthConstants.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/SynthContext.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/SynthGraphicsUtils.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/SynthPainter.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/SynthStyle.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/SynthStyleFactory.java create mode 100644 libjava/classpath/javax/swing/plaf/synth/package.html create mode 100644 libjava/classpath/javax/swing/table/AbstractTableModel.java create mode 100644 libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java create mode 100644 libjava/classpath/javax/swing/table/DefaultTableColumnModel.java create mode 100644 libjava/classpath/javax/swing/table/DefaultTableModel.java create mode 100644 libjava/classpath/javax/swing/table/JTableHeader.java create mode 100644 libjava/classpath/javax/swing/table/TableCellEditor.java create mode 100644 libjava/classpath/javax/swing/table/TableCellRenderer.java create mode 100644 libjava/classpath/javax/swing/table/TableColumn.java create mode 100644 libjava/classpath/javax/swing/table/TableColumnModel.java create mode 100644 libjava/classpath/javax/swing/table/TableModel.java create mode 100644 libjava/classpath/javax/swing/table/package.html create mode 100644 libjava/classpath/javax/swing/text/AbstractDocument.java create mode 100644 libjava/classpath/javax/swing/text/AbstractWriter.java create mode 100644 libjava/classpath/javax/swing/text/AsyncBoxView.java create mode 100644 libjava/classpath/javax/swing/text/AttributeSet.java create mode 100644 libjava/classpath/javax/swing/text/BadLocationException.java create mode 100644 libjava/classpath/javax/swing/text/BoxView.java create mode 100644 libjava/classpath/javax/swing/text/Caret.java create mode 100644 libjava/classpath/javax/swing/text/ChangedCharSetException.java create mode 100644 libjava/classpath/javax/swing/text/ComponentView.java create mode 100644 libjava/classpath/javax/swing/text/CompositeView.java create mode 100644 libjava/classpath/javax/swing/text/DateFormatter.java create mode 100644 libjava/classpath/javax/swing/text/DefaultCaret.java create mode 100644 libjava/classpath/javax/swing/text/DefaultEditorKit.java create mode 100644 libjava/classpath/javax/swing/text/DefaultFormatter.java create mode 100644 libjava/classpath/javax/swing/text/DefaultFormatterFactory.java create mode 100644 libjava/classpath/javax/swing/text/DefaultHighlighter.java create mode 100644 libjava/classpath/javax/swing/text/DefaultStyledDocument.java create mode 100644 libjava/classpath/javax/swing/text/DefaultTextUI.java create mode 100644 libjava/classpath/javax/swing/text/Document.java create mode 100644 libjava/classpath/javax/swing/text/DocumentFilter.java create mode 100644 libjava/classpath/javax/swing/text/EditorKit.java create mode 100644 libjava/classpath/javax/swing/text/Element.java create mode 100644 libjava/classpath/javax/swing/text/ElementIterator.java create mode 100644 libjava/classpath/javax/swing/text/EmptyAttributeSet.java create mode 100644 libjava/classpath/javax/swing/text/FieldView.java create mode 100644 libjava/classpath/javax/swing/text/FlowView.java create mode 100644 libjava/classpath/javax/swing/text/GapContent.java create mode 100644 libjava/classpath/javax/swing/text/GlyphView.java create mode 100644 libjava/classpath/javax/swing/text/Highlighter.java create mode 100644 libjava/classpath/javax/swing/text/IconView.java create mode 100644 libjava/classpath/javax/swing/text/InternationalFormatter.java create mode 100644 libjava/classpath/javax/swing/text/JTextComponent.java create mode 100644 libjava/classpath/javax/swing/text/Keymap.java create mode 100644 libjava/classpath/javax/swing/text/LabelView.java create mode 100644 libjava/classpath/javax/swing/text/LayeredHighlighter.java create mode 100644 libjava/classpath/javax/swing/text/LayoutQueue.java create mode 100644 libjava/classpath/javax/swing/text/MaskFormatter.java create mode 100644 libjava/classpath/javax/swing/text/MutableAttributeSet.java create mode 100644 libjava/classpath/javax/swing/text/NavigationFilter.java create mode 100644 libjava/classpath/javax/swing/text/NumberFormatter.java create mode 100644 libjava/classpath/javax/swing/text/ParagraphView.java create mode 100644 libjava/classpath/javax/swing/text/PasswordView.java create mode 100644 libjava/classpath/javax/swing/text/PlainDocument.java create mode 100644 libjava/classpath/javax/swing/text/PlainView.java create mode 100644 libjava/classpath/javax/swing/text/Position.java create mode 100644 libjava/classpath/javax/swing/text/Segment.java create mode 100644 libjava/classpath/javax/swing/text/SimpleAttributeSet.java create mode 100644 libjava/classpath/javax/swing/text/StringContent.java create mode 100644 libjava/classpath/javax/swing/text/Style.java create mode 100644 libjava/classpath/javax/swing/text/StyleConstants.java create mode 100644 libjava/classpath/javax/swing/text/StyleContext.java create mode 100644 libjava/classpath/javax/swing/text/StyledDocument.java create mode 100644 libjava/classpath/javax/swing/text/StyledEditorKit.java create mode 100644 libjava/classpath/javax/swing/text/TabExpander.java create mode 100644 libjava/classpath/javax/swing/text/TabSet.java create mode 100644 libjava/classpath/javax/swing/text/TabStop.java create mode 100644 libjava/classpath/javax/swing/text/TabableView.java create mode 100644 libjava/classpath/javax/swing/text/TableView.java create mode 100644 libjava/classpath/javax/swing/text/TextAction.java create mode 100644 libjava/classpath/javax/swing/text/Utilities.java create mode 100644 libjava/classpath/javax/swing/text/View.java create mode 100644 libjava/classpath/javax/swing/text/ViewFactory.java create mode 100644 libjava/classpath/javax/swing/text/WrappedPlainView.java create mode 100644 libjava/classpath/javax/swing/text/ZoneView.java create mode 100644 libjava/classpath/javax/swing/text/html/BRView.java create mode 100644 libjava/classpath/javax/swing/text/html/BlockView.java create mode 100644 libjava/classpath/javax/swing/text/html/CSS.java create mode 100644 libjava/classpath/javax/swing/text/html/CSSBorder.java create mode 100644 libjava/classpath/javax/swing/text/html/CSSParser.java create mode 100644 libjava/classpath/javax/swing/text/html/FormSubmitEvent.java create mode 100644 libjava/classpath/javax/swing/text/html/FormView.java create mode 100644 libjava/classpath/javax/swing/text/html/FrameSetView.java create mode 100644 libjava/classpath/javax/swing/text/html/FrameView.java create mode 100644 libjava/classpath/javax/swing/text/html/HRuleView.java create mode 100644 libjava/classpath/javax/swing/text/html/HTML.java create mode 100644 libjava/classpath/javax/swing/text/html/HTMLDocument.java create mode 100644 libjava/classpath/javax/swing/text/html/HTMLEditorKit.java create mode 100644 libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java create mode 100644 libjava/classpath/javax/swing/text/html/HTMLWriter.java create mode 100644 libjava/classpath/javax/swing/text/html/ImageView.java create mode 100644 libjava/classpath/javax/swing/text/html/InlineView.java create mode 100644 libjava/classpath/javax/swing/text/html/ListView.java create mode 100644 libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java create mode 100644 libjava/classpath/javax/swing/text/html/MultiAttributeSet.java create mode 100644 libjava/classpath/javax/swing/text/html/MultiStyle.java create mode 100644 libjava/classpath/javax/swing/text/html/NullView.java create mode 100644 libjava/classpath/javax/swing/text/html/ObjectView.java create mode 100644 libjava/classpath/javax/swing/text/html/Option.java create mode 100644 libjava/classpath/javax/swing/text/html/ParagraphView.java create mode 100644 libjava/classpath/javax/swing/text/html/ResetableModel.java create mode 100644 libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java create mode 100644 libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java create mode 100644 libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java create mode 100644 libjava/classpath/javax/swing/text/html/SelectListModel.java create mode 100644 libjava/classpath/javax/swing/text/html/StyleSheet.java create mode 100644 libjava/classpath/javax/swing/text/html/TableView.java create mode 100644 libjava/classpath/javax/swing/text/html/ViewAttributeSet.java create mode 100644 libjava/classpath/javax/swing/text/html/package.html create mode 100644 libjava/classpath/javax/swing/text/html/parser/AttributeList.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/ContentModel.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/DTD.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/DTDConstants.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/DocumentParser.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/Element.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/Entity.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/Parser.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/TagElement.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/package.html create mode 100644 libjava/classpath/javax/swing/text/package.html create mode 100644 libjava/classpath/javax/swing/text/rtf/ControlWordToken.java create mode 100644 libjava/classpath/javax/swing/text/rtf/RTFEditorKit.java create mode 100644 libjava/classpath/javax/swing/text/rtf/RTFParseException.java create mode 100644 libjava/classpath/javax/swing/text/rtf/RTFParser.java create mode 100644 libjava/classpath/javax/swing/text/rtf/RTFScanner.java create mode 100644 libjava/classpath/javax/swing/text/rtf/TextToken.java create mode 100644 libjava/classpath/javax/swing/text/rtf/Token.java create mode 100644 libjava/classpath/javax/swing/text/rtf/package.html create mode 100644 libjava/classpath/javax/swing/tree/AbstractLayoutCache.java create mode 100644 libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java create mode 100644 libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java create mode 100644 libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java create mode 100644 libjava/classpath/javax/swing/tree/DefaultTreeModel.java create mode 100644 libjava/classpath/javax/swing/tree/DefaultTreeSelectionModel.java create mode 100644 libjava/classpath/javax/swing/tree/ExpandVetoException.java create mode 100644 libjava/classpath/javax/swing/tree/FixedHeightLayoutCache.java create mode 100644 libjava/classpath/javax/swing/tree/MutableTreeNode.java create mode 100644 libjava/classpath/javax/swing/tree/RowMapper.java create mode 100644 libjava/classpath/javax/swing/tree/TreeCellEditor.java create mode 100644 libjava/classpath/javax/swing/tree/TreeCellRenderer.java create mode 100644 libjava/classpath/javax/swing/tree/TreeModel.java create mode 100644 libjava/classpath/javax/swing/tree/TreeNode.java create mode 100644 libjava/classpath/javax/swing/tree/TreePath.java create mode 100644 libjava/classpath/javax/swing/tree/TreeSelectionModel.java create mode 100644 libjava/classpath/javax/swing/tree/VariableHeightLayoutCache.java create mode 100644 libjava/classpath/javax/swing/tree/package.html create mode 100644 libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java create mode 100644 libjava/classpath/javax/swing/undo/CannotRedoException.java create mode 100644 libjava/classpath/javax/swing/undo/CannotUndoException.java create mode 100644 libjava/classpath/javax/swing/undo/CompoundEdit.java create mode 100644 libjava/classpath/javax/swing/undo/StateEdit.java create mode 100644 libjava/classpath/javax/swing/undo/StateEditable.java create mode 100644 libjava/classpath/javax/swing/undo/UndoManager.java create mode 100644 libjava/classpath/javax/swing/undo/UndoableEdit.java create mode 100644 libjava/classpath/javax/swing/undo/UndoableEditSupport.java create mode 100644 libjava/classpath/javax/swing/undo/package.html (limited to 'libjava/classpath/javax/swing') 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 (null 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 (null permitted). + * @param icon the icon (null 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 null). + * + * @return The value associated with the specified key, or + * null 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: + * + * Any existing value associated with the key will be overwritten. + * + * @param key the key (not null). + * @param value the value (null 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 & feel. + * + *

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.

+ * + *

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:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Property Stored inBound?
action button no
actionCommand model no
borderPainted button yes
contentAreaFilled button yes
disabledIcon button yes
disabledSelectedIcon button yes
displayedMnemonicIndex button no
enabled model no
focusPainted button yes
horizontalAlignment button yes
horizontalTextPosition button yes
icon button yes
iconTextGap button no
label (same as text) model yes
margin button yes
multiClickThreshold button no
pressedIcon button yes
rolloverEnabled button yes
rolloverIcon button yes
rolloverSelectedIcon button yes
selected model no
selectedIcon button yes
selectedObjects button no
text model yes
UI button yes
verticalAlignment button yes
verticalTextPosition button yes
+ * + *

The various behavioral aspects of these properties follows:

+ * + * + * + * @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-null. + */ + 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 + * ActionEvent. + */ + 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 AccessibleAbstractButton + * 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 null. + * + * @return the accessible icons of this object, or null 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 this. + * + * @return this + */ + public AccessibleAction getAccessibleAction() + { + return this; + } + + /** + * Returns the accessible value of this AccessibleAbstractButton, which + * is always this. + * + * @return the accessible value of this AccessibleAbstractButton, which + * is always this + */ + 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 1. + * + * @return 1, the number of supported accessible actions + */ + public int getAccessibleActionCount() + { + return 1; + } + + /** + * Returns a description for the action with the specified index or + * null 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 + * null 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 0 and nothing otherwise. + * + * @param actionIndex a zero based index into the actions of this button + * + * @return true if the specified action has been performed + * successfully, false 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 Integer(1) if the button is + * selected, Integer(0) 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 true if the value has been set, false + * 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 0. + * + * @return the minimimum accessible value for the AccessibleAbstractButton, + * which is 0 + */ + public Number getMinimumAccessibleValue() + { + return new Integer(0); + } + + /** + * Returns the maximum accessible value for the AccessibleAbstractButton, + * which is 1. + * + * @return the maximum accessible value for the AccessibleAbstractButton, + * which is 1 + */ + public Number getMaximumAccessibleValue() + { + return new Integer(1); + } + + /** + * Returns the accessible text for this AccessibleAbstractButton. This + * will be null if the button has a non-HTML label, otherwise + * this. + * + * @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 -1 since there is no caret in a button. + * + * @return -1 since there is no caret in a button + */ + public int getCaretPosition() + { + return -1; + } + + /** + * Returns the character, word or sentence at the specified index. The + * part 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 index + */ + 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 + * part 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 index + */ + 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 + * part 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 index + */ + 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 + * null 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 -1 since + * button labels can't be selected. + * + * @return -1, button labels can't be selected + */ + public int getSelectionStart() + { + return -1; + } + + /** + * This always returns -1 since + * button labels can't be selected. + * + * @return -1, button labels can't be selected + */ + public int getSelectionEnd() + { + return -1; + } + + /** + * Returns the selected text. This always returns null since + * button labels can't be selected. + * + * @return null, 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: + *
+   * super();
+   * init(text, icon);
+   * 
+ * + * 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(); + } + + /** + *

Returns the action command string for this button's model.

+ * + *

If the action command was set to null, the button's + * text (label) is returned instead.

+ * + * @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 ActionListener 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 ItemListener 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 ChangeListener 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 index 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: RIGHT, LEFT, CENTER, + * LEADING or TRAILING. The default is + * CENTER. + * + * @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: RIGHT, LEFT, CENTER, + * LEADING or TRAILING. The default is + * CENTER. + * + * @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: RIGHT, + * LEFT, CENTER, LEADING or + * TRAILING. The default is TRAILING. + * + * @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: RIGHT, + * LEFT, CENTER, LEADING or + * TRAILING. The default is TRAILING. + * + * @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: CENTER, TOP, or + * BOTTOM. The default is CENTER. + * + * @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: CENTER, TOP, or + * BOTTOM. The default is CENTER. + * + * @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: CENTER, + * TOP, or BOTTOM. The default is + * CENTER. + * + * @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: CENTER, + * TOP, or BOTTOM. The default is + * CENTER. + * + * @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 + * false, the button's look and feel class should not paint + * a border for the button. The default is true. + * + * @return The current value of the property. + */ + public boolean isBorderPainted() + { + return borderPainted; + } + + /** + * Set the value of the "borderPainted" property. If set to + * false, the button's look and feel class should not paint + * a border for the button. The default is true. + * + * @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; + } + + /** + *

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.

+ * + *

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.

+ * + * @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 setText(text) + */ + 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 getText() + */ + 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 true. This property may be + * null, 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 true. This property may be + * null, 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 false. This property may be + * null, 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 false. This property may be + * null, 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}: + * RIGHT, LEFT, CENTER, + * LEADING or TRAILING. + * + * @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}: TOP, + * BOTTOM or CENTER. + * + * @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: + * + * + * + * + * + * + * + * + * + * + * + *
Action keyed property AbstractButton property
NAME text
SMALL_ICON icon
SHORT_DESCRIPTION toolTipText
MNEMONIC_KEY mnemonic
ACTION_COMMAND_KEY actionCommand
+ * + *

In addition, this method always sets the button's "enabled" property to + * the value of the Action's "enabled" property.

+ * + *

If the provided Action is null, the text, icon, and + * toolTipText properties of the button are set to null, and + * the "enabled" property is set to true; the mnemonic and + * actionCommand properties are unchanged.

+ * + * @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()); + } + } + + /** + *

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}.

+ * + *

The button calls this method during construction, stores the + * resulting ActionListener in its actionListener 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.

+ * + * @return A new ActionListener + */ + protected ActionListener createActionListener() + { + return getEventHandler(); + } + + /** + *

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.

+ * + *

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 + * actionPropertyChangeListener 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.

+ * + * @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))); + } + }; + } + + /** + *

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.

+ * + *

The button calls this method during construction, stores the + * resulting ChangeListener in its changeListener 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.

+ * + * @return The new ChangeListener + */ + protected ChangeListener createChangeListener() + { + return getEventHandler(); + } + + /** + *

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.

+ * + *

The button calls this method during construction, stores the + * resulting ItemListener in its changeListener 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.

+ * + *

Note that ItemEvents are only generated from the button's model + * when the model's selected 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 false and its "selected" property is + * true. This icon can be null, 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 false and its "selected" property is + * true. This icon can be null, 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 + * true 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 rolloverEnabled + * property to true. The look and feel class should + * paint this icon when the "rolloverEnabled" property of the button is + * true 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 true, the "selected" property of the button's model is + * true, 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 + * rolloverEnabled property to true. The look and + * feel class should paint this icon when the "rolloverEnabled" property of + * the button is true, the "selected" property of the button's + * model is true, 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 + * true, and either the "rolloverEnabled" property of the + * button is false 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 + * true, and either the "rolloverEnabled" property of the + * button is false 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 + * true, otherwise returns null. + * + * @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 true if img is equal to the button's current icon, + * otherwise false + */ + 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 false, 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 false, 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 + * true, 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 ActionEvent. + * + * @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 + * ActionEvent. + * + * @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 c or + * propertyValue is null + */ + 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 true if the cell is editable using + * event, false + * if it's not. The default behaviour is to return true. + * + * @param event an event + * + * @return true if the cell is editable using + * event, false if it's not + */ + public boolean isCellEditable(EventObject event) + { + return true; + } + + /** + * Returns true if the editing cell should be selected, + * false otherwise. This is usually returning true, + * but in some special cases it might be useful not to switch cell selection + * when editing one cell. + * + * @param event an event + * + * @return true if the editing cell should be selected, + * false 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 true if editing has been stopped successfully, + * falseotherwise + */ + 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 this + * @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 this + * @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 this + * @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[] getListeners(Class listenerType) + { + return listenerList.getListeners(listenerType); + } + + /** + * A synonym for getListeners(ListDataListener.class). + * + * @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 AbstractSpinnerModel. + */ + public AbstractSpinnerModel() + { + // Nothing to do here. + } + + /** + * Registers a ChangeListener with the model so that it will + * receive {@link ChangeEvent} notifications when the model changes. + * + * @param listener the listener to add (null 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[] getListeners(Class c) + { + return listenerList.getListeners(c); + } + + /** + * Gets all the ChangeListeners. + * + * @return all the ChangeListeners + */ + 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 ChangeEvent to all the ChangeListeners + * 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 null). + * + * @return The value associated with the specified key, or + * null 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 null). + * @param value the value (null 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 InputMap. The result is an object which + * serves as a key to the components ActionMap. Finally + * the Action 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 ActionMap 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 Action into the ActionMap. + * 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 ActionMap. + * + * @param key the key of the entry to remove + */ + public void remove(Object key) + { + actionMap.remove(key); + } + + /** + * Returns the parent of this ActionMap. + * + * @return the parent, may be null. + */ + public ActionMap getParent() + { + return parent; + } + + /** + * Sets a parent for this ActionMap. + * + * @param parentMap the new parent + */ + public void setParent(ActionMap parentMap) + { + if (parentMap != this) + parent = parentMap; + } + + /** + * Returns the number of entries in this ActionMap. + * + * @return the number of entries + */ + public int size() + { + return actionMap.size(); + } + + /** + * Clears the ActionMap. + */ + public void clear() + { + actionMap.clear(); + } + + /** + * Returns all keys of entries in this ActionMap. + * + * @return an array of keys + */ + public Object[] keys() + { + if (size() != 0) + return actionMap.keySet().toArray(); + return null; + } + + /** + * Returns all keys of entries in this ActionMap + * 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 range that is constrained to fit + * within specified bounds. The range is defined as value + * to value + extent, where both value and + * extent are integers, and extent >= 0. The bounds + * are defined by integers minimum and maximum. + *

+ * 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 + * min <= value <= value + extent <= max. + * + * @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 valueIsAdjusting property. + * + * @return true if value is adjusting, + * otherwise false + * + * @see #setValueIsAdjusting(boolean) + */ + boolean getValueIsAdjusting(); + + /** + * Sets the valueIsAdjusting property. + * + * @param adjusting true if adjusting, + * false 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 ChangeListener to this object. + * + * @param listener the listener to add + * + * @see #removeChangeListener(ChangeListener) + */ + void addChangeListener(ChangeListener listener); + + /** + * Removes a ChangeListener 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 Boxes. + */ + 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 Box.Filler. + */ + 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 axis 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 BoxLayout 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 ButtonGroup can be selected at the same time. If one + * button in a ButtonGroup is selected, all other buttons + * are automatically deselected. + * + * While ButtonGroup 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 ButtonGroup because they don't implement the + * selected 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 buttons = new Vector(); + + /** 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: + *

+ * + * @param b the button to add (null 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 null. + * The group for the removed button's model is set to null. + * + * @param b the button to remove (null 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 Enumeration over all added buttons + */ + public Enumeration 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 null if + * there is no such button in the group. + * + * @param m the button model. + * + * @return The button that has the specified model, or null. + */ + 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 ButtonModel is selected in this button + * group. + * + * @param m the button model (null permitted). + * + * @return true if m is the selected button model + * in this group, and false 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 true if the button is armed, false + * otherwise. + * + * A button is armed, when the user has pressed the mouse over it, but has + * not yet released the mouse. + * + * @return true if the button is armed, false + * 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 true if the button is armed, false + * otherwise + * + * @see #isArmed() + */ + void setArmed(boolean b); + + /** + * Returns true if the button is enabled, false + * otherwise. + * + * When a button is disabled, it is usually grayed out and the user cannot + * change its state. + * + * @return true if the button is enabled, false + * 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 true if the button is enabled, false + * 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 true if the button is pressed, false + * otherwise + * + * @see #isPressed() + */ + void setPressed(boolean b); + + /** + * Returns true if the button is pressed, false + * 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 true if the button is pressed, false + * 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 ActionEvent 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 ItemEvent 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 ChangeEvent 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 true if the button is rollover, false + * otherwise + * + * @see #isRollover() + */ + void setRollover(boolean b); + + /** + * Returns true if the button is rollover-ed, false + * otherwise. + * + * A button is rollover-ed, when the user has moved the mouse over it, but has + * not yet pressed the mouse. + * + * @return true if the button is rollover, false + * 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 + * ActionEvents 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 true if the button is selected, false + * otherwise + * + * @see #isSelected() + */ + void setSelected(boolean b); + + /** + * Returns true if the button is selected, false + * 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 true if the button is selected, false + * 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 CellEditor. + * + * @return The value. + */ + Object getCellEditorValue(); + + /** + * Returns true if the specified event makes the editor + * editable, and false 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 CellEditor that it should stop editing, + * accepting the current cell value, and returns true if the + * editor actually stops editing, and false otherwise. + * + * @return A boolean. + */ + boolean stopCellEditing(); + + /** + * Signals to the CellEditor that it should cancel editing. + */ + void cancelCellEditing(); + + /** + * Registers a listener to receive {@link ChangeEvent} notifications from the + * CellEditor. + * + * @param listener the listener. + */ + void addCellEditorListener(CellEditorListener listener); + + /** + * Deregisters a listener so that it no longer receives {@link ChangeEvent} + * notifications from the CellEditor. + * + * @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 not 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 + * super.addImpl(). + * + * @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 c on the {@link Graphics} + * context graphics. The Graphics context is tranlated to + * (x,y) and the components bounds are set to (w,h). If + * shouldValidate + * 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 c 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 c on the {@link Graphics} + * context graphics. The Graphics context is tranlated to (x,y) + * and the components bounds are set to (w,h). The component is not + * 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 c on the {@link Graphics} + * context g. The Graphics context is tranlated to (r.x,r.y) and + * the components bounds are set to (r.width,r.height). + * The component is not + * 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 TODO + * @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 JComboBox as well as the current + * combo box selection. Whenever the selection in the JComboBox + * changes, the ComboBoxModel 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 (null permitted). + */ + void setSelectedItem(Object item); + + /** + * Returns the currently selected item in the combo box. + * + * @return The selected item (possibly null). + */ + 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 ComponentInputMap changes. + * + * @author Andrew Selkirk + * @author Michael Koch + */ +public class ComponentInputMap extends InputMap +{ + /** + * The component to notify. + */ + private JComponent component; + + /** + * Creates ComponentInputMap 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 InputMap. + * 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 InputMap. + */ + public void clear() + { + super.clear(); + if (component != null) + component.updateComponentInputMap(this); + } + + /** + * Remove an entry from the InputMap. + * + * @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 ComponentInputMap. + * + * @param parentMap the new parent + * + * @exception IllegalArgumentException if parentMap is not a + * ComponentInputMap 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 JComponent 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. DebugGraphics 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 DebugGraphics object. + */ + public DebugGraphics() + { + counter++; + } + + /** + * Creates a DebugGraphics object. + * + * @param graphics The Graphics object to wrap + * @param component TODO + */ + public DebugGraphics(Graphics graphics, JComponent component) + { + this(graphics); + // FIXME: What shall we do with component ? + } + + /** + * Creates a DebugGraphics object. + * + * @param graphics The Graphics 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 Graphics.create to create a + * DebugGraphics object. + * + * @return a new DebugGraphics object. + */ + public Graphics create() + { + DebugGraphics copy = new DebugGraphics(graphics.create()); + copy.debugOptions = debugOptions; + return copy; + } + + /** + * Creates a overrides Graphics.create to create a + * DebugGraphics object. + * + * @param x the x coordinate + * @param y the y coordinate + * @param width the width + * @param height the height + * + * @return a new DebugGraphics 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 PrintStream to write logging messages to + */ + public static PrintStream logStream() + { + return debugLogStream; + } + + /** + * setLogStream + * + * @param stream The currently set PrintStream. + */ + 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 FontMetrics 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 FontMetrics 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 Graphics 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: + * + * Graphics(-1) 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 BoundedRangeModel. + * + * @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. + * + *

The event object is created on demand, the first time it + * is actually needed.

+ * + * @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 + * value 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 extent 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 DefaultBoundedRangeModel with default + * values for the properties. The properties value, + * extent and minimum will be initialized + * to zero; maximum will be set to 100; the property + * valueIsAdjusting will be false. + */ + 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 DefaultBoundedRangeModel with the + * specified values for some properties. + * + * @param value the initial value of the range model, which must be + * a number between minimum and (maximum - + * extent). In a scroll bar visualization of a {@link + * BoundedRangeModel}, the value 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 + * extent 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: minimum <= value <= value + extent <= + * maximum. + */ + 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 + * value 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 + * value is displayed as the position of the thumb; + * changing the value 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 + * extent 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 + * extent 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 true while the thumb is being dragged + * around; when the mouse is relased, they set the property to + * false and post a final {@link ChangeEvent}. + * + * @return true if the value will change soon again; + * false 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 true while the thumb is being dragged + * around; when the mouse is relased, they set the property to + * false. + * + * @param isAdjusting true if the value will change + * soon again; false 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 + * value 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 + * extent 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 true while the thumb is being + * dragged around; when the mouse is relased, they set the property + * to false. + */ + 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}.class. + * + * @return an array with the currently subscribed listeners, or + * an empty array if there are currently no listeners. + * + * @since 1.3 + */ + public T[] getListeners(Class listenerType) + { + return listenerList.getListeners(listenerType); + } + + /** + * Returns all ChangeListeners that are currently + * subscribed for changes to this + * DefaultBoundedRangeModel. + * + * @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 (null 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 (null 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 partially 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 null. Use {@link AbstractButton} if you wish to + * interface with a button via an {@link ItemSelectable} interface. + * + * @return null + */ + 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[] getListeners(Class 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 ActionListener objects. + * + * @return array of ActionListener 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 ItemListener objects. + * + * @return array of ItemListener 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 ChangeListener objects. + * + * @return array of ChangeListener 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 true. + * + * @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 + * true at a time. + * + * @param g The new "group" property (null 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 + * null). 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 (null 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 + * null if the array length is zero. + * + * @param items an array containing items for the model (null + * not permitted). + * + * @throws NullPointerException if items is null. + */ + 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 null if the vector length is zero. + * + * @param vector a vector containing items for the model (null + * not permitted). + * + * @throws NullPointerException if vector is null. + */ + 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 null, 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 index 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 index 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 null, 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 (null 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 null). + */ + 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 + * null if the index 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 DefaultListCellRenderers and implements + * {@link javax.swing.plaf.UIResource}. This is used by + * {@link javax.swing.plaf.ListUI} subclasses to provide a default for + * the List.cellRenderer property. If you want to override + * this property, use DefaultListCellRenderer 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 [0, size()) + */ + 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 + * obj exists such that obj.equals(element) is + * true; if no such object exists, the method returns + * -1 + */ + public int indexOf(Object element) + { + return elements.indexOf(element); + } + + /** + * Gets the first index of a particular element in a list which occurs + * at or after 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 + * startIndex, at which an object obj exists + * such that obj.equals(element) is true; if no + * such object exists, the method returns -1 + */ + 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 + * obj exists such that obj.equals(element) is + * true; if no such object exists, the method returns + * -1 + */ + public int lastIndexOf(Object element) + { + return elements.lastIndexOf(element); + } + + /** + * Gets the last index of a particular element in a list which occurs + * at or before 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 + * endIndexIndex, at which an object obj exists + * such that obj.equals(element) is true; if no + * such object exists, the method returns -1 + */ + 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 [0, size()) + */ + 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 [0, size()) + */ + 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 i >= index is shifted to position i + 1. + * If index is equal to size(), this is + * equivalent to appending an element to the array. Any + * index greater than size() 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 [0, size()] + */ + 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 list.add(list.size(), element). + * + * @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 true if element is a member of the + * list, otherwise false + */ + 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 [0, size()) + */ + public Object remove(int index) + { + Object result; + result = elements.remove(index); + fireIntervalRemoved(this, index, index); + return result; + } + + /** + * Determines whether the list is empty. + * + * @return true if the list is empty, otherwise + * false + */ + 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 + * size. 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 null. 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 [0, size()) + */ + 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 [0, size()) + */ + 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 i >= index is shifted to position i + 1. + * If index is equal to size(), this is + * equivalent to appending an element to the array. Any + * index greater than size() 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 [0, size()] + */ + 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 true if the element existed in the list (and was + * removed), false 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 startIndex and + * endIndex 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 [0, size()) + * @throws IllegalArgumentException if startIndex > endIndex + */ + 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 [0, size()) + */ + 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. + * + *

The class is organized abstractly 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.

+ */ +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 + * SINGLE_SELECTION, SINGLE_INTERVAL_SELECTION + * or MULTIPLE_INTERVAL_SELECTION from {@link + * ListSelectionModel}. The default value is + * MULTIPLE_INTERVAL_SELECTION. + */ + 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 + * [A,L] be the range of indices between {@link + * #anchorSelectionIndex} and {@link #leadSelectionIndex} inclusive, and + * let [i0,i1] be the range of indices changed in a given + * call which generates a {@link ListSelectionEvent}. Then when this + * property is true, the {@link ListSelectionEvent} contains + * the range [A,L] union [i0,i1]; when false it + * will contain only [i0,i1]. The default is + * true. + * + * @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; + } + + /** + *

Sets the value of the {@link #anchorSelectionIndex} property. As a + * side effect, alters the selection status of two ranges of indices. Let + * OL be the old lead selection index, NL be + * the new lead selection index, and A be the anchor + * selection index. Then if A is a valid selection index, + * one of two things happens depending on the seleciton status of + * A:

+ * + *
    + * + *
  • isSelectedIndex(A) == true: set [A,OL] + * to deselected, then set [A,NL] to + * selected.
  • + * + *
  • isSelectedIndex(A) == false: set [A,OL] + * to selected, then set [A,NL] to + * deselected.
  • + * + *
+ * + *

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.

+ * + * @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 leadIndex 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 true if the selection is empty, otherwise + * false + */ + public boolean isSelectionEmpty() + { + return sel.isEmpty(); + } + + /** + * Gets the smallest index which is currently a member of a selection + * interval. + * + * @return The least integer i such that i >= + * 0 and i is a member of a selected interval, or + * -1 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 i such that i >= + * 0 and i is a member of a selected interval, or + * -1 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 true if the index is a member of a selection interval, + * otherwise false + */ + 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 + * SINGLE_SELECTION equivalent to calling + * setSelectionInterval(index1, index2); + * If the {@link #selectionMode} property is equal to + * SINGLE_INTERVAL_SELECTION and the interval being + * added is not adjacent to an already selected interval, + * equivalent to setSelectionInterval(index1, index2). + * Otherwise adds the range [index0, index1] + * 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 + * [index0,index1]. + * + * @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 SINGLE_SELECTION only the + * index index2 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 true if this is the final change + * in a series of adjustments, 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[] getListeners(Class 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. + * listenerList 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 DefaultSingleSelectionModel with no current + * selection. + */ + public DefaultSingleSelectionModel() + { + // Do nothing. + } + + /** + * Returns the selected index or -1 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 -1 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 -1 and + * sends a {@link ChangeEvent} to all registered listeners. If the selected + * index is already -1, this method does nothing. + */ + public void clearSelection() + { + setSelectedIndex(-1); + } + + /** + * Returns true if there is a selection, and false + * 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[] getListeners(Class 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 true if the event has been dispatched, + * false otherwise + */ + public boolean dispatchEvent(AWTEvent ev) + { + return wrapped.dispatchEvent(ev); + } + + /** + * Wraps {@link DefaultKeyboardFocusManager#dispatchKeyEvent(KeyEvent)}. + * + * @param ev the event to dispatch + * + * @return true if the event has been dispatched, + * false 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 ImageIcon + * class. + */ + protected class AccessibleImageIcon + extends AccessibleContext + implements AccessibleIcon, Serializable + { + private static final long serialVersionUID = 2113430526551336564L; + + /** + * Creates a new instance of AccessibleImageIcon. + */ + protected AccessibleImageIcon() + { + // Nothing to do here. + } + + /** + * Returns the accessible role for the ImageIcon. + * + * @return {@link AccessibleRole#ICON}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.ICON; + } + + /** + * Returns the accessible state for the ImageIcon. To + * match the reference implementation, this method always returns + * null. + * + * @return null. + */ + 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 null. + * + * @return null. + */ + 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 -1. + * + * @return -1. + */ + 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 0. + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns the accessible child at index i, which is + * null in this case because an {@link ImageIcon} has no + * children. + * + * @param i the index of the child to be fetched + * + * @return null. + */ + public Accessible getAccessibleChild(int i) + { + return null; + } + + /** + * Returns the locale of this object. To match the reference + * implementation, this method always returns null. + * + * @return null. + */ + public Locale getLocale() + throws IllegalComponentStateException + { + // refer to Sun's bug report 4269253 + return null; + } + + /** + * Returns the accessible icon description. This returns the + * description property of the underlying {@link ImageIcon}. + * + * @return The description (possibly null). + * + * @see #setAccessibleIconDescription(String) + */ + public String getAccessibleIconDescription() + { + return getDescription(); + } + + /** + * Sets the accessible icon description. This sets the + * description property of the underlying {@link ImageIcon}. + * + * @param newDescr the description (null permitted). + * + * @see #getAccessibleIconDescription() + */ + public void setAccessibleIconDescription(String newDescr) + { + setDescription(newDescr); + } + + /** + * Returns the icon height. This returns the iconHeight + * property of the underlying {@link ImageIcon}. + * + * @return The icon height. + */ + public int getAccessibleIconHeight() + { + return getIconHeight(); + } + + /** + * Returns the icon width. This returns the iconWidth 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 g.drawImage() 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 + * ImageIcon 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 InputMap. The result is an object which + * serves as a key to the component's ActionMap. Finally + * the Action 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 InputMap 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 (null is ignored). + * + * @return The binding associated with the specified keystroke (or + * null). + */ + 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 InputMap. If + * actionMapKey is null any existing entry will be + * removed. + * + * @param keystroke the keystroke for the entry (null is + * ignored). + * @param actionMapKey the action (null 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 InputMap. 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 (null is + * ignored). + */ + public void remove(KeyStroke keystroke) + { + if (inputMap != null) + inputMap.remove(keystroke); + } + + /** + * Returns the parent of this InputMap. The default value + * is null. + * + * @return The parent map (possibly null). + * + * @see #setParent(InputMap) + */ + public InputMap getParent() + { + return parent; + } + + /** + * Sets a parent for this InputMap. 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 (null permitted). + * + * @see #getParent() + */ + public void setParent(InputMap parentMap) + { + parent = parentMap; + } + + /** + * Returns the number of entries in this InputMap. 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 InputMap. 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 InputMap. This does not + * include keys defined in the parent, if there is one (use the + * {@link #allKeys()} method for that case). + *

+ * Following the behaviour of the reference implementation, this method will + * return null 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 null 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 InputMap and all its + * parents. + * + * @return An array of keys (may be null 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 InputVerifier + * subclass registered, that permits or vetos a focus change request. + * + * @author Andrew Selkirk + */ +public abstract class InputVerifier +{ + /** + * Creates a InputVerifier + */ + public InputVerifier() + { + // Nothing to do here. + } + + /** + * verify + * + * @param component the component to verify + * + * @return true if valid, false otherwise. + */ + public abstract boolean verify(JComponent component); + + /** + * shouldYieldFocus + * + * @param component the component to verify + * + * @return true if valid, false 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 JApplet. + */ + protected class AccessibleJApplet extends Applet.AccessibleApplet + { + /** + * Creates a new instance of AccessibleJApplet. + */ + protected AccessibleJApplet() + { + super(); + // Nothing to do here. + } + } + + /** + * The accessible context for this JApplet. + */ + 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. JButtons 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 JButtons. + * + * @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 (null 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 (null permitted). + */ + public JButton(Icon icon) + { + this(null, icon); + } + + /** + * Creates a new button with the specified text and no icon. + * + * @param text the button text (null 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 (null permitted, will be + * substituted by an empty string). + * @param icon the icon (null 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 + * JButton component. + * + * @return The accessible context (an instance of {@link AccessibleJButton}). + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJButton(); + return accessibleContext; + } + + /** + * Returns the suffix ("ButtonUI" in this case) used to + * determine the class name for a UI delegate that can provide the look and + * feel for a JButton. + * + * @return "ButtonUI". + */ + public String getUIClassID() + { + // Returns a string that specifies the name of the L&F class that renders + // this component. + return "ButtonUI"; + } + + /** + * Returns true if this button is the default button in + * its JRootPane. The default button gets automatically + * activated when the user presses ENTER (or whatever + * key this is bound to in the current Look and Feel). + * + * @return true if this button is the default button in + * its JRootPane + * + * @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 true if this button can act as the default button. + * This is true by default. + * + * @return true 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 JButton. + * + * @return A string describing the attributes of this JButton + * (never null). + */ + 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 defaultCapable property which indicates if + * this button may become the default button in its JRootPane. + * + * @param defaultCapable true if this button can become the + * default button in its JRootPane, false 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 + * selected state. This works very similar to + * {@link JToggleButton} and {@link JRadioButton}, but in UI design it + * has different semantics. JCheckBoxes 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 + * JCheckBox. + * + * @author Ronald Veldema (rveldema@cs.vu.nl) + */ +public class JCheckBox extends JToggleButton implements Accessible +{ + + /** + * Provides accessibility support for JCheckBox. + */ + protected class AccessibleJCheckBox extends AccessibleJToggleButton + { + /** + * Creates a new instance of AccessibleJCheckBox. + */ + protected AccessibleJCheckBox() + { + // Nothing to do here. + } + + /** + * Returns the accessble role of JCheckBox, + * {@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 JCheckBox. + * + * @return the accessible context for this JCheckBox + */ + 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 JCheckBox, 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. JCheckBoxMenuItem uses + * ToggleButtonModel 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 true if this item should be in checked state + * and false 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 true 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 + * JCheckBoxMenuItem component, for use in debugging. The + * return value is guaranteed to be non-null, but the format + * of the string may vary between implementations. + * + * @return A string describing the attributes of the + * JCheckBoxMenuItem. + */ + 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 + * JCheckBoxMenuItem 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 JCheckBoxMenuItem + * component. + * + * @see JCheckBoxMenuItem#getAccessibleContext() + */ + protected class AccessibleJCheckBoxMenuItem + extends AccessibleJMenuItem + { + private static final long serialVersionUID = 1079958073579370777L; + + /** + * Creates a new AccessibleJCheckBoxMenuItem instance. + */ + protected AccessibleJCheckBoxMenuItem() + { + // Nothing to do here. + } + + /** + * Returns the accessible role for the JCheckBoxMenuItem + * 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 JColorChooser. + */ + 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. + *

Note that the JComboBox will not change the value that + * is preselected by your ComboBoxModel implementation.

+ * + * @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 null 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. + *

If the index is below -1 or exceeds the upper bound an + * IllegalArgumentException is thrown.

+ *

If the index is -1 then no item gets selected.

+ * + * @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. + *

+ * Note: For performance reasons you should minimize invocation of this + * method. If the data model is not an instance of + * DefaultComboBoxModel the complexity is O(n) where n is the + * number of elements in the combo box. + *

+ * + * @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 null). + * + * @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 prototypeDisplayValue) is sent to all registered + * listeners. + * + * @param value the new value (null 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. + *

A RuntimeException is thrown if the data model is not + * an instance of {@link MutableComboBoxModel}.

+ * + * @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. + *

A RuntimeException is thrown if the data model is not + * an instance of {@link MutableComboBoxModel}.

+ * + * @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. + *

A RuntimeException is thrown if the data model is not + * an instance of {@link MutableComboBoxModel}.

+ * + * @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. + *

A RuntimeException is thrown if the data model is not + * an instance of {@link MutableComboBoxModel}.

+ * + * @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. + *

+ * A RuntimeException is thrown if the data model is not an + * instance of {@link MutableComboBoxModel}. + *

+ */ + 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 PopupMenuListeners. + * + * 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 + * PopupMenuListeners. + * + * 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 + * PopupMenuListeners. + * + * 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 JComboBox. + * + * @return A string describing the attributes of this JComboBox + * (never null). + */ + 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 + * JComboBox 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 JComboBox. + */ + 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 this. + * + * @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 true if the accessible child with the specified + * index is selected, false otherwise. + * + * @param index the index of the accessible child + * + * @return true if the accessible child with the specified + * index is selected, false otherwise + */ + public boolean isAccessibleChildSelected(int index) + { + return getSelectedIndex() == index; + } + + /** + * Returns the accessible role for the JComboBox 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 this. + * + * @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 + * UIManager.getString("ComboBox.togglePopupText") + * + * @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 1. + * + * @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 + * (actionIndex == 0), which is to toggle the + * popup menu. All other action indices have no effect and return + * false
. + * + * @param actionIndex the index of the action to perform + * + * @return true if the action has been performed, + * false 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 1 if the combobox has a selected entry, + * 0 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 index. + * + * @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 JComponent. + */ + protected AccessibleContext accessibleContext; + + /** + * Basic accessibility support for JComponent 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 + * super.addPropertyChangeListener(listener). + * + * @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 + * super.removePropertyChangeListener(listener). + * + * @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 i. + * + * @param i the index of the accessible child to return + * + * @return the accessible child component at index i + */ + 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 null 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 null + * 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 border) for + * a titled border and returns the title if one is found, null + * 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 null. + * + * @return the title of the border of this accessible component if + * this component has a titled border, otherwise returns + * null + */ + public String getTitledBorderText() + { + return getBorderTitle(getBorder()); + } + + /** + * Returns the keybindings associated with this accessible component or + * null 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 0.0, + * 0.5, and 1.0, 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 0.0, + * 0.5, and 1.0, 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 componentPopupMenu + * field is null. + */ + boolean inheritsPopupMenu; + + /** + *

Whether to double buffer this component when painting. This flag + * should generally be true, to ensure good painting + * performance.

+ * + *

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.

+ * + * @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; + + /** + *

This property controls two independent behaviors simultaneously.

+ * + *

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.

+ * + *

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.

+ * + *

The default value for this property is false, but most + * components will want to set it to true when installing UI + * defaults in {@link ComponentUI#installUI}.

+ * + * @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 false, swing will not try to + * request focus on this component; if true, 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 stops moving. If + * true, 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 JComponent 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 value to this component, associated + * with key. If there is an existing client property + * associated with key, it will be replaced. A + * {@link PropertyChangeEvent} is sent to registered listeners (with the + * name of the property being key.toString()). + * + * @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 AncestorListener. + * + * @param listener The listener to unregister + * + * @see #addAncestorListener + */ + public void removeAncestorListener(AncestorListener listener) + { + listenerList.remove(AncestorListener.class, listener); + } + + /** + * Unregister a VetoableChangeChangeListener. + * + * @param listener The listener to unregister + * + * @see #addVetoableChangeListener + */ + public void removeVetoableChangeListener(VetoableChangeListener listener) + { + if (vetoableChangeSupport != null) + vetoableChangeSupport.removeVetoableChangeListener(listener); + } + + /** + * Register an AncestorListener. + * + * @param listener The listener to register + * + * @see #removeVetoableChangeListener + */ + public void addAncestorListener(AncestorListener listener) + { + listenerList.add(AncestorListener.class, listener); + } + + /** + * Register a VetoableChangeListener. + * + * @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 + * listenerType. + * + * @param listenerType the class of listeners to filter (null + * not permitted). + * + * @return An array of registered listeners. + * + * @throws ClassCastException if listenerType does not implement + * the {@link EventListener} interface. + * @throws NullPointerException if listenerType is + * null. + * + * @see #getAncestorListeners() + * @see #listenerList + * + * @since 1.3 + */ + public T[] getListeners(Class listenerType) + { + if (listenerType == PropertyChangeListener.class) + return (T[]) getPropertyChangeListeners(); + else if (listenerType == VetoableChangeListener.class) + return (T[]) getVetoableChangeListeners(); + else + return listenerList.getListeners(listenerType); + } + + /** + * Return all registered AncestorListener objects. + * + * @return The set of AncestorListener objects in {@link + * #listenerList} + */ + public AncestorListener[] getAncestorListeners() + { + return (AncestorListener[]) getListeners(AncestorListener.class); + } + + /** + * Return all registered VetoableChangeListener objects. + * + * @return An array of the VetoableChangeListener objects + * registered with this component (possibly empty but never + * null). + * + * @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 null 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 null, + * 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 null, 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 maximumSize property + * has been explicitly set, it is returned. If the maximumSize + * 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 minimumSize property + * has been explicitly set, it is returned. If the minimumSize + * 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 preferredSize + * property has been explicitly set, it is returned. If the + * preferredSize 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 nextFocusableComponent property. + * + * @return The current value of the property, or null + * 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 + * null). + */ + 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 SwingUtilities.getRootPane(this);. + * + * @return An ancestral JRootPane, or null 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 toolTip property of this component, creating it and + * setting it if it is currently null. 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 toolTipText 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 null + * 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-null + * value is set, this component is registered in the + * ToolTipManager in order to turn on tooltips for this + * component. If a null 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 null + * if none has been set. + * + * @return the current tooltip text for this component, or null + * 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 + * null AND the {@link #getInheritsPopupMenu()} method returns + * true, this method will return the parent's popup menu (if it + * has one). + * + * @return The popup menu (possibly null. + * + * @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 (null 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 null 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; + } + + /** + *

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.

+ * + *

This method should not be called by clients; it is intended for + * focus implementations. Use {@link Component#requestFocus()} instead.

+ * + * @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 true 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 true if you wish this component to manage its own + * focus. In particular: if you want this component to be sent + * TAB and SHIFT+TAB key events, and to not + * have its children considered as focus transfer targets. If + * true, focus traversal around this component changes to + * CTRL+TAB and CTRL+SHIFT+TAB. + * + * @return true if you want this component to manage its own + * focus, otherwise (by default) false + * + * @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 true 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 true, but some components such as + * {@link JLayeredPane} should override this to return false. + * + * @return Whether the component tiles its children + */ + public boolean isOptimizedDrawingEnabled() + { + return true; + } + + /** + * Return true if this component is currently painting a tile, + * this means that paint() is called again on another child component. This + * method returns false 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 true 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 false, but some + * components such as {@link JViewport} will want to return + * true. + * + * @return Whether this component is a validation root + */ + public boolean isValidateRoot() + { + return false; + } + + /** + *

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.

+ * + *

The body of the paint 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 paint.

+ * + *

For more details on the painting sequence, see + * this article.

+ * + * @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 true if the specified area is completely covered + * by a child component, false 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-null. 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 true if the region is completely obscured by a + * sibling, false 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 true 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-null. 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. + * + *

This method will acquire a double buffer from the {@link + * RepaintManager} if the component's {@link #doubleBuffered} property is + * true and the paint call is the + * first recursive paint call inside swing.

+ * + *

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 paint.

+ * + * @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 true 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 true 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 null 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 getInputMap and getActionMap + * 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 condition 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 condition 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 + * this report 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 scrollRectToVisible 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 enabled 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 font 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 background 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 foreground 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 "ComponentUI" + * + * @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 null). + * + * @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 null, the {@link #getDefaultLocale()} method will + * return the platform default locale. + * + * @param l the locale (null permitted). + */ + public static void setDefaultLocale(Locale l) + { + defaultLocale = l; + } + + /** + * Returns the currently set input verifier for this component. + * + * @return the input verifier, or null if none + */ + public InputVerifier getInputVerifier() + { + return inputVerifier; + } + + /** + * Sets the input verifier to use by this component. + * + * @param verifier the input verifier, or null + */ + 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 true. + * 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 false if the focus change request will definitly + * fail, true 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 true. + * + * 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 false if the focus change request will definitly + * fail, true 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 false if the focus change request will definitly + * fail, true 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 true if the coordinates (x, y) lie within + * the bounds of this component and false 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 true if the specified point lies within the bounds + * of this component, false 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 changed + // 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 c or + * propertyValue is null + */ + 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 JDesktopPane + * component. + */ + protected class AccessibleJDesktopPane extends AccessibleJComponent + { + private static final long serialVersionUID = 6079388927946077570L; + + /** + * Creates a new AccessibleJDesktopPane instance. + */ + protected AccessibleJDesktopPane() + { + // Nothing to do here. + } + + /** + * Returns the accessible role for the JSlider 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 JDesktopPane. + * + * @return A string describing the attributes of this JDesktopPane + * (never null). + */ + 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 + * JDesktopPane 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 c or + * propertyValue is null + */ + 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 java.awt.Dialogs, JDialogs support the + * Swing Pluggable Look & Feel architecture. + * + * @author Ronald Veldema (rveldema@cs.vu.nl) + */ +public class JDialog extends Dialog implements Accessible, WindowConstants, + RootPaneContainer +{ + /** + * Provides accessibility support for JDialogs. + */ + protected class AccessibleJDialog extends Dialog.AccessibleAWTDialog + { + /** + * Creates a new instance of AccessibleJDialog. + */ + 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: + *
    + *
  • text/plain: Plain text, handled by + * {@link javax.swing.text.DefaultEditorKit}.
  • + *
  • text/html: HTML 4.0 styled text, handled by + * {@link javax.swing.text.html.HTMLEditorKit}.
  • + *
  • text/rtf: RTF text, handled by + * {@link javax.swing.text.rtf.RTFEditorKit}.
  • + *
+ * + * @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 JEditorPane. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJEditorPane extends AccessibleJTextComponent + { + + /** + * Creates a new AccessibleJEditorPane object. + */ + protected AccessibleJEditorPane() + { + super(); + } + + /** + * Returns a description of this AccessibleJEditorPane. 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 AccessibleJEditorPane. + * + * @return the accessible state of this AccessibleJEditorPane + */ + 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 JEditorPanes, 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 JEditorPane. This will + * be an instance of + * {@link JEditorPaneAccessibleHypertextSupport}. + * + * @return the accessible text of the JEditorPane + */ + 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 HTMLLink. + * + * @param el the link element + */ + public HTMLLink(Element el) + { + this.element = el; + } + + /** + * Returns true if this HTMLLink is still + * valid. A HTMLLink can become invalid when the document + * changes. + * + * @return true if this HTMLLink 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 true if the action has been performed + * successfully, false 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 i. + * 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 i + */ + 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 i. + * + * @param i the action index + * + * @return an {@link URL} object, that represents the action at action + * index i + */ + 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 String object is + * returned, if the link encloses an <img> tag, then an + * ImageIcon 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 i-th hyperlink in the document or + * null if there is no hyperlink with the specified index. + * + * @param i the index of the hyperlink to return + * + * @return the i-th hyperlink in the document or + * null 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 + * c within the document, or -1 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 + * c within the document, or -1 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 i, or + * null, 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 i, or + * null, 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 HyperlinkEvent 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 + * setEditorKitForContentType + * 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 true, + * 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 true when a Viewport should force the height of + * this component to match the viewport height. This is implemented to return + * true when the parent is an instance of JViewport and + * the viewport height > the UI's minimum height. + * + * @return true 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 true when a Viewport should force the width of + * this component to match the viewport width. This is implemented to return + * true when the parent is an instance of JViewport and + * the viewport width > the UI's minimum width. + * + * @return true 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 t + * 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 HyperlinkListener object to this editor pane. + * + * @param listener the listener to add + */ + public void addHyperlinkListener(HyperlinkListener listener) + { + listenerList.add(HyperlinkListener.class, listener); + } + + /** + * Removes a HyperlinkListener object to this editor pane. + * + * @param listener the listener to remove + */ + public void removeHyperlinkListener(HyperlinkListener listener) + { + listenerList.remove(HyperlinkListener.class, listener); + } + + /** + * Returns all added HyperlinkListener 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 JFileChooser object. + */ + public JFileChooser() + { + setup(null); + setCurrentDirectory(null); + } + + /** + * Creates a new JFileChooser object. + * + * @param currentDirectoryPath the directory that should initially be + * shown in the filechooser (if null, the user's home + * directory is used). + */ + public JFileChooser(String currentDirectoryPath) + { + this(currentDirectoryPath, null); + } + + /** + * Creates a new JFileChooser object with the specified + * directory and {@link FileSystemView}. + * + * @param currentDirectoryPath the directory that should initially be + * shown in the filechooser (if null, the user's home + * directory is used). + * @param fsv the file system view (if null, 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 JFileChooser object. + * + * @param currentDirectory the directory that should initially be + * shown in the filechooser (if null, the user's home + * directory is used). + */ + public JFileChooser(File currentDirectory) + { + setup(null); + setCurrentDirectory(currentDirectory); + } + + /** + * Creates a new JFileChooser object. + * + * @param fsv the file system view (if null, the default file + * system view is used). + */ + public JFileChooser(FileSystemView fsv) + { + setup(fsv); + setCurrentDirectory(null); + } + + /** + * Creates a new JFileChooser object. + * + * @param currentDirectory the directory that should initially be + * shown in the filechooser (if null, the user's home + * directory is used). + * @param fsv the file system view (if null, 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 null, 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 null). + * + * @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 (null 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 (null 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 dir is null, the current + * directory is set to the default directory returned by the file system + * view. + * + * @param dir the new directory (null 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 dialogType 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 (null 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 null). + * + * @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 (null 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 null). + * + * @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 (null 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 true if the filter was removed and + * false 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 null. + * + * @return The accessory component (possibly null). + * + * @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 true if file selection is enabled, and + * false otherwise. File selection is enabled when the + * file selection mode is {@link #FILES_ONLY} or + * {@link #FILES_AND_DIRECTORIES}. + * + * @return true if file selection is enabled. + * + * @see #getFileSelectionMode() + */ + public boolean isFileSelectionEnabled() + { + return (fileSelectionMode == FILES_ONLY + || fileSelectionMode == FILES_AND_DIRECTORIES); + } + + /** + * Returns true if directory selection is enabled, and + * false otherwise. Directory selection is enabled when the + * file selection mode is {@link #DIRECTORIES_ONLY} or + * {@link #FILES_AND_DIRECTORIES}. + * + * @return true 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 true if multiple selections are allowed within this + * file chooser, and false otherwise. + * + * @return A boolean. + * + * @see #setMultiSelectionEnabled(boolean) + */ + public boolean isMultiSelectionEnabled() + { + return multiSelection; + } + + /** + * Returns true if hidden files are to be hidden, and + * false 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 (null 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 (null 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 null). + */ + 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 true if the file is traversable, and + * false otherwise. + * + * @param f the file or directory. + * + * @return A boolean. + */ + public boolean isTraversable(File f) + { + return getFileSystemView().isTraversable(f).booleanValue(); + } + + /** + * Returns true 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 FileChooserUI. + */ + 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 + * JFileChooser component, for use in debugging. The return + * value is guaranteed to be non-null, but the format of the + * string may vary between implementations. + * + * @return A string describing the attributes of the + * JFileChooser. + */ + 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 + * JFileChooser 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 JFileChooser + * component. + */ + protected class AccessibleJFileChooser + extends JComponent.AccessibleJComponent + { + /** + * Creates a new instance of AccessibleJFileChooser. + */ + protected AccessibleJFileChooser() + { + // Nothing to do here. + } + + /** + * Returns the accessible role for the JFileChooser + * 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 null. 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 null. 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 null. 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 getActions. + * It also installs a DocumentFilter and NavigationFilter on the + * JFormattedTextField. + *

+ * If there is a ParseException 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 getActions()). + */ + 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 editValid property of + * JFormattedTextField. + * + * @param valid the new state for the editValid property + */ + protected void setEditValid(boolean valid) + { + textField.editValid = valid; + } + + /** + * Parses text to return a corresponding Object. + * + * @param text the String to parse + * @return an Object that text 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 + * value. + * + * @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. + * setValue or setFormatterFactory 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 COMMIT, COMMIT_OR_REVERT, + * PERSIST, or REVERT. + * @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 COMMIT, COMMIT_OR_REVERT, + * PERSIST, or REVERT or an + * IllegalArgumentException will be thrown. + * + * @param behavior + * @throws IllegalArgumentException if behaviour 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 value. + * + * @param value an object which should be formatted by the formatter factory. + * + * @return a formatter factory able to format objects of the class of + * value + */ + 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 java.awt.Frames, JFrames support the + * Swing Pluggable Look & Feel architecture. + * + * @author Ronald Veldema (rveldema@cs.vu.nl) + */ +public class JFrame extends Frame + implements WindowConstants, RootPaneContainer, Accessible +{ + /** + * Provides accessibility support for JFrames. + */ + protected class AccessibleJFrame extends Frame.AccessibleAWTFrame + { + /** + * Creates a new instance of AccessibleJFrame. + */ + protected AccessibleJFrame() + { + super(); + // Nothing to do here. + } + } + + /** + * A flag for {@link #setDefaultCloseOperation(int)}, indicating that the + * application should be exited, when this JFrame 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 JFrame with the specified title. + * + * @param title the frame title (null 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 GraphicsConfiguration that is used for + * the new JFrame + * + * @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 JFrame + * @param gc the GraphicsConfiguration that is used for + * the new JFrame + * + * @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 + * JFrame. + * + * @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 JFrame, + * for use in debugging. The return value is guaranteed to be + * non-null, but the format may vary between implementations. + * + * @return A string describing the attributes of the JFrame. + */ + 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 HIDE_ON_CLOSE. When + * EXIT_ON_CLOSE is specified this method calls + * SecurityManager.checkExit(0) which might throw a + * SecurityException. + * + * @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 operation 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 JInternalFrame + * component. + */ + protected class AccessibleJInternalFrame extends AccessibleJComponent + implements AccessibleValue + { + private static final long serialVersionUID = 5931936924175476797L; + + /** + * Creates a new AccessibleJInternalFrame instance. + */ + protected AccessibleJInternalFrame() + { + super(); + } + + /** + * Returns the frame title. + * + * @return The frame title. + */ + public String getAccessibleName() + { + return getTitle(); + } + + /** + * Returns the accessible role for the JInternalFrame + * 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 Integer(Integer.MAX_VALUE). + */ + public Number getMaximumAccessibleValue() + { + return new Integer(Integer.MAX_VALUE); + } + + /** + * Returns the minimum permitted accessible value. + * + * @return Integer(Integer.MIN_VALUE). + */ + 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 true if the value is set, and false + * 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 JDesktopIcon + * component. + */ + protected class AccessibleJDesktopIcon extends AccessibleJComponent + implements AccessibleValue + { + private static final long serialVersionUID = 5035560458941637802L; + + /** + * Creates a new AccessibleJDesktopIcon instance. + */ + protected AccessibleJDesktopIcon() + { + super(); + } + + /** + * Returns the accessible role for the JDesktopIcon + * 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 JDesktopIcon, as an {@link Integer}. + * + * @return The layer. + */ + public Number getCurrentAccessibleValue() + { + return new Integer(frame.getLayer()); + } + + /** + * Returns the maximum permitted accessible value. + * + * @return Integer(Integer.MAX_VALUE). + */ + public Number getMaximumAccessibleValue() + { + return new Integer(Integer.MAX_VALUE); + } + + /** + * Returns the minimum permitted accessible value. + * + * @return Integer(Integer.MIN_VALUE). + */ + public Number getMinimumAccessibleValue() + { + return new Integer(Integer.MIN_VALUE); + } + + /** + * Sets the layer for the internal frame represented by this + * JDesktopIcon component. + * + * @param n the layer (see the constants defined in + * {@link JLayeredPane}). + * + * @return true if the value is set, and false + * 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 + * JDesktopIcon 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 + * JInternalFrame 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 + * JInternalFrame 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 JDesktopIcon that represents this + * JInternalFrame 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 null). + * + * @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 + * JInternalFrame instance. + * + * @return A string describing the current state of this + * JInternalFrame 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 + * JInternalFrame is closed. Note that no validation is + * performed on the operation 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 JDesktopIcon instance that represents this + * JInternalFrame 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 + * "desktopIcon") 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 JInternalFrame and sends a + * {@link PropertyChangeEvent} (with the property name + * {@link #TITLE_PROPERTY}) to all registered listeners. + * + * @param title the new title (null 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 JLabel + * 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 JLabel component. + * + * @return {@link AccessibleRole#LABEL}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LABEL; + } + + /** + * Returns the selected text. This is null since JLabels + * are not selectable. + * + * @return null 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 + * AttributeSet 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 + * part 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 index + */ + 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 + * part 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 index + */ + 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 + * part 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 index + */ + 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, null 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, null premitted. + * @param horizontalAlignment The horizontal alignment of the label, must be + * either CENTER, LEFT, RIGHT, + * LEADING or TRAILING. + */ + 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, null 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, null permitted. + * @param horizontalAlignment The horizontal alignment of the label, must be + * either CENTER, LEFT, RIGHT, + * LEADING or TRAILING. + */ + 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, null permitted. + * @param icon The icon to use with the label, null premitted. + * @param horizontalAlignment The horizontal alignment of the label, must be + * either CENTER, LEFT, RIGHT, + * LEADING or TRAILING. + */ + 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 (null 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 "LabelUI" + */ + public String getUIClassID() + { + return "LabelUI"; + } + + /** + * Returns a string describing the attributes for the JLabel + * component, for use in debugging. The return value is guaranteed to be + * non-null, but the format of the string may vary between + * implementations. + * + * @return A string describing the attributes of the JLabel. + */ + 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 null). + * + * @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 displayedMnemonicIndex, if necessary. + * + * @param newText The text (null 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 (null 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 null 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 disabledIcon. + * + * @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 (null 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). + *

+ * 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 alignment 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 alignment 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 textPosition 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 textPosition 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 JLabel 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 null). + */ + public Component getLabelFor() + { + return labelFor; + } + + /** + * Sets the component that this JLabel 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 (null 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 (null permitted). + */ + public void setFont(Font f) + { + super.setFont(f); + repaint(); + } + + /** + * Returns the object that provides accessibility features for this + * JLabel 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 Container semantics. + * Each child component of a Layered Pane is placed within one + * of several layers. JLayeredPane defines a set of standard + * layers. The pre-defined sets are (in the order from button to top): + * + *

+ *
{@link #DEFAULT_LAYER}
+ *
The layer where most of the normal components are placed. This + * is the bottommost layer.
+ * + *
{@link #PALETTE_LAYER}
+ *
Palette windows are placed in this layer.
+ * + *
{@link #MODAL_LAYER}
+ *
The layer where internal modal dialog windows are placed.
+ * + *
{@link #POPUP_LAYER}
+ *
The layer for popup menus
+ * + *
{@link #DRAG_LAYER}
+ *
Components that are beeing dragged are temporarily placed in + * this layer.
+ *
+ * + *

A child is in exactly one of these layers at any time, though there may + * be other layers if someone creates them.

+ * + *

You can add a component to a specific layer using the + * {@link Container#add(Component, Object)} method. I.e. + * layeredPane.add(comp, JLayeredPane.MODAL_LAYER) will add the + * component comp to the modal layer of layeredPane. + *

+ * + *

To change the layer of a component that is already a child of + * a JLayeredPane, use the {@link #setLayer(Component, int)} + * method.

+ * + *

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}.

+ * + *

There is a precise set of words we will use to refer to numbers within + * this class:

+ * + *
+ *
Component Index:
+ *
An offset into the component array held in our ancestor, + * {@link java.awt.Container}, from [0 .. component.length). The drawing + * rule with indices is that 0 is drawn last.
+ * + *
Layer Number:
+ *
A general int specifying a layer within this component. Negative + * numbers are drawn first, then layer 0, then positive numbered layers, in + * ascending order.
+ * + *
Position:
+ *
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").
+ *
+ * + *

Note: the layer numbering order is the reverse of the + * component indexing and position order

+ * + * @author Graydon Hoare (graydon@redhat.com) + * @author Roman Kennke (kennke@aicas.com) + */ +public class JLayeredPane extends JComponent implements Accessible +{ + + /** + * Provides accessibility support for JLayeredPane. + */ + protected class AccessibleJLayeredPane extends AccessibleJComponent + { + /** + * Creates a new instance of AccessibleJLayeredPane. + */ + protected AccessibleJLayeredPane() + { + // Nothing to do here. + } + + /** + * Returns the accessble role of JLayeredPane, + * {@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 c 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 comp. If no such property can be + * found, we return 0 ({@link #DEFAULT_LAYER}). + * + * @param comp the component for which the layer is looked up + * + * @return the layer of comp as stored in the corresponding + * client property, or 0 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 + * comp or null if comp 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 + * comp or null if comp 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 or 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 or 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); + } + + /** + *

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.

+ * + *

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.

+ * + * @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 c within its layer or -1 if + * c 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 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 -1 if + * c 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(); + } + + /** + *

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 after calling this method.

+ * + *

Read that carefully: this method should be called before the + * component is added to the container.

+ * + * @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 layerConstraint is interpreted as an {@link + * Integer}, specifying the layer to which the component will be added + * (at the bottom position). + * + * The argument index specifies the position within the layer + * at which the component should be added, where 0 is the top + * position greater values specify positions below that and -1 + * 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 JLayeredPane. + * + * @return the accessible context for this JLayeredPane + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJLayeredPane(); + return accessibleContext; + } + + /** + * This method is overridden order to provide a reasonable painting + * mechanism for JLayeredPane. This is necessary since + * JLayeredPane's do not have an own UI delegate. + * + * Basically this method clears the background for the + * JLayeredPane and then calls super.paint(g). + * + * @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 false if components in this layered pane can overlap, + * otherwise true. + * + * @return false if components in this layered pane can overlap, + * otherwise true + */ + 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; + +/** + *

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}.

+ * + *

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:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Property Stored inBound?
accessibleContext list no
anchorSelectionIndex selectionno
cellRenderer list yes
dragEnabled list no
firstVisibleIndex list no
fixedCellHeight list yes
fixedCellWidth list yes
lastVisibleIndex list no
layoutOrientation list yes
leadSelectionIndex selectionno
maxSelectionIndex selectionno
minSelectionIndex selectionno
model list yes
opaque list no
preferredScrollableViewportSizelist no
prototypeCellValue list yes
scrollableTracksViewportHeight list no
scrollableTracksViewportWidth list no
selectedIndex selectionno
selectedIndices selectionno
selectedValue model no
selectedValues model no
selectionBackground list yes
selectionEmpty selectionno
selectionForeground list yes
selectionMode selectionno
selectionModel list yes
UI list yes
UIClassID list no
valueIsAdjusting list no
visibleRowCount list no
+ * + * @author Graydon Hoare (graydon@redhat.com) + */ + +public class JList extends JComponent implements Accessible, Scrollable +{ + + /** + * Provides accessibility support for JList. + */ + protected class AccessibleJList extends AccessibleJComponent + implements AccessibleSelection, PropertyChangeListener, + ListSelectionListener, ListDataListener + { + + /** + * Provides accessibility support for list elements in JLists. + */ + 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 AccessibleJListChild. + * + * @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 + * this since AccessibleJListChilds are their + * own accessible contexts. + * + * @return the accessible context of this object, this + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + /** + * Returns the background color for this list child. This returns the + * background of the JList 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 JList 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 JList since it is not possible to + * set fonts for list children individually. + * + * @return the font of the JList + */ + 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 JList. + * + * @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 true if the parent JList is enabled, + * false otherwise. The list children cannot have an enabled + * flag set individually. + * + * @return true if the parent JList is enabled, + * false 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 true if this list child is visible, + * false otherwise. The value of this property depends + * on {@link JList#getFirstVisibleIndex()} and + * {@link JList#getLastVisibleIndex()}. + * + * @return true if this list child is visible, + * false 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 true if this list child is currently showing on + * screen and false otherwise. The list child is showing if + * it is visible and if it's parent JList is currently showing. + * + * @return true if this list child is currently showing on + * screen and false otherwise + */ + public boolean isShowing() + { + return isVisible() && parent.isShowing(); + } + + /** + * Returns true if this list child covers the screen location + * point (relative to the JList coordinate + * system, false otherwise. + * + * @return true if this list child covers the screen location + * point , false 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 null because list children do not have children + * themselves + * + * @return null + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns true 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 0 since list children don't have children + * themselves. + * + * @return 0 + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns null since list children don't have children + * themselves. + * + * @return null + */ + 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 true if the n-th child is selected, + * false otherwise. + * + * @param n the index of the child of which the selected state is queried + * + * @return true if the n-th child is selected, + * false 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 + * JList. 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 + * JList. 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 + * JList. 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 JList'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 JList. + * + * @return the state set of the JList + */ + 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 JList, + * {@link AccessibleRole#LIST}. + * + * @return the accessible role for JList + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LIST; + } + + /** + * Returns the accessible child at the visual location p + * (relative to the upper left corner of the JList). If there + * is no child at that location, this returns null. + * + * @param p the screen location for which to return the accessible child + * + * @return the accessible child at the specified location, or + * null 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 JList. + * + * @return the number of accessible children in the JList + */ + public int getAccessibleChildrenCount() + { + return getModel().getSize(); + } + + /** + * Returns the n-th accessible child of this JList. This will + * be an instance of {@link AccessibleJListChild}. If there is no child + * at that index, null is returned. + * + * @param n the index of the child to return + * + * @return the n-th accessible child of this JList + */ + 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 -1, 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 -1, 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; + + /** + *

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.

+ * + *

It is important that you not set this value to a + * component. It has to be a data value such as the objects you + * would find in the list's model. Setting it to a component will have + * undefined (and undesirable) affects.

+ */ + 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 preference 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 JList object. + */ + public JList() + { + init(new DefaultListModel()); + } + + /** + * Creates a new JList object. + * + * @param items the initial list items. + */ + public JList(Object[] items) + { + init(createListModel(items)); + } + + /** + * Creates a new JList object. + * + * @param items the initial list items. + */ + public JList(Vector items) + { + init(createListModel(items)); + } + + /** + * Creates a new JList object. + * + * @param model a model containing the list items (null not + * permitted). + * + * @throws IllegalArgumentException if model is + * null. + */ + public JList(ListModel model) + { + init(model); + } + + /** + * Initializes the list. + * + * @param m the list model (null 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 ListSelectionModel. + * + * @return the ListSelectionModel + */ + protected ListSelectionModel createSelectionModel() + { + return new DefaultListSelectionModel(); + } + + /** + * Gets the value of the {@link #fixedCellHeight} property. This property + * may be -1 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 -1 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 -1 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 -1 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 + * SINGLE_SELECTION, SINGLE_INTERVAL_SELECTION, + * or MULTIPLE_INTERVAL_SELECTION from the {@link + * ListSelectionModel} interface. + * + * @param a The new selection mode + */ + public void setSelectionMode(int a) + { + selectionModel.setSelectionMode(a); + } + + /** + * Adds the interval [a,a] 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 [0, x) where + * x = getModel.getSize(), indicating the index of an + * element in the list to select. When < 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 a[i] of the provided array + * a, calls {@link #setSelectedIndex} on a[i]. + * + * @param a an array of selected indices (null not permitted). + * + * @throws NullPointerException if a is null. + * @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 [0, x) where + * x = getModel.getSize(), indicating the minimum index of + * an element in the list for which the element is selected, or + * -1 if no elements are selected + */ + public int getSelectedIndex() + { + return selectionModel.getMinSelectionIndex(); + } + + /** + * Returns true if the model's selection is empty, otherwise + * false. + * + * @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 -1 + * 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 + * -1 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 -1 + * 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 true if a 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 + * getModel()getElementAt(getSelectedIndex()), with a check + * for the special index value of -1 which returns null + * null. + * + * @return The first selected element, or null 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 null). + * + * @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 null). + * + * @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 (null not permitted). + * + * @throws IllegalArgumentException if model is + * null. + */ + 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:
+ *
    + *
  • {@link #clearSelection()};
  • + *
  • {@link #setSelectionMode(int)};
  • + *
  • {@link #addSelectionInterval(int, int)};
  • + *
  • {@link #setSelectedIndex(int)};
  • + *
  • {@link #setSelectedIndices(int[])};
  • + *
  • {@link #setSelectionInterval(int, int)}.
  • + *
+ * + * @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 "ListUI", 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; + } + + /** + *

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.

+ * + *

It is important that you not set this value to a + * component. It has to be a data value such as the objects you + * would find in the list's model. Setting it to a component will have + * undefined (and undesirable) affects.

+ * + * @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); + } + + /** + *

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 greater 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 lesser index in the list than + * those elements currently showing.

+ * + *

If the provided orientation is HORIZONTAL, the above + * comments refer to "rightwards" for positive direction, and "leftwards" + * for negative.

+ * + * + * @param visibleRect The rectangle to scroll an element into + * @param orientation One of the numeric consants VERTICAL + * or HORIZONTAL + * @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; + } + + /** + *

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 greater 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 lesser index in the list than + * those elements currently showing.

+ * + *

If the provided orientation is HORIZONTAL, the above + * comments refer to "rightwards" for positive direction, and "leftwards" + * for negative.

+ * + * + * @param visibleRect The rectangle to scroll an element into + * @param orientation One of the numeric consants VERTICAL + * or HORIZONTAL + * @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 scrollableTracksViewportWidth property. + * + * @return true if the viewport is larger (horizontally) + * than the list and the list should be expanded to fit the viewport; + * false 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
scrollableTracksViewportWidth property. + * + * @return true if the viewport is larger (vertically) + * than the list and the list should be expanded to fit the viewport; + * false 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 + * -1 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 + * -1 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 -1 + * 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 + * -1 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 anchor can be less than, equal to, or greater than + * lead. + * + * @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 + * anchor can be less than, equal to, or greater than + * lead. + * + * @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 + * index0 can be less than, equal to, or greater than + * index1. + * + * @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 valueIsAdjusting flag from the list's selection + * model. + * + * @return the value + */ + public boolean getValueIsAdjusting() + { + return selectionModel.getValueIsAdjusting(); + } + + /** + * Sets the valueIsAdjusting 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 dragEnabled property. + * + * @return the value + * + * @since 1.4 + */ + public boolean getDragEnabled() + { + return dragEnabled; + } + + /** + * Set the dragEnabled 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 orientation 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, null 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 + * startIndex and moving in the specified direction through the + * list, looping around if necessary) that starts with prefix + * (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} (null 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 null 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 JList + * component, for use in debugging. The return value is guaranteed to be + * non-null, but the format of the string may vary between + * implementations. + * + * @return A string describing the attributes of the JList. + */ + 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. + * + *

+ * 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. + *

+ */ +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 JMenu 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 JMenu 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 JMenu 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 MenuListener 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 JMenu. + */ + 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 index. + * + * @param index the index of the child to fetch + * + * @return the accessible child with the specified index + */ + 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 this here. + * + * @return the accessible selection of this object + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /** + * Returns the selected accessible child with the specified + * index. + * + * @param index the index of the accessible selected child to return + * + * @return the selected accessible child with the specified + * index + */ + 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 true if the accessible child with the specified + * index is selected, false otherwise. + * + * @param index the index of the accessible child to check + * + * @return true if the accessible child with the specified + * index is selected, false 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 + * 0 if no item is selected, or 1 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 WinListener. + * + * @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 JMenuBar. + * + * @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 0 if nothing is selected, or 1 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 i menu, or + * null if the specified menu is not selected. + * + * @param i the index of the menu to return + * + * @return the selected with index i menu, or + * null if the specified menu is not selected + */ + public Accessible getAccessibleSelection(int i) + { + if (getSelectionModel().getSelectedIndex() != i) + return null; + return getMenu(i); + } + + /** + * Returns true if the specified menu is selected, + * false otherwise. + * + * @param i the index of the menu to check + * + *@return true if the specified menu is selected, + * false otherwise + */ + public boolean isAccessibleChildSelected(int i) + { + return getSelectionModel().getSelectedIndex() == i; + } + + /** + * Selects the menu with index i. 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 i. + * + * @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 JMenuBar, which is + * {@link AccessibleRole#MENU_BAR}. + * + * @return the accessible role of JMenuBar, which is + * {@link AccessibleRole#MENU_BAR} + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU_BAR; + } + + /** + * Returns the AccessibleSelection for this object. This + * method returns this, since the + * AccessibleJMenuBar manages its selection itself. + * + * @return the AccessibleSelection for this object + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /** + * Returns the state of this AccessibleJMenuBar. + * + * @return the state of this AccessibleJMenuBar. + */ + 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 null, this method returns + * new Insets(0, 0, 0, 0). + * + * @return The margin (never null). + * + * @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 menuElement 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 (null 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 JMenuItem + * component, for use in debugging. The return value is guaranteed to be + * non-null, but the format of the string may vary between + * implementations. + * + * @return A string describing the attributes of the JMenuItem. + */ + protected String paramString() + { + // calling super seems to be sufficient here... + return super.paramString(); + } + + /** + * Returns the object that provides accessibility features for this + * JMenuItem 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 JMenuItem + * 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 AccessibleJMenuItem 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 JMenuItem component. + * + * @return {@link AccessibleRole#MENU_ITEM}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU_ITEM; + } + } + + /** + * Returns true 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 true 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 JOptionPane + * component. + */ + protected class AccessibleJOptionPane extends JComponent.AccessibleJComponent + { + private static final long serialVersionUID = 686071432213084821L; + + /** + * Creates a new AccessibleJOptionPane 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 JDesktopPane that is found in + * parent's ancestors. The behaviour of the JDK + * is that it actually looks up the nearest + * JLayeredPane in parent'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 + * JOptionPane 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 JPanel. + * + * @author Roman Kennke (roman@kennke.org) + */ + protected class AccessibleJPanel extends AccessibleJComponent + { + /** + * Creates a new instance of AccessibleJPanel. + */ + protected AccessibleJPanel() + { + // Nothing to do here. + } + + /** + * Returns the accessible role for JPanel, which is + * {@link AccessibleRole#PANEL}. + * + * @return the accessible role for JPanel + */ + 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 (null 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 (null 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 ("PanelUI" in this case) used to + * determine the class name for a UI delegate that can provide the look and + * feel for a JPanel. + * + * @return "PanelUI". + */ + public String getUIClassID() + { + return "PanelUI"; + } + + /** + * Sets the UI delegate for the JPanel 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 JPanel 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 + * JPanel 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 JPanel. + * + * @return A string describing the attributes of this JPanel + * (never null). + */ + 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 JPasswordField object. + */ + public JPasswordField() + { + this(null, null, 0); + } + + /** + * Creates a JPasswordField object. + * + * @param text the initial text + */ + public JPasswordField(String text) + { + this(null, text, 0); + } + + /** + * Creates a JPasswordField object. + * + * @param columns the number of columns + */ + public JPasswordField(int columns) + { + this(null, null, columns); + } + + /** + * Creates a JPasswordField object. + * + * @param text the initial text + * @param columns the number of columns + */ + public JPasswordField(String text, int columns) + { + this(null, text, columns); + } + + /** + * Creates a JPasswordField 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 UIClassID + * + * @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 true if the echo char is set, + * false 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 AccessibleContext 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 & 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 MenuElement 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 true 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 true 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 JProgressBar fills a percentage of its bar based on its + * current value. In indeterminate mode, it creates box and bounces it between + * its bounds. + *

+ * This component has the following properties: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
Property Stored in Bound?
borderPainted progressBar yes
changeListeners progressBar no
indeterminate progressBar yes
maximum model no
minimum model no
model progressBar no
orientation progressBar yes
percentComplete progressBar no
string progressBar yes
stringPainted progressBar yes
value model no
+ */ +public class JProgressBar extends JComponent implements SwingConstants, + Accessible +{ + /** + * Provides the accessibility features for the JProgressBar + * component. + */ + protected class AccessibleJProgressBar extends AccessibleJComponent + implements AccessibleValue + { + private static final long serialVersionUID = -2938130009392721813L; + + /** + * Creates a new AccessibleJProgressBar 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 JProgressBar 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 null, this method + * does nothing and returns false. + * + * @param value the new progress bar value (null permitted). + * + * @return true if the slider value is updated, and + * false 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 (true for indeterminate, + * false for determinate). + */ + private transient boolean indeterminate = false; + + /** + * The orientation of the JProgressBar + * ({@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 true. + * @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 + * null, 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 JProgressBar with default attributes. The + * following defaults are used: + *

+ *

    + *
  • value: 0;
  • + *
  • minimum: 0;
  • + *
  • maximum: 100;
  • + *
  • orientation: {@link SwingConstants#HORIZONTAL}.
  • + *
+ */ + public JProgressBar() + { + this(HORIZONTAL, 0, 100); + } + + /** + * Creates a new JProgressBar with the specified + * orientation. The following defaults are used: + *

+ *

    + *
  • value: 0;
  • + *
  • minimum: 0;
  • + *
  • maximum: 100;
  • + *
+ * + * @param orientation the orientation ({@link #HORIZONTAL} or + * {@link #VERTICAL}). + * + * @throws IllegalArgumentException if orientation is not one of + * the specified values. + */ + public JProgressBar(int orientation) + { + this(orientation, 0, 100); + } + + /** + * Creates a new JProgressBar with the specified value range. + * The following defaults are used: + *

+ *

    + *
  • value: minimum;
  • + *
  • orientation: {@link SwingConstants#HORIZONTAL}.
  • + *
+ * + * @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 JProgressBar with the specified range and + * orientation. The following defaults are used: + *

+ *

    + *
  • value: minimum;
  • + *
+ * + * @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 orientation 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 JProgressBar with the specified model. The + * following defaults are used: + *

+ *

    + *
  • orientation: {@link SwingConstants#HORIZONTAL}.
  • + *
+ * + * @param model the model (null not permitted). + */ + public JProgressBar(BoundedRangeModel model) + { + this.model = model; + changeListener = createChangeListener(); + if (model != null) + model.addChangeListener(changeListener); + updateUI(); + } + + /** + * Returns the current value for the JProgressBar. 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 JProgressBar. The value is + * stored in the component's model (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 ChangeEvent + * to this component's registered listeners. + *

+ * If value is outside the range minimum to + * maximum, 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 true. + * + * @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 JProgressBar component, which + * is either {@link SwingConstants#HORIZONTAL} or + * {@link SwingConstants#VERTICAL}. The default orientation is + * HORIZONTAL. + * + * @return The orientation. + * + * @see #setOrientation(int) + */ + public int getOrientation() + { + return orientation; + } + + /** + * Sets the orientation for this JProgressBar component and, + * if the value changes, sends a {@link PropertyChangeEvent} (with the + * property name "orientation") to all registered listeners. + * + * @param orientation the orientation ({@link #HORIZONTAL} or + * {@link #VERTICAL}). + * + * @throws IllegalArgumentException if orientation 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 JProgressBar + * component. + * + * @return true if the string should be displayed, and + * false 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 JProgressBar + * component. If the flag value changes, a {@link PropertyChangeEvent} (with + * the property name "stringPainted") 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 JProgressBar if + * {@link #isStringPainted()} returns true. 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 "string") to all registered listeners. If + * the string is set to null, {@link #getString()} will return + * a default string. + * + * @param string the string (null 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 (value - min) / (max - min). + * + * @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 true. + * + * @return true if the component's border should be painted, + * and false 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 JProgressBar. + * + * @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 JProgressBar'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 ("ProgressBarUI" in this case) used to + * determine the class name for a UI delegate that can provide the look and + * feel for a JProgressBar. + * + * @return "ProgressBarUI". + */ + 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 model). 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 null. + * + * @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 JProgressBar 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 JProgressBar. + * + * @return The model (never null). + * + * @see #setModel(BoundedRangeModel) + */ + public BoundedRangeModel getModel() + { + return model; + } + + /** + * Sets the model for the JProgressBar and sends a + * {@link ChangeEvent} to all registered listeners. + * + * @param model the model (null 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 JProgressBar. This defines + * the lower bound for the current value, and is stored in the component's + * model. + * + * @return The minimum value. + * + * @see #setMinimum(int) + */ + public int getMinimum() + { + return model.getMinimum(); + } + + /** + * Sets the minimum value for the JProgressBar. The value is + * stored in the component's model (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 ChangeEvent + * 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 JProgressBar. This defines + * the upper bound for the current value, and is stored in the component's + * model. + * + * @return The maximum value. + * + * @see #setMaximum(int) + */ + public int getMaximum() + { + return model.getMaximum(); + } + + /** + * Sets the maximum value for the JProgressBar. The value is + * stored in the component's model (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 ChangeEvent + * 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 JProgressBar. + * + * @return A string describing the attributes of this + * JProgressBar (never null). + */ + 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 JProgressBar + * (true for indeterminate mode, and false for + * determinate mode). If the flag value changes, this method sends a + * {@link PropertyChangeEvent} (with the property name + * "indeterminate") to all registered listeners. + *

+ * If the JProgressBar 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 + * JProgressBar 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 JProgressBar + * (true for indeterminate mode, and false for + * determinate mode). + * + * @return A flag indicating the mode for the JProgressBar. + * + * @see #setIndeterminate(boolean) + * @since 1.4 + */ + public boolean isIndeterminate() + { + return indeterminate; + } + + /** + * Returns the object that provides accessibility features for this + * JProgressBar 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 JRadioButton component provides a visually selectable + * button with mutually exclusive behaviour within a ButtonGroup. + * 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, JToggleButton. + * JRadioButton 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. + *

+ * + * Buttons are grouped by adding each instance to a ButtonGroup. + * 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 AccessibleRole, 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 JRadioButton, + * in the form of an instance of AccessibleJRadioButton. + * 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 JRadioButtons + * as a String. + */ + 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 String 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 + * JRadioButtonMenuItem component, for use in debugging. The + * return value is guaranteed to be non-null, but the format of + * the string may vary between implementations. + * + * @return A string describing the attributes of the + * JRadioButtonMenuItem. + */ + protected String paramString() + { + // calling super seems to be sufficient here... + return super.paramString(); + } + + /** + * Returns the object that provides accessibility features for this + * JRadioButtonMenuItem 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 + * JRadioButtonMenuItem component. + * + * @see JRadioButtonMenuItem#getAccessibleContext() + */ + protected class AccessibleJRadioButtonMenuItem extends AccessibleJMenuItem + { + private static final long serialVersionUID = 4381471510145292179L; + + /** + * Creates a new AccessibleJRadioButtonMenuItem instance. + */ + protected AccessibleJRadioButtonMenuItem() + { + // Nothing to do here. + } + + /** + * Returns the accessible role for the JRadioButtonMenuItem + * 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 AccessibleJRootPane 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 RootLayout 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 setJMenuBar() + */ + public void setMenuBar(JMenuBar m) + { + setJMenuBar(m); + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public JMenuBar getJMenuBar() + { + return menuBar; + } + + /** + * @deprecated Replaced by getJMenuBar() + */ + 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 JRootPane 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 true if the glassPane 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 + * optimizeDrawingEnabled state on its own). + * + * @return true if the glassPane 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 JScrollBar + * component. + */ + protected class AccessibleJScrollBar extends JComponent.AccessibleJComponent + implements AccessibleValue + { + private static final long serialVersionUID = -7758162392045586663L; + + /** + * Creates a new AccessibleJScrollBar 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 JScrollBar 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 null, this method + * does nothing and returns false. + * + * @param value the new slider value (null permitted). + * + * @return true if the slider value is updated, and + * false 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 JScrollBar + * component, for use in debugging. The return value is guaranteed to be + * non-null, but the format of the string may vary between + * implementations. + * + * @return A string describing the attributes of the JScrollBar. + */ + 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 + * JScrollBar 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. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Property Stored in Bound?
columnHeader scrollPane yes
columnHeaderView columnHeader no
componentOrientation scrollPane yes
horizontalScrollBar scrollPane yes
horizontalScrollBarPolicy scrollPane yes
layout scrollPane yes
rowHeader scrollPane yes
rowHeaderView rowHeader no
validateRoot scrollPane no
verticalScrollBar scrollPane yes
verticalScrollBarPolicy scrollPane yes
viewport scrollPane yes
viewportBorder scrollPane yes
viewportBorderBounds scrollPane no
viewportView viewport no
wheelScrollingEnabled scrollPane yes
+ */ +public class JScrollPane extends JComponent + implements Accessible, ScrollPaneConstants +{ + /** + * Provides accessibility support for the JScrollPane. + * + * @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 AccessibleJScrollPane object. This + * initializes the viewport field with the current viewport + * from the scrollpane associated with this + * AccessibleJScrollPane. + */ + 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 viewPort field when the scrollpane's viewport + * changes. This method is called by + * {@link JScrollPane#setViewport(JViewport)} in order to update the + * viewPort 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 JScrollPane 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 JScrollPane that embeds the specified + * view 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 JScrollPane without a view; The scrollbar + * policies are set to vsbPolicy and hsbPolicy. + * + * @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 JScrollPane that embeds the specified + * view component; The scrollbar + * policies are set to vsbPolicy and hsbPolicy. + * + * @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 + * JScrollPane. + * + * @return the accessible context associated with this + * JScrollPane + */ + 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 JSeparator + * component. + */ + protected class AccessibleJSeparator extends AccessibleJComponent + { + private static final long serialVersionUID = 916332890553201095L; + + /** + * Creates a new AccessibleJSeparator instance. + */ + protected AccessibleJSeparator() + { + // Nothing to do here. + } + + /** + * Returns the accessible role for the JSeparator 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 JSeparator object. + */ + public JSeparator() + { + this(HORIZONTAL); + } + + /** + * Creates a new JSeparator object with the given orientation. + * + * @param orientation the orientation (either {@link #HORIZONTAL} or + * {@link #VERTICAL}). + * + * @throws IllegalArgumentException if orientation 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 JSeparator. + * + * @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 ("SeparatorUI" in this case) used to + * determine the class name for a UI delegate that can provide the look and + * feel for a JSeparator. + * + * @return "SeparatorUI". + */ + public String getUIClassID() + { + return "SeparatorUI"; + } + + /** + * Returns the orientation of the JSeparator. + * + * @return The orientation (one of {@link #HORIZONTAL} and {@link #VERTICAL}). + * + * @see #setOrientation(int) + */ + public int getOrientation() + { + return orientation; + } + + /** + * Sets the orientation for the JSeparator and sends a + * {@link PropertyChangeEvent} (with the property name + * orientation) to all registered listeners. + * + * @param orientation the orientation (either {@link #HORIZONTAL} or + * {@link #VERTICAL}). + * + * @throws IllegalArgumentException if orientation 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 JSeparator. + * + * @return A string describing the attributes of this JSeparator + * (never null). + */ + 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 + * JSeparator 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}. + *

+ * A JSlider component has the following properties: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Property Stored in Bound?
extent model no
inverted slider yes
labelTable slider yes
majorTickSpacing slider yes
maximum model yes
minimum model yes
minorTickSpacing slider yes
model slider yes
orientation slider yes
paintLabels slider yes
paintTicks slider yes
snapToTicks slider yes
value model no
valueIsAdjusting model no
+ * + *

+ * The various behavioural aspects of these properties follows: + *

+ * + *
    + *
  • + * When a non-bound property stored in the slider changes, the slider fires + * a {@link ChangeEvent} to its change listeners. + *
  • + *
  • + * When a bound property stored in the slider changes, the slider fires a + * {@link PropertyChangeEvent} to its property change listeners. + *
  • + *
  • + * If any of the model's properties change, it fires a {@link ChangeEvent} to + * its listeners, which include the slider. + *
  • + *
  • + * 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. + *
  • + *
+ */ +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 JSlider + * component. + */ + protected class AccessibleJSlider extends JComponent.AccessibleJComponent + implements AccessibleValue + { + private static final long serialVersionUID = -6301740148041106789L; + + /** + * Creates a new AccessibleJSlider 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 JSlider 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 null, this method + * does nothing and returns false. + * + * @param value the new slider value (null permitted). + * + * @return true if the slider value is updated, and + * false 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 createChangeListener()). + */ + protected ChangeListener changeListener; + + /** The change event that is passed to all listeners of this slider. */ + protected transient ChangeEvent changeEvent; + + /** + * Creates a new horizontal JSlider 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 JSlider 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 orientation is not one of + * the specified values. + */ + public JSlider(int orientation) + { + this(orientation, 0, 100, 50); + } + + /** + * Creates a new horizontal JSlider 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 minimum is greater than + * maximum. + */ + public JSlider(int minimum, int maximum) + { + this(HORIZONTAL, minimum, maximum, (maximum + minimum) / 2); + } + + /** + * Creates a new horizontal JSlider 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 value is not in the + * specified range. + * @throws IllegalArgumentException if minimum is greater than + * maximum. + */ + public JSlider(int minimum, int maximum, int value) + { + this(HORIZONTAL, minimum, maximum, value); + } + + /** + * Creates a new JSlider 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 orientation is not one of + * the specified values. + * @throws IllegalArgumentException if value is not in the + * specified range. + * @throws IllegalArgumentException if minimum is greater than + * maximum. + */ + 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 JSlider instance with the given + * model. + * + * @param model The model (null not permitted). + * + * @throws NullPointerException if model is null. + */ + 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 ("SliderUI" in this case) used to + * determine the class name for a UI delegate that can provide the look and + * feel for a JSlider. + * + * @return "SliderUI". + */ + 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 JSlider (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 + * null). + */ + 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: + *

+ *

    + *
  • the minimum value is stored in the slider's model (see + * {@link #getModel()});
  • + *
  • in addition to the property change event, the slider also fires a + * {@link ChangeEvent}.
  • + *
+ * + * @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: + *

+ *

    + *
  • the maximum value is stored in the slider's model (see + * {@link #getModel()});
  • + *
  • in addition to the property change event, the slider also fires a + * {@link ChangeEvent}.
  • + *
+ * + * @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 valueIsAdjusting flag from the slider's model. + * + * @return The valueIsAdjusting flag from the slider's model. + * + * @see #setValueIsAdjusting(boolean) + */ + public boolean getValueIsAdjusting() + { + return sliderModel.getValueIsAdjusting(); + } + + /** + * Sets the valueIsAdjusting 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 orientation 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 null). + * + * @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 (null 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 labelTable 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 (Integer, JLabel) 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 increment is not greater + * than zero. + */ + public Hashtable createStandardLabels(int increment) + { + return createStandardLabels(increment, sliderModel.getMinimum()); + } + + /** + * Creates a hashtable of (Integer, JLabel) 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 increment is not greater + * than zero, or start 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 false). + * + * @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 + * true. 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 true. + * + * @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 true 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 true if tick marks should be painted, and + * false 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 true, 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 JSlider. + * + * @return A string describing the attributes of this JSlider + * (never null). + */ + 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 + * JSlider 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 JSpinner 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 DefaultEditor object. The editor is + * registered with the spinner as a {@link ChangeListener} here. + * + * @param spinner the JSpinner 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 JSpinner 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 spinner. + * + * @param spinner the spinner (null 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 NumberEditor object for the specified + * spinner. 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 NumberEditor 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 JSpinner editor used for the {@link SpinnerListModel}. + * This editor uses a JFormattedTextField to edit the values + * of the spinner. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public static class ListEditor extends DefaultEditor + { + /** + * Creates a new instance of ListEditor. + * + * @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 JSpinner that is used + * for displaying and editing dates (e.g. that uses + * SpinnerDateModel 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 + * JSpinner. + * + * @param spinner the JSpinner for which to + * create a DateEditor 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 + * JSpinner using the specified date format + * pattern. + * + * @param spinner the JSpinner for which to + * create a DateEditor 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 SimpleDateFormat instance that is used to + * format the date value. + * + * @return the SimpleDateFormat 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 SpinnerDateModel 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 JSpinner 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 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 (null not permitted). + * + * @throws NullPointerException if model is null. + */ + public JSpinner(SpinnerModel model) + { + this.model = model; + this.editor = createEditor(model); + model.addChangeListener(new ModelListener()); + updateUI(); + } + + /** + * If the editor is JSpinner.DefaultEditor, 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 (null not permitted. + * + * @throws IllegalArgumentException if editor is + * null. + * + * @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 null + */ + 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 SpinnerUI that handles this spinner + * + * @return the SpinnerUI + */ + 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 "SpinnerUI". + */ + 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 ChangeListener + * + * @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 ChangeListeners + * + * @return all the ChangeListeners + */ + public ChangeListener[] getChangeListeners() + { + return (ChangeListener[]) listenerList.getListeners(ChangeListener.class); + } + + /** + * Fires a ChangeEvent to all the ChangeListeners + * added to this JSpinner + */ + 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 model. + * + * @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 JSplitPane + * component. + */ + protected class AccessibleJSplitPane extends JComponent.AccessibleJComponent + implements AccessibleValue + { + private static final long serialVersionUID = -1788116871416305366L; + + /** + * Creates a new AccessibleJSplitPane 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 JSplitPane 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 null, this method + * does nothing and returns false. + * + * @param value the new divider location (null permitted). + * + * @return true if the divider location value is updated, and + * false 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 + * JSplitPane 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 JSplitPane. + * + * @return A string describing the attributes of this JSplitPane + * (never null). + */ + 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 proportionalLocation 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 JSplitPane 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 orientation 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 c or + * propertyValue is null + */ + 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. + * + *

+ * 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. + *

+ */ +public class JTabbedPane extends JComponent implements Serializable, + Accessible, + SwingConstants +{ + /** + * Accessibility support for JTabbedPane. + */ + 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 + * JTabbedPane 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 JTabbedPane, which is + * {@link AccessibleRole#PAGE_TAB_LIST}. + * + * @return the accessible role of the JTabbedPane + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.PAGE_TAB_LIST; + } + + /** + * Returns the number of accessible child components of the + * JTabbedPane. + * + * @return the number of accessible child components of the + * JTabbedPane + */ + 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 JTabbedPane + * as AccessibleSelection object. + * + * @return the current selection state of the JTabbedPane + */ + 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 + * JTabbedPane. The reference implementation appears + * to return 1 always and we do the same. + * + * @return 1 + */ + public int getAccessibleSelectionCount() + { + return 1; + } + + /** + * Returns the selected tab, or null if there is no + * selection. + * + * @param i the selection index (ignored here). + * + * @return The selected tab, or null. + */ + public Accessible getAccessibleSelection(int i) + { + Accessible result = null; + int selected = getSelectedIndex(); + if (selected >= 0) + result = (Page) tabs.get(selected); + return result; + } + + /** + * Returns true if the specified child is selected, + * and false 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 + * tabPlacement, where tabPlacement 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 tabPlacement + * and tabLayoutPolicy. The tabPlacement can be one + * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or + * {@link #RIGHT}. The tabLayoutPolicy 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 null + * @param icon the tab's icon; may be null + * @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 null + * @param icon the icon for the tab; may be null + * @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 null + * @param icon the icon for the tab; may be null + * @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 null + * @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 null + * @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 + * JTabbedPane component, for use in debugging. The return + * value is guaranteed to be non-null, but the format of the + * string may vary between implementations. + * + * @return A string describing the attributes of the + * JTabbedPane. + */ + 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 + * JTabbedPane 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 JTable. + * + * @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 AccessibleJTableCell. + * + * @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 0 here. + * + * @return 0 + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns the accessible child at index i. Table cells + * don't have children, so we return null here. + * + * @return null + */ + 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 + * this. + * + * @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 true if this table cell is enabled, + * false otherwise. + * + * @return true if this table cell is enabled, + * false 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 true if this cell is visible, false + * otherwise. + * + * @return true if this cell is visible, false + * 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 true if this table cell is currently showing on + * screen. + * + * @return true if this table cell is currently showing on + * screen + */ + public boolean isShowing() + { + return table.isShowing(); + } + + /** + * Returns true if this table cell contains the location + * at point, false otherwise. + * point is interpreted as relative to the coordinate system + * of the table cell. + * + * @return true if this table cell contains the location + * at point, false 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 null here. + * + * @return null + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns true if this table cell is focus traversable, + * false otherwise. + * + * @return true if this table cell is focus traversable, + * false 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 this. + */ + 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 AccessibleJTable. + * + * @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 JTable component. + * + * @return {@link AccessibleRole#TABLE}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.TABLE; + } + + /** + * Returns the accessible table. + * + * @return this. + */ + 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 + * i. 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 + * i + */ + 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 true if the accessible child with the index + * i is selected, false otherwise. + * + * @param i the index of the accessible to check + * + * @return true if the accessible child with the index + * i is selected, false 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 i 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 i + * from the current selection. This will only work on tables that have + * cell selection enabled (rowSelectionAllowed == false && + * columnSelectionAllowed == false). + * + * @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 1 + * 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 1 + * 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 null 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 null 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 null 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 null 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 null 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 true if the accessible child at the specified + * row and column is selected, false otherwise. + * + * @param r the row number of the child + * @param c the column number of the child + * + * @return true if the accessible child at the specified + * row and column is selected, false otherwise + */ + public boolean isAccessibleSelected(int r, int c) + { + return isCellSelected(r, c); + } + + /** + * Returns true if the row with the specified index is + * selected, false otherwise. + * + * @param r the row number + * + * @return true if the row with the specified index is + * selected, false otherwise + */ + public boolean isAccessibleRowSelected(int r) + { + return isRowSelected(r); + } + + /** + * Returns true if the column with the specified index is + * selected, false otherwise. + * + * @param c the column number + * + * @return true if the column with the specified index is + * selected, false 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 TableColumns of this + * JTable. + * + * 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 i, automatically change only the + * single column i+1 to provide or absorb excess space + * requirements. + */ + public static final int AUTO_RESIZE_NEXT_COLUMN = 1; + + /** + * When resizing column i in a table of n + * columns, automatically change all columns in the range [i+1, + * n), uniformly, to provide or absorb excess space requirements. + */ + public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2; + + /** + * When resizing column i in a table of n + * columns, automatically change all columns in the range [0, + * n) (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 i in a table of n + * columns, automatically change column n-1 (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. + * null 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 and 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 dataModel, despite its + * property name. The table listens to its model as a {@link + * TableModelListener}. + * + * @see #tableChanged(TableModelEvent) + * @see TableModel#addTableModelListener(TableModelListener) + */ + protected TableModel dataModel; + + /** + *

A model of various aspects of the columns of the table, not + * including 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.

+ * + *

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 table selection: a combination of row and + * column selections.

+ * + *

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.

+ * + * @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 JTable instance. + */ + public JTable () + { + this(null, null, null); + } + + /** + * Creates a new JTable instance with the given number + * of rows and columns. + * + * @param numRows an int value + * @param numColumns an int value + */ + public JTable (int numRows, int numColumns) + { + this(new DefaultTableModel(numRows, numColumns)); + } + + /** + * Creates a new JTable 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 Object[][] the table data + * @param columnNames an Object[] the column headers + */ + public JTable(Object[][] data, Object[] columnNames) + { + this(new DefaultTableModel(data, columnNames)); + } + + /** + * Creates a new JTable 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 JTable 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 JTable 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 JTable 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 new JScrollPane(JTable) + */ + 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. + * null 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 false, 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 scrollableTracksViewportHeight property. + * + * @return The constant value false + */ + public boolean getScrollableTracksViewportHeight() + { + return false; + } + + /** + * Get the value of the scrollableTracksViewportWidth property. + * + * @return true unless the {@link #autoResizeMode} property is + * AUTO_RESIZE_OFF + */ + 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 columnCount 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 rowCount 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 selectedColumn 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 selectedColumnCount 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 selectedColumns 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 columnSelectionAllowed property. + * + * @return The current value of the columnSelectionAllowed property + * + * @see #setColumnSelectionAllowed(boolean) + */ + public boolean getColumnSelectionAllowed() + { + return getColumnModel().getColumnSelectionAllowed(); + } + + /** + * Get the value of the selectedRowCount 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 selectedRows 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 intercellSpacing 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 false to true, 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; + } + + /** + *

Set the value of the {@link #dataModel} property.

+ * + *

Unregister this as a {@link TableModelListener} from + * previous {@link #dataModel} and register it with new parameter + * m.

+ * + * @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(); + } + + /** + *

Set the value of the {@link #columnModel} property.

+ * + *

Unregister this as a {@link TableColumnModelListener} + * from previous {@link #columnModel} and register it with new parameter + * c.

+ * + * @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 columnSelectionAllowed 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(); + } + } + + /** + *

Set the value of the {@link #selectionModel} property.

+ * + *

Unregister this as a {@link ListSelectionListener} + * from previous {@link #selectionModel} and register it with new + * parameter s.

+ * + * @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 selectionMode 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(); + } + + /** + *

Set the value of the {@link #cellEditor} property.

+ * + *

Unregister this as a {@link CellEditorListener} from + * previous {@link #cellEditor} and register it with new parameter + * c.

+ * + * @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 intercellSpacing 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(); + } + + /** + *

Set the value of the {@link #selectionBackground} property.

+ * + *

Fire a PropertyChangeEvent with name {@link + * #SELECTION_BACKGROUND_CHANGED_PROPERTY} to registered listeners, if + * selectionBackground changed.

+ * + * @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(); + } + + /** + *

Set the value of the {@link #selectionForeground} property.

+ * + *

Fire a PropertyChangeEvent with name {@link + * #SELECTION_FOREGROUND_CHANGED_PROPERTY} to registered listeners, if + * selectionForeground changed.

+ * + * @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 showGrid 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 doLayout() + */ + public void sizeColumnsToFit(boolean lastColumnOnly) + { + doLayout(); + } + + /** + * Obsolete since JDK 1.4. Please use doLayout(). + */ + 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 identifier is + * null or there is no column with that identifier. + * + * @see TableColumn#setIdentifier(Object) + */ + public TableColumn getColumn(Object identifier) + { + return columnModel.getColumn(columnModel.getColumnIndex(identifier)); + } + + /** + * Returns true if the specified cell is editable, and + * false 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 JTable'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 JTable 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 + * false 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 + * false 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 c or + * propertyValue is null + */ + 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 JTextArea 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 java.awt.TextArea component, + * which provides similar functionality using native widgets. + *

+ * + * This component has additional functionality to the AWT class. It follows + * the same design pattern as seen in other text components, such as + * JTextField, JTextPane and JEditorPane, + * and embodied in JTextComponent. These classes separate the text + * (the model) from its appearance within the onscreen component (the view). The + * text is held within a javax.swing.text.Document 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 + * DocumentEvents delivered to registered + * DocumentListeners, rather than this component. + *

+ * + * Unlike java.awt.TextArea, JTextArea does not + * handle scrolling. Instead, this functionality is delegated to a + * JScrollPane, 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 rows and columns 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 JTextArea. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJTextArea extends AccessibleJTextComponent + { + + /** + * Creates a new AccessibleJTextArea object. + */ + protected AccessibleJTextArea() + { + super(); + } + + /** + * Returns the accessible state of this AccessibleJTextArea. + * + * @return the accessible state of this AccessibleJTextArea + */ + 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 JTextArea object. + */ + public JTextArea() + { + this(null, null, 0, 0); + } + + /** + * Creates a new JTextArea object. + * + * @param text the initial text + */ + public JTextArea(String text) + { + this(null, text, 0, 0); + } + + /** + * Creates a new JTextArea 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 JTextArea 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 JTextArea object. + * + * @param doc the document model to use + */ + public JTextArea(Document doc) + { + this(doc, null, 0, 0); + } + + /** + * Creates a new JTextArea 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 direction. + * + * @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 orientation 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 true if line wrapping is enabled, + * false otherwise + */ + public boolean getLineWrap() + { + return lineWrap; + } + + /** + * Enables/disables line wrapping. + * + * @param flag true to enable line wrapping, + * false 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 true if word style wrapping is enabled, + * false otherwise + */ + public boolean getWrapStyleWord() + { + return wrapStyleWord; + } + + /** + * Enables/Disables word style wrapping. + * + * @param flag true to enable word style wrapping, + * false 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 < 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 JTextArea. + * + * @return the accessible context associated with the JTextArea + */ + 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 AccessibleJTextField. + * + * @return the accessible state of this AccessibleJTextField + */ + 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 JTextField. + */ + public JTextField() + { + this(null, null, 0); + } + + /** + * Creates a new instance of JTextField. + * + * @param text the initial text + */ + public JTextField(String text) + { + this(null, text, 0); + } + + /** + * Creates a new instance of JTextField. + * + * @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 JTextField. + * + * @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 JTextField. + * + * @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 PlainDocument. + * + * @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 filterNewlines to + * true 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 ActionListener objects. + * + * @return an array of listeners + * + * @since 1.4 + */ + public ActionListener[] getActionListeners() + { + return (ActionListener[]) getListeners(ActionListener.class); + } + + /** + * Sends an action event to all registered + * ActionListener 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 JTextField. + * + * @return the accessible context associated with the JTextField + */ + 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 true, unless this is embedded in a + * JViewport in which case the viewport takes responsibility of + * validating. + * + * @return true, unless this is embedded in a + * JViewport 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 JTextPane with a null document. + */ + public JTextPane() + { + super(); + } + + /** + * Creates a new JTextPane and sets the specified + * document. + * + * @param document the content model to use + */ + public JTextPane(StyledDocument document) + { + this(); + setStyledDocument(document); + } + + /** + * Returns the UI class ID. This is TextPaneUI. + * + * @return TextPaneUI + */ + public String getUIClassID() + { + return "TextPaneUI"; + } + + /** + * Sets the content model for this JTextPane. + * JTextPane can only be used with {@link StyledDocument}s, + * if you try to set a different type of Document, an + * IllegalArgumentException is thrown. + * + * @param document the content model to set + * + * @throws IllegalArgumentException if document is not an + * instance of StyledDocument + * + * @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 JTextPane. This is a typed wrapper for + * {@link #getDocument()}. + * + * @return the content model of this JTextPane + */ + public StyledDocument getStyledDocument() + { + return (StyledDocument) super.getDocument(); + } + + /** + * Sets the content model for this JTextPane. + * + * @param document the content model to set + */ + public void setStyledDocument(StyledDocument document) + { + super.setDocument(document); + } + + /** + * Replaces the currently selected text with the specified + * content. If there is no selected text, this results + * in a simple insertion at the current caret position. If there is + * no content 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 Icon into the text at the current caret position. + * + * @param icon the Icon 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 parent style, if one is specified. + * + * While it is legal to add nameless styles (nm == nullnull if the style should + * be unnamed + * @param parent the parent in which unspecified style attributes are + * resolved, or null if that is not necessary + * + * @return the newly created Style + */ + public Style addStyle(String nm, Style parent) + { + return getStyledDocument().addStyle(nm, parent); + } + + /** + * Removes a named Style from the style hierarchy. + * + * @param nm the name of the Style to be removed + */ + public void removeStyle(String nm) + { + getStyledDocument().removeStyle(nm); + } + + /** + * Looks up and returns a named Style. + * + * @param nm the name of the Style + * + * @return the found Style of null if no such + * Style 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 true, 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 true, 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 + * JTextPane. + * + * @return the current StyledEditorKit of this + * JTextPane + */ + protected final StyledEditorKit getStyledEditorKit() + { + return (StyledEditorKit) getEditorKit(); + } + + /** + * Creates the default {@link EditorKit} that is used in + * JTextPanes. This is an instance of {@link StyledEditorKit}. + * + * @return the default {@link EditorKit} that is used in + * JTextPanes + */ + protected EditorKit createDefaultEditorKit() + { + return new StyledEditorKit(); + } + + /** + * Sets the {@link EditorKit} to use for this JTextPane. + * JTextPanes can only handle {@link StyledEditorKit}s, + * if client programs try to set a different type of EditorKit + * then an IllegalArgumentException is thrown + * + * @param editor the EditorKit to set + * + * @throws IllegalArgumentException if editor is no + * StyledEditorKit + */ + 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 JToggleButton component provides a stateful button, + * which can be either selected or unselected. This provides the basis + * for the implementations of radio buttons (JRadioButton) + * and check boxes (JCheckBox). + * + * @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 AccessibleRole, 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 true 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 JToggleButton. + * 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 String 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 String 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 JToolBar + * component. + */ + protected class AccessibleJToolBar extends AccessibleJComponent + { + private static final long serialVersionUID = -5516888265903814215L; + + /** + * Creates a new AccessibleJToolBar 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 JToolBar 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 JToolBar + * component, for use in debugging. The return value is guaranteed to be + * non-null, but the format of the string may vary between + * implementations. + * + * @return A string describing the attributes of the JToolBar. + */ + 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 + * JToolBar 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 JToolTip + * 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 JToolTip 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 JToolTip instance. + */ + public JToolTip() + { + disableEvents(AWTEvent.MOUSE_EVENT_MASK); + updateUI(); + } + + /** + * Returns the text displayed by the tool tip. + * + * @return The text (possibly null). + * + * @see #setTipText(String) + */ + public String getTipText() + { + return text; + } + + /** + * Returns the object that provides accessibility features for this + * JToolTip 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 null). + * + * @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 + * "ToolTipUI". + * + * @return "ToolTipUI". + */ + public String getUIClassID() + { + return "ToolTipUI"; + } + + /** + * Returns a string describing the attributes for the JToolTip + * component, for use in debugging. The return value is guaranteed to be + * non-null, but the format of the string may vary between + * implementations. + * + * @return A string describing the attributes of the JToolTip. + */ + 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 (null 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 (null 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 true 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 true 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 pos. 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 + * expandedState 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 JTree object. + */ + public JTree() + { + this(getDefaultTreeModel()); + } + + /** + * Creates a new JTree object. + * + * @param value the initial nodes in the tree + */ + public JTree(Hashtable value) + { + this(createTreeModel(value)); + } + + /** + * Creates a new JTree object. + * + * @param value the initial nodes in the tree + */ + public JTree(Object[] value) + { + this(createTreeModel(value)); + } + + /** + * Creates a new JTree 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 JTree object. + * + * @param root the root node + */ + public JTree(TreeNode root) + { + this(root, false); + } + + /** + * Creates a new JTree 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 JTree 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 TreeModel 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 JTree object. + * + * @return the associated TreeUI object + */ + public TreeUI getUI() + { + return (TreeUI) ui; + } + + /** + * Sets the UI associated with this JTree object. + * + * @param ui the TreeUI 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 + * JTree. + * + * @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 TreeExpansionListener object to the tree. + * + * @param listener the listener to add + */ + public void addTreeExpansionListener(TreeExpansionListener listener) + { + listenerList.add(TreeExpansionListener.class, listener); + } + + /** + * Removes a TreeExpansionListener object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeExpansionListener(TreeExpansionListener listener) + { + listenerList.remove(TreeExpansionListener.class, listener); + } + + /** + * Returns all added TreeExpansionListener 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 TreeSelctionListener object to the tree. + * + * @param listener the listener to add + */ + public void addTreeSelectionListener(TreeSelectionListener listener) + { + listenerList.add(TreeSelectionListener.class, listener); + } + + /** + * Removes a TreeSelectionListener object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeSelectionListener(TreeSelectionListener listener) + { + listenerList.remove(TreeSelectionListener.class, listener); + } + + /** + * Returns all added TreeSelectionListener 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 TreeWillExpandListener object to the tree. + * + * @param listener the listener to add + */ + public void addTreeWillExpandListener(TreeWillExpandListener listener) + { + listenerList.add(TreeWillExpandListener.class, listener); + } + + /** + * Removes a TreeWillExpandListener object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeWillExpandListener(TreeWillExpandListener listener) + { + listenerList.remove(TreeWillExpandListener.class, listener); + } + + /** + * Returns all added TreeWillExpandListener 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 JTree object. + * + * @return the associated TreeModel + */ + public TreeModel getModel() + { + return treeModel; + } + + /** + * Sets the model to use in JTree. + * + * @param model the TreeModel 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 JTree object is editable. + * + * @return true if this tree object is editable, + * false otherwise + */ + public boolean isEditable() + { + return editable; + } + + /** + * Sets the editable property. + * + * @param flag true to make this tree object editable, + * false 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 true if the root element is visible, + * false 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 true when the specified row is selected, + * false otherwise. This call is delegated to the + * {@link TreeSelectionModel#isRowSelected(int)} method. + * + * @param row the row to check + * + * @return true when the specified row is selected, + * false otherwise + */ + public boolean isRowSelected(int row) + { + return selectionModel.isRowSelected(row); + } + + public boolean isSelectionEmpty() + { + return selectionModel.isSelectionEmpty(); + } + + /** + * Return the value of the dragEnabled property. + * + * @return the value + * + * @since 1.4 + */ + public boolean getDragEnabled() + { + return dragEnabled; + } + + /** + * Set the dragEnabled 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 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 value.toString() 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 + * null. + * + * @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 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 + * startingRow that starts with prefix. + * Searching is done in the direction specified by bias. + * + * @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 null 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 path. If includePath is set + * to true and path itself is selected, then + * it will be removed too. + * + * @param path the path from which selected descendants are to be removed + * @param includeSelected if true then path itself + * will also be remove if it's selected + * + * @return true if something has been removed, + * false 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 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); + } + } + } + + /** + *

+ * 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. + *

+ *

+ * 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 not 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 c or + * propertyValue is null + */ + 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; + +/** + * + *

+ *                                                     _
+ *   +-------------------------------+    ...........Y1 \
+ *   |  view                         |                .  \
+ *   |  (this component's child)     |                .   > VY
+ *   |                               |                .  / = Y2-Y1
+ *   |         +------------------------------+  ....Y2_/
+ *   |         | viewport            |        |       .
+ *   |         | (this component)    |        |       .
+ *   |         |                     |        |       .
+ *   |         |                     |        |       .
+ *   |         |                     |        |       .
+ *   |         |                     |        |       .
+ *   |         +------------------------------+  ....Y3
+ *   |                               |                .
+ *   |         .                     |        .       .
+ *   |         .                     |        .       .
+ *   +---------.---------------------+    ...........Y4
+ *   .         .                     .        .
+ *   .         .                     .        .
+ *   .         .                     .        .
+ *   X1.......X2.....................X3.......X4
+ *   \____  ___/
+ *        \/
+ *        VX = X2-X1
+ *
+ * + *

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".

+ * + *

But in terms of drawing its child, the viewport thinks of itself as + * covering a particular position of the view's coordinate space. + * For example, the {@link #getViewPosition} method returns + * the position (VX,VY) shown above, which is an position in + * "view space", even though this is implemented by positioning + * the underlying child at position (-VX,-VY)

+ * + */ +public class JViewport extends JComponent implements Accessible +{ + /** + * Provides accessibility support for JViewport. + * + * @author Roman Kennke (roman@kennke.org) + */ + protected class AccessibleJViewport extends AccessibleJComponent + { + /** + * Creates a new instance of AccessibleJViewport. + */ + protected AccessibleJViewport() + { + // Nothing to do here. + } + + /** + * Returns the accessible role of JViewport, which is + * {@link AccessibleRole#VIEWPORT}. + * + * @return the accessible role of JViewport + */ + 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 false, 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 false + */ + 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 JViewport. This + * will be an instance of {@link AccessibleJViewport}. + * + * @return the accessible context for this JViewport + */ + 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. dx + * and dy specifiy the X and Y offset by which the viewport + * is scrolled. All other arguments are output parameters and are filled by + * this method. + * + * blitFrom holds the position of the blit rectangle in the + * viewport rectangle before scrolling, blitTo where the blitArea + * is copied to. + * + * blitSize holds the size of the blit area and + * blitPaint is the area of the view that needs to be painted. + * + * This method returns true if blitting is possible and + * false 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 true if blitting is possible, + * false 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 JWindow. + */ + protected class AccessibleJWindow extends Window.AccessibleAWTWindow + { + /** + * Creates a new instance of AccessibleJWindow. + */ + 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 JWindow that has a shared invisible owner frame + * as its parent. + */ + public JWindow() + { + super(SwingUtilities.getOwnerFrame(null)); + windowInit(); + } + + /** + * Creates a new JWindow 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 JWindow that has the specified + * owner frame. If owner is null, then + * an invisible shared owner frame is installed as owner frame. + * + * @param owner the owner frame of this window; if null a shared + * invisible owner frame is used + */ + public JWindow(Frame owner) + { + super(SwingUtilities.getOwnerFrame(owner)); + windowInit(); + } + + /** + * Creates a new JWindow that has the specified + * owner window. If owner is null, + * then an invisible shared owner frame is installed as owner frame. + * + * @param owner the owner window of this window; if null a + * shared invisible owner frame is used + */ + public JWindow(Window owner) + { + super(SwingUtilities.getOwnerFrame(owner)); + windowInit(); + } + + /** + * Creates a new JWindow for the given graphics configuration + * and that has the specified owner window. If + * owner is null, then an invisible shared owner + * frame is installed as owner frame. + * + * The gc parameter can be used to open the window on a + * different screen for example. + * + * @param owner the owner window of this window; if null 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 getAWTKeyStroke(). + * But it returns null instead of throwing + * IllegalArugmentException 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 comp. 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: + *
    + *
  • SINGLE_SELECTION - only one item in the list may be + * selected;
  • + *
  • SINGLE_INTERVAL_SELECTION - only one interval in the list + * may be selected;
  • + *
  • MULTIPLE_INTERVAL_SELECTION - any combination of items in + * the list may be selected.
  • + *
+ * The model uses an event notification mechanism to notify listeners (see + * {@link ListSelectionListener}) about updates to the selection model. + *

+ * 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. + *

+ * 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 mode 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. + *

+ * FIXME: what happens to the anchor and lead selection indices (the spec + * is silent about this)? See: + *

+ * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4334792 + */ + void clearSelection(); + + /** + * Returns the lowest selected index, or -1 if there is no + * selection. + * + * @return The lowest selected index. + * + * @see #getMaxSelectionIndex() + */ + int getMinSelectionIndex(); + + /** + * Returns the highest selected index, or -1 if there is no + * selection. + * + * @return The highest selected index. + * + * @see #getMinSelectionIndex() + */ + int getMaxSelectionIndex(); + + /** + * Returns true if the specified item is selected, and + * false otherwise. Special note: if index is + * negative, this method should return false (no exception + * should be thrown). + * + * @param index the item index (zero-based). + * + * @return true if the specified item is selected, and + * false otherwise. + */ + boolean isSelectedIndex(int index); + + /** + * Returns true if there is no selection, and false + * otherwise. + * + * @return true if there is no selection, and + * false otherwise. + */ + boolean isSelectionEmpty(); + + /** + * Sets the selection interval to the specified range (note that + * anchor can be less than, equal to, or greater than + * lead). If this results in the selection being changed, + * a {@link ListSelectionEvent} is sent to all registered listeners. + *

+ * If the selection mode is {@link #SINGLE_SELECTION}, only the + * lead 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: + *

    + *
  • SINGLE_SELECTION - only the lead item is + * selected;
  • + *
  • SINGLE_INTERVAL_SELECTION - the existing selection + * interval is replaced by the specified interval;
  • + *
  • MULTIPLE_INTERVAL_SELECTION - the specified interval is + * merged into the currently selected intervals.
  • + *
+ * Note that anchor can be less than, equal to, or greater than + * lead. + * + * @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: + *
    + *
  • SINGLE_SELECTION - XXX;
  • + *
  • SINGLE_INTERVAL_SELECTION - XXX;
  • + *
  • MULTIPLE_INTERVAL_SELECTION - XXX.
  • + *
+ * Note that anchor can be less than, equal to, or greater than + * lead. + * + * @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 length items at the + * specified index (the before flag indicates + * whether the range is inserted before or after the existing item at + * index). + * + * 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 true, the interval should be inserted + * before index, 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 true at the start of the sequence, and + * false 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 (null 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 null, this method does nothing. + * + * @param listener the listener (null 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 look-and-feel controls most aspects of the appearance and + * operation of user interface components in javax.swing. 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 Toolkit.getDefaultToolkit() + * .getDesktopProperty(systemPropertyName), or + * fallbackValue if no such property is defined. + * + * @param systemPropertyName the system property name. + * @param fallbackValue the fallback value. + * + * @return The property value or fallbackValue. + */ + 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 true when the look-and-feel supports window + * decorations, and false otherwise. This default implementation + * always returns false and needs to be overridden when the + * derived look-and-feel supports this. + * + * @return false. + * + * @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 + * null or already an instance of {@link UIResource}. + * + * @param c the component (null 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 (null 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 (null 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 true if the look-and-feel is the "native" + * look-and-feel for the current platform, and false 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 true if the look-and-feel is supported on the + * current operating system, and false 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. keys 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. + * keys 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. + * keys 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. + * keyBindingList 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 (null 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. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TypeSupported properties
JComponentopaque, autoscrolls
AbstractButtonborderPainted, rolloverEnabled, iconTextGap, + * contentAreaFilled
JDesktopPanedragMode
JSplitPanedividerSize, oneTouchExpandable
JTablerowHeight
JTreerowHeight, scrollsOnExpand, showsRootHandles
+ * + * @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 c or + * propertyValue is null + * + * @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 + * alignmentX and alignmentY 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: + * + *
+ *          +-------+
+ *          |   1   |
+ *          +-------+
+ *      +-------+
+ *      |   2   |
+ *      +-------+
+ * +---------+
+ * |    3    +
+ * +---------+
+ * 
+ * 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 + * alignmentX and alignmentY 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: + * + *
+   *          +-------+
+   *          |   1   |
+   *          +-------+
+   *      +-------+
+   *      |   2   |
+   *      +-------+
+   * +---------+
+   * |    3    +
+   * +---------+
+   * 
+ * 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. + * + *

To obtain an instance of Popup, use the + * {@link javax.swing.PopupFactory}. + * + * @since 1.4 + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class Popup +{ + /** + * Constructs a new Popup given its owner, + * contents and the screen position where the popup + * will appear. + * + * @param owner the Component to which x and + * y are relative, or null for + * placing the popup relative to the origin of the screen. + * + * @param contents the contents that will be displayed inside + * the Popup. + * + * @param x the horizontal position where the Popup will appear. + * + * @param y the vertical position where the Popup will appear. + * + * @throws IllegalArgumentException if contents + * is null. + */ + 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 Popup. + */ + protected Popup() + { + // Nothing to do here. + } + + + /** + * Displays the Popup on the screen. Nothing happens + * if it is currently shown. + */ + public void show() + { + // Implemented by subclasses, for instance JWindowPopup. + } + + + /** + * Removes the Popup from the screen. Nothing happens + * if it is currently hidden. + */ + public void hide() + { + // Implemented by subclasses, for instance JWindowPopup. + } + + + /** + * A Popup that uses a JWindow for + * displaying its contents. + * + * @see PopupFactory#getPopup + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + static class JWindowPopup + extends Popup + { + /** + * The JWindow used for displaying the contents + * of the popup. + */ + JWindow window; + + private Component contents; + + /** + * Constructs a new JWindowPopup given its owner, + * contents and the screen position where the popup + * will appear. + * + * @param owner the Component to which x and + * y are relative, or null for + * placing the popup relative to the origin of the screen. + * + * @param contents the contents that will be displayed inside + * the Popup. + * + * @param x the horizontal position where the Popup will appear. + * + * @param y the vertical position where the Popup will appear. + * + * @throws IllegalArgumentException if contents + * is null. + */ + 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 JWindow on the screen. + * Nothing happens if it is already visible. + */ + public void show() + { + window.setSize(contents.getSize()); + window.show(); + } + + + /** + * Removes the popup's JWindow 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 LightweightPopup 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 Popup. + * + * @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 contents + * is null. + */ + 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 Popup 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 PopupFactory. Usually, a single + * PopupFactory is shared among multiple consumers + * of Popup. 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 factory + * is null. + */ + 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 PopupFactory that can be used + * to create Popup 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 Popup given its owner, + * contents and the screen position where the popup + * will appear. + * + * @param owner the Component to which x and + * y are relative, or null for + * placing the popup relative to the origin of the screen. + * + * @param contents the contents that will be displayed inside + * the Popup. + * + * @param x the horizontal position where the Popup will appear. + * + * @param y the vertical position where the Popup will appear. + * + * @throws IllegalArgumentException if contents + * is null. + */ + 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; + +/** + *

Using this class you can easily monitor tasks where you cannot + * estimate the duration exactly.

+ * + *

A ProgressMonitor instance waits until the first time setProgress + * is called. When millisToDecideToPopup time elapsed the + * instance estimates the duration until the whole operation is completed. + * If this duration exceeds millisToPopup a non-modal dialog + * with a message and a progress bar is shown.

+ * + *

The value of millisToDecideToPopup defaults to + * 500 and millisToPopup to + * 2000.

+ * + * @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 ProgressMonitor 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 + * null. + * @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; + } + + /** + *

Hides the dialog and stops any measurements.

+ * + *

Has no effect when setProgress is not at least + * called once.

+ */ + public void close() + { + if (progressDialog != null) + { + progressDialog.setVisible(false); + } + + if (timer != null) + { + timer.stop(); + timer = null; + } + } + + /** + *

Updates the progress value.

+ * + *

When called for the first time this initializes a timer + * which decides after millisToDecideToPopup time + * whether to show a progress dialog or not.

+ * + *

If the progress value equals or exceeds the maximum + * value the progress dialog is closed automatically.

+ * + * @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; + } + + /** + *

Use this method to set the minimum or start value of + * your operation.

+ * + *

For typical application like copy operation this will be + * zero.

+ * + *

Keep in mind that changing this value after the progress + * dialog is made visible has no effect upon the progress bar.

+ * + * @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; + } + + /** + *

Sets the maximum or end value of the operation to the + * given integer.

+ * + * @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. + * + *

This method has no effect when the progress dialog + * is already visible.

+ * + * @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; + } + + /** + *

Set the message shown in the progess dialog.

+ * + *

Changing the note while the progress dialog is visible + * is possible.

+ * + * @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 ProgressMonitorInputStream. + * + * @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 + * int 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 length 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; + +/** + *

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.

+ * + *

See this + * document for more details.

+ * document for more details.

+ * + * @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(); + + /** + *

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}.

+ * + *

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.

+ */ + 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 RepaintManager for the current thread's + * thread group. The default implementation ignores the + * component 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 RepaintManager for the current thread's + * thread group. The default implementation ignores the + * component 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 null + * 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 true if the specified component is completely + * contained within its dirty region, otherwise false + * + * @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 dirty parameter. This searches up the component + * hierarchy of dirty 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 (proposedWidth, + * proposedHeight) 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 JScrollPane, 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 -1 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 -1 to represent no selection). + * + * @see #getSelectedIndex() + * @see #clearSelection + */ + void setSelectedIndex(int index); + + /** + * Clears the selection by setting the selected index to -1 and + * sends a {@link ChangeEvent} to all registered listeners. If the selected + * index is already -1, this method does nothing. + */ + void clearSelection(); + + /** + * Returns true if there is a selection, and false + * 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: + *
    + *
  • Tiled: the components are placed at position top-left or bottom-right + * position within their allocated space
  • + *
  • Aligned: the components are placed aligned in their allocated space + * according to their alignment value
  • + *
+ * + * @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 children. + * + * @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 + * children. + * + * @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 + * allocated, the total size requirements of the set of + * components in total (this can be calculated using + * {@link #getTiledSizeRequirements} and the size requirements of the + * components in children. + * + * The calculated offset and span values for each component are then + * stored in the arrays offsets and spans. + * + * 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 + * allocated, the total size requirements of the set of + * components in total (this can be calculated using + * {@link #getTiledSizeRequirements} and the size requirements of the + * components in children. + * + * The calculated offset and span values for each component are then + * stored in the arrays offsets and spans. + * + * Depending on the value of forward 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 + * allocated, the total size requirements of the set of + * components in total (this can be calculated using + * {@link #getTiledSizeRequirements} and the size requirements of the + * components in children. + * + * The calculated offset and span values for each component are then + * stored in the arrays offsets and spans. + * + * 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 + * allocated, the total size requirements of the set of + * components in total (this can be calculated using + * {@link #getTiledSizeRequirements} and the size requirements of the + * components in children. + * + * The calculated offset and span values for each component are then + * stored in the arrays offsets and spans. + * + * Depending on the value of forward 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 + * delta. delta 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 SizeSequence instance. + */ + public SizeSequence() + { + sizes = new int[0]; + } + + /** + * Creates a new SizeSequence 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 SizeSequence instance with the specified number + * of elements all having the same size (value). + * + * @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 SizeSequence instance using the specified + * element sizes. + * + * @param sizes the element sizes (null 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 (null 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 start position. + * There are length new entries each having the specified + * value. + * + * @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 start (the number of elements + * removed is length). + * + * @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; + + /** + *

Whether or not to perform an "implicit DownCycle" when selecting + * successor components within a focus cycle.

+ * + *

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.

+ * + *

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.

+ * + * @see #getImplicitDownCycleTraversal() + */ + boolean implicitDownCycleTraversal = true; + + /** + * Creates a new SortingFocusTraversalPolicy with no + * comparator set. + */ + protected SortingFocusTraversalPolicy() + { + // Do nothing here. + } + + /** + * Creates a new SortingFocusTraversalPolicy with the given + * comparator set. + * + * @param comparator the comparator to set + */ + public SortingFocusTraversalPolicy(Comparator 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 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 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 true and the + * comp is a focus cycle root, an "implicit DownCycle" + * occurs and the method returns the + * getDefaultComponent(comp). + * + * @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 root, 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 root + * + * @throws IllegalArgumentException if root is null + */ + public Component getDefaultComponent(Container root) + { + return getFirstComponent(root); + } + + /** + * Return the first focusable component of the focus cycle root + * comp 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 root, 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 + * comp 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 root, 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 (null + * for no minimum). + */ + private Comparable start; + + /** + * A constraint on the end or latest permitted date (null 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 SpinnerDateModel 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 SpinnerDateModel with the specified value, lower + * and upper bounds, and which spins the specified calendar field. + *

+ * The start and end limits must have a + * compareTo 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 (null not permitted). + * @param start a constraint that specifies the earliest permitted date + * value, or null for no lower limit. + * @param end a constraint that specifies the latest permitted date value, + * or null for no upper limit. + * @param calendarField the Calendar 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 null). + * + * @see #getValue() + */ + public Date getDate() + { + return date.getTime(); + } + + /** + * Returns the lower limit on the date/time value, or null 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 null 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 null). + */ + public Object getValue() + { + return date.getTime(); + } + + /** + * Returns the next date in the sequence, or null if the + * next date is past the upper limit (if one is specified). The current date + * is not changed. + * + * @return The next date, or null 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 null 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 null 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 calendarField 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 null 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 + * start is {@link Serializable}. + * + * @param start the new lower limit for the date/time value + * (null 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 null 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 + * end is {@link Serializable}. + * + * @param end the new upper limit for the date/time value (null + * 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 (null not permitted, must be an + * instance of Date). + * + * @throws IllegalArgumentException if value is not an instance + * of Date. + */ + 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 SpinnerModel 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. + *

+ * + * The model itself inherits a list of ChangeListeners from + * AbstractSpinnerModel. 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 fireStateChanged(), + * in order to notify any ChangeListeners, when the list or array + * changes. The model handles notification when the reference itself + * is changed via setList() or when the current value is + * set directly using setValue(). + * + * @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 SpinnerListModel. This + * is a model backed by a list containing only the single + * String 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 SpinnerListModel using the supplied list. + * The model maintains a reference to this list, and returns + * consecutive elements in response to calls to getNextValue(). + * The initial value is that at position 0, so an initial call + * to getValue() returns the same as list.get(0). + * + * @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 SpinnerListModel using the supplied array. + * The model stores a reference to the wrapper list returned by + * Arrays.asList(). 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 getNextValue(). The initial + * value returned by getValue() is the same as + * array[0]. + * + * @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 getNextValue() + * or getPreviousValue(). + * + * @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 + * ChangeEvent 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, getNextValue() and + * getPreviousValue() 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 ChangeListeners. + * + * @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 ChangeListener 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 ChangeListener 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 null). */ + private Comparable minimum; + + /** The maximum value (or null). */ + private Comparable maximum; + + /** The step size. */ + private Number stepSize; + + /** + * Creates a SpinnerNumberModel with initial value 0, step 1, + * and no maximum nor minimum. + */ + public SpinnerNumberModel() + { + this(new Integer(0), null, null, new Integer(1)); + } + + /** + * Creates a SpinnerNumberModel 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 <= value <= 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 SpinnerNumberModel 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 <= value <= 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 SpinnerNumberModel with the given attributes. The + * caller should ensure that both minimum and + * maximum are serializable. + * + * @param value the initial value (null not permitted). + * @param minimum the minimum value (null permitted). + * @param maximum the maximum value (null permitted). + * @param stepSize the step size (null not permitted). + * + * @throws IllegalArgumentException if minimum <= value <= maximum + * does not hold + * @throws IllegalArgumentException if value is + * null. + * @throws IllegalArgumentException if stepSize is + * null. + */ + 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 (null not permitted, must be an + * instance of Number). + * + * @throws IllegalArgumentException if value is not an instance + * of Number. + */ + 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 null 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 null 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 null 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 null 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 null 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 + * null 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 + * newMinimum is {@link Serializable}. + * + * @param newMinimum the new minimum value (null permitted). + * + * @see #getMinimum() + */ + public void setMinimum(Comparable newMinimum) + { + if (minimum != null ? !minimum.equals(newMinimum) : newMinimum != null) + { + minimum = newMinimum; + fireStateChanged(); + } + } + + /** + * Returns the maximum value, or null 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 + * null 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 + * newMaximum is {@link Serializable}. + * + * @param newMaximum the new maximum (null 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 null). + */ + 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 (null not permitted). + * + * @throws IllegalArgumentException if newStepSize is + * null. + */ + 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}. + *

+ * A Spring defines a minimum, preferred and maximum distance for each edge + * (north, east, south, west) of a component. + *

+ * 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 s. + */ + public static Spring minus(Spring s) + { + return new MinusSpring(s); + } + + /** + * Sets the actual value. If value 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 s1 + s2. + */ + 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. + *

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.

+ * + * @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 edgeName. + * 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 edgeName. + * + * @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 edgeName 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 p. + * + * @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 p. + * + * @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 p. + * + * @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 e1 of component c1 to + * the edge e2 of component c2 width the + * fixed strut pad. + * + * @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 e1 of component c1 to + * the edge e2 of component c2 width the + * {@link Spring} s. + * + * @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 component's 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 null, + * this method returns null). + * @param r a carrier to store the return value in (if null, a + * new Rectangle 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 null if comp 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 null + * + * @deprecated 1.4 Replaced by + * KeyboardFocusManager.getFocusOwner(). + */ + 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 Accessible child of the specified component + * which appears at the supplied Point. 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 Accessible child at the point, p, + * 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); + } + + /** + *

+ * Returns the Accessible 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. + *

+ *

+ * 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. + *

+ * + * @param c the component whose child should be returned. + * @param i the index of the child within the parent component. + * @return the Accessible child at index i + * in the component, c. + * @see javax.accessibility.AccessibleContext#getAccessibleChild + * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChild + */ + public static Accessible getAccessibleChild(Component c, int i) + { + return c.getAccessibleContext().getAccessibleChild(i); + } + + /** + *

+ * Returns the number of Accessible children within + * the supplied component. + *

+ *

+ * 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. + *

+ * + * @param c the component whose children should be counted. + * @return the number of children belonging to the component, + * c. + * @see javax.accessibility.AccessibleContext#getAccessibleChildrenCount + * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChildrenCount + */ + public static int getAccessibleChildrenCount(Component c) + { + return c.getAccessibleContext().getAccessibleChildrenCount(); + } + + /** + *

+ * Returns the zero-based index of the specified component + * within its parent. If the component doesn't have a parent, + * -1 is returned. + *

+ *

+ * 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. + *

+ * + * @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(); + } + + /** + *

+ * Returns a set of AccessibleStates, which represent + * the state of the supplied component. + *

+ *

+ * 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. + *

+ * + * @param c the component whose accessible state should be retrieved. + * @return a set of AccessibleState 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 comp is a RootPaneContainer, return its JRootPane. + * Otherwise call getAncestorOfClass(JRootPane.class, a). + * + * @param comp The component to get the JRootPane of + * + * @return a suitable JRootPane for comp, or null + * + * @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 comp 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 comp with the given + * name, or null 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 comp 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 comp which is an instance + * of the given class, or null 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 comp that is a {@link Window} + * or null if comp is not contained in a + * {@link Window}. + * + * This is equivalent to calling + * getAncestorOfClass(Window, comp) or + * windowForComponent(comp). + * + * @param comp the component for which we are searching the ancestor Window + * + * @return the first ancestor Window of comp or + * null if comp is not contained in a Window + */ + public static Window getWindowAncestor(Component comp) + { + return (Window) getAncestorOfClass(Window.class, comp); + } + + /** + * Equivalent to calling getAncestorOfClass(Window, comp). + * + * @param comp The component to search for an ancestor window + * + * @return An ancestral window, or null if none exists + */ + public static Window windowForComponent(Component comp) + { + return (Window) getAncestorOfClass(Window.class, comp); + } + + /** + * Returns the "root" of the component tree containint comp + * The root is defined as either the least ancestor of + * comp which is a {@link Window}, or the greatest + * ancestor of comp 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 null + */ + 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 (x,y). Returns parent when either + * parent is not a container, or has no children which contain + * (x,y). Returns null when either + * (x,y) is outside the bounds of parent, or parent is + * null. + * + * @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 (x,y), or + * null + * + * @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 (x,y) from the coordinate space of one + * component to another. This is equivalent to converting the point from + * source space to screen space, then back from screen space + * to destination space. If exactly one of the two + * Components is null, it is taken to refer to the root + * ancestor of the other component. If both are null, 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 (x,y) 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 + * source space to screen space, then back from screen space + * to destination space. If exactly one of the two + * Components is null, it is taken to refer to the root + * ancestor of the other component. If both are null, 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 source is + * null, it is taken to refer to destination's + * root component. If destination is null, the + * new event will remain expressed in source'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 comp calling + * updateUI 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 updateUI + */ + 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]); + } + } + + /** + *

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.

+ * + *

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.

+ * + *

The position values control where the text is placed relative to + * the icon. The horizontal position value should be one of the constants + * LEADING, TRAILING, LEFT, + * RIGHT or CENTER. The vertical position value + * should be one fo the constants TOP, BOTTOM + * or CENTER.

+ * + *

The text-icon gap value controls the number of pixels between the + * icon and the text.

+ * + *

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 LEADING, + * TRAILING, LEFT, RIGHT or + * CENTER. The vertical alignment valus should be one of the + * constants TOP, BOTTOM or + * CENTER.

+ * + *

If the LEADING or TRAILING 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 LEFT_TO_RIGHT, + * then the LEADING value is a synonym for LEFT + * and the TRAILING value is a synonym for + * RIGHT

+ * + *

If the text and icon are equal to or larger than the view + * rectangle, the horizontal and vertical alignment values have no + * affect.

+ * + * @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); + } + + /** + *

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.

+ * + *

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.

+ * + *

The position values control where the text is placed relative to + * the icon. The horizontal position value should be one of the constants + * LEFT, RIGHT or CENTER. The + * vertical position value should be one fo the constants + * TOP, BOTTOM or CENTER.

+ * + *

The text-icon gap value controls the number of pixels between the + * icon and the text.

+ * + *

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 LEFT, RIGHT or + * CENTER. The vertical alignment valus should be one of the + * constants TOP, BOTTOM or + * CENTER.

+ * + *

If the text and icon are equal to or larger than the view + * rectangle, the horizontal and vertical alignment values have no + * affect.

+ * + *

Note that this method does not know how to deal with + * horizontal alignments or positions given as LEADING or + * TRAILING 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); + } + + /** + *

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.

+ * + *

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.

+ * + *

The position values control where the text is placed relative to + * the icon. The horizontal position value should be one of the constants + * LEFT, RIGHT or CENTER. The + * vertical position value should be one fo the constants + * TOP, BOTTOM or CENTER.

+ * + *

The text-icon gap value controls the number of pixels between the + * icon and the text.

+ * + *

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 LEFT, RIGHT or + * CENTER. The vertical alignment valus should be one of the + * constants TOP, BOTTOM or + * CENTER.

+ * + *

If the text and icon are equal to or larger than the view + * rectangle, the horizontal and vertical alignment values have no + * affect.

+ * + *

Note that this method does not know how to deal with + * horizontal alignments or positions given as LEADING or + * TRAILING 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 true 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; + } + + /** + *

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 end of the + * parent-pointer chain, as illustrated:

+ * + *
+   *  [{@link javax.swing.JComponent#getActionMap()}]
+   *          --> [{@link javax.swing.ActionMap}]
+   *     parent --> [{@link javax.swing.text.JTextComponent.KeymapActionMap}]
+   *       parent --> [{@link javax.swing.plaf.ActionMapUIResource}]
+   * 
+ * + *

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.

+ * + *

If the provided ActionMap is null, we interpret the + * call as a request to remove the UI-managed ActionMap from the + * component's ActionMap parent chain.

+ */ + 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); + } + } + + /** + *

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 + * end of the parent-pointer chain, as illustrated:

+ * + *
+   *  [{@link javax.swing.JComponent#getInputMap()}]
+   *          --> [{@link javax.swing.InputMap}]
+   *     parent --> [{@link javax.swing.text.JTextComponent.KeymapWrapper}]
+   *       parent --> [{@link javax.swing.plaf.InputMapUIResource}]
+   * 
+ * + *

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.

+ * + *

If the provided InputMap is null, we interpret the + * call as a request to remove the UI-managed InputMap from the + * component's InputMap parent chain.

+ */ + 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 + * null, 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 rect. 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 + * rect. + * + * @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 FontMetrics 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 + * rect. 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 + * rect. + * + * @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 + * component 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 + * component. + * + * @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 JComponent that has a binding + * for the key event in the WHEN_IN_FOCUSED_WINDOW scope. + * + * @param ev the key event + * + * @return true if a binding has been found and processed, + * false 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: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Code:Returned String:
{@link SwingConstants#CENTER}"CENTER"
{@link SwingConstants#LEFT}"LEFT"
{@link SwingConstants#RIGHT}"RIGHT"
{@link SwingConstants#LEADING}"LEADING"
{@link SwingConstants#TRAILING}"TRAILING"
+ *

+ * 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: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Code:Returned String:
{@link SwingConstants#CENTER}"CENTER"
{@link SwingConstants#TOP}"TOP"
{@link SwingConstants#BOTTOM}"BOTTOM"
+ *

+ * 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: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Code:Returned String:
{@link WindowConstants#DO_NOTHING_ON_CLOSE}"DO_NOTHING_ON_CLOSE"
{@link WindowConstants#HIDE_ON_CLOSE}"HIDE_ON_CLOSE"
{@link WindowConstants#DISPOSE_ON_CLOSE}"DISPOSE_ON_CLOSE"
{@link WindowConstants#EXIT_ON_CLOSE}"EXIT_ON_CLOSE"
+ *

+ * 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 java.util.Timer just for + * firing ActionEvents. 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 true, 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(); + + /** + * true if the timer coalesces events. + */ + boolean coalesce = true; + + /** + * true 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; + + /** + * synchronized(queueLock) replaces + * synchronized(queue) 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 null. + */ + 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 true (default) to enable the event coalescing, + * false 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 true if the coalescing is enabled, + * false 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[] getListeners(Class listenerType) + { + return listenerList.getListeners(listenerType); + } + + /** + * Set the timer logging state. If it is set to true, the + * timer prints a message to {@link System#out} when firing each + * action event. + * + * @param lt true if logging is enabled, false + * (default value) otherwise + */ + public static void setLogTimers(boolean lt) + { + logTimers = lt; + } + + /** + * Return the logging state. + * + * @return true 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 d 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 i 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 true (default value) to fire repetetive events. + * false to fire + * only one event after the initial delay + */ + public void setRepeats(boolean r) + { + repeats = r; + } + + /** + * Check is this timer fires repetetive events. + * + * @return true if the timer fires repetetive events, + * false if it fires + * only one event after the initial delay + */ + public boolean isRepeats() + { + return repeats; + } + + /** + * Get the timer state. + * + * @return true if the timer has been started and is firing + * the action events as scheduled. false + * 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 delay 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 delay 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 delay 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 true when the specified data flavor is supported, + * false otherwise. + * + * @return true when the specified data flavor is supported, + * false 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 true if the data in this TransferHandler can be + * imported into the specified component. This will be the case when: + *
    + *
  • The component has a readable and writable property with the property + * name specified in the TransferHandler constructor.
  • + *
  • There is a dataflavor with a mime type of + * application/x-java-jvm-local-object-ref.
  • + *
  • The dataflavor's representation class matches the class of the + * property in the component.
  • + * + * + * @param c the component to check + * @param flavors the possible data flavors + * + * @return true if the data in this TransferHandler can be + * imported into the specified component, false + * 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 null when the specified component + * doesn't have a readable property that matches the property name + * specified in the TransferHandler 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 MOVE. + * + * The default implementation does nothing because MOVE is not supported. + * + * @param c the source component + * @param data the data that has been transferred or null + * 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 c 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 t into the specified + * component c by setting the property of this TransferHandler + * on that component. If this succeeds, this method returns + * true, otherwise false. + * + * + * @param c the component to import into + * @param t the transfer data to import + * + * @return true if the transfer succeeds, false + * 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 null 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 null 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 flavors 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 clazz. + * When no such data flavor is found, this returns null. + * + * @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" -> new BasicButtonUI(). + * + * @author Ronald Veldema (rveldema@cs.vu.nl) + */ +public class UIDefaults extends Hashtable +{ + + /** 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 ActiveValue 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 LazyValue 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 + * entries[0] is a key, entries[1] is a value, + * entries[2] 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 key in the default + * locale. + * + * @return the entry for the specified key + */ + public Object get(Object key) + { + return this.get(key, getDefaultLocale()); + } + + /** + * Returns the entry for the specified key in the Locale + * loc. + * + * @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.
    + * In contrast to + * {@link java.util.Hashtable}s null-values are accepted + * here and treated like #remove(key). + *
    + * 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 null if key + * 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 + * entries[0] is a key, entries[1] is a value, + * entries[2] a key and so forth. + *
    + * If a value is null it is treated like #remove(key). + *
    + * This unconditionally fires a PropertyChangeEvent with + * 'UIDefaults' as name and null 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 null and put it into the Hashtable, if + * it is not null. If the value is null 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 key + */ + 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 key 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 key 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 key 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 key 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 key 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 key 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 key 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 key 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 key 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 key 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 key 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 key 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 key or false 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 key 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 key 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 key 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 key 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 key 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. id + * 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 id + */ + public Class getUIClass(String id, ClassLoader loader) + { + String className = (String) get(id); + if (className == null) + return null; + try + { + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + return (Class) loader.loadClass (className); + } + catch (Exception e) + { + return null; + } + } + + /** + * Returns the ComponentUI class that renders a component. id + * 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 id + */ + public Class 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 null. + * + * @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 loc. + * + * @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 MultiplexUIDefaults instance with + * d as the fallback defaults. + * + * @param d the fallback defaults (null 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 UIManager. There is no need + * to construct an instance of this class, since all methods are static. + */ + public UIManager() + { + // Do nothing here. + } + + /** + * Add a PropertyChangeListener to the listener list. + * + * @param listener the listener to add + */ + public static void addPropertyChangeListener(PropertyChangeListener listener) + { + listeners.addPropertyChangeListener(listener); + } + + /** + * Remove a PropertyChangeListener 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 PropertyChangeListener 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 (null not permitted). + * + * @throws NullPointerException if laf is null. + * + * @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 true if the LAF was removed, and false + * 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 null) 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 null). + * + * @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 false. + * + * @param key the key (null not permitted). + * + * @return The boolean value associated with the specified key. + * + * @throws NullPointerException if key is null. + * + * @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 false. + * + * @param key the key (null not permitted). + * @param locale the locale. + * + * @return The boolean value associated with the specified key. + * + * @throws NullPointerException if key is null. + * + * @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 (null not permitted). + * + * @return The border associated with the given key, or null. + * + * @throws NullPointerException if key is null. + */ + 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 (null not permitted). + * @param locale the locale. + * + * @return The border associated with the given key, or null. + * + * @throws NullPointerException if key is null. + * + * @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 (null not permitted). + * + * @return The color associated with the given key, or null. + * + * @throws NullPointerException if key is null. + */ + 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 (null not permitted). + * @param locale the locale. + * + * @return The color associated with the given key, or null. + * + * @throws NullPointerException if key is null. + * + * @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 "javax.swing.plaf.metal.MetalLookAndFeel" + */ + 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 (null not permitted). + * + * @return The color associated with the given key, or null. + * + * @throws NullPointerException if key is null. + */ + 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 (null not permitted). + * @param locale the locale. + * + * @return The color associated with the given key, or null. + * + * @throws NullPointerException if key is null. + * @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 + * TitledBorder.font. + * + * @return The font associated with the given key, or null. + * + * @throws NullPointerException if key is null. + */ + 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 + * TitledBorder.font. + * @param locale the locale. + * + * @return The font associated with the given key, or null. + * + * @throws NullPointerException if key is null. + * + * @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 (null not permitted). + * + * @return The icon associated with the given key, or null. + * + * @throws NullPointerException if key is null. + */ + 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 (null not permitted). + * @param locale the locale. + * + * @return The icon associated with the given key, or null. + * + * @throws NullPointerException if key is null. + * @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 (null not permitted). + * + * @return The insets associated with the given key, or null. + * + * @throws NullPointerException if key is null. + */ + 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 (null not permitted). + * @param locale the locale. + * + * @return The insets associated with the given key, or null. + * + * @throws NullPointerException if key is null. + * @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 (null 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 (null 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 null). + * + * @return The current look and feel. + * + * @see #setLookAndFeel(LookAndFeel) + */ + public static LookAndFeel getLookAndFeel() + { + return currentLookAndFeel; + } + + /** + * Returns the UIDefaults 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 null. + * + * @param key the key (null not permitted). + * + * @return The string associated with the given key, or null. + */ + 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 null. + * + * @param key the key (null not permitted). + * @param locale the locale. + * + * @return The string associated with the given key, or null. + * + * @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 (null 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 past 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: + *
      + *
    1. If the port is smaller than the view, leave the view at its current + * size.
    2. + *
    3. If the view is smaller than the port, the view is top aligned.
    4. + *
    5. If the view tracks the port size, the view position is always zero and + * the size equal to the viewport size
    6. + *
    7. In {@link JViewport#setViewSize(Dimension)}, the view size is never + * set smaller that its minimum size.
    8. + *
    + * + * @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: + *
      + *
    • {@link JFrame#setDefaultCloseOperation(int)};
    • + *
    • {@link JInternalFrame#setDefaultCloseOperation(int)};
    • + *
    • {@link JDialog#setDefaultCloseOperation(int)};
    • + *
    + * + * @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 left, right, + * top and bottom 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 + * left, right, top and + * bottom fields of the passed insets 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 (null not permitted). + * + * @return The border insets (the same object that was passed as the + * insets argument). + * + * @see #getBorderInsets(Component) + * + * @throws NullPointerException if insets is null. + */ + 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 false, or ugly + * artifacts can appear on screen. The default implementation + * provided here always returns false. + * + * @return false. + */ + 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 null, + * 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 (null 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. + * + *

    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 serialver 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 null 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 null 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 null 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 null 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’s background color. + * + *

    [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 bevelType 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. + * + *

    [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 bevelType 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 bevelType is {@link #RAISED}; top + * and left otherwise). The color for the inner side + * is a brightened version of this color. + * + * @throws IllegalArgumentException if bevelType has + * an unsupported value. + * + * @throws NullPointerException if highlight or + * shadow is null. + * + * @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. + * + *

    [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 + * bevelType 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 bevelType 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 bevelType has + * an unsupported value. + * + * @throws NullPointerException if one of the passed colors + * is null. + */ + 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 left, right, + * top and bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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 null + * if that color will be derived from the background of the enclosed + * Component. + * + * @return The color (possibly null). + */ + public Color getHighlightOuterColor() + { + return highlightOuter; + } + + + /** + * Returns the color that will be used for the inner side of + * highlighted edges when painting the border, or null + * if that color will be derived from the background of the enclosed + * Component. + * + * @return The color (possibly null). + */ + public Color getHighlightInnerColor() + { + return highlightInner; + } + + + /** + * Returns the color that will be used for the inner side of + * shadowed edges when painting the border, or null if + * that color will be derived from the background of the enclosed + * Component. + * + * @return The color (possibly null). + */ + public Color getShadowInnerColor() + { + return shadowInner; + } + + + /** + * Returns the color that will be used for the outer side of + * shadowed edges when painting the border, or null if + * that color will be derived from the background of the enclosed + * Component. + * + * @return The color (possibly null). + */ + 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. + * + *

    If the border colors are derived from the background color of + * the enclosed component, the result is true because + * the derivation method always returns opaque colors. Otherwise, + * the result depends on the opacity of the individual colors. + * + * @return true if the border is fully opaque, or + * false 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. + * + *

    +   * ++++++++++++
    +   * +..........#    + = color a
    +   * +.        X#    . = color b
    +   * +.        X#    X = color c
    +   * +.XXXXXXXXX#    # = color d
    +   * ############
    + * + * @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. + * + *

    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 left, right, + * top and bottom 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 true if the border is fully opaque, or + * false 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 serialver 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 + * insideBorder to be null. + */ + protected Border insideBorder; + + /** + * The outside border, which is painted outside both the + * bordered Component and the inside border. It is valid for + * outsideBorder to be null. + */ + protected Border outsideBorder; + + /** + * Constructs a CompoundBorder whose inside and outside borders + * are both null. 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 insideBorder and the enclosed + * component. It is acceptable to pass null, in + * which case no outside border is painted. + * + * @param insideBorder the inside border, which is painted to + * between outsideBorder and the enclosed + * component. It is acceptable to pass null, 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 false, or ugly + * artifacts can appear on screen. + * + * @return true if both the inside and outside borders + * are opaque, or false 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 insets 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 null. + * + * @return The outside border (possibly null). + */ + 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 null. + * + * @return The inside border (possibly null). + */ + 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. + * + *

    [An illustration of EmptyBorder] + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class EmptyBorder extends AbstractBorder +{ + /** + * Determined using the serialver 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 left, right, + * top and bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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 left, right, + * top and bottom 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 false. + * + * @return false. + */ + 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. + * + *

    [An illustration of the two EtchedBorder variants] + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class EtchedBorder extends AbstractBorder +{ + /** + * Determined using the serialver 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 null to indicate that the + * color shall be derived from the background of the enclosed + * component. + */ + protected Color highlight; + + + /** + * The shadow color, or null 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. + * + *

    [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 etchType 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. + * + *

    [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 etchType 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 left, right, + * top and bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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. + * + *

    If the border colors are derived from the background color of + * the enclosed component, the result is true because + * the derivation method always returns opaque colors. Otherwise, + * the result depends on the opacity of the individual colors. + * + * @return true if the border is fully opaque, or + * false 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 null if that color will be + * derived from the background of the enclosed Component. + * + * @return The highlight color (possibly null). + */ + 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 null if that color will be + * derived from the background of the enclosed Component. + * + * @return The shadow color (possibly null). + */ + public Color getShadowColor() + { + return shadow; + } + + + /** + * Paints a two-pixel etching in two colors. + * + *

    +   * +++++++++++.
    +   * +.........+.    + = color a
    +   * +.        +.    . = color b
    +   * +.        +.
    +   * +++++++++++.
    +   * ............
    + * + * @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 serialver 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 + * (true) or not ((false). + */ + 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. + * + *

    [An illustration of two LineBorders] + * + *

    Note that the enlarged view in the right-hand picture shows + * that the implementation draws one more pixel than specified, + * provided that roundedCorders is true + * 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 true for rounded corners, + * false 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 left, right, + * top and bottom 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 left, + * right, top and + * bottom 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 insets. + * + * @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 null). + */ + 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 true if the corners are rounded, + * false if the corners are plain. + */ + public boolean getRoundedCorners() + { + return roundedCorners; + } + + + /** + * Determines whether this border fills every pixel in its area + * when painting. + * + * @return true if the corners are plain and the line + * color is fully opaque; false 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. + * + *

    [Two MatteBorders] + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class MatteBorder extends EmptyBorder +{ + /** + * Determined using the serialver 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 + * null 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 + * null 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. + * + *

    [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. + * + *

    [A picture of a MatteBorder made by this constructor] + * + * @param borderInsets an Insets object whose top, + * left, bottom and right + * 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. + * + *

    [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. + * + *

    [A picture of a MatteBorder made by this constructor] + * + * @param borderInsets an Insets object whose top, + * left, bottom and right + * 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. + * + *

    [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 left, right, + * top and bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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 left, right, + * top and bottom 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 + * null if the border is filled with repetitions of a + * tile icon. + * + * @return The color (possibly null). + */ + public Color getMatteColor() + { + return color; + } + + + /** + * Returns the icon is used for tiling the border, or + * null if the border is filled with a color instead of + * an icon. + * + * @return The icon (possibly null). + */ + public Icon getTileIcon() + { + return tileIcon; + } + + + /** + * Determines whether this border fills every pixel in its area + * when painting. + * + * @return true if the border is filled with an + * opaque color; false 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. + * + *

    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 serialver 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’s background color. + * + *

    [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 bevelType 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. + * + *

    [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 + * bevelType 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 + * bevelType is {@link BevelBorder#RAISED}; top + * and left otherwise). The color for the inner side is a + * brightened version of this color. + * + * @throws IllegalArgumentException if bevelType has an + * unsupported value. + * + * @throws NullPointerException if highlight or + * shadow is null. + * + * @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. + * + *

    [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 + * bevelType 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 + * bevelType 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 bevelType has + * an unsupported value. + * + * @throws NullPointerException if one of the passed colors + * is null. + */ + 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 left, right, + * top and bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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. + * + *

    The enlarged view (see documentation for constructors) shows + * that a SoftBevelBorder does not paint all pixels. Therefore, + * this method always returns false. + * + * @return false. + */ + public boolean isBorderOpaque() + { + return false; + } + + + /** + * Paints a soft bevel in four colors. + * + *

    +   * +++++++++++.
    +   * ++.........#    + = color a
    +   * +..        #    . = color b
    +   * +.         #    X = color c
    +   * ..        X#    # = color d
    +   * . ##########
    + * + * @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 titlePosition 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 titlePosition 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 titlePosition 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 titlePosition 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 titlePosition 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 titlePosition 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 titlePosition 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 titleJustification 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 titleJustification 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 titleJustification 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 titleJustification 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 titleJustification 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 titleJustification 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 serialver tool of Apple/Sun JDK 1.3.1 + * on MacOS X 10.1.5. + */ + static final long serialVersionUID = 8012999415147721601L; + + + /** + * The title, or null to display no title. + */ + protected String title; + + + /** + * The border underneath the title. If this value is + * null, the border will be retrieved from the {@link + * javax.swing.UIManager}’s defaults table using the key + * TitledBorder.border. + */ + 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 + * null, the font will be retrieved from the {@link + * javax.swing.UIManager}’s defaults table using the key + * TitledBorder.font. + */ + protected Font titleFont; + + + /** + * The color for displaying the title text. If this value is + * null, the color will be retrieved from the {@link + * javax.swing.UIManager}’s defaults table using the key + * TitledBorder.titleColor. + */ + protected Color titleColor; + + + /** + * Constructs a TitledBorder given the text of its title. + * + * @param title the title text, or null 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 null + * 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 null + * to use a default from the current look and feel. + * + * @param title the title text, or null 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 null + * to use a default from the current look and feel. + * + * @param title the title text, or null 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 titleJustification + * or titlePosition 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 null + * to use a default from the current look and feel. + * + * @param title the title text, or null 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 null + * to use a default from the current look and feel. + * + * @throws IllegalArgumentException if titleJustification + * or titlePosition 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 null + * to use a default from the current look and feel. + * + * @param title the title text, or null 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 null + * to use a default from the current look and feel. + * + * @param titleColor the color for the title text, or null + * to use a default from the current look and feel. + * + * @throws IllegalArgumentException if titleJustification + * or titlePosition 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 left, right, + * top and bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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 false, indicating that there are pixels inside + * the area of this border where the background shines through. + * + * @return false. + */ + 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 null 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 tonull, the current + * {@link javax.swing.LookAndFeel} will be asked for a border + * using the key TitledBorder.border. + * + * @return a border, or null if the current LookAndFeel + * does not provide a border for the key + * TitledBorder.border. + * + * @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 tonull, the current + * {@link javax.swing.LookAndFeel} will be asked for a font + * using the key TitledBorder.font. + * + * @return a font, or null if the current LookAndFeel + * does not provide a font for the key + * TitledBorder.font. + * + * @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 tonull, the current + * {@link javax.swing.LookAndFeel} will be asked for a color + * using the key TitledBorder.titleColor. + * + * @return a color, or null if the current LookAndFeel + * does not provide a color for the key + * TitledBorder.titleColor. + * + * @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 null 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 null 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 titlePosition. + */ + 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 titleJustification. + */ + 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 null 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 null 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 null. + */ + 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-2.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/BevelBorder-3.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/EmptyBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/EtchedBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/EtchedBorder-2.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/LineBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-2.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-3.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-4.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-5.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/MatteBorder-6.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-2.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/border/doc-files/SoftBevelBorder-3.png 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 @@ + + + + +GNU Classpath - javax.swing.border + + +

    Provides borders for use by components in the javax.swing +package.

    + + + 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 0, 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 -1 to indicate no mnemonic, + * subclasses can override. + * + * @return -1, 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 ChangeListener objects. + * + * @return Array of ChangeListener objects. + */ + public ChangeListener[] getChangeListeners() + { + return (ChangeListener[]) listenerList.getListeners(ChangeListener.class); + } + + /** + * Calls all the stateChanged() method of all added + * ChangeListener objects with changeEvent 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 @@ + + + + +GNU Classpath - javax.swing.colorchooser + + +

    Provides support classes for the {@link javax.swing.JColorChooser} +component.

    + + + 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 ChangeEvent instance for the specified source. + * + * @param source the source for the event (null 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 ChangeListener 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 event identifies the + * source 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. + * + *

    Example for using this class: + * + *

     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.add(BarListener.class, l);
    + *   }
    + *
    + *   public void removeBarListener(BarListener l)
    + *   {
    + *     listeners.remove(BarListener.class, l);
    + *   }
    + *
    + *   protected void fireBarClosedEvent()
    + *   {
    + *     Object[] l = listeners.getListenerList();
    + *
    + *     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);
    + *         }
    + *   }
    + * }
    + * + * @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 i, listenerList[i] indicates + * the registered class, and listenerList[i + 1] 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 + * t, or of a subclass of t. + * + * @throws IllegalArgumentException if listener is not + * an instance of t (or a subclass thereof). + * + * @throws NullPointerException if t is null. + */ + public void add(Class 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 + * t. Thus, subclasses of t 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 c; subclasses are + * not considered equal. + * + *

    The returned array can always be cast to c[]. + * 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 c does not implement + * the {@link EventListener} interface. + * + * @throws NullPointerException if c is + * null. + * + * @return an array of c 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[] getListeners(Class 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 t, or of a subclass of t. + * + * @throws IllegalArgumentException if listener is not + * an instance of t (or a subclass thereof). + * + * @throws NullPointerException if t is null. + */ + public void remove(Class 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 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) 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 String 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 HyperlinkEvent 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 HyperlinkEvent 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 HyperlinkEvent 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 JInternalFrameEvent instance. + * + * @param source the source of this event (null not permitted). + * @param id the event ID of this event (see the constants defined by this + * class). + * + * @throws IllegalArgumentException if source is + * null. + */ + public InternalFrameEvent(JInternalFrame source, int id) + { + super(source, id); + } + + /** + * Returns the JInternalFrame 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 ListDataEvent object. + * + * @param source the source of the event (null 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 ListDataListener 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 event 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 event 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 ListSelectionEvent. + * + * @param source the event source (null 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 source is + * null. + */ + 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 (null not permitted). + * + * @throws NullPointerException if source is null. + */ + 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 TableColumnModelListener 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 TableModelEvent indicating an {@link #UPDATE} + * to the data in all columns and rows. + * + * @param source the source object (null not permitted). + * + * @throws IllegalArgumentException if source is + * null. + */ + public TableModelEvent(TableModel source) + { + this(source, 0, Integer.MAX_VALUE, ALL_COLUMNS, UPDATE); + } + + /** + * Creates a new TableModelEvent indicating an {@link #UPDATE} + * to the data in a single row across all columns. + * + * @param source the source object (null not permitted). + * @param row the updated row. + * + * @throws IllegalArgumentException if source is + * null. + */ + public TableModelEvent(TableModel source, int row) + { + this(source, row, row, ALL_COLUMNS, UPDATE); + } + + /** + * Creates a new TableModelEvent indicating an {@link #UPDATE} + * to the data in the specified rows across all columns. + * + * @param source the source object (null not permitted). + * @param firstRow the first row of update. + * @param lastRow the last row of update. + * + * @throws IllegalArgumentException if source is + * null. + */ + public TableModelEvent(TableModel source, int firstRow, int lastRow) + { + this(source, firstRow, lastRow, ALL_COLUMNS, UPDATE); + } + + /** + * Creates a new TableModelEvent indicating an {@link #UPDATE} + * to the data in the specified rows and column. Use {@link #ALL_COLUMNS} + * for the column argument to indicate all columns. + * + * @param source the source object (null not permitted). + * @param firstRow the first row of update. + * @param lastRow the last row of update. + * @param column the affected column. + * + * @throws IllegalArgumentException if source is + * null. + */ + public TableModelEvent(TableModel source, int firstRow, int lastRow, + int column) + { + this(source, firstRow, lastRow, column, UPDATE); + } + + /** + * Creates a new TableModelEvent indicating an operation of + * the specified type 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 (null 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 source is + * null. + */ + 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 TableModelListener 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 (true) or removed + * (false) from the selection. + */ + protected boolean[] areNew; + + /** + * The old lead selection path (may be null). + */ + protected TreePath oldLeadSelectionPath; + + /** + * The new lead selection path (may be null). + */ + protected TreePath newLeadSelectionPath; + + /** + * Creates a new TreeSelectionEvent. + * + * @param source the source (usually a {@link TreeSelectionModel}, + * null 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 true indicates the + * corresponding path has been added to the selection and + * false indicates the path has been removed. + * @param oldLeadSelectionPath the old lead selection path (null + * permitted). + * @param newLeadSelectionPath the new lead selection path (null + * permitted). + * + * @throws IllegalArgumentException if source is + * null. + */ + 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 TreeSelectionEvent. + * + * @param source the event source (usually a {@link TreeSelectionModel}, + * null not permitted). + * @param path the path. + * @param isNew true indicates that path has been + * added to the selection, and false indicates that it has + * been removed. + * @param oldLeadSelectionPath the old lead selection path (null + * permitted). + * @param newLeadSelectionPath the new lead selection path (null + * permitted). + * + * @throws IllegalArgumentException if source is + * null. + */ + 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 true if the path returned by {@link #getPath()} has + * been added to the selection, and false if it has been + * removed. + * + * @return A boolean. + * + * @see #isAddedPath(int) + */ + public boolean isAddedPath() + { + return areNew[0]; + } + + /** + * Returns true if path has been added to the + * selection, and false 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 path 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 true if the path at the specified index has been + * added to the selection, and false 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 null). + * + * @see #getNewLeadSelectionPath() + */ + public TreePath getOldLeadSelectionPath() + { + return oldLeadSelectionPath; + } + + /** + * Returns the new lead selection path. + * + * @return The new lead selection path (possibly null). + * + * @see #getOldLeadSelectionPath() + */ + public TreePath getNewLeadSelectionPath() + { + return newLeadSelectionPath; + } + + /** + * Creates a shallow copy of this TreeSelectionEvent, replacing + * the source with source. + * + * @param source the new event source (null not permitted). + * + * @return A cloned event with another event source. + * + * @throws IllegalArgumentException if source is + * null. + */ + 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 @@ + + + + +GNU Classpath - javax.swing.event + + +

    Provides events and listeners used by components in the +javax.swing package.

    + + + 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 true if the specified file matches the filter, and + * false 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 (null 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 + * (null 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 + * useFileHiding controls whether or not hidden files are + * included in the result. + * + * @param dir the directory (if null + * @param useFileHiding a flag that controls whether or not hidden files are + * included in the result (pass in true to + * exclude hidden files). + * + * @return The files in the given directory (possibly null). + */ + 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 null 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. + *

    + * This method is implemented to return null, 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 null, + * subclasses must override. + * + * @param f the file. + * + * @return null. + */ + 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 null, + * subclasses must override. + * + * @param f the file. + * + * @return null. + */ + public String getSystemTypeDescription(File f) + { + return null; + } + + /** + * DOCUMENT ME! + * + * @param dir DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public boolean isComputerNode(File dir) + { + return false; + } + + /** + * Returns true if the given directory represents a disk + * drive, and false otherwise. This default implementation + * always returns false. + * + * @param dir the directory. + * + * @return false. + */ + public boolean isDrive(File dir) + { + return false; + } + + /** + * Returns true if f is a file or directory, and + * false otherwise. + * + * @param f the file/directory. + * + * @return true if f is a file or directory, and + * false otherwise. + */ + public boolean isFileSystem(File f) + { + return (f.isFile() || f.isDirectory()); + } + + /** + * Returns true if the given directory is a file system root, + * and false otherwise. + * + * @param dir the directory. + * + * @return true if the given directory is a file system root, + * and false 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 true if the given directory represents a floppy + * drive, and false otherwise. This default implementation + * always returns false. + * + * @param dir the directory. + * + * @return false. + */ + public boolean isFloppyDrive(File dir) + { + return false; + } + + /** + * Returns true if the given file is hidden, and + * false otherwise. + * + * @param f the file. + * + * @return true if the given file is hidden, and + * false otherwise. + */ + public boolean isHiddenFile(File f) + { + return f.isHidden(); + } + + /** + * Returns true if folder is the parent of + * file, and false otherwise. + * + * @param folder the folder (null not permitted). + * @param file the file (null not permitted). + * + * @return true if folder is the parent of + * file, and false 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 true if the file is traversable, and + * false otherwise. Here, all directories are considered + * traversable, and files are considered non-traversable. + * + * @param f the file or directory (null not permitted). + * + * @return true if the file is traversable, and + * false 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 FileView instance. + */ + public FileView() + { + // Nothing to do here. + } + + /** + * Returns the name for the specified file. This method always returns + * null and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always null. + */ + public String getName(File file) + { + return null; + } + + /** + * Returns a description for the specified file. This method always returns + * null and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always null. + */ + public String getDescription(File file) + { + return null; + } + + /** + * Returns a description for the type of the specified file. This method + * always returns null and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always null. + */ + public String getTypeDescription(File file) + { + return null; + } + + /** + * Returns an {@link Icon} to represent the specified file. This method + * always returns null and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always null. + */ + 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 + * null and should be overridden by subclasses. + * + * @param directory the directory. + * + * @return Always null. + */ + 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 NewFolder or, if a directory or file + * with that name already exists, NewFolder.n where + * n is the lowest integer greater than zero that results in + * a unique directory name. + * + * @param containingDir the directory to contain the new folder + * (null 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 null. + */ + 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 null. + */ + 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 @@ + + + + +GNU Classpath - javax.swing.filechooser + + +

    Provides support classes for the {@link javax.swing.JFileChooser} +component.

    + + + 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 @@ + + + + +GNU Classpath - javax.swing + + +

    Provides a collection of cross-platform user interface +components.

    + + + 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 ActionMap 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 + * UIResource, such as the borders provided by this + * class. + * + * @serial + * @serialField delegate Border the Border wrapped + * + * @author Brian Jones (cbj@gnu.org) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class BorderUIResource implements Border, UIResource, Serializable +{ + /** + * Verified using the serialver 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 + * null if the {@link #getEtchedBorderUIResource()} + * method has not yet been called. + */ + private static Border etchedBorderUIResource; + + + /** + * A shared instance of a {@link BevelBorderUIResource} whose + * bevelType is {@link + * javax.swing.border.BevelBorder#LOWERED}, or null if + * the {@link #getLoweredBevelBorderUIResource()} has not yet been + * called. + */ + private static Border loweredBevelBorderUIResource; + + + /** + * A shared instance of a {@link BevelBorderUIResource} whose + * bevelType is {@link + * javax.swing.border.BevelBorder#RAISED}, or null 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 null 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 + * bevelType 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 + * bevelType 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 BorderUIResource for wrapping + * a Border 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 paintBorder 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 + * getBorderInsets method of the wrapped + * delegate. + * + * @param c the component whose border is to be measured. + * + * @return an Insets object whose left, right, + * top and bottom 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 isBorderOpaque + * method of the wrapped delegate. + * + * @return true if the border is fully opaque, or + * false 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 UIResource, + * 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’s background color. + * + *

    [An illustration showing raised and lowered BevelBorders]

    + * + * @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 bevelType 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. + * + *

    [An illustration showing BevelBorders that were
+     * constructed with this method]

    + * + * @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 + * bevelType 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 + * bevelType 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 bevelType has + * an unsupported value. + * + * @throws NullPointerException if highlight or + * shadow is null. + */ + public BevelBorderUIResource(int bevelType, + Color highlight, + Color shadow) + { + super(bevelType, highlight, shadow); + } + + + /** + * Constructs a BevelBorderUIResource given its appearance type + * and all its colors. + * + *

    [An illustration showing BevelBorders that
+     * were constructed with this method]

    + * + * @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 + * bevelType 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 + * bevelType 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 bevelType has + * an unsupported value. + * + * @throws NullPointerException if one of the passed colors + * is null. + */ + 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 UIResource, + * 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 insideBorder and the enclosed + * component. It is acceptable to pass null, in + * which case no outside border is painted. + * + * @param insideBorder the inside border, which is painted to + * between outsideBorder and the enclosed + * component. It is acceptable to pass null, 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 UIResource, + * such as the borders provided by this class. + * + *

    [An illustration of EmptyBorder]

    + * + * @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 UIResource, + * such as the borders provided by this class. + * + *

    [An illustration of the two EtchedBorder
+   * variants]

    + * + * @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. + * + *

    [An illustration of the two
+     * EtchedBorder variants]

    + * + * @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 etchType 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. + * + *

    [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 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 etchType 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 UIResource, + * such as the borders provided by this class. + * + *

    [An illustration of two LineBorders]

    + * + * @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 UIResource, + * such as the borders provided by this class. + * + *

    [An illustration of two MatteBorders]

    + * + * @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. + * + *

    [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 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. + * + *

    [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 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. + * + *

    [A picture of a MatteBorder made by this
+     * constructor]

    + * + * @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 UIResource, + * 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 null 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 + * null 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 + * null to use a default from + * the current look and feel. + * + * @param title the title text, or null + * 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 + * null to use a default + * from the current look and feel. + * + * @param title the title text, or null + * 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 titleJustification + * or titlePosition 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 + * null to use a default + * from the current look and feel. + * + * @param title the title text, or null + * 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 null + * to use a default from the current look and feel. + * + * @throws IllegalArgumentException if titleJustification + * or titlePosition 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 + * null to use a default + * from the current look and feel. + * + * @param title the title text, or null + * 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 null + * to use a default from the current look and feel. + * + * @param titleColor the color for the title text, or null + * to use a default from the current look and feel. + * + * @throws IllegalArgumentException if titleJustification + * or titlePosition 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 JButton. + * + * @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 JColorChooser. + * + * @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 UIResource, 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 ColorUIResource 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 ColorUIResource 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 ColorUIResource 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 ColorUIResource, using the intensities + * of another color. + * + * @param c the color whose intensities will be considered when + * constructing this ColorUIResource (null + * not permitted). + * + * @throws NullPointerException if c is null. + */ + 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 ComboBoxUI. + */ + public ComboBoxUI() + { + // Nothing to do here. + } + + /** + * Sets the visibility of the popup button. + * + * @param c the JComboBox whose popup + * is shown or hidden. + * + * @param visible true to show the popup, false + * to hide it. + */ + public abstract void setPopupVisible(JComboBox c, boolean visible); + + /** + * Determines whether the popup button is currently visible. + * + * @param c the JComboBox whose popup visibility + * is retrieved. + * + * @return true if the popup button is currently + * visible, false otherwise. + */ + public abstract boolean isPopupVisible(JComboBox c); + + /** + * Determines whether the combo box can receive input focus. + * + * @param c JComboBox whose focus traversability + * is to be retrieved. + * + * @return true if c can receive + * input focus, false 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 ComponentInputMap 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 ComponentInputMapUIResource. + * + * @param component the JComponent associated with + * this InputMap. + */ + 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. + * + *

    [UML diagram illustrating the architecture for pluggable
+ * look and feels]

    + * + *

    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 JSlider, the + * user interface would be provided by some concrete subclass of + * {@link javax.swing.plaf.SliderUI}. + * + *

    Soon after its creation, a ComponentUI will be sent + * an {@link #installUI} message. The ComponentUI will + * react by setting properties such as the border or the background + * color of the JComponent for which it provides its + * services. Soon before the end of its lifecycle, the + * ComponentUI will receive an {@link #uninstallUI} + * message, at which time the ComponentUI is expected to + * undo any changes.

    + * + *

    Note that the ui of a JComponent + * changes whenever the user switches between look and feels. For + * example, the ui property of a JSlider + * could change from an instance of MetalSliderUI to an + * instance of FooSliderUI. This switch can happen at any + * time, but it will always be performed from inside the Swing thread.

    + * + * @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 ComponentUI delegate is created. + * The delegate object then receives an installUI + * message. + * + *

    This method should perform the following tasks:

    + * + *
      + *
    • 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}.
    • + *
    • If necessary, install a {@link java.awt.LayoutManager}.
    • + *
    • Embed custom sub-components. For instance, the UI delegate + * for a {@link javax.swing.JSplitPane} might install a special + * component for the divider.
    • + *
    • Register event listeners.
    • + *
    • Set up properties related to keyborad navigation, such as + * mnemonics or focus traversal policies.
    • + *
    + * + * @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 opaque property is false) + * before calling {@link #paint}. + * + *

    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 null, which means that + * c’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 null to indicate that + * c’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 null to indicate that + * c’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 null to indicate that + * c’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’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’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’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 createUI method of a suitable + * subclass. The implementation of ComponentUI + * 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 c. + * + * @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 c. + * + * @param i the index of the accessible child, starting at zero. + * + * @param c the component whose i-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 DesktopIconUI. + */ + 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 JDesktopPane. + * + * @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 DesktopPaneUI. + */ + 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 UIResource, 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 JFileChooser. + * + * @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 FileChooserUI. + */ + public FileChooserUI() + { + // Nothing to do here. + } + + + /** + * Returns a FileFilter that accepts every file. While + * the filtering itself is not specific to any look and feel, the + * text returned by FileFilter.getDescription() need + * not be the same across all look and feels. + * + * @param chooser the JFileChooser for which + * a FileFilter 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 JFileChooser for which + * a FileFilter 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 JFileChooser 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 + * JFileChooser according to the design guidelines of + * the implemented look and feel. + * + * @param chooser the JFileChooser whose + * dialog title is requested. + * + * @see javax.swing.JFileChooser#getDialogTitle + */ + public abstract String getDialogTitle(JFileChooser chooser); + + + /** + * Refreshes the currently displayed directory. + * + * @param chooser the JFileChooser whose + * dialog title needs re-scanning. + */ + public abstract void rescanCurrentDirectory(JFileChooser chooser); + + + /** + * Ensures that a specified file is visible in the + * JFileChooser + * + * @param chooser the JFileChooser that + * should display the file file. + * + * @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 UIResource, 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 FontUIResource given + * the name, style and size of the font. + * + * @param name the name of the font. A number of + * “logical” names are supported by any Java + * implementation. These are + * “Dialog”, + * “DialogInput”, + * “Monospaced”, + * “Serif”, and + * “SansSerif”. + * + * @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 FontUIResource 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 UIResource, 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 serialver 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 IconUIResource (never + * null). + */ + private Icon delegate; + + + /** + * Constructs a IconUIResource that wraps another + * icon. All messages are forwarded to the delegate icon. + * + * @param delegate the icon that is wrapped by this + * IconUIResource (null 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 InputMap that is marked as UIResource, + * 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 InputMapUIResource. + */ + 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 Insets that is marked as UIResource, + * 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 serialver tool + * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5. + */ + static final long serialVersionUID = 5622110143266315421L; + + + /** + * Constructs a new InsetsUIResource 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 JInternalFrame. + * + * @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 InternalFrameUI. + */ + 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 JLabel. + * + * @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 LabelUI. + */ + 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 JList. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class ListUI extends ComponentUI +{ + /** + * Constructs a new ListUI. + */ + 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 JList for which this delegate object + * provides the pluggable user interface. + * + * @param location a point in the JList 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 JList 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 + * JList coordinate system, or null + * if cell 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 JList 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 null if either + * index1 or index2 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 JMenuBar. + * + * @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 MenuBarUI. + */ + 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 JMenuItem. + * + * @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 MenuItemUI. + */ + 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 JOptionPane. + * + * @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 JOptionPane 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 JOptionPane for which this delegate + * object provides the pluggable user interface. + * + * @return true if the user has supplied any custom + * components; false 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 JPanel. + * + * @see javax.swing.JPanel + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class PanelUI extends ComponentUI +{ + /** + * Constructs a new PanelUI. + */ + 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 JPopupMenu. + * + * @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 PopupMenuUI. + */ + public PopupMenuUI() + { + // Nothing to do here. + } + + + /** + * Tests whether or not a mouse event triggers a popup menu. + * + *

    The default implementation calls + * event.isPopupTrigger(), 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 true if the event triggers a popup menu; + * false otherwise. + * + * @since 1.3 + */ + public boolean isPopupTrigger(MouseEvent event) + { + return event.isPopupTrigger(); + } + + + /** + * Creates a Popup for displaying the popup menu. The + * default implementation uses the {@link javax.swing.PopupFactory} + * for retrieving a suitable Popup, but subclasses + * might want to override this method if a LookAndFeel needs special + * Popups. + * + * @param popup the JPopupMenu for whose display + * a Popup 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 Popup 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 JProgressBar. + * + * @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 ProgressBarUI. + */ + 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 JRootPane. + * + * @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 RootPaneUI. + */ + 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 JScrollBar. + * + * @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 ScrollBarUI. + */ + 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 JScrollPane. + * + * @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 ScrollPaneUI. + */ + 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 JSeparator. + * + * @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 SeparatorUI. + */ + 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 JSlider. + * + * @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 SliderUI. + */ + 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 JSpinner. + * + * @since 1.4 + * @see javax.swing.JSpinner + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class SpinnerUI extends ComponentUI +{ + /** + * Constructs a new SpinnerUI. + */ + 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 JSplitPane. + * + * @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 SplitPaneUI. + */ + public SplitPaneUI() + { + // Nothing to do here. + } + + + /** + * Moves the divider to the location which best respects + * the preferred sizes of the children. + * + * @param pane the JSplitPane 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 JSplitPane 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 JSplitPane 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 JSplitPane 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 JSplitPane 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 JSplitPane after it has finished + * painting its children. + * + * @param pane the JSplitPane 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 JTabbedPane. + * + * @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 TabbedPaneUI. + */ + public TabbedPaneUI() + { + // Nothing to do here. + } + + + /** + * Determines which tab lies at a given position. + * + * @param pane the JTabbedPane for which this + * delegate object provides the user interface. + * + * @param x the horizontal position, where zero is the left + * edge of pane. + * + * @param y the vertical position, where zero is the top + * edge of pane. + * + * @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 JTabbedPane for which this + * delegate object provides the user interface. + * + * @param index the index of the tab, which must be an integer + * in the range [0 .. pane.getTabCount() - 1]. + * + * @return the bounding box of the index-th tab, + * in the coordinate system of pane. + */ + public abstract Rectangle getTabBounds(JTabbedPane pane, int index); + + + /** + * Determines how many runs are used to display tabs. + * + * @param pane the JTabbedPane 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 JTableHeader. + * + * @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 TableHeaderUI. + */ + 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 JTable. + * + * @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 TableUI. + */ + 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 TextUI. + */ + public TextUI() + { + // Nothing to do here. + } + + + /** + * Calculates the geometric extent of the character at the + * given offset. + * + * @param tc the JTextComponent 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 + * pos, in view coordinates. + * + * @throws BadLocationException if pos 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 JTextComponent 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 pos. 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 + * pos, in view coordinates. + * + * @throws BadLocationException if pos 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 JTextComponent for which this + * delegate object provides the user interface. + * + * @param pt the position in view coordinates. + * + * @return the caret position which is closest to loc. + * + * @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 JTextComponent 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, outBias[0] will indicate + * whether loc is in the glyph before + * (Position.Bias.Backward) or after + * (Position.Bias.Forward) the returned + * caret position. + * + * @return the caret position which is closest to loc. + */ + 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 JTextComponent 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 pos. 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 pos does not + * designate a valid position in the document model. + * + * @throws IllegalArgumentException if direction + * is not one of Position.Bias.Forward + * or Position.Bias.Backward. + */ + 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 JTextComponent 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. + * end must be greater than or equal to + * start. + */ + 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 JTextComponent 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. + * end must be greater than or equal to + * start. + */ + public abstract void damageRange(JTextComponent tc, + int start, int end, + Position.Bias startBias, + Position.Bias endBias); + + + /** + * Retrieves the EditorKit managing policies and + * persistent state. + * + * @param tc the JTextComponent for which this + * delegate object provides the user interface. + * + * @return the EditorKit used by tc. + */ + public abstract EditorKit getEditorKit(JTextComponent tc); + + + /** + * Retrieves the root of the view tree that visually presents + * the text. + * + * @param tc the JTextComponent for which this + * delegate object provides the user interface. + * + * @return the root View used by tc. + */ + public abstract View getRootView(JTextComponent tc); + + + /** + * Returns a String for presenting a tool tip at the specified + * location. + * + * @param tc the JTextComponent 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 null 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 JToolBar. + * + * @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 ToolBarUI. + */ + 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 JToolTip. + * + * @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 ToolTipUI. + */ + 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 JTree. + * + * @see javax.swing.JTree + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public abstract class TreeUI extends ComponentUI +{ + /** + * Constructs a new TreeUI. + */ + public TreeUI() + { + // Nothing to do here. + } + + + /** + * Determines the geometric extent of the label that is + * drawn for a path. + * + * @param tree the JTree 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 null + * if path contains invalid nodes. + */ + public abstract Rectangle getPathBounds(JTree tree, TreePath path); + + + /** + * Creates a TreePath for the specified row. + * + * @param tree the JTree for which this delegate + * object provides the user interface. + * + * @param row the index of the row, which should be a number + * in the range [0, getRowCount(tree) - 1]. + * + * @return a TreePath for the specified row, or + * null if row is outside + * the valid range. + */ + public abstract TreePath getPathForRow(JTree tree, int row); + + + /** + * Determines in which row a TreePath is currently + * being displayed. + * + * @param tree the JTree 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 [0, getRowCount(tree) + * - 1] if the path is currently on display; + * -1 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 JTree 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. + * + *

    [A screen shot of a JTree] + * + *

    As shown by the above illustration, the bounds of the + * closest path do not necessarily need to contain the passed + * location. + * + * @param tree the JTree for which this delegate + * object provides the user interface. + * + * @param x the horizontal location, relative to the origin + * of tree. + * + * @param y the vertical location, relative to the origin + * of tree. + * + * @return the closest path, or null 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 JTree 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’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 JTree for which this delegate + * object provides the user interface. + * + * @return false if the editing still goes on because + * the cell editor has objected to stopping the session; + * true 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 JTree 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 JTree 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 null + * 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 ComponentUI.uninstallUI() the renderer + * property is set to null. + *
    + * A comparison against null can be used with all properties except for + * the java.awt.Component properties font, foreground, and + * background. The container can provide the value of the properties if + * they are initialized or set to null. + * + * @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 JViewport. + * + * @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 ViewportUI. + */ + 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 BasicArrowButton object with an arrow pointing + * in the specified direction. If the direction 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 false. + * + * @return false. + */ + 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 true 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 true 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 true 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 true 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 true 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. + * + *

    The colors of the border are retrieved from the + * UIDefaults of the currently active look and feel + * using the keys “Button.shadow”, + * “Button.darkShadow”, + * “Button.light”, and + * “Button.highlight”. + * + *

    [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. + * + *

    The colors of the border are retrieved from the + * UIDefaults of the currently active look and feel + * using the keys “RadioButton.shadow”, + * “RadioButton.darkShadow”, + * “RadioButton.light”, and + * “RadioButton.highlight”. + * + *

    [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. + * + *

    The colors of the border are retrieved from the + * UIDefaults of the currently active look and feel + * using the keys “ToggleButton.shadow”, + * “ToggleButton.darkShadow”, + * “ToggleButton.light”, and + * “ToggleButton.highlight”. + * + *

    [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. + * + *

    The colors of the border are retrieved from the + * UIDefaults of the currently active look and feel + * using the keys “MenuBar.shadow” and + * “MenuBar.highlight”. + * + *

    [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. + * + *

    The colors of the border are retrieved from the + * UIDefaults of the currently active look and feel + * using the keys “SplitPane.darkShadow” and + * “SplitPane.highlight”. + * + *

    [A screen shot for JSplitPane.HORIZONTAL_SPLIT] + * + *

    [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. + * + *

    The colors of the edges that are adjacent to the child components + * of the JSplitPane are retrieved from the + * UIDefaults of the currently active look and feel + * using the keys “SplitPane.darkShadow” and + * “SplitPane.highlight”. The color of the + * other two edges is the background color of the divider. + * + *

    + + * + * @return an instance of SplitPaneDividerBorder, 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. + * + *

    The colors of the border are retrieved from the + * UIDefaults of the currently active look and feel + * using the keys “TextField.shadow”, + * “TextField.darkShadow”, + * “TextField.light”, and + * “TextField.highlight”. + * + *

    [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 + * LineBorderUIResource. This is so ugly that look and + * feels better use different borders for their progress bars, or + * they will look really terrible. + * + *

    [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. + * + *

    [A screen shot of a border returned by this method] + * + *

    The colors of the border are retrieved from the + * UIDefaults of the currently active look and feel + * using the keys “InternalFrame.borderShadow”, + * “InternalFrame.borderDarkShadow”, + * “InternalFrame.borderLight”, + * “InternalFrame.borderHighlight”, and + * (for the inner one-pixel thick line) + * “InternalFrame.borderColor”. + */ + 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. + * + *

    [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 serialver 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. + * + *

    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 left, + * right, top and + * bottom 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. + * + *

    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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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. + * + *

    [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 serialver 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 c is an instance of {@link + * javax.swing.text.JTextComponent}, its margin is + * added to the border size. + * + * @return an Insets object whose left, + * right, top and + * bottom 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 c 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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 getMargin() method of the enclosed + * component. If the enclosed component has no such method, + * this border will not occupy any space. + * + *

    [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 serialver 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 left, right, + * top and bottom 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 + * getMargin() method of the enclosed component. The + * resulting margin will be stored into the the left, + * right, top and bottom + * fields of the passed insets parameter. + * + *

    Unfortunately, getMargin() 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 c is an + * instance of a known class, the respective + * getMargin() 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 insets, + * 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. + * + *

    [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 serialver 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. + * + *

    [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 left, + * right, top and + * bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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. + * + *

    [A screen shot of this border] + * + *

    Note about the screen shot: Normally, the + * borderPainted property is false for + * JRadioButtons. For this screen shot, it has been set to + * true so the borders get drawn. Also, a + * concretization of the Basic look and would typically provide + * icons for the various states of radio buttons. + * + *

    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 serialver 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. + * + *

    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 left, + * right, top and + * bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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 serialver 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 c + * is not an {@link javax.swing.AbstractButton} whose model + * returns true 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()}. + * + *

    [A screen shot for JSplitPane.HORIZONTAL_SPLIT] + * + *

    [A screen shot for JSplitPane.VERTICAL_SPLIT] + * + *

    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. + * + *

    In the Sun JDK, the bottom edge of the divider also gets + * painted if the orientation of the enclosed JSplitPane is + * JSplitPane.VERTICAL_SPLIT (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). + * + *

    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, SplitPaneDividerBorder. + * + * @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 JSplitPane. + * + *

    [A screen shot for JSplitPane.HORIZONTAL_SPLIT] + * + *

    [A screen shot for JSplitPane.VERTICAL_SPLIT] + * + * @param c the JSplitPane 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 JSplitPane, + * omitting some of the edges. + * + * @param g the graphics for painting. + * + * @param suppress a bit mask indicating the set of suppressed + * edges, for example SUPPRESS_TOP | SUPPRESS_RIGHT. + * + * @param x the x coordinate of the SplitPaneBorder. + * + * @param y the y coordinate of the SplitPaneBorder. + * + * @param shadeBottomLeftPixel true to paint the + * bottom left pixel in the shadow color, + * false 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 left, + * right, top and + * bottom 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 false 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. + * + *

    [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, SplitPaneBorder. + */ + public SplitPaneDividerBorder() + { + // Nothing to do here. + } + + /** + * Paints the border around the divider of a JSplitPane. + * + *

    [A picture that shows which pixels
+     * get painted in what color] + * + * @param c the JSplitPane whose divider’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 left, + * right, top and + * bottom 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 true + */ + public boolean isBorderOpaque() + { + return true; + } + + + /** + * Determines the JSplitPane whose divider is being painted. + * + * @param c an instance of BasicSplitPaneDivider. + * + * @return a JSplitPane, or null if + * c 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. + * + *

    [A screen shot of this border] + * + *

    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 serialver 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. + * + *

    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 left, + * right, top and + * bottom 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 left, + * right, top and + * bottom fields indicate the width of the + * border at the respective edge. + * + * @return the same object that was passed for insets. + * + * @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 true when enabled, false 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 contentAreaFilled 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 true, 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 + * true, if the button's "rolloverEnabled" property is + * true. 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 false, if it's "rolloverEnabled" property is + * true. 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 'Button' for this class. + * + * @return the prefix for the UI defaults property + */ + protected String getPropertyPrefix() + { + return "Button."; + } + + /** + * Installs the default settings. + * + * @param b the button (null 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 (null 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 (null 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 (null 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 true. + * + * @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 BasicCheckBoxUI. + */ + public static ComponentUI createUI(JComponent c) + { + return new BasicCheckBoxUI(); + } + + /** + * Returns the prefix for entries in the {@link UIManager} defaults table + * ("CheckBox." 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 BasicComboBoxEditor 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 BasicComboBoxEditor.UIResource 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 BasicComboBoxRenderer 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 + * value. + * + * @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 UIResource 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 BasicComboBoxUI 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 null. + * + * @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 true if the popup is visible, and false + * otherwise. + * + * @param c The JComboBox to check + * + * @return true if popup part of the JComboBox is visible and + * false 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 (Dimension(32767, 32767)). + */ + 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 true 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 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 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 true if a > b, false 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 not 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 true 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 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 BasicEditorPaneUI for the text + * component comp. + * + * @param comp the component for which to create an UI + * + * @return the UI for comp + */ + public static ComponentUI createUI(JComponent comp) + { + return new BasicEditorPaneUI(); + } + + /** + * Creates a new BasicEditorPaneUI + */ + public BasicEditorPaneUI() + { + // Do nothing here. + } + + /** + * Returns the property prefix to be used by this UI class. This is + * EditorPane in this case. + * + * @return EditorPane + */ + 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 true always, as all files are accepted by this + * filter. + * + * @param f the file. + * + * @return Always true. + */ + 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 iconCache = new Hashtable(); + + /** + * 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 null). + */ + 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 CancelSelectionAction 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 ChangeToParentDirectoryAction 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 GoHomeAction 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 NewFolderAction 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 SelectionListener 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 BasicFileChooserUI 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 true if a directory is selected, and + * false 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 (null 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 (null 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 (null 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. + * + *

    [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 Insets object whose top, + * left, bottom and + * right 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. + * + *

    [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 Insets object whose top, + * left, bottom and + * right 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. + * + *

    [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 true to draw the button border + * with a pressed-in appearance; false for + * normal (unpressed) appearance. + * + * @param isDefault true to draw the border with + * the appearance it has when hitting the enter key in a + * dialog will simulate a click to this button; + * false 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. + * + *

    [An illustration that shows which pixels
+   * get painted in what color] + * + *

    Compatibility with the Sun reference + * implementation: The Sun reference implementation seems + * to ignore the x and y 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 “bug ID” 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 text, the text will be + * drawn without underlining. Drawing is performed in the current + * color and font of g. + * + *

    [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 + * text will be underlined. It is not clear + * why the API specification declares this argument to be + * of type int instead of char. + * 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 underlinedChar is outside + * the range of char. + * + * @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 g. + * + *

    [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 + * text. If underlinedIndex falls + * outside the range [0, text.length() - 1], 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 g. + * + *

    [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 + * text. If underlinedIndex falls + * outside the range [0, text.length() - 1], 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 g. + * Any other pixels are left unchanged. + * + *

    [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’s text and icon. + * + * @param b the button whose preferred size is determined. + * + * @param textIconGap the gap between the button’s text and + * icon. + * + * @return a Dimension object whose width + * and height 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 true if the width preference has changed + * @param height true 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 1 since the RootView always contains one + * child, that is the real root of the View hierarchy. + * + * @return 1 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 Container that contains this view. This + * normally will be the text component that is managed by this TextUI. + * + * @return the Container that contains this view + */ + public Container getContainer() + { + return component; + } + + /** + * Returns the preferred span along the specified axis. + * 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 Graphics 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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 View'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 View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + */ + 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 pos in the given direction d. + * + * @param pos the document position + * @param b the bias for pos + * @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 pos in the given direction + * d + * + * @throws BadLocationException if pos 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 + * c to render the HTML string html. + * + * @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 true if s is HTML, false + * otherwise. + * + * @param s the string to test + * + * @return true if s is HTML, false + * 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 c's client property if + * text 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&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&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 TitlePaneLayout 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 -1 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 + * fixedCellHeight property of the list is not set. Otherwise + * this field is null 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 -1. + * + * @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 (0,0). + * + * @param l Ignored; calculates over this.list + * @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 + * index1 and index2 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 0 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 -1 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 0 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 -1 + * 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). -1 + * 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 null 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 (0,0) 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 -1 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 defaults table with mappings between class IDs + * and fully qualified class names for the UI delegates. + * + * @param defaults the defaults table (null 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 defaults 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 (null 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 defaults table with the system colors. If + * useNative is true, the table is populated + * with the constants in {@link SystemColor}, otherwise the + * systemColors 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 "#C0C0C0", which is decoded using + * {@link Color#decode(String)}. + * + * @param defaults the defaults table (null not permitted). + * @param systemColors defaults to use when useNative is + * false + * @param useNative when true, installs the values of the + * SystemColor constants, when false, install the values + * from systemColors + */ + 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 defaults 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 defaults table with UI default values for + * colors, fonts, keybindings and much more. + * + * @param defaults the defaults table (null 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 ActionMap 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 Action 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 + * AuditoryCues.playList. + * + * @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 i 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}, null 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 (null 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 (null not permitted). + */ + public void uninstallUI(JComponent c) + { + uninstallDefaults((JPanel) c); + } + + /** + * Uninstalls the UI defaults for the specified panel. + * + * @param p the panel (null 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 true: navigate to next, false: + * 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 true: navigate to child, + * false: 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 BasicRadioButtonUI. + * + * @return a new instance of BasicRadioButtonUI + */ + public static ComponentUI createUI(final JComponent c) + { + return new BasicRadioButtonUI(); + } + + /** + * Creates a new instance of BasicButtonUI. + */ + public BasicRadioButtonUI() + { + // nothing to do + } + + /** + * Installs defaults from the Look & 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 + * RadioButton 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 JRadioButton 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 scrollBar by one block (according + * to the scrollable protocol) in the specified direction. + * + * 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 scrollbac/code> by units units + * in the specified direction. + * + * 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 + * ScrollBarUI. + * + * @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 columnHeader 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 rowHeader 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 scrollBarDisplayPolicy + * 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 viewport 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; + +/** + *

    + * BasicSliderUI.java This is the UI delegate in the Basic look and feel that + * paints JSliders. + *

    + * + *

    + * The UI delegate keeps track of 6 rectangles that place the various parts of + * the JSlider inside the component. + *

    + * + *

    + * The rectangles are organized as follows: + *

    + *
    + *     +-------------------------------------------------------+ <-- focusRect
    + *     |                                                       |
    + *     |  +==+-------------------+==+--------------------+==+<------ contentRect
    + *     |  |  |                   |  |<---thumbRect       |  |  |
    + *     |  |  |    TRACK          |  |                    |<--------- trackRect
    + *     |  |  +-------------------+==+--------------------+  |  |
    + *     |  |  |                                           |  |  |
    + *     |  |  |          TICKS GO HERE                    |<-------- tickRect
    + *     |  |  |                                           |  |  |
    + *     |  +==+-------------------------------------------+==+  |
    + *     |  |  |                                           |  |  |
    + *     |  |  |                                           |  |<----- labelRect
    + *     |  |  |                 LABELS GO HERE            |  |  |
    + *     |  |  |                                           |  |  |
    + *     |  |  |                                           |  |  |
    + *     |  |  |                                           |  |  |
    + *     |  |  |                                           |  |  |
    + *     |  |                                              |  |  |
    + * 
    + * + *

    + * The space between the contentRect and the focusRect are the FocusInsets. + *

    + * + *

    + * The space between the focusRect and the component bounds is the insetCache + * which are the component's insets. + *

    + * + *

    + * The top of the thumb is the top of the contentRect. The trackRect has to be + * as tall as the thumb. + *

    + * + *

    + * 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. + *

    + * + *

    + * The labelRect does start right against the contentRect's left and right + * edges and it gets all remaining space. + *

    + */ +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 thumbRect field, using the + * dimensions returned by {@link #getThumbSize()}. + */ + protected void calculateThumbSize() + { + Dimension d = getThumbSize(); + thumbRect.width = d.width; + thumbRect.height = d.height; + } + + /** + * Updates the contentRect field to an area inside the + * focusRect. 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 contentRect + * and the edge of the trackRect, storing the result in the + * trackBuffer 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 + * 11 x 20 for horizontal sliders, and 20 x 11 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 labelRect 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 true if the slider scale is to be drawn inverted, + * and false if not. + * + * @return true 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 null 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); + } + + /** + *

    + * 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. + *

    + *
    +   *    a---d
    +   *    |   |
    +   *    |   |   a------------------------d
    +   *    |   |   |                        |
    +   *    |   |   b------------------------c
    +   *    |   |
    +   *    |   |
    +   *    b---c
    +   * 
    + * + *

    + * 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. + *

    + * + * @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); + } + + /** + *

    + * This method paints a thumb. There are two types of thumb: + *

    + *
    +   *   Vertical         Horizontal
    +   *    a---b            a-----b
    +   *    |   |            |      \
    +   *    e   c            |       c
    +   *     \ /             |      /
    +   *      d              e-----d
    +   *  
    + * + *

    + * 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. + *

    + * + * @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 trackRect 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 trackRect 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 BasicSpinnerUI for the specified + * JComponent + * + * @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 + * JSpinner.getEditor() + * + * @return a JComponent as an editor + * + * @see javax.swing.JSpinner#getEditor + */ + protected JComponent createEditor() + { + return spinner.getEditor(); + } + + /** + * Creates a LayoutManager 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 PropertyChangeListener that will be attached by + * installListeners. It should watch for the "editor" + * property, when it's changed, replace the old editor with the new one, + * probably by calling replaceEditor + * + * @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 installUI. This should set various defaults + * obtained from UIManager.getLookAndFeelDefaults, as well as + * set the layout obtained from createLayout + * + * @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 installUI, which basically adds the + * PropertyChangeListener created by + * createPropertyChangeListener + * + * @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 JComponent, which in reality, is a + * JSpinner. Calls installDefaults, + * installListeners, 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 installDefaults. Called by + * uninstallUI + */ + protected void uninstallDefaults() + { + spinner.setLayout(null); + } + + /** + * The reverse of installListeners, called by + * uninstallUI + */ + protected void uninstallListeners() + { + spinner.removePropertyChangeListener(listener); + } + + /** + * Called when the current L&F is replaced with another one, should call + * uninstallDefaults and uninstallListeners 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. + * + *

    + * 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. + *

    + * + * @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 serialver 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 null if the user is currently not dragging + * the divider. + */ + protected DragController dragger; + + /** + * The delegate object that is responsible for the UI of the + * JSplitPane 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 JSplitPane. + * + *

    + * The reason for also handling MouseEvents from the containing + * JSplitPane is that users should be able to start a drag + * gesture from inside the JSplitPane, but slightly outisde the divider. + *

    + */ + protected MouseHandler mouseHandler = new MouseHandler(); + + /** + * The current orientation of the containing JSplitPane, 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 + * JSplitPane. + */ + protected JButton leftButton; + + /** + * The button for showing and hiding the right (or bottom) component of the + * JSplitPane. + */ + 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 JSplitPane. + */ + 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 null 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 null 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 getBorderInsets + * 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 + * dividerSize by dividerSize 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 + * dividerSize by dividerSize pixels. + * + * @return The minimal size of the divider. + */ + public Dimension getMinimumSize() + { + return getPreferredSize(); + } + + /** + * Processes events from the JSplitPane 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 oneToughExpandable property of the + * containing JSplitPane. + */ + 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 + * JSplitPane. + * + * @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 + * JSplitPane. + * + * @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 + * startDragging method of the UI delegate of the enclosing + * JSplitPane. + * + * @see BasicSplitPaneUI#startDragging() + */ + protected void prepareForDragging() + { + if (splitPaneUI != null) + splitPaneUI.startDragging(); + } + + /** + * Drags the divider to a given location by calling the + * dragDividerTo method of the UI delegate of the enclosing + * JSplitPane. + * + * @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 finishDraggingTo + * method of the UI delegate of the enclosing JSplitPane. + * + * @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 JSplitPane. + * + *

    + * The reason for also handling MouseEvents from the containing + * JSplitPane is that users should be able to start a drag + * gesture from inside the JSplitPane, but slightly outisde the divider. + *

    + * + * @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. + *

    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.

    + *

    The FocusHandler is installed on the scrollpane and + * the tabbed pane and sets the variable hasFocus to + * false only when both components do not hold the focus.

    + * + * @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. + *

    The value at index i denotes the index of the first tab in run i.

    + *

    If the value for any index (i > 0) is 0 then (i - 1) is the last + * run.

    + */ + 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); + } + + /** + *

    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.

    + * + *

    Subclassses should retrievs a tab's bounds by this method + * if they want to find out whether the tab is currently visible.

    + * + * @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; + } + + /** + *

    This method returns the tab bounds in the given rectangle.

    + * + *

    The returned rectangle will be shifted by the current scroll + * offset if the tabbed pane is in scrolling tab layout mode.

    . + * + * @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 + * index of -1 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, -1 + * 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 -1 for no tab. + * + * @return the index of the tab over which the mouse is currently moving, + * or -1 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 table 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 (0,0) 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. + * + *

    The colors are only changed if they are not a + * ColorUIResource.

    + * + * @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 JTextPane. 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 true if the width preference has changed + * @param height true 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 1 since the RootView always contains one + * child, that is the real root of the View hierarchy. + * + * @return 1 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 Container that contains this view. This + * normally will be the text component that is managed by this TextUI. + * + * @return the Container 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 Graphics 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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 View'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 View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + */ + 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 pos in the given direction d. + * + * @param pos the document position + * @param b the bias for pos + * @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 pos in the given direction + * d + * + * @throws BadLocationException if pos 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 BasicTextUI 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 Highlighter 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 condition. + * + * @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 Graphics 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: + *
      + *
    1. If the text component is opaque, the background is painted by + * calling {@link #paintBackground(Graphics)}.
    2. + *
    3. If there is a highlighter, the highlighter is painted.
    4. + *
    5. The view hierarchy is painted.
    6. + *
    7. The Caret is painter.
    8. + *
    + * + * @param g the Graphics 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 Graphics 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 pos. + * + * @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 pos 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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 View 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 + * pt + */ + public int viewToModel(JTextComponent t, Point pt) + { + return viewToModel(t, pt, new Position.Bias[1]); + } + + /** + * Maps a point in the View 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 + * pt + */ + 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 Element to create a View 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 Element to create a View 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 View 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 BasicToggleButtonUI. + */ + public static ComponentUI createUI(JComponent component) + { + return new BasicToggleButtonUI(); + } + + /** + * Returns the prefix for entries in the {@link UIManager} defaults table + * ("ToggleButton." in this case). + * + * @return "ToggleButton." + */ + 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 BasicToolBarUI 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 JTree 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 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 JComponent for which we need a UI delegate for. + * @return the ComponentUI for c. + */ + public static ComponentUI createUI(JComponent c) + { + return new BasicTreeUI(); + } + + /** + * Returns the Hash color. + * + * @return the Color of the Hash. + */ + protected Color getHashColor() + { + return hashColor; + } + + /** + * Sets the Hash color. + * + * @param color the Color 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 tcr. This invokes + * updateRenderer. + * + * @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 TreeCellEditor 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 path + * 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 parent + * + * @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 + * treeDidChange. + */ + protected void updateSize() + { + preferredSize = null; + updateCachedPreferredSize(); + tree.treeDidChange(); + } + + /** + * Updates the preferredSize instance variable, which is + * returned from getPreferredSize(). + */ + 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 + * getInvokesStopCellEditing, 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 mouseX and mouseY 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 mouseX and mouseY 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 mouseX and mouseY 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.

    + * + * @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 row is a leaf. + * + * @param row is the row we are concerned with. + * @return true if the node at row 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, -1 for home, + * 1 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, -1 for up, 1 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 Action 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.png 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 @@ + + + + +GNU Classpath - javax.swing.plaf.basic + + +

    Provides a "basic" look and feel implementation.

    + + + 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.dia 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/doc-files/ComponentUI-1.png 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 Binary files /dev/null and b/libjava/classpath/javax/swing/plaf/doc-files/TreeUI-1.png 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 "Steel". + */ + 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 + * FontUIResource("Dialog", Font.BOLD, 12), unless the + * swing.boldMetal UI default is set to {@link Boolean#FALSE} + * in which case it is FontUIResource("Dialog", Font.PLAIN, 12). + * + * @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 + * FontUIResource("Dialog", Font.BOLD, 12), unless the + * swing.boldMetal UI default is set to {@link Boolean#FALSE} + * in which case it is FontUIResource("Dialog", Font.PLAIN, 12). + * + * @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 + * FontUIResource("Dialog", Font.PLAIN, 10). + * + * @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 + * FontUIResource("Dialog", Font.PLAIN, 12). + * + * @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 + * FontUIResource("Dialog", Font.PLAIN, 12). + * + * @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 + * FontUIResource("Dialog", Font.BOLD, 12). + * + * @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 true, when the theme is bold, false + * 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; + + /** + *

    A border used for {@link JButton} components.

    + * + *

    This {@link Border} implementation can handle only instances of + * {@link AbstractButton} and their subclasses.

    + * + *

    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 rollover of the + * button's model is true) and is not a direct + * child of a {@link JToolBar}.

    + */ + 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 ButtonBorder. + */ + 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 ButtonBorder. + * + * @param c the component for which the border is used (ignored). + * + * @return The insets of the ButtonBorder. + */ + public Insets getBorderInsets(Component c) + { + return borderInsets; + } + + /** + * Returns the insets of the ButtonBorder in the specified + * newInsets object. + * + * @param c the component for which the border is used (ignored). + * @param newInsets the insets object where to put the values ( + * null not permitted). + * + * @return The newInsets 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 + * (null not permitted). + * + * @return The newInsets 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 PaletteBorder. + */ + 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 + * (null not permitted). + * + * @return The newInsets 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 + * (null not permitted). + * + * @return The newInsets 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 insets 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 insets is null. + */ + 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 insets 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 insets is null. + */ + 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 c. + * + * @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 c using the + * Graphics context g with the dimension + * x, y, w, h. + * + * @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 TableHeaderBorder. + */ + 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 & Feel. + * + * @return a border for Swing buttons in the Metal Look & 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 & Feel. + * + * @return a border for Toolbar buttons in the Metal Look & 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 MetalButtonUI. + */ + 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 Button.gradient 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 true when the button is a toolbar button, + * false otherwise. + * + * @param b the button component to test + * + * @return true when the button is a toolbar button, + * false otherwise + */ + private boolean isToolbarButton(Component b) + { + Component parent = b.getParent(); + return parent instanceof JToolBar; + } + + /** + * Returns true if we should draw the button gradient, + * false otherwise. + * + * @param uiKey the UIManager key for the gradient + * + * @return true if we should draw the button gradient, + * false 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 MetalCheckBoxUI. + * + * @param component the component for which we return an UI instance + * + * @return A shared instance of MetalCheckBoxUI. + */ + 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 ("CheckBox."). + */ + 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 (null 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 (null 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 false, to indicate that this component is not part + * of the focus traversal group. + * + * @return false + */ + 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 left, right, + * top and bottom 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 10. + */ + public int getIconWidth() + { + return 10; + } + + /** + * Returns the icon height, which for this icon is 5 pixels. + * + * @return 5. + */ + 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 layoutContainer(Container) 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 MetalDesktopIconUI. + */ + public MetalDesktopIconUI() + { + super(); + } + + /** + * Returns a new MetalDesktopIconUI instance. + * + * @param component the component (ignored). + * + * @return A new MetalDesktopIconUI 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 ActionEvent 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 ActionEvent 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 index. + * + * @param index the item index. + * + * @return The depth. + */ + public int getDepth(int index) + { + return Math.max(index, 0); + } + + /** + * Returns the selected item, or null if no item is selected. + * + * @return The selected item, or null. + */ + 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 (null 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 SelectionListener 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 ActionEvent 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 ActionEvent 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 + * 16 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 (0 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 + * 16 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 (0 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 & 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-null 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 2 + * pixels. + * + * @return 2. + */ + public int getAdditionalHeight() + { + return 2; + } + + /** + * Returns the vertical shift, in pixels, applied when painting the icon. + * This overridden method returns -1. + * + * @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 4 + * pixels. + * + * @return 4. + */ + public int getAdditionalHeight() + { + return 4; + } + + /** + * Returns the vertical shift, in pixels, applied when painting the icon. + * This overridden method returns 2. + * + * @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 16. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return 16. + */ + 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 16. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return 16. + */ + 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 16. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return 16. + */ + 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&F. + * + * @return an icon for RadioButtons in the Metal L&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 16x16 icon representing a computer. + * + * @return The icon. + */ + public static Icon getTreeComputerIcon() + { + if (treeComputerIcon == null) + treeComputerIcon = new TreeComputerIcon(); + return treeComputerIcon; + } + + /** + * Returns a 16x16 icon representing a floppy disk. + * + * @return The icon. + */ + public static Icon getTreeFloppyDriveIcon() + { + if (treeFloppyDriveIcon == null) + treeFloppyDriveIcon = new TreeFloppyDriveIcon(); + return treeFloppyDriveIcon; + } + + /** + * Returns a 16x16 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 + * JInternalFrame.isPalette property and updates the title + * pane as appropriate. + */ + class MetalInternalFrameTitlePanePropertyChangeHandler + extends PropertyChangeHandler + { + /** + * Creates a new handler. + */ + public MetalInternalFrameTitlePanePropertyChangeHandler() + { + super(); + } + + /** + * Handles JInternalFrame.isPalette 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 TitlePaneLayout 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 InternalFrame.paletteCloseIcon. + */ + protected Icon paletteCloseIcon; + + /** + * The height of the title pane when isPalette is + * true. This value is fetched from the look and feel defaults + * using the key InternalFrame.paletteTitleHeight. + */ + 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 + * setBorderPainted(false) and + * setContentAreaFilled(false) 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 MetalTitlePaneLayout (not part of + * the public API). + * + * @return A new instance of MetalTitlePaneLayout. + */ + 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 (JInternalFrame.isPalette) 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 MetalInternalFrameUI. + * + * @param frame the frame. + */ + public MetalInternalFrameUI(JInternalFrame frame) + { + super(frame); + } + + /** + * Returns an instance of MetalInternalFrameUI. + * + * @param component the internal frame. + * + * @return an instance of MetalInternalFrameUI. + */ + 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 + * showSystemMenu Action that is installed by the + * BasicInternalFrameUI, 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 MetalLabelUI. + */ + public MetalLabelUI() + { + super(); + } + + /** + * Returns a shared instance of MetalLabelUI. + * + * @param component the component for which we return an UI instance + * + * @return A shared instance of MetalLabelUI. + */ + 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 + * Label.disabledForeground. + * + * @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:

    + *
    + * try
    + * {
    + *   UIManager.setLookAndFeel(new MetalLookAndFeel());
    + * }
    + * catch (UnsupportedLookAndFeelException e)
    + * {
    + *   e.printStackTrace();
    + * }
    + */ +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 false to indicate that this look and feel does not + * attempt to emulate the look and feel of native applications on the host + * platform. + * + * @return false. + */ + public boolean isNativeLookAndFeel() + { + return false; + } + + /** + * Returns true to indicate that this look and feel is supported + * on all platforms. + * + * @return true. + */ + 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 before 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 (null not permitted). + * + * @throws NullPointerException if theme is null. + * + * @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: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    KeyValue
    ButtonUI{@link MetalButtonUI}
    CheckBoxUI{@link MetalCheckBoxUI}
    ComboBoxUI{@link MetalComboBoxUI}
    DesktopIconUI{@link MetalDesktopIconUI}
    InternalFrameUI{@link MetalInternalFrameUI}
    LabelUI{@link MetalLabelUI}
    PopupMenuSeparatorUI{@link MetalPopupMenuSeparatorUI}
    ProgressBarUI{@link MetalProgressBarUI}
    RadioButtonUI{@link MetalRadioButtonUI}
    RootPaneUI{@link MetalRootPaneUI}
    ScrollBarUI{@link MetalScrollBarUI}
    ScrollPaneUI{@link MetalScrollPaneUI}
    SeparatorUI{@link MetalSeparatorUI}
    SliderUI{@link MetalSliderUI}
    SplitPaneUI{@link MetalSplitPaneUI}
    TabbedPaneUI{@link MetalTabbedPaneUI}
    TextFieldUI{@link MetalTextFieldUI}
    ToggleButtonUI{@link MetalToggleButtonUI}
    ToolBarUI{@link MetalToolBarUI}
    ToolTipUI{@link MetalToolTipUI}
    TreeUI{@link MetalTreeUI}
    + * + * @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 & Feel. + * + * In particular this sets the following keys (the colors are given + * as RGB hex values): + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    KeyValue
    Button.background0xcccccc
    Button.border{@link MetalBorders#getButtonBorder()}
    Button.font{@link #getControlTextFont}
    Button.marginnew java.awt.Insets(2, 14, 2, 14) + *
    CheckBox.background0xcccccc
    CheckBoxMenuItem.background0xcccccc
    ToolBar.background0xcccccc
    Panel.background0xcccccc
    Slider.background0xcccccc
    OptionPane.background0xcccccc
    ProgressBar.background0xcccccc
    TabbedPane.background0xcccccc
    Label.background0xcccccc
    Label.font{@link #getControlTextFont}
    Menu.background0xcccccc
    MenuBar.background0xcccccc
    MenuItem.background0xcccccc
    ScrollBar.background0xcccccc
    PopupMenu.bordernew javax.swing.plaf.metal.MetalBorders.PopupMenuBorder()
    + * + * @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: + * + * + * + * + * + * + * + *
    KeyValueDescription
    control0xccccccThe default color for components
    + */ + 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 null). + * + * @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 true because the Metal look + * and feel supports window decorations for toplevel + * containers. + * + * @return true + */ + 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 & 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 MenuBar.gradient 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 MetalPopupMenuSeparatorUI. + */ + public MetalPopupMenuSeparatorUI() + { + super(); + } + + /** + * Returns a shared instance of MetalPopupMenuSeparatorUI. + * + * @param component the component for which we return an UI instance + * + * @return A shared instance of MetalPopupMenuSeparatorUI. + */ + 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 MetalProgressBarUI. + */ + public MetalProgressBarUI() + { + super(); + } + + /** + * Returns a new instance of MetalProgressBarUI. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of MetalProgressBarUI. + */ + 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 MetalRadioButtonUI. + */ + public MetalRadioButtonUI() + { + super(); + } + + /** + * Returns a new instance of MetalRadioButtonUI. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of MetalRadioButtonUI. + */ + 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 + * RadioButton.select. + * + * @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 RadioButton.disabledText. + * + * @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 + * RadioButton.focus. + * + * @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 windowDecorationStyle 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 + * null 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 windowDecorationStyle + * 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 TitlePaneLayout 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 MetalRootPaneUI. + */ + public MetalRootPaneUI() + { + super(); + } + + /** + * Returns a shared instance of MetalRootPaneUI. + * + * @param component the component for which we return an UI instance + * + * @return A shared instance of MetalRootPaneUI. + */ + public static ComponentUI createUI(JComponent component) + { + if (instance == null) + instance = new MetalRootPaneUI(); + return instance; + } + + /** + * Installs this UI to the root pane. If the + * windowDecorationsStyle 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 windowDecorationStyle + * 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 + * JSlider.isFreeStanding, 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 MetalScrollBarUI, with no + * specific initialisation. + */ + public MetalScrollBarUI() + { + super(); + } + + /** + * Returns a new instance of MetalScrollBarUI. + * + * @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 + * JScrollBar.isFreeStanding 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 + * ScrollBar.width 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 + * ScrollBar.width 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 17 x 17 pixels, whereas for a non free + * standing scroll bar the minimum size is 15 x 15 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 preferredSize for the specified scroll bar. + * For a vertical scrollbar the height is the sum of the preferred heights + * of the buttons plus 30. The width is fetched from the + * UIManager property ScrollBar.width. + * + * For horizontal scrollbars the width is the sum of the preferred widths + * of the buttons plus 30. The height is fetched from the + * UIManager property ScrollBar.height. + * + * @param c the scrollbar for which to calculate the preferred size + * + * @return the preferredSize 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 Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE). + */ + 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 MetalScrollPaneUI. + */ + public MetalScrollPaneUI() + { + super(); + } + + /** + * Returns a shared instance of MetalScrollPaneUI. + * + * @param component the component for which we return an UI instance + * + * @return A shared instance of MetalScrollPaneUI. + */ + 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 MetalSeparatorUI. + */ + public MetalSeparatorUI() + { + super(); + } + + /** + * Returns a shared instance of MetalSeparatorUI. + * + * @param component the component for which we return an UI instance + * + * @return A shared instance of MetalSeparatorUI. + */ + 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 MetalSliderUI. + * + * @param component the component (ignored). + * + * @return A new instance of MetalSliderUI. + */ + 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 MetalSplitPaneDivider. + * + * @param ui the MetalSplitPaneUI 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 Graphics 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 MetalSplitPaneUI. + */ + public MetalSplitPaneUI() + { + super(); + } + + /** + * Returns a new instance of MetalSplitPaneUI. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of MetalSplitPaneUI. + */ + public static ComponentUI createUI(JComponent component) + { + return new MetalSplitPaneUI(); + } + + /** + * Returns the divider that is used by the JSplitPane. + * + * The divider returned by this method is a {@link BasicSplitPaneDivider} + * that is drawn using the Metal look. + * + * @return the default divider to use for JSplitPanes. + */ + 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 true 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 MetalTextFieldUI. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of MetalTextFieldUI. + */ + 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 MetalToggleButtonUI. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of MetalToggleButtonUI. + */ + public static ComponentUI createUI(JComponent component) + { + return new MetalToggleButtonUI(); + } + + /** + * Constructs a new instance of MetalToggleButtonUI. + */ + 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 ToggleButton.disabledText 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 ToggleButton.gradient 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 MetalToolBarUI. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of MetalToolBarUI. + */ + public static ComponentUI createUI(JComponent component) + { + return new MetalToolBarUI(); + } + + /** + * Returns null 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 null. + */ + protected PropertyChangeListener createRolloverListener() + { + return null; + } + + /** + * Returns null 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 null. + */ + 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 UIManager property + * "ToolBar.isRollover". + * + * @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 + * paint(g, c). + * + * 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 MetalToolTipUI. + */ + 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 MetalToolTipUI 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 MetalToolTipUI 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 true if the accelerator string is hidden, and + * false otherwise. This setting is controlled by the + * ToolTip.hideAccelerator 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 + * null if the component has no accelerator. + * + * @param c the component. + * + * @return A string representing the accelerator (possibly + * null). + */ + 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 (null 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 MetalTreeUI. + */ + public MetalTreeUI() + { + super(); + } + + /** + * Returns a new instance of MetalTreeUI. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of MetalTreeUI. + */ + 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 & 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 Graphics 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 Graphics2D 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 + * uiProp. 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: + *
    +   *
    +   * +-------+--------+--------+-----------------------------+
    +   * |       |        |        |                             |
    +   * +-------+--------+--------+-----------------------------+
    +   * c1  ->  c2  --   c2  ->   c1         -------->          c3
    +   * < -g1- > < -g2- > < -g1- >
    +   * 
    + * + * There are 4 distinct areas in this gradient: + *
      + *
    1. A gradient from color 1 to color 2 with the relative width specified + * by g1
    2. + *
    3. A solid area with the color 2 and the relative width specified by + * g2
    4. + *
    5. A gradient from color 2 to color 1 with the relative width specified + * by g1
    6. + * + * The mask 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 inside
      the surrounded area is filled by + * the gradient: + * + *
      +   *     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
      +   * 
      + * + * The mask array is expected to have w or + * h array elements, depending on the direction. + * + * If the mask 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 & 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, "Ocean" + */ + 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 @@ + + + + +GNU Classpath - javax.swing.plaf.metal + + +

      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:

      +
      try
      +  {
      +  UIManager.setLookAndFeel(new MetalLookAndFeel());
      +  }
      +catch (UnsupportedLookAndFeelException e)
      +  {
      +  e.printStackTrace();
      +  }
      + + 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 MultiButtonUI 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 MultiButtonUI 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 MultiButtonUI. + * + * @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 MultiButtonUI. + * + * @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 + * MultiButtonUI. 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 MultiButtonUI, + * 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 true 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 false 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 MultiButtonUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiButtonUI. + * + * @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 MultiButtonUI, + * 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 MultiButtonUI, + * 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 MultiButtonUI, + * 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 MultiButtonUI, + * 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 MultiButtonUI, + * 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 MultiColorChooserUI 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 MultiColorChooserUI + * 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 MultiColorChooserUI. + * + * @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 MultiColorChooserUI. + * + * @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 + * MultiColorChooserUI. 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 MultiColorChooserUI, + * 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 true 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 false 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 MultiColorChooserUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiColorChooserUI. + * + * @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 MultiColorChooserUI, + * 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 MultiColorChooserUI, + * 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 MultiColorChooserUI, + * 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 MultiColorChooserUI, + * 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 MultiColorChooserUI, + * 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 MultiComboBoxUI 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 MultiComboBoxUI + * 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 MultiComboBoxUI. + * + * @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 MultiComboBoxUI. + * + * @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 + * MultiComboBoxUI. 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 MultiComboBoxUI, + * 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 true 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 false 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 MultiComboBoxUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiComboBoxUI. + * + * @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 MultiComboBoxUI, + * 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 MultiComboBoxUI, + * 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 MultiComboBoxUI, + * 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 MultiComboBoxUI, + * 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 MultiComboBoxUI, + * 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 MultiComboBoxUI. + * + * @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 MultiComboBoxUI, + * 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 MultiComboBoxUI, + * returning the result for the UI delegate from the primary look and + * feel. + * + * @param c the component. + * + * @return true if the combo box is traversable according to the + * UI delegate in the primary look and feel, and false + * 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 MultiDesktopIconUI 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 MultiDesktopIconUI 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 MultiDesktopIconUI. + * + * @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 MultiDesktopIconUI. + * + * @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 + * MultiDesktopIconUI. 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 MultiDesktopIconUI, + * 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 true 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 false 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 MultiDesktopIconUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiDesktopIconUI. + * + * @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 MultiDesktopIconUI, + * 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 MultiDesktopIconUI, + * 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 MultiDesktopIconUI, + * 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 MultiDesktopIconUI, + * 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 MultiDesktopIconUI, + * 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 MultiDesktopPaneUI 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 MultiDesktopPaneUI 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 MultiDesktopPaneUI. + * + * @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 MultiDesktopPaneUI. + * + * @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 + * MultiDesktopPaneUI. 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 MultiDesktopPaneUI, + * 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 true 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 false 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 MultiDesktopPaneUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiDesktopPaneUI. + * + * @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 MultiDesktopPaneUI, + * 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 MultiDesktopPaneUI, + * 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 MultiDesktopPaneUI, + * 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 MultiDesktopPaneUI, + * 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 MultiDesktopPaneUI, + * 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 MultiFileChooserUI 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 MultiFileChooserUI 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 MultiFileChooserUI. + * + * @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 MultiFileChooserUI. + * + * @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 + * MultiFileChooserUI. 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 MultiFileChooserUI, + * 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 true 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 false 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 MultiFileChooserUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiFileChooserUI. + * + * @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 MultiFileChooserUI, + * 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 MultiFileChooserUI, + * 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 MultiFileChooserUI, + * 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 MultiFileChooserUI, + * 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 MultiFileChooserUI, + * 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 MultiFileChooserUI, + * 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 MultiFileChooserUI, + * 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 MultiFileChooserUI, + * 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 MultiFileChooserUI, + * 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 + * MultiFileChooserUI. + * + * @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 + * MultiFileChooserUI. + * + * @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 MultiInternalFrameUI 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 MultiInternalFrameUI + * 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 MultiInternalFrameUI. + * + * @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 MultiInternalFrameUI. + * + * @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 + * MultiInternalFrameUI. 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 MultiInternalFrameUI, + * 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 true 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 false 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 MultiInternalFrameUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiInternalFrameUI. + * + * @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 MultiInternalFrameUI, + * 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 MultiInternalFrameUI, + * 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 MultiInternalFrameUI, + * 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 MultiInternalFrameUI, + * 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 MultiInternalFrameUI, + * 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 MultiLabelUI 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 MultiLabelUI 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 MultiLabelUI. + * + * @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 MultiLabelUI. + * + * @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 + * MultiLabelUI. 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 MultiLabelUI, + * 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 true 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 false 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 MultiLabelUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiLabelUI. + * + * @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 MultiLabelUI, + * 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 MultiLabelUI, + * 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 MultiLabelUI, + * 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 MultiLabelUI, + * 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 MultiLabelUI, + * 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 MultiListUI 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 MultiListUI 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 MultiListUI. + * + * @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 MultiListUI. + * + * @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 + * MultiListUI. 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 MultiListUI, + * 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 true 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 false 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 MultiListUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiListUI. + * + * @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 MultiListUI, + * 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 MultiListUI, + * 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 MultiListUI, + * 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 MultiListUI, + * 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 MultiListUI, + * 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 MultiListUI, + * 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 MultiListUI, + * 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 MultiListUI, + * 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 false to indicate that this look and feel is not + * native to any platform. + * + * @return false. + */ + public boolean isNativeLookAndFeel() + { + return false; + } + + /** + * Returns true always, since this look and feel is supported on + * all platforms. + * + * @return true. + */ + 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 target component and + * returns a multiplexing UI delegate (mui) 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 + * uis. If uis is null, a zero-length + * array is returned. + * + * @param uis a list of {@link ComponentUI} references (null + * permitted). + * + * @return An array containing the same {@link ComponentUI} instances as + * uis, or null if uis 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 MultiMenuBarUI 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 MultiMenuBarUI 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 MultiMenuBarUI. + * + * @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 MultiMenuBarUI. + * + * @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 + * MultiMenuBarUI. 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 MultiMenuBarUI, + * 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 true 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 false 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 MultiMenuBarUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiMenuBarUI. + * + * @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 MultiMenuBarUI, + * 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 MultiMenuBarUI, + * 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 MultiMenuBarUI, + * 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 MultiMenuBarUI, + * 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 MultiMenuBarUI, + * 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 MultiMenuItemUI 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 MultiItemUI 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 MultiMenuItemUI. + * + * @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 MultiMenuItemUI. + * + * @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 + * MultiMenuItemUI. 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 MultiMenuItemUI, + * 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 true 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 false 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 MultiMenuItemUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiMenuItemUI. + * + * @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 MultiMenuItemUI, + * 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 MultiMenuItemUI, + * 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 MultiMenuItemUI, + * 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 MultiMenuItemUI, + * 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 MultiMenuItemUI, + * 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 MultiOptionPaneUI 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 MultiOptionPaneUI 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 MultiOptionPaneUI. + * + * @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 MultiOptionPaneUI. + * + * @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 + * MultiOptionPaneUI. 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 MultiOptionPaneUI, + * 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 true 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 false 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 MultiOptionPaneUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiOptionPaneUI. + * + * @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 MultiOptionPaneUI, + * 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 MultiOptionPaneUI, + * 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 MultiOptionPaneUI, + * 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 MultiOptionPaneUI, + * 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 MultiOptionPaneUI, + * 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 MultiOptionPaneUI. + * + * @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 + * MultiOptionPaneUI, 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 MultiPanelUI 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 MultiPanelUI 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 MultiPanelUI. + * + * @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 MultiPanelUI. + * + * @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 + * MultiPanelUI. 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 MultiPanelUI, + * 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 true 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 false 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 MultiPanelUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiPanelUI. + * + * @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 MultiPanelUI, + * 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 MultiPanelUI, + * 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 MultiPanelUI, + * 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 MultiPanelUI, + * 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 MultiPanelUI, + * 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 MultiPopupMenuUI 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 MultiPopupMenuUI 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 MultiPopupMenuUI. + * + * @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 MultiPopupMenuUI. + * + * @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 + * MultiPopupMenuUI. 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 MultiPopupMenuUI, + * 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 true 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 false 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 MultiPopupMenuUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiPopupMenuUI. + * + * @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 MultiPopupMenuUI, + * 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 MultiPopupMenuUI, + * 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 MultiPopupMenuUI, + * 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 MultiPopupMenuUI, + * 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 MultiPopupMenuUI, + * 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 MultiProgressBarUI 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 MultiProgressBarUI 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 MultiProgressBarUI. + * + * @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 MultiProgressBarUI. + * + * @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 + * MultiProgressBarUI. 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 MultiProgressBarUI, + * 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 true 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 false 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 MultiProgressBarUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiProgressBarUI. + * + * @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 MultiProgressBarUI, + * 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 MultiProgressBarUI, + * 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 MultiProgressBarUI, + * 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 MultiProgressBarUI, + * 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 MultiProgressBarUI, + * 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 MultiRootPanelUI 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 MultiRootPaneUI 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 MultiRootPaneUI. + * + * @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 MultiRootPaneUI. + * + * @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 + * MultiRootPaneUI. 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 MultiRootPaneUI, + * 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 true 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 false 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 MultiRootPaneUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiRootPaneUI. + * + * @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 MultiRootPaneUI, + * 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 MultiRootPaneUI, + * 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 MultiRootPaneUI, + * 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 MultiRootPaneUI, + * 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 MultiRootPaneUI, + * 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 MultiScrollBarUI 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 MultiScrollBarUI 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 MultiScrollBarUI. + * + * @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 MultiScrollBarUI. + * + * @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 + * MultiScrollBarUI. 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 MultiScrollBarUI, + * 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 true 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 false 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 MultiScrollBarUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiScrollBarUI. + * + * @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 MultiScrollBarUI, + * 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 MultiScrollBarUI, + * 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 MultiScrollBarUI, + * 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 MultiScrollBarUI, + * 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 MultiScrollBarUI, + * 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 MultiScrollPaneUI 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 MultiScrollPaneUI 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 MultiScrollPaneUI. + * + * @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 MultiScrollPaneUI. + * + * @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 + * MultiScrollPaneUI. 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 MultiScrollPaneUI, + * 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 true 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 false 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 MultiScrollPaneUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiScrollPaneUI. + * + * @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 MultiScrollPaneUI, + * 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 MultiScrollPaneUI, + * 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 MultiScrollPaneUI, + * 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 MultiScrollPaneUI, + * 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 MultiScrollPaneUI, + * 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 MultiSeparatorUI 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 MultiSeparatorUI 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 MultiSeparatorUI. + * + * @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 MultiSeparatorUI. + * + * @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 + * MultiSeparatorUI. 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 MultiSeparatorUI, + * 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 true 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 false 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 MultiSeparatorUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiSeparatorUI. + * + * @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 MultiSeparatorUI, + * 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 MultiSeparatorUI, + * 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 MultiSeparatorUI, + * 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 MultiSeparatorUI, + * 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 MultiSeparatorUI, + * 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 MultiSliderUI 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 MultiSliderUI 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 MultiSliderUI. + * + * @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 MultiSliderUI. + * + * @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 + * MultiSliderUI. 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 MultiSliderUI, + * 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 true 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 false 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 MultiSliderUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiSliderUI. + * + * @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 MultiSliderUI, + * 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 MultiSliderUI, + * 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 MultiSliderUI, + * 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 MultiSliderUI, + * 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 MultiSliderUI, + * 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 MultiSpinnerUI 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 MultiSpinnerUI 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 MultiSpinnerUI. + * + * @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 MultiSpinnerUI. + * + * @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 + * MultiSpinnerUI. 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 MultiSpinnerUI, + * 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 true 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 false 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 MultiSpinnerUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiSpinnerUI. + * + * @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 MultiSpinnerUI, + * 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 MultiSpinnerUI, + * 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 MultiSpinnerUI, + * 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 MultiSpinnerUI, + * 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 MultiSpinnerUI, + * 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 MultiSplitPaneUI 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 MultiSplitPaneUI 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 MultiSplitPaneUI. + * + * @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 MultiSplitPaneUI. + * + * @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 + * MultiSplitPaneUI. 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 MultiSplitPaneUI, + * 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 true 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 false 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 MultiSplitPaneUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiSplitPaneUI. + * + * @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 MultiSplitPaneUI, + * 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 MultiSplitPaneUI, + * 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 MultiSplitPaneUI, + * 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 MultiSplitPaneUI, + * 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 MultiSplitPaneUI, + * 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 MultiSplitPaneUI. + * + * @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 MultiSplitPaneUI. + * + * @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 MultiSplitPaneUI, + * 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 MultiSplitPaneUI, + * 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 MultiSplitPaneUI, + * 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 + * MultiSplitPaneUI. + * + * @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 MultiTabbedPaneUI 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 MultiTabbedPaneUI 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 MultiTabbedPaneUI. + * + * @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 MultiTabbedPaneUI. + * + * @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 + * MultiTabbedPaneUI. 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 MultiTabbedPaneUI, + * 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 true 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 false 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 MultiTabbedPaneUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiTabbedPaneUI. + * + * @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 MultiTabbedPaneUI, + * 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 MultiTabbedPaneUI, + * 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 MultiTabbedPaneUI, + * 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 MultiTabbedPaneUI, + * 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 MultiTabbedPaneUI, + * 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 + * MultiTabbedPaneUI, 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 + * MultiTabbedPaneUI, 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 + * MultiTabbedPaneUI, 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 MultiTableHeaderUI 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 MultiTableHeaderUI 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 MultiTableHeaderUI. + * + * @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 MultiTableHeaderUI. + * + * @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 + * MultiTableHeaderUI. 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 MultiTableHeaderUI, + * 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 true 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 false 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 MultiTableHeaderUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiTableHeaderUI. + * + * @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 MultiTableHeaderUI, + * 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 MultiTableHeaderUI, + * 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 MultiTableHeaderUI, + * 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 MultiTableHeaderUI, + * 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 MultiTableHeaderUI, + * 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 MultiTableUI 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 MultiTableUI 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 MultiTableUI. + * + * @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 MultiTableUI. + * + * @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 + * MultiTableUI. 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 MultiTableUI, + * 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 true 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 false 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 MultiTableUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiTableUI. + * + * @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 MultiTableUI, + * 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 MultiTableUI, + * 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 MultiTableUI, + * 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 MultiTableUI, + * 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 MultiTableUI, + * 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 MultiTextUI 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 MultiTextUI 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 MultiTextUI. + * + * @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 MultiTextUI. + * + * @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 + * MultiTextUI. 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 MultiTextUI, + * 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 true 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 false 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 MultiTextUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiTextUI. + * + * @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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiTextUI. + * + * @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 MultiTextUI. + * + * @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 MultiTextUI, + * 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 MultiTextUI, + * 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 MultiToolBarUI 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 MultiToolBarUI 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 MultiToolBarUI. + * + * @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 MultiToolBarUI. + * + * @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 + * MultiToolBarUI. 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 MultiToolBarUI, + * 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 true 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 false 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 MultiToolBarUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiToolBarUI. + * + * @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 MultiToolBarUI, + * 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 MultiToolBarUI, + * 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 MultiToolBarUI, + * 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 MultiToolBarUI, + * 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 MultiToolBarUI, + * 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 MultiToolTipUI 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 MultiToolTipUI 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 MultiToolTipUI. + * + * @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 MultiToolTipUI. + * + * @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 + * MultiToolTipUI. 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 MultiToolTipUI, + * 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 true 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 false 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 MultiToolTipUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiToolTipUI. + * + * @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 MultiToolTipUI, + * 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 MultiToolTipUI, + * 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 MultiToolTipUI, + * 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 MultiToolTipUI, + * 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 MultiToolTipUI, + * 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 MultiTreeUI 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 MultiTreeUI 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 MultiTreeUI. + * + * @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 MultiTreeUI. + * + * @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 + * MultiTreeUI. 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 MultiTreeUI, + * 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 true 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 false 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 MultiTreeUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiTreeUI. + * + * @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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI, + * 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 MultiTreeUI. + * + * @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 MultiTreeUI. + * + * @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 MultiTreeUI, + * 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 MultiViewPortUI 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 MultiViewportUI 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 MultiViewportUI. + * + * @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 MultiViewportUI. + * + * @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 + * MultiViewportUI. 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 MultiViewportUI, + * 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 true 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 false 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 MultiViewportUI. + * + * @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 paint(Graphics, JComponent) method for all the UI + * delegates managed by this MultiViewportUI. + * + * @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 MultiViewportUI, + * 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 MultiViewportUI, + * 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 MultiViewportUI, + * 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 MultiViewportUI, + * 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 MultiViewportUI, + * 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 @@ + + + + +GNU Classpath - javax.swing.plaf.multi + + +

      Provides a look and feel that can combine a primary look and feel with one or more auxiliary look and feels.

      + + + 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 @@ + + + + +GNU Classpath - javax.swing.plaf + + +

      A base package for the "pluggable look and feel" (plaf) mechanism used by +the javax.swing classes.

      + + + 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 Color 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 Region with the specified name and ui ID. + * The ui must be the same what + * {@link javax.swing.JComponent#getUIClassID()} returns for toplevel regions. For + * subregions this should be null. + * + * @param name the name of the region + * @param ui the UI class ID of the region or null for + * subregions + * @param subregion true if this region is a subregion, + * false otherwise + */ + protected Region(String name, String ui, boolean subregion) + { + this.name = name; + this.ui = ui; + this.subregion = subregion; + } + + /** + * Returns true if this region describes a subregion of a + * component, false if it describes a component region itself. + * + * @return true if this region describes a subregion of a + * component, false 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 SynthContext 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 SynthGraphicsUtils 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 null + * @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 viewR, iconR and + * textR. + * + * 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 null + * @param icon the icon to lay out, may be null + * @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 text 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 text 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 + * text and icon 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 + * text + * + * @return the minimums size that is needed to render the label with + * text and icon 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 + * text and icon 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 + * text + * + * @return the preferred size that is needed to render the label with + * text and icon 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 + * text and icon 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 + * text + * + * @return the maximum size that is needed to render the label with + * text and icon 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 context. + * + * @param context identifies the current component and region + * + * @return the maximum character height of the font from the component of the + * passed in context + */ + public int getMaximumCharHeight(SynthContext context) + { + Component comp = context.getComponent(); + Font font = comp.getFont(); + return comp.getFontMetrics(font).getHeight(); + } + + /** + * Renders the specified text within the bounds. + * + * @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 + * text + */ + 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 text 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 + * text + */ + 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 SynthLookAndFeel. 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 resourceBase 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 + * null + */ + 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 + * "Synth look and feel". + * + * @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 "Synth". + * + * @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 + * "Synth look and feel". + * + * @return the name of the Synth look and feel + */ + public String getName() + { + return "Synth look and feel"; + } + + /** + * Returns false since the Synth look and feel is not a native + * look and feel. + * + * @return false + */ + public boolean isNativeLookAndFeel() + { + return false; + } + + /** + * Returns true since the Synth look and feel is always a + * supported look and feel. + * + * @return true + */ + 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 SynthPainter 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 SynthStyle 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 SynthStyleFactory. + */ + 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 @@ + + + + +GNU Classpath - javax.swing.plaf.synth + + +

      Provides a look and feel that can be customized by and XML file or by + providing a custom {@link SynthStyleFactory}. +

      + + 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 -1 if there is + * no column with the specified name. + * + * @param columnName the name of the column (null not permitted). + * + * @return The index of the column, -1 if not found. + * + * @see #getColumnName(int) + * @throws NullPointerException if columnName is + * null. + */ + 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 Class for all Object instances + * in the specified column. + * + * @param columnIndex the column index. + * + * @return The class. + */ + public Class getColumnClass(int columnIndex) + { + return Object.class; + } + + /** + * Returns true if the specified cell is editable, and + * false if it is not. This implementation returns + * false 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 false. + */ + 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 (null 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[] getListeners(Class 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. + * + *

      Replaces the current UI object with the latest version from + * the UIManager.

      + */ + 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. + * + *

      This method needs to be overridden in a subclass to actually + * do something.

      + * + * @return always true + */ + public boolean isOpaque() + { + return true; + } + + /** + * Overriden for performance. + * + *

      This method needs to be overridden in a subclass to actually + * do something.

      + */ + public void validate() + { + // Does nothing. + } + + public void revalidate() + { + // Does nothing. + } + + /** + * Overriden for performance. + * + *

      This method needs to be overridden in a subclass to actually + * do something.

      + */ + public void repaint(long tm, int x, int y, int width, int height) + { + // Does nothing. + } + + /** + * Overriden for performance. + * + *

      This method needs to be overridden in a subclass to actually + * do something.

      + */ + public void repaint(Rectangle r) + { + // Does nothing. + } + + /** + * Overriden for performance. + * + *

      This method needs to be overridden in a subclass to actually + * do something.

      + */ + protected void firePropertyChange(String propertyName, Object oldValue, + Object newValue) + { + // Does nothing. + } + + /** + * Overriden for performance. + * + *

      This method needs to be overridden in a subclass to actually + * do something.

      + */ + 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 tableColumns; + + /** + * A selection model that keeps track of column selections. + */ + protected ListSelectionModel selectionModel; + + /** + * The space between the columns (the default value is 1). + */ + 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 + * columnMargin 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 columnMargin is 1 and + * the default value for columnSelectionAllowed is + * false. + */ + 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 (null not permitted). + * + * @throws IllegalArgumentException if column is + * null. + * + * @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 + * null, this method does nothing. + * + * @param column the column to be removed (null 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 i or j are + * outside the range 0 to N-1, where + * N 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 getColumns() + { + return tableColumns.elements(); + } + + /** + * Returns the index of the {@link TableColumn} with the given identifier. + * + * @param identifier the identifier (null not permitted). + * + * @return The index of the {@link TableColumn} with the given identifier. + * + * @throws IllegalArgumentException if identifier is + * null 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 0 to + * N-1, where N is the number of columns in + * the model). + * + * @return The column at the specified index. + * + * @throws ArrayIndexOutOfBoundsException if i 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: + *
        + *
      • column zero begins at position zero;
      • + *
      • all columns appear in order;
      • + *
      • individual column widths are taken into account, but the column margin + * is ignored.
      • + *
      + * If no column contains the specified position, this method returns + * -1. + * + * @param x the x-position. + * + * @return The column index, or -1. + */ + 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 (null not permitted). + * + * @throws IllegalArgumentException if model is + * null. + * + * @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 true if column selection is allowed, and + * false 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 (null 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 toIndex 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 fromIndex 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 fromIndex attribute should contain the old column + * index, and the toIndex 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}, null not permitted). + * + * @return An array containing the listeners (of the specified type) that + * are registered with this model. + */ + public T[] getListeners(Class listenerType) + { + return listenerList.getListeners(listenerType); + } + + /** + * Receives notification of property changes for the columns in the model. + * If the width 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 + * -1. 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 -1. + * + * @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 Object + * instances, usually for display in a JTable 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 Vector). + */ + 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 null). + * + * @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 Vector 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 + * null 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 null. + */ + 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 null. + */ + public void setDataVector(Object[][] data, Object[] columnNames) + { + setDataVector(convertToVector(data), + convertToVector(columnNames)); + } + + /** + * Sends the specified event 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 event 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 event 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 null 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 null 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 rowCount is less + * than the current number of rows in the table, rows are discarded. + * If rowCount 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 null 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 null. + * + * @param columnName the column name (null 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 (null 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 (null 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 (null 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 (null 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 startIndex to endIndex + * (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 true if the specified cell can be modified, and + * false otherwise. For this implementation, the method + * always returns true. + * + * @param row the row index. + * @param column the column index. + * + * @return true 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 (Object, possibly null) 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 (Object, null 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 Vector. + * + * @param data the data array (null permitted). + * + * @return A vector (or null if the data array + * is null). + */ + 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 Vector of rows. + * + * @param data the data array (null permitted). + * + * @return A vector (or null if the data array + * is null. + */ + 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 dataVector. + * + * @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 + * getColumnCount() and getRowCount(). + * If the supposed one are bigger, then we grow columIdentifiers + * and dataVector 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 + * null. + * + * @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 null as the header entry has no accessible + * children. + * + * @return null. + */ + 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 this. + */ + public AccessibleComponent getAccessibleComponent() + { + return this; + } + + /** + * Returns the accessible context for this header entry. + * + * @return this. + */ + 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 cm is null, a new + * table column model is created by calling + * {@link #createDefaultColumnModel()}. + * + * @param cm the table column model (null 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 revalidate() followed by + * repaint(). + */ + 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 columnWidth 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 headerValue property. + */ + public static final String HEADER_VALUE_PROPERTY = "headerValue"; + + /** + * The name for the headerRenderer property. + */ + public static final String HEADER_RENDERER_PROPERTY = "headerRenderer"; + + /** + * The name for the cellRenderer 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 true). + */ + 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 TableColumn that maps to column 0 in the + * related table model. The default width is 75 units. + */ + public TableColumn() + { + this(0, 75, null, null); + } + + /** + * Creates a new TableColumn that maps to the specified column + * in the related table model. The default width is 75 units. + * + * @param modelIndex the index of the column in the model + */ + public TableColumn(int modelIndex) + { + this(modelIndex, 75, null, null); + } + + /** + * Creates a new TableColumn that maps to the specified column + * in the related table model, and has the specified width. + * + * @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 TableColumn that maps to the specified column + * in the related table model, and has the specified width, + * cellRenderer and cellEditor. + * + * @param modelIndex the index of the column in the model + * @param width the width + * @param cellRenderer the cell renderer (null permitted). + * @param cellEditor the cell editor (null 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 + * TableColumn 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 TableColumn 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 (null 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 null. + * + * @return The identifier (or {@link #getHeaderValue()} if the identifier is + * null). + */ + 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 (null 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 (null 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 null). + * + * @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 (null 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 null). + * + * @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 (null 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 + * null). + * + * @return The cell editor (possibly null). + * + * @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 75). + * + * @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 + * 75). + * + * @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 width and/or + * preferredWidth 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 TableColumn's minimum width (the default value + * is 15). + * + * @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 width and/or + * preferredWidth 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 true if this column is resizable, + * false 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 true if this column is resizable, + * false 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: + *
        + *
      • width - see {@link #setWidth(int)};
      • + *
      • preferredWidth - see {@link #setPreferredWidth(int)};
      • + *
      • minWidth - see {@link #setMinWidth(int)};
      • + *
      • maxWidth - see {@link #setMaxWidth(int)};
      • + *
      • modelIndex - see {@link #setModelIndex(int)};
      • + *
      • isResizable - see {@link #setResizable(boolean)};
      • + *
      • cellRenderer - see + * {@link #setCellRenderer(TableCellRenderer)};
      • + *
      • cellEditor - see + * {@link #setCellEditor(TableCellEditor)};
      • + *
      • headerRenderer - see + * {@link #setHeaderRenderer(TableCellRenderer)};
      • + *
      • headerValue - see {@link #setHeaderValue(Object)};
      • + *
      • identifier - see {@link #setIdentifier(Object)}.
      • + *
      + * + * @param listener the listener to add (null 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 + * listener is not registered with the column, or is + * null, this method does nothing. + * + * @param listener the listener to remove (null is ignored). + */ + public synchronized void removePropertyChangeListener( + PropertyChangeListener listener) + { + changeSupport.removePropertyChangeListener(listener); + } + + /** + * Returns the property change listeners for this TableColumn. + * 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 (null not permitted). + * + * @throws IllegalArgumentException if column is + * null. + */ + void addColumn(TableColumn column); + + /** + * Removes a column from the model. If column 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 getColumns(); + + /** + * Returns the index of the {@link TableColumn} with the given identifier. + * + * @param identifier the identifier (null not permitted). + * + * @return The index of the {@link TableColumn} with the given identifier. + * + * @throws IllegalArgumentException if identifier is + * null or there is no column with that identifier. + */ + int getColumnIndex(Object identifier); + + /** + * Returns the TableColumn 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: + *
        + *
      • column zero begins at position zero;
      • + *
      • all columns appear in order;
      • + *
      • individual column widths are taken into account, but the column margin + * is ignored.
      • + *
      + * If no column contains the specified position, this method returns + * -1. + * + * @param xPosition the x-position. + * + * @return The column index, or -1. + */ + 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 true if column selection is allowed, and + * false 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 (null not permitted). + * + * @throws IllegalArgumentException if model is + * null. + */ + 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 (null 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 TableModel is a two dimensional data structure that + * can store arbitrary Object 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. + *

      + * 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 Class for all Object instances + * in the specified column. + * + * @param columnIndex the column index. + * + * @return The class. + */ + Class getColumnClass(int columnIndex); + + /** + * Returns true if the cell is editable, and false + * otherwise. + * + * @param rowIndex the row index. + * @param columnIndex the column index. + * + * @return true if editable, false otherwise. + */ + boolean isCellEditable(int rowIndex, int columnIndex); + + /** + * Returns the value (Object) 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 (null 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 @@ + + + + +GNU Classpath - javax.swing.table + + +

      Interfaces and classes that support the {@link javax.swing.JTable} +component.

      + + + 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 Elements, + * 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 Elements. + */ + public static final String BidiElementName = "bidi level"; + + /** + * Standard name for content Elements. These are usually + * {@link LeafElement}s. + */ + public static final String ContentElementName = "content"; + + /** + * Standard name for paragraph Elements. These are usually + * {@link BranchElement}s. + */ + public static final String ParagraphElementName = "paragraph"; + + /** + * Standard name for section Elements. 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 Document. + */ + Content content; + + /** + * The AttributeContext for this Document. + */ + AttributeContext context; + + /** + * The currently installed DocumentFilter. + */ + DocumentFilter documentFilter; + + /** + * The documents properties. + */ + Dictionary properties; + + /** + * Manages event listeners for this Document. + */ + 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 AbstractDocument with the specified + * {@link Content} model. + * + * @param doc the Content model to be used in this + * Document + * + * @see GapContent + * @see StringContent + */ + protected AbstractDocument(Content doc) + { + this(doc, StyleContext.getDefaultStyleContext()); + } + + /** + * Creates a new AbstractDocument with the specified + * {@link Content} model and {@link AttributeContext}. + * + * @param doc the Content model to be used in this + * Document + * @param ctx the AttributeContext 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 Document. + * Usual Documents only have one root element and return this. + * However, there may be Document implementations that + * support multiple root elements, they have to return a default root element + * here. + * + * @return the default root {@link Element} of this Document + */ + public abstract Element getDefaultRootElement(); + + /** + * Creates and returns a branch element with the specified + * parent and attributes. Note that the new + * Element is linked to the parent Element + * through {@link Element#getParentElement}, but it is not yet added + * to the parent Element as child. + * + * @param parent the parent Element for the new branch element + * @param attributes the text attributes to be installed in the new element + * + * @return the new branch Element + * + * @see BranchElement + */ + protected Element createBranchElement(Element parent, + AttributeSet attributes) + { + return new BranchElement(parent, attributes); + } + + /** + * Creates and returns a leaf element with the specified + * parent and attributes. Note that the new + * Element is linked to the parent Element + * through {@link Element#getParentElement}, but it is not yet added + * to the parent Element as child. + * + * @param parent the parent Element for the new branch element + * @param attributes the text attributes to be installed in the new element + * + * @return the new branch Element + * + * @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 offset. + * + * @param offset the location in the document to keep track by the new + * Position + * + * @return the newly created Position + * + * @throws BadLocationException if offset 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 DocumentEvent 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 DocumentEvent 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 DocumentEvent 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 UndoableEdit has + * been performed on this Document. + * + * @param event the UndoableEditEvent 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 -1 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 Document. + * + * @return the {@link AttributeContext} used in this Document + */ + 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 Document + * + * @return the {@link Content} model for this Document + * + * @see GapContent + * @see StringContent + */ + protected final Content getContent() + { + return content; + } + + /** + * Returns the thread that currently modifies this Document + * if there is one, otherwise null. 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 Document + * if there is one, otherwise null + */ + protected final synchronized Thread getCurrentWriter() + { + return currentWriter; + } + + /** + * Returns the properties of this Document. + * + * @return the properties of this Document + */ + public Dictionary 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 + * Document. + * + * @return a {@link Position} which will always mark the end of the + * Document + */ + 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 Document's content. + * + * @return the length of this Document'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[] getListeners(Class listenerType) + { + return listenerList.getListeners(listenerType); + } + + /** + * Returns a property from this Document's property list. + * + * @param key the key of the property to be fetched + * + * @return the property for key or null 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 Document. By default + * this just returns the single root element returned by + * {@link #getDefaultRootElement()}. Document implementations + * that support multiple roots must override this method and return all roots + * here. + * + * @return all root elements of this Document + */ + 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 + * Document. + * + * @return a {@link Position} which will always mark the beginning of the + * Document + */ + 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 Document's content. + * + * @param offset the start offset of the content + * @param length the length of the content + * + * @return the piece of content specified by offset and + * length + * + * @throws BadLocationException if offset or offset + + * length are invalid locations with this + * Document + */ + public String getText(int offset, int length) throws BadLocationException + { + return content.getString(offset, length); + } + + /** + * Fetches a piece of this Document'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 Segment to store the content in + * + * @throws BadLocationException if offset or offset + + * length are invalid locations with this + * Document + */ + public void getText(int offset, int length, Segment segment) + throws BadLocationException + { + content.getChars(offset, length, segment); + } + + /** + * Inserts a String into this Document at the specified + * position and assigning the specified attributes to it. + * + *

      If a {@link DocumentFilter} is installed in this document, the + * corresponding method of the filter object is called.

      + * + *

      The method has no effect when text is null + * or has a length of zero.

      + * + * + * @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 offset is not a valid + * location in this Document + */ + 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 + * Document. The default implementation does nothing. + * This method is executed within a write lock. + * + * @param chng the DefaultDocumentEvent 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 + * Document. The default implementation does nothing. + * This method is executed within a write lock. + * + * @param chng the DefaultDocumentEvent describing the change + */ + protected void postRemoveUpdate(DefaultDocumentEvent chng) + { + if (Boolean.TRUE.equals(getProperty(I18N))) + updateBidi(chng); + } + + /** + * Stores a property in this Document'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 Document. + */ + 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 + * Document, 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 Document. + * + *

      If a {@link DocumentFilter} is installed in this document, the + * corresponding method of the filter object is called. The + * DocumentFilter is called even if length + * is zero. This is different from {@link #replace}.

      + * + *

      Note: When length is zero or below the call is not + * forwarded to the underlying {@link AbstractDocument.Content} instance + * of this document and no exception is thrown.

      + * + * @param offset the start offset of the fragment to be removed + * @param length the length of the fragment to be removed + * + * @throws BadLocationException if offset or + * offset + length 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 Document with + * another piece of content. + * + *

      If a {@link DocumentFilter} is installed in this document, the + * corresponding method of the filter object is called.

      + * + *

      The method has no effect if length is zero (and + * only zero) and, at the same time, text is + * null or has zero length.

      + * + * @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 offset or + * offset + length 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 DocumentListener object to this document. + * + * @param listener the listener to add + */ + public void addDocumentListener(DocumentListener listener) + { + listenerList.add(DocumentListener.class, listener); + } + + /** + * Removes a DocumentListener object from this document. + * + * @param listener the listener to remove + */ + public void removeDocumentListener(DocumentListener listener) + { + listenerList.remove(DocumentListener.class, listener); + } + + /** + * Returns all registered DocumentListeners. + * + * @return all registered DocumentListeners + */ + public DocumentListener[] getDocumentListeners() + { + return (DocumentListener[]) getListeners(DocumentListener.class); + } + + /** + * Adds an {@link UndoableEditListener} to this Document. + * + * @param listener the listener to add + */ + public void addUndoableEditListener(UndoableEditListener listener) + { + listenerList.add(UndoableEditListener.class, listener); + } + + /** + * Removes an {@link UndoableEditListener} from this Document. + * + * @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 Document. + * The default implementation does nothing but may be overridden by + * subclasses to modify the Document structure in response + * to a remove request. The method is executed within a write lock. + * + * @param chng the DefaultDocumentEvent describing the change + */ + protected void removeUpdate(DefaultDocumentEvent chng) + { + // Do nothing here. Subclasses may wish to override this. + } + + /** + * Called to render this Document visually. It obtains a read + * lock, ensuring that no changes will be made to the document + * during the rendering process. It then calls the {@link Runnable#run()} + * method on runnable. This method must not attempt + * to modifiy the Document, 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 Document. + * A value of -1 indicates that this Document + * 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 Document. + * + * @param p the document properties to set + */ + public void setDocumentProperties(Dictionary 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 + * Document. + * + * @return the currently installed {@link DocumentFilter} for this + * Document + * + * @since 1.4 + */ + public DocumentFilter getDocumentFilter() + { + return documentFilter; + } + + /** + * Sets the {@link DocumentFilter} for this Document. + * + * @param filter the DocumentFilter to set + * + * @since 1.4 + */ + public void setDocumentFilter(DocumentFilter filter) + { + this.documentFilter = filter; + } + + /** + * Dumps diagnostic information to the specified PrintStream. + * + * @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 + * Documents. + * + * Replicating {@link AttributeSet}s throughout a Document can + * be very expensive. Implementations of this interface are intended to + * provide intelligent management of AttributeSets, eliminating + * costly duplication. + * + * @see StyleContext + */ + public interface AttributeContext + { + /** + * Returns an {@link AttributeSet} that contains the attributes + * of old plus the new attribute specified by + * name and value. + * + * @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 old plus the new attributes in attributes. + * + * @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 old plus the new attributes in + * attributes + */ + 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 attributes 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 + * name removed from old. + * + * @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 old minus the attribute + * specified by name + */ + AttributeSet removeAttribute(AttributeSet old, Object name); + + /** + * Removes all attributes in attributes from old + * and returns the resulting AttributeSet. + * + * @param old the set of attributes from which to remove attributes + * @param attributes the attributes to be removed from old + * + * @return the attributes of old minus the attributes in + * attributes + */ + AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes); + + /** + * Removes all attributes specified by names from + * old and returns the resulting AttributeSet. + * + * @param old the set of attributes from which to remove attributes + * @param names the names of the attributes to be removed from + * old + * + * @return the attributes of old minus the attributes in + * attributes + */ + AttributeSet removeAttributes(AttributeSet old, Enumeration names); + } + + /** + * A sequence of data that can be edited. This is were the actual content + * in AbstractDocument's is stored. + */ + public interface Content + { + /** + * Creates a {@link Position} that keeps track of the location at + * offset. + * + * @return a {@link Position} that keeps track of the location at + * offset. + * + * @throw BadLocationException if offset is not a valid + * location in this Content 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 UndoableEdit or null if undo is + * not supported by this Content model + * + * @throws BadLocationException if where is not a valid + * location in this Content 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 UndoableEdit or null if undo is + * not supported by this Content model + * + * @throws BadLocationException if where is not a valid + * location in this Content 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 offset or + * offset + lenis not a valid + * location in this Content model + */ + String getString(int where, int len) throws BadLocationException; + + /** + * Fetches a piece of content and stores it in txt. + * + * @param where the start offset of the requested fragment + * @param len the length of the requested fragment + * @param txt the Segment where to fragment is stored into + * + * @throws BadLocationException if offset or + * offset + lenis not a valid + * location in this Content 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 AbstractElement with a + * specified parent Element and AttributeSet. + * + * @param p the parent of this AbstractElement + * @param s the attributes to be assigned to this + * AbstractElement + */ + 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 Element as an + * Enumeration of {@link TreeNode}s. + * + * @return the child nodes of this Element as an + * Enumeration of {@link TreeNode}s + */ + public abstract Enumeration children(); + + /** + * Returns true if this AbstractElement + * allows children. + * + * @return true if this AbstractElement + * allows children + */ + public abstract boolean getAllowsChildren(); + + /** + * Returns the child of this AbstractElement at + * index. + * + * @param index the position in the child list of the child element to + * be returned + * + * @return the child of this AbstractElement at + * index + */ + public TreeNode getChildAt(int index) + { + return (TreeNode) tree_children.get(index); + } + + /** + * Returns the number of children of this AbstractElement. + * + * @return the number of children of this AbstractElement + */ + public int getChildCount() + { + return tree_children.size(); + } + + /** + * Returns the index of a given child TreeNode or + * -1 if node is not a child of this + * AbstractElement. + * + * @param node the node for which the index is requested + * + * @return the index of a given child TreeNode or + * -1 if node is not a child of this + * AbstractElement + */ + public int getIndex(TreeNode node) + { + return tree_children.indexOf(node); + } + + /** + * Returns the parent TreeNode of this + * AbstractElement or null if this element + * has no parent. + * + * @return the parent TreeNode of this + * AbstractElement or null if this + * element has no parent + */ + public TreeNode getParent() + { + return tree_parent; + } + + /** + * Returns true if this AbstractElement is a + * leaf element, false otherwise. + * + * @return true if this AbstractElement is a + * leaf element, false 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 true 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 true if this element contains the specified + * attribute + */ + public boolean containsAttribute(Object name, Object value) + { + return attributes.containsAttribute(name, value); + } + + /** + * Returns true if this element contains all of the + * specified attributes. + * + * @param attrs the attributes to check + * + * @return true 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 key of null + * if key 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 true if an attribute with the specified name + * is defined in this element, false otherwise. + * + * @param attrName the name of the requested attributes + * + * @return true if an attribute with the specified name + * is defined in this element, false otherwise + */ + public boolean isDefined(Object attrName) + { + return attributes.isDefined(attrName); + } + + /** + * Returns true if the specified AttributeSet + * is equal to this element's AttributeSet, false + * otherwise. + * + * @param attrs the attributes to compare this element to + * + * @return true if the specified AttributeSet + * is equal to this element's AttributeSet, + * false 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 index. + * + * @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 + * Elements that contain other Elements. + */ + 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 BranchElement with the specified + * parent and attributes. + * + * @param parent the parent element of this BranchElement + * @param attributes the attributes to set on this + * BranchElement + */ + public BranchElement(Element parent, AttributeSet attributes) + { + super(parent, attributes); + children = new Element[1]; + numChildren = 0; + lastIndex = -1; + } + + /** + * Returns the children of this BranchElement. + * + * @return the children of this BranchElement + */ + 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 true since BranchElements allow + * child elements. + * + * @return true since BranchElements allow + * child elements + */ + public boolean getAllowsChildren() + { + return true; + } + + /** + * Returns the child element at the specified index. + * + * @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 NullPointerException. + * + * @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 NullPointerException. + * + * @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 false since BranchElement are no + * leafes. + * + * @return false since BranchElement are no + * leafes + */ + public boolean isLeaf() + { + return false; + } + + /** + * Returns the Element at the specified Document + * offset. + * + * @return the Element at the specified Document + * 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 Document 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 Element 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 DefaultDocumentEvent. + * + * @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 DocumentEvent. 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 elem or null if + * elem 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 ElementEdit. + * + * @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 LeafElement. + * + * @param parent the parent of this LeafElement + * @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 null since LeafElements cannot have + * children. + * + * @return null since LeafElements cannot have + * children + */ + public Enumeration children() + { + return null; + } + + /** + * Returns false since LeafElements cannot have + * children. + * + * @return false since LeafElements cannot have + * children + */ + public boolean getAllowsChildren() + { + return false; + } + + /** + * Returns null since LeafElements cannot have + * children. + * + * @return null since LeafElements cannot have + * children + */ + public Element getElement(int index) + { + return null; + } + + /** + * Returns 0 since LeafElements cannot have + * children. + * + * @return 0 since LeafElements cannot have + * children + */ + public int getElementCount() + { + return 0; + } + + /** + * Returns -1 since LeafElements cannot have + * children. + * + * @return -1 since LeafElements cannot have + * children + */ + public int getElementIndex(int offset) + { + return -1; + } + + /** + * Returns the end offset of this Element inside the + * document. + * + * @return the end offset of this Element inside the + * document + */ + public int getEndOffset() + { + return endPos.getOffset(); + } + + /** + * Returns the name of this Element. This is + * {@link #ContentElementName} in this case. + * + * @return the name of this Element + */ + public String getName() + { + String name = super.getName(); + if (name == null) + name = ContentElementName; + return name; + } + + /** + * Returns the start offset of this Element inside the + * document. + * + * @return the start offset of this Element inside the + * document + */ + public int getStartOffset() + { + return startPos.getOffset(); + } + + /** + * Returns true. + * + * @return true + */ + public boolean isLeaf() + { + return true; + } + + /** + * Returns a string representation of this Element. + * + * @return a string representation of this Element + */ + 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: + * + *
      + * 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;
      + *   }
      + * }
      + * 
      + * + * @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 cs if + * the new child state's view start offset is smaller than the start offset + * of the current child state's view or when lastValidOffset + * is null. + * + * @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 + * -1 if there is no such child view. + * + * @param x the x coordinate (relative to a) + * @param y the y coordinate (relative to a) + * @param a the current allocation of this view + * + * @return the view index of the view that occupies the specified area, or + * -1 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 before 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 not 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 ChildState 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 ChildState represents + * the layout state. + * + * @return the child view for this child state object + */ + public View getChildView() + { + return childView; + } + + /** + * Returns true if the current layout information is valid, + * false otherwise. + * + * @return true if the current layout information is valid, + * false otherwise + */ + public boolean isLayoutValid() + { + return minorValid && majorValid && childSizeValid; + } + + /** + * Performs the layout update for the child view managed by this + * ChildState. + */ + 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 true if the width preference has changed + * @param height true 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 AsyncBoxView 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 index. + * + * @param index the index of the requested child view + * + * @return the view at the specified index + */ + 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 estimatedMajorSpan 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 true if the major span should be treated as + * estimated, false 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 + * parent is not null and there have not been any + * child views initializes. + * + * @param parent the new parent view; null 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 a) + * @param y the y coordinate (relative to a) + * @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 + * index. + * + * @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 preferredSpan 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 true if the width preference has changed + * @param height true 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 null 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 ChildState object associated with the child view + * at the specified index. + * + * @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 LayoutQueue used for layouting the box view. + * This simply returns {@link LayoutQueue#getDefaultQueue()}. + * + * @return the LayoutQueue 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 ChildState 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 AttributeSet may have a resolving parent, + * that is another AttributeSet that is searched for attribute + * keys that are not stored locally in this AttributeSet. + * + * @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 + * AttributeSet. + */ + Object NameAttribute = StyleConstants.NameAttribute; + + /** + * Key of the attribute that is used to identify the resolving parent of + * an AttributeSet. + */ + Object ResolveAttribute = StyleConstants.ResolveAttribute; + + /** + * Returns true if this AttributeSet contains + * an attribute with the specified name and value, + * false otherwise. + * + * @param name the name of the requested attribute + * @param value the value of the requested attribute + * + * @return true if this AttributeSet contains + * an attribute with the specified name and + * value, false otherwise + */ + boolean containsAttribute(Object name, Object value); + + /** + * Returns true of this AttributeSet contains all + * of the specified attributes. + * + * @param attributes the requested attributes + * + * @return true of this AttributeSet contains all + * of the specified attributes + */ + boolean containsAttributes(AttributeSet attributes); + + /** + * Creates and returns a copy of this AttributeSet. + * + * @return a copy of this AttributeSet + */ + AttributeSet copyAttributes(); + + /** + * Returns the attribute with the specified key or + * null if no such attribute is defined in this + * AttributeSet and its resolving parents. + * + * @param key the key of the attribute that is looked up + * + * @return the attribute with the specified key or + * null if no such attribute is defined in this + * AttributeSet and its resolving parents + */ + Object getAttribute(Object key); + + /** + * Returns the number of attributes that are stored locally in this + * AttributeSet. + * + * @return the number of attributes that are stored locally in this + * AttributeSet + */ + int getAttributeCount(); + + /** + * Returns the names of the attributes that are stored in this + * AttributeSet. + * + * @return the names of the attributes that are stored in this + * AttributeSet + */ + Enumeration getAttributeNames(); + + /** + * Returns the resolving parent of this AttributeSet. + * If a key is not stored locally, then a {@link #getAttribute(Object)} + * request is resolved up in the resolving parent of this + * AttributeSet. + * + * @return the resolving parent of this AttributeSet + */ + AttributeSet getResolveParent(); + + /** + * Returns true if an attribute with the specified name is + * defined locally in this AttributeSet, without resolving + * through the resolving parents. + * + * @return true if an attribute with the specified name is + * defined locally in this AttributeSet + */ + boolean isDefined(Object attrName); + + /** + * Returns true if all of the attributes in attr + * are equal to the attributes in this AttributeSet, + * false otherwise. + * + * @param attr the attributes to be compared to this + * + * @return true if all of the attributes in attr + * are equal to the attributes in this AttributeSet, + * false 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 Document 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 BadLocationException + * + * @param str a string indicating what was wrong with the arguments + * @param offset offset within the document that was requested >= 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 BoxLayout + * works, but for View children. + * + * @author Roman Kennke (roman@kennke.org) + */ +public class BoxView + extends CompositeView +{ + + /** + * The axis along which this BoxView 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 BoxView for the given + * Element 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 BoxView is laid out. + * + * @return the axis along which this BoxView is laid out + * + * @since 1.3 + */ + public int getAxis() + { + return myAxis; + } + + /** + * Sets the axis along which this BoxView 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 BoxView 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 int 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 true if the layout along the specified + * axis is valid, false otherwise. + * + * Valid values for the axis are {@link View#X_AXIS} and + * {@link View#Y_AXIS}. + * + * @param axis the axis + * + * @return true if the layout along the specified + * axis is valid, false 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 View at the specified index. + * This method modifies the actual values in alloc so make + * sure you have a copy of the original values if you need them. + * + * @param g the Graphics 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 (length == 0), the result is a simple insert, if + * there are no children to add (view == null) 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 Element that is associated with this + * View. + * + * @param g the Graphics context to render to + * @param a the allocated region for the Element + */ + 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 + * View along the specified axis. + * + * @param axis the axis + * + * @return the preferred span of this View. + */ + 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 Integer.MAX_VALUE 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 SizeRequirements object to hold the result, + * if null, a new one is created + * + * @return the size requirements for this BoxView 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 + * BoxView 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 BoxView along + * its major axis, that is the axis specified in the constructor. + * + * @param axis the axis that is examined + * @param sr the SizeRequirements object to hold the result, + * if null, a new one is created + * + * @return the size requirements for this BoxView 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 BoxView 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 SizeRequirements object to hold the result, + * if null, a new one is created + * + * @return the size requirements for this BoxView 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 true if the specified point lies before the + * given Rectangle, false otherwise. + * + * "Before" 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 true if the specified point lies before the + * given Rectangle, false 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 true if the specified point lies after the + * given Rectangle, false otherwise. + * + * "After" 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 true if the specified point lies after the + * given Rectangle, false 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 View at the specified location. + * + * @param x the X coordinate + * @param y the Y coordinate + * @param r the inner allocation of this BoxView on entry, + * the allocation of the found child on exit + * + * @return the child View 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 View. The parameter + * a stores the allocation of this CompositeView + * and is then adjusted to hold the allocation of the child view. + * + * @param index + * the index of the child View + * @param a + * the allocation of this CompositeView 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 BoxView with the specified + * bounds. + * + * @param width the width of the allocated region for the children (that + * is the inner allocation of this BoxView + * @param height the height of the allocated region for the children (that + * is the inner allocation of this BoxView + */ + 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 BoxView. + * + * @param targetSpan the (inner) span of the BoxView 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 BoxView. + * + * @param targetSpan the (inner) span of the BoxView 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 true if the cached allocations for the children + * are still valid, false otherwise. + * + * @return true if the cached allocations for the children + * are still valid, false 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 X_AXIS or + * Y_AXIS + * @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 X_AXIS or + * Y_AXIS + * @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 pos 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 0 or less + * means this view is not resizeable. Positive values make the view + * resizeable. This implementation returns 0 for the major + * axis and 1 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 axis 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 + * index. If the layout is invalid, this returns + * null. + * + * @param index the child view index + * @param a the allocation to this view + * + * @return the child allocation for the child view with the specified + * index or null if the layout is invalid + * or a 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 Caret 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 Caret 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 Caret 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 Caret in milliseconds. + * A value of 0 means that the caret does not blink. + * + * @return the blink rate of this Caret or 0 if + * this caret does not blink + */ + int getBlinkRate(); + + /** + * Sets the blink rate of this Caret in milliseconds. + * A value of 0 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 Caret within the + * Document. + * + * @return the current position of this Caret within the + * Document + */ + int getDot(); + + /** + * Sets the current position of this Caret within the + * Document. This also sets the mark to the + * new location. + * + * @param dot the new position to be set + * + * @see #moveDot(int) + */ + void setDot(int dot); + + /** + * Moves the dot location without touching the + * mark. 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 mark. The + * mark marks the location in the Document that + * is the end of a selection. If there is no selection, the mark + * is the same as the dot. + * + * @return the current position of the mark + */ + int getMark(); + + /** + * Returns the current visual position of this Caret. + * + * @return the current visual position of this Caret + * + * @see #setMagicCaretPosition + */ + Point getMagicCaretPosition(); + + /** + * Sets the current visual position of this Caret. + * + * @param p the Point to use for the saved location. May be null + * to indicate that there is no visual location + */ + void setMagicCaretPosition(Point p); + + /** + * Returns true if the selection is currently visible, + * false otherwise. + * + * @return true if the selection is currently visible, + * false otherwise + */ + boolean isSelectionVisible(); + + /** + * Sets the visiblity state of the selection. + * + * @param v true if the selection should be visible, + * false otherwise + */ + void setSelectionVisible(boolean v); + + /** + * Returns true if this Caret is currently visible, + * and false if it is not. + * + * @return true if this Caret is currently visible, + * and false if it is not + */ + boolean isVisible(); + + /** + * Sets the visibility state of the caret. true shows the + * Caret, false hides it. + * + * @param v the visibility to set + */ + void setVisible(boolean v); + + /** + * Paints this Caret to the specified Graphics + * 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 + * Component that should be rendered. This Component + * becomes a direct child of the JTextComponent that contains + * this ComponentView, so this view must not be shared between + * multiple JTextComponents. + * + * @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 ComponentView for the specified + * Element. + * + * @param elem the element that this View is rendering + */ + public ComponentView(Element elem) + { + super(elem); + } + + /** + * Creates the Component that this View is + * rendering. The Component is determined using + * the {@link StyleConstants#ComponentAttribute} of the associated + * Element. + * + * @return the component that is rendered + */ + protected Component createComponent() + { + return StyleConstants.getComponent(getElement().getAttributes()); + } + + /** + * Returns the alignment of this View along the specified axis. + * + * @param axis either {@link View#X_AXIS} or {@link View#Y_AXIS} + * + * @return the alignment of this View 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 Component that is rendered by this + * ComponentView. + * + * @return the Component that is rendered by this + * ComponentView + */ + public final Component getComponent() + { + return comp; + } + + /** + * Returns the maximum span of this View along the specified + * axis. + * + * This will return {@link Component#getMaximumSize()} for the specified + * axis. + * + * @return the maximum span of this View 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 p is + * null, 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 null 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 View'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 View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + */ + 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 + * Views. + * + * @author Roman Kennke (roman@kennke.org) + */ +public abstract class CompositeView + extends View +{ + + /** + * The child views of this CompositeView. + */ + private View[] children; + + /** + * The number of child views. + */ + private int numChildren; + + /** + * The allocation of this View 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 CompositeView. This is initialized + * in {@link #setInsets}. + */ + private short top; + private short bottom; + private short left; + private short right; + + /** + * Creates a new CompositeView for the given + * Element. + * + * @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 CompositeView. 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 View. + * In addition to setting the parent, this calls {@link #loadChildren}, if + * this View 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 n. + * + * @param n the index of the requested child view + * + * @return the child view at index n + */ + public View getView(int n) + { + return children[n]; + } + + /** + * Replaces child views by some other child views. If there are no views to + * remove (length == 0), the result is a simple insert, if + * there are no children to add (view == null) 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 View. + * + * @param index the index of the child view + * @param a the allocation for this view + * + * @return the allocation for the specified child View + */ + 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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 p1 or p2 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 View'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 View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y >= 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 pos + * @param a + * the allocated region for this view + * @param direction + * the direction from the current position, can be one of the + * following: + *
        + *
      • {@link SwingConstants#WEST}
      • + *
      • {@link SwingConstants#EAST}
      • + *
      • {@link SwingConstants#NORTH}
      • + *
      • {@link SwingConstants#SOUTH}
      • + *
      + * @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 pos is not a valid location inside the document + * model + * @throws IllegalArgumentException + * if direction 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 pos + * + * @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 true if the specified point lies before the + * given Rectangle, false otherwise. + * + * "Before" 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 true if the specified point lies before the + * given Rectangle, false otherwise + */ + protected abstract boolean isBefore(int x, int y, Rectangle r); + + /** + * Returns true if the specified point lies after the + * given Rectangle, false otherwise. + * + * "After" 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 true if the specified point lies after the + * given Rectangle, false otherwise + */ + protected abstract boolean isAfter(int x, int y, Rectangle r); + + /** + * Returns the child View at the specified location. + * + * @param x the X coordinate + * @param y the Y coordinate + * @param r the inner allocation of this BoxView on entry, + * the allocation of the found child on exit + * + * @return the child View at the specified location + */ + protected abstract View getViewAtPoint(int x, int y, Rectangle r); + + /** + * Computes the allocation for a child View. The parameter + * a stores the allocation of this CompositeView + * and is then adjusted to hold the allocation of the child view. + * + * @param index the index of the child View + * @param a the allocation of this CompositeView before the + * call, the allocation of the child on exit + */ + protected abstract void childAllocation(int index, Rectangle a); + + /** + * Returns the child View that contains the given model + * position. The given Rectangle gives the parent's allocation + * and is changed to the child's allocation on exit. + * + * @param pos the model position to query the child View 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 View for the given model + * position. + * + * @param pos the model position for whicht the child View is + * queried + * + * @return the index of the child View 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 CompositeView + * minus this CompositeView'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 CompositeView + * + * @return the allocation that is given to this CompositeView + * minus this CompositeView's insets or + * null if a was null + */ + 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 attributes. 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 CompositeView. + * + * @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 CompositeView. + * + * @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 CompositeView. + * + * @return the left inset of this CompositeView + */ + protected short getLeftInset() + { + return left; + } + + /** + * Returns the right inset of this CompositeView. + * + * @return the right inset of this CompositeView + */ + protected short getRightInset() + { + return right; + } + + /** + * Returns the top inset of this CompositeView. + * + * @return the top inset of this CompositeView + */ + protected short getTopInset() + { + return top; + } + + /** + * Returns the bottom inset of this CompositeView. + * + * @return the bottom inset of this CompositeView + */ + 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 pos + * @param a the allocated region for this view + * @param direction the direction from the current position, can be one of + * the following: + *
        + *
      • {@link SwingConstants#NORTH}
      • + *
      • {@link SwingConstants#SOUTH}
      • + *
      + * @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 pos is not a valid location + * inside the document model + * @throws IllegalArgumentException if direction 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 pos + * @param a the allocated region for this view + * @param direction the direction from the current position, can be one of + * the following: + *
        + *
      • {@link SwingConstants#EAST}
      • + *
      • {@link SwingConstants#WEST}
      • + *
      + * @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 pos is not a valid location + * inside the document model + * @throws IllegalArgumentException if direction 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 pos. Usually + * the Views are laid out from the east to the west, so + * we unconditionally return false 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 pos + * + * @return true if the next View is located + * to the EAST, false 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; + +/** + * DateFormatter 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 DateFormatter using the + * specified DateFormat + * + * @param format the DateFormat to use + */ + public DateFormatter(DateFormat format) + { + super(); + setFormat(format); + } + + /** + * Sets the format that is used by this DateFormatter. + * + * @param format the DateFormat 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 null. + */ + static JTextComponent componentWithSelection; + + /** An implementation of NavigationFilter.FilterBypass which delegates + * to the corresponding methods of the DefaultCaret. + * + * @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 ChangeEvent 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 Caret. + */ + 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 Caret 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 false + * the caret is not drawn. + */ + boolean active = true; + + /** + * The current highlight entry. + */ + private Object highlightEntry; + + private Timer blinkTimer; + + private BlinkTimerListener blinkListener; + + /** + * A NavigationFilter.FilterBypass instance which + * is provided to the a NavigationFilter to + * unconditionally set or move the caret. + */ + NavigationFilter.FilterBypass bypass; + + /** + * Creates a new DefaultCaret instance. + */ + public DefaultCaret() + { + // Nothing to do here. + } + + /** Returns the caret's NavigationFilter.FilterBypass instance + * and creates it if it does not yet exist. + * + * @return The caret's NavigationFilter.FilterBypass 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. + * + *

      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.

      + * + * @param event the MouseEvent 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 MouseEvent 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: + * + *
        + *
      • 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.
      • + *
      • 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.
      • + *
      + * + * @param event the MouseEvent 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 MouseEvent 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 MouseEvent 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 MouseEvent 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 MouseEvent describing the mouse operation + */ + public void mouseReleased(MouseEvent event) + { + // Nothing to do here. + } + + /** + * Sets the caret to visible if the text component is editable. + * + * @param event the FocusEvent + */ + public void focusGained(FocusEvent event) + { + if (textComponent.isEditable()) + { + setVisible(true); + updateTimerStatus(); + } + } + + /** + * Sets the caret to invisible. + * + * @param event the FocusEvent + */ + 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 MouseEvent. + * This will cause a selection if the dot and mark are different. + * + * @param event the MouseEvent 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 + * MouseEvent. + * + * @param event the MouseEvent from which to fetch the position + */ + protected void positionCaret(MouseEvent event) + { + int newDot = getComponent().viewToModel(event.getPoint()); + setDot(newDot); + } + + /** + * Deinstalls this Caret from the specified + * JTextComponent. This removes any listeners that have been + * registered by this Caret. + * + * @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 Caret on the specified + * JTextComponent. 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 Caret. + * + * @param p the Point to use for the saved location. May be null + * to indicate that there is no visual location + */ + public void setMagicCaretPosition(Point p) + { + magicCaretPosition = p; + } + + /** + * Returns the current visual position of this Caret. + * + * @return the current visual position of this Caret + * + * @see #setMagicCaretPosition + */ + public Point getMagicCaretPosition() + { + return magicCaretPosition; + } + + /** + * Returns the current position of the mark. The + * mark marks the location in the Document that + * is the end of a selection. If there is no selection, the mark + * is the same as the dot. + * + * @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 true if the selection should be visible, + * false otherwise + */ + public void setSelectionVisible(boolean v) + { + if (selectionVisible == v) + return; + + selectionVisible = v; + handleHighlight(); + repaint(); + } + + /** + * Returns true if the selection is currently visible, + * false otherwise. + * + * @return true if the selection is currently visible, + * false otherwise + */ + public boolean isSelectionVisible() + { + return selectionVisible; + } + + /** + * Causes the Caret to repaint itself. + */ + protected final void repaint() + { + getComponent().repaint(x, y, width, height); + } + + /** + * Paints this Caret using the specified Graphics + * 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[] getListeners(Class listenerType) + { + return listenerList.getListeners(listenerType); + } + + /** + * Registers a {@link ChangeListener} that is notified whenever that state + * of this Caret 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 Caret. + * + * @return all registered {@link ChangeListener}s of this Caret + */ + public ChangeListener[] getChangeListeners() + { + return (ChangeListener[]) getListeners(ChangeListener.class); + } + + /** + * Notifies all registered {@link ChangeListener}s that the state + * of this Caret has changed. + */ + protected void fireStateChanged() + { + ChangeListener[] listeners = getChangeListeners(); + + for (int index = 0; index < listeners.length; ++index) + listeners[index].stateChanged(changeEvent); + } + + /** + * Returns the JTextComponent on which this Caret + * is installed. + * + * @return the JTextComponent on which this Caret + * is installed + */ + protected final JTextComponent getComponent() + { + return textComponent; + } + + /** + * Returns the blink rate of this Caret in milliseconds. + * A value of 0 means that the caret does not blink. + * + * @return the blink rate of this Caret or 0 if + * this caret does not blink + */ + public int getBlinkRate() + { + return blinkRate; + } + + /** + * Sets the blink rate of this Caret in milliseconds. + * A value of 0 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 Caret within the + * Document. + * + * @return the current position of this Caret within the + * Document + */ + public int getDot() + { + return dot; + } + + /** + * Moves the dot location without touching the + * mark. This is used when making a selection. + * + *

      If the underlying text component has a {@link NavigationFilter} + * installed the caret will call the corresponding method of that object.

      + * + * @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 Caret within the + * Document. This also sets the mark to the new + * location. + * + *

      If the underlying text component has a {@link NavigationFilter} + * installed the caret will call the corresponding method of that object.

      + * + * @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 true if this Caret is blinking, + * and false if not. The returned value is independent of + * the visiblity of this Caret as returned by {@link #isVisible()}. + * + * @return true if this Caret is blinking, + * and false if not. + * @see #isVisible() + * @since 1.5 + */ + public boolean isActive() + { + if (blinkTimer != null) + return blinkTimer.isRunning(); + + return false; + } + + /** + * Returns true if this Caret is currently visible, + * and false if it is not. + * + * @return true if this Caret is currently visible, + * and false if it is not + */ + public boolean isVisible() + { + return visible && active; + } + + /** + * Sets the visibility state of the caret. true shows the + * Caret, false 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 EditorKit + * a plain text Document 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 BeepAction. + */ + public BeepAction() + { + super(beepAction); + } + + /** + * Performs the Action. + * + * @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 CopyAction. + */ + public CopyAction() + { + super(copyAction); + } + + /** + * Performs the Action. + * + * @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 CutAction. + */ + public CutAction() + { + super(cutAction); + } + + /** + * Performs the Action. + * + * @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 PasteAction. + */ + public PasteAction() + { + super(pasteAction); + } + + /** + * Performs the Action. + * + * @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 DefaultKeyTypedAction. + */ + public DefaultKeyTypedAction() + { + super(defaultKeyTypedAction); + } + + /** + * Performs the Action. + * + * @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 InsertBreakAction. + */ + public InsertBreakAction() + { + super(insertBreakAction); + } + + /** + * Performs the Action. + * + * @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 InsertContentAction. + */ + public InsertContentAction() + { + super(insertContentAction); + } + + /** + * Performs the Action. + * + * @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 TabAction. + */ + public InsertTabAction() + { + super(insertTabAction); + } + + /** + * Performs the Action. + * + * @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 Action that moves the caret one character + * backwards. + * + * @see #getActions() + */ + public static final String backwardAction = "caret-backward"; + + /** + * The name of the Action that creates a beep in the speaker. + * + * @see #getActions() + */ + public static final String beepAction = "beep"; + + /** + * The name of the Action that moves the caret to the beginning + * of the Document. + * + * @see #getActions() + */ + public static final String beginAction = "caret-begin"; + + /** + * The name of the Action 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 Action 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 Action 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 Action that copies the selected content + * into the system clipboard. + * + * @see #getActions() + */ + public static final String copyAction = "copy-to-clipboard"; + + /** + * The name of the Action 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 Action 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 Action that deletes the character that + * follows the current caret position. + * + * @see #getActions() + */ + public static final String deleteNextCharAction = "delete-next"; + + /** + * The name of the Action that deletes the character that + * precedes the current caret position. + * + * @see #getActions() + */ + public static final String deletePrevCharAction = "delete-previous"; + + /** + * The name of the Action that moves the caret one line down. + * + * @see #getActions() + */ + public static final String downAction = "caret-down"; + + /** + * The name of the Action that moves the caret to the end + * of the Document. + * + * @see #getActions() + */ + public static final String endAction = "caret-end"; + + /** + * The name of the Action 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 "\r\n". + */ + public static final String EndOfLineStringProperty = "__EndOfLine__"; + + /** + * The name of the Action 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 Action 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 Action that moves the caret one character + * forward. + * + * @see #getActions() + */ + public static final String forwardAction = "caret-forward"; + + /** + * The name of the Action that inserts a line break. + * + * @see #getActions() + */ + public static final String insertBreakAction = "insert-break"; + + /** + * The name of the Action that inserts some content. + * + * @see #getActions() + */ + public static final String insertContentAction = "insert-content"; + + /** + * The name of the Action that inserts a TAB. + * + * @see #getActions() + */ + public static final String insertTabAction = "insert-tab"; + + /** + * The name of the Action 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 Action that moves the caret one page down. + * + * @see #getActions() + */ + public static final String pageDownAction = "page-down"; + + /** + * The name of the Action that moves the caret one page up. + * + * @see #getActions() + */ + public static final String pageUpAction = "page-up"; + + /** + * The name of the Action that copies content from the system + * clipboard into the document. + * + * @see #getActions() + */ + public static final String pasteAction = "paste-from-clipboard"; + + /** + * The name of the Action 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 Action that sets the editor in read only + * mode. + * + * @see #getActions() + */ + public static final String readOnlyAction = "set-read-only"; + + /** + * The name of the Action that selects the whole document. + * + * @see #getActions() + */ + public static final String selectAllAction = "select-all"; + + /** + * The name of the Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action 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 Action that selects the line around the + * caret. + * + * @see #getActions() + */ + public static final String selectLineAction = "select-line"; + + /** + * The name of the Action that selects the paragraph around the + * caret. + * + * @see #getActions() + */ + public static final String selectParagraphAction = "select-paragraph"; + + /** + * The name of the Action that selects the word around the + * caret. + * + * @see #getActions() + */ + public static final String selectWordAction = "select-word"; + + /** + * The name of the Action that moves the caret one line up. + * + * @see #getActions() + */ + public static final String upAction = "caret-up"; + + /** + * The name of the Action that sets the editor in read-write + * mode. + * + * @see #getActions() + */ + public static final String writableAction = "set-writable"; + + /** + * Creates a new DefaultEditorKit. + */ + public DefaultEditorKit() + { + // Nothing to do here. + } + + /** + * The Actions that are supported by the + * DefaultEditorKit. + */ + 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 Caret for this EditorKit. This + * returns a {@link DefaultCaret} in this case. + * + * @return the Caret for this EditorKit + */ + public Caret createCaret() + { + return new DefaultCaret(); + } + + /** + * Creates the default {@link Document} that this EditorKit + * supports. This is a {@link PlainDocument} in this case. + * + * @return the default {@link Document} that this EditorKit + * supports + */ + public Document createDefaultDocument() + { + return new PlainDocument(); + } + + /** + * Returns the Actions supported by this EditorKit. + * + * @return the Actions supported by this EditorKit + */ + public Action[] getActions() + { + return defaultActions; + } + + /** + * Returns the content type that this EditorKit supports. + * The DefaultEditorKit supports the content type + * text/plain. + * + * @return the content type that this EditorKit supports + */ + public String getContentType() + { + return "text/plain"; + } + + /** + * Returns a {@link ViewFactory} that is able to create {@link View}s for + * the Elements that are used in this EditorKit's + * model. This returns null which lets the UI of the text component supply + * Views. + * + * @return a {@link ViewFactory} that is able to create {@link View}s for + * the Elements that are used in this + * EditorKit'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 offset is an invalid location + * inside document + * @throws IOException if something goes wrong while reading from + * in + */ + 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 offset is an invalid location + * inside document + * @throws IOException if something goes wrong while reading from + * in + */ + 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 Document (or a fragment of the + * Document) 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 offset or + * offset + lenis an invalid location inside + * document + * @throws IOException if something goes wrong while writing to + * out + */ + public void write(OutputStream out, Document document, int offset, int len) + throws BadLocationException, IOException + { + write(new OutputStreamWriter(out), document, offset, len); + } + + /** + * Writes the Document (or a fragment of the + * Document) 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 offset is an + * invalid location inside document. + * @throws IOException if something goes wrong while writing to + * out + */ + 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 DefaultFormatter 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 commitsOnValidEdit 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 + * commitsOnValidEdit is set to true. + */ + 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 false, 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 true newly inserted characters overwrite existing + * values, otherwise insertion is done the normal way. + */ + boolean overwriteMode; + + /** + * If true invalid edits are allowed for a limited + * time. + */ + boolean allowsInvalid; + + /** + * The class that is used for values. + */ + Class valueClass; + + /** + * Creates a new instance of DefaultFormatter. + */ + public DefaultFormatter() + { + commitsOnValidEdit = false; + overwriteMode = true; + allowsInvalid = true; + } + + /** + * Installs the formatter on the specified {@link JFormattedTextField}. + * + * This method does the following things: + *
        + *
      • Display the value of #valueToString in the + * JFormattedTextField
      • + *
      • Install the Actions from #getActions on the JTextField + *
      • + *
      • Install the DocumentFilter returned by #getDocumentFilter
      • + *
      • Install the NavigationFilter returned by #getNavigationFilter
      • + *
      + * + * 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 true if the value should be committed after + * each valid modification of the input field, false if + * it should never be committed by this formatter. + * + * @return the state of the commitsOnValidEdit property + * + * @see #setCommitsOnValidEdit + */ + public boolean getCommitsOnValidEdit() + { + return commitsOnValidEdit; + } + + /** + * Sets the value of the commitsOnValidEdit property. + * + * @param commitsOnValidEdit the new state of the + * commitsOnValidEdit property + * + * @see #getCommitsOnValidEdit + */ + public void setCommitsOnValidEdit(boolean commitsOnValidEdit) + { + this.commitsOnValidEdit = commitsOnValidEdit; + } + + /** + * Returns the value of the overwriteMode property. + * If that is set to true then newly inserted characters + * overwrite existing values, otherwise the characters are inserted like + * normal. The default is true. + * + * @return the value of the overwriteMode property + */ + public boolean getOverwriteMode() + { + return overwriteMode; + } + + /** + * Sets the value of the overwriteMode property. + * + * If that is set to true then newly inserted characters + * overwrite existing values, otherwise the characters are inserted like + * normal. The default is true. + * + * @param overwriteMode the new value for the overwriteMode + * 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 allowsInvalid 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 nullFormatter + * 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 + * nullFormatter 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 nullFormatter 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 nullFormatter 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 + * nullFormatter 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 nullFormatter 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 + * nullFormatter 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 displayFormatter. 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 nullFormatter 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 nullFormatter has been + * specified. + */ + public AbstractFormatter getDisplayFormatter() + { + return displayFormatter; + } + + /** + * Sets the displayFormatter. 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 nullFormatter has been + * specified. + * @param displayFormatter the formatter to use. + */ + public void setDisplayFormatter(AbstractFormatter displayFormatter) + { + this.displayFormatter = displayFormatter; + } + + /** + * Gets the editFormatter. 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 nullFormatter 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 editFormatter. 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 nullFormatter 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 nullFormatter. 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 + * tf. If tf is null we return null, otherwise + * we return one of the following: + * 1. Returns nullFormatter if tf.getValue() is + * null and nullFormatter is not. + * 2. Returns editFormatter if tf.hasFocus() is + * true and editFormatter is not null. + * 3. Returns displayFormatter if tf.hasFocus() is + * false and displayFormatter is not null. + * 4. Otherwise returns defaultFormatter. + */ + 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 AttributeUndoableEdit. + * + * @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 copy field is set as + * attributes on element. + */ + 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 newAttributes to the + * element's attribute set, possibly clearing all attributes + * if isReplacing 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 Element 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 ElementSpec 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 ElementSpec that specifies the length but + * not the offset of an element. Such ElementSpecs 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 ElementSpec 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 txt 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 ElementSpec + * describing the type, direction and length of this + * ElementSpec. + * + * @return a String representation of this ElementSpec + */ + 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 structural changes to the Element + * 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 ElementBuffer for the specified + * root element. + * + * @param root + * the root element for this ElementBuffer + */ + public ElementBuffer(Element root) + { + this.root = root; + } + + /** + * Returns the root element of this ElementBuffer. + * + * @return the root element of this ElementBuffer + */ + 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 + * DefaultDocumentEvent 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 DefaultDocumentEvent 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 clonee + * 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 Element 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 + * "section". + * + * @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 EditorBuffer that is used to manage to + * Element hierarchy. + */ + protected DefaultStyledDocument.ElementBuffer buffer; + + /** + * Listens for changes on this document's styles and notifies styleChanged(). + */ + private StyleChangeListener styleChangeListener; + + /** + * Creates a new DefaultStyledDocument. + */ + public DefaultStyledDocument() + { + this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext()); + } + + /** + * Creates a new DefaultStyledDocument that uses the specified + * {@link StyleContext}. + * + * @param context + * the StyleContext to use + */ + public DefaultStyledDocument(StyleContext context) + { + this(new GapContent(BUFFER_SIZE_DEFAULT), context); + } + + /** + * Creates a new DefaultStyledDocument that uses the specified + * {@link StyleContext} and {@link Content} buffer. + * + * @param content + * the Content buffer to use + * @param context + * the StyleContext 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 parent style, if one is specified. While it + * is legal to add nameless styles (nm == nullnull if the style should + * be unnamed + * @param parent the parent in which unspecified style attributes are + * resolved, or null if that is not necessary + * + * @return the newly created Style + */ + 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 Document. + * + * @return the default root element for this kind of Document + */ + 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 Element that corresponds to the character at the + * specified position. + * + * @param position + * the position of which we query the corresponding + * Element + * @return the Element 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 Style for the specified position. + * + * @param position + * the position from which to query to logical style + * @return the logical Style 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 + * position >= endIndex or the first paragraph if + * position < startIndex. + * + * @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 Style. + * + * @param nm + * the name of the Style + * @return the found Style of null if no such + * Style exists + */ + public Style getStyle(String nm) + { + StyleContext context = (StyleContext) getAttributeContext(); + return context.getStyle(nm); + } + + /** + * Removes a named Style from the style hierarchy. + * + * @param nm + * the name of the Style to be removed + */ + public void removeStyle(String nm) + { + StyleContext context = (StyleContext) getAttributeContext(); + context.removeStyle(nm); + } + + /** + * Sets text attributes for the fragment specified by offset + * and length. + * + * @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 true, 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 true, 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 DocumentEvent 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 DefaultStyledDocument 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 key 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 offset with + * a length of length. + * + * @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 offset with + * a length of length + * + * @throws BadLocationException if offset or length + * are no valid locations in the document content + */ + String getText(int offset, int length) + throws BadLocationException; + + /** + * Fetch the textual content starting at offset with + * a length of length and store it in txt. + * + * @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 offset or length + * 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 + * offset. + * + * @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 offset + * 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 offs or len + * 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 el. + * + * @param el the base element + * + * @returnthe deepest leaf of the element el + */ + 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. + * + *

      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.

      + */ + 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 shape 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 View that can flows it's children into it's layout space. + * + * The FlowView 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 FlowView + * into the real views. + */ + public static class FlowStrategy + { + /** + * Creates a new instance of FlowStragegy. + */ + public FlowStrategy() + { + // Nothing to do here. + } + + /** + * Receives notification from a FlowView that some content + * has been inserted into the document at a location that the + * FlowView 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 FlowView that some content + * has been removed from the document at a location that the + * FlowView 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 FlowView that some attributes + * have changed in the document at a location that the + * FlowView 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 FlowView. + * + * @param fv the flow view for which to return the logical view + * + * @return the logical view of the managed FlowView + */ + 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 null (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 + * startOffset. 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 null 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 view and all of its + * decendents to the parent (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 View 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 FlowView. + */ + 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 FlowView 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 LogicalView + * 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 FlowStrategy to use for translating between the + * logical and physical view. + */ + protected FlowStrategy strategy; + + /** + * Creates a new FlowView for the given + * Element and axis. + * + * @param element the element that is rendered by this FlowView + * @param axis the axis along which the view is tiled, either + * View.X_AXIS or View.Y_AXIS, 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 FlowView 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 View 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 View is + * queried + * + * @return the index of the child View 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 BoxView 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 SizeRequirements object to hold the result, + * if null, a new one is created + * + * @return the size requirements for this BoxView 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 GapContent. + */ + 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 where 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 where 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 where or + * where + len 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 where or + * where + len 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 Positions that are in the range specified by + * offset and
      length within the buffer array. + * + * @param v the vector to use; if null, 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 Position that have an offset of 0, + * to also have an array index of 0. This might be necessary + * after a call to shiftGap(0), since then the marks at offset + * 0 get shifted to gapEnd. + */ + 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 < sign, the gapEnd is marked by a > + * 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 o in list + * l. 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 + * Collections.binarySearch(). + * + * @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 Element it is responsible for using + * the style information from that Element. + * + * @author Roman Kennke (roman@kennke.org) + */ +public class GlyphView extends View implements TabableView, Cloneable +{ + + /** + * An abstract base implementation for a glyph painter for + * GlyphView. + */ + public abstract static class GlyphPainter + { + /** + * Creates a new GlyphPainer. + */ + 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 p0 + * and this offset fits within the span starting at x with + * the length of len. + * + * @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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 p0 to + * location p1. If te is not null, + * then TABs are expanded using this TabExpander. + * The parameter x 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 p0 to + * location p1, 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 p + * @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 pos is an invalid model + * location + * @throws IllegalArgumentException if d 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 GlyphPainter used in GlyphView. + */ + 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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 p0 to + * location p1. If te is not null, + * then TABs are expanded using this TabExpander. + * The parameter x 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 p0 to + * location p1, 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 + * GlyphPainter. + * + * @param v the glyph view + * + * @return the ascent of the text run that is rendered by this + * GlyphPainter + * + * @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 + * GlyphPainter. + * + * @param v the glyph view + * + * @return the descent of the text run that is rendered by this + * GlyphPainter + * + * @see FontMetrics#getDescent() + */ + public float getDescent(GlyphView v) + { + updateFontMetrics(v); + return fontMetrics.getDescent(); + } + + /** + * Determines the model offset, so that the text between p0 + * and this offset fits within the span starting at x with + * the length of len. + * + * @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 GlyphView for the given Element. + * + * @param element the element that is rendered by this GlyphView + */ + public GlyphView(Element element) + { + super(element); + offset = 0; + length = 0; + } + + /** + * Returns the GlyphPainter that is used by this + * GlyphView. If no GlyphPainer has been installed + * null is returned. + * + * @return the glyph painter that is used by this + * glyph view or null if no glyph painter has been + * installed + */ + public GlyphPainter getGlyphPainter() + { + return glyphPainter; + } + + /** + * Sets the {@link GlyphPainter} to be used for this GlyphView. + * + * @param painter the glyph painter to be used for this glyph view + */ + public void setGlyphPainter(GlyphPainter painter) + { + glyphPainter = painter; + } + + /** + * Checks if a GlyphPainer 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 Element that is associated with this + * View. + * + * @param g the Graphics context to render to + * @param a the allocated region for the Element + */ + 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 + * View along the specified axis. + * + * @param axis the axis + * + * @return the preferred span of this View. + */ + 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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 View'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 View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + */ + 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 GlyphView + * is responsible. + * + * @return the font for the text run for which this GlyphView + * 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 len. + * 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 this 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 axis is View.Y_AXIS, then + * this method forwards to the superclass, if axis is + * View.X_AXIS 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 p0 and + * ends at p1. + * + * @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 (height - descent) / height 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 p + * @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 pos is an invalid model + * location + * @throws IllegalArgumentException if d 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 JTextPane 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 IconView for the given Element. + * + * @param element the element that is rendered by this IconView + */ + public IconView(Element element) + { + super(element); + } + + /** + * Renders the Element that is associated with this + * View. + * + * @param g the Graphics context to render to + * @param a the allocated region for the Element + */ + 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 + * View along the specified axis. + * + * @param axis the axis + * + * @return the preferred span of this View. + */ + 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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 View'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 View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + */ + 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 minValue is null, then the Formatter has no restrictions + * at the lower end. + * + * If value class is not yet specified and minValue is not + * null, then valueClass 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 null value means that there is no restriction. + * + * @return the minimal value that is allowed by this Formatter or + * null 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 maxValue is null, then the Formatter has no restrictions + * at the upper end. + * + * If value class is not yet specified and maxValue is not + * null, then valueClass 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 null value means that there is no restriction. + * + * @return the maximal value that is allowed by this Formatter or + * null if there is no restriction + */ + public Comparable getMaximum() + { + return maximum; + } + + /** + * Installs the formatter on the specified {@link JFormattedTextField}. + * + * This method does the following things: + *
        + *
      • Display the value of #valueToString in the + * JFormattedTextField
      • + *
      • Install the Actions from #getActions on the JTextField + *
      • + *
      • Install the DocumentFilter returned by #getDocumentFilter
      • + *
      • Install the NavigationFilter returned by #getNavigationFilter
      • + *
      + * + * 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 Format 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 Format object. + * + * This implementation differs slightly from {@link DefaultFormatter}, + * it does: + *
        + *
      1. Convert the string to an Object using the + * Formatter.
      2. + *
      3. If a valueClass 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.
      4. + *
      5. If no {@link ParseException} has been thrown so far, we check if the + * value exceeds either minimum or maximum if + * one of those has been specified and throw a ParseException + * if it does.
      6. + *
      7. Return the value.
      8. + *
      + * + * 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 offset 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: If + * getSupportsIncrement returns true, this returns two + * Actions suitable for incrementing/decrementing the value. + * The questsion is, which method getSupportsIncrement? + * 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 KeyBinding instance. + * + * @param key a KeyStroke value + * @param actionName a String value + */ + public KeyBinding(KeyStroke key, String actionName) + { + this.key = key; + this.actionName = actionName; + } + } + + /** + * According to this + * report, 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. + * + *

      A little bit of experimentation with these classes reveals the following + * structure: + * + *

        + * + *
      • KeymapWrapper extends {@link InputMap} and holds a reference to + * the underlying {@link Keymap}.
      • + * + *
      • KeymapWrapper maps {@link KeyStroke} objects to {@link Action} + * objects, by delegation to the underlying {@link Keymap}.
      • + * + *
      • KeymapActionMap extends {@link ActionMap} also holds a reference to + * the underlying {@link Keymap} but only appears to use it for listing + * its keys.
      • + * + *
      • KeymapActionMap maps all {@link Action} objects to + * themselves, whether they exist in the underlying {@link + * Keymap} or not, and passes other objects to the parent {@link + * ActionMap} for resolving. + * + *
      + */ + + 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 not return the default action if lookup fails. + * + * @param key The KeyStroke to look up an Action for. + * + * @return The mapping for key, or null + * 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 + * null 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 null, + * in which case the new Keymap will not be added to the global + * Keymap table. The parent may also be null, which is + * harmless. + * + * @param n The name of the new Keymap, or null + * @param parent The parent of the new Keymap, or null + * + * @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 "keymap". + * + * @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 + * b, if there exists a provided action a such + * that a.getValue(Action.NAME) == b.ActionName then an + * entry is added to the Keymap mapping b to + * a. + * + * @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 + * getUI().getEditorKit().getActions(). 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 JTextComponent 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 AccessibleContext of this object. + * + * @return an AccessibleContext 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 Caret object used in this text component. + * + * @return the caret object + */ + public Caret getCaret() + { + return caret; + } + + /** + * Sets a new Caret for this text component. + * + * @param newCaret the new Caret 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 CaretListener object to this text component. + * + * @param listener the listener to add + */ + public void addCaretListener(CaretListener listener) + { + listenerList.add(CaretListener.class, listener); + } + + /** + * Removed a CaretListener object from this text component. + * + * @param listener the listener to remove + */ + public void removeCaretListener(CaretListener listener) + { + listenerList.remove(CaretListener.class, listener); + } + + /** + * Returns all added CaretListener objects. + * + * @return an array of listeners + */ + public CaretListener[] getCaretListeners() + { + return (CaretListener[]) getListeners(CaretListener.class); + } + + /** + * Notifies all registered CaretListener 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 InputListener object to this text component. + * + * @param listener the listener to add + */ + public void addInputMethodListener(InputMethodListener listener) + { + listenerList.add(InputMethodListener.class, listener); + } + + /** + * Removes an InputListener object from this text component. + * + * @param listener the listener to remove + */ + public void removeInputMethodListener(InputMethodListener listener) + { + listenerList.remove(InputMethodListener.class, listener); + } + + /** + * Returns all added InputMethodListener 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 GlyphView for the given Element. + * + * @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 null + * means the background of the parent view should shine through. + * + * @param bg the background to set or null + * + * @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 true if the glyphs are rendered underlined, + * false otherwise. + * + * @return true if the glyphs are rendered underlined, + * false otherwise + */ + public boolean isUnderline() + { + if (! valid) + setPropertiesFromAttributes(); + return underline; + } + + /** + * Sets the underline flag. + * + * @param flag true if the glyphs are rendered underlined, + * false otherwise + */ + protected void setUnderline(boolean flag) + { + underline = flag; + } + + /** + * Returns true if the glyphs are rendered as subscript, + * false otherwise. + * + * @return true if the glyphs are rendered as subscript, + * false otherwise + */ + public boolean isSubscript() + { + if (! valid) + setPropertiesFromAttributes(); + return subscript; + } + + /** + * Sets the subscript flag. + * + * @param flag true if the glyphs are rendered as subscript, + * false otherwise + */ + protected void setSubscript(boolean flag) + { + subscript = flag; + } + + /** + * Returns true if the glyphs are rendered as superscript, + * false otherwise. + * + * @return true if the glyphs are rendered as superscript, + * false otherwise + */ + public boolean isSuperscript() + { + if (! valid) + setPropertiesFromAttributes(); + return superscript; + } + + /** + * Sets the superscript flag. + * + * @param flag true if the glyphs are rendered as superscript, + * false otherwise + */ + protected void setSuperscript(boolean flag) + { + superscript = flag; + } + + /** + * Returns true if the glyphs are rendered strike-through, + * false otherwise. + * + * @return true if the glyphs are rendered strike-through, + * false otherwise + */ + public boolean isStrikeThrough() + { + if (! valid) + setPropertiesFromAttributes(); + return strikeThrough; + } + + /** + * Sets the strike-through flag. + * + * @param flag true if the glyphs are rendered strike-through, + * false 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 + * invalidCharacters 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 + * validCharacters 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 mask 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 value 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 convert 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 value 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 name and value + * to the set. If the set already contains an attribute with the given + * name, the attribute value is updated. + * + * @param name the attribute name (null not permitted). + * @param value the value (null not permitted). + * + * @throws NullPointerException if either argument is null. + */ + void addAttribute(Object name, Object value); + + /** + * Adds all the attributes from attributes to this set. + * + * @param attributes the set of attributes to add (null not + * permitted). + * + * @throws NullPointerException if attributes is + * null. + */ + void addAttributes(AttributeSet attributes); + + /** + * Removes the attribute with the specified name, 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 (null not permitted). + * + * @throws NullPointerException if name is null. + */ + void removeAttribute(Object name); + + /** + * Removes the attributes listed in names. + * + * @param names the attribute names (null not permitted). + * + * @throws NullPointerException if names is null + * or contains any null 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 + * attributes has the same resolving parent as this set, the + * parent will be removed from this set. + * + * @param attributes the attributes (null 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 (null not permitted). + * + * @throws NullPointerException if parent is null. + */ + 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 pos + * @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 pos 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; + +/** + * NumberFormatter 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 BoxView that represents exactly + * one row in a ParagraphView. + */ + class Row extends BoxView + { + /** + * Creates a new instance of Row. + */ + 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 ParagraphView for the given + * Element. + * + * @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 string. 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 string + */ + 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 + * startOffset and endOffset. 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 startOffset and + * endOffset + */ + 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 + * 0.0F 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 Graphics 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 Graphics 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 Graphics 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 + * 'filterNewLines' is set to Boolean.TRUE, 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 baseline 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 p0 or p1 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 tabStop 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 View'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 View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + */ + 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 Segment. + */ + public Segment() + { + // Nothing to do here. + } + + /** + * Creates a new Segment. + * + * @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 position, or + * {@link #DONE} if position is equal to + * {@link #getEndIndex()}. + * + * @throws IllegalArgumentException if position 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 String containing the same characters as this + * Segment. + * + * @return A String containing the same characters as this + * Segment. + */ + 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 SimpleAttributeSet with the same attributes + * and resolve parent as the specified set. + * + * @param a the attributes (null not permitted). + * + * @throws NullPointerException if a is null. + */ + public SimpleAttributeSet(AttributeSet a) + { + tab = new Hashtable(); + addAttributes(a); + } + + /** + * Adds an attribute with the given name and value + * to the set. If the set already contains an attribute with the given + * name, the attribute value is updated. + * + * @param name the attribute name (null not permitted). + * @param value the value (null not permitted). + * + * @throws NullPointerException if either argument is null. + */ + public void addAttribute(Object name, Object value) + { + tab.put(name, value); + } + + /** + * Adds all the attributes from attributes to this set. + * + * @param attributes the set of attributes to add (null not + * permitted). + * + * @throws NullPointerException if attributes is + * null. + */ + 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 true of this AttributeSet contains all + * of the specified attributes. + * + * @param attributes the requested attributes + * + * @return true of this AttributeSet contains all + * of the specified attributes + */ + 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 AttributeSet. + * + * @return a copy of this AttributeSet + */ + public AttributeSet copyAttributes() + { + return (AttributeSet) clone(); + } + + /** + * Checks this set for equality with an arbitrary object. + * + * @param obj the object (null permitted). + * + * @return true if this set is equal to obj, and + * false otherwise. + */ + public boolean equals(Object obj) + { + return + (obj instanceof AttributeSet) + && this.isEqual((AttributeSet) obj); + } + + /** + * Returns the value of the specified attribute, or null 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 (null not permitted). + * + * @throws NullPointerException if name is null. + */ + 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 null). + * + * @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 true if the given attribute is defined in this set, + * and false otherwise. The parent attribute set is not + * checked. + * + * @param attrName the attribute name (null not permitted). + */ + public boolean isDefined(Object attrName) + { + return tab.containsKey(attrName); + } + + /** + * Returns true if the set contains no attributes, and + * false otherwise. Note that the resolving parent is + * stored as an attribute, so this method will return false if + * a resolving parent is set. + * + * @return true if the set contains no attributes, and + * false otherwise. + */ + public boolean isEmpty() + { + return tab.isEmpty(); + } + + /** + * Returns true if the given set has the same number of attributes + * as this set and containsAttributes(attr) returns + * true. + * + * @param attr the attribute set (null not permitted). + * + * @return A boolean. + * + * @throws NullPointerException if attr is null. + */ + public boolean isEqual(AttributeSet attr) + { + return getAttributeCount() == attr.getAttributeCount() + && this.containsAttributes(attr); + } + + /** + * Removes the attribute with the specified name, 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 (null not permitted). + * + * @throws NullPointerException if name is null. + */ + 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 + * attributes has the same resolving parent as this set, the + * parent will be removed from this set. + * + * @param attributes the attributes (null 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 names. + * + * @param names the attribute names (null not permitted). + * + * @throws NullPointerException if names is null + * or contains any null 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. + *

      + * 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 (null not permitted). + * + * @throws NullPointerException if parent is null. + * + * @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 AbstractDocument.Content + * interface useful for small documents or debugging. The character + * content is a simple character array. It's not really efficient. + * + *

      Do not use this class for large size.

      + */ +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 initialLength + * 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 str at the given position and returns an + * {@link UndoableEdit} that enables undo/redo support. + * + * @param where the insertion point (must be less than + * length()). + * @param str the string to insert (null 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 String 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 txt 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 (null not + * permitted). + * + * @throws BadLocationException if the requested character range is not + * within the bounds of the content. + * @throws NullPointerException if txt is null. + */ + 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 ChangeListener object to the style. + * + * @param listener the listener object to add + */ + void addChangeListener(ChangeListener listener); + + /** + * Removes a ChangeListener 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 (null 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 (null 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 + * 0 if no bidi level is specified. + * + * @param a the attribute set (null 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 + * null if no component is specified. + * + * @param a the attribute set (null not permitted). + * + * @return The component (possibly null). + * + * @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 + * 0.0f if no indentation is specified. + * + * @param a the attribute set (null 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 + * Monospaced if no font family is specified. + * + * @param a the attribute set (null 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 + * 12 if no font size is specified. + * + * @param a the attribute set (null 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 (null 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 + * null if no icon is specified. + * + * @param a the attribute set (null not permitted). + * + * @return The icon (possibly null). + * + * @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 + * 0.0f if no left indentation is specified. + * + * @param a the attribute set (null 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 + * 0.0f if no line spacing is specified. + * + * @param a the attribute set (null 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 + * 0.0f if no right indentation is specified. + * + * @param a the attribute set (null 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 + * 0.0f if no 'space above' is specified. + * + * @param a the attribute set (null 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 + * 0.0f if no 'space below' is specified. + * + * @param a the attribute set (null 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 + * null if no tab set is specified. + * + * @param a the attribute set (null 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 + * false if no bold flag is specified. + * + * @param a the attribute set (null 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 + * false if no italic flag is specified. + * + * @param a the attribute set (null 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 + * false if no strike-through flag is specified. + * + * @param a the attribute set (null 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 + * false if no subscript flag is specified. + * + * @param a the attribute set (null 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 + * false if no superscript flag is specified. + * + * @param a the attribute set (null 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 + * false if no underline flag is specified. + * + * @param a the attribute set (null 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 (null 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 a is null. + * + * @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 (null not permitted). + * @param bg the background (null not permitted). + * + * @throws NullPointerException if either argument is null. + * + * @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 (null not permitted). + * @param lev the level. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param b the new value of the bold attribute. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param c the component (null not permitted). + * + * @throws NullPointerException if either argument is null. + * + * @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 (null not permitted). + * @param i the indentation. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param fam the font family name (null not permitted). + * + * @throws NullPointerException if either argument is null. + * + * @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 (null not permitted). + * @param s the font size (in points). + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param fg the foreground color (null not permitted). + * + * @throws NullPointerException if either argument is null. + * + * @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 (null not permitted). + * @param c the icon (null not permitted). + * + * @throws NullPointerException if either argument is null. + * + * @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 (null not permitted). + * @param b the new value of the italic attribute. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param i the indentation. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param i the line spacing. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param i the right indentation. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param i the space above attribute value. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param i the space below attribute value. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param b the strike-through attribute value. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param b the subscript attribute value. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param b the superscript attribute value. + * + * @throws NullPointerException if a is null. + * + * @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 (null not permitted). + * @param tabs the tab set (null not permitted). + * + * @throws NullPointerException if either argument is null. + * + * @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 (null not permitted). + * @param b the underline attribute value. + * + * @throws NullPointerException if a is null. + * + * @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[] getListeners(Class listenerType) + { + return listenerList.getListeners(listenerType); + } + + public ChangeListener[] getChangeListeners() + { + return (ChangeListener[]) getListeners(ChangeListener.class); + } + + protected void fireStateChanged() + { + ChangeListener[] listeners = getChangeListeners(); + for (int i = 0; i < listeners.length; ++i) + { + // 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 + * null 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 StyleContext 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 UnderlineAction. + */ + public UnderlineAction() + { + super("font-underline"); + } + + /** + * Performs the action. + * + * @param event the ActionEvent 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 ItalicAction. + */ + public ItalicAction() + { + super("font-italic"); + } + + /** + * Performs the action. + * + * @param event the ActionEvent 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 BoldAction. + */ + public BoldAction() + { + super("font-bold"); + } + + /** + * Performs the action. + * + * @param event the ActionEvent 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 AlignmentAction to set the + * alignment to a. + * + * @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 ActionEvent 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 ForegroundAction to set the + * foreground color to fg. + * + * @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 ActionEvent 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 FontSizeAction to set the + * font size to size. + * + * @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 ActionEvent 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 FontFamilyAction to set the + * font family to family. + * + * @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 ActionEvent 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 StyledTextAction. + * + * @param nm the name of the StyledTextAction + */ + public StyledTextAction(String nm) + { + super(nm); + } + + /** + * Returns the JEditorPane component from which the + * ActionEvent originated. + * + * @param event the ActionEvent + * @return the JEditorPane component from which the + * ActionEvent originated + */ + protected final JEditorPane getEditor(ActionEvent event) + { + return (JEditorPane) getTextComponent(event); + } + + /** + * Sets the specified character attributes on the currently selected + * text of editor. If editor does not have + * a selection, then the attributes are used as input attributes + * for newly inserted content. + * + * @param editor the JEditorPane component + * @param atts the text attributes to set + * @param replace if true 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 editor. + * + * @param editor the JEditorPane from which to get the + * StyledDocument + * + * @return the {@link StyledDocument} that is used by editor + */ + 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 editor. + * + * @param editor the JEditorPane from which to get the + * StyledEditorKit + * + * @return the {@link StyledEditorKit} that is used by editor + */ + 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 editor. If editor does not have + * a selection, then the attributes are set on the paragraph that + * contains the current caret position. + * + * @param editor the JEditorPane component + * @param atts the text attributes to set + * @param replace if true 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 Elements that are supported by + * StyledEditorKit, namely the following types of Elements: + * + *
        + *
      • {@link AbstractDocument#ContentElementName}
      • + *
      • {@link AbstractDocument#ParagraphElementName}
      • + *
      • {@link AbstractDocument#SectionElementName}
      • + *
      • {@link StyleConstants#ComponentElementName}
      • + *
      • {@link StyleConstants#IconElementName}
      • + *
      + */ + static class StyledViewFactory + implements ViewFactory + { + /** + * Creates a {@link View} for the specified Element. + * + * @param element the Element to create a View + * for + * @return the View for the specified Element + * or null if the type of element 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 + * Element and the inputAttributes. + */ + 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 Element 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 StyledEditorKit. + */ + public StyledEditorKit() + { + inputAttributes = new SimpleAttributeSet(); + } + + /** + * Creates an exact copy of this StyledEditorKit. + * + * @return an exact copy of this StyledEditorKit + */ + public Object clone() + { + StyledEditorKit clone = (StyledEditorKit) super.clone(); + // FIXME: Investigate which fields must be copied. + return clone; + } + + /** + * Returns the Actions supported by this {@link EditorKit}. + * This includes the {@link BoldAction}, {@link ItalicAction} and + * {@link UnderlineAction} as well as the Actions supported + * by {@link DefaultEditorKit}. + * + * The other Actions of StyledEditorKit are not + * returned here, since they require a parameter and thus custom + * instantiation. + * + * @return the Actions 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 + * EditorKit. This is an instance of + * {@link DefaultStyledDocument} in this case but may be overridden by + * subclasses. + * + * @return an instance of DefaultStyledDocument + */ + public Document createDefaultDocument() + { + return new DefaultStyledDocument(); + } + + /** + * Installs this EditorKit on the specified {@link JEditorPane}. + * This basically involves setting up required listeners on the + * JEditorPane. + * + * @param component the JEditorPane to install this + * EditorKit on + */ + public void install(JEditorPane component) + { + CaretTracker tracker = new CaretTracker(); + component.addCaretListener(tracker); + } + + /** + * Deinstalls this EditorKit from the specified + * {@link JEditorPane}. This basically involves removing all listeners from + * JEditorPane that have been set up by this + * EditorKit. + * + * @param component the JEditorPane from which to deinstall this + * EditorKit + */ + 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 EditorKit, + * namely the following types of Elements: + * + *
        + *
      • {@link AbstractDocument#ContentElementName}
      • + *
      • {@link AbstractDocument#ParagraphElementName}
      • + *
      • {@link AbstractDocument#SectionElementName}
      • + *
      • {@link StyleConstants#ComponentElementName}
      • + *
      • {@link StyleConstants#IconElementName}
      • + *
      + * + * @return a {@link ViewFactory} that is able to create {@link View}s + * for {@link Element}s that are supported by this EditorKit + */ + public ViewFactory getViewFactory() + { + if (viewFactory == null) + viewFactory = new StyledViewFactory(); + return viewFactory; + } + + /** + * Copies the text attributes from element to set. + * This is called everytime when the caret position changes to keep + * track of the current input attributes. The attributes in set + * are cleaned before adding the attributes of element. + * + * This method filters out attributes for element names, Icons + * and Components. + * + * @param element the Element 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 TabSet containing the specified tab stops. + * + * @param t the tab stops (null 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 i is not in the range + * 0 to getTabCount() - 1. + */ + 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 null). + */ + 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 (null 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 TabSet for equality with an arbitrary object. + * + * @param obj the object (null permitted). + * + * @return true if this TabSet is equal to + * obj, and false 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 TabSet. + * + * @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 TabSet. + * + * @return A string representation of this TabSet. + */ + 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 TabStop for the specified tab position. + * + * @param pos the tab position. + */ + public TabStop(float pos) + { + this(pos, ALIGN_LEFT, LEAD_NONE); + } + + /** + * Creates a new TabStop 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 TabStop for equality with an arbitrary object. + * + * @param other the other object (null permitted). + * + * @return true if this TabStop is equal to + * the specified object, and false 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 TabStop. + * + * @return A hash code. + */ + public int hashCode() + { + return (int) pos + (int) leader + (int) align; + } + + /** + * Returns a string describing this TabStop. + * + * @return A string describing this TabStop. + */ + 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 TableView. + * 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 TableRow. + * + * @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 <= 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 + * null 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 View implementation. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @deprecated Table cells are now rendered by an arbitrary View + * 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 + * View 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 + * View 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 + * View 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 + * View 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 + * View 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 + * View 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 TableView. + * + * @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 + * View 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 null 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 + * null 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 JTextComponent object associated with the given + * ActionEvent. If the source of the event is not a + * JTextComponent the currently focused text component is returned. + * + * @param event the action event + * + * @return the JTextComponent + */ + 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 Action 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 actions = new HashMap(); + + 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 it = actions.values().iterator(); it.hasNext(); i++) + augmented[i] = it.next(); + return augmented; + + } + + /** + * Returns the current focused JTextComponent object. + * + * @return the JTextComponent + */ + 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 DefaultEditorKit 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 DefaultEditorKit 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 Utilities 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 baseline 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 s 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 s 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 [x0, x]. + * + * The parameter round controls which model location is returned + * if the view coordinates are on a character: If round is + * true, 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 round is false, 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 null, TABs are + * expanded to one space character + * @param p0 the starting model location + * @param round if true 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 [x0, x]. + * + * 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 null, 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 c at + * the specified location offset. + * + * @param c the text component + * @param offset the offset of the paragraph element to return + * + * @return the paragraph element at offset + */ + 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 offset. + * + * @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 offset + * + * @throws BadLocationException if offset 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 offset. + * + * @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 offset + * + * @throws BadLocationException if offset 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 + * javax.swing.text package. It simply delegates the + * call to a method with the same name on the NavigationFilter + * of the provided JTextComponent (if it has one) or its UI. + * + * If the underlying method throws a BadLocationException 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 View instance. + * + * @param elem an Element 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 parent is null. + * + * If parent is null, a call to this method also + * calls setParent 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, null 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 0 or less + * means this view is not resizeable. Positive values make the view + * resizeable. The default implementation returns 0 + * 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 0. + * + * @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 0.0 will align this view with the left edge + * along the baseline, an alignment of 0.5 will align it + * centered to the baseline, an alignment of 1.0 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 + * length == 0 then this is a simple insertion, if + * views == null 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 null + */ + 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: + *
        + *
      • 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.
      • + *
      • Call {@link #forwardUpdate}. This forwards the DocumentEvent to + * the child views.
      • + *
      • Call {@link #updateLayout}. Gives the view a chance to either + * repair its layout, reschedule layout or do nothing at all.
      • + *
      + * + * @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: + *
        + *
      • 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.
      • + *
      • Call {@link #forwardUpdate}. This forwards the DocumentEvent to + * the child views.
      • + *
      • Call {@link #updateLayout}. Gives the view a chance to either + * repair its layout, reschedule layout or do nothing at all.
      • + *
      + * + * @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: + *
        + *
      • 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.
      • + *
      • Call {@link #forwardUpdate}. This forwards the DocumentEvent to + * the child views.
      • + *
      • Call {@link #updateLayout}. Gives the view a chance to either + * repair its layout, reschedule layout or do nothing at all.
      • + *
      + * + * @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 ec is not null (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 + * null 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 null this defaults to + * Position.Bias.Forward + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if pos 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 p1 or p2 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 pos 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 View'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 View + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + */ + public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b); + + /** + * Maps coordinates from the View'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 View + * + * @return the position in the document that corresponds to the screen + * coordinates x, y + * + * @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 pos in the given direction d. + * + * @param pos the document position + * @param b the bias for pos + * @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 pos in the given direction + * d + * + * @throws BadLocationException if pos is not a valid offset in + * the document model + * @throws IllegalArgumentException if d 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 tabStop 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 Element that is associated with this + * View. Caches the metrics and then calls + * super.paint to paint all the child views. + * + * @param g the Graphics context to render to + * @param a the allocated region for the Element + */ + 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; + } + + /** + *

      This method is called from insertUpdate and removeUpdate.

      + * + *

      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.

      + * + *

      Note that the Rectangle argument may be null + * when the allocation area is empty. + * + * @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 num <= 0 + * + * @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 true when the specified zone is loaded, + * false otherwise. The default implementation checks if + * the zone view has child elements. + * + * @param zone the zone view to check + * + * @return true when the specified zone is loaded, + * false 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 + * pos. + * + * 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 + * pos + */ + 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 pos 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 + */ +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 true when the CSS span has been set, + * false 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 r requirements according to + * min. + * + * @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 null 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 null + */ + 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 true if the attribute should be inherited from + * the parent, false otherwise. + * + * @return true if the attribute should be inherited from + * the parent, false otherwise + */ + public boolean isInherited() + { + return isInherited; + } + + /** + * Returns the default value of this attribute if one exists, + * null otherwise. + * + * @return the default value of this attribute if one exists, + * null 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: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      Element typeSwing component
      input, buttonJButton
      input, checkboxJButton
      input, imageJButton
      input, passwordJButton
      input, radioJButton
      input, resetJButton
      input, submitJButton
      input, textJButton
      select, size > 1 or with multiple attributeJList in JScrollPane
      select, size unspecified or == 1JComboBox
      textarea, textJTextArea in JScrollPane
      input, fileJTextField
      + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class FormView + extends ComponentView + implements ActionListener +{ + + protected class MouseEventListener + extends MouseAdapter + { + /** + * Creates a new MouseEventListener. + */ + 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, null + * 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 <input type="submit">> + * tag is not specified, then this string is used. + * + * @deprecated As of JDK1.3 the value is fetched from the UIManager property + * FormView.submitButtonText. + */ + public static final String SUBMIT = + UIManager.getString("FormView.submitButtonText"); + + /** + * If the value attribute of an <input type="reset">> + * tag is not specified, then this string is used. + * + * @deprecated As of JDK1.3 the value is fetched from the UIManager property + * FormView.resetButtonText. + */ + 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 FormView. + * + * @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 + * <input type="image"> 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=&y=' if the name + * attribute of the element is null or '' or + * .x=&.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 null 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 <a> tag + */ + public static final Tag A = new Tag("a"); + + /** + * The <address> tag + */ + public static final Tag ADDRESS = new Tag("address"); + + /** + * The <applet> tag + */ + public static final Tag APPLET = new Tag("applet"); + + /** + * The <area> tag + */ + public static final Tag AREA = new Tag("area"); + + /** + * The <b> tag + */ + public static final Tag B = new Tag("b"); + + /** + * The <base> tag + */ + public static final Tag BASE = new Tag("base"); + + /** + * The <basefont> tag + */ + public static final Tag BASEFONT = new Tag("basefont"); + + /** + * The <big> tag + */ + public static final Tag BIG = new Tag("big"); + + /** + * The <blockquote> tag , breaks flow, block tag. + */ + public static final Tag BLOCKQUOTE = new Tag("blockquote", BREAKS | BLOCK); + + /** + * The <body> tag , breaks flow, block tag. + */ + public static final Tag BODY = new Tag("body", BREAKS | BLOCK); + + /** + * The <br> tag , breaks flow. + */ + public static final Tag BR = new Tag("br", BREAKS); + + /** + * The <caption> tag + */ + public static final Tag CAPTION = new Tag("caption"); + + /** + * The <center> tag , breaks flow. + */ + public static final Tag CENTER = new Tag("center", BREAKS); + + /** + * The <cite> tag + */ + public static final Tag CITE = new Tag("cite"); + + /** + * The <code> tag + */ + public static final Tag CODE = new Tag("code"); + + /** + * The <dd> tag , breaks flow, block tag. + */ + public static final Tag DD = new Tag("dd", BREAKS | BLOCK); + + /** + * The <dfn> tag + */ + public static final Tag DFN = new Tag("dfn"); + + /** + * The <dir> tag , breaks flow, block tag. + */ + public static final Tag DIR = new Tag("dir", BREAKS | BLOCK); + + /** + * The <div> tag , breaks flow, block tag. + */ + public static final Tag DIV = new Tag("div", BREAKS | BLOCK); + + /** + * The <dl> tag , breaks flow, block tag. + */ + public static final Tag DL = new Tag("dl", BREAKS | BLOCK); + + /** + * The <dt> tag , breaks flow, block tag. + */ + public static final Tag DT = new Tag("dt", BREAKS | BLOCK); + + /** + * The <em> tag + */ + public static final Tag EM = new Tag("em"); + + /** + * The <font> tag + */ + public static final Tag FONT = new Tag("font"); + + /** + * The <form> tag , breaks flow. + */ + public static final Tag FORM = new Tag("form", BREAKS); + + /** + * The <frame> tag + */ + public static final Tag FRAME = new Tag("frame"); + + /** + * The <frameset> tag + */ + public static final Tag FRAMESET = new Tag("frameset"); + + /** + * The <h1> tag , breaks flow, block tag. + */ + public static final Tag H1 = new Tag("h1", BREAKS | BLOCK); + + /** + * The <h2> tag , breaks flow, block tag. + */ + public static final Tag H2 = new Tag("h2", BREAKS | BLOCK); + + /** + * The <h3> tag , breaks flow, block tag. + */ + public static final Tag H3 = new Tag("h3", BREAKS | BLOCK); + + /** + * The <h4> tag , breaks flow, block tag. + */ + public static final Tag H4 = new Tag("h4", BREAKS | BLOCK); + + /** + * The <h5> tag , breaks flow, block tag. + */ + public static final Tag H5 = new Tag("h5", BREAKS | BLOCK); + + /** + * The <h6> tag , breaks flow, block tag. + */ + public static final Tag H6 = new Tag("h6", BREAKS | BLOCK); + + /** + * The <head> tag , breaks flow, block tag. + */ + public static final Tag HEAD = new Tag("head", BREAKS | BLOCK); + + /** + * The <hr> tag , breaks flow. + */ + public static final Tag HR = new Tag("hr", BREAKS); + + /** + * The <html> tag , breaks flow. + */ + public static final Tag HTML = new Tag("html", BREAKS); + + /** + * The <i> tag + */ + public static final Tag I = new Tag("i"); + + /** + * The <img> tag + */ + public static final Tag IMG = new Tag("img"); + + /** + * The <input> tag + */ + public static final Tag INPUT = new Tag("input"); + + /** + * The <isindex> tag , breaks flow. + */ + public static final Tag ISINDEX = new Tag("isindex", BREAKS); + + /** + * The <kbd> tag + */ + public static final Tag KBD = new Tag("kbd"); + + /** + * The <li> tag , breaks flow, block tag. + */ + public static final Tag LI = new Tag("li", BREAKS | BLOCK); + + /** + * The <link> tag + */ + public static final Tag LINK = new Tag("link"); + + /** + * The <map> tag + */ + public static final Tag MAP = new Tag("map"); + + /** + * The <menu> tag , breaks flow, block tag. + */ + public static final Tag MENU = new Tag("menu", BREAKS | BLOCK); + + /** + * The <meta> tag + */ + public static final Tag META = new Tag("meta"); + + /** + * The <nobr> tag + */ + static final Tag NOBR = new Tag("nobr"); + + /** + * The <noframes> tag , breaks flow, block tag. + */ + public static final Tag NOFRAMES = new Tag("noframes", BREAKS | BLOCK); + + /** + * The <object> tag + */ + public static final Tag OBJECT = new Tag("object"); + + /** + * The <ol> tag , breaks flow, block tag. + */ + public static final Tag OL = new Tag("ol", BREAKS | BLOCK); + + /** + * The <option> tag + */ + public static final Tag OPTION = new Tag("option"); + + /** + * The <p> tag , breaks flow, block tag. + */ + public static final Tag P = new Tag("p", BREAKS | BLOCK); + + /** + * The <param> tag + */ + public static final Tag PARAM = new Tag("param"); + + /** + * The <pre> tag , breaks flow, block tag, preformatted. + */ + public static final Tag PRE = new Tag("pre", BREAKS | BLOCK | PREFORMATTED); + + /** + * The <s> tag + */ + public static final Tag S = new Tag("s"); + + /** + * The <samp> tag + */ + public static final Tag SAMP = new Tag("samp"); + + /** + * The <script> tag + */ + public static final Tag SCRIPT = new Tag("script"); + + /** + * The <select> tag + */ + public static final Tag SELECT = new Tag("select"); + + /** + * The <small> tag + */ + public static final Tag SMALL = new Tag("small"); + + /** + * The <span> tag + */ + public static final Tag SPAN = new Tag("span"); + + /** + * The <strike> tag + */ + public static final Tag STRIKE = new Tag("strike"); + + /** + * The <strong> tag + */ + public static final Tag STRONG = new Tag("strong"); + + /** + * The <style> tag + */ + public static final Tag STYLE = new Tag("style"); + + /** + * The <sub> tag + */ + public static final Tag SUB = new Tag("sub"); + + /** + * The <sup> tag + */ + public static final Tag SUP = new Tag("sup"); + + /** + * The <table> tag , block tag. + */ + public static final Tag TABLE = new Tag("table", BLOCK); + + /** + * The <td> tag , breaks flow, block tag. + */ + public static final Tag TD = new Tag("td", BREAKS | BLOCK); + + /** + * The <textarea> tag , preformatted. + */ + public static final Tag TEXTAREA = new Tag("textarea", PREFORMATTED); + + /** + * The <th> tag , breaks flow, block tag. + */ + public static final Tag TH = new Tag("th", BREAKS | BLOCK); + + /** + * The <title> tag , breaks flow, block tag. + */ + public static final Tag TITLE = new Tag("title", BREAKS | BLOCK); + + /** + * The <tr> tag , block tag. + */ + public static final Tag TR = new Tag("tr", BLOCK); + + /** + * The <tt> tag + */ + public static final Tag TT = new Tag("tt"); + + /** + * The <u> tag + */ + public static final Tag U = new Tag("u"); + + /** + * The <ul> tag , breaks flow, block tag. + */ + public static final Tag UL = new Tag("ul", BREAKS | BLOCK); + + /** + * The <var> 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 null. + * + * @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 tagMap; + private static Map 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(); + + 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:

      + * 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". + *

      + * + * + * @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(); + + 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 base 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, null + * 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("'); + 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 AttributeSet (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 parseBuffer = new Vector(); + + /** + * The parse stack. It holds the current element tree path. + */ + private Stack parseStack = new Stack(); + + /** + * 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 '' 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 <textarea> 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 a. + 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: <input>, <textarea>, + * <select> and <option>. + */ + 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 <isindex> 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 <pre> 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 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
       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, <p id
      +   * ='my paragraph >'). 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 true when this document is inside a frame,
      +   * false otherwise.
      +   *
      +   * @return true when this document is inside a frame,
      +   *         false otherwise
      +   */
      +  boolean isFrameDocument()
      +  {
      +    return frameDocument;
      +  }
      +
      +  /**
      +   * Set true when this document is inside a frame,
      +   * false otherwise.
      +   *
      +   * @param frameDoc true when this document is inside a frame,
      +   *                 false 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 null 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 addTag.
      +       *
      +       * @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 true when the html has been inserted successfully,
      +       *         false 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 Elements that are supported.
      +   */
      +  public static class HTMLFactory
      +    implements ViewFactory
      +  {
      +
      +    /**
      +     * Constructor
      +     */
      +    public HTMLFactory()
      +    {
      +      // Do Nothing here.
      +    }
      +
      +    /**
      +     * Creates a {@link View} for the specified Element.
      +     *
      +     * @param element the Element to create a View
      +     *        for
      +     * @return the View for the specified Element
      +     *         or null if the type of element 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 </table>)
      +     * 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 <br>. 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 <table>)
      +     * 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 bold action identifier.
      +   */
      +  public static final String BOLD_ACTION = "html-bold-action";
      +
      +  /**
      +   * The italic action identifier.
      +   */
      +  public static final String ITALIC_ACTION = "html-italic-action";
      +
      +  /**
      +   * The color action indentifier
      +   * (passing the color as an argument).
      +   */
      +  public static final String COLOR_ACTION = "html-color-action";
      +
      +  /**
      +   * The increase font action identifier.
      +   */
      +  public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
      +
      +  /**
      +   * The decrease 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",
      +                             "
      ", + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertTableRow", + "
      ", + HTML.Tag.TABLE, HTML.Tag.TR, + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertTableCell", + "
      ", + HTML.Tag.TR, HTML.Tag.TD, + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertUnorderedList", + "
      ", + HTML.Tag.BODY, HTML.Tag.UL), + new InsertHTMLTextAction("InsertUnorderedListItem", + "
      ", + HTML.Tag.UL, HTML.Tag.LI, + HTML.Tag.BODY, HTML.Tag.UL), + new InsertHTMLTextAction("InsertOrderedList", + "
      ", + HTML.Tag.BODY, HTML.Tag.OL), + new InsertHTMLTextAction("InsertOrderedListItem", + "
      ", + HTML.Tag.OL, HTML.Tag.LI, + HTML.Tag.BODY, HTML.Tag.OL), + new InsertHTMLTextAction("InsertPre", + "
      ", 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 true when forms should be automatically submitted
      +   * by the editor kit. Set this to false 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 true.
      +   *
      +   * @return true when forms should be automatically submitted
      +   *         by the editor kit, false 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 true when the editor kit should handle form
      +   *        submission, false 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 openEmbeddedTagHashSet = null;
      +
      +  private String new_line_str = "" + NEWLINE;
      +
      +  private char[] html_entity_char_arr = {'<',    '>',    '&',     '"'};
      +
      +  private String[] html_entity_escape_str_arr = {"<", ">", "&",
      +                                                 """};
      +
      +  // 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 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();
      +  } // public HTMLWriter(Writer writer, HTMLDocument doc)
      +
      +  /**
      +   * Constructs a HTMLWriter which outputs a Html Fragment.
      +   *
      +   * @param writer Writer to write output to
      +   * @param doc the javax.swing.text.html.HTMLDocument
      +   *        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();
      +
      +    doc_pos = pos;
      +    doc_offset_remaining = pos;
      +    doc_len = len;
      +    doc_len_remaining = len;
      +    htmlFragmentParentHashSet = new HashSet();
      +  } // 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("");
      +      } // 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 javax.swing.text.html.HTML.Tag,
      +   * javax.swing.text.StyleConstants or
      +   * javax.swing.text.html.HTML.Attribute.ENDTAG.
      +   *
      +   * @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("");
      +      } // 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 true if it is a block tag
      +   *         false 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("");
      +
      +    Document tempDocument =
      +      (Document) attrSet.getAttribute(StyleConstants.ModelAttribute);
      +
      +    writeRaw(tempDocument.getText(0, tempDocument.getLength()));
      +    indent();
      +    writeRaw("");
      +
      +  } // 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("");
      +    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("");
      +
      +  } // 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("");
      +
      +    writeContent(option.getLabel());
      +
      +    writeRaw("");
      +    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("");
      +    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("");
      +
      +  } // protected void comment(Element paramElem)
      +    //   throws IOException, BadLocationException
      +
      +  /**
      +   * Determines if element is a synthesized
      +   * javax.swing.text.Element or not.
      +   *
      +   * @param element the element to test
      +   *
      +   * @return true if it is a synthesized element,
      +   *         false 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
      +   * javax.swing.text.StyleConstants.NameAttribute
      +   * matches tag or not.
      +   *
      +   * @param attrSet the javax.swing.text.AttributeSet of
      +   *        element to be matched
      +   * @param tag the HTML.Tag to match
      +   *
      +   * @return true if it matches,
      +   *         false 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 javax.swing.text.AttributeSet 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("");
      +            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 <, >
      +   * 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("");
      +            else
      +              {
      +                indent();
      +                writeRaw("");
      +
      +                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("");
      +              else
      +                {
      +                  indent();
      +                  writeRaw("");
      +
      +                  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><ul></code> and
      + * <code><ol></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><object></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><param></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><option></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><option></code> tag.
      +   *
      +   * @param l the label to set
      +   */
      +  public void setLabel(String l)
      +  {
      +    label = l;
      +  }
      +
      +  /**
      +   * Returns the label of this <code><option></code> tag.
      +   *
      +   * @return the label of this <code><option></code> tag
      +   */
      +  public String getLabel()
      +  {
      +    return label;
      +  }
      +
      +  /**
      +   * Returns the attributes used to render this <code><option></code>
      +   * tag.
      +   *
      +   * @return the attributes used to render this <code><option></code> tag
      +   */
      +  public AttributeSet getAttributes()
      +  {
      +    return attributes;
      +  }
      +
      +  /**
      +   * Returns a string representation of this <code><option></code> tag.
      +   * This returns the <code>label</code> property.
      +   *
      +   * @return a string representation of this <code><option></code> tag
      +   */
      +  public String toString()
      +  {
      +    return label;
      +  }
      +
      +  /**
      +   * Sets the selected state of this <code><option></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
      +
      +
      +

      Provides supporting classes for web browsers, + web robots, web page content analysers, web editors and + other applications applications working with Hypertext + Markup Language (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; + +/** + *

      + * Stores the attribute information, obtained by parsing SGML (DTD) tag + * <!ATTLIST .. >

      + *

      + * 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 (.) . + *

      + *

      + * The AttributeList 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 Element. + * Use the getNext() method repeatedly to see all attributes in + * the list. + *

      + * @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: + *
        + *
      • REQUIRED if the attribute value is always required,
      • + *
      • IMPLIED if the user agent must supply the default value itself,
      • + *
      • FIXED if the attribute value is fixed to some value and cannot + * be changed.
      • + *
      • DEFAULT if the attribute default value has been supplied.
      • + *
      • 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
      • + *
      • 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.
      • + *
      + */ + 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 DTDConstants. + * @param a_modifier The modifier of this attribute. The possible values + * are defined in DTDConstants. + * @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: + *
        + *
      • REQUIRED if the attribute value is always required,
      • + *
      • IMPLIED if the user agent must supply the default value itself,
      • + *
      • FIXED if the attribute value is fixed to some value and cannot + * be changed.
      • + *
      • DEFAULT if the attribute default value has been supplied.
      • + *
      • 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
      • + *
      • 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.
      • + *
      + */ + 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: + *
      + * a = new ContentModel('+', A, null); // a reprensents A+
      + * b = new ContentModel('&', B, a); // b represents B & A+
      + * c = new ContentModel('*', b, null); // c represents ( B & A+) *
      + * d = new ContentModel('|', new ContentModel('*', A, null),
      + *          new ContentModel('?', B, null)); // d represents ( A* | B? )
      + * 
      + * where the valid operations are: + *
        + *
      • E* E occurs zero or more times
      • + *
      • E+ E occurs one or more times
      • + *
      • E? E occurs once or not atl all
      • + *
      • A,B A occurs before B
      • + *
      • A|B both A and B are permitted in any order. + * The '|' alone does not permit the repetetive occurence of A or B + * (use (A|B)*.
      • + *
      • A&B both A and B must occur once (in any order)
      • + *
      + * @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 (',','&' 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 next (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: + * + * a = new ContentModel('+', A, null); // a reprensents A+ + * b = new ContentModel('&', B, a); // b represents B & A+ + * c = new ContentModel('*', b, null); // c represents ( B & A+) * + * d = new ContentModel('|', A, + * new ContentModel('?',b, null); + * // d represents + * + */ + 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 '&'). + * @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 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 next.content. + * The method is programmed as the part of the standard API, but not + * used in this implementation. + * @return the value of the field next. + */ + 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; + +/** + *

      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).

      + *

      + * If you need more information about SGML DTD documents, + * the author suggests to read SGML tutorial on + * http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html. + * We also recommend Goldfarb C.F (1991) The SGML Handbook, + * Oxford University Press, 688 p, ISBN: 0198537379. + *

      + *

      + * 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. + *

      + * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class DTD + implements DTDConstants +{ + /** + * The version of the persistent data format. + * @specnote This was made final in 1.5. + */ + public static final int FILE_VERSION = 1; + + /** + * The table of existing available DTDs. + */ + static Hashtable dtdHash = new Hashtable(); + + /** + * 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 elementHash = + new Hashtable(); + + /** + * The entity table for accessing all DTD entities by name. + */ + public Hashtable entityHash = new Hashtable(); + + /** + * 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 elements = new Vector(); + + /** 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 &lt; + * (means '<' ); + * @param name The entity name (without heading & 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 + * Element.index 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 + * <hr> + * @param content the element content + * @param exclusions the set of elements that must not occur inside + * this element. The Element.index value defines which + * bit in this bitset corresponds to that element. + * @param inclusions the set of elements that can occur inside this + * element. the Element.index 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); + } + + /** + *

      Reads DTD from an archived format. This format is not standardized + * and differs between implementations.

      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.

      + * @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 DTDConstants). + * @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 <!ATTLIST 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 v = new Vector(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; + +/** + *

      This class defines the SGML basic types, used for describing HTML 4.01 + * at http://www.w3.org/TR/html4/types.html. 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. + *

      + *

      + * If you need more information about SGML DTD documents, + * the author suggests to read SGML tutorial on + * http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html. + * We also recommend Goldfarb C.F (1991) The SGML Handbook, + * Oxford University Press, 688 p, ISBN: 0198537379. + *

      + * + * @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, + * <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"> + */ + 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, + * <DOCTYPE html SYSTEM "/path/to/file.dtd"> . + */ + 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 + * "<" is threated as an ordinary character, but + * "&name;" 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; + +/** + *

      A simple error-tolerant HTML parser that uses a DTD document + * to access data on the possible tokens, arguments and syntax.

      + *

      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.

      + *

      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 + *

      <table><tr><td>a<td>b<td>c</tr>
      + * will invoke exactly the handling methods exactly in the same order + * (and with the same parameters) as if parsing the document:
      + * <html><head></head><body><table>< + * tbody><tr><td>a</td><td>b + * </td><td>c</td></tr>< + * /tbody></table></body></html>

      + * (supposed tags are given in italics). The parser also supports + * obsolete elements of HTML syntax.

      + *

      + * 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 <br>. 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 </table>) + * 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 <table>) + * 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; + +/** + *

      + * Stores the element information, obtained by parsing SGML DTD + * tag <!ELEMENT .. >. This class has no public + * constructor and can only be instantiated using the + * {@link javax.swing.text.html.parser.DTD } methods

      + * + *

      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 <hr>) have no content. Element names + * are case sensitive.

      + * @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 <hr>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 <head> or + * <body>) 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 inclusions and exclusions 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 a_type 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 inclusions and exclusions 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 <hr>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 <head> or + * <body>) 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 + * <!ENTITY % .. > 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; + +/** + *

      Stores information, obtained by parsing SGML DTL + * <!ENTITY % .. > tag.

      + *

      + * 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 &entity-name;. Such entities are + * defined by the SGML DTD tag + * <!ENTITY name "value">. 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 &entity-name;. Such entities are + * defined by the SGML DTD tag + * <!ENTITY % name "value">. 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. + */ + +/** + *

      A simple error-tolerant HTML parser that uses a DTD document + * to access data on the possible tokens, arguments and syntax.

      + *

      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.

      + *

      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 + *

      <table><tr><td>a<td>b<td>c</tr>
      + * will invoke exactly the handling methods exactly in the same order + * (and with the same parameters) as if parsing the document:
      + * <html><head></head><body><table>< + * tbody><tr><td>a</td><td>b + * </td><td>c</td></tr>< + * /tbody></table></body></html>

      + * (supposed tags are given in italics). The parser also supports + * obsolete elements of HTML syntax.

      + *

      + * @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 </html> 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 <br>. 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 </table>) + * 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 <table>) + * 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. + *

      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. + *

      + *

      For pre-formatted text (inside TEXAREA and PRE), the parser preserves + * all tabs and spaces, but removes one bounding \r, \n or \r\n, + * if it is present. Additionally, it replaces each occurence of \r or \r\n + * by a single \n.

      + * + * @param text A section text. + */ + protected void handleText(char[] text) + { + // This default implementation does nothing. + } + + /** + * Handle HTML <title> 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 <table>) + * 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 @@ + + + + +GNU Classpath - javax.swing.text.html.parser + + +

      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). +

      + + + 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 @@ + + + + +GNU Classpath - javax.swing.text + + +

      Provides core text classes and interfaces representing models and views +used by the text components for display and editing of text.

      + + 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 + * 'text/rtf' + * + * @return the MIME content type for RTFEditorKit + */ + public String getContentType() + { + return "text/rtf"; + } + + /** + * Reads RTF data from stream into doc at the + * specified position pos. + * + * @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 reader into doc at the + * specified position pos. + * + * @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 stream. + * + * @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 reader. + * + * @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 <file>. + */ + 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 <header>. + * + * TODO: implement this properly + */ + private void parseHeader() + //throws IOException, BadLocationException + { + // TODO add parse rules here + } + + + /** + * The parse rules for <document>. + * + * 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: + * + * + * RTF specification at MSDN + * + * @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 @@ + + + + +GNU Classpath - javax.swing.text.rtf + + +

      Provides support for Rich Text Format (RTF) data to be used with the +{@link JEditorPane} component. +

      + + + 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 NodeDimensions 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 true if root should be visible, + * false otherwise + */ + public void setRootVisible(boolean visible) + { + rootVisible = visible; + } + + /** + * isRootVisible + * + * @return true if root is visible, + * false 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 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 EMPTY_ENUMERATION = + new EmptyEnumeration(); + + /** + * The parent of this node (possibly null). + */ + protected MutableTreeNode parent; + + /** + * The child nodes for this node (may be empty). + */ + protected Vector children = new Vector(); + + /** + * userObject + */ + protected transient Object userObject; + + /** + * allowsChildren + */ + protected boolean allowsChildren; + + /** + * Creates a DefaultMutableTreeNode object. + * This is equivalent to DefaultMutableTreeNode(null, true). + */ + public DefaultMutableTreeNode() + { + this(null, true); + } + + /** + * Creates a DefaultMutableTreeNode object with the given + * user object attached to it. This is equivalent to + * DefaultMutableTreeNode(userObject, true). + * + * @param userObject the user object (null permitted). + */ + public DefaultMutableTreeNode(Object userObject) + { + this(userObject, true); + } + + /** + * Creates a DefaultMutableTreeNode object with the given + * user object attached to it. + * + * @param userObject the user object (null permitted). + * @param allowsChildren true if the code allows to add child + * nodes, false 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 + * getUserObject().toString(), or null if there + * is no user object. + * + * @return A string representation of the node (possibly null). + */ + 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 (null not permitted). + * + * @throws IllegalStateException if {@link #getAllowsChildren()} returns + * false. + * @throws IllegalArgumentException if {@link #isNodeAncestor} returns + * true. + * @throws IllegalArgumentException if child is + * null. + */ + 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 null). + */ + public TreeNode getParent() + { + return parent; + } + + /** + * Removes the child with the given index from this node. + * + * @param index the index (in the range 0 to + * getChildCount() - 1). + * + * @throws ArrayIndexOutOfBoundsException if index 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 + * null. + * + * @param node the child node (null not permitted). + * + * @throws IllegalArgumentException if node is not a child of + * this node. + * @throws IllegalArgumentException if node 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 (null not permitted). + * @param index the index. + * + * @throws IllegalArgumentException if node is + *
      null
      . + */ + 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. + * EMPTY_ENUMERATION 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 (null not permitted). + * + * @return The index of the specified child node, or -1. + * + * @throws IllegalArgumentException if node is null. + */ + 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 false, 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. null 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 true if node is an ancestor of this + * tree node, and false otherwise. An ancestor node is any of: + *
        + *
      • this tree node;
      • + *
      • the parent node (if there is one);
      • + *
      • any ancestor of the parent node;
      • + *
      + * If node is null, this method returns + * false. + * + * @param node the node (null 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 true if node is a descendant of this + * tree node, and false otherwise. A descendant node is any of: + *
        + *
      • this tree node;
      • + *
      • the child nodes belonging to this tree node, if there are any;
      • + *
      • any descendants of the child nodes;
      • + *
      + * If node is null, this method returns + * false. + * + * @param node the node (null 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 list = new ArrayList(); + + 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 stack = new Stack(); + 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 true if this is the root node, + * falseotherwise + */ + 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 nodes = new Vector(); + 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 true if node is a child of this tree + * node, and false otherwise. If node is + * null, this method returns false. + * + * @param node the node (null 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 node, or + * null if there is no child after the specified + * node. + * + * @param node a child of this node (null not permitted). + * + * @return The next child, or null. + * + * @throws IllegalArgumentException if node is not a child of + * this node, or is null. + */ + 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 node, or + * null if there is no child before the specified + * node. + * + * @param node a child of this node (null not permitted). + * + * @return The previous child, or null. + * + * @throws IllegalArgumentException if node is not a child of + * this node, or is null. + */ + 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 true if this tree node and node share + * the same parent. If node is this tree node, the method + * returns true and if node is null + * this method returns false. + * + * @param node the node (null 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 1. + * + * @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 + * null. + * + * @return The next sibling, or null. + */ + 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 + * null. + * + * @return The previous sibling, or null. + */ + 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 true if this tree node is a lead node (that is, it + * has no children), and false. + * + * @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 null. + */ + 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 null. + */ + 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 + { + + LinkedList queue = new LinkedList(); + + 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 children = + (Enumeration) 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 next; + + Stack> childrenEnums = + new Stack>(); + + @SuppressWarnings("unchecked") + PreorderEnumeration(TreeNode node) + { + next = node; + childrenEnums.push((Enumeration) 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 children = childrenEnums.peek(); + + // Retrieves the next element. + next = traverse(children); + + return current; + } + + @SuppressWarnings("unchecked") + private TreeNode traverse(Enumeration children) + { + // If more children are available step down. + if (children.hasMoreElements()) + { + TreeNode child = children.nextElement(); + childrenEnums.push((Enumeration) 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 + { + + Stack nodes = new Stack(); + Stack> childrenEnums = + new Stack>(); + + @SuppressWarnings("unchecked") + PostorderEnumeration(TreeNode node) + { + nodes.push(node); + childrenEnums.push((Enumeration) node.children()); + } + + public boolean hasMoreElements() + { + return !nodes.isEmpty(); + } + + public TreeNode nextElement() + { + if (nodes.isEmpty()) + throw new NoSuchElementException("No more elements left!"); + + Enumeration children = childrenEnums.peek(); + + return traverse(children); + } + + @SuppressWarnings("unchecked") + private TreeNode traverse(Enumeration children) + { + if (children.hasMoreElements()) + { + TreeNode node = children.nextElement(); + nodes.push(node); + + Enumeration newChildren = + (Enumeration) 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 EditorContainer 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 DefaultTextField 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 CellEditorListener object to this editor. + * + * @param listener + * the listener to add + */ + public void addCellEditorListener(CellEditorListener listener) + { + realEditor.addCellEditorListener(listener); + } + + /** + * Removes a CellEditorListener object. + * + * @param listener the listener to remove + */ + public void removeCellEditorListener(CellEditorListener listener) + { + realEditor.removeCellEditorListener(listener); + } + + /** + * Returns all added CellEditorListener 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 Tree.openIcon. + * + * @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 Tree.closedIcon. + * + * @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 + * Tree.leafIcon. + * + * @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 null if no icon is required. + * + * @param icon the icon (null 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 null). + * + * @see #setOpenIcon(Icon) + */ + public Icon getOpenIcon() + { + return openIcon; + } + + /** + * Sets the icon to be displayed for non-leaf nodes that are closed. Set + * this to null if no icon is required. + * + * @param icon the icon (null 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 null). + * + * @see #setClosedIcon(Icon) + */ + public Icon getClosedIcon() + { + return closedIcon; + } + + /** + * Sets the icon to be displayed for leaf nodes. Set this to + * null if no icon is required. + * + * @param icon the icon (null 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 null). + * + * @see #setLeafIcon(Icon) + */ + public Icon getLeafIcon() + { + return leafIcon; + } + + /** + * Sets the text color for tree cells that are selected. + * + * @param c the color (null 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 Tree.selectionForeground. + * + * @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 (null 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 Tree.selectionForeground. + * + * @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 (null 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 Tree.selectionBackground. + * + * @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 (null 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 Tree.textBackground. + * + * @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 (null 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 Tree.selectionBorderColor. + * + * @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 this) 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 this. + */ + 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 leaf + * property. + * + * @param node the node to check + * + * @return boolean true if the node is a leaf node, + * false otherwise + * + * @throws ClassCastException if the specified node is not a + * TreeNode 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; + } + + /** + *

      + * 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 not be properly refreshed if you + * call the JTree.repaint instead. + *

      + *

      + * 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)}. + *

      + */ + 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 not 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 TreeModelListener 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[] getListeners(Class 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 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 tmpPaths; + + /** + * Constructs a new DefaultTreeSelectionModel. + */ + public DefaultTreeSelectionModel() + { + setSelectionMode(DISCONTIGUOUS_TREE_SELECTION); + listSelectionModel = new DefaultListSelectionModel(); + listenerList = new EventListenerList(); + leadIndex = -1; + tmpPaths = new HashSet(); + selectedPaths = new HashSet(); + } + + /** + * 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(); + cloned.tmpPaths = new HashSet(); + + 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 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(); + 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 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(); + changedPaths.add(new PathPlaceHolder(selection[i], false)); + } + } + + // Perform changes and notification. + selection = newSelection; + HashSet 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 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(); + 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 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 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(); + 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 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 true if the path is in the selection, + * false 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 true if the selection is empty, false + * 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 TreeSelectionListener object to this model. + * + * @param listener the listener to add + */ + public void addTreeSelectionListener(TreeSelectionListener listener) + { + listenerList.add(TreeSelectionListener.class, listener); + } + + /** + * Removes a TreeSelectionListener object from this model. + * + * @param listener the listener to remove + */ + public void removeTreeSelectionListener(TreeSelectionListener listener) + { + listenerList.remove(TreeSelectionListener.class, listener); + } + + /** + * Returns all TreeSelectionListener 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[] getListeners(Class 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 true if the row is in this selection, + * false 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 PropertyChangeListener 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 PropertyChangeListener object from this model. + * + * @param listener the listener to remove. + */ + public void removePropertyChangeListener(PropertyChangeListener listener) + { + if (changeSupport != null) + changeSupport.removePropertyChangeListener(listener); + } + + /** + * Returns all added PropertyChangeListener 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 true if the paths are contiguous (take subsequent + * rows in the diplayed tree view. The method returns true if + * we have no RowMapper assigned. + * + * @param paths the paths to check for continuity + * @return true 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 true if: + *
        + *
      • paths is null or empty
      • + *
      • we have no RowMapper assigned
      • + *
      • nothing is currently selected
      • + *
      • selectionMode is {@link #DISCONTIGUOUS_TREE_SELECTION}
      • + *
      • adding the paths to the selection still results in a contiguous set of + * paths
      • + * + * @param paths the paths to check + * @return true 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 true 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 set = new HashSet(); + 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 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 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 lpath = new LinkedList(); + 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 expanded = new HashSet(); + + /** + * Maps nodes to the row numbers. + */ + Hashtable nodes = new Hashtable(); + + /** + * Maps row numbers to nodes. + */ + Hashtable row2node = new Hashtable(); + + /** + * 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 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 getVisiblePathsFrom(TreePath parentPath) + { + if (dirty) + update(); + Vector p = new Vector(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 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 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 TreeCellEditor 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 TreeCellRenderer 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 null if this + * node has no parent. + * + * @return The parent node (possibly null). + */ + 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 (null not permitted). + * + * @return The index of the specified child node, or -1. + * + * @throws IllegalArgumentException if node is null. + */ + int getIndex(TreeNode node); + + /** + * Returns the child node at the given index. + * + * @param index the index (in the range 0 to + * getChildCount() - 1). + * + * @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 true if this node allows children, and + * false otherwise. + * + * @return A boolean. + */ + boolean getAllowsChildren(); + + /** + * Returns true if this node is a leaf node, and + * false 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 TreePath 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 (null not permitted). + * + * @throws IllegalArgumentException if path is null. + */ + 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 (null not permitted). + * + * @throws IllegalArgumentException if element is + * null. + */ + public TreePath(Object element) + { + path = new Object[1]; + path[0] = element; + } + + /** + * Creates a new tree path by adding the specified element to + * the path. + * + * @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 length 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: + *
          + *
        • the object is not null;
        • + *
        • the object is an instanceof {@link TreePath};
        • + *
        • the object contains the same elements in the same order as this + * {@link TreePath};
        • + *
        + * + * @param object the object (null permitted). + * + * @return true if obj is equal to this tree path, + * and false 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 (0 < N - 1, where + * N is the number of elements in the path). + * + * @return The element at the specified position. + * + * @throws IllegalArgumentException if position 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 true if path is a descendant of this + * path, and false otherwise. If path is + * null, this method returns false. + * + * @param path the path to check (null permitted). + * + * @return true if path is a descendant of this + * path, and false 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 null. + * + * @return The parent path, or null 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 lpath = new LinkedList(); + 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 expanded = new HashSet(); + + /** + * Maps nodes to the row numbers. + */ + Hashtable nodes = new Hashtable(); + + /** + * Maps row numbers to nodes. + */ + ArrayList row2node = new ArrayList(); + + /** + * 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 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 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 getVisiblePathsFrom(TreePath parentPath) + { + if (dirty) + update(); + Vector p = new Vector(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 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 @@ + + + + +GNU Classpath - javax.swing.tree + + +

        Interfaces and classes that support the {@link javax.swing.JTree} +component.

        + + + 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 UndoableEdit 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 serialver + * 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 “Undo”, 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 + * “AbstractUndoableEdit.undoText”. + */ + protected static final String UndoName = "Undo"; + + + /** + * The constant string “Redo”, 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 + * “AbstractUndoableEdit.redoText”. + */ + protected static final String RedoName = "Redo"; + + + /** + * Indicates whether this editing action has been executed. A value + * of true means that the action was performed, or that + * a redo operation was successful. A value of false + * 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 true by the constructor, and to + * false by the {@link #die()} method. + */ + private boolean alive; + + + /** + * Constructs a new AbstractUndoableEdit. The initial + * state is that the editing action is alive, and + * hasBeenDone is true. + */ + 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 + * false, 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 true to indicate that this action can be + * undone, false otherwise. + * + * @see #undo() + * @see #canRedo() + */ + public boolean canUndo() + { + return alive && hasBeenDone; + } + + + /** + * Redoes this editing action. + * + * @throws CannotRedoException if {@link #canRedo()} returns + * false, 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 true to indicate that this action can be + * redone, false 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. + * + *

        The default implementation always returns false, + * 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. + * + *

        The default implementation always returns false, + * 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. + * + *

        The default implementation returns true. + * + * @return true to indicate that the action is + * significant enough for being separately undoable, or + * false otherwise. + */ + public boolean isSignificant() + { + return true; + } + + + /** + * Returns a human-readable, localized name that describes this + * editing action and can be displayed to the user. + * + *

        The default implementation returns an empty string. + */ + public String getPresentationName() + { + return ""; + } + + + /** + * Calculates a localized name for presenting the undo action to the + * user. + * + *

        The default implementation returns the concatenation of the + * string “Undo” and the action name, which is + * determined by calling {@link #getPresentationName()}. + * + *

        The string “Undo” is retrieved from the {@link + * javax.swing.UIManager}, using the key + * “AbstractUndoableEdit.undoText”. 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. + * + *

        The default implementation returns the concatenation of the + * string “Redo” and the action name, which is + * determined by calling {@link #getPresentationName()}. + * + *

        The string “Redo” is retrieved from the {@link + * javax.swing.UIManager}, using the key + * “AbstractUndoableEdit.redoText”. 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 CannotRedoException. + */ + 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 CannotUndoException. + */ + 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 + * UndoableEdits. + * + *

        The use of a CompoundEdit is divided in two separate + * phases.

        + * + *
          + *
        1. In the first phase, the CompoundEdit is + * initialized. After a new instance of CompoundEdit has + * been created, {@link #addEdit(UndoableEdit)} is called for each + * element of the compound. To terminate the initialization phase, + * call {@link #end()}.
        2. + *
        3. In the second phase, the the CompoundEdit can be + * used, typically by invoking {@link #undo()} and + * {@link #redo()}.
        4. + *
        + * + * @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 UndoableEdits being combined into a compound + * editing action. + */ + protected Vector edits; + + + /** + * Indicates whether the creation of this CompoundEdit is still in + * progress. Initially, the value of this flag is + * true. The {@link #end()} method changes the flag to + * false. + */ + private boolean inProgress; + + + /** + * Constructs a new CompoundEdit. + */ + public CompoundEdit() + { + edits = new Vector(); + inProgress = true; + } + + + /** + * Undoes all edits that are part of of this + * CompoundEdit. The compound elements will receive the + * undo message in the reverse order of addition. + * + * @throws CannotUndoException if {@link #canUndo()} returns + * false. This can happen if {@link #end()} has not + * been called on this CompoundEdit, 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 + * CompoundEdit. The compound elements will receive the + * undo message in the same order as they were added. + * + * @throws CannotRedoException if {@link #canRedo()} returns + * false. This can happen if {@link #end()} has not + * been called on this CompoundEdit, 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 UndoableEdit 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. + * + *

        The compound elements will receive the + * die 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. + * + *

        If this edit’s {@link #end()} method has been called + * before, false is returned immediately. Otherwise, + * the {@linkplain #lastEdit() last added edit} is given the + * opportunity to {@linkplain UndoableEdit#addEdit(UndoableEdit) + * incorporate} edit. If this fails, edit + * is given the opportunity to {@linkplain + * UndoableEdit#replaceEdit(UndoableEdit) replace} the last added + * edit. If this fails as well, edit gets added as a + * new compound to {@link #edits}. + * + * @param edit the editing action being added. + * + * @return true if edit could somehow be + * incorporated; false if edit 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 CompoundEdit 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 false, 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 true if {@link #end()} + * has been called on this CompoundEdit, {@link #die()} + * has not yet been called, and the edit has not been undone + * already. + * + * @return true to indicate that this action can be + * undone; false 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 true if {@link #end()} + * has been called on this CompoundEdit, {@link #die()} + * has not yet been called, and the edit has not been redone + * already. + * + * @return true to indicate that this action can be + * redone; false otherwise. + * + * @see #redo() + * @see #canUndo() + */ + public boolean canRedo() + { + return !inProgress && super.canRedo(); + } + + + /** + * Determines whether the initial construction phase of this + * CompoundEdit 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 true if the initialization phase is still in + * progress; false 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. + * + *

        A CompoundEdit 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. + * + *

        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. + * + *

        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. + * + *

        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. + * + *

        The following example shows how to use this class.

        + * + *
        + * Foo foo; // class Foo implements {@link StateEditable}
        + * StateEdit edit;
        + *
        + * edit = new StateEdit(foo, "Name Change");
        + * foo.setName("Jane Doe");
        + * edit.end();
        + * undoManager.addEdit(edit);
        + * 
        + * + *

        If Foo’s implementation of {@link + * StateEditable} considers the name as part of the editable state, + * the user can now choose “Undo Name Change” or + * “Redo Name Change” from the respective menu. No + * further undo support is needed from the application.

        + * + *

        The following explains what happens in the example.

        + * + *
          + *
        1. When a StateEdit is created, the associated + * {@link StateEditable} gets asked to store its state into a hash + * table, {@link #preState}.
        2. + *
        3. The application will now perform some changes to the edited + * object. This typically happens by invoking methods on the edited + * object.
        4. + *
        5. The editing phase is terminated by invoking the {@link #end()} + * method of the StateEdit. The end() method + * does two things. + * + *
            + *
          • The edited object receives a second request for storing + * its state. This time, it will use a different hash table, {@link + * #postState}.
          • + *
          • To increase efficiency, the StateEdit 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 equals method inherited from + * {@link java.lang.Object}.
          • + *
        6. + *
        7. When the user later chooses to undo the StateEdit, + * the edited object is asked to {@linkplain StateEditable#restoreState + * restore its state} from the {@link #preState} table. Similarly, + * when the user chooses to redo the StateEdit, + * the edited object gets asked to restore its state from the {@link + * #postState}.
        8. + *
        + * + * @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’s Revision Control + * System (RCS). This certainly should not be part of the API + * specification. But in order to be API-compatible with + * Sun’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 StateEdit. + */ + protected StateEditable object; + + + /** + * The state of object at the time of constructing + * this StateEdit. + */ + protected Hashtable preState; + + + /** + * The state of object at the time when {@link #end()} + * was called. + */ + protected Hashtable postState; + + + /** + * A human-readable name for this edit action. + */ + protected String undoRedoName; + + + /** + * Constructs a StateEdit, specifying the object whose + * state is being edited. + * + * @param obj the object whose state is being edited by this + * StateEdit. + */ + public StateEdit(StateEditable obj) + { + init(obj, null); + } + + + /** + * Constructs a StateEdit, specifying the object whose + * state is being edited. + * + * @param obj the object whose state is being edited by this + * StateEdit. + * + * @param name the human-readable name of the editing action. + */ + public StateEdit(StateEditable obj, String name) + { + init(obj, name); + } + + + /** + * Initializes this StateEdit. 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(); + postState = new Hashtable(); + obj.storeState(preState); + } + + + /** + * Informs this StateEdit 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 + * false, 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 + * false, 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 null 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 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. + * + *

        The following example shows how to write a class that implements + * this interface. + * + *

         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);
        + *   }
        + * }
        + * + * @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’s Revision Control + * System (RCS). This certainly should not be part of the API + * specification. But in order to be API-compatible with + * Sun’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. + * + *

        Note to implementors of this interface: To increase + * efficiency, the StateEdit 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 restoreState 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 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’s undo/redo + * functionality. + * + *

        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 + * UndoManager. To implement the “undo” and + * “redo” menu commands, the application invokes the + * UndoManager’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}. + * + *

        The UndoManager will only keep a specified number of editing + * actions, the limit. 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. + * + *

        Some applications do not provide separate menu commands for + * “undo” and “redo.” Instead, they + * have just a single command whose text switches between the two. + * Such applications would use an UndoManager with a limit + * of 1. The text of this combined menu item is available via + * {@link #getUndoOrRedoPresentationName}, and it is implemented + * by calling {@link #undoOrRedo}. + * + *

        Thread Safety: In constrast to the other classes of the + * javax.swing.undo package, the public methods of an + * UndoManager 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 serialver 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. + * + *

        Normally, the value of indexOfNextAdd equals + * the number of UndoableEdits stored by this UndoManager, i.e. + * edits.size(). For each call to {@link #undo}, + * indexOfNextAdd 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. + * + *

        The limit 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 limit 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 [from + * .. to] will receive a {@linkplain UndoableEdit#die() die + * message} before being removed from the edits array. If + * from is greater than to, 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 + * null 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 + * null 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. + * + *

        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 UndoManager whose limit 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. + * + *

        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 UndoManager whose limit is 1. + * + * @return true to indicate that this action can be + * undone or redone; false 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. + * + *

        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 UndoManager. + * + * @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 true to indicate that this action can be + * undone; false 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. + * + *

        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 redone + * in order of addition. Typical applications will never call {@link + * #end()} on their UndoManager. + * + * @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 true to indicate that this action can be + * redone; false 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 limit 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 true if edit could be + * incorporated; false if edit has not + * been incorporated because {@link #end()} has already been called + * on this UndoManager. + */ + 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. + * + *

        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 UndoManager whose limit 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. + * + *

        Thread Safety: 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 edit 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 true if the edit was combined successfully, and + * false if it could not be combined. + */ + boolean addEdit(UndoableEdit edit); + + /** + * Determines whether it would be possible to redo this editing + * action. + * + * @return true to indicate that this action can be + * redone, false otherwise. + * + * @see #redo() + * @see #canUndo() + */ + boolean canRedo(); + + /** + * Determines whether it would be possible to undo this editing + * action. + * + * @return true to indicate that this action can be + * undone, false 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 true to indicate that the action is + * significant enough for being separately undoable, or + * false 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 true if the edit is successfully replaced, and + * false 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 listeners = + new Vector(); + + + /** + * The source of the broadcast UndoableEditEvents. + */ + protected Object realSource; + + + /** + * Constructs a new helper for broadcasting UndoableEditEvents. The + * events will indicate the newly constructed + * UndoableEditSupport 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 realSource is + * null, the events will indicate the newly constructed + * UndoableEditSupport 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. + * + *

        Lack of Thread Safety: It is not safe to call + * this method from concurrent threads, unless the call is protected + * by a synchronization on this UndoableEditSupport + * instance. + * + * @param edit the edit action to be posted. + */ + protected void _postEdit(UndoableEdit edit) + { + UndoableEditEvent event; + Iterator 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(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)}. + * + *

        Thread Safety: 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. + * + *

        Thread Safety: 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 + * end message, and {@link #_postEdit} gets called in + * order to notify any listeners. Finally, the + * compoundEdit is discarded. + * + *

        Thread Safety: 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 @@ + + + + +GNU Classpath - javax.swing.undo + + +

        Provides a mechanism to support undo/redo operations.

        + + + -- cgit v1.2.3