/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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.*; /** * TinyLookAndFeel * * @version 1.0 * @author Hans Bickel */ 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( "TinyLookAndFeel", "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) {} } } String info = "TinyLaF v" + VERSION_STRING + "\n"; if(loadedFrom == null) { System.out.println(info + "'Default.theme' not found - using YQ default theme."); } else { System.out.println(info + "Theme: " + loadedFrom); } } /** * 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 "TinyLookAndFeel"; } /** * 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 "TinyLookAndFeel"; } /** * 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 "TinyLookAndFeel"; } /** * 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( "tinylookandfeel/icons/" + fileName); if(url == null) { // Another try url = TinyLookAndFeel.class.getResource( "/tinylookandfeel/icons/" + fileName); if(url == null) { System.err.println("TinyLaF: Icon directory could not be resolved."); return null; } } return new ImageIcon(url); } }