d8edaf74154d4a71ed46735d6120197d33cce3b6
[idea/community.git] / platform / platform-impl / src / com / intellij / ide / ui / LafManagerImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.ide.ui;
17
18 import com.intellij.CommonBundle;
19 import com.intellij.ide.IdeBundle;
20 import com.intellij.idea.StartupUtil;
21 import com.intellij.openapi.components.*;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.ui.Messages;
24 import com.intellij.openapi.util.Comparing;
25 import com.intellij.openapi.util.IconLoader;
26 import com.intellij.openapi.util.SystemInfo;
27 import com.intellij.ui.ColoredSideBorder;
28 import com.intellij.ui.IdeaBlueMetalTheme;
29 import com.intellij.ui.ScreenUtil;
30 import com.intellij.ui.mac.MacPopupMenuUI;
31 import com.intellij.ui.plaf.beg.*;
32 import com.intellij.util.ui.UIUtil;
33 import com.sun.java.swing.plaf.windows.WindowsLookAndFeel;
34 import org.jdom.Element;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38
39 import javax.swing.*;
40 import javax.swing.border.Border;
41 import javax.swing.event.EventListenerList;
42 import javax.swing.plaf.ColorUIResource;
43 import javax.swing.plaf.FontUIResource;
44 import javax.swing.plaf.metal.DefaultMetalTheme;
45 import javax.swing.plaf.metal.MetalLookAndFeel;
46 import javax.swing.text.DefaultEditorKit;
47 import java.awt.*;
48 import java.awt.event.KeyEvent;
49 import java.lang.reflect.InvocationTargetException;
50 import java.lang.reflect.Method;
51 import java.util.Arrays;
52 import java.util.Comparator;
53 import java.util.HashMap;
54
55 /**
56  * @author Eugene Belyaev
57  * @author Vladimir Kondratyev
58  */
59 @State(
60     name = "LafManager",
61     roamingType = RoamingType.PER_PLATFORM,
62     storages = {@Storage(
63         id = "other",
64         file = "$APP_CONFIG$/options.xml")})
65 public final class LafManagerImpl extends LafManager implements ApplicationComponent, PersistentStateComponent<Element> {
66   private static final Logger LOG=Logger.getInstance("#com.intellij.ide.ui.LafManager");
67
68   @NonNls private static final String IDEA_LAF_CLASSNAME = "idea.laf.classname";
69
70   /**
71    * One of the possible values of -Didea.popup.weight property. Heavy weight means
72    * that all popups are shown inside the window. Under UNIXes it's possible to configure
73    * window manager "Focus follows mouse with Auto Raise". In this case popup window will
74    * be immediately closed after showing.
75    */
76   @NonNls private static final String HEAVY_WEIGHT_POPUP="heavy";
77   /**
78    * One of the possible values of -Didea.popup.weight property. Medium weight means
79    * that popup will be shouw inside the paren't JLayeredPane if it can be fit to it.
80    * Otherwise popup will be shown in the window. This mode is defaut for the Swing but
81    * it's very slow (much slower then heavy weight popups).
82    */
83   @NonNls private static final String MEDIUM_WEIGHT_POPUP="medium";
84
85   private final EventListenerList myListenerList;
86   private final UIManager.LookAndFeelInfo[] myLafs;
87   private UIManager.LookAndFeelInfo myCurrentLaf;
88
89   @NonNls private static final String[] ourPatcheableFontResources = new String[]{
90     "Button.font", "ToggleButton.font", "RadioButton.font", "CheckBox.font", "ColorChooser.font", "ComboBox.font",
91     "Label.font", "List.font", "MenuBar.font", "MenuItem.font", "MenuItem.acceleratorFont", "RadioButtonMenuItem.font",
92     "CheckBoxMenuItem.font", "Menu.font", "PopupMenu.font", "OptionPane.font", "Panel.font", "ProgressBar.font",
93     "ScrollPane.font", "Viewport.font", "TabbedPane.font", "Table.font", "TableHeader.font", "TextField.font",
94     "PasswordField.font", "TextArea.font", "TextPane.font", "EditorPane.font", "TitledBorder.font", "ToolBar.font",
95     "ToolTip.font", "Tree.font"
96   };
97   @NonNls private static final String[] ourFileChooserTextKeys = new String[] {
98     "FileChooser.viewMenuLabelText", "FileChooser.newFolderActionLabelText", "FileChooser.listViewActionLabelText",
99     "FileChooser.detailsViewActionLabelText", "FileChooser.refreshActionLabelText"
100   };
101
102   private final HashMap<UIManager.LookAndFeelInfo, HashMap<String, Object>> myStoredDefaults = new HashMap<UIManager.LookAndFeelInfo, HashMap<String, Object>>();
103   private final UISettings myUiSettings;
104
105   @NonNls private static final String ELEMENT_LAF = "laf";
106   @NonNls private static final String ATTRIBUTE_CLASS_NAME = "class-name";
107
108   /** invoked by reflection
109    * @param uiSettings   */
110   LafManagerImpl(UISettings uiSettings){
111     myUiSettings = uiSettings;
112     myListenerList=new EventListenerList();
113
114     IdeaLookAndFeelInfo ideaLaf=new IdeaLookAndFeelInfo();
115     UIManager.LookAndFeelInfo[] installedLafs=UIManager.getInstalledLookAndFeels();
116
117     // Get all installed LAFs
118     myLafs=new UIManager.LookAndFeelInfo[1+installedLafs.length];
119     myLafs[0]=ideaLaf;
120     System.arraycopy(installedLafs,0,myLafs,1,installedLafs.length);
121     Arrays.sort(myLafs,new MyComparator());
122
123     // Setup current LAF. Unfortunately it's system depended.
124     myCurrentLaf=getDefaultLaf();    
125   }
126
127   /**
128    * Adds specified listener
129    */
130   public void addLafManagerListener(@NotNull final LafManagerListener l){
131     myListenerList.add(LafManagerListener.class, l);
132   }
133
134   /**
135    * Removes specified listener
136    */
137   public void removeLafManagerListener(@NotNull final LafManagerListener l){
138     myListenerList.remove(LafManagerListener.class, l);
139   }
140
141   private void fireLookAndFeelChanged(){
142     LafManagerListener[] listeners = myListenerList.getListeners(LafManagerListener.class);
143     for (LafManagerListener listener : listeners) {
144       listener.lookAndFeelChanged(this);
145     }
146   }
147
148   @NotNull
149   public String getComponentName(){
150     return "LafManager";
151   }
152
153   public void initComponent() {
154     setCurrentLookAndFeel(findLaf(myCurrentLaf.getClassName())); // setup default LAF or one specfied by readExternal.
155     updateUI();
156   }
157
158   public void disposeComponent(){}
159
160   public void loadState(final Element element) {
161     String className=null;
162     for (final Object o : element.getChildren()) {
163       Element child = (Element)o;
164       if (ELEMENT_LAF.equals(child.getName())) {
165         className = child.getAttributeValue(ATTRIBUTE_CLASS_NAME);
166         break;
167       }
168     }
169
170     UIManager.LookAndFeelInfo laf=findLaf(className);
171     // If LAF is undefined (wrong class name or something else) we have set default LAF anyway.
172     if(laf==null){
173       laf=getDefaultLaf();
174     }
175
176     if (myCurrentLaf != null && !laf.getClassName().equals(myCurrentLaf.getClassName())) {
177       setCurrentLookAndFeel(laf);
178       updateUI();
179     }
180
181     myCurrentLaf=laf;
182   }
183
184   public Element getState() {
185     Element element = new Element("state");
186     if(myCurrentLaf.getClassName()!=null){
187       Element child=new Element(ELEMENT_LAF);
188       child.setAttribute(ATTRIBUTE_CLASS_NAME,myCurrentLaf.getClassName());
189       element.addContent(child);
190     }
191     return element;
192   }
193
194   public UIManager.LookAndFeelInfo[] getInstalledLookAndFeels(){
195     return myLafs.clone();
196   }
197
198   public UIManager.LookAndFeelInfo getCurrentLookAndFeel(){
199     return myCurrentLaf;
200   }
201
202   public boolean isUnderAquaLookAndFeel() {
203     //noinspection HardCodedStringLiteral
204     return "Mac OS X".equals(getCurrentLookAndFeel().getName());
205   }
206
207   public boolean isUnderQuaquaLookAndFeel() {
208     //noinspection HardCodedStringLiteral
209     return "Quaqua".equals(getCurrentLookAndFeel().getName());
210   }
211
212   /**
213    * @return default LookAndFeelInfo for the running OS. For Win32 and
214    * Linux the method returns Alloy LAF or IDEA LAF if first not found, for Mac OS X it returns Aqua
215    */
216   private UIManager.LookAndFeelInfo getDefaultLaf(){
217     if(SystemInfo.isMac) {
218       UIManager.LookAndFeelInfo laf=findLaf(UIManager.getSystemLookAndFeelClassName());
219       LOG.assertTrue(laf!=null);
220       return laf;
221     }
222     else {
223       String defaultLafName = StartupUtil.getDefaultLAF();
224       if (defaultLafName != null) {
225         UIManager.LookAndFeelInfo defaultLaf = findLaf(defaultLafName);
226         if (defaultLaf != null) {
227           return defaultLaf;
228         }
229       }
230       return findLaf(IDEA_LAF_CLASSNAME);
231     }
232   }
233
234   /**
235    * Finds LAF by its class name.
236    * will be returned.
237    */
238   private UIManager.LookAndFeelInfo findLaf(String className){
239     for (UIManager.LookAndFeelInfo laf : myLafs) {
240       if (Comparing.equal(laf.getClassName(), className)) {
241         return laf;
242       }
243     }
244     return null;
245   }
246
247   /**
248    * Sets current LAF. The method doesn't update component hierarchy.
249    */
250   public void setCurrentLookAndFeel(UIManager.LookAndFeelInfo lookAndFeelInfo){
251     if(findLaf(lookAndFeelInfo.getClassName())==null){
252       LOG.error("unknown LookAndFeel : "+lookAndFeelInfo);
253       return;
254     }
255
256     // Set L&F
257
258     if(IDEA_LAF_CLASSNAME.equals(lookAndFeelInfo.getClassName())){ // that is IDEA default LAF
259       IdeaLaf laf=new IdeaLaf();
260       IdeaLaf.setCurrentTheme(new IdeaBlueMetalTheme());
261       try {
262         UIManager.setLookAndFeel(laf);
263       } catch (Exception exc) {
264         Messages.showMessageDialog(
265           IdeBundle.message("error.cannot.set.look.and.feel", lookAndFeelInfo.getName()),
266           CommonBundle.getErrorTitle(),
267           Messages.getErrorIcon()
268         );
269         return;
270       }
271     }else{ // non default LAF
272       try {
273         LookAndFeel laf=((LookAndFeel)Class.forName(lookAndFeelInfo.getClassName()).newInstance());
274         if(laf instanceof MetalLookAndFeel){
275           MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
276         }
277         UIManager.setLookAndFeel(laf);
278       } catch(Exception exc) {
279         Messages.showMessageDialog(
280           IdeBundle.message("error.cannot.set.look.and.feel", lookAndFeelInfo.getName()),
281           CommonBundle.getErrorTitle(),
282           Messages.getErrorIcon()
283         );
284         return;
285       }
286     }
287     myCurrentLaf=lookAndFeelInfo;
288
289     // The following code is a trick! By default Swing uses lightweight and "medium" weight
290     // popups to show JPopupMenu. The code below force the creation of real heavyweight menus.
291     // It dramatically increases speed of popups.
292
293     //noinspection HardCodedStringLiteral
294     String popupWeight=System.getProperty("idea.popup.weight");
295     if(popupWeight==null){ // use defaults if popup weight isn't specified
296       if(SystemInfo.isWindows){
297         popupWeight=HEAVY_WEIGHT_POPUP;
298       }else{ // UNIXes (Linux and MAC) go here
299         popupWeight=MEDIUM_WEIGHT_POPUP;
300       }
301     }
302
303     if (SystemInfo.isMacOSLeopard) {
304       // Force heavy weight popups under Leopard, otherwise they don't have shadow or any kind of border.
305       popupWeight = HEAVY_WEIGHT_POPUP;
306     }
307
308     popupWeight = popupWeight.trim();
309
310     PopupFactory popupFactory;
311
312     final PopupFactory oldFactory = PopupFactory.getSharedInstance();
313     if (!(oldFactory instanceof OurPopupFactory)) {
314       if (HEAVY_WEIGHT_POPUP.equals(popupWeight)) {
315         popupFactory = new OurPopupFactory() {
316           public Popup getPopup(
317             Component owner,
318             Component contents,
319             int x,
320             int y
321           ) throws IllegalArgumentException {
322             final Point point = fixPopupLocation(contents, x, y);
323             if (SystemInfo.isLinux) {
324               return new Popup(owner, contents, point.x, point.y){};
325             }
326             return oldFactory.getPopup(owner, contents, point.x, point.y);
327           }
328         };
329       }
330       else if (MEDIUM_WEIGHT_POPUP.equals(popupWeight)) {
331         popupFactory = new OurPopupFactory() {
332
333           public Popup getPopup(final Component owner, final Component contents, final int x, final int y) throws IllegalArgumentException {
334             return createPopup(owner, contents, x, y);
335           }
336
337           private Popup createPopup(final Component owner, final Component contents, final int x, final int y) {
338             final Point point = fixPopupLocation(contents, x, y);
339             if (SystemInfo.isLinux) {
340               return new Popup(owner, contents, point.x, point.y){};
341             }
342             return oldFactory.getPopup(owner, contents, point.x, point.y);
343           }
344         };
345       }
346       else {
347         throw new IllegalStateException("unknown value of property -Didea.popup.weight: " + popupWeight);
348       }
349       PopupFactory.setSharedInstance(popupFactory);
350     }
351
352     // update ui for popup menu to get round corners
353     if (UIUtil.isUnderAquaLookAndFeel()) {
354       final UIDefaults uiDefaults = UIManager.getLookAndFeelDefaults();
355       uiDefaults.put("PopupMenuUI", MacPopupMenuUI.class.getCanonicalName());
356       final Icon icon = getAquaMenuInvertedIcon();
357       if (icon != null) {
358         uiDefaults.put("Menu.invertedArrowIcon", icon);
359       }
360     }
361   }
362
363   @Nullable
364   private static Icon getAquaMenuInvertedIcon() {
365     if (!UIUtil.isUnderAquaLookAndFeel()) return null;
366     final Icon arrow = (Icon) UIManager.get("Menu.arrowIcon");
367     if (arrow == null) return null;
368
369     try {
370       final Method method = arrow.getClass().getMethod("getInvertedIcon");
371       if (method != null) {
372         method.setAccessible(true);
373         return (Icon) method.invoke(arrow);
374       }
375
376       return null;
377     }
378     catch (NoSuchMethodException e1) {
379       return null;
380     }
381     catch (InvocationTargetException e1) {
382       return null;
383     }
384     catch (IllegalAccessException e1) {
385       return null;
386     }
387   }
388
389   private Point fixPopupLocation(final Component contents, final int x, final int y) {
390     if (!(contents instanceof JToolTip)) return new Point(x, y);
391
392     final PointerInfo info;
393     try {
394       info = MouseInfo.getPointerInfo();
395     }
396     catch (InternalError e) {
397       // http://www.jetbrains.net/jira/browse/IDEADEV-21390
398       // may happen under Mac OSX 10.5
399       return new Point(x, y);
400     }
401     int deltaY = 0;
402
403     if (info != null) {
404       final Point mouse = info.getLocation();
405       deltaY = mouse.y - y;
406     }
407
408     final Dimension size = contents.getPreferredSize();
409     final Rectangle rec = new Rectangle(new Point(x, y), size);
410     ScreenUtil.moveRectangleToFitTheScreen(rec);
411
412     if (rec.y < y) {
413       rec.y += deltaY;
414     }
415
416     return rec.getLocation();
417   }
418
419
420   /**
421    * Updates LAF of all windows. The method also updates font of components
422    * as it's configured in <code>UISettings</code>.
423    */
424   public void updateUI(){
425     UIDefaults lookAndFeelDefaults=UIManager.getLookAndFeelDefaults();
426     initInputMapDefaults(lookAndFeelDefaults);
427     patchFileChooserStrings(lookAndFeelDefaults);
428     if (shouldPatchLAFFonts()) {
429       storeOriginalFontDefaults(lookAndFeelDefaults);
430       initFontDefaults(lookAndFeelDefaults);
431     }
432     else {
433       restoreOriginalFontDefaults(lookAndFeelDefaults);
434     }
435
436     Frame[] frames=Frame.getFrames();
437     for (Frame frame : frames) {
438       updateUI(frame);
439     }
440     fireLookAndFeelChanged();
441   }
442
443   private void patchFileChooserStrings(final UIDefaults defaults) {
444     if (!defaults.containsKey(ourFileChooserTextKeys [0])) {
445       // Alloy L&F does not define strings for names of context menu actions, so we have to patch them in here
446       for (String key : ourFileChooserTextKeys) {
447         defaults.put(key, IdeBundle.message(key));
448       }
449     }
450   }
451
452   private void restoreOriginalFontDefaults(UIDefaults defaults) {
453     UIManager.LookAndFeelInfo lf = getCurrentLookAndFeel();
454     HashMap<String, Object> lfDefaults = myStoredDefaults.get(lf);
455     if (lfDefaults != null) {
456       for (String resource : ourPatcheableFontResources) {
457         defaults.put(resource, lfDefaults.get(resource));
458       }
459     }
460   }
461
462   private void storeOriginalFontDefaults(UIDefaults defaults) {
463     UIManager.LookAndFeelInfo lf = getCurrentLookAndFeel();
464     HashMap<String, Object> lfDefaults = myStoredDefaults.get(lf);
465     if (lfDefaults == null) {
466       lfDefaults = new HashMap<String, Object>();
467       for (String resource : ourPatcheableFontResources) {
468         lfDefaults.put(resource, defaults.get(resource));
469       }
470       myStoredDefaults.put(lf, lfDefaults);
471     }
472   }
473
474   private boolean shouldPatchLAFFonts() {
475     //noinspection HardCodedStringLiteral
476     return getCurrentLookAndFeel().getName().startsWith("IDEA") || UISettings.getInstance().OVERRIDE_NONIDEA_LAF_FONTS;
477   }
478
479   private static void updateUI(Window window){
480     if(!window.isDisplayable()){
481       return;
482     }
483     SwingUtilities.updateComponentTreeUI(window);
484     Window[] children=window.getOwnedWindows();
485     for (Window aChildren : children) {
486       updateUI(aChildren);
487     }
488   }
489
490   /**
491    * Repaints all displayable window.
492    */
493   public void repaintUI(){
494     Frame[] frames=Frame.getFrames();
495     for (Frame frame : frames) {
496       repaintUI(frame);
497     }
498   }
499
500   private static void repaintUI(Window window){
501     if(!window.isDisplayable()){
502       return;
503     }
504     window.repaint();
505     Window[] children=window.getOwnedWindows();
506     for (Window aChildren : children) {
507       repaintUI(aChildren);
508     }
509   }
510
511   private static void installCutCopyPasteShortcuts(InputMap inputMap, boolean useSimpleActionKeys){
512     String copyActionKey = useSimpleActionKeys ? "copy" : DefaultEditorKit.copyAction;
513     String pasteActionKey = useSimpleActionKeys ? "paste" : DefaultEditorKit.pasteAction;
514     String cutActionKey = useSimpleActionKeys ? "cut" : DefaultEditorKit.cutAction;
515     // Ctrl+Ins, Shift+Ins, Shift+Del
516     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT,KeyEvent.CTRL_MASK|KeyEvent.CTRL_DOWN_MASK),copyActionKey);
517     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT,KeyEvent.SHIFT_MASK|KeyEvent.SHIFT_DOWN_MASK),pasteActionKey);
518     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,KeyEvent.SHIFT_MASK|KeyEvent.SHIFT_DOWN_MASK),cutActionKey);
519     // Ctrl+C, Ctrl+V, Ctrl+X
520     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C,KeyEvent.CTRL_MASK|KeyEvent.CTRL_DOWN_MASK),copyActionKey);
521     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V,KeyEvent.CTRL_MASK|KeyEvent.CTRL_DOWN_MASK),pasteActionKey);
522     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_X,KeyEvent.CTRL_MASK|KeyEvent.CTRL_DOWN_MASK),DefaultEditorKit.cutAction);
523   }
524
525   @SuppressWarnings({"HardCodedStringLiteral"})
526   private static void initInputMapDefaults(UIDefaults defaults){
527     // Make ENTER work in JTrees
528     InputMap treeInputMap = (InputMap)defaults.get("Tree.focusInputMap");
529     if(treeInputMap!=null){ // it's really possible. For examle,  GTK+ doesn't have such map
530       treeInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),"toggle");
531     }
532     // Cut/Copy/Paste in JTextAreas
533     InputMap textAreaInputMap=(InputMap)defaults.get("TextArea.focusInputMap");
534     if(textAreaInputMap!=null){ // It really can be null, for example when LAF isn't properly initialized (Alloy license problem)
535       installCutCopyPasteShortcuts(textAreaInputMap, false);
536     }
537     // Cut/Copy/Paste in JTextFields
538     InputMap textFieldInputMap=(InputMap)defaults.get("TextField.focusInputMap");
539     if(textFieldInputMap!=null){ // It really can be null, for example when LAF isn't properly initialized (Alloy license problem)
540       installCutCopyPasteShortcuts(textFieldInputMap, false);
541     }
542     // Cut/Copy/Paste in JPAsswordField
543     InputMap passwordFieldInputMap=(InputMap)defaults.get("PasswordField.focusInputMap");
544     if(passwordFieldInputMap!=null){ // It really can be null, for example when LAF isn't properly initialized (Alloy license problem)
545       installCutCopyPasteShortcuts(passwordFieldInputMap, false);
546     }
547     // Cut/Copy/Paste in JTables
548     InputMap tableInputMap=(InputMap)defaults.get("Table.ancestorInputMap");
549     if(tableInputMap!=null){ // It really can be null, for example when LAF isn't properly initialized (Alloy license problem)
550       installCutCopyPasteShortcuts(tableInputMap, true);
551     }
552   }
553
554   @SuppressWarnings({"HardCodedStringLiteral"})
555   private void initFontDefaults(UIDefaults defaults) {
556     defaults.put("Tree.ancestorInputMap", null);
557     int uiFontSize = myUiSettings.FONT_SIZE;
558     String uiFontFace = myUiSettings.FONT_FACE;
559     FontUIResource font = new FontUIResource(uiFontFace, Font.PLAIN, uiFontSize);
560     FontUIResource font1 = new FontUIResource("Serif", Font.PLAIN, uiFontSize);
561     FontUIResource font3 = new FontUIResource("Monospaced", Font.PLAIN, uiFontSize);
562
563     for (String fontResource : ourPatcheableFontResources) {
564       defaults.put(fontResource, font);
565     }
566
567     defaults.put("PasswordField.font", font3);
568     defaults.put("TextArea.font", font3);
569     defaults.put("TextPane.font", font1);
570     defaults.put("EditorPane.font", font1);
571     defaults.put("TitledBorder.font", font);
572   }
573
574   private static final class IdeaLookAndFeelInfo extends UIManager.LookAndFeelInfo{
575     public IdeaLookAndFeelInfo(){
576       super(IdeBundle.message("idea.default.look.and.feel"), IDEA_LAF_CLASSNAME);
577     }
578
579     public boolean equals(Object obj){
580       return (obj instanceof IdeaLookAndFeelInfo);
581     }
582
583     public int hashCode(){
584       return getName().hashCode();
585     }
586   }
587
588   private static final class MyComparator implements Comparator{
589     public int compare(Object obj1,Object obj2){
590       String name1=((UIManager.LookAndFeelInfo)obj1).getName();
591       String name2=((UIManager.LookAndFeelInfo)obj2).getName();
592       return name1.compareToIgnoreCase(name2);
593     }
594   }
595
596   private static final class IdeaLaf extends MetalLookAndFeel{
597     protected void initComponentDefaults(UIDefaults table) {
598       super.initComponentDefaults(table);
599       initInputMapDefaults(table);
600       initIdeaDefaults(table);
601     }
602
603     protected void initSystemColorDefaults(UIDefaults table) {
604       super.initSystemColorDefaults(table);
605       /*
606       table.put("control", new ColorUIResource(236, 233, 216));
607       table.put("controlHighlight", new ColorUIResource(255, 255, 255));
608       table.put("controlShadow", new ColorUIResource(172, 167, 153));
609       */
610     }
611
612     @SuppressWarnings({"HardCodedStringLiteral"})
613     private static void initIdeaDefaults(UIDefaults defaults) {
614       defaults.put("Menu.maxGutterIconWidth", 18);
615       defaults.put("MenuItem.maxGutterIconWidth", 18);
616       // TODO[vova,anton] REMOVE!!! INVESTIGATE??? Borland???
617       defaults.put("MenuItem.acceleratorDelimiter", "-");
618
619       defaults.put("TitledBorder.titleColor",IdeaBlueMetalTheme.primary1);
620       ColorUIResource col = new ColorUIResource(230, 230, 230);
621       defaults.put("ScrollBar.background", col);
622       defaults.put("ScrollBar.track", col);
623
624 //      Border scrollPaneBorder = new BorderUIResource(new BegBorders.ScrollPaneBorder());
625 //      defaults.put("ScrollPane.border", scrollPaneBorder);
626       defaults.put("TextField.border", BegBorders.getTextFieldBorder());
627       defaults.put("PasswordField.border", BegBorders.getTextFieldBorder());
628       Border popupMenuBorder = new BegPopupMenuBorder();
629       defaults.put("PopupMenu.border", popupMenuBorder);
630       defaults.put("ScrollPane.border", BegBorders.getScrollPaneBorder());
631
632       defaults.put("ButtonUI", BegButtonUI.class.getName());
633       defaults.put("ComboBoxUI", BegComboBoxUI.class.getName());
634       defaults.put("RadioButtonUI", BegRadioButtonUI.class.getName());
635       defaults.put("CheckBoxUI", BegCheckBoxUI.class.getName());
636       defaults.put("TabbedPaneUI", BegTabbedPaneUI.class.getName());
637       defaults.put("TableUI", BegTableUI.class.getName());
638       defaults.put("TreeUI", BegTreeUI.class.getName());
639 //      defaults.put("ScrollPaneUI", BegScrollPaneUI.class.getName());
640
641       defaults.put("TabbedPane.tabInsets", new Insets(0, 4, 0, 4));
642       defaults.put("ToolTip.background", new ColorUIResource(255, 255, 231));
643       defaults.put("ToolTip.border", new ColoredSideBorder(Color.gray, Color.gray, Color.black, Color.black, 1));
644       defaults.put("Tree.ancestorInputMap", null);
645       defaults.put("FileView.directoryIcon", IconLoader.getIcon("/nodes/folder.png"));
646       defaults.put("FileChooser.upFolderIcon", IconLoader.getIcon("/nodes/upFolder.png"));
647       defaults.put("FileChooser.newFolderIcon", IconLoader.getIcon("/nodes/newFolder.png"));
648       defaults.put("FileChooser.homeFolderIcon", IconLoader.getIcon("/nodes/homeFolder.png"));
649       defaults.put("OptionPane.errorIcon", IconLoader.getIcon("/general/errorDialog.png"));
650       defaults.put("OptionPane.informationIcon", IconLoader.getIcon("/general/informationDialog.png"));
651       defaults.put("OptionPane.warningIcon", IconLoader.getIcon("/general/warningDialog.png"));
652       defaults.put("OptionPane.questionIcon", IconLoader.getIcon("/general/questionDialog.png"));
653       defaults.put("Tree.openIcon", LookAndFeel.makeIcon(WindowsLookAndFeel.class, "icons/TreeOpen.gif"));
654       defaults.put("Tree.closedIcon", LookAndFeel.makeIcon(WindowsLookAndFeel.class, "icons/TreeClosed.gif"));
655       defaults.put("Tree.leafIcon", LookAndFeel.makeIcon(WindowsLookAndFeel.class, "icons/TreeLeaf.gif"));
656       defaults.put("Tree.expandedIcon", com.sun.java.swing.plaf.windows.WindowsTreeUI.ExpandedIcon.createExpandedIcon());
657       defaults.put("Tree.collapsedIcon", com.sun.java.swing.plaf.windows.WindowsTreeUI.CollapsedIcon.createCollapsedIcon());
658       defaults.put("Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
659                          "ctrl C", "copy",
660                          "ctrl V", "paste",
661                          "ctrl X", "cut",
662                            "COPY", "copy",
663                           "PASTE", "paste",
664                             "CUT", "cut",
665                  "control INSERT", "copy",
666                    "shift INSERT", "paste",
667                    "shift DELETE", "cut",
668                           "RIGHT", "selectNextColumn",
669                        "KP_RIGHT", "selectNextColumn",
670                            "LEFT", "selectPreviousColumn",
671                         "KP_LEFT", "selectPreviousColumn",
672                            "DOWN", "selectNextRow",
673                         "KP_DOWN", "selectNextRow",
674                              "UP", "selectPreviousRow",
675                           "KP_UP", "selectPreviousRow",
676                     "shift RIGHT", "selectNextColumnExtendSelection",
677                  "shift KP_RIGHT", "selectNextColumnExtendSelection",
678                      "shift LEFT", "selectPreviousColumnExtendSelection",
679                   "shift KP_LEFT", "selectPreviousColumnExtendSelection",
680                      "shift DOWN", "selectNextRowExtendSelection",
681                   "shift KP_DOWN", "selectNextRowExtendSelection",
682                        "shift UP", "selectPreviousRowExtendSelection",
683                     "shift KP_UP", "selectPreviousRowExtendSelection",
684                         "PAGE_UP", "scrollUpChangeSelection",
685                       "PAGE_DOWN", "scrollDownChangeSelection",
686                            "HOME", "selectFirstColumn",
687                             "END", "selectLastColumn",
688                   "shift PAGE_UP", "scrollUpExtendSelection",
689                 "shift PAGE_DOWN", "scrollDownExtendSelection",
690                      "shift HOME", "selectFirstColumnExtendSelection",
691                       "shift END", "selectLastColumnExtendSelection",
692                    "ctrl PAGE_UP", "scrollLeftChangeSelection",
693                  "ctrl PAGE_DOWN", "scrollRightChangeSelection",
694                       "ctrl HOME", "selectFirstRow",
695                        "ctrl END", "selectLastRow",
696              "ctrl shift PAGE_UP", "scrollRightExtendSelection",
697            "ctrl shift PAGE_DOWN", "scrollLeftExtendSelection",
698                 "ctrl shift HOME", "selectFirstRowExtendSelection",
699                  "ctrl shift END", "selectLastRowExtendSelection",
700                             "TAB", "selectNextColumnCell",
701                       "shift TAB", "selectPreviousColumnCell",
702                           //"ENTER", "selectNextRowCell",
703                     "shift ENTER", "selectPreviousRowCell",
704                          "ctrl A", "selectAll",
705                          //"ESCAPE", "cancel",
706                              "F2", "startEditing"
707            }));
708     }
709   }
710
711   private static class OurPopupFactory extends PopupFactory {}
712 }