9f5f5d5720dad1b5a0b5b190dcafb0d7572064e7
[idea/community.git] / platform / editor-ui-api / src / com / intellij / ide / ui / UISettings.java
1 /*
2  * Copyright 2000-2014 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.ide.IdeBundle;
19 import com.intellij.openapi.Disposable;
20 import com.intellij.openapi.application.Application;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.application.PathManager;
23 import com.intellij.openapi.components.*;
24 import com.intellij.openapi.util.Pair;
25 import com.intellij.openapi.util.SimpleModificationTracker;
26 import com.intellij.openapi.util.SystemInfo;
27 import com.intellij.util.EventDispatcher;
28 import com.intellij.util.PlatformUtilsCore;
29 import com.intellij.util.SystemProperties;
30 import com.intellij.util.ui.UIUtil;
31 import com.intellij.util.xmlb.Accessor;
32 import com.intellij.util.xmlb.SerializationFilter;
33 import com.intellij.util.xmlb.XmlSerializerUtil;
34 import com.intellij.util.xmlb.annotations.Property;
35 import com.intellij.util.xmlb.annotations.Transient;
36 import org.jetbrains.annotations.NonNls;
37 import org.jetbrains.annotations.NotNull;
38
39 import javax.swing.*;
40 import java.awt.*;
41 import java.io.File;
42 import java.util.Map;
43
44 import static com.intellij.util.ui.UIUtil.isValidFont;
45
46 @State(
47   name = "UISettings",
48   storages = {
49     @Storage(
50       file = StoragePathMacros.APP_CONFIG + "/ui.lnf.xml"
51     )}
52 )
53 public class UISettings extends SimpleModificationTracker implements PersistentStateComponent<UISettings>, ExportableApplicationComponent {
54   /** Not tabbed pane. */
55   public static final int TABS_NONE = 0;
56
57   public static UISettings getInstance() {
58     return ApplicationManager.getApplication().getComponent(UISettings.class);
59   }
60
61   /**
62    * Use this method if you are not sure whether the application is initialized.
63    * @return persisted UISettings instance or default values.
64    */
65   public static UISettings getShadowInstance() {
66     Application application = ApplicationManager.getApplication();
67     UISettings settings = application == null ? null : application.getComponent(UISettings.class);
68     return settings == null ? new UISettings() : settings;
69   }
70
71   @Property(filter = FontFilter.class) public String FONT_FACE;
72   @Property(filter = FontFilter.class) public int FONT_SIZE;
73   public int RECENT_FILES_LIMIT = 50;
74   public int CONSOLE_COMMAND_HISTORY_LIMIT = 300;
75   public int EDITOR_TAB_LIMIT = 10;
76   public int EDITOR_TAB_TITLE_LIMIT = 100;
77   public boolean ANIMATE_WINDOWS = true;
78   public int ANIMATION_SPEED = 2000; // Pixels per second
79   public boolean SHOW_TOOL_WINDOW_NUMBERS = true;
80   public boolean HIDE_TOOL_STRIPES = true;
81   public boolean WIDESCREEN_SUPPORT = false;
82   public boolean LEFT_HORIZONTAL_SPLIT = false;
83   public boolean RIGHT_HORIZONTAL_SPLIT = false;
84   public boolean SHOW_EDITOR_TOOLTIP = true;
85   public boolean SHOW_MEMORY_INDICATOR = false;
86   public boolean ALLOW_MERGE_BUTTONS = true;
87   public boolean SHOW_MAIN_TOOLBAR = false;
88   public boolean SHOW_STATUS_BAR = true;
89   public boolean SHOW_NAVIGATION_BAR = true;
90   public boolean ALWAYS_SHOW_WINDOW_BUTTONS = false;
91   public boolean CYCLE_SCROLLING = true;
92   public boolean SCROLL_TAB_LAYOUT_IN_EDITOR = false;
93   public boolean SHOW_CLOSE_BUTTON = true;
94   public int EDITOR_TAB_PLACEMENT = 1;
95   public boolean HIDE_KNOWN_EXTENSION_IN_TABS = false;
96   public boolean SHOW_ICONS_IN_QUICK_NAVIGATION = true;
97   public boolean CLOSE_NON_MODIFIED_FILES_FIRST = false;
98   public boolean ACTIVATE_MRU_EDITOR_ON_CLOSE = false;
99   public boolean ACTIVATE_RIGHT_EDITOR_ON_CLOSE = false;
100   public boolean ANTIALIASING_IN_EDITOR = true;
101   public boolean MOVE_MOUSE_ON_DEFAULT_BUTTON = false;
102   public boolean ENABLE_ALPHA_MODE = false;
103   public int ALPHA_MODE_DELAY = 1500;
104   public float ALPHA_MODE_RATIO = 0.5f;
105   public int MAX_CLIPBOARD_CONTENTS = 5;
106   public boolean OVERRIDE_NONIDEA_LAF_FONTS = false;
107   public boolean SHOW_ICONS_IN_MENUS = true;
108   public boolean DISABLE_MNEMONICS = SystemInfo.isMac; // IDEADEV-33409, should be disabled by default on MacOS
109   public boolean DISABLE_MNEMONICS_IN_CONTROLS = false;
110   public boolean USE_SMALL_LABELS_ON_TABS = SystemInfo.isMac;
111   public boolean SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY = false;
112   public int MAX_LOOKUP_WIDTH2 = 500;
113   public int MAX_LOOKUP_LIST_HEIGHT = 11;
114   public boolean HIDE_NAVIGATION_ON_FOCUS_LOSS = true;
115   public boolean DND_WITH_PRESSED_ALT_ONLY = false;
116   public boolean FILE_COLORS_IN_PROJECT_VIEW = false;
117   public boolean DEFAULT_AUTOSCROLL_TO_SOURCE = false;
118   @Transient
119   public boolean PRESENTATION_MODE = false;
120   public int PRESENTATION_MODE_FONT_SIZE = 24;
121   public boolean MARK_MODIFIED_TABS_WITH_ASTERISK = false;
122   public boolean SHOW_TABS_TOOLTIPS = true;
123   public boolean SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true;
124
125   private final EventDispatcher<UISettingsListener> myDispatcher = EventDispatcher.create(UISettingsListener.class);
126
127   public UISettings() {
128     tweakPlatformDefaults();
129     setSystemFontFaceAndSize();
130   }
131
132   private void tweakPlatformDefaults() {
133     // TODO[anton] consider making all IDEs use the same settings
134     if (PlatformUtilsCore.isAppCode()) {
135       SCROLL_TAB_LAYOUT_IN_EDITOR = true;
136       ACTIVATE_RIGHT_EDITOR_ON_CLOSE = true;
137       SHOW_ICONS_IN_MENUS = false;
138     }
139   }
140
141   /**
142    * @deprecated use {@link UISettings#addUISettingsListener(com.intellij.ide.ui.UISettingsListener, Disposable disposable)} instead.
143    */
144   public void addUISettingsListener(UISettingsListener listener) {
145     myDispatcher.addListener(listener);
146   }
147
148   public void addUISettingsListener(@NotNull final UISettingsListener listener, @NotNull Disposable parentDisposable) {
149     myDispatcher.addListener(listener, parentDisposable);
150   }
151
152   /**
153    * Notifies all registered listeners that UI settings has been changed.
154    */
155   public void fireUISettingsChanged() {
156     incModificationCount();
157     myDispatcher.getMulticaster().uiSettingsChanged(this);
158     ApplicationManager.getApplication().getMessageBus().syncPublisher(UISettingsListener.TOPIC).uiSettingsChanged(this);
159   }
160
161   public void removeUISettingsListener(UISettingsListener listener) {
162     myDispatcher.removeListener(listener);
163   }
164
165   private void setSystemFontFaceAndSize() {
166     if (FONT_FACE == null || FONT_SIZE <= 0) {
167       final Pair<String, Integer> fontData = getSystemFontFaceAndSize();
168       FONT_FACE = fontData.first;
169       FONT_SIZE = fontData.second;
170     }
171   }
172
173   private static Pair<String, Integer> getSystemFontFaceAndSize() {
174     final Pair<String,Integer> fontData = UIUtil.getSystemFontData();
175     if (fontData != null) {
176       return fontData;
177     }
178
179     return Pair.create("Dialog", 12);
180   }
181
182   public static class FontFilter implements SerializationFilter {
183     public boolean accepts(Accessor accessor, Object bean) {
184       UISettings settings = (UISettings)bean;
185       return !hasDefaultFontSetting(settings);
186     }
187   }
188
189   private static boolean hasDefaultFontSetting(final UISettings settings) {
190     final Pair<String, Integer> fontData = getSystemFontFaceAndSize();
191     return fontData.first.equals(settings.FONT_FACE) && fontData.second.equals(settings.FONT_SIZE);
192   }
193
194   public UISettings getState() {
195     return this;
196   }
197
198   public void loadState(UISettings object) {
199     XmlSerializerUtil.copyBean(object, this);
200
201     // Check tab placement in editor
202     if (EDITOR_TAB_PLACEMENT != TABS_NONE &&
203         EDITOR_TAB_PLACEMENT != SwingConstants.TOP &&
204         EDITOR_TAB_PLACEMENT != SwingConstants.LEFT &&
205         EDITOR_TAB_PLACEMENT != SwingConstants.BOTTOM &&
206         EDITOR_TAB_PLACEMENT != SwingConstants.RIGHT) {
207       EDITOR_TAB_PLACEMENT = SwingConstants.TOP;
208     }
209
210     // Check that alpha delay and ratio are valid
211     if (ALPHA_MODE_DELAY < 0) {
212       ALPHA_MODE_DELAY = 1500;
213     }
214     if (ALPHA_MODE_RATIO < 0.0f || ALPHA_MODE_RATIO > 1.0f) {
215       ALPHA_MODE_RATIO = 0.5f;
216     }
217
218     setSystemFontFaceAndSize();
219     // 1. Sometimes system font cannot display standard ASCII symbols. If so we have
220     // find any other suitable font withing "preferred" fonts first.
221     boolean fontIsValid = isValidFont(new Font(FONT_FACE, Font.PLAIN, FONT_SIZE));
222     if (!fontIsValid) {
223       @NonNls final String[] preferredFonts = {"dialog", "Arial", "Tahoma"};
224       for (String preferredFont : preferredFonts) {
225         if (isValidFont(new Font(preferredFont, Font.PLAIN, FONT_SIZE))) {
226           FONT_FACE = preferredFont;
227           fontIsValid = true;
228           break;
229         }
230       }
231
232       // 2. If all preferred fonts are not valid in current environment
233       // we have to find first valid font (if any)
234       if (!fontIsValid) {
235         String[] fontNames = UIUtil.getValidFontNames(false);
236         if (fontNames.length > 0) {
237           FONT_FACE = fontNames[0];
238         }
239       }
240     }
241
242     if (MAX_CLIPBOARD_CONTENTS <= 0) {
243       MAX_CLIPBOARD_CONTENTS = 5;
244     }
245
246     fireUISettingsChanged();
247   }
248
249   private static final boolean DEFAULT_ALIASING             =
250     SystemProperties.getBooleanProperty("idea.use.default.antialiasing.in.editor", false);
251   private static final boolean FORCE_USE_FRACTIONAL_METRICS =
252     SystemProperties.getBooleanProperty("idea.force.use.fractional.metrics", false);
253
254   public static void setupAntialiasing(final Graphics g) {
255     if (DEFAULT_ALIASING) return;
256
257     Graphics2D g2d = (Graphics2D)g;
258     UISettings uiSettings = getInstance();
259
260     g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
261     if (!isRemoteDesktopConnected() && UIUtil.isRetina()) {
262       g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
263     }
264     else {
265       if (uiSettings == null || uiSettings.ANTIALIASING_IN_EDITOR) {
266         Toolkit tk = Toolkit.getDefaultToolkit();
267         //noinspection HardCodedStringLiteral
268         Map map = (Map)tk.getDesktopProperty("awt.font.desktophints");
269         if (map != null) {
270           if (isRemoteDesktopConnected()) {
271             g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
272           }
273           else {
274             g2d.addRenderingHints(map);
275           }
276         }
277         else {
278           g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
279         }
280         if (FORCE_USE_FRACTIONAL_METRICS) {
281           g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
282         }
283       }
284       else {
285         g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
286       }
287     }
288   }
289
290   /**
291    * @return true when Remote Desktop (i.e. Windows RDP) is connected
292    */
293   // TODO[neuro]: move to UIUtil
294   public static boolean isRemoteDesktopConnected() {
295     if (System.getProperty("os.name").contains("Windows")) {
296       final Map map = (Map)Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints");
297       return map != null && RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT.equals(map.get(RenderingHints.KEY_TEXT_ANTIALIASING));
298     }
299     return false;
300   }
301
302   @NotNull
303   @Override
304   public File[] getExportFiles() {
305     return new File[]{PathManager.getOptionsFile("ui.lnf")};
306   }
307
308   @NotNull
309   @Override
310   public String getPresentableName() {
311     return IdeBundle.message("ui.settings");
312   }
313
314   @NonNls
315   @NotNull
316   @Override
317   public String getComponentName() {
318     return "UISettings";
319   }
320
321   @Override
322   public void initComponent() { }
323
324   @Override
325   public void disposeComponent() { }
326 }