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