KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > swing > tabcontrol > plaf > BasicTabDisplayerUI


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.swing.tabcontrol.plaf;
21
22 import java.awt.Graphics JavaDoc;
23 import java.awt.Graphics2D JavaDoc;
24 import java.awt.GraphicsConfiguration JavaDoc;
25 import java.awt.GraphicsEnvironment JavaDoc;
26 import java.awt.Image JavaDoc;
27 import java.awt.Insets JavaDoc;
28 import java.awt.Point JavaDoc;
29 import java.awt.Polygon JavaDoc;
30 import java.awt.Rectangle JavaDoc;
31 import java.awt.Shape JavaDoc;
32 import java.awt.event.MouseEvent JavaDoc;
33 import java.awt.event.MouseListener JavaDoc;
34 import java.awt.event.MouseMotionListener JavaDoc;
35 import java.awt.event.MouseWheelEvent JavaDoc;
36 import java.awt.event.MouseWheelListener JavaDoc;
37 import java.awt.geom.Area JavaDoc;
38 import java.awt.image.BufferedImage JavaDoc;
39 import java.beans.PropertyChangeListener JavaDoc;
40 import javax.swing.JComponent JavaDoc;
41 import javax.swing.JLabel JavaDoc;
42 import javax.swing.SwingUtilities JavaDoc;
43 import javax.swing.event.ChangeEvent JavaDoc;
44 import javax.swing.event.ChangeListener JavaDoc;
45 import javax.swing.event.ListDataEvent JavaDoc;
46 import org.netbeans.swing.tabcontrol.TabData;
47 import org.netbeans.swing.tabcontrol.TabDisplayer;
48 import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
49
50 /**
51  * Base class for tab displayer UIs which use cell renderers to display tabs.
52  * This class does not contain much logic itself, but rather acts to connect events
53  * and data from various objects relating to the tab displayer, which it creates and
54  * installs. Basically, the things that are involved are:
55  * <ul>
56  * <li>A layout model ({@link TabLayoutModel}) - A data model providing the positions and sizes of tabs</li>
57  * <li>A state model ({@link TabState}) - A data model which tracks state data (selected, pressed, etc.)
58  * for each tab, and can be queried when a tab is painted to determine how that should be done.</li>
59  * <li>A selection model ({@link javax.swing.SingleSelectionModel}) - Which tracks which tab is selected</li>
60  * <li>The {@link TabDisplayer} component itself</li>
61  * <li>The {@link TabDisplayer}'s data model, which contains the list of tab names, their icons and
62  * tooltips and the user object (or {@link java.awt.Component}) they identify</li>
63  * <li>Assorted listeners on the component and data models, specifically
64  * <ul><li>A mouse listener that tells the state model when a state-affecting event
65  * has happened, such as the mouse entering a tab</li>
66  * <li>A change listener that repaints appropriately when the selection changes</li>
67  * <li>A property change listener to trigger any repaints needed due to property
68  * changes on the displayer component</li>
69  * <li>A component listener to attach and detach listeners when the component is shown/
70  * hidden, and if neccessary, notify the layout model when the component is resized</li>
71  * <li>A default {@link TabCellRenderer}, which is what will actually paint the tabs, and which
72  * is also responsible for providing some miscellaneous data such as the number of
73  * pixels the layout model should add to tab widths to make room for decorations,
74  * etc.</li>
75  * </ul>
76  * </ul>
77  * The usage pattern of this class is similar to other {@link javax.swing.plaf.ComponentUI} subclasses -
78  * {@link javax.swing.plaf.ComponentUI#installUI}
79  * is called via {@link JComponent#updateUI}. <code>installUI</code> initializes protected fields which
80  * subclasses will need, in a well defined way; abstract methods are provided for subclasses to
81  * create these objects (such as the things listed above), and convenience implementations of some
82  * are provided. <strong>Under no circumstances</strong> should subclasses modify these protected fields -
83  * due to the circuitousness of the way Swing installs UIs, they cannot be declared final, but should
84  * be treated as read-only.
85  * <p>
86  * The goal of this class is to make it quite easy to implement new appearances
87  * for tabs: To create a new appearance, implement a {@link TabCellRenderer} that can
88  * paint individual tabs as desired. This is made even easier via the
89  * {@link TabPainter} interface - simply create the painting logic needed there. Then
90  * subclass <code>BasicTabDisplayerUI</code> and include any painting logic for the background,
91  * scroll buttons, etc. needed. A good example is {@link AquaEditorTabDisplayerUI}.
92  *
93  */

