| package org.jetbrains.plugins.terminal; |
| |
| import com.google.common.base.Predicate; |
| import com.intellij.ide.dnd.DnDDropHandler; |
| import com.intellij.ide.dnd.DnDEvent; |
| import com.intellij.ide.dnd.DnDSupport; |
| import com.intellij.ide.dnd.TransferableWrapper; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.actionSystem.ActionManager; |
| import com.intellij.openapi.actionSystem.AnAction; |
| import com.intellij.openapi.actionSystem.AnActionEvent; |
| import com.intellij.openapi.actionSystem.Presentation; |
| import com.intellij.openapi.fileEditor.impl.EditorTabbedContainer; |
| import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; |
| import com.intellij.openapi.project.DumbAwareAction; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.wm.IdeFocusManager; |
| import com.intellij.psi.PsiDirectory; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiFileSystemItem; |
| import com.intellij.ui.SimpleColoredComponent; |
| import com.intellij.ui.components.JBTextField; |
| import com.intellij.ui.docking.DockManager; |
| import com.intellij.ui.docking.DragSession; |
| import com.intellij.ui.tabs.TabInfo; |
| import com.intellij.ui.tabs.TabsListener; |
| import com.intellij.ui.tabs.impl.JBEditorTabs; |
| import com.intellij.ui.tabs.impl.JBTabsImpl; |
| import com.intellij.ui.tabs.impl.TabLabel; |
| import com.intellij.util.ui.UIUtil; |
| import com.jediterm.terminal.ui.*; |
| import com.jediterm.terminal.ui.settings.SettingsProvider; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.terminal.vfs.TerminalSessionVirtualFileImpl; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.util.List; |
| import java.util.concurrent.CopyOnWriteArraySet; |
| |
| /** |
| * @author traff |
| */ |
| public class JBTabbedTerminalWidget extends TabbedTerminalWidget implements Disposable { |
| |
| private Project myProject; |
| private final JBTerminalSystemSettingsProvider mySettingsProvider; |
| private Disposable myParent; |
| |
| public JBTabbedTerminalWidget(@NotNull Project project, |
| @NotNull JBTerminalSystemSettingsProvider settingsProvider, |
| final @NotNull Predicate<Pair<TerminalWidget, String>> createNewSessionAction, @NotNull Disposable parent) { |
| super(settingsProvider, new Predicate<TerminalWidget>() { |
| @Override |
| public boolean apply(TerminalWidget input) { |
| return createNewSessionAction.apply(Pair.<TerminalWidget, String>create(input, null)); |
| } |
| }); |
| myProject = project; |
| |
| mySettingsProvider = settingsProvider; |
| myParent = parent; |
| |
| convertActions(this, getActions()); |
| |
| Disposer.register(parent, this); |
| Disposer.register(this, settingsProvider); |
| |
| DnDSupport.createBuilder(this).setDropHandler(new DnDDropHandler() { |
| @Override |
| public void drop(DnDEvent event) { |
| if (event.getAttachedObject() instanceof TransferableWrapper) { |
| TransferableWrapper ao = (TransferableWrapper)event.getAttachedObject(); |
| if (ao != null && |
| ao.getPsiElements() != null && |
| ao.getPsiElements().length == 1 && |
| ao.getPsiElements()[0] instanceof PsiFileSystemItem) { |
| PsiFileSystemItem element = (PsiFileSystemItem)ao.getPsiElements()[0]; |
| PsiDirectory dir = element instanceof PsiFile ? ((PsiFile)element).getContainingDirectory() : (PsiDirectory)element; |
| |
| createNewSessionAction.apply(Pair.<TerminalWidget, String>create(JBTabbedTerminalWidget.this, dir.getVirtualFile().getPath())); |
| } |
| } |
| } |
| } |
| |
| ).install(); |
| } |
| |
| public static void convertActions(@NotNull JComponent component, |
| @NotNull List<TerminalAction> actions) { |
| convertActions(component, actions, null); |
| } |
| |
| public static void convertActions(@NotNull JComponent component, |
| @NotNull List<TerminalAction> actions, |
| @Nullable final Predicate<KeyEvent> elseAction) { |
| for (final TerminalAction action : actions) { |
| AnAction a = new DumbAwareAction() { |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| KeyEvent event = e.getInputEvent() instanceof KeyEvent ? (KeyEvent)e.getInputEvent() : null; |
| if (!action.perform(event)) { |
| if (elseAction != null) { |
| elseAction.apply(event); |
| } |
| } |
| } |
| }; |
| a.registerCustomShortcutSet(action.getKeyCode(), action.getModifiers(), component); |
| } |
| } |
| |
| @Override |
| protected JediTermWidget createInnerTerminalWidget(SettingsProvider settingsProvider) { |
| return new JBTerminalWidget(mySettingsProvider, myParent); |
| } |
| |
| @Override |
| protected TerminalTabs createTabbedPane() { |
| return new JBTerminalTabs(myProject, myParent); |
| } |
| |
| public class JBTerminalTabs implements TerminalTabs { |
| private final JBEditorTabs myTabs; |
| |
| private TabInfo.DragOutDelegate myDragDelegate = new MyDragOutDelegate(); |
| |
| private final CopyOnWriteArraySet<TabChangeListener> myListeners = new CopyOnWriteArraySet<TabChangeListener>(); |
| |
| public JBTerminalTabs(@NotNull Project project, @NotNull Disposable parent) { |
| final ActionManager actionManager = ActionManager.getInstance(); |
| myTabs = new JBEditorTabs(project, actionManager, IdeFocusManager.getInstance(project), parent) { |
| @Override |
| protected TabLabel createTabLabel(TabInfo info) { |
| return new TerminalTabLabel(this, info); |
| } |
| }; |
| |
| myTabs.addListener(new TabsListener.Adapter() { |
| @Override |
| public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) { |
| for (TabChangeListener each : myListeners) { |
| each.selectionChanged(); |
| } |
| } |
| |
| @Override |
| public void tabRemoved(TabInfo tabInfo) { |
| for (TabChangeListener each : myListeners) { |
| each.tabRemoved(); |
| } |
| } |
| }); |
| |
| myTabs.setTabDraggingEnabled(true); |
| } |
| |
| @Override |
| public int getSelectedIndex() { |
| return myTabs.getIndexOf(myTabs.getSelectedInfo()); |
| } |
| |
| @Override |
| public void setSelectedIndex(int index) { |
| myTabs.select(myTabs.getTabAt(index), true); |
| } |
| |
| @Override |
| public void setTabComponentAt(int index, Component component) { |
| //nop |
| } |
| |
| @Override |
| public int indexOfTabComponent(Component component) { |
| return 0; //nop |
| } |
| |
| |
| private TabInfo getTabAt(int index) { |
| checkIndex(index); |
| return myTabs.getTabAt(index); |
| } |
| |
| private void checkIndex(int index) { |
| if (index < 0 || index >= getTabCount()) { |
| throw new ArrayIndexOutOfBoundsException("tabCount=" + getTabCount() + " index=" + index); |
| } |
| } |
| |
| |
| @Override |
| public JediTermWidget getComponentAt(int i) { |
| return (JediTermWidget)getTabAt(i).getComponent(); |
| } |
| |
| @Override |
| public void addChangeListener(TabChangeListener listener) { |
| myListeners.add(listener); |
| } |
| |
| @Override |
| public void setTitleAt(int index, String title) { |
| getTabAt(index).setText(title); |
| } |
| |
| @Override |
| public void setSelectedComponent(JediTermWidget terminal) { |
| TabInfo info = myTabs.findInfo(terminal); |
| if (info != null) { |
| myTabs.select(info, true); |
| } |
| } |
| |
| @Override |
| public JComponent getComponent() { |
| return myTabs.getComponent(); |
| } |
| |
| @Override |
| public int getTabCount() { |
| return myTabs.getTabCount(); |
| } |
| |
| @Override |
| public void addTab(String name, JediTermWidget terminal) { |
| myTabs.addTab(createTabInfo(name, terminal)); |
| } |
| |
| private TabInfo createTabInfo(String name, JediTermWidget terminal) { |
| TabInfo tabInfo = new TabInfo(terminal).setText(name).setDragOutDelegate(myDragDelegate); |
| return tabInfo |
| .setObject(new TerminalSessionVirtualFileImpl(tabInfo, terminal, mySettingsProvider)); |
| } |
| |
| public String getTitleAt(int i) { |
| return getTabAt(i).getText(); |
| } |
| |
| public void removeAll() { |
| myTabs.removeAllTabs(); |
| } |
| |
| @Override |
| public void remove(JediTermWidget terminal) { |
| TabInfo info = myTabs.findInfo(terminal); |
| if (info != null) { |
| myTabs.removeTab(info); |
| } |
| } |
| |
| private class TerminalTabLabel extends TabLabel { |
| public TerminalTabLabel(final JBTabsImpl tabs, final TabInfo info) { |
| super(tabs, info); |
| |
| setOpaque(false); |
| |
| setFocusable(false); |
| |
| SimpleColoredComponent label = myLabel; |
| |
| //add more space between the label and the button |
| label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); |
| |
| label.addMouseListener(new MouseAdapter() { |
| |
| @Override |
| public void mouseReleased(MouseEvent event) { |
| handleMouse(event); |
| } |
| |
| @Override |
| public void mousePressed(MouseEvent event) { |
| handleMouse(event); |
| } |
| |
| private void handleMouse(MouseEvent e) { |
| if (e.isPopupTrigger()) { |
| JPopupMenu menu = createPopup(); |
| menu.show(e.getComponent(), e.getX(), e.getY()); |
| } |
| else if (e.getButton() != MouseEvent.BUTTON2) { |
| myTabs.select(getInfo(), true); |
| |
| if (e.getClickCount() == 2 && !e.isConsumed()) { |
| e.consume(); |
| renameTab(); |
| } |
| } |
| } |
| |
| @Override |
| public void mouseClicked(MouseEvent e) { |
| if (e.getButton() == MouseEvent.BUTTON2) { |
| if (myTabs.getSelectedInfo() == info) { |
| closeCurrentSession(); |
| } |
| else { |
| myTabs.select(info, true); |
| } |
| } |
| } |
| }); |
| } |
| |
| protected JPopupMenu createPopup() { |
| JPopupMenu popupMenu = new JPopupMenu(); |
| |
| TerminalAction.addToMenu(popupMenu, JBTabbedTerminalWidget.this); |
| |
| JMenuItem rename = new JMenuItem("Rename Tab"); |
| |
| rename.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent actionEvent) { |
| renameTab(); |
| } |
| }); |
| |
| popupMenu.add(rename); |
| |
| return popupMenu; |
| } |
| |
| private void renameTab() { |
| new TabRenamer() { |
| @Override |
| protected JTextField createTextField() { |
| JBTextField textField = new JBTextField() { |
| private int myMinimalWidth; |
| |
| @Override |
| public Dimension getPreferredSize() { |
| Dimension size = super.getPreferredSize(); |
| if (size.width > myMinimalWidth) { |
| myMinimalWidth = size.width; |
| } |
| |
| return wider(size, myMinimalWidth); |
| } |
| |
| private Dimension wider(Dimension size, int minimalWidth) { |
| return new Dimension(minimalWidth + 10, size.height); |
| } |
| }; |
| if (myTabs.useSmallLabels()) { |
| textField.setFont(com.intellij.util.ui.UIUtil.getFont(UIUtil.FontSize.SMALL, textField.getFont())); |
| } |
| textField.setOpaque(true); |
| return textField; |
| } |
| }.install(getSelectedIndex(), getInfo().getText(), myLabel, new TabRenamer.RenameCallBack() { |
| @Override |
| public void setComponent(Component c) { |
| myTabs.setTabDraggingEnabled(!(c instanceof JBTextField)); |
| |
| setPlaceholderContent(true, (JComponent)c); |
| } |
| |
| @Override |
| public void setNewName(int index, String name) { |
| setTitleAt(index, name); |
| } |
| }); |
| } |
| } |
| |
| class MyDragOutDelegate implements TabInfo.DragOutDelegate { |
| |
| private TerminalSessionVirtualFileImpl myFile; |
| private DragSession mySession; |
| |
| @Override |
| public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) { |
| final TabInfo previousSelection = info.getPreviousSelection(); |
| final Image img = JBTabsImpl.getComponentImage(info); |
| info.setHidden(true); |
| if (previousSelection != null) { |
| myTabs.select(previousSelection, true); |
| } |
| |
| myFile = (TerminalSessionVirtualFileImpl)info.getObject(); |
| Presentation presentation = new Presentation(info.getText()); |
| presentation.setIcon(info.getIcon()); |
| mySession = getDockManager() |
| .createDragSession(mouseEvent, new EditorTabbedContainer.DockableEditor(myProject, img, myFile, presentation, |
| info.getComponent().getPreferredSize(), false)); |
| } |
| |
| private DockManager getDockManager() { |
| return DockManager.getInstance(myProject); |
| } |
| |
| @Override |
| public void processDragOut(MouseEvent event, TabInfo source) { |
| mySession.process(event); |
| } |
| |
| @Override |
| public void dragOutFinished(MouseEvent event, TabInfo source) { |
| myFile.putUserData(FileEditorManagerImpl.CLOSING_TO_REOPEN, Boolean.TRUE); |
| |
| |
| myTabs.removeTab(source); |
| |
| mySession.process(event); |
| |
| myFile.putUserData(FileEditorManagerImpl.CLOSING_TO_REOPEN, null); |
| |
| |
| myFile = null; |
| mySession = null; |
| } |
| |
| @Override |
| public void dragOutCancelled(TabInfo source) { |
| source.setHidden(false); |
| if (mySession != null) { |
| mySession.cancel(); |
| } |
| |
| myFile = null; |
| mySession = null; |
| } |
| } |
| } |
| } |