2
* Copyright (c) 2005-2010 Laf-Widget Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
10
* o Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
14
* o Neither the name of Laf-Widget Kirill Grouchnikov nor the names of
15
* its contributors may be used to endorse or promote products derived
16
* from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
package org.pushingpixels.lafwidget.tabbed;
33
import java.awt.event.MouseAdapter;
34
import java.awt.event.MouseEvent;
35
import java.awt.image.BufferedImage;
39
import org.pushingpixels.lafwidget.LafWidgetUtilities2;
40
import org.pushingpixels.lafwidget.animation.AnimationConfigurationManager;
41
import org.pushingpixels.lafwidget.tabbed.TabPreviewThread.TabPreviewCallback;
42
import org.pushingpixels.lafwidget.tabbed.TabPreviewThread.TabPreviewInfo;
43
import org.pushingpixels.trident.Timeline;
44
import org.pushingpixels.trident.Timeline.TimelineState;
45
import org.pushingpixels.trident.callback.UIThreadTimelineCallbackAdapter;
50
* @author Kirill Grouchnikov
52
public class TabPagerManager {
54
* Singleton instance of the tab pager manager.
56
protected static TabPagerManager instance;
59
* The tabbed pane that is currently paged.
61
protected JTabbedPane currTabbedPane;
64
* Index of the central tab.
66
protected int currTabIndex;
69
* Index of the next tab.
71
protected int nextTabIndex;
74
* Index of the previous tab.
76
protected int prevTabIndex;
78
// protected Map smallPreviewMap;
80
// protected Map regularPreviewMap;
83
* Preview window for the left (previous) tab.
85
protected JWindow prevTabWindow;
88
* Preview window for the central (current) tab.
90
protected JWindow currTabWindow;
93
* Preview window for the right (next) tab.
95
protected JWindow nextTabWindow;
98
* Indicates whether the tab pager windows are visible.
100
protected boolean isVisible;
103
* Implementation of the tab preview callback for the tab pager.
105
* @author Kirill Grouchnikov.
107
public class TabPagerPreviewCallback implements TabPreviewCallback {
109
* The associated preview window.
111
private JWindow previewWindow;
114
* The associated tab preview control.
116
private TabPreviewControl previewControl;
119
* Creates a new tab preview callback for the tab pager.
121
* @param previewWindow
122
* The associated preview window.
124
* The associated tab preview control.
128
public TabPagerPreviewCallback(JWindow previewWindow,
129
JTabbedPane tabPane, int tabIndex) {
130
this.previewWindow = previewWindow;
131
this.previewControl = new TabPreviewControl(tabPane, tabIndex);
132
this.previewWindow.getContentPane().removeAll();
133
this.previewWindow.getContentPane().add(this.previewControl,
134
BorderLayout.CENTER);
135
this.previewWindow.getContentPane().doLayout();
136
this.previewControl.doLayout();
143
* org.pushingpixels.lafwidget.tabbed.TabPreviewThread.TabPreviewCallback
144
* #start(javax.swing.JTabbedPane, int,
145
* org.pushingpixels.lafwidget.tabbed.TabPreviewThread.TabPreviewInfo)
148
public void start(JTabbedPane tabPane, int tabCount,
149
TabPreviewInfo tabPreviewInfo) {
150
// Nothing to do since the callback was registered
151
// for a specific tab.
158
* org.pushingpixels.lafwidget.tabbed.TabPreviewThread.TabPreviewCallback
159
* #offer(javax.swing.JTabbedPane, int, java.awt.image.BufferedImage)
162
public void offer(JTabbedPane tabPane, int tabIndex,
163
BufferedImage componentSnap) {
164
if (TabPagerManager.this.currTabbedPane != tabPane) {
165
// has since been cancelled
169
if (!this.previewWindow.isVisible()) {
170
// has since been hidden
174
this.previewControl.setPreviewImage(componentSnap, true);
179
* Returns the tab pager instance.
181
* @return Tab pager instance.
183
public static synchronized TabPagerManager getPager() {
184
if (TabPagerManager.instance == null)
185
TabPagerManager.instance = new TabPagerManager();
186
return TabPagerManager.instance;
190
* Constructs a new tab pager manager. Is made private to enforce single
193
private TabPagerManager() {
194
// this.smallPreviewMap = new HashMap();
195
// this.regularPreviewMap = new HashMap();
197
// Rectangle virtualBounds = new Rectangle();
198
// GraphicsEnvironment ge = GraphicsEnvironment
199
// .getLocalGraphicsEnvironment();
200
// GraphicsDevice[] gds = ge.getScreenDevices();
201
// for (int i = 0; i < gds.length; i++) {
202
// GraphicsDevice gd = gds[i];
203
// GraphicsConfiguration gc = gd.getDefaultConfiguration();
204
// virtualBounds = virtualBounds.union(gc.getBounds());
207
// int screenWidth = virtualBounds.width;
208
// int screenHeight = virtualBounds.height;
210
this.currTabWindow = new JWindow();
211
this.currTabWindow.getContentPane().setLayout(new BorderLayout());
212
// int currWidth = screenWidth / 3;
213
// int currHeight = screenHeight / 3;
214
// this.currTabWindow.setSize(currWidth, currHeight);
215
// // Fix for issue 178 on Substance (multiple screens)
216
// this.currTabWindow.setLocation(currWidth + virtualBounds.x,
218
this.currTabWindow.addMouseListener(new MouseAdapter() {
220
public void mouseClicked(MouseEvent e) {
221
SwingUtilities.invokeLater(new Runnable() {
224
// fix for issue 177 in Substance (disallowing selection
225
// of disabled tabs).
226
TabPreviewPainter tpp = LafWidgetUtilities2
227
.getTabPreviewPainter(currTabbedPane);
228
if (tpp.isSensitiveToEvents(currTabbedPane,
231
currTabbedPane.setSelectedIndex(currTabIndex);
238
.addMouseWheelListener(new TabPagerMouseWheelListener());
240
// int smallWidth = 2 * screenWidth / 9;
241
// int smallHeight = 2 * screenHeight / 9;
242
this.prevTabWindow = new JWindow();
243
this.prevTabWindow.getContentPane().setLayout(new BorderLayout());
244
// this.prevTabWindow.setSize(smallWidth, smallHeight);
245
// // Fix for issue 178 on Substance (multiple screens)
246
// this.prevTabWindow.setLocation((screenWidth / 18) + virtualBounds.x,
247
// 7 * screenHeight / 18);
248
this.prevTabWindow.addMouseListener(new MouseAdapter() {
250
public void mouseClicked(MouseEvent e) {
251
SwingUtilities.invokeLater(new Runnable() {
254
// fix for issue 177 in Substance (disallowing selection
255
// of disabled tabs).
256
TabPreviewPainter tpp = LafWidgetUtilities2
257
.getTabPreviewPainter(currTabbedPane);
258
if (tpp.isSensitiveToEvents(currTabbedPane,
261
currTabbedPane.setSelectedIndex(prevTabIndex);
268
.addMouseWheelListener(new TabPagerMouseWheelListener());
270
this.nextTabWindow = new JWindow();
271
// this.nextTabWindow.getContentPane().setLayout(new BorderLayout());
272
// this.nextTabWindow.setSize(smallWidth, smallHeight);
273
// // Fix for issue 178 on Substance (multiple screens)
274
// this.nextTabWindow.setLocation((13 * screenWidth / 18)
275
// + virtualBounds.x, 7 * screenHeight / 18);
276
this.nextTabWindow.addMouseListener(new MouseAdapter() {
278
public void mouseClicked(MouseEvent e) {
279
SwingUtilities.invokeLater(new Runnable() {
282
// fix for issue 177 in Substance (disallowing selection
283
// of disabled tabs).
284
TabPreviewPainter tpp = LafWidgetUtilities2
285
.getTabPreviewPainter(currTabbedPane);
286
if (tpp.isSensitiveToEvents(currTabbedPane,
289
currTabbedPane.setSelectedIndex(nextTabIndex);
296
.addMouseWheelListener(new TabPagerMouseWheelListener());
298
this.recomputeBounds();
300
this.isVisible = false;
304
* Recomputes the bounds of tab pager windows.
306
private void recomputeBounds() {
307
Rectangle virtualBounds = new Rectangle();
308
GraphicsEnvironment ge = GraphicsEnvironment
309
.getLocalGraphicsEnvironment();
310
GraphicsDevice[] gds = ge.getScreenDevices();
311
for (int i = 0; i < gds.length; i++) {
312
GraphicsDevice gd = gds[i];
313
GraphicsConfiguration gc = gd.getDefaultConfiguration();
314
virtualBounds = virtualBounds.union(gc.getBounds());
317
int screenWidth = virtualBounds.width;
318
int screenHeight = virtualBounds.height;
320
int currWidth = screenWidth / 3;
321
int currHeight = screenHeight / 3;
322
this.currTabWindow.setSize(currWidth, currHeight);
323
// Fix for issue 178 on Substance (multiple screens)
324
this.currTabWindow.setLocation(currWidth + virtualBounds.x, currHeight);
326
int smallWidth = 2 * screenWidth / 9;
327
int smallHeight = 2 * screenHeight / 9;
328
this.prevTabWindow.setSize(smallWidth, smallHeight);
329
// Fix for issue 178 on Substance (multiple screens)
330
this.prevTabWindow.setLocation((screenWidth / 18) + virtualBounds.x,
331
7 * screenHeight / 18);
333
this.nextTabWindow.getContentPane().setLayout(new BorderLayout());
334
this.nextTabWindow.setSize(smallWidth, smallHeight);
335
// Fix for issue 178 on Substance (multiple screens)
336
this.nextTabWindow.setLocation((13 * screenWidth / 18)
337
+ virtualBounds.x, 7 * screenHeight / 18);
341
* Sets the tabbed pane on <code>this</code> tab pager manager.
344
* Tabbed pane to page.
346
private void setTabbedPane(JTabbedPane jtp) {
347
if (this.currTabbedPane == jtp)
349
this.currTabbedPane = jtp;
350
// this.smallPreviewMap.clear();
351
// this.regularPreviewMap.clear();
360
* if <code>true</code>, the tabs are flipped one page (tab)
361
* forward, if <code>false</code>, the tabs are flipped one page
364
public synchronized void page(JTabbedPane tabbedPane, boolean isForward) {
365
this.setTabbedPane(tabbedPane);
366
if (!this.isVisible) {
367
this.recomputeBounds();
368
this.currTabWindow.setVisible(true);
369
this.prevTabWindow.setVisible(true);
370
this.nextTabWindow.setVisible(true);
371
this.isVisible = true;
372
this.currTabIndex = this.currTabbedPane.getSelectedIndex();
375
int delta = isForward ? 1 : -1;
376
this.currTabIndex += delta;
377
if (this.currTabIndex == this.currTabbedPane.getTabCount())
378
this.currTabIndex = 0;
379
if (this.currTabIndex == -1)
380
this.currTabIndex = this.currTabbedPane.getTabCount() - 1;
382
this.nextTabIndex = this.currTabIndex + 1;
383
this.prevTabIndex = this.currTabIndex - 1;
384
if (this.nextTabIndex == this.currTabbedPane.getTabCount())
385
this.nextTabIndex = 0;
386
if (this.prevTabIndex == -1)
387
this.prevTabIndex = this.currTabbedPane.getTabCount() - 1;
389
TabPreviewThread.TabPreviewInfo currTabPreviewInfo = new TabPreviewThread.TabPreviewInfo();
390
currTabPreviewInfo.tabPane = this.currTabbedPane;
391
currTabPreviewInfo.tabIndexToPreview = this.currTabIndex;
392
currTabPreviewInfo.setPreviewWidth(this.currTabWindow.getWidth() - 4);
393
currTabPreviewInfo.setPreviewHeight(this.currTabWindow.getHeight() - 20);
394
currTabPreviewInfo.previewCallback = new TabPagerPreviewCallback(
395
this.currTabWindow, this.currTabbedPane, this.currTabIndex);
396
currTabPreviewInfo.initiator = this;
397
TabPreviewPainter previewPainter = LafWidgetUtilities2
398
.getTabPreviewPainter(currTabPreviewInfo.tabPane);
399
if ((previewPainter != null)
400
&& (previewPainter.hasPreviewWindow(this.currTabbedPane,
401
this.currTabIndex))) {
402
TabPreviewThread.getInstance().queueTabPreviewRequest(
406
TabPreviewThread.TabPreviewInfo prevTabPreviewInfo = new TabPreviewThread.TabPreviewInfo();
407
prevTabPreviewInfo.tabPane = this.currTabbedPane;
408
prevTabPreviewInfo.tabIndexToPreview = this.prevTabIndex;
409
prevTabPreviewInfo.setPreviewWidth(this.prevTabWindow.getWidth() - 4);
410
prevTabPreviewInfo.setPreviewHeight(this.prevTabWindow.getHeight() - 20);
411
prevTabPreviewInfo.previewCallback = new TabPagerPreviewCallback(
412
this.prevTabWindow, this.currTabbedPane, this.prevTabIndex);
413
prevTabPreviewInfo.initiator = this;
414
if ((previewPainter != null)
415
&& (previewPainter.hasPreviewWindow(this.currTabbedPane,
416
this.prevTabIndex))) {
417
TabPreviewThread.getInstance().queueTabPreviewRequest(
421
TabPreviewThread.TabPreviewInfo nextTabPreviewInfo = new TabPreviewThread.TabPreviewInfo();
422
nextTabPreviewInfo.tabPane = this.currTabbedPane;
423
nextTabPreviewInfo.tabIndexToPreview = this.nextTabIndex;
424
nextTabPreviewInfo.setPreviewWidth(this.nextTabWindow.getWidth() - 4);
425
nextTabPreviewInfo.setPreviewHeight(this.nextTabWindow.getHeight() - 20);
426
nextTabPreviewInfo.previewCallback = new TabPagerPreviewCallback(
427
this.nextTabWindow, this.currTabbedPane, this.nextTabIndex);
428
nextTabPreviewInfo.initiator = this;
429
if ((previewPainter != null)
430
&& (previewPainter.hasPreviewWindow(this.currTabbedPane,
431
this.nextTabIndex))) {
432
TabPreviewThread.getInstance().queueTabPreviewRequest(
439
* Flips the pages in the currently shown tabbed pane.
442
* if <code>true</code>, the tabs are flipped one page (tab)
443
* forward, if <code>false</code>, the tabs are flipped one page
446
public void page(boolean isForward) {
447
if (this.currTabbedPane == null)
449
this.page(this.currTabbedPane, isForward);
453
* Returns indication whether the tab pager windows are showing.
455
* @return <code>true</code> if the tab pager windows are visible,
456
* <code>false</code> otherwise.
458
public boolean isVisible() {
459
return this.isVisible;
463
* Hides the tab pager windows.
465
* @return The index of the center (current) tab.
467
public synchronized int hide() {
468
int result = this.isVisible ? this.currTabIndex : -1;
470
final Point currWindowLocation = this.currTabWindow.getLocation();
471
final Dimension currWindowSize = this.currTabWindow.getSize();
472
final Point nextWindowLocation = this.nextTabWindow.getLocation();
473
final Dimension nextWindowSize = this.nextTabWindow.getSize();
474
final Point prevWindowLocation = this.prevTabWindow.getLocation();
475
final Dimension prevWindowSize = this.prevTabWindow.getSize();
477
Timeline hideTabPagerTimeline = new Timeline(this.currTabbedPane);
478
AnimationConfigurationManager.getInstance().configureTimeline(
479
hideTabPagerTimeline);
480
hideTabPagerTimeline.addPropertyToInterpolate(Timeline
481
.<Rectangle> property("bounds").on(this.currTabWindow).from(
482
new Rectangle(currWindowLocation, currWindowSize)).to(
483
new Rectangle(currWindowLocation.x
484
+ currWindowSize.width / 2,
485
currWindowLocation.y + currWindowSize.height
487
hideTabPagerTimeline.addPropertyToInterpolate(Timeline
488
.<Rectangle> property("bounds").on(this.prevTabWindow).from(
489
new Rectangle(prevWindowLocation, prevWindowSize)).to(
490
new Rectangle(prevWindowLocation.x
491
+ prevWindowSize.width / 2,
492
prevWindowLocation.y + prevWindowSize.height
494
hideTabPagerTimeline.addPropertyToInterpolate(Timeline
495
.<Rectangle> property("bounds").on(this.nextTabWindow).from(
496
new Rectangle(nextWindowLocation, nextWindowSize)).to(
497
new Rectangle(nextWindowLocation.x
498
+ nextWindowSize.width / 2,
499
nextWindowLocation.y + nextWindowSize.height
501
hideTabPagerTimeline.addCallback(new UIThreadTimelineCallbackAdapter() {
503
public void onTimelineStateChanged(TimelineState oldState,
504
TimelineState newState, float durationFraction,
505
float timelinePosition) {
506
if ((oldState == TimelineState.DONE)
507
&& (newState == TimelineState.IDLE)) {
508
currTabWindow.setVisible(false);
509
currTabWindow.dispose();
510
prevTabWindow.setVisible(false);
511
prevTabWindow.dispose();
512
nextTabWindow.setVisible(false);
513
nextTabWindow.dispose();
518
// public void onTimelinePulse(float durationFraction,
519
// float timelinePosition) {
520
// int cx = currWindowLocation.x + currWindowSize.width / 2;
521
// int cy = currWindowLocation.y + currWindowSize.height / 2;
522
// int nWidth = (int) (currWindowSize.width * timelinePosition);
523
// int nHeight = (int) (currWindowSize.height * timelinePosition);
524
// currTabWindow.setBounds(cx - nWidth / 2, cy - nHeight / 2,
527
// cx = prevWindowLocation.x + prevWindowSize.width / 2;
528
// cy = prevWindowLocation.y + prevWindowSize.height / 2;
529
// nWidth = (int) (prevWindowSize.width * timelinePosition);
530
// nHeight = (int) (prevWindowSize.height * timelinePosition);
531
// prevTabWindow.setBounds(cx - nWidth / 2, cy - nHeight / 2,
534
// cx = nextWindowLocation.x + nextWindowSize.width / 2;
535
// cy = nextWindowLocation.y + nextWindowSize.height / 2;
536
// nWidth = (int) (nextWindowSize.width * timelinePosition);
537
// nHeight = (int) (nextWindowSize.height * timelinePosition);
538
// nextTabWindow.setBounds(cx - nWidth / 2, cy - nHeight / 2,
541
// currTabWindow.getRootPane().doLayout();
542
// currTabWindow.repaint();
544
// nextTabWindow.getRootPane().doLayout();
545
// nextTabWindow.repaint();
547
// prevTabWindow.getRootPane().doLayout();
548
// prevTabWindow.repaint();
551
hideTabPagerTimeline.play();
553
this.isVisible = false;
558
* Resets the internal caches.
560
public static void reset() {
561
// TabPagerManager.instance.regularPreviewMap.clear();
562
// TabPagerManager.instance.smallPreviewMap.clear();