94 public abstract class BasicTabDisplayerUI extends AbstractTabDisplayerUI {
95     protected TabState tabState = null;
96     private static final boolean swingpainting = Boolean.getBoolean(
97             "nb.tabs.swingpainting"); //NOI18N
98

99     protected TabCellRenderer defaultRenderer = null;
100     protected int repaintPolicy = 0;
101
102     //A couple rectangles for calculation purposes
103
private Rectangle JavaDoc scratch = new Rectangle JavaDoc();
104     private Rectangle JavaDoc scratch2 = new Rectangle JavaDoc();
105     private Rectangle JavaDoc scratch3 = new Rectangle JavaDoc();
106
107     private Point JavaDoc lastKnownMouseLocation = new Point JavaDoc();
108
109     int pixelsToAdd = 0;
110     
111     public BasicTabDisplayerUI(TabDisplayer displayer) {
112         super(displayer);
113     }
114
115     /**
116      * Overridden to initialize the <code>tabState</code> and <code>defaultRenderer</code>.
117      */

118     protected void install() {
119         super.install();
120         tabState = createTabState();
121         defaultRenderer = createDefaultRenderer();
122         layoutModel.setPadding (defaultRenderer.getPadding());
123         pixelsToAdd = defaultRenderer.getPixelsToAddToSelection();
124         repaintPolicy = createRepaintPolicy();
125         if (displayer.getSelectionModel().getSelectedIndex() != -1) {
126             tabState.setSelected(displayer.getSelectionModel().getSelectedIndex());
127             tabState.setActive(displayer.isActive());
128         }
129     }
130
131     protected void uninstall() {
132         tabState = null;
133         defaultRenderer = null;
134         super.uninstall();
135     }
136     
137     /** Used by unit tests */
138     TabState getTabState() {
139         return tabState;
140     }
141
142     /**
143      * Create a TabState instance. TabState manages the state of tabs - that is, which one
144      * contains the mouse, which one is pressed, and so forth, providing methods such as
145      * <code>setMouseInTab(int tab)</code>. Its getState() method returns a bitmask of
146      * states a tab may have which affect the way it is painted.
147      * <p>
148      * <b>Usage:</b> It is expected that UIs will subclass TabState, to implement the
149      * repaint methods, and possibly override <code>getState(int tab)</code> to mix
150      * additional state bits into the bitmask. For example, scrollable tabs have the
151      * possible states CLIP_LEFT and CLIP_RIGHT; BasicScrollingTabDisplayerUI's
152      * implementation of this determines these states by consulting its layout model, and
153      * adds them in when appropriate.
154      *
155      * @return An implementation of TabState
156      * @see BasicTabDisplayerUI.BasicTabState
157      * @see BasicScrollingTabDisplayerUI.ScrollingTabState
158      */

159     protected TabState createTabState() {
160         return new BasicTabState();
161     }
162
163     /**
164      * Create the default cell renderer for this control. If it is desirable to
165      * have more than one renderer, override getTabCellRenderer()
166      */

167     protected abstract TabCellRenderer createDefaultRenderer();
168
169     /**
170      * Return a set of insets defining the margins into which tabs should not be
171      * painted. Subclasses that want to paint some controls to the right of the
172      * tabs should include space for those controls in these insets. If a
173      * bottom margin under the tabs is to be painted, include that as well.
174      */

175     public abstract Insets JavaDoc getTabAreaInsets();
176
177     /**
178      * Get the cell renderer for a given tab. The default implementation simply
179      * returns the renderer created by createDefaultRenderer().
180      */

181     protected TabCellRenderer getTabCellRenderer(int tab) {
182         return defaultRenderer;
183     }
184
185     /**
186      * Set the passed rectangle's bounds to the recangle in which tabs will be
187      * painted; if your look and feel reserves some part of the tab area for its
188      * own painting. The rectangle is determined by what is returned by
189      * getTabAreaInsets() - this is simply a convenience method for finding the
190      * rectange into which tabs will be painted.
191      */

192     protected final void getTabsVisibleArea(Rectangle JavaDoc rect) {
193         Insets JavaDoc ins = getTabAreaInsets();
194         rect.x = ins.left;
195         rect.y = ins.top;
196         rect.width = displayer.getWidth() - ins.right - ins.left;
197         rect.height = displayer.getHeight() - ins.bottom - ins.top;
198     }
199
200     protected MouseListener JavaDoc createMouseListener() {
201         return new BasicDisplayerMouseListener();
202     }
203
204     protected PropertyChangeListener JavaDoc createPropertyChangeListener() {
205         return new BasicDisplayerPropertyChangeListener();
206     }
207
208     public Polygon JavaDoc getExactTabIndication(int index) {
209         Rectangle JavaDoc r = getTabRect(index, scratch);
210         return getTabCellRenderer(index).getTabShape(tabState.getState(index), r);
211     }
212
213     public Polygon JavaDoc getInsertTabIndication(int index) {
214         Polygon JavaDoc p;
215         if (index == getLastVisibleTab() + 1) {
216             p = (Polygon JavaDoc) getExactTabIndication (index-1);
217             Rectangle JavaDoc r = getTabRect(index-1, scratch);
218             p.translate(r.width/2, 0);
219         } else {
220             p = (Polygon JavaDoc) getExactTabIndication (index);
221             Rectangle JavaDoc r = getTabRect(index, scratch);
222             p.translate(-(r.width/2), 0);
223         }
224         return p;
225     }
226
227     public int tabForCoordinate(Point JavaDoc p) {
228         if (displayer.getModel().size() == 0) {
229             return -1;
230         }
231         getTabsVisibleArea(scratch);
232         if (!scratch.contains(p)) {
233             return -1;
234         }
235         return layoutModel.indexOfPoint(p.x, p.y);
236     }
237
238     public Rectangle JavaDoc getTabRect(int idx, Rectangle JavaDoc rect) {
239         if (rect == null) {
240             rect = new Rectangle JavaDoc();
241         }
242         if (idx < 0 || idx >= displayer.getModel().size()) {
243             rect.x = rect.y = rect.width = rect.height = 0;
244             return rect;
245         }
246         rect.x = layoutModel.getX(idx);
247         rect.y = layoutModel.getY(idx);
248         rect.width = layoutModel.getW(idx);
249         getTabsVisibleArea(scratch3);
250         //XXX for R->L component orientation cannot assume x = 0
251
int maxPos = scratch.x + scratch3.width;
252         if (rect.x > maxPos) {
253             rect.width = 0;
254         } else if (rect.x + rect.width > maxPos) {
255             rect.width = (maxPos - rect.x);
256         }
257         rect.height = layoutModel.getH(idx);
258         getTabsVisibleArea(scratch2);
259         if (rect.y + rect.height > scratch2.y + scratch2.height) {
260             rect.height = (scratch2.y + scratch2.height) - rect.y;
261         }
262         if (rect.x + rect.width > scratch2.x + scratch2.width) {
263             rect.width = (scratch2.x + scratch2.width) - rect.x;
264         }
265         return rect;
266     }
267
268     public Image JavaDoc createImageOfTab(int index) {
269         TabData td = displayer.getModel().getTab(index);
270         
271         JLabel JavaDoc lbl = new JLabel JavaDoc(td.getText());
272         int width = lbl.getFontMetrics(lbl.getFont()).stringWidth(td.getText());
273         int height = lbl.getFontMetrics(lbl.getFont()).getHeight();
274         width = width + td.getIcon().getIconWidth() + 6;
275         height = Math.max(height, td.getIcon().getIconHeight()) + 5;
276         
277         GraphicsConfiguration JavaDoc config = GraphicsEnvironment.getLocalGraphicsEnvironment()
278                                         .getDefaultScreenDevice().getDefaultConfiguration();
279         
280         BufferedImage JavaDoc image = config.createCompatibleImage(width, height);
281         Graphics2D JavaDoc g = image.createGraphics();
282         g.setColor(lbl.getForeground());
283         g.setFont(lbl.getFont());
284         td.getIcon().paintIcon(lbl, g, 0, 0);
285         g.drawString(td.getText(), 18, height / 2);
286         
287         
288         return image;
289     }
290
291     public int dropIndexOfPoint(Point JavaDoc p) {
292         Point JavaDoc p2 = toDropPoint(p);
293         int start = getFirstVisibleTab();
294         int end = getLastVisibleTab();
295         int target;
296         for (target = start; target <= end; target ++) {
297             getTabRect (target, scratch);
298             if (scratch.contains(p2)) {
299                 if (target == end) {
300                     Object JavaDoc orientation = displayer.getClientProperty (TabDisplayer.PROP_ORIENTATION);
301                     boolean flip = displayer.getType() == TabDisplayer.TYPE_SLIDING && (
302                             orientation == TabDisplayer.ORIENTATION_EAST ||
303                             orientation == TabDisplayer.ORIENTATION_WEST);
304
305                     if (flip) {
306                         if (p2.y > scratch.y + (scratch.height / 2)) {
307                             return target+1;
308                         }
309                     } else {
310                         if (p2.x > scratch.x + (scratch.width / 2)) {
311                             return target+1;
312                         }
313                     }
314                 }
315                 return target;
316             }
317         }
318         return -1;
319     }
320
321     protected boolean isAntialiased() {
322         return ColorUtil.shouldAntialias();
323     }
324
325     /**
326      * Paints the tab control. Calls paintBackground(), then paints the tabs using
327      * their cell renderers,
328      * then calls paintAfterTabs
329      */

330     public final void paint(Graphics JavaDoc g, JComponent JavaDoc c) {
331         assert c == displayer;
332         
333         ColorUtil.setupAntialiasing(g);
334         
335         boolean showClose = displayer.isShowCloseButton();
336         
337         paintBackground(g);
338         int start = getFirstVisibleTab();
339         if (start == -1 || !displayer.isShowing()) {
340             return;
341         }
342         //Possible to have a repaint called by a mouse-clicked event if close on mouse press
343
int stop = Math.min(getLastVisibleTab(), displayer.getModel().size() - 1);
344         getTabsVisibleArea(scratch);
345         
346 //System.err.println("paint, clip bounds: " + g.getClipBounds() + " first visible: " + start + " last: " + stop);
347

348         if (g.hitClip(scratch.x, scratch.y, scratch.width, scratch.height)) {
349             Shape JavaDoc s = g.getClip();
350             try {
351                 //Ensure that we will never paint an icon into the controls area
352
//by setting the clipping bounds
353
if (s != null) {
354                     //Okay, some clip area is already set. Get the intersection.
355
Area JavaDoc a = new Area JavaDoc(s);
356                     a.intersect(new Area JavaDoc(scratch.getBounds2D()));
357                     g.setClip(a);
358                 } else {
359                     //Clip was not set (it's a normal call to repaint() or something
360
//like that). Just set the bounds.
361
g.setClip(scratch.x, scratch.y, scratch.width,
362                               scratch.height);
363                 }
364
365
366                 for (int i = start; i <= stop; i++) {
367                     getTabRect(i, scratch);
368                     if (g.hitClip(scratch.x, scratch.y, scratch.width + 1,
369                                   scratch.height + 1)) {
370                                       
371                         int state = tabState.getState(i);
372                         
373                         if ((state & TabState.NOT_ONSCREEN) == 0) {
374                             TabCellRenderer ren = getTabCellRenderer(i);
375                             ren.setShowCloseButton(showClose);
376                             
377                             TabData data = displayer.getModel().getTab(i);
378                             
379                             
380                             JComponent JavaDoc renderer = ren.getRendererComponent(
381                                     data, scratch, state);
382                             
383                             renderer.setFont(displayer.getFont());
384                             //prepareRenderer ( renderer, data, ren.getLastKnownState () );
385
if (swingpainting) {
386                                 //Conceivable that some L&F may need this, but it generates
387
//lots of useless events - better to do direct painting where
388
//possible
389
SwingUtilities.paintComponent(g, renderer,
390                                                               displayer,
391                                                               scratch);
392                             } else {
393                                 try {
394                                     g.translate(scratch.x, scratch.y);
395                                     renderer.setBounds(scratch);
396                                     renderer.paint(g);
397                                 } finally {
398                                     g.translate(-scratch.x, -scratch.y);
399                                 }
400                             }
401                         }
402                     }
403                 }
404             } finally {
405                 g.setClip(s);
406             }
407         }
408         paintAfterTabs(g);
409     }
410
411     /**
412      * Fill in the background of the component prior to painting the tabs. The default
413      * implementation does nothing. If it's just a matter of filling in a background color,
414      * setOpaque (true) on the displayer, and ComponentUI.update() will take care of the rest.
415      */

416     protected void paintBackground(Graphics JavaDoc g) {
417
418     }
419
420     /**
421      * Override this method to provide painting of areas outside the tabs
422      * rectangle, such as margins and controls
423      */

424     protected void paintAfterTabs(Graphics JavaDoc g) {
425         //do nothing
426
}
427
428     /**
429      * Scrollable implementations will override this method to provide the first
430      * visible (even if clipped) tab. The default implementation returns 0 if
431      * there is at least one tab in the data model, or -1 to indicate the model
432      * is completely empty
433      */

434     protected int getFirstVisibleTab() {
435         return displayer.getModel().size() > 0 ? 0 : -1;
436     }
437
438     /**
439      * Scrollable implementations will override this method to provide the last
440      * visible (even if clipped) tab. The default implementation returns 0 if
441      * there is at least one tab in the data model, or -1 to indicate the model
442      * is completely empty
443      */

444     protected int getLastVisibleTab() {
445         return displayer.getModel().size() - 1;
446     }
447
448     protected ChangeListener JavaDoc createSelectionListener() {
449         return new BasicSelectionListener();
450     }
451
452     protected final Point JavaDoc getLastKnownMouseLocation() {
453         return lastKnownMouseLocation;
454     }
455
456     /**
457      * Convenience method to override for handling mouse wheel events. The
458      * defualt implementation does nothing.
459      */

460     protected void processMouseWheelEvent(MouseWheelEvent JavaDoc e) {
461         //do nothing
462
}
463     
464     protected final void requestAttention (int tab) {
465         tabState.addAlarmTab(tab);
466     }
467     
468     protected final void cancelRequestAttention (int tab) {
469         tabState.removeAlarmTab(tab);
470     }
471     
472
473     protected void modelChanged() {
474         tabState.clearTransientStates();
475         //DefaultTabSelectionModel automatically updates its selected index when things
476
//are added/removed from the model, so just make sure our state machine stays in
477
//sync
478
int idx = selectionModel.getSelectedIndex();
479         tabState.setSelected(idx);
480         tabState.pruneAlarmTabs(displayer.getModel().size());
481         super.modelChanged();
482     }
483
484     /**
485      * Create the policy that will determine what types of events trigger a repaint of one or more tabs.
486      * This is a bitmask composed of constants defined in TabState. The default value is
487      * <pre>
488      * TabState.REPAINT_SELECTION_ON_ACTIVATION_CHANGE
489                 | TabState.REPAINT_ON_SELECTION_CHANGE
490                 | TabState.REPAINT_ON_MOUSE_ENTER_TAB
491                 | TabState.REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON
492                 | TabState.REPAINT_ON_MOUSE_PRESSED;
493      *</pre>
494      *
495      *
496      * @return The repaint policy that should be used in conjunction with mouse events to determine when a
497      * repaint is needed.
498      */

499     protected int createRepaintPolicy () {
500         return TabState.REPAINT_SELECTION_ON_ACTIVATION_CHANGE
501                 | TabState.REPAINT_ON_SELECTION_CHANGE
502                 | TabState.REPAINT_ON_MOUSE_ENTER_TAB
503                 | TabState.REPAINT_ON_MOUSE_ENTER_CLOSE_BUTTON
504                 | TabState.REPAINT_ON_MOUSE_PRESSED;
505     }
506
507     /**
508      * @return Rectangle of the tab to be repainted
509      */

510     protected Rectangle JavaDoc getTabRectForRepaint( int tab, Rectangle JavaDoc rect ) {
511         return getTabRect( tab, rect );
512     }
513
514     protected class BasicTabState extends TabState {
515
516         public int getState(int tab) {
517             if (displayer.getModel().size() == 0) {
518                 return TabState.NOT_ONSCREEN;
519             }
520             int result = super.getState(tab);
521             if (tab == 0) {
522                 result |= TabState.LEFTMOST;
523             }
524             if (tab == displayer.getModel().size() - 1) {
525                 result |= TabState.RIGHTMOST;
526             }
527             return result;
528         }
529
530         protected void repaintAllTabs() {
531             //XXX would be nicer to just repaint the tabs area,
532
//but we also need to repaint below all the tabs in the
533
//event of activated/deactivated. No actual reason to
534
//repaint the buttons here.
535
displayer.repaint();
536         }
537
538         public int getRepaintPolicy(int tab) {
539             //Defined in createRepaintPolicy()
540
return repaintPolicy;
541         }
542
543         protected void repaintTab(int tab) {
544             if (tab == -1 || tab > displayer.getModel().size()) {
545                 return;
546             }
547             getTabRectForRepaint(tab, scratch);
548             scratch.y = 0;
549             scratch.height = displayer.getHeight();
550             displayer.repaint(scratch.x, scratch.y, scratch.width,
551                               scratch.height);
552         }
553     }
554     
555     protected ModelListener createModelListener() {
556         return new BasicModelListener();
557     }
558
559     private class BasicDisplayerPropertyChangeListener
560             extends DisplayerPropertyChangeListener {
561
562         protected void activationChanged() {
563             tabState.setActive(displayer.isActive());
564         }
565     }
566
567     protected class BasicDisplayerMouseListener implements MouseListener JavaDoc,
568             MouseMotionListener JavaDoc, MouseWheelListener JavaDoc {
569         private int updateMouseLocation(MouseEvent JavaDoc e) {
570             lastKnownMouseLocation.x = e.getX();
571             lastKnownMouseLocation.y = e.getY();
572             return tabForCoordinate(lastKnownMouseLocation);
573         }
574
575         public void mouseClicked(MouseEvent JavaDoc e) {
576             int idx = updateMouseLocation(e);
577             if (idx == -1) {
578                 return;
579             }
580
581             TabCellRenderer tcr = getTabCellRenderer(idx);
582             getTabRect(idx, scratch);
583             int state = tabState.getState(idx);
584
585             potentialCommand (idx, e, state, tcr, scratch);
586         }
587
588         public void mouseDragged(MouseEvent JavaDoc e) {
589             mouseMoved (e);
590         }
591
592         public void mouseEntered(MouseEvent JavaDoc e) {
593             int idx = updateMouseLocation(e);
594             tabState.setMouseInTabsArea(true);
595             tabState.setContainsMouse(idx);
596         }
597
598         public void mouseExited(MouseEvent JavaDoc e) {
599             updateMouseLocation(e);
600             tabState.setMouseInTabsArea(false);
601             tabState.setContainsMouse(-1);
602             tabState.setCloseButtonContainsMouse(-1);
603         }
604
605         public void mouseMoved(MouseEvent JavaDoc e) {
606             int idx = updateMouseLocation(e);
607             tabState.setMouseInTabsArea(true);
608             tabState.setContainsMouse(idx);
609             if (idx != -1) {
610                 TabCellRenderer tcr = getTabCellRenderer(idx);
611                 getTabRect(idx, scratch);
612                 int state = tabState.getState(idx);
613
614                 String JavaDoc s = tcr.getCommandAtPoint(e.getPoint(), state, scratch);
615                 if (TabDisplayer.COMMAND_CLOSE == s) {
616                     tabState.setCloseButtonContainsMouse(idx);
617                 } else {
618                     tabState.setCloseButtonContainsMouse(-1);
619                 }
620             } else {
621                 tabState.setContainsMouse(-1);
622             }
623         }
624
625         private int lastPressedTab = -1;
626         private long pressTime = -1;
627         public void mousePressed(MouseEvent JavaDoc e) {
628             int idx = updateMouseLocation(e);
629             tabState.setPressed(idx);
630
631             //One a double click, preserve the tab that was initially clicked, in case
632
//a re-layout happened. We'll pass that to the action.
633
long time = e.getWhen();
634             if (time - pressTime > 200) {
635                 lastPressedTab = idx;
636             }
637             pressTime = time;
638             lastPressedTab = idx;
639             if (idx != -1) {
640                 TabCellRenderer tcr = getTabCellRenderer(idx);
641                 getTabRect(idx, scratch);
642                 int state = tabState.getState(idx);
643
644                 //First find the command for the location with the default button -
645
//TabState may trigger a repaint
646
String JavaDoc command = tcr.getCommandAtPoint (e.getPoint(), state, scratch);
647                 if (TabDisplayer.COMMAND_CLOSE == command) {
648                     tabState.setCloseButtonContainsMouse(idx);
649                     tabState.setMousePressedInCloseButton(idx);
650
651                     //We're closing, don't try to maximize this tab if it turns out to be
652
//a double click
653
pressTime = -1;
654                     lastPressedTab = -1;
655                 }
656
657                 potentialCommand (idx, e, state, tcr, scratch);
658             } else {
659                 tabState.setMousePressedInCloseButton(-1); //just in case
660
}
661         }
662
663         private void potentialCommand (int idx, MouseEvent JavaDoc e, int state, TabCellRenderer tcr, Rectangle JavaDoc bounds) {
664             String JavaDoc command = tcr.getCommandAtPoint (e.getPoint(), state, bounds,
665                     e.getButton(), e.getID(), e.getModifiersEx());
666             if (command == null || TabDisplayer.COMMAND_SELECT == command) {
667                 if (e.isPopupTrigger()) {
668                     displayer.repaint();
669                     performCommand (TabDisplayer.COMMAND_POPUP_REQUEST, idx, e);
670                     return;
671                 } else if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() >= 2) {
672                     performCommand (TabDisplayer.COMMAND_MAXIMIZE, idx, e);
673                     return;
674                 }
675             }
676
677             if (command != null) {
678                 performCommand (command, lastPressedTab == -1 || lastPressedTab >
679                     displayer.getModel().size() ? idx : lastPressedTab, e);
680             }
681         }
682
683         private void performCommand (String JavaDoc command, int idx, MouseEvent JavaDoc evt) {
684             evt.consume();
685             if (TabDisplayer.COMMAND_SELECT == command) {
686                 if (idx != displayer.getSelectionModel().getSelectedIndex()) {
687                     boolean go = shouldPerformAction (command, idx, evt);
688                     if (go) {
689                         selectionModel.setSelectedIndex (idx);
690                     }
691                 }
692             } else {
693                 boolean should = shouldPerformAction (command, idx, evt) && displayer.isShowCloseButton();
694                 if (should) {
695                     if (TabDisplayer.COMMAND_CLOSE == command) {
696                         displayer.getModel().removeTab(idx);
697                     } else if (TabDisplayer.COMMAND_CLOSE_ALL == command) {
698                         displayer.getModel().removeTabs (0, displayer.getModel().size());
699                     } else if (TabDisplayer.COMMAND_CLOSE_ALL_BUT_THIS == command) {
700                         int start;
701                         int end;
702                         if (idx != displayer.getModel().size()-1) {
703                             start = idx+1;
704                             end = displayer.getModel().size();
705                             displayer.getModel().removeTabs(start, end);
706                         }
707                         if (idx != 0) {
708                             start = 0;
709                             end = idx;
710                             displayer.getModel().removeTabs(start, end);
711                         }
712                     }
713                 }
714             }
715         }
716
717         public void mouseReleased(MouseEvent JavaDoc e) {
718             int idx = updateMouseLocation(e);
719             if (idx != -1) {
720                 TabCellRenderer tcr = getTabCellRenderer(idx);
721                 getTabRect(idx, scratch);
722                 int state = tabState.getState(idx);
723                 if ((state & TabState.PRESSED) != 0 && ((state & TabState.CLIP_LEFT) != 0) || (state & TabState.CLIP_RIGHT) != 0) {
724                     makeTabVisible(idx);
725                 }
726                 potentialCommand (idx, e, state, tcr, scratch);
727             }
728             tabState.setMouseInTabsArea(idx != -1);
729             tabState.setPressed(-1);
730             tabState.setMousePressedInCloseButton(-1);
731         }
732
733         public final void mouseWheelMoved(MouseWheelEvent JavaDoc e) {
734             updateMouseLocation(e);
735             processMouseWheelEvent(e);
736         }
737     }
738
739     /** A simple selection listener implementation which updates the TabState model
740      * with the new selected index from the selection model when it changes.
741      */

