fix popups one again
[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     } else if (HEAVY_WEIGHT_POPUP.equals(popupWeight) && !MEDIUM_WEIGHT_POPUP.equals(popupWeight)) {
302       throw new IllegalStateException("unknown value of property -Didea.popup.weight: " + popupWeight);
303     }
304
305     if (SystemInfo.isMacOSLeopard) {
306       // Force heavy weight popups under Leopard, otherwise they don't have shadow or any kind of border.
307       popupWeight = HEAVY_WEIGHT_POPUP;
308     }
309
310     popupWeight = popupWeight.trim();
311     final boolean heavyWeighPopup = HEAVY_WEIGHT_POPUP.equals(popupWeight);
312
313     PopupFactory popupFactory;
314
315     final PopupFactory oldFactory = PopupFactory.getSharedInstance();
316     if (!(oldFactory instanceof OurPopupFactory)) {
317       popupFactory = new OurPopupFactory() {
318         public Popup getPopup(
319           Component owner,
320           Component contents,
321           int x,
322           int y
323         ) throws IllegalArgumentException {
324           final Point point = fixPopupLocation(contents, x, y);
325           try {
326             final Method method = PopupFactory.class.getDeclaredMethod("setPopupType", int.class);
327             method.setAccessible(true);
328             method.invoke(oldFactory, heavyWeighPopup ? 2 : 1);
329
330           }
331           catch (Throwable e) {
332             LOG.error(e);
333           }
334
335           return oldFactory.getPopup(owner, contents, point.x, point.y);
336         }
337       };
338
339       PopupFactory.setSharedInstance(popupFactory);
340     }
341
342     // update ui for popup menu to get round corners
343     if (UIUtil.isUnderAquaLookAndFeel()) {
344       final UIDefaults uiDefaults = UIManager.getLookAndFeelDefaults();
345       uiDefaults.put("PopupMenuUI", MacPopupMenuUI.class.getCanonicalName());
346       final Icon icon = getAquaMenuInvertedIcon();
347       if (icon != null) {
348         uiDefaults.put("Menu.invertedArrowIcon", icon);
349       }
350     }
351   }
352
353   @Nullable
354   private static Icon getAquaMenuInvertedIcon() {
355     if (!UIUtil.isUnderAquaLookAndFeel()) return null;
356     final Icon arrow = (Icon) UIManager.get("Menu.arrowIcon");
357     if (arrow == null) return null;
358
359     try {
360       final Method method = arrow.getClass().getMethod("getInvertedIcon");
361       if (method != null) {
362         method.setAccessible(true);
363         return (Icon) method.invoke(arrow);
364       }
365
366       return null;
367     }
368     catch (NoSuchMethodException e1) {
369       return null;
370     }
371     catch (InvocationTargetException e1) {
372       return null;
373     }
374     catch (IllegalAccessException e1) {
375       return null;
376     }
377   }
378
379   private Point fixPopupLocation(final Component contents, final int x, final int y) {
380     if (!(contents instanceof JToolTip)) return new Point(x, y);
381
382     final PointerInfo info;
383     try {
384       info = MouseInfo.getPointerInfo();
385     }
386     catch (InternalError e) {
387       // http://www.jetbrains.net/jira/browse/IDEADEV-21390
388       // may happen under Mac OSX 10.5
389       return new Point(x, y);
390     }
391     int deltaY = 0;
392
393     if (info != null) {
394       final Point mouse = info.getLocation();
395       deltaY = mouse.y - y;
396     }
397
398     final Dimension size = contents.getPreferredSize();
399     final Rectangle rec = new Rectangle(new Point(x, y), size);
400     ScreenUtil.moveRectangleToFitTheScreen(rec);
401
402     if (rec.y < y) {
403       rec.y += deltaY;
404     }
405
406     return rec.getLocation();
407   }
408
409
410   /**
411    * Updates LAF of all windows. The method also updates font of components
412    * as it's configured in <code>UISettings</code>.
413    */
414   public void updateUI(){
415     UIDefaults lookAndFeelDefaults=UIManager.getLookAndFeelDefaults();
416     initInputMapDefaults(lookAndFeelDefaults);
417     patchFileChooserStrings(lookAndFeelDefaults);
418     if (shouldPatchLAFFonts()) {
419       storeOriginalFontDefaults(lookAndFeelDefaults);
420       initFontDefaults(lookAndFeelDefaults);
421     }
422     else {
423       restoreOriginalFontDefaults(lookAndFeelDefaults);
424     }
425
426     Frame[] frames=Frame.getFrames();
427     for (Frame frame : frames) {
428       updateUI(frame);
429     }
430     fireLookAndFeelChanged();
431   }
432
433   private void patchFileChooserStrings(final UIDefaults defaults) {
434     if (!defaults.containsKey(ourFileChooserTextKeys [0])) {
435       // Alloy L&F does not define strings for names of context menu actions, so we have to patch them in here
436       for (String key : ourFileChooserTextKeys) {
437         defaults.put(key, IdeBundle.message(key));
438       }
439     }
440   }
441
442   private void restoreOriginalFontDefaults(UIDefaults defaults) {
443     UIManager.LookAndFeelInfo lf = getCurrentLookAndFeel();
444     HashMap<String, Object> lfDefaults = myStoredDefaults.get(lf);
445     if (lfDefaults != null) {
446       for (String resource : ourPatcheableFontResources) {
447         defaults.put(resource, lfDefaults.get(resource));
448       }
449     }
450   }
451
452   private void storeOriginalFontDefaults(UIDefaults defaults) {
453     UIManager.LookAndFeelInfo lf = getCurrentLookAndFeel();
454     HashMap<String, Object> lfDefaults = myStoredDefaults.get(lf);
455     if (lfDefaults == null) {
456       lfDefaults = new HashMap<String, Object>();
457       for (String resource : ourPatcheableFontResources) {
458         lfDefaults.put(resource, defaults.get(resource));
459       }
460       myStoredDefaults.put(lf, lfDefaults);
461     }
462   }
463
464   private boolean shouldPatchLAFFonts() {
465     //noinspection HardCodedStringLiteral
466     return getCurrentLookAndFeel().getName().startsWith("IDEA") || UISettings.getInstance().OVERRIDE_NONIDEA_LAF_FONTS;
467   }
468
469   private static void updateUI(Window window){
470     if(!window.isDisplayable()){
471       return;
472     }
473     SwingUtilities.updateComponentTreeUI(window);
474     Window[] children=window.getOwnedWindows();
475     for (Window aChildren : children) {
476       updateUI(aChildren);
477     }
478   }
479
480   /**
481    * Repaints all displayable window.
482    */
483   public void repaintUI(){
484     Frame[] frames=Frame.getFrames();
485     for (Frame frame : frames) {
486       repaintUI(frame);
487     }
488   }
489
490   private static void repaintUI(Window window){
491     if(!window.isDisplayable()){
492       return;
493     }
494     window.repaint();
495     Window[] children=window.getOwnedWindows();
496     for (Window aChildren : children) {
497       repaintUI(aChildren);
498     }
499   }
500
501   private static void installCutCopyPasteShortcuts(InputMap inputMap, boolean useSimpleActionKeys){
502     String copyActionKey = useSimpleActionKeys ? "copy" : DefaultEditorKit.copyAction;
503     String pasteActionKey = useSimpleActionKeys ? "paste" : DefaultEditorKit.pasteAction;
504     String cutActionKey = useSimpleActionKeys ? "cut" : DefaultEditorKit.cutAction;
505     // Ctrl+Ins, Shift+Ins, Shift+Del
506     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT,KeyEvent.CTRL_MASK|KeyEvent.CTRL_DOWN_MASK),copyActionKey);
507     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT,KeyEvent.SHIFT_MASK|KeyEvent.SHIFT_DOWN_MASK),pasteActionKey);
508     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,KeyEvent.SHIFT_MASK|KeyEvent.SHIFT_DOWN_MASK),cutActionKey);
509     // Ctrl+C, Ctrl+V, Ctrl+X
510     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C,KeyEvent.CTRL_MASK|KeyEvent.CTRL_DOWN_MASK),copyActionKey);
511     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V,KeyEvent.CTRL_MASK|KeyEvent.CTRL_DOWN_MASK),pasteActionKey);
512     inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_X,KeyEvent.CTRL_MASK|KeyEvent.CTRL_DOWN_MASK),DefaultEditorKit.cutAction);
513   }
514
515   @SuppressWarnings({"HardCodedStringLiteral"})
516   private static void initInputMapDefaults(UIDefaults defaults){
517     // Make ENTER work in JTrees
518     InputMap treeInputMap = (InputMap)defaults.get("Tree.focusInputMap");
519     if(treeInputMap!=null){ // it's really possible. For examle,  GTK+ doesn't have such map
520       treeInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),"toggle");
521     }
522     // Cut/Copy/Paste in JTextAreas
523     InputMap textAreaInputMap=(InputMap)defaults.get("TextArea.focusInputMap");
524     if(textAreaInputMap!=null){ // It really can be null, for example when LAF isn't properly initialized (Alloy license problem)
525       installCutCopyPasteShortcuts(textAreaInputMap, false);
526     }
527     // Cut/Copy/Paste in JTextFields
528     InputMap textFieldInputMap=(InputMap)defaults.get("TextField.focusInputMap");
529     if(textFieldInputMap!=null){ // It really can be null, for example when LAF isn't properly initialized (Alloy license problem)
530       installCutCopyPasteShortcuts(textFieldInputMap, false);
531     }
532     // Cut/Copy/Paste in JPAsswordField
533     InputMap passwordFieldInputMap=(InputMap)defaults.get("PasswordField.focusInputMap");
534     if(passwordFieldInputMap!=null){ // It really can be null, for example when LAF isn't properly initialized (Alloy license problem)
535       installCutCopyPasteShortcuts(passwordFieldInputMap, false);
536     }
537     // Cut/Copy/Paste in JTables
538     InputMap tableInputMap=(InputMap)defaults.get("Table.ancestorInputMap");
539     if(tableInputMap!=null){ // It really can be null, for example when LAF isn't properly initialized (Alloy license problem)
540       installCutCopyPasteShortcuts(tableInputMap, true);
541     }
542   }
543
544   @SuppressWarnings({"HardCodedStringLiteral"})
545   private void initFontDefaults(UIDefaults defaults) {
546     defaults.put("Tree.ancestorInputMap", null);
547     int uiFontSize = myUiSettings.FONT_SIZE;
548     String uiFontFace = myUiSettings.FONT_FACE;
549     FontUIResource font = new FontUIResource(uiFontFace, Font.PLAIN, uiFontSize);
550     FontUIResource font1 = new FontUIResource("Serif", Font.PLAIN, uiFontSize);
551     FontUIResource font3 = new FontUIResource("Monospaced", Font.PLAIN, uiFontSize);
552
553     for (String fontResource : ourPatcheableFontResources) {
554       defaults.put(fontResource, font);
555     }
556
557     defaults.put("PasswordField.font", font3);
558     defaults.put("TextArea.font", font3);
559     defaults.put("TextPane.font", font1);
560     defaults.put("EditorPane.font", font1);
561     defaults.put("TitledBorder.font", font);
562   }
563
564   private static final class IdeaLookAndFeelInfo extends UIManager.LookAndFeelInfo{
565     public IdeaLookAndFeelInfo(){
566       super(IdeBundle.message("idea.default.look.and.feel"), IDEA_LAF_CLASSNAME);
567     }
568
569     public boolean equals(Object obj){
570       return (obj instanceof IdeaLookAndFeelInfo);
571     }
572
573     public int hashCode(){
574       return getName().hashCode();
575     }
576   }
577
578   private static final class MyComparator implements Comparator{
579     public int compare(Object obj1,Object obj2){
580       String name1=((UIManager.LookAndFeelInfo)obj1).getName();
581       String name2=((UIManager.LookAndFeelInfo)obj2).getName();
582       return name1.compareToIgnoreCase(name2);
583     }
584   }
585
586   private static final class IdeaLaf extends MetalLookAndFeel{
587     protected void initComponentDefaults(UIDefaults table) {
588       super.initComponentDefaults(table);
589       initInputMapDefaults(table);
590       initIdeaDefaults(table);
591     }
592
593     protected void initSystemColorDefaults(UIDefaults table) {
594       super.initSystemColorDefaults(table);
595       /*
596       table.put("control", new ColorUIResource(236, 233, 216));
597       table.put("controlHighlight", new ColorUIResource(255, 255, 255));
598       table.put("controlShadow", new ColorUIResource(172, 167, 153));
599       */
600     }
601
602     @SuppressWarnings({"HardCodedStringLiteral"})
603     private static void initIdeaDefaults(UIDefaults defaults) {
604       defaults.put("Menu.maxGutterIconWidth", 18);
605       defaults.put("MenuItem.maxGutterIconWidth", 18);
606       // TODO[vova,anton] REMOVE!!! INVESTIGATE??? Borland???
607       defaults.put("MenuItem.acceleratorDelimiter", "-");
608
609       defaults.put("TitledBorder.titleColor",IdeaBlueMetalTheme.primary1);
610       ColorUIResource col = new ColorUIResource(230, 230, 230);
611       defaults.put("ScrollBar.background", col);
612       defaults.put("ScrollBar.track", col);
613
614 //      Border scrollPaneBorder = new BorderUIResource(new BegBorders.ScrollPaneBorder());
615 //      defaults.put("ScrollPane.border", scrollPaneBorder);
616       defaults.put("TextField.border", BegBorders.getTextFieldBorder());
617       defaults.put("PasswordField.border", BegBorders.getTextFieldBorder());
618       Border popupMenuBorder = new BegPopupMenuBorder();
619       defaults.put("PopupMenu.border", popupMenuBorder);
620       defaults.put("ScrollPane.border", BegBorders.getScrollPaneBorder());
621
622       defaults.put("ButtonUI", BegButtonUI.class.getName());
623       defaults.put("ComboBoxUI", BegComboBoxUI.class.getName());
624       defaults.put("RadioButtonUI", BegRadioButtonUI.class.getName());
625       defaults.put("CheckBoxUI", BegCheckBoxUI.class.getName());
626       defaults.put("TabbedPaneUI", BegTabbedPaneUI.class.getName());
627       defaults.put("TableUI", BegTableUI.class.getName());
628       defaults.put("TreeUI", BegTreeUI.class.getName());
629 //      defaults.put("ScrollPaneUI", BegScrollPaneUI.class.getName());
630
631       defaults.put("TabbedPane.tabInsets", new Insets(0, 4, 0, 4));
632       defaults.put("ToolTip.background", new ColorUIResource(255, 255, 231));
633       defaults.put("ToolTip.border", new ColoredSideBorder(Color.gray, Color.gray, Color.black, Color.black, 1));
634       defaults.put("Tree.ancestorInputMap", null);
635       defaults.put("FileView.directoryIcon", IconLoader.getIcon("/nodes/folder.png"));
636       defaults.put("FileChooser.upFolderIcon", IconLoader.getIcon("/nodes/upFolder.png"));
637       defaults.put("FileChooser.newFolderIcon", IconLoader.getIcon("/nodes/newFolder.png"));
638       defaults.put("FileChooser.homeFolderIcon", IconLoader.getIcon("/nodes/homeFolder.png"));
639       defaults.put("OptionPane.errorIcon", IconLoader.getIcon("/general/errorDialog.png"));
640       defaults.put("OptionPane.informationIcon", IconLoader.getIcon("/general/informationDialog.png"));
641       defaults.put("OptionPane.warningIcon", IconLoader.getIcon("/general/warningDialog.png"));
642       defaults.put("OptionPane.questionIcon", IconLoader.getIcon("/general/questionDialog.png"));
643       defaults.put("Tree.openIcon", LookAndFeel.makeIcon(WindowsLookAndFeel.class, "icons/TreeOpen.gif"));
644       defaults.put("Tree.closedIcon", LookAndFeel.makeIcon(WindowsLookAndFeel.class, "icons/TreeClosed.gif"));
645       defaults.put("Tree.leafIcon", LookAndFeel.makeIcon(WindowsLookAndFeel.class, "icons/TreeLeaf.gif"));
646       defaults.put("Tree.expandedIcon", com.sun.java.swing.plaf.windows.WindowsTreeUI.ExpandedIcon.createExpandedIcon());
647       defaults.put("Tree.collapsedIcon", com.sun.java.swing.plaf.windows.WindowsTreeUI.CollapsedIcon.createCollapsedIcon());
648       defaults.put("Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
649                          "ctrl C", "copy",
650                          "ctrl V", "paste",
651                          "ctrl X", "cut",
652                            "COPY", "copy",
653                           "PASTE", "paste",
654                             "CUT", "cut",
655                  "control INSERT", "copy",
656                    "shift INSERT", "paste",
657                    "shift DELETE", "cut",
658                           "RIGHT", "selectNextColumn",
659                        "KP_RIGHT", "selectNextColumn",
660                            "LEFT", "selectPreviousColumn",
661                         "KP_LEFT", "selectPreviousColumn",
662                            "DOWN", "selectNextRow",
663                         "KP_DOWN", "selectNextRow",
664                              "UP", "selectPreviousRow",
665                           "KP_UP", "selectPreviousRow",
666                     "shift RIGHT", "selectNextColumnExtendSelection",
667                  "shift KP_RIGHT", "selectNextColumnExtendSelection",
668                      "shift LEFT", "selectPreviousColumnExtendSelection",
669                   "shift KP_LEFT", "selectPreviousColumnExtendSelection",
670                      "shift DOWN", "selectNextRowExtendSelection",
671                   "shift KP_DOWN", "selectNextRowExtendSelection",
672                        "shift UP", "selectPreviousRowExtendSelection",
673                     "shift KP_UP", "selectPreviousRowExtendSelection",
674                         "PAGE_UP", "scrollUpChangeSelection",
675                       "PAGE_DOWN", "scrollDownChangeSelection",
676                            "HOME", "selectFirstColumn",
677                             "END", "selectLastColumn",
678                   "shift PAGE_UP", "scrollUpExtendSelection",
679                 "shift PAGE_DOWN", "scrollDownExtendSelection",
680                      "shift HOME", "selectFirstColumnExtendSelection",
681                       "shift END", "selectLastColumnExtendSelection",
682                    "ctrl PAGE_UP", "scrollLeftChangeSelection",
683                  "ctrl PAGE_DOWN", "scrollRightChangeSelection",
684                       "ctrl HOME", "selectFirstRow",
685                        "ctrl END", "selectLastRow",
686              "ctrl shift PAGE_UP", "scrollRightExtendSelection",
687            "ctrl shift PAGE_DOWN", "scrollLeftExtendSelection",
688                 "ctrl shift HOME", "selectFirstRowExtendSelection",
689                  "ctrl shift END", "selectLastRowExtendSelection",
690                             "TAB", "selectNextColumnCell",
691                       "shift TAB", "selectPreviousColumnCell",
692                           //"ENTER", "selectNextRowCell",
693                     "shift ENTER", "selectPreviousRowCell",
694                          "ctrl A", "selectAll",
695                          //"ESCAPE", "cancel",
696                              "F2", "startEditing"
697            }));
698     }
699   }
700
701   private static class OurPopupFactory extends PopupFactory {}
702 }