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.*;
34
import java.awt.image.BufferedImage;
38
import org.pushingpixels.lafwidget.*;
39
import org.pushingpixels.lafwidget.animation.AnimationConfigurationManager;
40
import org.pushingpixels.lafwidget.tabbed.TabPreviewThread.TabPreviewInfo;
41
import org.pushingpixels.trident.Timeline;
44
* Tab preview window. Is displayed when the mouse hovers over relevant
47
* @author Kirill Grouchnikov
49
public class TabPreviewWindow extends JWindow implements ActionListener {
50
public static final class PreviewLabel extends JLabel {
53
private PreviewLabel(Icon image) {
57
public void setAlpha(float alpha) {
63
protected void paintComponent(Graphics g) {
64
Graphics2D g2 = (Graphics2D) g.create();
65
g2.setComposite(AlphaComposite.SrcOver.derive(this.alpha));
66
super.paintComponent(g2);
72
* Singleton instance of tab preview window.
74
protected static TabPreviewWindow instance;
77
* Information on the current tab preview request.
79
protected static TabPreviewInfo currTabPreviewInfo;
82
* Currently running timer task.
84
protected static Timer currTabPreviewTimer;
87
* Returns the singleton instance of tab preview window.
89
* @return The singleton instance of tab preview window.
91
public static synchronized TabPreviewWindow getInstance() {
92
if (TabPreviewWindow.instance == null) {
93
TabPreviewWindow.instance = new TabPreviewWindow();
94
TabPreviewWindow.instance.setLayout(new BorderLayout());
95
// instance.addHierarchyListener(new HierarchyListener() {
96
// public void hierarchyChanged(HierarchyEvent e) {
97
// System.err.println(e.getID());
101
return TabPreviewWindow.instance;
105
* Posts a preview request for a tab component in the specified tabbed pane.
110
* Index of the tab to preview.
112
public synchronized void postPreviewRequest(JTabbedPane tabPane,
114
TabPreviewPainter previewPainter = LafWidgetUtilities2
115
.getTabPreviewPainter(tabPane);
116
if ((previewPainter == null)
117
|| (!previewPainter.hasPreviewWindow(tabPane, tabIndex)))
120
// check if already showing
121
if (currTabPreviewInfo != null) {
122
if ((currTabPreviewInfo.tabPane == tabPane)
123
&& (currTabPreviewInfo.tabIndexToPreview == tabIndex))
127
if (currTabPreviewTimer != null) {
128
if (currTabPreviewTimer.isRunning())
129
currTabPreviewTimer.stop();
132
Dimension previewDim = previewPainter.getPreviewWindowDimension(
134
int pWidth = previewDim.width;
135
int pHeight = previewDim.height;
136
Component tabComponent = tabPane.getComponentAt(tabIndex);
137
if (tabComponent != null) {
138
int width = tabComponent.getWidth();
139
int height = tabComponent.getHeight();
140
double ratio = (double) width / (double) height;
141
double pRatio = (double) previewDim.width
142
/ (double) previewDim.height;
143
if (pRatio > ratio) {
144
pWidth = (int) (pHeight * ratio);
146
pHeight = (int) (pWidth / ratio);
150
currTabPreviewInfo = new TabPreviewInfo();
151
currTabPreviewInfo.tabPane = tabPane;
152
currTabPreviewInfo.tabIndexToPreview = tabIndex;
153
currTabPreviewInfo.setPreviewWidth(pWidth);
154
currTabPreviewInfo.setPreviewHeight(pHeight);
155
currTabPreviewInfo.initiator = tabPane;
156
currTabPreviewInfo.previewCallback = new TabPreviewThread.TabPreviewCallback() {
160
* @seeorg.pushingpixels.lafwidget.tabbed.TabPreviewThread.
161
* TabPreviewCallback #start(javax.swing.JTabbedPane, int,
163
* .lafwidget.tabbed.TabPreviewThread.TabPreviewInfo)
166
public void start(JTabbedPane tabPane, int tabCount,
167
TabPreviewInfo tabPreviewInfo) {
168
// Nothing to do since the callback was registered
169
// for a specific tab.
175
* @seeorg.pushingpixels.lafwidget.tabbed.TabPreviewThread.
176
* TabPreviewCallback #offer(javax.swing.JTabbedPane, int,
177
* java.awt.image.BufferedImage)
180
public void offer(JTabbedPane tabPane, int tabIndex,
181
BufferedImage componentSnap) {
182
if (currTabPreviewInfo == null) {
183
// has since been cancelled
186
if ((tabPane != currTabPreviewInfo.tabPane)
187
|| (tabIndex != currTabPreviewInfo.tabIndexToPreview)) {
188
// has since been cancelled
191
Rectangle previewScreenRectangle = TabPreviewWindow.this
192
.getPreviewWindowScreenRect(tabPane, tabIndex,
193
currTabPreviewInfo.getPreviewWidth(),
194
currTabPreviewInfo.getPreviewHeight());
195
TabPreviewWindow.this.getContentPane().removeAll();
196
final JLabel previewLabel = new PreviewLabel(new ImageIcon(
198
TabPreviewWindow.this
199
.addComponentListener(new ComponentAdapter() {
201
public void componentShown(ComponentEvent e) {
202
previewLabel.setVisible(true);
203
// Start fading-in of the image.
204
Timeline timeline = new Timeline(previewLabel);
205
AnimationConfigurationManager.getInstance()
206
.configureTimeline(timeline);
207
timeline.addPropertyToInterpolate("alpha",
212
// previewLabel.setBorder(new SubstanceBorder());
213
TabPreviewWindow.this.getContentPane().add(previewLabel,
214
BorderLayout.CENTER);
215
TabPreviewWindow.this.setSize(previewScreenRectangle.width,
216
previewScreenRectangle.height);
217
TabPreviewWindow.this.setLocation(previewScreenRectangle.x,
218
previewScreenRectangle.y);
219
previewLabel.setVisible(false);
220
TabPreviewWindow.this.setVisible(true);
224
int extraDelay = previewPainter.getPreviewWindowExtraDelay(tabPane,
226
if (extraDelay < 0) {
227
throw new IllegalArgumentException(
228
"Extra delay for tab preview must be non-negative");
230
currTabPreviewTimer = new Timer(2000 + extraDelay, this);
231
currTabPreviewTimer.setRepeats(false);
232
currTabPreviewTimer.start();
239
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
242
public void actionPerformed(ActionEvent e) {
243
if (currTabPreviewInfo == null)
246
// If we are here - the delay timer has expired.
248
// System.err.println("Post " + tabIndex);
249
TabPreviewPainter previewPainter = LafWidgetUtilities2
250
.getTabPreviewPainter(currTabPreviewInfo.tabPane);
251
if ((previewPainter == null)
252
|| (!previewPainter.hasPreviewWindow(
253
currTabPreviewInfo.tabPane,
254
currTabPreviewInfo.tabIndexToPreview)))
257
// Queue the request with the preview thread.
258
TabPreviewThread.getInstance().queueTabPreviewRequest(
263
* Returns the screen rectangle for the preview window.
273
* @return The screen rectangle for the preview window.
275
protected Rectangle getPreviewWindowScreenRect(JTabbedPane tabPane,
276
int tabIndex, int pWidth, int pHeight) {
277
LafWidgetSupport lafSupport = LafWidgetRepository.getRepository()
280
Rectangle relative = lafSupport.getTabRectangle(tabPane, tabIndex);
281
if (relative == null)
284
Rectangle result = new Rectangle(pWidth, pHeight);
285
boolean ltr = tabPane.getComponentOrientation().isLeftToRight();
287
if (tabPane.getTabPlacement() != SwingConstants.BOTTOM) {
288
result.setLocation(relative.x, relative.y + relative.height);
290
result.setLocation(relative.x, relative.y - pHeight);
293
if (tabPane.getTabPlacement() != SwingConstants.BOTTOM) {
294
result.setLocation(relative.x + relative.width - pWidth,
295
relative.y + relative.height);
297
result.setLocation(relative.x + relative.width - pWidth,
298
relative.y - pHeight);
301
int dx = tabPane.getLocationOnScreen().x;
302
int dy = tabPane.getLocationOnScreen().y;
306
// Fix to make the tab preview window stay in screen bounds
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
if (result.x + result.width > (virtualBounds.width - 1)) {
318
result.x -= (result.x + result.width - virtualBounds.width + 1);
320
if (result.y + result.height > (virtualBounds.height - 1)) {
321
result.y -= (result.y + result.height - virtualBounds.height + 1);
323
if (result.x < virtualBounds.x)
324
result.x = virtualBounds.x + 1;
325
if (result.y < virtualBounds.y)
326
result.y = virtualBounds.y + 1;
331
* Cancels the currently pending preview request.
333
public static synchronized void cancelPreviewRequest() {
334
// System.err.println("Cancel");
335
currTabPreviewInfo = null;
336
if ((currTabPreviewTimer != null) && currTabPreviewTimer.isRunning()) {
337
currTabPreviewTimer.stop();
338
currTabPreviewTimer = null;
340
if (instance != null)
341
instance.dispose();// setVisible(false);