001: /*
002: * The contents of this file are subject to the Mozilla Public License
003: * Version 1.1 (the "License"); you may not use this file except in
004: * compliance with the License. You may obtain a copy of the License at
005: * http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
009: * License for the specific language governing rights and limitations
010: * under the License.
011: *
012: * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
013: *
014: * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
015: * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
016: *
017: * Contributor(s):
018: * Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
019: *
020: * If you didn't download this code from the following link, you should check
021: * if you aren't using an obsolete version: http://www.isqlviewer.com
022: */
023: package org.isqlviewer.swing;
024:
025: import java.awt.Component;
026: import java.awt.Cursor;
027: import java.awt.IllegalComponentStateException;
028: import java.awt.Rectangle;
029: import java.awt.event.ActionEvent;
030: import java.awt.event.ActionListener;
031: import java.awt.event.MouseEvent;
032: import java.util.Hashtable;
033: import java.util.Vector;
034:
035: import javax.swing.Icon;
036: import javax.swing.JComponent;
037: import javax.swing.JOptionPane;
038: import javax.swing.JScrollPane;
039: import javax.swing.JTabbedPane;
040: import javax.swing.JViewport;
041: import javax.swing.SwingConstants;
042: import javax.swing.event.EventListenerList;
043: import javax.swing.event.MouseInputAdapter;
044:
045: import org.isqlviewer.ui.dnd.TabbedPaneDropTarget;
046:
047: /**
048: * Enhanced tabbed pane with new input events and lockable and closable tabs.
049: * <p>
050: *
051: * @author Mark A. Kobold
052: * @version 1.0
053: */
054: public class EnhancedTabbedPane extends JTabbedPane {
055:
056: private static final long serialVersionUID = 9139921016326191363L;
057:
058: public final static int ACTION_TAB_CLICKED = 0xFD00;
059: public final static int ACTION_TAB_DBL_CLICKED = 0xFD01;
060:
061: protected static final Icon LOCK_ICON = SwingUtilities
062: .loadIconResource("encrypted", 16);
063: protected static final Icon CLOSE_ICON = SwingUtilities
064: .loadIconResource("close_view", 16);
065:
066: protected int maxtabcount = -1;
067: protected int oldestIndex = -1;
068:
069: private static final String CLIENT_IS_LOCKED = "EnhancedTabbedPane.isLocked";
070: private transient Vector<ActionListener> actionListeners;
071: private transient EventListenerList eventListeners = new EventListenerList();
072: private transient Hashtable<JComponent, Icon> iconCache;
073:
074: public EnhancedTabbedPane() {
075:
076: this (SwingConstants.TOP, 5);
077: }
078:
079: public EnhancedTabbedPane(int tabplacement) {
080:
081: this (tabplacement, 5);
082: }
083:
084: public EnhancedTabbedPane(int TabPlacement, int MaxTabCount) {
085:
086: super (TabPlacement);
087: this .maxtabcount = MaxTabCount;
088: iconCache = new Hashtable<JComponent, Icon>(maxtabcount);
089: TabMouseInputHandler ma = new TabMouseInputHandler();
090: addMouseListener(ma);
091: addMouseMotionListener(ma);
092: setDropTarget(new TabbedPaneDropTarget(this , this , null));
093: }
094:
095: @Override
096: public void addTab(String s, Component c) {
097:
098: addTab(s, null, c, null);
099: }
100:
101: @Override
102: public void addTab(String s, Icon icon, Component c) {
103:
104: addTab(s, new CompoundIcon(CLOSE_ICON, icon), c, null);
105: }
106:
107: @Override
108: public void addTab(String s, Icon icon, Component c, String tip) {
109:
110: if (getTabCount() < maxtabcount) {
111: if (c instanceof JComponent) {
112: JComponent jc = (JComponent) c;
113: jc.putClientProperty(CLIENT_IS_LOCKED, Boolean.FALSE);
114: }
115: super .addTab(s, new CompoundIcon(null, icon), c, tip);
116: int index = indexOfComponent(c);
117: fireTabAdded(index, c);
118: } else {
119: int tab = locateFirstUnlockedTab();
120: if (tab >= 0 && tab < getTabCount()) {
121: setTitleAt(tab, s);
122: setIconAt(tab, new CompoundIcon(null, icon));
123: setComponentAt(tab, c);
124: setToolTipTextAt(tab, tip);
125: } else {
126: throw new IllegalStateException();
127: }
128: }
129: }
130:
131: @Override
132: public void setIconAt(int idx, Icon ico) {
133:
134: try {
135: CompoundIcon icon = (CompoundIcon) getIconAt(idx);
136: icon.setRightIcon(ico);
137: } catch (Throwable t) {
138: } finally {
139: invalidate();
140: repaint();
141: }
142: }
143:
144: @Override
145: public void removeTabAt(int index) {
146:
147: if (isTabLocked(index)) {
148: String msg = "LockedTab_Close_Warning";
149: String title = "Warning";
150: int res = JOptionPane.showConfirmDialog(this , msg, title,
151: JOptionPane.YES_NO_OPTION);
152: if (res == JOptionPane.NO_OPTION) {
153: return;
154: }
155: }
156: Component component = getComponent(index);
157: iconCache.remove(component);
158: super .removeTabAt(index);
159: fireTabRemoved(index, component);
160: }
161:
162: public void removeAllTabs() {
163:
164: synchronized (this ) {
165: for (int i = getTabCount() - 1; i >= 0; i--)
166: removeTabAt(i);
167: }
168: }
169:
170: public boolean isEmpty() {
171:
172: return (getTabCount() == 0);
173: }
174:
175: public void removeUnlockedTabs() {
176:
177: synchronized (this ) {
178: for (int i = getTabCount() - 1; i >= 0; i--)
179: if (!isTabLocked(i))
180: removeTabAt(i);
181: }
182: }
183:
184: public String getSelectedToolTip() {
185:
186: int tab = getSelectedIndex();
187: if (tab >= 0) {
188: return getToolTipTextAt(tab);
189: }
190: return null;
191: }
192:
193: public void setMaxTabCount(int newMax) {
194:
195: if (newMax < 1) {
196: throw new IllegalArgumentException(Integer.toString(newMax));
197: }
198: if (newMax >= maxtabcount) {
199: maxtabcount = newMax;
200: } else {
201: while (maxtabcount > newMax && getTabCount() > newMax) {
202: removeTabAt(0);
203: }
204: maxtabcount = newMax;
205: }
206: }
207:
208: public void removeSelectedTab() {
209:
210: int tab = getSelectedIndex();
211: if (tab >= 0) {
212: removeTabAt(tab);
213: }
214: }
215:
216: public void lockSelectedTab() {
217:
218: int tab = getSelectedIndex();
219: if (tab >= 0)
220: lockTab(tab);
221: }
222:
223: public void unlockSelectedTab() {
224:
225: int tab = getSelectedIndex();
226: if (tab >= 0)
227: unlockTab(tab);
228: }
229:
230: public int getIndexOfTitle(String title) {
231:
232: if (title != null) {
233: for (int i = 0; i < getTabCount(); i++) {
234: if (title.equals(getTitleAt(i))) {
235: return i;
236: }
237: }
238: }
239: return -1;
240: }
241:
242: public void setClosableTab(int index, boolean closable) {
243:
244: if (index < 0 || index >= getTabCount()) {
245: return;
246: }
247: try {
248: CompoundIcon icon = (CompoundIcon) getIconAt(index);
249: icon.setLeftIcon((closable ? CLOSE_ICON : null));
250: } catch (Throwable t) {
251: } finally {
252: invalidate();
253: repaint();
254: }
255: }
256:
257: public boolean isTabLocked(int i) {
258:
259: if (i >= 0) {
260: Component c = getComponent(i);
261: if (c instanceof JComponent) {
262: JComponent jc = (JComponent) c;
263: Boolean flag = (Boolean) jc
264: .getClientProperty(CLIENT_IS_LOCKED);
265: return flag == null ? false : flag.booleanValue();
266: }
267: return false;
268: }
269:
270: throw new IllegalArgumentException(Integer.toString(i));
271: }
272:
273: public void unlockTab(int i) {
274:
275: if (i >= 0)
276: if (isTabLocked(i)) {
277: Component c = getComponent(i);
278: if (c instanceof JComponent) {
279: JComponent jc = (JComponent) c;
280: jc.putClientProperty(CLIENT_IS_LOCKED,
281: Boolean.FALSE);
282: Icon icon = iconCache.get(c);
283: setIconAt(i, icon);
284: validateTree();
285: repaint();
286: }
287: }
288: }
289:
290: public void lockTab(int i) {
291:
292: if (i >= 0)
293: if (!isTabLocked(i)) {
294: Component c = getComponent(i);
295: if (c instanceof JComponent) {
296: JComponent jc = (JComponent) c;
297: jc
298: .putClientProperty(CLIENT_IS_LOCKED,
299: Boolean.TRUE);
300: CompoundIcon icon = (CompoundIcon) getIconAt(i);
301: if (icon != null) {
302: Icon cache = icon.getRightIcon();
303: if (cache != null) {
304: iconCache.put(jc, cache);
305: }
306: icon.setRightIcon(LOCK_ICON);
307: }
308: validateTree();
309: repaint();
310: } else {
311: String message = "Resultcontainer_Component_Error";
312: throw new IllegalComponentStateException(message);
313: }
314: }
315: }
316:
317: public void removeActionListener(ActionListener l) {
318:
319: if (actionListeners != null && actionListeners.contains(l)) {
320: Vector v = actionListeners;
321: v.removeElement(l);
322: }
323: }
324:
325: public void addActionListener(ActionListener listener) {
326:
327: synchronized (listenerList) {
328: listenerList.add(ActionListener.class, listener);
329: }
330: }
331:
332: public void addTabbedPaneListener(TabbedPaneLister listener) {
333:
334: synchronized (listenerList) {
335: listenerList.add(TabbedPaneLister.class, listener);
336: }
337: }
338:
339: protected int locateFirstUnlockedTab() {
340:
341: if (oldestIndex >= getTabCount() || oldestIndex < 0) {
342: oldestIndex = 0;
343: }
344: for (int i = oldestIndex; i < getTabCount(); i++) {
345: if (!isTabLocked(i)) {
346: oldestIndex = (i + 1);
347: return i;
348: }
349: }
350: oldestIndex = -1;
351: return -1;
352: }
353:
354: protected void fireActionPerformed(ActionEvent e) {
355:
356: ActionListener[] listeners = listenerList
357: .getListeners(ActionListener.class);
358: for (int i = 0; i < listeners.length; i++) {
359: ActionListener delegate = listeners[i];
360: synchronized (delegate) {
361: delegate.actionPerformed(e);
362: }
363: }
364: }
365:
366: protected void fireTabRemoved(int index, Component component) {
367:
368: TabbedPaneLister[] listeners = listenerList
369: .getListeners(TabbedPaneLister.class);
370: Component actualComponent = component;
371: if (component instanceof JScrollPane) {
372: JViewport viewPort = ((JScrollPane) component)
373: .getViewport();
374: actualComponent = viewPort.getView();
375: }
376: for (int i = 0; i < listeners.length; i++) {
377: TabbedPaneLister delegate = listeners[i];
378: synchronized (delegate) {
379: delegate.tabRemoved(this , index, actualComponent);
380: }
381: }
382: }
383:
384: protected void fireTabAdded(int index, Component component) {
385:
386: TabbedPaneLister[] listeners = eventListeners
387: .getListeners(TabbedPaneLister.class);
388: Component actualComponent = component;
389: if (component instanceof JScrollPane) {
390: JViewport viewPort = ((JScrollPane) component)
391: .getViewport();
392: actualComponent = viewPort.getView();
393: }
394: for (int i = 0; i < listeners.length; i++) {
395: TabbedPaneLister delegate = listeners[i];
396: synchronized (delegate) {
397: delegate.tabAdded(this , index, actualComponent);
398: }
399: }
400: }
401:
402: private class TabMouseInputHandler extends MouseInputAdapter {
403:
404: @Override
405: public void mousePressed(MouseEvent e) {
406:
407: if (getSelectedIndex() < 0) {
408: return;
409: }
410: if (e.getClickCount() == 2 && getSelectedIndex() >= 0) {
411: fireActionPerformed(new ActionEvent(
412: EnhancedTabbedPane.this ,
413: ACTION_TAB_DBL_CLICKED, getSelectedToolTip(), e
414: .getModifiers()));
415: } else if (e.getClickCount() == 1) {
416: int idx = getSelectedIndex();
417: Rectangle r = getUI().getTabBounds(
418: EnhancedTabbedPane.this , idx);
419: if (!r.contains(e.getPoint())) {
420: return;
421: }
422: CompoundIcon ico = (CompoundIcon) getIconAt(idx);
423: if (ico != null) {
424: if (ico.isOverIcon(e.getX(), e.getY())) {
425: removeSelectedTab();
426: setCursor(Cursor
427: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
428: return;
429: }
430:
431: ActionEvent evt = new ActionEvent(
432: EnhancedTabbedPane.this ,
433: ACTION_TAB_CLICKED, "", e.getModifiers());
434: fireActionPerformed(evt);
435: } else {
436: ActionEvent evt = new ActionEvent(
437: EnhancedTabbedPane.this ,
438: ACTION_TAB_CLICKED, "", e.getModifiers());
439: fireActionPerformed(evt);
440: }
441: }
442: }
443:
444: @Override
445: public void mouseMoved(MouseEvent e) {
446:
447: try {
448: if (getTabCount() >= 1) {
449: int idx = getSelectedIndex();
450: Rectangle r = getUI().getTabBounds(
451: EnhancedTabbedPane.this , idx);
452: if (!r.contains(e.getPoint())) {
453: if (getCursor().getType() != Cursor.DEFAULT_CURSOR)
454: setCursor(Cursor
455: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
456: return;
457: }
458: CompoundIcon ico = (CompoundIcon) getIconAt(idx);
459: repaint(r);
460: if (ico != null) {
461: if (ico.isOverIcon(e.getX(), e.getY())) {
462: setCursor(Cursor
463: .getPredefinedCursor(Cursor.HAND_CURSOR));
464: return;
465: }
466: }
467: }
468: } catch (Exception ignored) {
469: }
470:
471: if (getCursor().getType() != Cursor.DEFAULT_CURSOR) {
472: setCursor(Cursor
473: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
474: }
475: }
476: }
477: }
|