source: src/main/java/genius/gui/panels/tab/CloseTabPaneUI.java@ 135

Last change on this file since 135 was 1, checked in by Wouter Pasman, 6 years ago

Initial import : Genius 9.0.0

File size: 43.8 KB
Line 
1/*
2 * To change this template, choose Tools | Templates
3 * and open the template in the editor.
4 */
5
6package genius.gui.panels.tab;
7
8/*
9 * David Bismut, davidou@mageos.com
10 * Intern, SETLabs, Infosys Technologies Ltd. May 2004 - Jul 2004
11 * Ecole des Mines de Nantes, France
12 */
13
14/*
15 *
16 * Extended from
17 * @(#)BasicTabbedPaneUI.java 1.126 03/01/23
18 *
19 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
20 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
21 */
22
23import java.awt.Color;
24import java.awt.Component;
25import java.awt.Container;
26import java.awt.Dimension;
27import java.awt.Event;
28import java.awt.Font;
29import java.awt.FontMetrics;
30import java.awt.Graphics;
31import java.awt.Graphics2D;
32import java.awt.Insets;
33import java.awt.LayoutManager;
34import java.awt.Point;
35import java.awt.Rectangle;
36import java.awt.Shape;
37import java.awt.event.ActionEvent;
38import java.awt.event.ActionListener;
39import java.awt.event.ContainerEvent;
40import java.awt.event.ContainerListener;
41import java.awt.event.MouseEvent;
42import java.awt.event.MouseListener;
43import java.awt.event.MouseMotionListener;
44import java.awt.image.BufferedImage;
45import java.io.IOException;
46import java.util.Hashtable;
47import java.util.Vector;
48
49import javax.imageio.ImageIO;
50import javax.swing.AbstractAction;
51import javax.swing.ActionMap;
52import javax.swing.Icon;
53import javax.swing.InputMap;
54import javax.swing.JButton;
55import javax.swing.JComponent;
56import javax.swing.JMenuItem;
57import javax.swing.JPanel;
58import javax.swing.JPopupMenu;
59import javax.swing.JTabbedPane;
60import javax.swing.JViewport;
61import javax.swing.KeyStroke;
62import javax.swing.SwingConstants;
63import javax.swing.SwingUtilities;
64import javax.swing.UIManager;
65import javax.swing.border.Border;
66import javax.swing.border.SoftBevelBorder;
67import javax.swing.event.ChangeEvent;
68import javax.swing.event.ChangeListener;
69import javax.swing.plaf.ActionMapUIResource;
70import javax.swing.plaf.ComponentUI;
71import javax.swing.plaf.InputMapUIResource;
72import javax.swing.plaf.UIResource;
73import javax.swing.plaf.basic.BasicArrowButton;
74import javax.swing.plaf.basic.BasicHTML;
75import javax.swing.plaf.basic.BasicTabbedPaneUI;
76import javax.swing.text.View;
77
78/**
79 * UI for <code>CloseAndMaxTabbedPane</code>.
80 * <p>
81 * Credits to:
82 *
83 * @author Amy Fowler
84 * @author Philip Milne
85 * @author Steve Wilson
86 * @author Tom Santos
87 * @author Dave Moore
88 */
89public class CloseTabPaneUI extends BasicTabbedPaneUI {
90
91 // Instance variables initialized at installation
92
93 private ContainerListener containerListener;
94
95 private Vector htmlViews;
96
97 private Hashtable mnemonicToIndexMap;
98
99 /**
100 * InputMap used for mnemonics. Only non-null if the JTabbedPane has
101 * mnemonics associated with it. Lazily created in initMnemonics.
102 */
103 private InputMap mnemonicInputMap;
104
105 // For use when tabLayoutPolicy = SCROLL_TAB_LAYOUT
106 protected ScrollableTabSupport tabScroller;
107
108 private int tabCount;
109
110 protected MyMouseMotionListener motionListener;
111
112 // UI creation
113
114 private static final int INACTIVE = 0;
115
116 private static final int OVER = 1;
117
118 private static final int PRESSED = 2;
119
120 protected static final int BUTTONSIZE = 16;
121
122 protected static final int WIDTHDELTA = 5;
123
124 private static final Border PRESSEDBORDER = new SoftBevelBorder(
125 SoftBevelBorder.LOWERED);
126
127 private static final Border OVERBORDER = new SoftBevelBorder(
128 SoftBevelBorder.RAISED);
129
130 private BufferedImage closeImgB;
131
132 // private BufferedImage maxImgB;
133
134 private BufferedImage closeImgI;
135
136 // private BufferedImage maxImgI;
137
138 private JButton closeB;
139
140 // private JButton maxB;
141
142 private int overTabIndex = -1;
143
144 private int closeIndexStatus = INACTIVE;
145
146 private int maxIndexStatus = INACTIVE;
147
148 private boolean mousePressed = false;
149
150 private boolean isCloseButtonEnabled = true;
151
152 // private boolean isMaxButtonEnabled = true;
153
154 protected JPopupMenu actionPopupMenu;
155
156 // protected JMenuItem maxItem;
157
158 protected JMenuItem closeItem;
159
160 public CloseTabPaneUI() {
161
162 super();
163
164 try {
165 closeImgI = ImageIO.read(getClass().getResource("delete_edit.gif"));
166 } catch (IOException e1) {
167 e1.printStackTrace();
168 }
169
170 closeImgB = new BufferedImage(BUTTONSIZE, BUTTONSIZE,
171 BufferedImage.TYPE_INT_ARGB);
172
173 closeB = new JButton();
174 closeB.setSize(BUTTONSIZE, BUTTONSIZE);
175
176 // WindowsIconFactory.createFrameCloseIcon().paintIcon(closeB,
177 // closeImgI.createGraphics(), 0, 0);
178 drawButton(closeImgB, BUTTONSIZE, BUTTONSIZE);
179
180 actionPopupMenu = new JPopupMenu();
181
182 closeItem = new JMenuItem("Close");
183
184 closeItem.addActionListener(new ActionListener() {
185 public void actionPerformed(ActionEvent e) {
186 ((CloseTabbedPane) tabPane).fireCloseTabEvent(null,
187 tabPane.getSelectedIndex());
188
189 }
190 });
191
192 setPopupMenu();
193 }
194
195 protected void drawButton(BufferedImage img, int w, int h) {
196 Graphics g = img.getGraphics();
197 g.setColor(Color.BLACK);
198 int fudge = 2;
199 int xw = w - fudge * 2;
200 int yh = h = fudge * 2;
201 g.drawLine(fudge, fudge, xw, yh);
202 g.drawLine(xw, fudge, fudge, yh);
203 g.dispose();
204 }
205
206 protected boolean isOneActionButtonEnabled() {
207 return isCloseButtonEnabled;
208 }
209
210 public boolean isCloseEnabled() {
211 return isCloseButtonEnabled;
212 }
213
214 public void setCloseIcon(boolean b) {
215 isCloseButtonEnabled = b;
216 setPopupMenu();
217 }
218
219 private void setPopupMenu() {
220 actionPopupMenu.removeAll();
221 if (isCloseButtonEnabled)
222 actionPopupMenu.add(closeItem);
223 }
224
225 protected int calculateTabWidth(int tabPlacement, int tabIndex,
226 FontMetrics metrics) {
227 int delta = 2;
228 if (!isOneActionButtonEnabled())
229 delta += 6;
230 else {
231 if (isCloseButtonEnabled)
232 delta += BUTTONSIZE + WIDTHDELTA;
233 }
234
235 return super.calculateTabWidth(tabPlacement, tabIndex, metrics) + delta;
236 }
237
238 protected int calculateTabHeight(int tabPlacement, int tabIndex,
239 int fontHeight) {
240
241 return super.calculateTabHeight(tabPlacement, tabIndex, fontHeight) + 5;
242 }
243
244 protected void layoutLabel(int tabPlacement, FontMetrics metrics,
245 int tabIndex, String title, Icon icon, Rectangle tabRect,
246 Rectangle iconRect, Rectangle textRect, boolean isSelected) {
247 textRect.x = textRect.y = iconRect.x = iconRect.y = 0;
248
249 View v = getTextViewForTab(tabIndex);
250 if (v != null) {
251 tabPane.putClientProperty("html", v);
252 }
253
254 SwingUtilities.layoutCompoundLabel((JComponent) tabPane, metrics,
255 title, icon, SwingUtilities.CENTER, SwingUtilities.LEFT,
256 SwingUtilities.CENTER, SwingUtilities.CENTER, tabRect,
257 iconRect, textRect, textIconGap);
258
259 tabPane.putClientProperty("html", null);
260
261 iconRect.x = tabRect.x + 8;
262 textRect.x = iconRect.x + iconRect.width + textIconGap;
263 }
264
265 protected MouseListener createMouseListener() {
266 return new MyMouseHandler();
267 }
268
269 protected ScrollableTabButton createScrollableTabButton(int direction) {
270 return new ScrollableTabButton(direction);
271 }
272
273 protected Rectangle newCloseRect(Rectangle rect) {
274 int dx = rect.x + rect.width;
275 int dy = (rect.y + rect.height) / 2 - 6;
276 return new Rectangle(dx - BUTTONSIZE - WIDTHDELTA, dy, BUTTONSIZE,
277 BUTTONSIZE);
278 }
279
280 protected void updateOverTab(int x, int y) {
281 if (overTabIndex != (overTabIndex = getTabAtLocation(x, y)))
282 tabScroller.tabPanel.repaint();
283
284 }
285
286 protected void updateCloseIcon(int x, int y) {
287
288 if (overTabIndex != -1) {
289 int newCloseIndexStatus = INACTIVE;
290
291 Rectangle closeRect = newCloseRect(rects[overTabIndex]);
292 if (closeRect.contains(x, y))
293 newCloseIndexStatus = mousePressed ? PRESSED : OVER;
294
295 if (closeIndexStatus != (closeIndexStatus = newCloseIndexStatus))
296 tabScroller.tabPanel.repaint();
297 }
298 }
299
300 private void setTabIcons(int x, int y) {
301 // if the mouse isPressed
302 if (!mousePressed) {
303 updateOverTab(x, y);
304 }
305
306 if (isCloseButtonEnabled)
307 updateCloseIcon(x, y);
308 }
309
310 public static ComponentUI createUI(JComponent c) {
311 return new CloseTabPaneUI();
312 }
313
314 /**
315 * Invoked by <code>installUI</code> to createFrom a layout manager object
316 * to manage the <code>JTabbedPane</code>.
317 *
318 * see {@link JTabbedPane#getTabLayoutPolicy()}
319 *
320 * @return a layout manager object
321 *
322 */
323 protected LayoutManager createLayoutManager() {
324
325 return new TabbedPaneScrollLayout();
326
327 }
328
329 /*
330 * In an attempt to preserve backward compatibility for programs which have
331 * extended BasicTabbedPaneUI to do their own layout, the UI uses the
332 * installed layoutManager (and not tabLayoutPolicy) to determine if
333 * scrollTabLayout is enabled.
334 */
335
336 /**
337 * Creates and installs any required subcomponents for the JTabbedPane.
338 * Invoked by installUI.
339 *
340 * @since 1.4
341 */
342 protected void installComponents() {
343
344 if (tabScroller == null) {
345 tabScroller = new ScrollableTabSupport(tabPane.getTabPlacement());
346 tabPane.add(tabScroller.viewport);
347 tabPane.add(tabScroller.scrollForwardButton);
348 tabPane.add(tabScroller.scrollBackwardButton);
349 }
350
351 }
352
353 /**
354 * Removes any installed subcomponents from the JTabbedPane. Invoked by
355 * uninstallUI.
356 *
357 * @since 1.4
358 */
359 protected void uninstallComponents() {
360
361 tabPane.remove(tabScroller.viewport);
362 tabPane.remove(tabScroller.scrollForwardButton);
363 tabPane.remove(tabScroller.scrollBackwardButton);
364 tabScroller = null;
365
366 }
367
368 protected void installListeners() {
369 if ((propertyChangeListener = createPropertyChangeListener()) != null) {
370 tabPane.addPropertyChangeListener(propertyChangeListener);
371 }
372 if ((tabChangeListener = createChangeListener()) != null) {
373 tabPane.addChangeListener(tabChangeListener);
374 }
375 if ((mouseListener = createMouseListener()) != null) {
376 tabScroller.tabPanel.addMouseListener(mouseListener);
377 }
378
379 if ((focusListener = createFocusListener()) != null) {
380 tabPane.addFocusListener(focusListener);
381 }
382
383 // PENDING(api) : See comment for ContainerHandler
384 if ((containerListener = new ContainerHandler()) != null) {
385 tabPane.addContainerListener(containerListener);
386 if (tabPane.getTabCount() > 0) {
387 htmlViews = createHTMLVector();
388 }
389 }
390
391 if ((motionListener = new MyMouseMotionListener()) != null) {
392 tabScroller.tabPanel.addMouseMotionListener(motionListener);
393 }
394
395 }
396
397 protected void uninstallListeners() {
398 if (mouseListener != null) {
399 tabScroller.tabPanel.removeMouseListener(mouseListener);
400 mouseListener = null;
401 }
402
403 if (motionListener != null) {
404 tabScroller.tabPanel.removeMouseMotionListener(motionListener);
405 motionListener = null;
406 }
407
408 if (focusListener != null) {
409 tabPane.removeFocusListener(focusListener);
410 focusListener = null;
411 }
412
413 // PENDING(api): See comment for ContainerHandler
414 if (containerListener != null) {
415 tabPane.removeContainerListener(containerListener);
416 containerListener = null;
417 if (htmlViews != null) {
418 htmlViews.removeAllElements();
419 htmlViews = null;
420 }
421 }
422 if (tabChangeListener != null) {
423 tabPane.removeChangeListener(tabChangeListener);
424 tabChangeListener = null;
425 }
426 if (propertyChangeListener != null) {
427 tabPane.removePropertyChangeListener(propertyChangeListener);
428 propertyChangeListener = null;
429 }
430
431 }
432
433 protected ChangeListener createChangeListener() {
434 return new TabSelectionHandler();
435 }
436
437 protected void installKeyboardActions() {
438 InputMap km = getMyInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
439
440 SwingUtilities.replaceUIInputMap(tabPane,
441 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km);
442 km = getMyInputMap(JComponent.WHEN_FOCUSED);
443 SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, km);
444
445 ActionMap am = createMyActionMap();
446
447 SwingUtilities.replaceUIActionMap(tabPane, am);
448
449 tabScroller.scrollForwardButton.setAction(am
450 .get("scrollTabsForwardAction"));
451 tabScroller.scrollBackwardButton.setAction(am
452 .get("scrollTabsBackwardAction"));
453
454 }
455
456 InputMap getMyInputMap(int condition) {
457 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
458 return (InputMap) UIManager.get("TabbedPane.ancestorInputMap");
459 } else if (condition == JComponent.WHEN_FOCUSED) {
460 return (InputMap) UIManager.get("TabbedPane.focusInputMap");
461 }
462 return null;
463 }
464
465 ActionMap createMyActionMap() {
466 ActionMap map = new ActionMapUIResource();
467 map.put("navigateNext", new NextAction());
468 map.put("navigatePrevious", new PreviousAction());
469 map.put("navigateRight", new RightAction());
470 map.put("navigateLeft", new LeftAction());
471 map.put("navigateUp", new UpAction());
472 map.put("navigateDown", new DownAction());
473 map.put("navigatePageUp", new PageUpAction());
474 map.put("navigatePageDown", new PageDownAction());
475 map.put("requestFocus", new RequestFocusAction());
476 map.put("requestFocusForVisibleComponent",
477 new RequestFocusForVisibleAction());
478 map.put("setSelectedIndex", new SetSelectedIndexAction());
479 map.put("scrollTabsForwardAction", new ScrollTabsForwardAction());
480 map.put("scrollTabsBackwardAction", new ScrollTabsBackwardAction());
481 return map;
482 }
483
484 protected void uninstallKeyboardActions() {
485 SwingUtilities.replaceUIActionMap(tabPane, null);
486 SwingUtilities.replaceUIInputMap(tabPane,
487 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
488 SwingUtilities
489 .replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null);
490 }
491
492 /**
493 * Reloads the mnemonics. This should be invoked when a memonic changes,
494 * when the title of a mnemonic changes, or when tabs are added/removed.
495 */
496 private void updateMnemonics() {
497 resetMnemonics();
498 for (int counter = tabPane.getTabCount() - 1; counter >= 0; counter--) {
499 int mnemonic = tabPane.getMnemonicAt(counter);
500
501 if (mnemonic > 0) {
502 addMnemonic(counter, mnemonic);
503 }
504 }
505 }
506
507 /**
508 * Resets the mnemonics bindings to an empty state.
509 */
510 private void resetMnemonics() {
511 if (mnemonicToIndexMap != null) {
512 mnemonicToIndexMap.clear();
513 mnemonicInputMap.clear();
514 }
515 }
516
517 /**
518 * Adds the specified mnemonic at the specified index.
519 */
520 private void addMnemonic(int index, int mnemonic) {
521 if (mnemonicToIndexMap == null) {
522 initMnemonics();
523 }
524 mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, Event.ALT_MASK),
525 "setSelectedIndex");
526 mnemonicToIndexMap.put(new Integer(mnemonic), new Integer(index));
527 }
528
529 /**
530 * Installs the state needed for mnemonics.
531 */
532 private void initMnemonics() {
533 mnemonicToIndexMap = new Hashtable();
534 mnemonicInputMap = new InputMapUIResource();
535 mnemonicInputMap.setParent(SwingUtilities.getUIInputMap(tabPane,
536 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
537 SwingUtilities
538 .replaceUIInputMap(tabPane,
539 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
540 mnemonicInputMap);
541 }
542
543 // UI Rendering
544
545 public void paint(Graphics g, JComponent c) {
546 int tc = tabPane.getTabCount();
547
548 if (tabCount != tc) {
549 tabCount = tc;
550 updateMnemonics();
551 }
552
553 int selectedIndex = tabPane.getSelectedIndex();
554 int tabPlacement = tabPane.getTabPlacement();
555
556 ensureCurrentLayout();
557
558 // Paint content border
559 paintContentBorder(g, tabPlacement, selectedIndex);
560
561 }
562
563 protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
564 int tabIndex, Rectangle iconRect, Rectangle textRect) {
565 Rectangle tabRect = rects[tabIndex];
566 int selectedIndex = tabPane.getSelectedIndex();
567 boolean isSelected = selectedIndex == tabIndex;
568 boolean isOver = overTabIndex == tabIndex;
569 Graphics2D g2 = null;
570 Shape save = null;
571 boolean cropShape = false;
572 int cropx = 0;
573 int cropy = 0;
574
575 if (g instanceof Graphics2D) {
576 g2 = (Graphics2D) g;
577
578 // Render visual for cropped tab edge...
579 Rectangle viewRect = tabScroller.viewport.getViewRect();
580 int cropline;
581
582 cropline = viewRect.x + viewRect.width;
583 if ((tabRect.x < cropline)
584 && (tabRect.x + tabRect.width > cropline)) {
585
586 cropx = cropline - 1;
587 cropy = tabRect.y;
588 cropShape = true;
589 }
590
591 if (cropShape) {
592 save = g2.getClip();
593 g2.clipRect(tabRect.x, tabRect.y, tabRect.width, tabRect.height);
594
595 }
596 }
597
598 paintTabBackground(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
599 tabRect.width, tabRect.height, isSelected);
600
601 paintTabBorder(g, tabPlacement, tabIndex, tabRect.x, tabRect.y,
602 tabRect.width, tabRect.height, isSelected);
603
604 String title = tabPane.getTitleAt(tabIndex);
605 Font font = tabPane.getFont();
606 FontMetrics metrics = g.getFontMetrics(font);
607 Icon icon = getIconForTab(tabIndex);
608
609 layoutLabel(tabPlacement, metrics, tabIndex, title, icon, tabRect,
610 iconRect, textRect, isSelected);
611
612 paintText(g, tabPlacement, font, metrics, tabIndex, title, textRect,
613 isSelected);
614
615 paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
616
617 paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect,
618 textRect, isSelected);
619
620 if (cropShape) {
621 paintCroppedTabEdge(g, tabPlacement, tabIndex, isSelected, cropx,
622 cropy);
623 g2.setClip(save);
624
625 } else if (isOver || isSelected) {
626
627 int dx = tabRect.x + tabRect.width - BUTTONSIZE - WIDTHDELTA;
628 int dy = (tabRect.y + tabRect.height) / 2 - 6;
629
630 if (isCloseButtonEnabled)
631 paintCloseIcon(g2, dx, dy, isOver);
632 }
633
634 }
635
636 protected void paintCloseIcon(Graphics g, int dx, int dy, boolean isOver) {
637 paintActionButton(g, dx, dy, closeIndexStatus, isOver, closeB,
638 closeImgB);
639 g.drawImage(closeImgI, dx, dy + 1, null);
640 }
641
642 protected void paintActionButton(Graphics g, int dx, int dy, int status,
643 boolean isOver, JButton button, BufferedImage image) {
644
645 button.setBorder(null);
646
647 if (isOver) {
648 switch (status) {
649 case OVER:
650 button.setBorder(OVERBORDER);
651 break;
652 case PRESSED:
653 button.setBorder(PRESSEDBORDER);
654 break;
655 }
656 }
657
658 button.setBackground(tabScroller.tabPanel.getBackground());
659 button.paint(image.getGraphics());
660 g.drawImage(image, dx, dy, null);
661 }
662
663 /*
664 * This method will createFrom and return a polygon shape for the given tab
665 * rectangle which has been cropped at the specified cropline with a torn
666 * edge visual. e.g. A "File" tab which has cropped been cropped just after
667 * the "i": ------------- | ..... | | . | | ... . | | . . | | . . | | . . |
668 * --------------
669 *
670 * The x, y arrays below define the pattern used to createFrom a "torn" edge
671 * segment which is repeated to fill the edge of the tab. For tabs placed on
672 * TOP and BOTTOM, this righthand torn edge is created by line segments
673 * which are defined by coordinates obtained by subtracting xCropLen[i] from
674 * (tab.x + tab.width) and adding yCroplen[i] to (tab.y). For tabs placed on
675 * LEFT or RIGHT, the bottom torn edge is created by subtracting xCropLen[i]
676 * from (tab.y + tab.height) and adding yCropLen[i] to (tab.x).
677 */
678
679 private void paintCroppedTabEdge(Graphics g, int tabPlacement,
680 int tabIndex, boolean isSelected, int x, int y) {
681
682 g.setColor(shadow);
683 g.drawLine(x, y, x, y + rects[tabIndex].height);
684
685 }
686
687 private void ensureCurrentLayout() {
688 if (!tabPane.isValid()) {
689 tabPane.validate();
690 }
691 /*
692 * If tabPane doesn't have a peer yet, the validate() call will silently
693 * fail. We handle that by forcing a layout if tabPane is still invalid.
694 * See bug 4237677.
695 */
696 if (!tabPane.isValid()) {
697 TabbedPaneLayout layout = (TabbedPaneLayout) tabPane.getLayout();
698 layout.calculateLayoutInfo();
699 }
700 }
701
702 /**
703 * Returns the bounds of the specified tab in the coordinate space of the
704 * JTabbedPane component. This is required because the tab rects are by
705 * default defined in the coordinate space of the component where they are
706 * rendered, which could be the JTabbedPane (for WRAP_TAB_LAYOUT) or a
707 * ScrollableTabPanel (SCROLL_TAB_LAYOUT). This method should be used
708 * whenever the tab rectangle must be relative to the JTabbedPane itself and
709 * the result should be placed in a designated Rectangle object (rather than
710 * instantiating and returning a new Rectangle each time). The tab index
711 * parameter must be a valid tabbed pane tab index (0 to tab count - 1,
712 * inclusive). The destination rectangle parameter must be a valid
713 * <code>Rectangle</code> instance. The handling of invalid parameters is
714 * unspecified.
715 *
716 * @param tabIndex
717 * the index of the tab
718 * @param dest
719 * the rectangle where the result should be placed
720 * @return the resulting rectangle
721 *
722 * @since 1.4
723 */
724
725 protected Rectangle getTabBounds(int tabIndex, Rectangle dest) {
726 dest.width = rects[tabIndex].width;
727 dest.height = rects[tabIndex].height;
728
729 Point vpp = tabScroller.viewport.getLocation();
730 Point viewp = tabScroller.viewport.getViewPosition();
731 dest.x = rects[tabIndex].x + vpp.x - viewp.x;
732 dest.y = rects[tabIndex].y + vpp.y - viewp.y;
733
734 return dest;
735 }
736
737 private int getTabAtLocation(int x, int y) {
738 ensureCurrentLayout();
739
740 int tabCount = tabPane.getTabCount();
741 for (int i = 0; i < tabCount; i++) {
742 if (rects[i].contains(x, y)) {
743 return i;
744 }
745 }
746 return -1;
747 }
748
749 public int getOverTabIndex() {
750 return overTabIndex;
751 }
752
753 /**
754 * Returns the index of the tab closest to the passed in location, note that
755 * the returned tab may not contain the location x,y.
756 */
757 private int getClosestTab(int x, int y) {
758 int min = 0;
759 int tabCount = Math.min(rects.length, tabPane.getTabCount());
760 int max = tabCount;
761 int tabPlacement = tabPane.getTabPlacement();
762 boolean useX = (tabPlacement == TOP || tabPlacement == BOTTOM);
763 int want = (useX) ? x : y;
764
765 while (min != max) {
766 int current = (max + min) / 2;
767 int minLoc;
768 int maxLoc;
769
770 if (useX) {
771 minLoc = rects[current].x;
772 maxLoc = minLoc + rects[current].width;
773 } else {
774 minLoc = rects[current].y;
775 maxLoc = minLoc + rects[current].height;
776 }
777 if (want < minLoc) {
778 max = current;
779 if (min == max) {
780 return Math.max(0, current - 1);
781 }
782 } else if (want >= maxLoc) {
783 min = current;
784 if (max - min <= 1) {
785 return Math.max(current + 1, tabCount - 1);
786 }
787 } else {
788 return current;
789 }
790 }
791 return min;
792 }
793
794 // BasicTabbedPaneUI methods
795
796 // Tab Navigation methods
797
798 // REMIND(aim,7/29/98): This method should be made
799 // protected in the next release where
800 // API changes are allowed
801 //
802 boolean requestMyFocusForVisibleComponent() {
803 Component visibleComponent = getVisibleComponent();
804 if (visibleComponent.isFocusTraversable()) {
805 visibleComponent.requestFocus();
806 return true;
807 } else if (visibleComponent instanceof JComponent) {
808 if (((JComponent) visibleComponent).requestDefaultFocus()) {
809 return true;
810 }
811 }
812 return false;
813 }
814
815 private static class RightAction extends AbstractAction {
816
817 private static final long serialVersionUID = 935893229208903734L;
818
819 public void actionPerformed(ActionEvent e) {
820 JTabbedPane pane = (JTabbedPane) e.getSource();
821 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
822 ui.navigateSelectedTab(EAST);
823 }
824 };
825
826 private static class LeftAction extends AbstractAction {
827 /**
828 *
829 */
830 private static final long serialVersionUID = 4809159341704902394L;
831
832 public void actionPerformed(ActionEvent e) {
833 JTabbedPane pane = (JTabbedPane) e.getSource();
834 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
835 ui.navigateSelectedTab(WEST);
836 }
837 };
838
839 private static class UpAction extends AbstractAction {
840 /**
841 *
842 */
843 private static final long serialVersionUID = 7625909677342383641L;
844
845 public void actionPerformed(ActionEvent e) {
846 JTabbedPane pane = (JTabbedPane) e.getSource();
847 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
848 ui.navigateSelectedTab(NORTH);
849 }
850 };
851
852 private static class DownAction extends AbstractAction {
853 /**
854 *
855 */
856 private static final long serialVersionUID = -8694915120302734352L;
857
858 public void actionPerformed(ActionEvent e) {
859 JTabbedPane pane = (JTabbedPane) e.getSource();
860 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
861 ui.navigateSelectedTab(SOUTH);
862 }
863 };
864
865 private static class NextAction extends AbstractAction {
866 /**
867 *
868 */
869 private static final long serialVersionUID = -6878700030400531959L;
870
871 public void actionPerformed(ActionEvent e) {
872 JTabbedPane pane = (JTabbedPane) e.getSource();
873 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
874 ui.navigateSelectedTab(NEXT);
875 }
876 };
877
878 private static class PreviousAction extends AbstractAction {
879 /**
880 *
881 */
882 private static final long serialVersionUID = -6457669199117221643L;
883
884 public void actionPerformed(ActionEvent e) {
885 JTabbedPane pane = (JTabbedPane) e.getSource();
886 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
887 ui.navigateSelectedTab(PREVIOUS);
888 }
889 };
890
891 private static class PageUpAction extends AbstractAction {
892 /**
893 *
894 */
895 private static final long serialVersionUID = 7982625940083686135L;
896
897 public void actionPerformed(ActionEvent e) {
898 JTabbedPane pane = (JTabbedPane) e.getSource();
899 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
900 int tabPlacement = pane.getTabPlacement();
901 if (tabPlacement == TOP || tabPlacement == BOTTOM) {
902 ui.navigateSelectedTab(WEST);
903 } else {
904 ui.navigateSelectedTab(NORTH);
905 }
906 }
907 };
908
909 private static class PageDownAction extends AbstractAction {
910 /**
911 *
912 */
913 private static final long serialVersionUID = -5282341515109000329L;
914
915 public void actionPerformed(ActionEvent e) {
916 JTabbedPane pane = (JTabbedPane) e.getSource();
917 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
918 int tabPlacement = pane.getTabPlacement();
919 if (tabPlacement == TOP || tabPlacement == BOTTOM) {
920 ui.navigateSelectedTab(EAST);
921 } else {
922 ui.navigateSelectedTab(SOUTH);
923 }
924 }
925 };
926
927 private static class RequestFocusAction extends AbstractAction {
928 /**
929 *
930 */
931 private static final long serialVersionUID = -6217783952850344598L;
932
933 public void actionPerformed(ActionEvent e) {
934 JTabbedPane pane = (JTabbedPane) e.getSource();
935 pane.requestFocus();
936 }
937 };
938
939 private static class RequestFocusForVisibleAction extends AbstractAction {
940 /**
941 *
942 */
943 private static final long serialVersionUID = 3084200378882675448L;
944
945 public void actionPerformed(ActionEvent e) {
946 JTabbedPane pane = (JTabbedPane) e.getSource();
947 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
948 ui.requestMyFocusForVisibleComponent();
949 }
950 };
951
952 /**
953 * Selects a tab in the JTabbedPane based on the String of the action
954 * command. The tab selected is based on the first tab that has a mnemonic
955 * matching the first character of the action command.
956 */
957 private static class SetSelectedIndexAction extends AbstractAction {
958 /**
959 *
960 */
961 private static final long serialVersionUID = 1387941409542691929L;
962
963 public void actionPerformed(ActionEvent e) {
964 JTabbedPane pane = (JTabbedPane) e.getSource();
965
966 if (pane != null && (pane.getUI() instanceof CloseTabPaneUI)) {
967 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
968 String command = e.getActionCommand();
969
970 if (command != null && command.length() > 0) {
971 int mnemonic = (int) e.getActionCommand().charAt(0);
972 if (mnemonic >= 'a' && mnemonic <= 'z') {
973 mnemonic -= ('a' - 'A');
974 }
975 Integer index = (Integer) ui.mnemonicToIndexMap
976 .get(new Integer(mnemonic));
977 if (index != null && pane.isEnabledAt(index.intValue())) {
978 pane.setSelectedIndex(index.intValue());
979 }
980 }
981 }
982 }
983 };
984
985 private static class ScrollTabsForwardAction extends AbstractAction {
986 /**
987 *
988 */
989 private static final long serialVersionUID = -6666938254664028073L;
990
991 public void actionPerformed(ActionEvent e) {
992 JTabbedPane pane = null;
993 Object src = e.getSource();
994 if (src instanceof JTabbedPane) {
995 pane = (JTabbedPane) src;
996 } else if (src instanceof ScrollableTabButton) {
997 pane = (JTabbedPane) ((ScrollableTabButton) src).getParent();
998 } else {
999 return; // shouldn't happen
1000 }
1001 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
1002
1003 ui.tabScroller.scrollForward(pane.getTabPlacement());
1004
1005 }
1006 }
1007
1008 private static class ScrollTabsBackwardAction extends AbstractAction {
1009 /**
1010 *
1011 */
1012 private static final long serialVersionUID = -1987973308242859217L;
1013
1014 public void actionPerformed(ActionEvent e) {
1015 JTabbedPane pane = null;
1016 Object src = e.getSource();
1017 if (src instanceof JTabbedPane) {
1018 pane = (JTabbedPane) src;
1019 } else if (src instanceof ScrollableTabButton) {
1020 pane = (JTabbedPane) ((ScrollableTabButton) src).getParent();
1021 } else {
1022 return; // shouldn't happen
1023 }
1024 CloseTabPaneUI ui = (CloseTabPaneUI) pane.getUI();
1025
1026 ui.tabScroller.scrollBackward(pane.getTabPlacement());
1027
1028 }
1029 }
1030
1031 /**
1032 * This inner class is marked &quot;public&quot; due to a compiler bug. This
1033 * class should be treated as a &quot;protected&quot; inner class.
1034 * Instantiate it only within subclasses of BasicTabbedPaneUI.
1035 */
1036
1037 private class TabbedPaneScrollLayout extends TabbedPaneLayout {
1038
1039 protected int preferredTabAreaHeight(int tabPlacement, int width) {
1040 return calculateMaxTabHeight(tabPlacement);
1041 }
1042
1043 protected int preferredTabAreaWidth(int tabPlacement, int height) {
1044 return calculateMaxTabWidth(tabPlacement);
1045 }
1046
1047 public void layoutContainer(Container parent) {
1048 int tabPlacement = tabPane.getTabPlacement();
1049 int tabCount = tabPane.getTabCount();
1050 Insets insets = tabPane.getInsets();
1051 int selectedIndex = tabPane.getSelectedIndex();
1052 Component visibleComponent = getVisibleComponent();
1053
1054 calculateLayoutInfo();
1055
1056 if (selectedIndex < 0) {
1057 if (visibleComponent != null) {
1058 // The last tab was removed, so remove the component
1059 setVisibleComponent(null);
1060 }
1061 } else {
1062 Component selectedComponent = tabPane
1063 .getComponentAt(selectedIndex);
1064 boolean shouldChangeFocus = false;
1065
1066 // In order to allow programs to use a single component
1067 // as the display for multiple tabs, we will not change
1068 // the visible compnent if the currently selected tab
1069 // has a null component. This is a bit dicey, as we don't
1070 // explicitly state we support this in the spec, but since
1071 // programs are now depending on this, we're making it work.
1072 //
1073 if (selectedComponent != null) {
1074 if (selectedComponent != visibleComponent
1075 && visibleComponent != null) {
1076 if (SwingUtilities.findFocusOwner(visibleComponent) != null) {
1077 shouldChangeFocus = true;
1078 }
1079 }
1080 setVisibleComponent(selectedComponent);
1081 }
1082 int tx, ty, tw, th; // tab area bounds
1083 int cx, cy, cw, ch; // content area bounds
1084 Insets contentInsets = getContentBorderInsets(tabPlacement);
1085 Rectangle bounds = tabPane.getBounds();
1086 int numChildren = tabPane.getComponentCount();
1087
1088 if (numChildren > 0) {
1089
1090 // calculate tab area bounds
1091 tw = bounds.width - insets.left - insets.right;
1092 th = calculateTabAreaHeight(tabPlacement, runCount,
1093 maxTabHeight);
1094 tx = insets.left;
1095 ty = insets.top;
1096
1097 // calculate content area bounds
1098 cx = tx + contentInsets.left;
1099 cy = ty + th + contentInsets.top;
1100 cw = bounds.width - insets.left - insets.right
1101 - contentInsets.left - contentInsets.right;
1102 ch = bounds.height - insets.top - insets.bottom - th
1103 - contentInsets.top - contentInsets.bottom;
1104
1105 for (int i = 0; i < numChildren; i++) {
1106 Component child = tabPane.getComponent(i);
1107
1108 if (child instanceof ScrollableTabViewport) {
1109 JViewport viewport = (JViewport) child;
1110 Rectangle viewRect = viewport.getViewRect();
1111 int vw = tw;
1112 int vh = th;
1113
1114 int totalTabWidth = rects[tabCount - 1].x
1115 + rects[tabCount - 1].width;
1116 if (totalTabWidth > tw) {
1117 // Need to allow space for scrollbuttons
1118 vw = Math.max(tw - 36, 36);
1119 ;
1120 if (totalTabWidth - viewRect.x <= vw) {
1121 // Scrolled to the end, so ensure the
1122 // viewport size is
1123 // such that the scroll offset aligns with a
1124 // tab
1125 vw = totalTabWidth - viewRect.x;
1126 }
1127 }
1128
1129 child.setBounds(tx, ty, vw, vh);
1130
1131 } else if (child instanceof ScrollableTabButton) {
1132 ScrollableTabButton scrollbutton = (ScrollableTabButton) child;
1133 Dimension bsize = scrollbutton.getPreferredSize();
1134 int bx = 0;
1135 int by = 0;
1136 int bw = bsize.width;
1137 int bh = bsize.height;
1138 boolean visible = false;
1139
1140 int totalTabWidth = rects[tabCount - 1].x
1141 + rects[tabCount - 1].width;
1142
1143 if (totalTabWidth > tw) {
1144 int dir = scrollbutton.scrollsForward() ? EAST
1145 : WEST;
1146 scrollbutton.setDirection(dir);
1147 visible = true;
1148 bx = dir == EAST ? bounds.width - insets.left
1149 - bsize.width : bounds.width
1150 - insets.left - 2 * bsize.width;
1151 by = (tabPlacement == TOP ? ty + th
1152 - bsize.height : ty);
1153 }
1154
1155 child.setVisible(visible);
1156 if (visible) {
1157 child.setBounds(bx, by, bw, bh);
1158 }
1159
1160 } else {
1161 // All content children...
1162 child.setBounds(cx, cy, cw, ch);
1163 }
1164 }
1165 if (shouldChangeFocus) {
1166 if (!requestMyFocusForVisibleComponent()) {
1167 tabPane.requestFocus();
1168 }
1169 }
1170 }
1171 }
1172 }
1173
1174 protected void calculateTabRects(int tabPlacement, int tabCount) {
1175 FontMetrics metrics = getFontMetrics();
1176 Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
1177 int i;
1178
1179 int x = tabAreaInsets.left - 2;
1180 int y = tabAreaInsets.top;
1181 int totalWidth = 0;
1182 int totalHeight = 0;
1183
1184 //
1185 // Calculate bounds within which a tab run must fit
1186 //
1187
1188 maxTabHeight = calculateMaxTabHeight(tabPlacement);
1189
1190 runCount = 0;
1191 selectedRun = -1;
1192
1193 if (tabCount == 0) {
1194 return;
1195 }
1196
1197 selectedRun = 0;
1198 runCount = 1;
1199
1200 // Run through tabs and lay them out in a single run
1201 Rectangle rect;
1202 for (i = 0; i < tabCount; i++) {
1203 rect = rects[i];
1204
1205 if (i > 0) {
1206 rect.x = rects[i - 1].x + rects[i - 1].width - 1;
1207 } else {
1208 tabRuns[0] = 0;
1209 maxTabWidth = 0;
1210 totalHeight += maxTabHeight;
1211 rect.x = x;
1212 }
1213 rect.width = calculateTabWidth(tabPlacement, i, metrics);
1214 totalWidth = rect.x + rect.width;
1215 maxTabWidth = Math.max(maxTabWidth, rect.width);
1216
1217 rect.y = y;
1218 rect.height = maxTabHeight /* - 2 */;
1219
1220 }
1221
1222 // tabPanel.setSize(totalWidth, totalHeight);
1223 tabScroller.tabPanel.setPreferredSize(new Dimension(totalWidth,
1224 totalHeight));
1225 }
1226 }
1227
1228 private class ScrollableTabSupport implements ChangeListener {
1229 public ScrollableTabViewport viewport;
1230
1231 public ScrollableTabPanel tabPanel;
1232
1233 public ScrollableTabButton scrollForwardButton;
1234
1235 public ScrollableTabButton scrollBackwardButton;
1236
1237 public int leadingTabIndex;
1238
1239 private Point tabViewPosition = new Point(0, 0);
1240
1241 ScrollableTabSupport(int tabPlacement) {
1242 viewport = new ScrollableTabViewport();
1243 tabPanel = new ScrollableTabPanel();
1244 viewport.setView(tabPanel);
1245 viewport.addChangeListener(this);
1246
1247 scrollForwardButton = createScrollableTabButton(EAST);
1248 scrollBackwardButton = createScrollableTabButton(WEST);
1249 // scrollForwardButton = new ScrollableTabButton(EAST);
1250 // scrollBackwardButton = new ScrollableTabButton(WEST);
1251 }
1252
1253 public void scrollForward(int tabPlacement) {
1254 Dimension viewSize = viewport.getViewSize();
1255 Rectangle viewRect = viewport.getViewRect();
1256
1257 if (tabPlacement == TOP || tabPlacement == BOTTOM) {
1258 if (viewRect.width >= viewSize.width - viewRect.x) {
1259 return; // no room left to scroll
1260 }
1261 } else { // tabPlacement == LEFT || tabPlacement == RIGHT
1262 if (viewRect.height >= viewSize.height - viewRect.y) {
1263 return;
1264 }
1265 }
1266 setLeadingTabIndex(tabPlacement, leadingTabIndex + 1);
1267 }
1268
1269 public void scrollBackward(int tabPlacement) {
1270 if (leadingTabIndex == 0) {
1271 return; // no room left to scroll
1272 }
1273 setLeadingTabIndex(tabPlacement, leadingTabIndex - 1);
1274 }
1275
1276 public void setLeadingTabIndex(int tabPlacement, int index) {
1277 leadingTabIndex = index;
1278 Dimension viewSize = viewport.getViewSize();
1279 Rectangle viewRect = viewport.getViewRect();
1280
1281 tabViewPosition.x = leadingTabIndex == 0 ? 0
1282 : rects[leadingTabIndex].x;
1283
1284 if ((viewSize.width - tabViewPosition.x) < viewRect.width) {
1285 // We've scrolled to the end, so adjust the viewport size
1286 // to ensure the view position remains aligned on a tab boundary
1287 Dimension extentSize = new Dimension(viewSize.width
1288 - tabViewPosition.x, viewRect.height);
1289 viewport.setExtentSize(extentSize);
1290 }
1291
1292 viewport.setViewPosition(tabViewPosition);
1293 }
1294
1295 public void stateChanged(ChangeEvent e) {
1296 JViewport viewport = (JViewport) e.getSource();
1297 int tabPlacement = tabPane.getTabPlacement();
1298 int tabCount = tabPane.getTabCount();
1299 Rectangle vpRect = viewport.getBounds();
1300 Dimension viewSize = viewport.getViewSize();
1301 Rectangle viewRect = viewport.getViewRect();
1302
1303 leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);
1304
1305 // If the tab isn't right aligned, adjust it.
1306 if (leadingTabIndex + 1 < tabCount) {
1307
1308 if (rects[leadingTabIndex].x < viewRect.x) {
1309 leadingTabIndex++;
1310 }
1311
1312 }
1313 Insets contentInsets = getContentBorderInsets(tabPlacement);
1314
1315 tabPane.repaint(vpRect.x, vpRect.y + vpRect.height, vpRect.width,
1316 contentInsets.top);
1317 scrollBackwardButton.setEnabled(viewRect.x > 0);
1318 scrollForwardButton.setEnabled(leadingTabIndex < tabCount - 1
1319 && viewSize.width - viewRect.x > viewRect.width);
1320
1321 }
1322
1323 public String toString() {
1324 return new String("viewport.viewSize=" + viewport.getViewSize()
1325 + "\n" + "viewport.viewRectangle=" + viewport.getViewRect()
1326 + "\n" + "leadingTabIndex=" + leadingTabIndex + "\n"
1327 + "tabViewPosition=" + tabViewPosition);
1328 }
1329
1330 }
1331
1332 private class ScrollableTabViewport extends JViewport implements UIResource {
1333 /**
1334 *
1335 */
1336 private static final long serialVersionUID = -1242867759592475276L;
1337
1338 public ScrollableTabViewport() {
1339 super();
1340 setScrollMode(SIMPLE_SCROLL_MODE);
1341 }
1342 }
1343
1344 private class ScrollableTabPanel extends JPanel implements UIResource {
1345 /**
1346 *
1347 */
1348 private static final long serialVersionUID = -1464842471862829924L;
1349
1350 public ScrollableTabPanel() {
1351 setLayout(null);
1352 }
1353
1354 public void paintComponent(Graphics g) {
1355 super.paintComponent(g);
1356 CloseTabPaneUI.this.paintTabArea(g, tabPane.getTabPlacement(),
1357 tabPane.getSelectedIndex());
1358
1359 }
1360 }
1361
1362 protected class ScrollableTabButton extends BasicArrowButton implements
1363 UIResource, SwingConstants {
1364 /**
1365 *
1366 */
1367 private static final long serialVersionUID = -495311838796397754L;
1368
1369 public ScrollableTabButton(int direction) {
1370 super(direction, UIManager.getColor("TabbedPane.selected"),
1371 UIManager.getColor("TabbedPane.shadow"), UIManager
1372 .getColor("TabbedPane.darkShadow"), UIManager
1373 .getColor("TabbedPane.highlight"));
1374
1375 }
1376
1377 public boolean scrollsForward() {
1378 return direction == EAST || direction == SOUTH;
1379 }
1380
1381 }
1382
1383 /**
1384 * This inner class is marked &quot;public&quot; due to a compiler bug. This
1385 * class should be treated as a &quot;protected&quot; inner class.
1386 * Instantiate it only within subclasses of BasicTabbedPaneUI.
1387 */
1388 public class TabSelectionHandler implements ChangeListener {
1389 public void stateChanged(ChangeEvent e) {
1390 JTabbedPane tabPane = (JTabbedPane) e.getSource();
1391 tabPane.revalidate();
1392 tabPane.repaint();
1393
1394 if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {
1395 int index = tabPane.getSelectedIndex();
1396 if (index < rects.length && index != -1) {
1397 tabScroller.tabPanel.scrollRectToVisible(rects[index]);
1398 }
1399 }
1400 }
1401 }
1402
1403 /**
1404 * This inner class is marked &quot;public&quot; due to a compiler bug. This
1405 * class should be treated as a &quot;protected&quot; inner class.
1406 * Instantiate it only within subclasses of BasicTabbedPaneUI.
1407 */
1408
1409 /*
1410 * GES 2/3/99: The container listener code was added to support HTML
1411 * rendering of tab titles.
1412 *
1413 * Ideally, we would be able to listen for property changes when a tab is
1414 * added or its text modified. At the moment there are no such events
1415 * because the Beans spec doesn't allow 'indexed' property changes (i.e. tab
1416 * 2's text changed from A to B).
1417 *
1418 * In order to get around this, we listen for tabs to be added or removed by
1419 * listening for the container events. we then queue up a runnable (so the
1420 * component has a chance to complete the add) which checks the tab title of
1421 * the new component to see if it requires HTML rendering.
1422 *
1423 * The Views (one per tab title requiring HTML rendering) are stored in the
1424 * htmlViews Vector, which is only allocated after the first time we run
1425 * into an HTML tab. Note that this vector is kept in step with the number
1426 * of pages, and nulls are added for those pages whose tab title do not
1427 * require HTML rendering.
1428 *
1429 * This makes it easy for the paint and layout code to tell whether to
1430 * invoke the HTML engine without having to check the string during
1431 * time-sensitive operations.
1432 *
1433 * When we have added a way to listen for tab additions and changes to tab
1434 * text, this code should be removed and replaced by something which uses
1435 * that.
1436 */
1437
1438 private class ContainerHandler implements ContainerListener {
1439 public void componentAdded(ContainerEvent e) {
1440 JTabbedPane tp = (JTabbedPane) e.getContainer();
1441 Component child = e.getChild();
1442 if (child instanceof UIResource) {
1443 return;
1444 }
1445 int index = tp.indexOfComponent(child);
1446 String title = tp.getTitleAt(index);
1447 boolean isHTML = BasicHTML.isHTMLString(title);
1448 if (isHTML) {
1449 if (htmlViews == null) { // Initialize vector
1450 htmlViews = createHTMLVector();
1451 } else { // Vector already exists
1452 View v = BasicHTML.createHTMLView(tp, title);
1453 htmlViews.insertElementAt(v, index);
1454 }
1455 } else { // Not HTML
1456 if (htmlViews != null) { // Add placeholder
1457 htmlViews.insertElementAt(null, index);
1458 } // else nada!
1459 }
1460 }
1461
1462 public void componentRemoved(ContainerEvent e) {
1463 JTabbedPane tp = (JTabbedPane) e.getContainer();
1464 Component child = e.getChild();
1465 if (child instanceof UIResource) {
1466 return;
1467 }
1468
1469 // NOTE 4/15/2002 (joutwate):
1470 // This fix is implemented using client properties since there is
1471 // currently no IndexPropertyChangeEvent. Once
1472 // IndexPropertyChangeEvents have been added this code should be
1473 // modified to use it.
1474 Integer indexObj = (Integer) tp
1475 .getClientProperty("__index_to_remove__");
1476 if (indexObj != null) {
1477 int index = indexObj.intValue();
1478 if (htmlViews != null && htmlViews.size() >= index) {
1479 htmlViews.removeElementAt(index);
1480 }
1481 }
1482 }
1483 }
1484
1485 private Vector<View> createHTMLVector() {
1486 Vector<View> htmlViews = new Vector<View>();
1487 int count = tabPane.getTabCount();
1488 if (count > 0) {
1489 for (int i = 0; i < count; i++) {
1490 String title = tabPane.getTitleAt(i);
1491 if (BasicHTML.isHTMLString(title)) {
1492 htmlViews.addElement(BasicHTML.createHTMLView(tabPane,
1493 title));
1494 } else {
1495 htmlViews.addElement(null);
1496 }
1497 }
1498 }
1499 return htmlViews;
1500 }
1501
1502 class MyMouseHandler extends MouseHandler {
1503 public MyMouseHandler() {
1504 super();
1505 }
1506
1507 public void mousePressed(MouseEvent e) {
1508 if (closeIndexStatus == OVER) {
1509 closeIndexStatus = PRESSED;
1510 tabScroller.tabPanel.repaint();
1511 return;
1512 }
1513
1514 if (maxIndexStatus == OVER) {
1515 maxIndexStatus = PRESSED;
1516 tabScroller.tabPanel.repaint();
1517 return;
1518 }
1519
1520 }
1521
1522 public void mouseClicked(MouseEvent e) {
1523 super.mousePressed(e);
1524 if (e.getClickCount() > 1 && overTabIndex != -1) {
1525 ((CloseTabbedPane) tabPane).fireDoubleClickTabEvent(e,
1526 overTabIndex);
1527 }
1528 }
1529
1530 public void mouseReleased(MouseEvent e) {
1531
1532 updateOverTab(e.getX(), e.getY());
1533
1534 if (overTabIndex == -1) {
1535 if (e.isPopupTrigger())
1536 ((CloseTabbedPane) tabPane).firePopupOutsideTabEvent(e);
1537 return;
1538 }
1539
1540 if (isOneActionButtonEnabled() && e.isPopupTrigger()) {
1541 super.mousePressed(e);
1542
1543 closeIndexStatus = INACTIVE; // Prevent undesired action when
1544 maxIndexStatus = INACTIVE; // right-clicking on icons
1545
1546 actionPopupMenu.show(tabScroller.tabPanel, e.getX(), e.getY());
1547 return;
1548 }
1549
1550 if (closeIndexStatus == PRESSED) {
1551 closeIndexStatus = OVER;
1552 tabScroller.tabPanel.repaint();
1553 ((CloseTabbedPane) tabPane).fireCloseTabEvent(e, overTabIndex);
1554 return;
1555 }
1556
1557 if (maxIndexStatus == PRESSED) {
1558 maxIndexStatus = OVER;
1559 tabScroller.tabPanel.repaint();
1560 ((CloseTabbedPane) tabPane).fireMaxTabEvent(e, overTabIndex);
1561 return;
1562 }
1563
1564 }
1565
1566 public void mouseExited(MouseEvent e) {
1567 if (!mousePressed) {
1568 overTabIndex = -1;
1569 tabScroller.tabPanel.repaint();
1570 }
1571 }
1572
1573 }
1574
1575 class MyMouseMotionListener implements MouseMotionListener {
1576
1577 public void mouseMoved(MouseEvent e) {
1578 if (actionPopupMenu.isVisible())
1579 return; // No updates when popup is visible
1580 mousePressed = false;
1581 setTabIcons(e.getX(), e.getY());
1582 }
1583
1584 public void mouseDragged(MouseEvent e) {
1585 if (actionPopupMenu.isVisible())
1586 return; // No updates when popup is visible
1587 mousePressed = true;
1588 setTabIcons(e.getX(), e.getY());
1589 }
1590 }
1591
1592}
Note: See TracBrowser for help on using the repository browser.