001: /**
002: * L2FProd.com Common Components 7.3 License.
003: *
004: * Copyright 2005-2007 L2FProd.com
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package com.l2fprod.common.swing.plaf.basic;
018:
019: import com.l2fprod.common.swing.JOutlookBar;
020: import com.l2fprod.common.swing.PercentLayout;
021: import com.l2fprod.common.swing.PercentLayoutAnimator;
022: import com.l2fprod.common.swing.plaf.OutlookBarUI;
023: import com.l2fprod.common.util.JVM;
024:
025: import java.awt.BorderLayout;
026: import java.awt.Color;
027: import java.awt.Component;
028: import java.awt.Container;
029: import java.awt.Dimension;
030: import java.awt.Font;
031: import java.awt.FontMetrics;
032: import java.awt.Graphics;
033: import java.awt.KeyboardFocusManager;
034: import java.awt.Rectangle;
035: import java.awt.event.ActionEvent;
036: import java.awt.event.ActionListener;
037: import java.awt.event.ContainerAdapter;
038: import java.awt.event.ContainerEvent;
039: import java.awt.event.ContainerListener;
040: import java.awt.event.MouseAdapter;
041: import java.awt.event.MouseListener;
042: import java.beans.PropertyChangeEvent;
043: import java.beans.PropertyChangeListener;
044: import java.util.ArrayList;
045: import java.util.HashMap;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Map;
049:
050: import javax.swing.BorderFactory;
051: import javax.swing.Icon;
052: import javax.swing.JButton;
053: import javax.swing.JComponent;
054: import javax.swing.JPanel;
055: import javax.swing.JScrollPane;
056: import javax.swing.JTabbedPane;
057: import javax.swing.LookAndFeel;
058: import javax.swing.Scrollable;
059: import javax.swing.SwingUtilities;
060: import javax.swing.event.ChangeEvent;
061: import javax.swing.event.ChangeListener;
062: import javax.swing.plaf.ButtonUI;
063: import javax.swing.plaf.ComponentUI;
064: import javax.swing.plaf.UIResource;
065: import javax.swing.plaf.basic.BasicTabbedPaneUI;
066:
067: /**
068: * BasicOutlookBarUI. <br>
069: *
070: */
071: public class BasicOutlookBarUI extends BasicTabbedPaneUI implements
072: OutlookBarUI {
073:
074: private static final String BUTTON_ORIGINAL_FOREGROUND = "TabButton/foreground";
075: private static final String BUTTON_ORIGINAL_BACKGROUND = "TabButton/background";
076:
077: public static ComponentUI createUI(JComponent c) {
078: return new BasicOutlookBarUI();
079: }
080:
081: private ContainerListener tabListener;
082: private Map buttonToTab;
083: private Map tabToButton;
084: private Component nextVisibleComponent;
085: private PercentLayoutAnimator animator;
086:
087: public JScrollPane makeScrollPane(Component component) {
088: // the component is not scrollable, wraps it in a ScrollableJPanel
089: JScrollPane scroll = new JScrollPane();
090: scroll.setBorder(BorderFactory.createEmptyBorder());
091: if (component instanceof Scrollable) {
092: scroll.getViewport().setView(component);
093: } else {
094: scroll.getViewport().setView(
095: new ScrollableJPanel(component));
096: }
097: scroll.setOpaque(false);
098: scroll.getViewport().setOpaque(false);
099: return scroll;
100: }
101:
102: protected void installDefaults() {
103: super .installDefaults();
104:
105: TabLayout layout = new TabLayout();
106: tabPane.setLayout(layout);
107: // ensure constraints is correct for existing components
108: layout.setLayoutConstraints(tabPane);
109: updateTabLayoutOrientation();
110:
111: buttonToTab = new HashMap();
112: tabToButton = new HashMap();
113:
114: LookAndFeel.installBorder(tabPane, "OutlookBar.border");
115: LookAndFeel.installColors(tabPane, "OutlookBar.background",
116: "OutlookBar.foreground");
117:
118: tabPane.setOpaque(true);
119:
120: // add buttons for the current components already added in this panel
121: Component[] components = tabPane.getComponents();
122: for (int i = 0, c = components.length; i < c; i++) {
123: tabAdded(components[i]);
124: }
125: }
126:
127: protected void uninstallDefaults() {
128: // remove all buttons created for components
129: List tabs = new ArrayList(buttonToTab.values());
130: for (Iterator iter = tabs.iterator(); iter.hasNext();) {
131: Component tab = (Component) iter.next();
132: tabRemoved(tab);
133: }
134: super .uninstallDefaults();
135: }
136:
137: protected void installListeners() {
138: tabPane.addContainerListener(tabListener = createTabListener());
139: tabPane
140: .addPropertyChangeListener(propertyChangeListener = createPropertyChangeListener());
141: tabPane
142: .addChangeListener(tabChangeListener = createChangeListener());
143: }
144:
145: protected ContainerListener createTabListener() {
146: return new ContainerTabHandler();
147: }
148:
149: protected PropertyChangeListener createPropertyChangeListener() {
150: return new PropertyChangeHandler();
151: }
152:
153: protected ChangeListener createChangeListener() {
154: return new ChangeHandler();
155: }
156:
157: protected void uninstallListeners() {
158: tabPane.removeChangeListener(tabChangeListener);
159: tabPane.removePropertyChangeListener(propertyChangeListener);
160: tabPane.removeContainerListener(tabListener);
161: }
162:
163: public Rectangle getTabBounds(JTabbedPane pane, int index) {
164: Component tab = pane.getComponentAt(index);
165: return tab.getBounds();
166: }
167:
168: public int getTabRunCount(JTabbedPane pane) {
169: return 0;
170: }
171:
172: public int tabForCoordinate(JTabbedPane pane, int x, int y) {
173: int index = -1;
174: for (int i = 0, c = pane.getTabCount(); i < c; i++) {
175: if (pane.getComponentAt(i).contains(x, y)) {
176: index = i;
177: break;
178: }
179: }
180: return index;
181: }
182:
183: protected int indexOfComponent(Component component) {
184: int index = -1;
185: Component[] components = tabPane.getComponents();
186: for (int i = 0; i < components.length; i++) {
187: if (components[i] == component) {
188: index = i;
189: break;
190: }
191: }
192: return index;
193: }
194:
195: protected TabButton createTabButton() {
196: TabButton button = new TabButton();
197: button.setOpaque(true);
198: return button;
199: }
200:
201: protected void tabAdded(final Component newTab) {
202: TabButton button = (TabButton) tabToButton.get(newTab);
203: if (button == null) {
204: button = createTabButton();
205:
206: // save the button original color,
207: // later they would be restored if the button colors are not customized on
208: // the OutlookBar
209: button.putClientProperty(BUTTON_ORIGINAL_FOREGROUND, button
210: .getForeground());
211: button.putClientProperty(BUTTON_ORIGINAL_BACKGROUND, button
212: .getBackground());
213:
214: buttonToTab.put(button, newTab);
215: tabToButton.put(newTab, button);
216: button.addActionListener(new ActionListener() {
217: public void actionPerformed(ActionEvent e) {
218: final Component current = getVisibleComponent();
219: Component target = newTab;
220:
221: // animate the tabPane if there is a current tab selected and if the
222: // tabPane allows animation
223: if (((JOutlookBar) tabPane).isAnimated()
224: && current != target && current != null
225: && target != null) {
226: if (animator != null) {
227: animator.stop();
228: }
229: animator = new PercentLayoutAnimator(tabPane,
230: (PercentLayout) tabPane.getLayout()) {
231: protected void complete() {
232: super .complete();
233: tabPane.setSelectedComponent(newTab);
234: nextVisibleComponent = null;
235: // ensure the no longer visible component has its constraint
236: // reset to 100% in the case it becomes visible again without
237: // animation
238: if (current.getParent() == tabPane) {
239: ((PercentLayout) tabPane
240: .getLayout())
241: .setConstraint(current,
242: "100%");
243: }
244: }
245: };
246: nextVisibleComponent = newTab;
247: animator.setTargetPercent(current, 1.0f, 0.0f);
248: animator.setTargetPercent(newTab, 0.0f, 1.0f);
249: animator.start();
250: } else {
251: nextVisibleComponent = null;
252: tabPane.setSelectedComponent(newTab);
253: }
254: }
255: });
256: } else {
257: // the tab is already in the list, remove the button, it will be
258: // added again later
259: tabPane.remove(button);
260: }
261:
262: // update the button with the tab information
263: updateTabButtonAt(tabPane.indexOfComponent(newTab));
264:
265: int index = indexOfComponent(newTab);
266: tabPane.add(button, index);
267:
268: // workaround for nullpointerexception in setRolloverTab
269: // introduced by J2SE 5
270: if (JVM.current().isOneDotFive()) {
271: assureRectsCreated(tabPane.getTabCount());
272: }
273: }
274:
275: protected void tabRemoved(Component removedTab) {
276: TabButton button = (TabButton) tabToButton.get(removedTab);
277: tabPane.remove(button);
278: buttonToTab.remove(button);
279: tabToButton.remove(removedTab);
280: }
281:
282: /**
283: * Called whenever a property of a tab is changed
284: *
285: * @param index
286: */
287: protected void updateTabButtonAt(int index) {
288: TabButton button = buttonForTab(index);
289: button.setText(tabPane.getTitleAt(index));
290: button.setIcon(tabPane.getIconAt(index));
291: button.setDisabledIcon(tabPane.getDisabledIconAt(index));
292:
293: Color background = tabPane.getBackgroundAt(index);
294: if (background == null) {
295: background = (Color) button
296: .getClientProperty(BUTTON_ORIGINAL_BACKGROUND);
297: }
298: button.setBackground(background);
299:
300: Color foreground = tabPane.getForegroundAt(index);
301: if (foreground == null) {
302: foreground = (Color) button
303: .getClientProperty(BUTTON_ORIGINAL_FOREGROUND);
304: }
305: button.setForeground(foreground);
306:
307: button.setToolTipText(tabPane.getToolTipTextAt(index));
308: button.setDisplayedMnemonicIndex(tabPane
309: .getDisplayedMnemonicIndexAt(index));
310: button.setMnemonic(tabPane.getMnemonicAt(index));
311: button.setEnabled(tabPane.isEnabledAt(index));
312: button.setHorizontalAlignment(((JOutlookBar) tabPane)
313: .getAlignmentAt(index));
314: }
315:
316: protected TabButton buttonForTab(int index) {
317: Component component = tabPane.getComponentAt(index);
318: return (TabButton) tabToButton.get(component);
319: }
320:
321: class ChangeHandler implements ChangeListener {
322: public void stateChanged(ChangeEvent e) {
323: JTabbedPane tabPane = (JTabbedPane) e.getSource();
324: tabPane.revalidate();
325: tabPane.repaint();
326: }
327: }
328:
329: class PropertyChangeHandler implements PropertyChangeListener {
330: public void propertyChange(PropertyChangeEvent e) {
331: //JTabbedPane pane = (JTabbedPane)e.getSource();
332: String name = e.getPropertyName();
333: if ("tabPropertyChangedAtIndex".equals(name)) {
334: int index = ((Integer) e.getNewValue()).intValue();
335: updateTabButtonAt(index);
336: } else if ("tabPlacement".equals(name)) {
337: updateTabLayoutOrientation();
338: }
339: }
340: }
341:
342: protected void updateTabLayoutOrientation() {
343: TabLayout layout = (TabLayout) tabPane.getLayout();
344: int placement = tabPane.getTabPlacement();
345: if (placement == JTabbedPane.TOP
346: || placement == JTabbedPane.BOTTOM) {
347: layout.setOrientation(PercentLayout.HORIZONTAL);
348: } else {
349: layout.setOrientation(PercentLayout.VERTICAL);
350: }
351: }
352:
353: /**
354: * Manages tabs being added or removed
355: */
356: class ContainerTabHandler extends ContainerAdapter {
357:
358: public void componentAdded(ContainerEvent e) {
359: if (!(e.getChild() instanceof UIResource)) {
360: Component newTab = e.getChild();
361: tabAdded(newTab);
362: }
363: }
364:
365: public void componentRemoved(ContainerEvent e) {
366: if (!(e.getChild() instanceof UIResource)) {
367: Component oldTab = e.getChild();
368: tabRemoved(oldTab);
369: }
370: }
371: }
372:
373: /**
374: * Layout for the tabs, buttons get preferred size, tabs get all
375: */
376: protected class TabLayout extends PercentLayout {
377: public void addLayoutComponent(Component component,
378: Object constraints) {
379: if (constraints == null) {
380: if (component instanceof TabButton) {
381: super .addLayoutComponent(component, "");
382: } else {
383: super .addLayoutComponent(component, "100%");
384: }
385: } else {
386: super .addLayoutComponent(component, constraints);
387: }
388: }
389:
390: public void setLayoutConstraints(Container parent) {
391: Component[] components = parent.getComponents();
392: for (int i = 0, c = components.length; i < c; i++) {
393: if (!(components[i] instanceof TabButton)) {
394: super .addLayoutComponent(components[i], "100%");
395: }
396: }
397: }
398:
399: public void layoutContainer(Container parent) {
400: int selectedIndex = tabPane.getSelectedIndex();
401: Component visibleComponent = getVisibleComponent();
402:
403: if (selectedIndex < 0) {
404: if (visibleComponent != null) {
405: // The last tab was removed, so remove the component
406: setVisibleComponent(null);
407: }
408: } else {
409: Component selectedComponent = tabPane
410: .getComponentAt(selectedIndex);
411: boolean shouldChangeFocus = false;
412:
413: // In order to allow programs to use a single component
414: // as the display for multiple tabs, we will not change
415: // the visible compnent if the currently selected tab
416: // has a null component. This is a bit dicey, as we don't
417: // explicitly state we support this in the spec, but since
418: // programs are now depending on this, we're making it work.
419: //
420: if (selectedComponent != null) {
421: if (selectedComponent != visibleComponent
422: && visibleComponent != null) {
423: // get the current focus owner
424: Component currentFocusOwner = KeyboardFocusManager
425: .getCurrentKeyboardFocusManager()
426: .getFocusOwner();
427: // check if currentFocusOwner is a descendant of visibleComponent
428: if (currentFocusOwner != null
429: && SwingUtilities.isDescendingFrom(
430: currentFocusOwner,
431: visibleComponent)) {
432: shouldChangeFocus = true;
433: }
434: }
435: setVisibleComponent(selectedComponent);
436:
437: // make sure only the selected component is visible
438: Component[] components = parent.getComponents();
439: for (int i = 0; i < components.length; i++) {
440: if (!(components[i] instanceof UIResource)
441: && components[i].isVisible()
442: && components[i] != selectedComponent) {
443: components[i].setVisible(false);
444: setConstraint(components[i], "*");
445: }
446: }
447:
448: if (BasicOutlookBarUI.this .nextVisibleComponent != null) {
449: BasicOutlookBarUI.this .nextVisibleComponent
450: .setVisible(true);
451: }
452: }
453:
454: super .layoutContainer(parent);
455:
456: if (shouldChangeFocus) {
457: if (!requestFocusForVisibleComponent0()) {
458: tabPane.requestFocus();
459: }
460: }
461: }
462: }
463: }
464:
465: // PENDING(fred) JDK 1.5 may have this method from superclass
466: protected boolean requestFocusForVisibleComponent0() {
467: Component visibleComponent = getVisibleComponent();
468: if (visibleComponent.isFocusable()) {
469: visibleComponent.requestFocus();
470: return true;
471: } else if (visibleComponent instanceof JComponent) {
472: if (((JComponent) visibleComponent).requestDefaultFocus()) {
473: return true;
474: }
475: }
476: return false;
477: }
478:
479: protected static class TabButton extends JButton implements
480: UIResource {
481: public TabButton() {
482: }
483:
484: public TabButton(ButtonUI ui) {
485: setUI(ui);
486: }
487: }
488:
489: //
490: //
491: //
492:
493: /**
494: * Overriden to return an empty adapter, the default listener was
495: * just implementing the tab selection mechanism
496: */
497: protected MouseListener createMouseListener() {
498: return new MouseAdapter() {
499: };
500: }
501:
502: /**
503: * Wraps any component in a Scrollable JPanel so it can work
504: * correctly within a viewport
505: */
506: private static class ScrollableJPanel extends JPanel implements
507: Scrollable {
508: public ScrollableJPanel(Component component) {
509: setLayout(new BorderLayout(0, 0));
510: add("Center", component);
511: setOpaque(false);
512: }
513:
514: public int getScrollableUnitIncrement(Rectangle visibleRect,
515: int orientation, int direction) {
516: return 16;
517: }
518:
519: public Dimension getPreferredScrollableViewportSize() {
520: return (super .getPreferredSize());
521: }
522:
523: public int getScrollableBlockIncrement(Rectangle visibleRect,
524: int orientation, int direction) {
525: return 16;
526: }
527:
528: public boolean getScrollableTracksViewportWidth() {
529: return true;
530: }
531:
532: public boolean getScrollableTracksViewportHeight() {
533: return false;
534: }
535: }
536:
537: //
538: // Override all paint methods of the BasicTabbedPaneUI to do nothing
539: //
540:
541: public void paint(Graphics g, JComponent c) {
542: }
543:
544: protected void paintContentBorder(Graphics g, int tabPlacement,
545: int selectedIndex) {
546: }
547:
548: protected void paintContentBorderBottomEdge(Graphics g,
549: int tabPlacement, int selectedIndex, int x, int y, int w,
550: int h) {
551: }
552:
553: protected void paintContentBorderLeftEdge(Graphics g,
554: int tabPlacement, int selectedIndex, int x, int y, int w,
555: int h) {
556: }
557:
558: protected void paintContentBorderRightEdge(Graphics g,
559: int tabPlacement, int selectedIndex, int x, int y, int w,
560: int h) {
561: }
562:
563: protected void paintContentBorderTopEdge(Graphics g,
564: int tabPlacement, int selectedIndex, int x, int y, int w,
565: int h) {
566: }
567:
568: protected void paintFocusIndicator(Graphics g, int tabPlacement,
569: Rectangle[] rects, int tabIndex, Rectangle iconRect,
570: Rectangle textRect, boolean isSelected) {
571: }
572:
573: protected void paintIcon(Graphics g, int tabPlacement,
574: int tabIndex, Icon icon, Rectangle iconRect,
575: boolean isSelected) {
576: }
577:
578: protected void paintTab(Graphics g, int tabPlacement,
579: Rectangle[] rects, int tabIndex, Rectangle iconRect,
580: Rectangle textRect) {
581: }
582:
583: protected void paintTabArea(Graphics g, int tabPlacement,
584: int selectedIndex) {
585: }
586:
587: protected void paintTabBackground(Graphics g, int tabPlacement,
588: int tabIndex, int x, int y, int w, int h, boolean isSelected) {
589: }
590:
591: protected void paintTabBorder(Graphics g, int tabPlacement,
592: int tabIndex, int x, int y, int w, int h, boolean isSelected) {
593: }
594:
595: protected void paintText(Graphics g, int tabPlacement, Font font,
596: FontMetrics metrics, int tabIndex, String title,
597: Rectangle textRect, boolean isSelected) {
598: }
599: }
|