blob: d39d2ec09b71d3e98196bb34fdc7c86415c8b05d [file] [log] [blame]
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;
}
}
}
}