c881d7f142bb07581a4f1256f1e046b6d6f2faee
[idea/community.git] / python / ipnb / src / org / jetbrains / plugins / ipnb / editor / panels / code / IpnbCodeSourcePanel.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 org.jetbrains.plugins.ipnb.editor.panels.code;
17
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.actionSystem.CustomShortcutSet;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.editor.EditorFactory;
22 import com.intellij.openapi.editor.event.EditorMouseListener;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.Disposer;
25 import com.intellij.ui.Gray;
26 import com.intellij.ui.components.panels.HorizontalLayout;
27 import com.intellij.util.ui.UIUtil;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
30 import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
31 import org.jetbrains.plugins.ipnb.editor.actions.IpnbRunCellAction;
32 import org.jetbrains.plugins.ipnb.editor.actions.IpnbRunCellBaseAction;
33 import org.jetbrains.plugins.ipnb.editor.actions.IpnbRunCellInplaceAction;
34 import org.jetbrains.plugins.ipnb.editor.panels.IpnbEditorPanel;
35 import org.jetbrains.plugins.ipnb.editor.panels.IpnbFilePanel;
36 import org.jetbrains.plugins.ipnb.editor.panels.IpnbPanel;
37 import org.jetbrains.plugins.ipnb.format.cells.IpnbCodeCell;
38
39 import javax.swing.*;
40 import java.awt.*;
41 import java.awt.event.*;
42
43 /**
44  * @author traff
45  */
46 public class IpnbCodeSourcePanel extends IpnbPanel<JComponent, IpnbCodeCell> implements IpnbEditorPanel {
47   private Editor myEditor;
48   @NotNull private final Project myProject;
49   @NotNull private final IpnbCodePanel myParent;
50   @NotNull private final String mySource;
51
52   public IpnbCodeSourcePanel(@NotNull final Project project, @NotNull final IpnbCodePanel parent, @NotNull final IpnbCodeCell cell) {
53     super(cell, new HorizontalLayout(5));
54     myProject = project;
55     myParent = parent;
56     mySource = cell.getSourceAsString();
57     final JComponent panel = createViewPanel();
58     add(panel);
59   }
60
61   public void addMouseListener(@NotNull final EditorMouseListener listener) {
62     myEditor.addEditorMouseListener(listener);
63   }
64
65   @NotNull
66   public IpnbCodePanel getIpnbCodePanel() {
67     return myParent;
68   }
69
70   @Override
71   @NotNull
72   public Editor getEditor() {
73     return myEditor;
74   }
75
76   @Override
77   protected JComponent createViewPanel() {
78     final JPanel panel = new JPanel(new BorderLayout());
79     panel.setBackground(UIUtil.isUnderDarcula() ? IpnbEditorUtil.getBackground() : Gray._247);
80
81     myEditor = IpnbEditorUtil.createPythonCodeEditor(myProject, this);
82     Disposer.register(myParent.getFileEditor(), new Disposable() {
83       @Override
84       public void dispose() {
85         EditorFactory.getInstance().releaseEditor(myEditor);
86       }
87     });
88     final JComponent component = myEditor.getComponent();
89     final JComponent contentComponent = myEditor.getContentComponent();
90
91     new IpnbRunCellAction().registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke("shift ENTER")), contentComponent);
92     new IpnbRunCellInplaceAction().registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke("ctrl ENTER")), contentComponent);
93
94     contentComponent.addKeyListener(new KeyAdapter() {
95       @Override
96       public void keyPressed(KeyEvent e) {
97         final int keyCode = e.getKeyCode();
98         final Container parent = myParent.getParent();
99         if (keyCode == KeyEvent.VK_ESCAPE && parent instanceof IpnbFilePanel) {
100           getIpnbCodePanel().setEditing(false);
101           parent.repaint();
102           UIUtil.requestFocus(getIpnbCodePanel().getFileEditor().getIpnbFilePanel());
103         }
104       }
105
106       private void updateVisibleArea(boolean up) {
107         final IpnbFileEditor fileEditor = myParent.getFileEditor();
108         final IpnbFilePanel ipnbPanel = fileEditor.getIpnbFilePanel();
109         final Rectangle rect = ipnbPanel.getVisibleRect();
110
111         final Rectangle cellBounds = IpnbCodeSourcePanel.this.getIpnbCodePanel().getBounds();
112         final JScrollPane scrollPane = fileEditor.getScrollPane();
113
114         final int y = cellBounds.y + myEditor.visualPositionToXY(myEditor.getCaretModel().getVisualPosition()).y;
115         int delta = myEditor.getLineHeight() * 2;
116         if (y <= rect.getY() && up) {
117           scrollPane.getVerticalScrollBar().setValue(y);
118         }
119         if (y + delta > rect.getY() + rect.getHeight() && !up) {
120           scrollPane.getVerticalScrollBar().setValue(y - rect.height + delta);
121         }
122       }
123
124       @Override
125       public void keyReleased(KeyEvent e) {
126         final int keyCode = e.getKeyCode();
127         final Container parent = myParent.getParent();
128
129         final int height = myEditor.getLineHeight() * Math.max(myEditor.getDocument().getLineCount(), 1) + 10;
130         contentComponent.setPreferredSize(new Dimension(parent.getWidth() - 300, height));
131         panel.setPreferredSize(new Dimension(parent.getWidth() - 300, height));
132         myParent.revalidate();
133         myParent.repaint();
134         panel.revalidate();
135         panel.repaint();
136
137         if (parent instanceof IpnbFilePanel) {
138           IpnbFilePanel ipnbFilePanel = (IpnbFilePanel)parent;
139           ipnbFilePanel.revalidate();
140           ipnbFilePanel.repaint();
141           if (keyCode == KeyEvent.VK_ENTER && InputEvent.CTRL_MASK == e.getModifiers()) {
142             IpnbRunCellBaseAction.runCell(ipnbFilePanel, false);
143           }
144           else if (keyCode == KeyEvent.VK_ENTER && InputEvent.SHIFT_DOWN_MASK == e.getModifiersEx()) {
145             IpnbRunCellBaseAction.runCell(ipnbFilePanel, true);
146           }
147           else if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_PAGE_DOWN ||
148                    keyCode == KeyEvent.VK_PAGE_UP) {
149             updateVisibleArea(keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_PAGE_UP);
150           }
151         }
152       }
153     });
154
155     contentComponent.addMouseListener(new MouseAdapter() {
156       @Override
157       public void mousePressed(MouseEvent e) {
158         if (InputEvent.CTRL_DOWN_MASK == e.getModifiersEx()) return;
159         final Container ipnbFilePanel = myParent.getParent();
160         if (ipnbFilePanel instanceof IpnbFilePanel) {
161           ((IpnbFilePanel)ipnbFilePanel).setSelectedCell(myParent, true);
162           myParent.switchToEditing();
163         }
164         UIUtil.requestFocus(contentComponent);
165       }
166     });
167
168     panel.add(component);
169     contentComponent.addHierarchyListener(new HierarchyListener() {
170       @Override
171       public void hierarchyChanged(HierarchyEvent e) {
172         final Container parent = myParent.getParent();
173         if (parent != null) {
174           final int height = myEditor.getLineHeight() * Math.max(myEditor.getDocument().getLineCount(), 1) + 10;
175           contentComponent.setPreferredSize(new Dimension(parent.getWidth() - 300, height));
176           panel.setPreferredSize(new Dimension(parent.getWidth() - 300, height));
177         }
178       }
179     });
180     contentComponent.addHierarchyBoundsListener(new HierarchyBoundsAdapter() {
181       @Override
182       public void ancestorResized(HierarchyEvent e) {
183         final Container parent = myParent.getParent();
184         final Component component = e.getChanged();
185         if (parent != null && component instanceof IpnbFilePanel) {
186           final int height = myEditor.getLineHeight() * Math.max(myEditor.getDocument().getLineCount(), 1) + 10;
187           contentComponent.setPreferredSize(new Dimension(parent.getWidth() - 300, height));
188           panel.setPreferredSize(new Dimension(parent.getWidth() - 300, height));
189           panel.revalidate();
190           panel.repaint();
191           parent.repaint();
192         }
193       }
194     });
195     return panel;
196   }
197 }