| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.ui.content; |
| |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.ui.*; |
| import com.intellij.ui.content.tabs.PinToolwindowTabAction; |
| import com.intellij.ui.content.tabs.TabbedContentAction; |
| import com.intellij.util.IJSwingUtilities; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import javax.swing.plaf.TabbedPaneUI; |
| import java.awt.*; |
| import java.awt.event.MouseEvent; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.List; |
| |
| /** |
| * @author Eugene Belyaev |
| * @author Anton Katilin |
| * @author Vladimir Kondratyev |
| */ |
| public class TabbedPaneContentUI implements ContentUI, PropertyChangeListener { |
| public static final String POPUP_PLACE = "TabbedPanePopup"; |
| |
| private ContentManager myManager; |
| private TabbedPaneWrapper myTabbedPaneWrapper; |
| |
| /** |
| * Creates <code>TabbedPaneContentUI</code> with bottom tab placement. |
| */ |
| public TabbedPaneContentUI() { |
| this(JTabbedPane.BOTTOM); |
| } |
| |
| /** |
| * Creates <code>TabbedPaneContentUI</code> with cpecified tab placement. |
| * |
| * @param tabPlacement constant which defines where the tabs are located. |
| * Acceptable values are <code>javax.swing.JTabbedPane#TOP</code>, |
| * <code>javax.swing.JTabbedPane#LEFT</code>, <code>javax.swing.JTabbedPane#BOTTOM</code> |
| * and <code>javax.swing.JTabbedPane#RIGHT</code>. |
| */ |
| public TabbedPaneContentUI(int tabPlacement) { |
| myTabbedPaneWrapper = new MyTabbedPaneWrapper(tabPlacement); |
| } |
| |
| public JComponent getComponent() { |
| return myTabbedPaneWrapper.getComponent(); |
| } |
| |
| public void setManager(@NotNull ContentManager manager) { |
| if (myManager != null) { |
| throw new IllegalStateException(); |
| } |
| myManager = manager; |
| myManager.addContentManagerListener(new MyContentManagerListener()); |
| } |
| |
| public void propertyChange(PropertyChangeEvent e) { |
| if (Content.PROP_DISPLAY_NAME.equals(e.getPropertyName())) { |
| Content content = (Content)e.getSource(); |
| int index = myTabbedPaneWrapper.indexOfComponent(content.getComponent()); |
| if (index != -1) { |
| myTabbedPaneWrapper.setTitleAt(index, content.getTabName()); |
| } |
| } |
| else if (Content.PROP_DESCRIPTION.equals(e.getPropertyName())) { |
| Content content = (Content)e.getSource(); |
| int index = myTabbedPaneWrapper.indexOfComponent(content.getComponent()); |
| if (index != -1) { |
| myTabbedPaneWrapper.setToolTipTextAt(index, content.getDescription()); |
| } |
| } |
| else if (Content.PROP_COMPONENT.equals(e.getPropertyName())) { |
| Content content = (Content)e.getSource(); |
| JComponent oldComponent = (JComponent)e.getOldValue(); |
| int index = myTabbedPaneWrapper.indexOfComponent(oldComponent); |
| if (index != -1) { |
| boolean hasFocus = IJSwingUtilities.hasFocus2(oldComponent); |
| myTabbedPaneWrapper.setComponentAt(index, content.getComponent()); |
| if (hasFocus) { |
| content.getComponent().requestDefaultFocus(); |
| } |
| } |
| } |
| else if (Content.PROP_ICON.equals(e.getPropertyName())) { |
| Content content = (Content)e.getSource(); |
| int index = myTabbedPaneWrapper.indexOfComponent(content.getComponent()); |
| if (index != -1) { |
| myTabbedPaneWrapper.setIconAt(index, (Icon)e.getNewValue()); |
| } |
| } |
| } |
| |
| private Content getSelectedContent() { |
| JComponent selectedComponent = myTabbedPaneWrapper.getSelectedComponent(); |
| return myManager.getContent(selectedComponent); |
| } |
| |
| |
| |
| |
| private class MyTabbedPaneWrapper extends TabbedPaneWrapper.AsJTabbedPane { |
| public MyTabbedPaneWrapper(int tabPlacement) { |
| super(tabPlacement); |
| } |
| |
| protected TabbedPane createTabbedPane(int tabPlacement) { |
| return new MyTabbedPane(tabPlacement); |
| } |
| |
| protected TabbedPaneHolder createTabbedPaneHolder() { |
| return new MyTabbedPaneHolder(this); |
| } |
| |
| private class MyTabbedPane extends TabbedPaneImpl { |
| public MyTabbedPane(int tabPlacement) { |
| super(tabPlacement); |
| addMouseListener(new MyPopupHandler()); |
| enableEvents(AWTEvent.MOUSE_EVENT_MASK); |
| } |
| |
| private void closeTabAt(int x, int y) { |
| TabbedPaneUI ui = getUI(); |
| int index = ui.tabForCoordinate(this, x, y); |
| if (index < 0 || !myManager.canCloseContents()) { |
| return; |
| } |
| final Content content = myManager.getContent(index); |
| if (content != null && content.isCloseable()) { |
| myManager.removeContent(content, true); |
| } |
| } |
| |
| /** |
| * Hides selected menu. |
| */ |
| private void hideMenu() { |
| MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager(); |
| menuSelectionManager.clearSelectedPath(); |
| } |
| |
| protected void processMouseEvent(MouseEvent e) { |
| if (e.isPopupTrigger()) { // Popup doesn't activate clicked tab. |
| showPopup(e.getX(), e.getY()); |
| return; |
| } |
| |
| if (!e.isShiftDown() && (MouseEvent.BUTTON1_MASK & e.getModifiers()) > 0) { // RightClick without Shift modifiers just select tab |
| if (MouseEvent.MOUSE_RELEASED == e.getID()) { |
| TabbedPaneUI ui = getUI(); |
| int index = ui.tabForCoordinate(this, e.getX(), e.getY()); |
| if (index != -1) { |
| setSelectedIndex(index); |
| } |
| hideMenu(); |
| } |
| } |
| else if (e.isShiftDown() && (MouseEvent.BUTTON1_MASK & e.getModifiers()) > 0) { // Shift+LeftClick closes the tab |
| if (MouseEvent.MOUSE_RELEASED == e.getID()) { |
| closeTabAt(e.getX(), e.getY()); |
| hideMenu(); |
| } |
| } |
| else if ((MouseEvent.BUTTON2_MASK & e.getModifiers()) > 0) { // MouseWheelClick closes the tab |
| if (MouseEvent.MOUSE_RELEASED == e.getID()) { |
| closeTabAt(e.getX(), e.getY()); |
| hideMenu(); |
| } |
| } |
| else if ((MouseEvent.BUTTON3_MASK & e.getModifiers()) > 0 && SystemInfo.isWindows) { // Right mouse button doesn't activate tab |
| } |
| else { |
| super.processMouseEvent(e); |
| } |
| } |
| |
| protected ChangeListener createChangeListener() { |
| return new MyModelListener(); |
| } |
| |
| private class MyModelListener extends ModelListener { |
| public void stateChanged(ChangeEvent e) { |
| Content content = getSelectedContent(); |
| if (content != null) { |
| myManager.setSelectedContent(content); |
| } |
| super.stateChanged(e); |
| } |
| } |
| |
| /** |
| * @return content at the specified location. <code>x</code> and <code>y</code> are in |
| * tabbed pane coordinate system. The method returns <code>null</code> if there is no contnt at the |
| * specified location. |
| */ |
| private Content getContentAt(int x, int y) { |
| TabbedPaneUI ui = getUI(); |
| int index = ui.tabForCoordinate(this, x, y); |
| if (index < 0) { |
| return null; |
| } |
| return myManager.getContent(index); |
| } |
| |
| protected class MyPopupHandler extends PopupHandler { |
| public void invokePopup(Component comp, int x, int y) { |
| if (myManager.getContentCount() == 0) return; |
| showPopup(x, y); |
| } |
| } |
| |
| /** |
| * Shows showPopup menu at the specified location. The <code>x</code> and <code>y</code> coordinates |
| * are in JTabbedPane coordinate system. |
| */ |
| private void showPopup(int x, int y) { |
| Content content = getContentAt(x, y); |
| if (content == null) { |
| return; |
| } |
| DefaultActionGroup group = new DefaultActionGroup(); |
| group.add(new TabbedContentAction.CloseAction(content)); |
| if (myTabbedPaneWrapper.getTabCount() > 1) { |
| group.add(new TabbedContentAction.CloseAllAction(myManager)); |
| group.add(new TabbedContentAction.CloseAllButThisAction(content)); |
| } |
| group.addSeparator(); |
| group.add(PinToolwindowTabAction.getPinAction()); |
| group.addSeparator(); |
| group.add(new TabbedContentAction.MyNextTabAction(myManager)); |
| group.add(new TabbedContentAction.MyPreviousTabAction(myManager)); |
| final List<AnAction> additionalActions = myManager.getAdditionalPopupActions(content); |
| if (additionalActions != null) { |
| group.addSeparator(); |
| for (AnAction anAction : additionalActions) { |
| group.add(anAction); |
| } |
| } |
| ActionPopupMenu menu = ActionManager.getInstance().createActionPopupMenu(POPUP_PLACE, group); |
| menu.getComponent().show(myTabbedPaneWrapper.getComponent(), x, y); |
| } |
| } |
| |
| private class MyTabbedPaneHolder extends TabbedPaneHolder implements DataProvider { |
| |
| private MyTabbedPaneHolder(TabbedPaneWrapper wrapper) { |
| super(wrapper); |
| } |
| |
| public Object getData(String dataId) { |
| if (PlatformDataKeys.CONTENT_MANAGER.is(dataId)) { |
| return myManager; |
| } |
| if (PlatformDataKeys.NONEMPTY_CONTENT_MANAGER.is(dataId) && myManager.getContentCount() > 1) { |
| return myManager; |
| } |
| return null; |
| } |
| } |
| } |
| |
| private class MyContentManagerListener extends ContentManagerAdapter { |
| public void contentAdded(ContentManagerEvent event) { |
| Content content = event.getContent(); |
| myTabbedPaneWrapper.insertTab(content.getTabName(), |
| content.getIcon(), |
| content.getComponent(), |
| content.getDescription(), |
| event.getIndex()); |
| content.addPropertyChangeListener(TabbedPaneContentUI.this); |
| } |
| |
| public void contentRemoved(ContentManagerEvent event) { |
| event.getContent().removePropertyChangeListener(TabbedPaneContentUI.this); |
| myTabbedPaneWrapper.removeTabAt(event.getIndex()); |
| } |
| |
| public void selectionChanged(ContentManagerEvent event) { |
| int index = event.getIndex(); |
| if (index != -1) { |
| myTabbedPaneWrapper.setSelectedIndex(index); |
| } |
| } |
| } |
| |
| public boolean isSingleSelection() { |
| return true; |
| } |
| |
| public boolean isToSelectAddedContent() { |
| return false; |
| } |
| |
| public boolean canBeEmptySelection() { |
| return false; |
| } |
| |
| public void beforeDispose() { |
| } |
| |
| public boolean canChangeSelectionTo(@NotNull Content content, boolean implicit) { |
| return true; |
| } |
| |
| @NotNull |
| @Override |
| public String getCloseActionName() { |
| return UIBundle.message("tabbed.pane.close.tab.action.name"); |
| } |
| |
| @NotNull |
| @Override |
| public String getCloseAllButThisActionName() { |
| return UIBundle.message("tabbed.pane.close.all.tabs.but.this.action.name"); |
| } |
| |
| @NotNull |
| @Override |
| public String getPreviousContentActionName() { |
| return "Select Previous Tab"; |
| } |
| |
| @NotNull |
| @Override |
| public String getNextContentActionName() { |
| return "Select Next Tab"; |
| } |
| |
| public void dispose() { |
| } |
| } |