001: /*
002: * Copyright (C) 2004 NNL Technology AB
003: * Visit www.infonode.net for information about InfoNode(R)
004: * products and how to contact NNL Technology AB.
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
019: * MA 02111-1307, USA.
020: */
021:
022: // $Id: Tab.java,v 1.33 2005/12/04 13:46:05 jesper Exp $
023: package net.infonode.tabbedpanel;
024:
025: import net.infonode.gui.draggable.DraggableComponent;
026: import net.infonode.tabbedpanel.titledtab.TitledTab;
027: import net.infonode.util.Direction;
028:
029: import javax.swing.*;
030: import java.awt.*;
031: import java.awt.event.KeyAdapter;
032: import java.awt.event.KeyEvent;
033: import java.awt.event.KeyListener;
034: import java.util.ArrayList;
035:
036: /**
037: * <p>A Tab is a component that represents a tab in a {@link TabbedPanel}.</p>
038: *
039: * <p>A tab can hold a content component. The content component will then be shown in
040: * the content area of the TabbedPanel that the tab is a member of when the tab is
041: * selected. If the tab doesn't have a content component, then the TabbedPanel will not
042: * show any content in the content area, i.e. it will be empty.</p>
043: *
044: * <p>The tab is basically a JPanel with a BorderLayout. The layout manager can be
045: * changed using setLayout. Components and borders can be added and removed from the
046: * tab. The tab can also be subclassed to create other types of tabs, see
047: * {@link TitledTab}. <strong>In most cases {@link TitledTab} is the preferred tab type
048: * to use because TitledTab adds support for a text, icon, looks etc.</strong></p>
049: *
050: * <p>The tab component will be shown in the tab area of a TabbedPanel
051: * after the tab has become a member of that TabbedPanel by either adding or inserting
052: * it. A tab can only be a member of one TabbedPanel at the same time.</p>
053: *
054: * <p>A tab can have different states when it is a member of a TabbedPanel:
055: * <ul>
056: * <li>Normal: This means that the tab is shown (and not selected) in the TabbedPanel. The
057: * content component is not shown until the user selects the tab.
058: * <li>Highlighted: This means that for some reason the tab should be highlighted in
059: * the TabbedPanel. Highlighted could mean that the user pressed the tab with the mouse
060: * and has not yet released the mouse, i.e. it has not been selected yet.
061: * <li>Selected: This means that the tab is selected in the TabbedPanel. The TabbedPanel
062: * will then show the Tab's content component (if any). A selected tab will also be
063: * be highlighted before it is selected.
064: * <li>Enabled: This means that the tab is enabled and can be selected, highlighted
065: * dragged, moved etc.
066: * <li>Disabled: This means that the tab cannot be selected, highlighted
067: * dragged, moved etc.
068: * </ul></p>
069: *
070: * @author $Author: jesper $
071: * @version $Revision: 1.33 $
072: * @see TabListener
073: * @see TabbedPanel
074: * @see TitledTab
075: */
076: public class Tab extends JPanel {
077: private TabbedPanel tabbedPanel;
078: private JComponent contentComponent;
079: private JComponent focusableComponent;
080: private ArrayList listeners;
081: private DraggableComponent draggableComponent;
082:
083: private KeyListener focusableKeyListener = new KeyAdapter() {
084: public void keyPressed(KeyEvent e) {
085: if (tabbedPanel != null) {
086: Direction tabOrientation = tabbedPanel.getProperties()
087: .getTabAreaOrientation();
088: int incKey = tabOrientation.isHorizontal() ? KeyEvent.VK_DOWN
089: : KeyEvent.VK_RIGHT;
090: int decKey = tabOrientation.isHorizontal() ? KeyEvent.VK_UP
091: : KeyEvent.VK_LEFT;
092: int index = tabbedPanel.getTabIndex(Tab.this );
093: while (true) {
094: index = (index + tabbedPanel.getTabCount() + (e
095: .getKeyCode() == incKey ? 1 : e
096: .getKeyCode() == decKey ? -1 : 0))
097: % tabbedPanel.getTabCount();
098: Tab tab = tabbedPanel.getTabAt(index);
099: if (tab == Tab.this )
100: return;
101:
102: if (tab.getFocusableComponent() != null) {
103: /*tab.getFocusableComponent().setFocusable(true);
104: tab.getFocusableComponent().requestFocusInWindow();*/
105: tab.setSelected(true);
106: break;
107: }
108: }
109: }
110: }
111: };
112:
113: private TabListener tabbedPanelListener = new TabListener() {
114: public void tabAdded(TabEvent event) {
115: if (event.getTab() == Tab.this )
116: fireAddedEvent();
117: }
118:
119: public void tabRemoved(TabRemovedEvent event) {
120: if (event.getTab() == Tab.this ) {
121: event.getTabbedPanel().removeTabListener(this );
122: fireRemovedEvent(event);
123: }
124: }
125:
126: public void tabMoved(TabEvent event) {
127: if (event.getTab() == Tab.this )
128: fireMovedEvent();
129: }
130:
131: public void tabDragged(TabDragEvent event) {
132: if (event.getTab() == Tab.this )
133: fireDraggedEvent(event);
134: }
135:
136: public void tabDropped(TabDragEvent event) {
137: if (event.getTab() == Tab.this )
138: fireDroppedEvent(event);
139: }
140:
141: public void tabDragAborted(TabEvent event) {
142: if (event.getTab() == Tab.this )
143: fireNotDroppedEvent();
144: }
145:
146: public void tabSelected(TabStateChangedEvent event) {
147: if (event.getTab() == Tab.this ) {
148: Tab tab = event.getPreviousTab();
149: boolean hasFocus = tab != null
150: && tab.getFocusableComponent() != null
151: && tab.getFocusableComponent().hasFocus();
152:
153: if (tab != null && tab.getFocusableComponent() != null)
154: tab.getFocusableComponent().setFocusable(false);
155:
156: if (focusableComponent != null) {
157: focusableComponent.setFocusable(true);
158:
159: if (hasFocus)
160: focusableComponent.requestFocusInWindow();
161: }
162:
163: fireSelectedEvent(event);
164: }
165: }
166:
167: public void tabDeselected(TabStateChangedEvent event) {
168: if (event.getTab() == Tab.this ) {
169: fireDeselectedEvent(event);
170: }
171: }
172:
173: public void tabHighlighted(TabStateChangedEvent event) {
174: if (event.getTab() == Tab.this )
175: fireHighlightedEvent(event);
176: }
177:
178: public void tabDehighlighted(TabStateChangedEvent event) {
179: if (event.getPreviousTab() == Tab.this )
180: fireDehighlightedEvent(event);
181: }
182: };
183:
184: /**
185: * Constructs a tab without a content component and this tab as event
186: * component
187: *
188: * @see #setEventComponent
189: */
190: public Tab() {
191: this (null);
192: }
193:
194: /**
195: * Constructs a tab with a content component and this tab as event
196: * component
197: *
198: * @param contentComponent content component for this tab or null for
199: * no content component.
200: * @see #setEventComponent
201: */
202: public Tab(JComponent contentComponent) {
203: super (new BorderLayout());
204: setOpaque(false);
205: this .contentComponent = contentComponent;
206: draggableComponent = new DraggableComponent(this );
207: }
208:
209: /**
210: * Adds a TabListener
211: *
212: * @param listener the TabListener to add
213: */
214: public void addTabListener(TabListener listener) {
215: if (listeners == null)
216: listeners = new ArrayList(2);
217:
218: listeners.add(listener);
219: }
220:
221: /**
222: * Removes a TabListener
223: *
224: * @param listener the TabListener to remove
225: */
226: public void removeTabListener(TabListener listener) {
227: if (listeners != null) {
228: listeners.remove(listener);
229:
230: if (listeners.size() == 0)
231: listeners = null;
232: }
233: }
234:
235: /**
236: * Gets the content component
237: *
238: * @return the content component for this tab or null if this Tab
239: * doesn't have a content component
240: */
241: public JComponent getContentComponent() {
242: return contentComponent;
243: }
244:
245: /**
246: * Gets the TabbedPanel that this tab is a member of
247: *
248: * @return the TabbedPanel or null if this tab is not a member of
249: * any TabbedPanel
250: */
251: public TabbedPanel getTabbedPanel() {
252: return tabbedPanel;
253: }
254:
255: /**
256: * <p>Enable or disable this tab.</p>
257: *
258: * <p>If the tab is disabled, then the tab will not signal any events
259: * until it is enabled again.</p>
260: *
261: * @param enabled true for enabled, otherwise false
262: */
263: public void setEnabled(boolean enabled) {
264: getDraggableComponent().setEnabled(enabled);
265: super .setEnabled(enabled);
266: }
267:
268: /**
269: * <p>Selects this tab. A tab can only have the selected state if it is a
270: * member of a TabbedPanel.</p>
271: *
272: * <p>Setting selected to true means that this tab will be the selected
273: * tab in the TabbedPanel it is a member of. If this tab is the selected
274: * tab in the TabbedPanel then setting selected to false means there will
275: * be no selected tab in the TabbedPanel until another tab is selected.</p>
276: *
277: * @param selected True for selected, otherwise false
278: */
279: public void setSelected(boolean selected) {
280: if (selected)
281: draggableComponent.select();
282: else if (tabbedPanel != null
283: && tabbedPanel.getSelectedTab() == this )
284: tabbedPanel.setSelectedTab(null);
285: }
286:
287: /**
288: * Returns if this tab is selected in the TabbedPanel that it is a member of.
289: *
290: * @return true if selected, false if not selected or this tab is not member
291: * of a TabbedPanel
292: */
293: public boolean isSelected() {
294: return tabbedPanel != null ? tabbedPanel.getSelectedTab() == this
295: : false;
296: }
297:
298: /**
299: * Highlights this tab. This tab will be the highlighted tab in the TabbedPanel
300: * that it is member of.
301: *
302: * @param highlighted true for highlight, otherwise false
303: */
304: public void setHighlighted(boolean highlighted) {
305: if (tabbedPanel != null) {
306: if (highlighted)
307: tabbedPanel.setHighlightedTab(this );
308: else if (tabbedPanel.getHighlightedTab() == this )
309: tabbedPanel.setHighlightedTab(null);
310: }
311: }
312:
313: /**
314: * Returns if this tab is highlighted in the TabbedPanel that it is a member of.
315: *
316: * @return true if highlighted, false if not highlighted or this tab is not member
317: * of a TabbedPanel
318: */
319: public boolean isHighlighted() {
320: return tabbedPanel != null ? tabbedPanel.getHighlightedTab() == this
321: : false;
322: }
323:
324: /**
325: * <p>Sets the event component. An event component is a component in the tab that
326: * is used for internal listening to mouse events on the tab.</p>
327: *
328: * <p><strong>Note:</strong> The event component must be part of this Tab</p>
329: *
330: * @param eventComponent a component in this tab that should be used for mouse
331: * event listening
332: */
333: public void setEventComponent(JComponent eventComponent) {
334: setEventComponents(new JComponent[] { eventComponent });
335: }
336:
337: /**
338: * <p>Sets a list of event components. An event component is a component in the
339: * tab that is used for internal listening to mouse events on the tab. This
340: * method makes it possible to use several components in the tab as event
341: * components.</p>
342: *
343: * <p><strong>Note:</strong> The event components must be part of this Tab</p>
344: *
345: * @param eventComponents a list of components in this tab that should be used for
346: * mouse event listening
347: */
348: public void setEventComponents(JComponent[] eventComponents) {
349: draggableComponent.setEventComponents(eventComponents);
350: }
351:
352: /**
353: * Gets the event components for this Tab
354: *
355: * @return a list of all event components for this tab
356: */
357: public JComponent[] getEventComponents() {
358: return draggableComponent.getEventComponents();
359: }
360:
361: /**
362: * Gets the index of this tab in the TabbedPanel.
363: *
364: * @return the tab index, -1 if this tab is not a member of a TabbedPanel.
365: */
366: public int getIndex() {
367: return tabbedPanel == null ? -1 : tabbedPanel.getTabIndex(this );
368: }
369:
370: /**
371: * Gets the component in this tab that is focusable
372: *
373: * @return focusable component or null if this tab doesn't have any focusable
374: * component
375: */
376: public JComponent getFocusableComponent() {
377: return focusableComponent;
378: }
379:
380: /**
381: * <p>Sets the component in this tab that represents the focusable part of the
382: * tab.</p>
383: *
384: * <p><strong>Note:</strong> The focusable component must be part of this Tab</p>
385: *
386: * @param focusableComponent a component in this tab or null if no component
387: * should be focusable
388: */
389: public void setFocusableComponent(JComponent focusableComponent) {
390: if (this .focusableComponent != focusableComponent) {
391: boolean hasFocus = false;
392:
393: if (this .focusableComponent != null) {
394: this .focusableComponent
395: .removeKeyListener(focusableKeyListener);
396: hasFocus = this .focusableComponent.hasFocus();
397: }
398:
399: this .focusableComponent = focusableComponent;
400: if (this .focusableComponent != null) {
401: this .focusableComponent.setFocusable(isSelected());
402: this .focusableComponent
403: .addKeyListener(focusableKeyListener);
404: if (hasFocus)
405: this .focusableComponent.requestFocusInWindow();
406: }
407: }
408: }
409:
410: /**
411: * <p>Gets the tab {@link Shape}.</p>
412: *
413: * <p>
414: * This returns the shape of the tab. This can be be used by for
415: * example content borders in the tabbed panel so they can skip a gap where the
416: * tab intersects the tabbed panel content area.
417: * </p>
418: *
419: * @return the tab {@link Shape}, null if the tab has the normal component rectangle shape
420: * @since ITP 1.2.0
421: */
422: public Shape getShape() {
423: return null;
424: }
425:
426: /**
427: * Called by the tabbed panel when the tab becomes a member or is no longer a member of the
428: * tabbed panel
429: *
430: * @param tabbedPanel tabbed panel that this tab is a member of or null if this tab is no
431: * longer a member o a tabbed panel
432: */
433: protected void setTabbedPanel(TabbedPanel tabbedPanel) {
434: this .tabbedPanel = tabbedPanel;
435: if (this .tabbedPanel != null)
436: this .tabbedPanel.addTabListener(tabbedPanelListener);
437: }
438:
439: DraggableComponent getDraggableComponent() {
440: return draggableComponent;
441: }
442:
443: private void fireHighlightedEvent(TabStateChangedEvent event) {
444: if (listeners != null) {
445: TabStateChangedEvent e = new TabStateChangedEvent(this ,
446: event.getTabbedPanel(), this , event
447: .getPreviousTab(), event.getCurrentTab());
448: Object[] l = listeners.toArray();
449: for (int i = 0; i < l.length; i++)
450: ((TabListener) l[i]).tabHighlighted(e);
451: }
452: }
453:
454: private void fireDehighlightedEvent(TabStateChangedEvent event) {
455: if (listeners != null) {
456: TabStateChangedEvent e = new TabStateChangedEvent(this ,
457: event.getTabbedPanel(), this , event
458: .getPreviousTab(), event.getCurrentTab());
459: Object[] l = listeners.toArray();
460: for (int i = 0; i < l.length; i++)
461: ((TabListener) l[i]).tabDehighlighted(e);
462: }
463: }
464:
465: private void fireSelectedEvent(TabStateChangedEvent event) {
466: if (listeners != null) {
467: TabStateChangedEvent e = new TabStateChangedEvent(this ,
468: event.getTabbedPanel(), this , event
469: .getPreviousTab(), event.getCurrentTab());
470: Object[] l = listeners.toArray();
471: for (int i = 0; i < l.length; i++)
472: ((TabListener) l[i]).tabSelected(e);
473: }
474: }
475:
476: private void fireDeselectedEvent(TabStateChangedEvent event) {
477: if (listeners != null) {
478: TabStateChangedEvent e = new TabStateChangedEvent(this ,
479: event.getTabbedPanel(), this , event
480: .getPreviousTab(), event.getCurrentTab());
481: Object[] l = listeners.toArray();
482: for (int i = 0; i < l.length; i++)
483: ((TabListener) l[i]).tabDeselected(e);
484: }
485: }
486:
487: private void fireDraggedEvent(TabDragEvent event) {
488: if (listeners != null) {
489: TabDragEvent e = new TabDragEvent(this , event
490: .getMouseEvent());
491: Object[] l = listeners.toArray();
492: for (int i = 0; i < l.length; i++)
493: ((TabListener) l[i]).tabDragged(e);
494: }
495: }
496:
497: private void fireDroppedEvent(TabDragEvent event) {
498: if (listeners != null) {
499: TabDragEvent e = new TabDragEvent(this , this , event
500: .getPoint());
501: Object[] l = listeners.toArray();
502: for (int i = 0; i < l.length; i++)
503: ((TabListener) l[i]).tabDropped(e);
504: }
505: }
506:
507: private void fireNotDroppedEvent() {
508: if (listeners != null) {
509: TabEvent e = new TabEvent(this , this );
510: Object[] l = listeners.toArray();
511: for (int i = 0; i < l.length; i++)
512: ((TabListener) l[i]).tabDragAborted(e);
513: }
514: }
515:
516: private void fireMovedEvent() {
517: if (listeners != null) {
518: TabEvent e = new TabEvent(this , this );
519: Object[] l = listeners.toArray();
520: for (int i = 0; i < l.length; i++)
521: ((TabListener) l[i]).tabMoved(e);
522: }
523: }
524:
525: private void fireAddedEvent() {
526: if (listeners != null) {
527: TabEvent e = new TabEvent(this , this );
528: Object[] l = listeners.toArray();
529: for (int i = 0; i < l.length; i++)
530: ((TabListener) l[i]).tabAdded(e);
531: }
532: }
533:
534: private void fireRemovedEvent(TabRemovedEvent event) {
535: if (listeners != null) {
536: TabRemovedEvent e = new TabRemovedEvent(this , this , event
537: .getTabbedPanel());
538: Object[] l = listeners.toArray();
539: for (int i = 0; i < l.length; i++)
540: ((TabListener) l[i]).tabRemoved(e);
541: }
542: }
543:
544: public void addNotify() {
545: if (!draggableComponent.isIgnoreAddNotify())
546: super .addNotify();
547: }
548:
549: public void removeNotify() {
550: if (!draggableComponent.isIgnoreAddNotify())
551: super.removeNotify();
552: }
553: }
|