72422034bea0748c399e273357a1a84e754a2bd2
[idea/community.git] / plugins / terminal / src / org / jetbrains / plugins / terminal / JBTerminalPanel.java
1 /*
2  * Copyright 2000-2016 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
17 /* -*-mode:java; c-basic-offset:2; -*- */
18
19
20 package org.jetbrains.plugins.terminal;
21
22 import com.google.common.base.Predicate;
23 import com.google.common.collect.Lists;
24 import com.intellij.ide.GeneralSettings;
25 import com.intellij.ide.IdeEventQueue;
26 import com.intellij.ide.ui.UISettings;
27 import com.intellij.openapi.Disposable;
28 import com.intellij.openapi.actionSystem.*;
29 import com.intellij.openapi.application.TransactionGuard;
30 import com.intellij.openapi.editor.impl.ComplementaryFontsRegistry;
31 import com.intellij.openapi.editor.impl.FontInfo;
32 import com.intellij.openapi.fileEditor.FileDocumentManager;
33 import com.intellij.openapi.ide.CopyPasteManager;
34 import com.intellij.openapi.project.DumbAwareAction;
35 import com.intellij.util.JBHiDPIScaledImage;
36 import com.intellij.util.RetinaImage;
37 import com.intellij.util.ui.UIUtil;
38 import com.jediterm.terminal.TextStyle;
39 import com.jediterm.terminal.model.StyleState;
40 import com.jediterm.terminal.model.TerminalTextBuffer;
41 import com.jediterm.terminal.ui.TerminalPanel;
42 import org.intellij.lang.annotations.JdkConstants;
43 import org.jetbrains.annotations.NotNull;
44
45 import javax.swing.*;
46 import java.awt.*;
47 import java.awt.datatransfer.StringSelection;
48 import java.awt.event.FocusEvent;
49 import java.awt.event.FocusListener;
50 import java.awt.event.KeyEvent;
51 import java.awt.image.BufferedImage;
52 import java.awt.image.ImageObserver;
53 import java.util.List;
54
55 public class JBTerminalPanel extends TerminalPanel implements FocusListener, TerminalSettingsListener, Disposable,
56                                                               IdeEventQueue.EventDispatcher {
57   private static final String[] ACTIONS_TO_SKIP = new String[]{
58     "ActivateTerminalToolWindow",
59     "ActivateMessagesToolWindow",
60     "ActivateProjectToolWindow",
61     "ActivateFavoritesToolWindow",
62     "ActivateFindToolWindow",
63     "ActivateRunToolWindow",
64     "ActivateDebugToolWindow",
65     "ActivateTODOToolWindow",
66     "ActivateStructureToolWindow",
67     "ActivateHierarchyToolWindow",
68     "ActivateVersionControlToolWindow",
69     "HideAllWindows",
70
71     "ShowBookmarks",
72     "GotoBookmark0",
73     "GotoBookmark1",
74     "GotoBookmark2",
75     "GotoBookmark3",
76     "GotoBookmark4",
77     "GotoBookmark5",
78     "GotoBookmark6",
79     "GotoBookmark7",
80     "GotoBookmark8",
81     "GotoBookmark9",
82     
83     "GotoAction",
84     "GotoFile",
85     "GotoClass",
86     "GotoSymbol",
87     
88     "ShowSettings",
89     "RecentFiles",
90     "Switcher"
91   };
92
93   private final JBTerminalSystemSettingsProvider mySettingsProvider;
94
95   private List<AnAction> myActionsToSkip;
96
97   public JBTerminalPanel(@NotNull JBTerminalSystemSettingsProvider settingsProvider,
98                          @NotNull TerminalTextBuffer backBuffer,
99                          @NotNull StyleState styleState) {
100     super(settingsProvider, backBuffer, styleState);
101
102     mySettingsProvider = settingsProvider;
103
104     JBTabbedTerminalWidget.convertActions(this, getActions(), new Predicate<KeyEvent>() {
105       @Override
106       public boolean apply(KeyEvent input) {
107         JBTerminalPanel.this.handleKeyEvent(input);
108         return true;
109       }
110     });
111
112     registerKeymapActions(this);
113
114     addFocusListener(this);
115
116     mySettingsProvider.addListener(this);
117   }
118
119   private static void registerKeymapActions(final TerminalPanel terminalPanel) {
120
121     ActionManager actionManager = ActionManager.getInstance();
122     for (String actionId : ACTIONS_TO_SKIP) {
123       final AnAction action = actionManager.getAction(actionId);
124       if (action != null) {
125         AnAction a = new DumbAwareAction() {
126           @Override
127           public void actionPerformed(AnActionEvent e) {
128             if (e.getInputEvent() instanceof KeyEvent) {
129               AnActionEvent event =
130                 new AnActionEvent(e.getInputEvent(), e.getDataContext(), e.getPlace(), new Presentation(), e.getActionManager(),
131                                   e.getModifiers());
132               action.update(event);
133               if (event.getPresentation().isEnabled()) {
134                 action.actionPerformed(event);
135               }
136               else {
137                 terminalPanel.handleKeyEvent((KeyEvent)event.getInputEvent());
138               }
139
140               event.getInputEvent().consume();
141             }
142           }
143         };
144         for (Shortcut sc : action.getShortcutSet().getShortcuts()) {
145           if (sc.isKeyboard() && sc instanceof KeyboardShortcut) {
146             KeyboardShortcut ksc = (KeyboardShortcut)sc;
147             a.registerCustomShortcutSet(ksc.getFirstKeyStroke().getKeyCode(), ksc.getFirstKeyStroke().getModifiers(), terminalPanel);
148           }
149         }
150       }
151     }
152   }
153
154   @Override
155   public boolean dispatch(AWTEvent e) {
156     if (e instanceof KeyEvent && !skipKeyEvent((KeyEvent)e)) {
157       dispatchEvent(e);
158       return true;
159     }
160     return false;
161   }
162
163   private boolean skipKeyEvent(KeyEvent e) {
164     if (myActionsToSkip == null) {
165       return false;
166     }
167     int kc = e.getKeyCode();
168     return kc == KeyEvent.VK_ESCAPE || skipAction(e, myActionsToSkip);
169   }
170
171   private static boolean skipAction(KeyEvent e, List<AnAction> actionsToSkip) {
172     if (actionsToSkip != null) {
173       final KeyboardShortcut eventShortcut = new KeyboardShortcut(KeyStroke.getKeyStrokeForEvent(e), null);
174       for (AnAction action : actionsToSkip) {
175         for (Shortcut sc : action.getShortcutSet().getShortcuts()) {
176           if (sc.isKeyboard() && sc.startsWith(eventShortcut)) {
177             return true;
178           }
179         }
180       }
181     }
182     return false;
183   }
184
185
186   @Override
187   protected void setupAntialiasing(Graphics graphics) {
188     UIUtil.setupComposite((Graphics2D)graphics);
189     UISettings.setupAntialiasing(graphics);
190   }
191
192   @Override
193   protected void setCopyContents(StringSelection selection) {
194     CopyPasteManager.getInstance().setContents(selection);
195   }
196
197   @Override
198   protected void drawImage(Graphics2D gfx, BufferedImage image, int x, int y, ImageObserver observer) {
199     UIUtil.drawImage(gfx, image, x, y, observer);
200   }
201
202   @Override
203   protected void drawImage(Graphics2D g, BufferedImage image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2) {
204     drawImage(g, image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
205   }
206
207   public static void drawImage(Graphics g,
208                                Image image,
209                                int dx1,
210                                int dy1,
211                                int dx2,
212                                int dy2,
213                                int sx1,
214                                int sy1,
215                                int sx2,
216                                int sy2,
217                                ImageObserver observer) {
218     if (image instanceof JBHiDPIScaledImage) {
219       final Graphics2D newG = (Graphics2D)g.create(0, 0, image.getWidth(observer), image.getHeight(observer));
220       newG.scale(0.5, 0.5);
221       Image img = ((JBHiDPIScaledImage)image).getDelegate();
222       if (img == null) {
223         img = image;
224       }
225       newG.drawImage(img, 2 * dx1, 2 * dy1, 2 * dx2, 2 * dy2, sx1 * 2, sy1 * 2, sx2 * 2, sy2 * 2, observer);
226       newG.scale(1, 1);
227       newG.dispose();
228     }
229     else if (RetinaImage.isAppleHiDPIScaledImage(image)) {
230       g.drawImage(image, dx1, dy1, dx2, dy2, sx1 * 2, sy1 * 2, sx2 * 2, sy2 * 2, observer);
231     }
232     else {
233       g.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
234     }
235   }
236
237   @Override
238   protected boolean isRetina() {
239     return UIUtil.isRetina();
240   }
241
242   @Override
243   protected BufferedImage createBufferedImage(int width, int height) {
244     return UIUtil.createImage(this, width, height, BufferedImage.TYPE_INT_ARGB);
245   }
246
247
248   @Override
249   public void focusGained(FocusEvent event) {
250     installKeyDispatcher();
251
252     if (GeneralSettings.getInstance().isSaveOnFrameDeactivation()) {
253       TransactionGuard.submitTransaction(this, () -> FileDocumentManager.getInstance().saveAllDocuments());
254     }
255   }
256
257   private void installKeyDispatcher() {
258     if (TerminalOptionsProvider.Companion.getInstance().overrideIdeShortcuts()) {
259       myActionsToSkip = setupActionsToSkip();
260       IdeEventQueue.getInstance().addDispatcher(this, this);
261     }
262     else {
263       myActionsToSkip = null;
264     }
265   }
266
267   private static List<AnAction> setupActionsToSkip() {
268     List<AnAction> res = Lists.newArrayList();
269     ActionManager actionManager = ActionManager.getInstance();
270     for (String actionId : ACTIONS_TO_SKIP) {
271       AnAction action = actionManager.getAction(actionId);
272       if (action != null) {
273         res.add(action);
274       }
275     }
276     return res;
277   }
278
279   @Override
280   public void focusLost(FocusEvent event) {
281     if (myActionsToSkip != null) {
282       myActionsToSkip = null;
283       IdeEventQueue.getInstance().removeDispatcher(this);
284     }
285
286     JBTerminalStarter.refreshAfterExecution();
287   }
288
289   @Override
290   protected Font getFontToDisplay(char c, TextStyle style) {
291     FontInfo fontInfo = fontForChar(c, style.hasOption(TextStyle.Option.BOLD) ? Font.BOLD : Font.PLAIN);
292     return fontInfo.getFont();
293   }
294
295   public FontInfo fontForChar(final char c, @JdkConstants.FontStyle int style) {
296     return ComplementaryFontsRegistry.getFontAbleToDisplay(c, style, mySettingsProvider.getColorScheme().getConsoleFontPreferences(),
297                                                            null);
298   }
299
300   @Override
301   public void fontChanged() {
302     reinitFontAndResize();
303   }
304
305   @Override
306   public void dispose() {
307     super.dispose();
308     mySettingsProvider.removeListener(this);
309   }
310 }
311