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.image.BufferedImage;
35
import java.util.List;
37
import javax.swing.JTabbedPane;
38
import javax.swing.SwingUtilities;
40
import org.pushingpixels.lafwidget.LafWidgetUtilities;
41
import org.pushingpixels.lafwidget.LafWidgetUtilities2;
42
import org.pushingpixels.lafwidget.utils.DeltaQueue;
43
import org.pushingpixels.lafwidget.utils.TrackableThread;
44
import org.pushingpixels.lafwidget.utils.DeltaQueue.DeltaMatcher;
45
import org.pushingpixels.lafwidget.utils.DeltaQueue.Deltable;
48
* Thread for running the tab preview requests.
50
* @author Kirill Grouchnikov
52
public class TabPreviewThread extends TrackableThread {
54
* Indication whether a stop request has been issued on <code>this</code>
57
private boolean stopRequested;
60
* Queue of preview requests. Contains {@link TabPreviewInfo}s.
62
protected DeltaQueue previewQueue;
65
* Information for previewing a tabbed pane.
67
* @author Kirill Grouchnikov
69
public static class TabPreviewInfo extends DeltaQueue.Deltable {
73
public JTabbedPane tabPane;
76
* Callback for passing the preview thumbnail once it is computed.
78
public TabPreviewThread.TabPreviewCallback previewCallback;
81
* Width of the preview thumbnail.
83
private int previewWidth;
86
* Height of the preview thumbnail.
88
private int previewHeight;
91
* Indicates whether all tabs in the {@link #tabPane} should be
94
public boolean toPreviewAllTabs;
97
* If {@link #toPreviewAllTabs} is <code>false</code>, contains the
98
* index of the tab to be previewed.
100
public int tabIndexToPreview;
103
* Points to the preview initiator.
105
public Object initiator;
107
public void setPreviewWidth(int previewWidth) {
108
this.previewWidth = previewWidth;
111
public int getPreviewWidth() {
115
public void setPreviewHeight(int previewHeight) {
116
this.previewHeight = previewHeight;
119
public int getPreviewHeight() {
120
return previewHeight;
125
* Interface for offering the tab preview image once it has been computed.
127
* @author Kirill Grouchnikov
129
public static interface TabPreviewCallback {
131
* Starts the current cycle of
132
* {@link #offer(JTabbedPane, int, BufferedImage)} calls. This can be
133
* used by the implementing class to revalidate itself in case the tab
134
* count in the specified tabbed pane has changed since the previous
135
* cycle of {@link #offer(JTabbedPane, int, BufferedImage)} call.
140
* Tab count in the tabbed pane.
141
* @param tabPreviewInfo
142
* Tab preview info. Can be changed in the implementation
145
public void start(JTabbedPane tabPane, int tabCount,
146
TabPreviewInfo tabPreviewInfo);
149
* Offers the preview image (thumbnail) of a tab in the specified tabbed
156
* @param componentSnap
159
public void offer(JTabbedPane tabPane, int tabIndex,
160
BufferedImage componentSnap);
164
* Simple constructor. Defined private for singleton.
166
* @see #getInstance()
168
private TabPreviewThread() {
170
this.setName("Laf-Widget tab preview");
171
this.stopRequested = false;
172
this.previewQueue = new DeltaQueue();
178
* @see java.lang.Thread#run()
182
while (!this.stopRequested) {
184
// System.out.println(System.currentTimeMillis() + " Polling");
186
List<Deltable> expired = this.dequeueTabPreviewRequest(delay);
187
for (Deltable dExpired : expired) {
188
final TabPreviewInfo nextPreviewInfo = (TabPreviewInfo) dExpired;
189
final JTabbedPane jtp = nextPreviewInfo.tabPane;
192
final TabPreviewPainter previewPainter = LafWidgetUtilities2
193
.getTabPreviewPainter(jtp);
194
final int tabCount = jtp.getTabCount();
196
// SwingUtilities.invokeLater(new Runnable() {
197
// public void run() {
198
// final TabPreviewInfo copyPreviewInfo = new
200
// copyPreviewInfo.initiator = nextPreviewInfo.initiator;
201
// copyPreviewInfo.previewCallback =
202
// nextPreviewInfo.previewCallback;
203
// copyPreviewInfo.previewHeight =
204
// nextPreviewInfo.previewHeight;
205
// copyPreviewInfo.previewWidth =
206
// nextPreviewInfo.previewWidth;
207
// copyPreviewInfo.tabIndexToPreview =
208
// nextPreviewInfo.tabIndexToPreview;
209
// copyPreviewInfo.tabPane = nextPreviewInfo.tabPane;
210
// copyPreviewInfo.toPreviewAllTabs =
211
// nextPreviewInfo.toPreviewAllTabs;
213
if (nextPreviewInfo.toPreviewAllTabs) {
214
SwingUtilities.invokeLater(new Runnable() {
217
// The call to start() is only relevant for the
218
// preview of all tabs.
219
nextPreviewInfo.previewCallback.start(jtp, jtp
220
.getTabCount(), nextPreviewInfo);
224
for (int i = 0; i < tabCount; i++) {
226
SwingUtilities.invokeLater(new Runnable() {
229
getSingleTabPreviewImage(jtp,
230
previewPainter, nextPreviewInfo,
236
SwingUtilities.invokeLater(new Runnable() {
239
getSingleTabPreviewImage(jtp, previewPainter,
241
nextPreviewInfo.tabIndexToPreview);
248
if (previewPainter.toUpdatePeriodically(jtp)) {
249
TabPreviewInfo cyclePreviewInfo = new TabPreviewInfo();
250
// copy all the fields from the currently processed
252
cyclePreviewInfo.tabPane = nextPreviewInfo.tabPane;
253
cyclePreviewInfo.tabIndexToPreview = nextPreviewInfo.tabIndexToPreview;
254
cyclePreviewInfo.toPreviewAllTabs = nextPreviewInfo.toPreviewAllTabs;
255
cyclePreviewInfo.previewCallback = nextPreviewInfo.previewCallback;
256
cyclePreviewInfo.setPreviewWidth(nextPreviewInfo.getPreviewWidth());
257
cyclePreviewInfo.setPreviewHeight(nextPreviewInfo.getPreviewHeight());
258
cyclePreviewInfo.initiator = nextPreviewInfo.initiator;
260
// schedule it to app-specific delay
261
cyclePreviewInfo.setDelta(previewPainter
262
.getUpdateCycle(cyclePreviewInfo.tabPane));
264
// queue the new request
265
this.queueTabPreviewRequest(cyclePreviewInfo);
269
} catch (InterruptedException ie) {
270
ie.printStackTrace();
273
// System.out.println("Tab preview finished");
277
* Computes and offers the preview thumbnail for a single tab.
281
* @param previewPainter
282
* Tab preview painter.
286
* Index of the tab to preview.
288
protected void getSingleTabPreviewImage(final JTabbedPane tabPane,
289
final TabPreviewPainter previewPainter,
290
final TabPreviewInfo previewInfo, final int tabIndex) {
291
int pWidth = previewInfo.getPreviewWidth();
292
int pHeight = previewInfo.getPreviewHeight();
293
final BufferedImage previewImage = new BufferedImage(pWidth, pHeight,
294
BufferedImage.TYPE_INT_ARGB);
295
Graphics2D gr = previewImage.createGraphics();
296
Component comp = tabPane.getComponentAt(tabIndex);
298
if (previewPainter.hasPreview(tabPane, tabIndex)) {
299
Map<Component, Boolean> dbSnapshot = new HashMap<Component, Boolean>();
300
LafWidgetUtilities.makePreviewable(comp, dbSnapshot);
301
previewPainter.previewTab(tabPane, tabIndex, gr, 0, 0, pWidth,
303
LafWidgetUtilities.restorePreviewable(comp, dbSnapshot);
305
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
306
RenderingHints.VALUE_ANTIALIAS_ON);
307
gr.setColor(Color.red);
308
gr.setStroke(new BasicStroke(Math.max(5.0f, Math.min(pWidth,
310
gr.drawLine(0, 0, pWidth, pHeight);
311
gr.drawLine(0, pHeight, pWidth, 0);
315
if (previewInfo.previewCallback != null) {
316
SwingUtilities.invokeLater(new Runnable() {
319
previewInfo.previewCallback.offer(tabPane, tabIndex,
327
* Queues the request to preview one or all tabs in the specified tabbed
328
* pane. Once the request is queued, the thread will pick it up from the
329
* queue (in at most 500 milliseconds in the current implementation) and
330
* start processing it. For each tab (if all tabs were requested to be
331
* previewed), the preview thumbnail will be offered to the relevant
332
* callback. This allows to maintain the interactivity of the application
333
* while generating the preview thumbnails for the tab overview dialog (see
334
* {@link TabOverviewDialog}).
339
public void queueTabPreviewRequest(TabPreviewInfo previewInfo) {
340
this.previewQueue.queue(previewInfo);
344
* Cancels all tab preview requests that were initiated by the specified
350
public void cancelTabPreviewRequests(final Object initiator) {
351
DeltaMatcher matcher = new DeltaMatcher() {
353
public boolean matches(Deltable deltable) {
354
TabPreviewInfo currInfo = (TabPreviewInfo) deltable;
355
return (currInfo.initiator == initiator);
358
this.previewQueue.removeMatching(matcher);
362
* Removes the tab preview requests that have at most specified delay left.
366
* @return The list of all tab preview requests that have at most specified
369
private List<Deltable> dequeueTabPreviewRequest(int delay) {
370
return this.previewQueue.dequeue(delay);
376
* @see org.pushingpixels.lafwidget.utils.TrackableThread#requestStop()
379
protected void requestStop() {
380
this.stopRequested = true;
381
TabPreviewThread.tabPreviewThread = null;
385
* The preview thread.
387
private static TabPreviewThread tabPreviewThread;
390
* Returns the singleton instance of the tab preview thread.
392
* @return The singleton instance of the tab preview thread.
394
public static synchronized TabPreviewThread getInstance() {
395
if (TabPreviewThread.tabPreviewThread == null) {
396
// System.err.println("Allocating ");
397
// Thread.dumpStack();
398
TabPreviewThread.tabPreviewThread = new TabPreviewThread();
399
TabPreviewThread.tabPreviewThread.start();
401
return TabPreviewThread.tabPreviewThread;
405
* Returns indication whether tab preview thread is running.
407
* @return <code>true</code> if the tab preview thread is running,
408
* <code>false</code> otherwise.
410
public static synchronized boolean instanceRunning() {
411
return (TabPreviewThread.tabPreviewThread != null);
414
// public void dump() {
415
// System.out.println("Dump");
416
// for (int i = 0; i < this.previewRequests.size(); i++) {
417
// TabPreviewInfo tpi = (TabPreviewInfo) this.previewRequests.get(i);
418
// System.out.println("\t" + tpi.tabIndexToPreview + " -> "
419
// + tpi.timeToExpire);
423
// public static void main(String[] args) {
424
// TabPreviewThread tpt = new TabPreviewThread();
425
// TabPreviewInfo tpi11 = new TabPreviewInfo();
426
// tpi11.tabIndexToPreview = 11;
427
// tpi11.timeToExpire = 100;
428
// tpt.queueTabPreviewRequest(tpi11);
431
// TabPreviewInfo tpi12 = new TabPreviewInfo();
432
// tpi12.tabIndexToPreview = 12;
433
// tpi12.timeToExpire = 100;
434
// tpt.queueTabPreviewRequest(tpi12);
437
// TabPreviewInfo tpi21 = new TabPreviewInfo();
438
// tpi21.tabIndexToPreview = 21;
439
// tpi21.timeToExpire = 200;
440
// tpt.queueTabPreviewRequest(tpi21);
443
// TabPreviewInfo tpi31 = new TabPreviewInfo();
444
// tpi31.tabIndexToPreview = 31;
445
// tpi31.timeToExpire = 300;
446
// tpt.queueTabPreviewRequest(tpi31);
449
// TabPreviewInfo tpi13 = new TabPreviewInfo();
450
// tpi13.tabIndexToPreview = 13;
451
// tpi13.timeToExpire = 100;
452
// tpt.queueTabPreviewRequest(tpi13);
455
// TabPreviewInfo tpi22 = new TabPreviewInfo();
456
// tpi22.tabIndexToPreview = 22;
457
// tpi22.timeToExpire = 200;
458
// tpt.queueTabPreviewRequest(tpi22);
461
// TabPreviewInfo tpi25 = new TabPreviewInfo();
462
// tpi25.tabIndexToPreview = 25;
463
// tpi25.timeToExpire = 250;
464
// tpt.queueTabPreviewRequest(tpi25);
467
// TabPreviewInfo tpi51 = new TabPreviewInfo();
468
// tpi51.tabIndexToPreview = 51;
469
// tpi51.timeToExpire = 500;
470
// tpt.queueTabPreviewRequest(tpi51);
473
// List gr150 = tpt.dequeueTabPreviewRequest(2500);
474
// System.out.println("Dump 150");
475
// for (int i = 0; i < gr150.size(); i++) {
476
// TabPreviewInfo tpi = (TabPreviewInfo) gr150.get(i);
477
// System.out.println("\t" + tpi.tabIndexToPreview);
481
// TrackableThread.requestStopAllThreads();
b'\\ No newline at end of file'