/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * 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 net.java.sip.communicator.plugin.desktoputil; /* * The following code borrowed from David Bismut, davidou@mageos.com Intern, * SETLabs, Infosys Technologies Ltd. May 2004 - Jul 2004 Ecole des Mines de * Nantes, France */ import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import net.java.sip.communicator.plugin.desktoputil.event.*; import net.java.sip.communicator.plugin.desktoputil.plaf.*; import net.java.sip.communicator.util.skin.*; /** * A JTabbedPane with some added UI functionalities. A close and max/detach * icons are added to every tab, typically to let the user close or detach the * tab by clicking on these icons. * * @author Yana Stamcheva * @author Adam Netocny */ public class SIPCommTabbedPane extends JTabbedPane implements ChangeListener, Skinnable { /** * Serial version UID. */ private static final long serialVersionUID = 0L; private int overTabIndex = -1; private int lastSelectedIndex; public SIPCommTabbedPane() { this(false, false); } /** * Creates the <code>CloseAndMaxTabbedPane</code> with an enhanced UI if * <code>enhancedUI</code> parameter is set to <code>true</code>. * * @param closingTabs support for closable tabs * @param maximizingTabs support for maximisable tabs */ public SIPCommTabbedPane(boolean closingTabs, boolean maximizingTabs) { super.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); UIManager.getDefaults() .put("TabbedPane.tabAreaInsets", new Insets(0, 5, 0, 0)); UIManager.getDefaults() .put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); this.setForeground( new Color(DesktopUtilActivator.getResources() .getColor("service.gui.TAB_TITLE"))); this.setUI(new SIPCommTabbedPaneEnhancedUI()); if(closingTabs) this.setCloseIcon(true); if(maximizingTabs) this.setMaxIcon(true); this.addChangeListener(this); } /** * Returns the index of the last tab on which the mouse did an action. * * @return */ public int getOverTabIndex() { return overTabIndex; } /** * Returns <code>true</code> if the close icon is enabled. * * @return */ public boolean isCloseEnabled() { SIPCommTabbedPaneUI ui = (SIPCommTabbedPaneUI) this.getUI(); return ui.isCloseEnabled(); } /** * Returns <code>true</code> if the max/detach icon is enabled. * * @return */ public boolean isMaxEnabled() { return ((SIPCommTabbedPaneUI) getUI()).isMaxEnabled(); } /** * Override JTabbedPane method. Does nothing. * @param tabLayoutPolicy The tab layout policy. */ @Override public void setTabLayoutPolicy(int tabLayoutPolicy) { } /** * Override JTabbedPane method. Does nothing. * @param tabPlacement The tab placement. */ @Override public void setTabPlacement(int tabPlacement) { } /** * Sets whether the tabbedPane should have a close icon or not. * * @param b whether the tabbedPane should have a close icon or not */ public void setCloseIcon(boolean b) { ((SIPCommTabbedPaneUI) getUI()).setCloseIcon(b); } /** * Sets whether the tabbedPane should have a max/detach icon or not. * * @param b whether the tabbedPane should have a max/detach icon or not */ public void setMaxIcon(boolean b) { ((SIPCommTabbedPaneUI) getUI()).setMaxIcon(b); } /** * Detaches the <code>index</code> tab in a separate frame. When the frame * is closed, the tab is automatically reinserted into the tabbedPane. * * @param index index of the tabbedPane to be detached */ public void detachTab(int index) { if (index < 0 || index >= getTabCount()) return; final int tabIndex = index; final JComponent c = (JComponent) getComponentAt(tabIndex); final Icon icon = getIconAt(tabIndex); final String title = getTitleAt(tabIndex); final String toolTip = getToolTipTextAt(tabIndex); final Border border = c.getBorder(); final JFrame frame = new SIPCommFrame() { /** * Serial version UID. */ private static final long serialVersionUID = 0L; @Override protected void close(boolean isEscaped) { if (isEscaped) return; dispose(); insertTab(title, icon, c, toolTip, Math.min(tabIndex, getTabCount())); c.setBorder(border); setSelectedComponent(c); } }; Window parentWindow = SwingUtilities.windowForComponent(this); removeTabAt(index); c.setPreferredSize(c.getSize()); frame.setTitle(title); frame.getContentPane().add(c); frame.setLocation(parentWindow.getLocation()); frame.pack(); WindowFocusListener windowFocusListener = new WindowFocusListener() { long start; long end; public void windowGainedFocus(WindowEvent e) { start = System.currentTimeMillis(); } public void windowLostFocus(WindowEvent e) { end = System.currentTimeMillis(); long elapsed = end - start; if (elapsed < 100) frame.toFront(); frame.removeWindowFocusListener(this); } }; /* * This is a small hack to avoid Windows GUI bug, that prevent a new * window from stealing focus (without this windowFocusListener, most of * the time the new frame would just blink from foreground to * background). A windowFocusListener is added to the frame, and if the * time between the frame beeing in foreground and the frame beeing in * background is less that 100ms, it just brings the windows to the * front once again. Then it removes the windowFocusListener. Note that * this hack would not be required on Linux or UNIX based systems. */ frame.addWindowFocusListener(windowFocusListener); // frame.show(); frame.setVisible(true); frame.toFront(); } /** * Adds a <code>CloseListener</code> to the tabbedPane. * * @param l the <code>CloseListener</code> to add * @see #fireCloseTabEvent * @see #removeCloseListener */ public synchronized void addCloseListener(CloseListener l) { listenerList.add(CloseListener.class, l); } /** * Adds a <code>MaxListener</code> to the tabbedPane. * * @param l the <code>MaxListener</code> to add * @see #fireMaxTabEvent * @see #removeMaxListener */ public synchronized void addMaxListener(MaxListener l) { listenerList.add(MaxListener.class, l); } /** * Adds a <code>DoubleClickListener</code> to the tabbedPane. * * @param l the <code>DoubleClickListener</code> to add * @see #fireDoubleClickTabEvent * @see #removeDoubleClickListener */ public synchronized void addDoubleClickListener(DoubleClickListener l) { listenerList.add(DoubleClickListener.class, l); } /** * Adds a <code>PopupOutsideListener</code> to the tabbedPane. * * @param l the <code>PopupOutsideListener</code> to add * @see #firePopupOutsideTabEvent * @see #removePopupOutsideListener */ public synchronized void addPopupOutsideListener(PopupOutsideListener l) { listenerList.add(PopupOutsideListener.class, l); } /** * Removes a <code>CloseListener</code> from this tabbedPane. * * @param l the <code>CloseListener</code> to remove * @see #fireCloseTabEvent * @see #addCloseListener */ public synchronized void removeCloseListener(CloseListener l) { listenerList.remove(CloseListener.class, l); } /** * Removes a <code>MaxListener</code> from this tabbedPane. * * @param l the <code>MaxListener</code> to remove * @see #fireMaxTabEvent * @see #addMaxListener */ public synchronized void removeMaxListener(MaxListener l) { listenerList.remove(MaxListener.class, l); } /** * Removes a <code>DoubleClickListener</code> from this tabbedPane. * * @param l * the <code>DoubleClickListener</code> to remove * @see #fireDoubleClickTabEvent * @see #addDoubleClickListener */ public synchronized void removeDoubleClickListener(DoubleClickListener l) { listenerList.remove(DoubleClickListener.class, l); } /** * Removes a <code>PopupOutsideListener</code> from this tabbedPane. * * @param l * the <code>PopupOutsideListener</code> to remove * @see #firePopupOutsideTabEvent * @see #addPopupOutsideListener */ public synchronized void removePopupOutsideListener( PopupOutsideListener l) { listenerList.remove(PopupOutsideListener.class, l); } /** * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to * every <code>CloseListener</code>. The method also updates the * <code>overTabIndex</code> of the tabbedPane with a value coming from * the UI. This method method is called each time a <code>MouseEvent</code> * is received from the UI when the user clicks on the close icon of the tab * which index is <code>overTabIndex</code>. * * @param e * the <code>MouseEvent</code> to be sent * @param overTabIndex * the index of a tab, usually the tab over which the mouse is * * @see #addCloseListener */ public void fireCloseTabEvent(MouseEvent e, int overTabIndex) { this.overTabIndex = overTabIndex; EventListener[] closeListeners = getListeners(CloseListener.class); for (int i = 0; i < closeListeners.length; i++) { ((CloseListener) closeListeners[i]).closeOperation(e); } } /** * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to * every <code>MaxListener</code>. The method also updates the * <code>overTabIndex</code> of the tabbedPane with a value coming from * the UI. This method method is called each time a <code>MouseEvent</code> * is received from the UI when the user clicks on the max icon of the tab * which index is <code>overTabIndex</code>. * * @param e * the <code>MouseEvent</code> to be sent * @param overTabIndex * the index of a tab, usually the tab over which the mouse is * * @see #addMaxListener */ public void fireMaxTabEvent(MouseEvent e, int overTabIndex) { this.overTabIndex = overTabIndex; EventListener[] maxListeners = getListeners(MaxListener.class); for (int i = 0; i < maxListeners.length; i++) { ((MaxListener) maxListeners[i]).maxOperation(e); } } /** * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to * every <code>DoubleClickListener</code>. The method also updates the * <code>overTabIndex</code> of the tabbedPane with a value coming from * the UI. This method method is called each time a <code>MouseEvent</code> * is received from the UI when the user double-clicks on the tab which * index is <code>overTabIndex</code>. * * @param e * the <code>MouseEvent</code> to be sent * @param overTabIndex * the index of a tab, usually the tab over which the mouse is * * @see #addDoubleClickListener */ public void fireDoubleClickTabEvent(MouseEvent e, int overTabIndex) { this.overTabIndex = overTabIndex; EventListener[] dClickListeners = getListeners(DoubleClickListener.class); for (int i = 0; i < dClickListeners.length; i++) { ((DoubleClickListener) dClickListeners[i]).doubleClickOperation(e); } } /** * Sends a <code>MouseEvent</code>, whose source is this tabbedpane, to * every <code>PopupOutsideListener</code>. The method also sets the * <code>overTabIndex</code> to -1. This method method is called each time * a <code>MouseEvent</code> is received from the UI when the user * right-clicks on the inactive part of a tabbedPane. * * @param e * the <code>MouseEvent</code> to be sent * * @see #addPopupOutsideListener */ public void firePopupOutsideTabEvent(MouseEvent e) { this.overTabIndex = -1; EventListener[] popupListeners = getListeners(PopupOutsideListener.class); for (int i = 0; i < popupListeners.length; i++) { ((PopupOutsideListener) popupListeners[i]).popupOutsideOperation(e); } } /** * Overrides setSelectedIndex in JTabbedPane in order to remove the * highlight if the tab which is selected. * @param tabIndex The index of the tab to be selected. */ @Override public void setSelectedIndex(int tabIndex) { SIPCommTabbedPaneEnhancedUI ui = (SIPCommTabbedPaneEnhancedUI) this.getUI(); if (ui.isTabHighlighted(tabIndex)) { ui.tabRemoveHighlight(tabIndex); } super.setSelectedIndex(tabIndex); } /** * Highlights the tab with the given index. * * @param tabIndex The tab index. */ public void highlightTab(int tabIndex) { SIPCommTabbedPaneEnhancedUI ui = (SIPCommTabbedPaneEnhancedUI) this.getUI(); if (!ui.isTabHighlighted(tabIndex) && this.getSelectedIndex() != tabIndex) ui.tabAddHightlight(tabIndex); this.repaint(); } @Override public void removeTabAt(int index) { if (index < lastSelectedIndex) { this.setSelectedIndex(lastSelectedIndex - 1); } else if (index > lastSelectedIndex) { this.setSelectedIndex(lastSelectedIndex); } super.removeTabAt(index); } public void stateChanged(ChangeEvent e) { lastSelectedIndex = this.getSelectedIndex(); } /** * Reloads skin information. */ public void loadSkin() { this.setForeground( new Color(DesktopUtilActivator.getResources() .getColor("service.gui.TAB_TITLE"))); ((SIPCommTabbedPaneEnhancedUI)this.getUI()).loadSkin(); } }