742     protected class BasicSelectionListener implements ChangeListener JavaDoc {
743         public void stateChanged(ChangeEvent JavaDoc e) {
744             assert e.getSource() == selectionModel : "Unknown event source: "
745                     + e.getSource();
746             int idx = selectionModel.getSelectedIndex();
747             tabState.setSelected(idx >= 0 ? idx : -1);
748             if (idx >= 0) {
749                 makeTabVisible (selectionModel.getSelectedIndex());
750             }
751         }
752     }
753     
754     /**
755      * Listener on data model which will pass modified indices to the
756      * TabState object, so it can update which tab indices are flashing in
757      * "attention" mode, if any.
758      */

759     protected class BasicModelListener extends AbstractTabDisplayerUI.ModelListener {
760         public void contentsChanged(ListDataEvent JavaDoc e) {
761             super.contentsChanged(e);
762             tabState.contentsChanged(e);
763         }
764
765         public void indicesAdded(ComplexListDataEvent e) {
766             super.indicesAdded(e);
767             tabState.indicesAdded(e);
768         }
769
770         public void indicesChanged(ComplexListDataEvent e) {
771             tabState.indicesChanged(e);
772         }
773
774         public void indicesRemoved(ComplexListDataEvent e) {
775             tabState.indicesRemoved(e);
776         }
777
778         public void intervalAdded(ListDataEvent JavaDoc e) {
779             tabState.intervalAdded(e);
780         }
781
782         public void intervalRemoved(ListDataEvent JavaDoc e) {
783             tabState.intervalRemoved(e);
784         }
785     }
786 }
787
Popular Tags