/******************************************************************************* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tiny Look and Feel * * (C) Copyright 2003 - 2005 by Hans Bickel * * * The * starting point for Tiny Look and Feel was the XP Look and Feel written * by * Stefan Krause. * The original header of this file was: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * XP Look and Feel * * (C) Copyright 2002, by Stefan Krause, Taufik Romdhane * and Contributors * * * The XP Look and Feel started as as extension to the * Metouia Look and Feel. * The original header of this file was: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Metouia Look And Feel: a free pluggable look and feel for java * * http://mlf.sourceforge.net * (C) Copyright 2002, by Taoufik Romdhane and * Contributors. * * This library is free software; you can redistribute it * and/or modify it * under the terms of the GNU Lesser General Public License * as published by * the Free Software Foundation; either version 2.1 of the * License, or (at * your option) any later version. * * This library is * distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; * without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU General Public * License along * with this program; if not, write to the Free Software * Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * * Original Author: Taoufik Romdhane * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package de.muntjak.tinylookandfeel; import java.awt.Color; import java.awt.Font; import java.awt.Insets; import java.awt.KeyboardFocusManager; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.security.AccessControlException; import java.security.AccessController; import java.security.PrivilegedAction; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.InsetsUIResource; import javax.swing.plaf.basic.BasicBorders; import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.plaf.metal.MetalTheme; import de.muntjak.tinylookandfeel.borders.TinyButtonBorder; import de.muntjak.tinylookandfeel.borders.TinyFrameBorder; import de.muntjak.tinylookandfeel.borders.TinyInternalFrameBorder; import de.muntjak.tinylookandfeel.borders.TinyPopupMenuBorder; import de.muntjak.tinylookandfeel.borders.TinyProgressBarBorder; import de.muntjak.tinylookandfeel.borders.TinyScrollPaneBorder; import de.muntjak.tinylookandfeel.borders.TinyTableHeaderBorder; import de.muntjak.tinylookandfeel.borders.TinyTableHeaderRolloverBorder; import de.muntjak.tinylookandfeel.borders.TinyTableScrollPaneBorder; import de.muntjak.tinylookandfeel.borders.TinyTextFieldBorder; import de.muntjak.tinylookandfeel.borders.TinyToolBarBorder; import de.muntjak.tinylookandfeel.borders.TinyToolTipBorder; /** * TinyLookAndFeel * * @version 1.0 * @author Hans Bickel */ @SuppressWarnings ( { "all" } ) public class TinyLookAndFeel extends MetalLookAndFeel { /** Will be set to true in ControlPanel.main */ public static boolean controlPanelInstantiated = false; // this constant is the minimum width for TitlePane.TitlePaneLayout, // it is from interest only if a frame or dialog is decorated. static final int MINIMUM_FRAME_WIDTH = 104; // the minimum width for internal frames and palettes static final int MINIMUM_INTERNAL_FRAME_WIDTH = 32; // -1 = unspecified // 0 = JRE v1.4 // 1 = JRE v1.5 or higher private static int is1dot4 = -1; public static final String VERSION_STRING = "1.3.8"; public static final String DATE_STRING = "2007-6-17"; protected static TinyDefaultTheme defaultTheme; /** * The installation state of the TinyLaF Look and Feel. */ private static boolean isInstalled = false; /** * The installation state of the TinyLaF Theme. */ private static boolean themeHasBeenSet = false; /** * UIManager.setLookAndFeel calls this method before the first call (and * typically the only call) to getDefaults(). Subclasses should do any * one-time setup they need here, rather than in a static initializer, because * look and feel class objects may be loaded just to discover that * isSupportedLookAndFeel() returns false. * * @see #uninitialize * @see UIManager#setLookAndFeel */ public void initialize () { super.initialize (); if ( !isInstalled ) { isInstalled = true; // Note: We must know (for TinyMouseHandler) on // which JRE version we are running. Because we // might be an applet, we execute a privileged block. if ( is1dot4 == -1 ) { String version = ( String ) AccessController .doPrivileged ( new PrivilegedAction () { public Object run () { return System.getProperty ( "java.version" ); } } ); if ( version != null ) { is1dot4 = version.startsWith ( "1.0" ) || version.startsWith ( "1.1" ) || version.startsWith ( "1.2" ) || version.startsWith ( "1.3" ) || version.startsWith ( "1.4" ) ? 0 : 1; } else { // give up - assume we are 1.5 or higher is1dot4 = 1; } } searchDefaultTheme (); UIManager.installLookAndFeel ( new UIManager.LookAndFeelInfo ( "TinyLaF", "de.muntjak.tinylookandfeel.TinyLookAndFeel" ) ); } // Execute this one even if isInstalled is true // New in 1.3.6 if ( !"false".equals ( System.getProperty ( "altClosesMenu" ) ) ) { KeyboardFocusManager.getCurrentKeyboardFocusManager () .addKeyEventPostProcessor ( TinyMenuUI.altProcessor ); } } /** * @return true if we are running Java 1.4 or lower, false otherwise */ public static boolean is1dot4 () { return ( is1dot4 == 0 ); } public void uninitialize () { super.uninitialize (); KeyboardFocusManager.getCurrentKeyboardFocusManager () .removeKeyEventPostProcessor ( TinyMenuUI.altProcessor ); } private void searchDefaultTheme () { // only if running without the control panel if ( controlPanelInstantiated ) return; String loadedFrom = null; URL defaultURL = TinyLookAndFeel.class.getResource ( "/" + Theme.DEFAULT_THEME ); if ( Theme.loadTheme ( defaultURL, Theme.CUSTOM_STYLE ) ) { Theme.style = Theme.CUSTOM_STYLE; loadedFrom = defaultURL.toExternalForm (); } else { defaultURL = Thread.currentThread ().getContextClassLoader () .getResource ( Theme.DEFAULT_THEME ); if ( Theme.loadTheme ( defaultURL, Theme.CUSTOM_STYLE ) ) { Theme.style = Theme.CUSTOM_STYLE; loadedFrom = defaultURL.toExternalForm (); } else { try { defaultURL = new File ( System.getProperty ( "user.home" ), Theme.DEFAULT_THEME ).toURI ().toURL (); if ( Theme.loadTheme ( defaultURL, Theme.CUSTOM_STYLE ) ) { Theme.style = Theme.CUSTOM_STYLE; loadedFrom = defaultURL.toExternalForm (); } else { defaultURL = new File ( System.getProperty ( "user.dir" ), Theme.DEFAULT_THEME ).toURI ().toURL (); if ( Theme.loadTheme ( defaultURL, Theme.CUSTOM_STYLE ) ) { Theme.style = Theme.CUSTOM_STYLE; loadedFrom = defaultURL.toExternalForm (); } // else we give up } } catch ( MalformedURLException ignore ) { } // AccessControlException is thrown when running // with Java Web Start catch ( AccessControlException ignore ) { } } } } /** * Return a string that identifies this look and feel. This string will be * used by applications/services that want to recognize well known look and * feel implementations. Presently the well known names are "Motif", * "Windows", "Mac", "Metal". Note that a LookAndFeel derived from a well * known superclass that doesn't make any fundamental changes to the look or * feel shouldn't override this method. * * @return The TinyLaF Look and Feel identifier. */ public String getID () { return "TinyLaF"; } /** * Return a short string that identifies this look and feel, e.g. "CDE/Motif". * This string should be appropriate for a menu item. Distinct look and feels * should have different names, e.g. a subclass of MotifLookAndFeel that * changes the way a few components are rendered should be called "CDE/Motif * My Way"; something that would be useful to a user trying to select a L&F * from a list of names. * * @return The look and feel short name. */ public String getName () { return "TinyLaF"; } /** * Return a one line description of this look and feel implementation, e.g. * "The CDE/Motif Look and Feel". This string is intended for the user, e.g. * in the title of a window or in a ToolTip message. * * @return The look and feel short description. */ public String getDescription () { return "TinyLaF"; } /** * If the underlying platform has a "native" look and feel, and this is an * implementation of it, return true. For example a CDE/Motif look and * implementation would return true when the underlying platform was Solaris. */ public boolean isNativeLookAndFeel () { return false; } /** * Return true if the underlying platform supports and or permits this look * and feel. This method returns false if the look and feel depends on special * resources or legal agreements that aren't defined for the current platform. */ public final boolean isSupportedLookAndFeel () { return true; } public boolean getSupportsWindowDecorations () { return true; } /** * Initializes the uiClassID to BasicComponentUI mapping. The JComponent * classes define their own uiClassID constants. This table must map those * constants to a BasicComponentUI class of the appropriate type. * * @param table The ui defaults table. */ protected void initClassDefaults ( UIDefaults table ) { super.initClassDefaults ( table ); table.putDefaults ( new Object [] { "ButtonUI", "de.muntjak.tinylookandfeel.TinyButtonUI", "CheckBoxUI", "de.muntjak.tinylookandfeel.TinyCheckBoxUI", "TextFieldUI", "de.muntjak.tinylookandfeel.TinyTextFieldUI", "TextAreaUI", "de.muntjak.tinylookandfeel.TinyTextAreaUI", /* New in 1.3.6: Removed entry for FormattedTextFieldUI */ "PasswordFieldUI", "de.muntjak.tinylookandfeel.TinyPasswordFieldUI", "EditorPaneUI", "de.muntjak.tinylookandfeel.TinyEditorPaneUI", "TextPaneUI", "de.muntjak.tinylookandfeel.TinyTextPaneUI", "SliderUI", "de.muntjak.tinylookandfeel.TinySliderUI", "SpinnerUI", "de.muntjak.tinylookandfeel.TinySpinnerUI", "ToolBarUI", "de.muntjak.tinylookandfeel.TinyToolBarUI", "ToolBarSeparatorUI", "de.muntjak.tinylookandfeel.TinyToolBarSeparatorUI", "MenuBarUI", "de.muntjak.tinylookandfeel.TinyMenuBarUI", "MenuUI", "de.muntjak.tinylookandfeel.TinyMenuUI", "MenuItemUI", "de.muntjak.tinylookandfeel.TinyMenuItemUI", "CheckBoxMenuItemUI", "de.muntjak.tinylookandfeel.TinyCheckBoxMenuItemUI", "RadioButtonMenuItemUI", "de.muntjak.tinylookandfeel.TinyRadioButtonMenuItemUI", "ScrollBarUI", "de.muntjak.tinylookandfeel.TinyScrollBarUI", "TabbedPaneUI", "de.muntjak.tinylookandfeel.TinyTabbedPaneUI", "ToggleButtonUI", "de.muntjak.tinylookandfeel.TinyButtonUI", "ScrollPaneUI", "de.muntjak.tinylookandfeel.TinyScrollPaneUI", "ProgressBarUI", "de.muntjak.tinylookandfeel.TinyProgressBarUI", "InternalFrameUI", "de.muntjak.tinylookandfeel.TinyInternalFrameUI", "RadioButtonUI", "de.muntjak.tinylookandfeel.TinyRadioButtonUI", "ComboBoxUI", "de.muntjak.tinylookandfeel.TinyComboBoxUI", "PopupMenuSeparatorUI", "de.muntjak.tinylookandfeel.TinyPopupMenuSeparatorUI", "SeparatorUI", "de.muntjak.tinylookandfeel.TinySeparatorUI", "SplitPaneUI", "de.muntjak.tinylookandfeel.TinySplitPaneUI", "FileChooserUI", "de.muntjak.tinylookandfeel.TinyFileChooserUI", "ListUI", "de.muntjak.tinylookandfeel.TinyListUI", "TreeUI", "de.muntjak.tinylookandfeel.TinyTreeUI", "LabelUI", "de.muntjak.tinylookandfeel.TinyLabelUI", "TableUI", "de.muntjak.tinylookandfeel.TinyTableUI", "TableHeaderUI", "de.muntjak.tinylookandfeel.TinyTableHeaderUI", "ToolTipUI", "de.muntjak.tinylookandfeel.TinyToolTipUI", "RootPaneUI", "de.muntjak.tinylookandfeel.TinyRootPaneUI", "DesktopPaneUI", "de.muntjak.tinylookandfeel.TinyDesktopPaneUI" } ); } /** * Creates the default theme and installs it. The TinyDefaultTheme is used as * default. */ protected void createDefaultTheme () { // if(JFrame.isDefaultLookAndFeelDecorated()) { // System.setProperty("sun.awt.noerasebackground", "true"); // } // Note: up to 1.3.02, the if-clause below prevented // the theme from being re-loaded when refreshing an Applet page // if(!themeHasBeenSet) { defaultTheme = new TinyDefaultTheme (); setCurrentTheme ( defaultTheme ); // } } /** * Sets the current color theme. Warning: The theme must be an instance of * TinyDefaultTheme! * * @param theme the theme to install. */ public static void setCurrentTheme ( MetalTheme theme ) { MetalLookAndFeel.setCurrentTheme ( theme ); themeHasBeenSet = true; } /** * Initializes the default values for many ui widgets and puts them in the * given ui defaults table. Here is the place where borders can be changed. * * @param table The ui defaults table. */ protected void initComponentDefaults ( UIDefaults table ) { super.initComponentDefaults ( table ); // Replace Metal borders: Border border = new EmptyBorder ( 0, 0, 0, 0 ); table.put ( "Button.border", new BorderUIResource.CompoundBorderUIResource ( new TinyButtonBorder (), new BasicBorders.MarginBorder () ) ); table.put ( "FormattedTextField.border", new TinyTextFieldBorder () ); table.put ( "TextField.border", new TinyTextFieldBorder () ); table.put ( "PasswordField.border", new TinyTextFieldBorder () ); table.put ( "ComboBox.border", border ); table.put ( "Table.scrollPaneBorder", new TinyTableScrollPaneBorder () ); table.put ( "TableHeader.cellBorder", new TinyTableHeaderBorder () ); table.put ( "TableHeader.cellRolloverBorder", new TinyTableHeaderRolloverBorder () ); table.put ( "Spinner.border", new TinyTextFieldBorder ( new Insets ( 2, 2, 2, 2 ) ) ); table.put ( "ProgressBar.border", new TinyProgressBarBorder () ); table.put ( "ToolBar.border", new TinyToolBarBorder () ); table.put ( "ToolTip.border", new BorderUIResource ( new TinyToolTipBorder ( true ) ) ); table.put ( "ToolTip.borderInactive", new BorderUIResource ( new TinyToolTipBorder ( false ) ) ); border = new TinyInternalFrameBorder (); table.put ( "InternalFrame.border", border ); table.put ( "InternalFrame.paletteBorder", border ); table.put ( "InternalFrame.optionDialogBorder", border ); border = new EmptyBorder ( 2, 4, 2, 4 ); table.put ( "Menu.border", border ); table.put ( "MenuItem.border", border ); table.put ( "CheckBoxMenuItem.border", border ); table.put ( "RadioButtonMenuItem.border", border ); table.put ( "PopupMenu.border", new TinyPopupMenuBorder () ); table.put ( "ScrollPane.border", new TinyScrollPaneBorder () ); table.put ( "Slider.trackWidth", new Integer ( 4 ) ); // Note: Margins correspond to borders. In TinyLaF 1.2 checkboxes // and radio buttons had the Metal border which itself adds insets // of (2, 2, 2, 2) - so a 1.3 checkbox/radio button has the same // visible margin as a 1.2 checkbox/radio button because margins // in 1.2 where (0, 0, 0, 0) table.put ( "CheckBox.border", new BasicBorders.MarginBorder () ); table.put ( "RadioButton.border", new BasicBorders.MarginBorder () ); table.put ( "RadioButton.margin", new InsetsUIResource ( 2, 2, 2, 2 ) ); table.put ( "CheckBox.margin", new InsetsUIResource ( 2, 2, 2, 2 ) ); // Tweak some subtle values: table.put ( "SplitPane.dividerSize", new Integer ( 7 ) ); table.put ( "InternalFrame.normalTitleFont", new Font ( "dialog", Font.BOLD, 13 ) ); // New in 1.3.7 - value is evaluated at // javax.swing.plaf.basic.BasicFileChooserUI.installDefaults(JFileChooser) if ( System.getProperty ( "os.name" ).toLowerCase ().startsWith ( "linux" ) ) { table.put ( "FileChooser.readOnly", Boolean.TRUE ); } table.put ( "TabbedPane.tabInsets", new Insets ( 1, 6, 4, 6 ) ); table.put ( "TabbedPane.selectedTabPadInsets", new Insets ( 2, 2, 1, 2 ) ); table.put ( "TabbedPane.unselected", new ColorUIResource ( 0, 0, 0 ) ); table.put ( "TabbedPane.tabAreaInsets", new Insets ( 6, 2, 0, 0 ) ); table.put ( "TabbedPane.contentBorderInsets", new Insets ( 1, 1, 3, 3 ) ); table.put ( "PopupMenu.foreground", new Color ( 255, 0, 0 ) ); table.put ( "RootPane.colorChooserDialogBorder", TinyFrameBorder .getInstance () ); table.put ( "RootPane.errorDialogBorder", TinyFrameBorder.getInstance () ); table.put ( "RootPane.fileChooserDialogBorder", TinyFrameBorder .getInstance () ); table.put ( "RootPane.frameBorder", TinyFrameBorder.getInstance () ); table.put ( "RootPane.informationDialogBorder", TinyFrameBorder .getInstance () ); table.put ( "RootPane.plainDialogBorder", TinyFrameBorder.getInstance () ); table .put ( "RootPane.questionDialogBorder", TinyFrameBorder.getInstance () ); table.put ( "RootPane.warningDialogBorder", TinyFrameBorder.getInstance () ); table.put ( "CheckBoxMenuItem.checkIcon", MenuItemIconFactory .getCheckBoxMenuItemIcon () ); table.put ( "RadioButtonMenuItem.checkIcon", MenuItemIconFactory .getRadioButtonMenuItemIcon () ); table.put ( "Menu.arrowIcon", MenuItemIconFactory.getMenuArrowIcon () ); table.put ( "InternalFrame.frameTitleHeight", new Integer ( 25 ) ); table.put ( "InternalFrame.paletteTitleHeight", new Integer ( 16 ) ); table .put ( "InternalFrame.icon", loadIcon ( "InternalFrameIcon.png", this ) ); table.put ( "Tree.expandedIcon", loadIcon ( "TreeMinusIcon.png", this ) ); table.put ( "Tree.collapsedIcon", loadIcon ( "TreePlusIcon.png", this ) ); table.put ( "Tree.openIcon", loadIcon ( "TreeFolderOpenedIcon.png", this ) ); table .put ( "Tree.closedIcon", loadIcon ( "TreeFolderClosedIcon.png", this ) ); table.put ( "Tree.leafIcon", loadIcon ( "TreeLeafIcon.png", this ) ); table .put ( "FileView.directoryIcon", loadIcon ( "DirectoryIcon.png", this ) ); table.put ( "FileView.computerIcon", loadIcon ( "ComputerIcon.png", this ) ); table.put ( "FileView.fileIcon", loadIcon ( "FileIcon.png", this ) ); table .put ( "FileView.floppyDriveIcon", loadIcon ( "FloppyIcon.png", this ) ); table .put ( "FileView.hardDriveIcon", loadIcon ( "HarddiskIcon.png", this ) ); table.put ( "FileChooser.detailsViewIcon", loadIcon ( "FileDetailsIcon.png", this ) ); table.put ( "FileChooser.homeFolderIcon", loadIcon ( "HomeFolderIcon.png", this ) ); table.put ( "FileChooser.listViewIcon", loadIcon ( "FileListIcon.png", this ) ); table.put ( "FileChooser.newFolderIcon", loadIcon ( "NewFolderIcon.png", this ) ); table.put ( "FileChooser.upFolderIcon", loadIcon ( "ParentDirectoryIcon.png", this ) ); table.put ( "OptionPane.errorIcon", loadIcon ( "ErrorIcon.png", this ) ); table.put ( "OptionPane.informationIcon", loadIcon ( "InformationIcon.png", this ) ); table.put ( "OptionPane.warningIcon", loadIcon ( "WarningIcon.png", this ) ); table .put ( "OptionPane.questionIcon", loadIcon ( "QuestionIcon.png", this ) ); } public static Icon getUncolorizedSystemIcon ( int index ) { switch ( index ) { case 0 : return loadIcon ( "InternalFrameIcon.png", null ); case 1 : return loadIcon ( "TreeFolderClosedIcon.png", null ); case 2 : return loadIcon ( "TreeFolderOpenedIcon.png", null ); case 3 : return loadIcon ( "TreeLeafIcon.png", null ); case 4 : return loadIcon ( "TreeMinusIcon.png", null ); case 5 : return loadIcon ( "TreePlusIcon.png", null ); case 6 : return loadIcon ( "ComputerIcon.png", null ); case 7 : return loadIcon ( "FloppyIcon.png", null ); case 8 : return loadIcon ( "HarddiskIcon.png", null ); case 9 : return loadIcon ( "DirectoryIcon.png", null ); case 10 : return loadIcon ( "FileIcon.png", null ); case 11 : return loadIcon ( "ParentDirectoryIcon.png", null ); case 12 : return loadIcon ( "HomeFolderIcon.png", null ); case 13 : return loadIcon ( "NewFolderIcon.png", null ); case 14 : return loadIcon ( "FileListIcon.png", null ); case 15 : return loadIcon ( "FileDetailsIcon.png", null ); case 16 : return loadIcon ( "InformationIcon.png", null ); case 17 : return loadIcon ( "QuestionIcon.png", null ); case 18 : return loadIcon ( "WarningIcon.png", null ); default : return loadIcon ( "ErrorIcon.png", null ); } } public static String getSystemIconName ( int index ) { switch ( index ) { case 0 : return "InternalFrame.icon"; case 1 : return "Tree.closedIcon"; case 2 : return "Tree.openIcon"; case 3 : return "Tree.leafIcon"; case 4 : return "Tree.expandedIcon"; case 5 : return "Tree.collapsedIcon"; case 6 : return "FileView.computerIcon"; case 7 : return "FileView.floppyDriveIcon"; case 8 : return "FileView.hardDriveIcon"; case 9 : return "FileView.directoryIcon"; case 10 : return "FileView.fileIcon"; case 11 : return "FileChooser.upFolderIcon"; case 12 : return "FileChooser.homeFolderIcon"; case 13 : return "FileChooser.newFolderIcon"; case 14 : return "FileChooser.listViewIcon"; case 15 : return "FileChooser.detailsViewIcon"; case 16 : return "OptionPane.informationIcon"; case 17 : return "OptionPane.questionIcon"; case 18 : return "OptionPane.warningIcon"; default : return "OptionPane.errorIcon"; } } /** * Loads an image icon. * * @param file The image file name. * @param invoker The refence of the invoking class, whose classloader will be * used for loading the image. */ public static ImageIcon loadIcon ( final String fileName, final Object invoker ) { // This should work for both applications and applets URL url = Thread.currentThread ().getContextClassLoader ().getResource ( "de/muntjak/tinylookandfeel/icons/" + fileName ); if ( url == null ) { // Another try url = TinyLookAndFeel.class .getResource ( "/de/muntjak/tinylookandfeel/icons/" + fileName ); if ( url == null ) { System.err.println ( "TinyLaF: Icon directory could not be resolved." ); return null; } } return new ImageIcon ( url ); } }