Source code: com/port80/swt/apps/ImageViewer4x.java
1 package com.port80.swt.apps;
2
3 import java.io.InputStream;
4 import java.net.URL;
5 import java.text.MessageFormat;
6
7 import org.eclipse.swt.SWT;
8 import org.eclipse.swt.SWTException;
9 import org.eclipse.swt.custom.StyledText;
10 import org.eclipse.swt.events.ControlAdapter;
11 import org.eclipse.swt.events.ControlEvent;
12 import org.eclipse.swt.events.DisposeEvent;
13 import org.eclipse.swt.events.DisposeListener;
14 import org.eclipse.swt.events.MouseEvent;
15 import org.eclipse.swt.events.MouseMoveListener;
16 import org.eclipse.swt.events.PaintEvent;
17 import org.eclipse.swt.events.PaintListener;
18 import org.eclipse.swt.events.SelectionAdapter;
19 import org.eclipse.swt.events.SelectionEvent;
20 import org.eclipse.swt.events.ShellAdapter;
21 import org.eclipse.swt.events.ShellEvent;
22 import org.eclipse.swt.graphics.Color;
23 import org.eclipse.swt.graphics.Cursor;
24 import org.eclipse.swt.graphics.Font;
25 import org.eclipse.swt.graphics.GC;
26 import org.eclipse.swt.graphics.Image;
27 import org.eclipse.swt.graphics.ImageData;
28 import org.eclipse.swt.graphics.ImageLoader;
29 import org.eclipse.swt.graphics.RGB;
30 import org.eclipse.swt.graphics.Rectangle;
31 import org.eclipse.swt.layout.GridData;
32 import org.eclipse.swt.layout.GridLayout;
33 import org.eclipse.swt.layout.RowLayout;
34 import org.eclipse.swt.widgets.Button;
35 import org.eclipse.swt.widgets.Canvas;
36 import org.eclipse.swt.widgets.Combo;
37 import org.eclipse.swt.widgets.Composite;
38 import org.eclipse.swt.widgets.Dialog;
39 import org.eclipse.swt.widgets.Display;
40 import org.eclipse.swt.widgets.FileDialog;
41 import org.eclipse.swt.widgets.Label;
42 import org.eclipse.swt.widgets.Menu;
43 import org.eclipse.swt.widgets.MenuItem;
44 import org.eclipse.swt.widgets.MessageBox;
45 import org.eclipse.swt.widgets.Sash;
46 import org.eclipse.swt.widgets.ScrollBar;
47 import org.eclipse.swt.widgets.Shell;
48 import org.eclipse.swt.widgets.Text;
49
50 import com.port80.eclipse.util.UIUtil;
51 import com.port80.util.Msg;
52 import com.port80.util.Sprint;
53 import com.port80.util.SystemWatch;
54
55 /**
56 * An image viewer using swt.
57 *
58 * <li>A GC can be created on an Image and then use it to draw into the Image.</li>
59 * <li>To display very large image, pixel data is loaded into ImageData and then
60 * tiles of the data are used to create Image's for display.</li>
61 *
62 * This is an experimental version modified from ImageViewer3. To reduce time lag between
63 * clear and update during scroll, a large Image is drawn to the display, so that it would use
64 * scroll in the offscreen image instead of wait for update from the PaintEvent. The PaintEvent
65 * then fill up the offscreen section that are scrolled away.
66 *
67 * Update is much faster. There are no visiable blank region for large scroll or jump scroll.
68 * But the update of the larger offscreen Image make scrolling more jumpy even for small scroll.
69 * So it may be better off simply use the ImageViewer3 approach if really needed to handle
70 * very large image.
71 *
72 * @bugs A mess, nothing work except a little bit of horizontal scrolling.
73 * @author chrisl
74 */
75 public class ImageViewer4x {
76
77 ////////////////////////////////////////////////////////////////////////
78
79 private static final String NAME = "ImageViewer4x";
80
81 private static final String[] IMAGE_SUFFICES =
82 new String[] {
83 "*.bmp; *.gif; *.ico; *.jpg; *.pcx; *.png; *.tif",
84 "*.bmp",
85 "*.gif",
86 "*.ico",
87 "*.jpg",
88 "*.pcx",
89 "*.png",
90 "*.tif" };
91
92 private static final String[] IMAGE_FILTER_NAMES =
93 new String[] {
94 i18n("All_images") + " (bmp, gif, ico, jpg, pcx, png, tif)",
95 "BMP (*.bmp)",
96 "GIF (*.gif)",
97 "ICO (*.ico)",
98 "JPEG (*.jpg)",
99 "PCX (*.pcx)",
100 "PNG (*.png)",
101 "TIFF (*.tif)" };
102
103 ////////////////////////////////////////////////////////////////////////
104
105 private Display fDisplay;
106 private Shell fShell;
107 private Font fFont;
108 private Color fWhiteColor, fBlackColor, fLightColor;
109 private Cursor fCrossCursor;
110 private Image fImage;
111 private ImageData[] fImageDatas;
112 private ImageData fImageData; // the currently-displayed image data
113 private int fImageDataIndex;
114 private double fScale;
115 private int fX, fY; // Top left corner of area being displayed.
116 private int fImageX, fImageY, fImageW, fImageH; // Top left corner of fImage.
117 private int fClientW, fClientH;
118 private String fLastPath;
119 private boolean fTransparent, fShowMask;
120 private boolean fIncremental;
121 //
122 private long fLoadTime;
123 private String fFilename;
124 //
125 // Widgets
126 private Combo fScaleCombo;
127 private Canvas fImageCanvas;
128 private Sash fSash;
129 private Label fDataLabel, fStatusLabel;
130 private StyledText fDataText;
131 private Label fInfoLabel;
132
133 ////////////////////////////////////////////////////////////////////////
134
135 public static void main(String[] args) {
136
137 Display display = new Display();
138 ImageViewer4x app = new ImageViewer4x();
139 Shell shell = app.open(display);
140
141 while (!shell.isDisposed())
142 if (!display.readAndDispatch())
143 display.sleep();
144 display.dispose();
145 }
146
147 ////////////////////////////////////////////////////////////////////////
148
149 public Shell open(Display display) {
150
151 // Create a window and set its title.
152 this.fDisplay = display;
153 this.fShell = new Shell(fDisplay);
154
155 // Create colors and fonts.
156 fWhiteColor = new Color(display, 255, 255, 255);
157 fBlackColor = new Color(display, 0, 0, 0);
158 fLightColor = new Color(display, 0xd8, 0xd8, 0xd8);
159 fFont = new Font(display, "verdana", 11, 0);
160 fCrossCursor = new Cursor(display, SWT.CURSOR_CROSS);
161
162 fShell.setText(NAME);
163
164 // Hook resize and dispose listeners.
165 fShell.addControlListener(new ControlAdapter() {
166 public void controlResized(ControlEvent event) {
167 resizeShell(event);
168 }
169 });
170 fShell.addShellListener(new ShellAdapter() {
171 public void shellClosed(ShellEvent e) {
172 e.doit = true;
173 }
174 });
175 fShell.addDisposeListener(new DisposeListener() {
176 public void widgetDisposed(DisposeEvent e) {
177 dispose();
178 }
179 });
180
181 // Add a menu bar and widgets.
182 createMenuBar();
183 createWidgets();
184 fShell.pack();
185
186 // Create a GC for drawing, and hook the listener to dispose it.
187 // fImageCanvasGC = new GC(fImageCanvas);
188 // fImageCanvas.addDisposeListener(new DisposeListener() {
189 // public void widgetDisposed(DisposeEvent e) {
190 // fImageCanvasGC.dispose();
191 // }
192 // });
193
194 // Open the window
195 fShell.open();
196 return fShell;
197 }
198
199 void dispose() {
200 // Clean up.
201 fWhiteColor.dispose();
202 fBlackColor.dispose();
203 fLightColor.dispose();
204 fFont.dispose();
205 fCrossCursor.dispose();
206 }
207
208 Shell getShell() {
209 return fShell;
210 }
211
212 Display getDisplay() {
213 return fDisplay;
214 }
215
216 void createWidgets() {
217 // Add the widgets to the shell in a grid layout.
218 GridLayout layout = new GridLayout();
219 layout.marginHeight = 0;
220 layout.numColumns = 2;
221 fShell.setLayout(layout);
222
223 // Add a composite to contain some control widgets across the top.
224 Composite controls = new Composite(fShell, SWT.NULL);
225 RowLayout rowLayout = new RowLayout();
226 rowLayout.marginTop = 2;
227 rowLayout.marginBottom = 2;
228 rowLayout.spacing = 8;
229 controls.setLayout(rowLayout);
230 controls.setLayoutData(UIUtil.fillCell(1, 1, false, false));
231
232 // RowLayout is top aligned. To make label text at middle, need to set label height.
233 fInfoLabel = new Label(fShell, SWT.NULL);
234 fInfoLabel.setToolTipText("Image info");
235 fInfoLabel.setText("Image info");
236 fInfoLabel.setLayoutData(UIUtil.fillCell(1, 1, true, false));
237 // Combo to change the x scale.
238 String[] values =
239 {
240 "5 %",
241 "10 %",
242 "25 %",
243 "33 %",
244 "50 %",
245 "75 %",
246 "100 %",
247 "125 %",
248 "133 %",
249 "150 %",
250 "175 %",
251 "200 %",
252 "300 %",
253 "500 %",
254 "750 %",
255 "1000 %",
256 };
257 fScaleCombo = new Combo(controls, SWT.DROP_DOWN);
258 for (int i = 0; i < values.length; i++) {
259 fScaleCombo.add(values[i]);
260 }
261 fScaleCombo.setToolTipText("Scale");
262 fScaleCombo.select(fScaleCombo.indexOf("100 %"));
263 fScaleCombo.addSelectionListener(new SelectionAdapter() {
264 public void widgetSelected(SelectionEvent event) {
265 scaleImage();
266 }
267 });
268
269 // Canvas to show the image.
270 fImageCanvas = new Canvas(fShell, SWT.V_SCROLL | SWT.H_SCROLL | SWT.NO_REDRAW_RESIZE);
271 fImageCanvas.setBackground(fWhiteColor);
272 fImageCanvas.setCursor(fCrossCursor);
273 fImageCanvas.setLayoutData(UIUtil.fillCell(15, 2, 1024, 768, true, true));
274 fImageCanvas.addPaintListener(new PaintListener() {
275 public void paintControl(PaintEvent event) {
276 //if (fImage != null)
277 paintImage(event);
278 }
279 });
280 fImageCanvas.addMouseMoveListener(new MouseMoveListener() {
281 public void mouseMove(MouseEvent event) {
282 showColorAt(event.x, event.y);
283 }
284 });
285
286 // Set up the image canvas scroll bars.
287 ScrollBar horizontal = fImageCanvas.getHorizontalBar();
288 horizontal.setVisible(true);
289 horizontal.setMinimum(0);
290 horizontal.setEnabled(false);
291 horizontal.addSelectionListener(new SelectionAdapter() {
292 public void widgetSelected(SelectionEvent event) {
293 scrollHorizontally((ScrollBar) event.widget);
294 }
295 });
296 ScrollBar vertical = fImageCanvas.getVerticalBar();
297 vertical.setVisible(true);
298 vertical.setMinimum(0);
299 vertical.setEnabled(false);
300 vertical.addSelectionListener(new SelectionAdapter() {
301 public void widgetSelected(SelectionEvent event) {
302 scrollVertically((ScrollBar) event.widget);
303 }
304 });
305
306 // Label to show status and cursor location in image.
307 fStatusLabel = new Label(fShell, SWT.NULL);
308 fStatusLabel.setText("");
309 fStatusLabel.setLayoutData(UIUtil.fillCell(1, 2, true, false));
310 }
311
312 Menu createMenuBar() {
313 // Menu bar.
314 Menu menuBar = new Menu(fShell, SWT.BAR);
315 fShell.setMenuBar(menuBar);
316 createFileMenu(menuBar);
317 return menuBar;
318 }
319
320 void createFileMenu(Menu menuBar) {
321 // File menu
322 MenuItem item = new MenuItem(menuBar, SWT.CASCADE);
323 item.setText("File");
324 Menu fileMenu = new Menu(fShell, SWT.DROP_DOWN);
325 item.setMenu(fileMenu);
326
327 item = new MenuItem(fileMenu, SWT.NULL);
328 item.setText("&Open");
329 item.setAccelerator(SWT.CTRL + 'O');
330 item.addSelectionListener(new SelectionAdapter() {
331 public void widgetSelected(SelectionEvent event) {
332 openFile();
333 }
334 });
335
336 item = new MenuItem(fileMenu, SWT.NULL);
337 item.setText("Open &URL");
338 item.setAccelerator(SWT.CTRL + 'U');
339 item.addSelectionListener(new SelectionAdapter() {
340 public void widgetSelected(SelectionEvent event) {
341 openURL();
342 }
343 });
344
345 new MenuItem(fileMenu, SWT.SEPARATOR);
346
347 // File -> Exit
348 item = new MenuItem(fileMenu, SWT.NULL);
349 item.setText("Exit");
350 item.addSelectionListener(new SelectionAdapter() {
351 public void widgetSelected(SelectionEvent event) {
352 getShell().close();
353 }
354 });
355
356 // Test menu
357 item = new MenuItem(menuBar, SWT.CASCADE);
358
359 }
360
361 ////////////////////////////////////////////////////////////////////////
362
363 void openFile() {
364 resetScaleCombo();
365
366 // Get the user to choose an image file.
367 FileDialog fileChooser = new FileDialog(fShell, SWT.OPEN);
368 if (fLastPath != null)
369 fileChooser.setFilterPath(fLastPath);
370 fileChooser.setFilterExtensions(IMAGE_SUFFICES);
371 fileChooser.setFilterNames(IMAGE_FILTER_NAMES);
372 String filename = fileChooser.open();
373 fLastPath = fileChooser.getFilterPath();
374 if (filename == null)
375 return;
376
377 Cursor waitCursor = new Cursor(fDisplay, SWT.CURSOR_WAIT);
378 fShell.setCursor(waitCursor);
379 fImageCanvas.setCursor(waitCursor);
380 try {
381 reset();
382 ImageLoader loader = new ImageLoader();
383 // Read the new image(s) from the chosen file.
384 SystemWatch timer = new SystemWatch().start();
385 long startTime = System.currentTimeMillis();
386 fImageDatas = loader.load(filename);
387 fLoadTime = System.currentTimeMillis() - startTime;
388 System.err.println(NAME + ".open(): load time=" + timer.stop().toString());
389 if (fImageDatas.length > 0) {
390 // Cache the filename.
391 fFilename = filename;
392 // Display the first image in the file.
393 fImageDataIndex = 0;
394 displayImage(fImageDatas[fImageDataIndex]);
395 }
396 } catch (SWTException e) {
397 showErrorDialog(i18n("Loading_lc"), filename, e);
398 } finally {
399 fShell.setCursor(null);
400 fImageCanvas.setCursor(fCrossCursor);
401 waitCursor.dispose();
402 }
403 }
404
405 void openURL() {
406 resetScaleCombo();
407
408 // Get the user to choose an image URL.
409 TextPrompter textPrompter = new TextPrompter(fShell, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
410 textPrompter.setText(i18n("OpenURLDialog"));
411 textPrompter.setMessage(i18n("EnterURL"));
412 String urlname = textPrompter.open();
413 if (urlname == null)
414 return;
415
416 Cursor waitCursor = new Cursor(fDisplay, SWT.CURSOR_WAIT);
417 fShell.setCursor(waitCursor);
418 fImageCanvas.setCursor(waitCursor);
419 try {
420 reset();
421 URL url = new URL(urlname);
422 InputStream stream = url.openStream();
423 ImageLoader loader = new ImageLoader();
424 // Read the new image(s) from the chosen file.
425 long startTime = System.currentTimeMillis();
426 fImageDatas = loader.load(stream);
427 fLoadTime = System.currentTimeMillis() - startTime;
428 if (fImageDatas.length > 0) {
429 fFilename = null;
430 // Display the first image in the file.
431 fImageDataIndex = 0;
432 displayImage(fImageDatas[fImageDataIndex]);
433 }
434 } catch (Exception e) {
435 showErrorDialog(i18n("Loading_lc"), urlname, e);
436 } finally {
437 fShell.setCursor(null);
438 fImageCanvas.setCursor(fCrossCursor);
439 waitCursor.dispose();
440 }
441 }
442
443 void displayImage(ImageData newImageData) {
444 // Dispose of the old image, if there was one.
445 if (fImage != null)
446 fImage.dispose();
447
448 // try {
449 // // Cache the new image and imageData.
450 fImageData = newImageData;
451 // Rectangle bounds = fImageCanvas.getClientArea();
452 // fImage = getImage(0, 0, bounds.width, bounds.height);
453 // } catch (SWTException e) {
454 // showErrorDialog(i18n("Creating_from") + " ", fFilename, e);
455 // fImage = null;
456 // return;
457 // }
458
459 // Update the widgets with the new image info.
460 String string = createMsg(i18n("Analyzer_on"), fFilename);
461 fShell.setText(string);
462
463 fInfoLabel.setText(
464 fFilename
465 + ": w="
466 + fImageData.width
467 + ", h="
468 + fImageData.height
469 + ", depth="
470 + fImageData.depth
471 + Sprint.f(" (%.2f)", fLoadTime / 1000f));
472 fImageCanvas.redraw();
473 resetScrollBars();
474 }
475
476 void paintImage(PaintEvent event) {
477 if (false)
478 Msg.err(
479 NAME
480 + ".paintImage(): event="
481 + event.x
482 + ","
483 + event.y
484 + ","
485 + event.width
486 + ","
487 + event.height
488 + ", fX,fY="
489 + fX
490 + ","
491 + fY);
492 if(fImageData==null) return;
493 int x = event.x;
494 int y = event.y;
495 int width = event.width;
496 int height = event.height;
497 // Top level corner of area currently being displayed.
498 int fx = fX;
499 int fy = -fY;
500 int w = Math.min(width, fImageData.width - fx - x);
501 int h = Math.min(height, fImageData.height - fy - y);
502 // Create a new image that enclose the damaged area.
503 Rectangle clientarea = fImageCanvas.getClientArea();
504 if (fImage == null
505 || fImage.isDisposed()
506 || clientarea.width != fClientW
507 || clientarea.height != fClientH) {
508 w = Math.min(clientarea.width*3/2, fImageData.width - fx);
509 h = Math.min(clientarea.height*3/2, fImageData.height - fy);
510 if (true)
511 System.err.println(NAME + ".getImage(): new: w=" + w + ", h=" + h);
512 if (fImage != null && !fImage.isDisposed())
513 fImage.dispose();
514 fImage = createImage(fx, fy, w, h);
515 fImageX = fx;
516 fImageY = fy;
517 fImageW = w;
518 fImageH = h;
519 fClientW = clientarea.width;
520 fClientH = clientarea.height;
521 event.gc.drawImage(fImage, 0, 0);
522 return;
523 }
524 if (fImage != null) {
525 // The damaged area.
526 int ulx = fx + x + clientarea.width/2;
527 int uly = fy + y;
528 int lrx = ulx + w;
529 int lry = uly + h;
530 if (true)
531 System.err.println(
532 NAME
533 + ".getImage(): fx="
534 + fx
535 + ", fy="
536 + fy
537 + ", w="
538 + w
539 + ", h="
540 + h
541 + ", damaged(x,y,w,h):"
542 + x
543 + ", "
544 + y
545 + ", "
546 + width
547 + ", "
548 + height
549 + ", fImage(x,y,w,h): "
550 + fImageX
551 + ", "
552 + fImageY
553 + ", "
554 + fImageW
555 + ", "
556 + fImageH);
557 //FIXME: check if damaged area outside fImageData.
558 if (ulx > fImageData.width
559 || uly > fImageData.height
560 || ulx >= fImageX
561 && lrx <= fImageX + fImageW
562 && uly >= fImageY
563 && lry <= fImageY + fImageH) {
564 // Damaged area is inside current buffered image.
565 event.gc.drawImage(fImage, 0, 0);
566 return;
567 }
568 }
569 event.gc.drawImage(fImage, fImageX - fx, fImageY - fy);
570 Image image = createImage(fx + x + clientarea.width/2, fy + y, w, h);
571 //if (clientarea.width != fImageW || clientarea.height != fImageH) {
572 // Size changed, create a new Image.
573 //} else
574 // Horizontal scroll
575 if (true)
576 System.err.println(NAME + ".getImage(): horizontal scroll: damaged x=" + (fx + x) + ", y=" + (fy + y));
577 GC gc = new GC(fImage);
578 gc.copyArea(0, 0, fImageW, fImageH, fImageX - fx, fImageY - fy);
579 gc.drawImage(image, x + clientarea.width/2, y);
580 gc.dispose();
581 fImageX = fx;
582 fImageY = fy;
583 gc = new GC(fImageCanvas);
584 gc.drawImage(image, x + clientarea.width/2, y);
585 gc.dispose();
586 image.dispose();
587 }
588
589 /**
590 * Extract an Image from the given area of the original image, fImageData.
591 */
592 Image createImage(int x, int y, int w, int h) {
593 if (true)
594 System.err.println(NAME + ".createImage(): x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
595 int[][] data = new int[h][w];
596 for (int r = 0; r < h; ++r) {
597 fImageData.getPixels(x, y + r, w, data[r], 0);
598 }
599 ImageData imagedata = new ImageData(w, h, fImageData.depth, fImageData.palette);
600 for (int r = 0; r < h; ++r) {
601 imagedata.setPixels(0, r, w, data[r], 0);
602 }
603 return new Image(fDisplay, imagedata);
604 }
605
606 /*
607 * Called when the mouse moves in the image canvas.
608 * Show the color of the image at the point under the mouse.
609 */
610 void showColorAt(int mx, int my) {
611 if(fImageData==null) return;
612 int x = mx - fImageData.x + fX;
613 int y = my - fImageData.y - fY;
614 showColorForPixel(x, y);
615 }
616
617 /*
618 * Set the status label to show color information
619 * for the specified pixel in the image.
620 */
621 void showColorForPixel(int x, int y) {
622 if (x >= 0 && x < fImageData.width && y >= 0 && y < fImageData.height) {
623 int pixel = fImageData.getPixel(x, y);
624 RGB rgb = fImageData.palette.getRGB(pixel);
625
626 Object[] args =
627 { new Integer(x), new Integer(y), new Integer(pixel), Integer.toHexString(pixel), rgb };
628 if (pixel == fImageData.transparentPixel) {
629 fStatusLabel.setText(createMsg("Color at transparent pixel: ", args));
630 } else {
631 fStatusLabel.setText(Sprint.f("Color at: %d,%d=%06x").a(x).a(y).a(pixel).end());
632 }
633 } else {
634 fStatusLabel.setText("");
635 }
636 }
637
638 /*
639 * Called when a mouse down or key press is detected
640 * in the data text. Show the color of the pixel at
641 * the caret position in the data text.
642 */
643 void showColorForData() {}
644
645 ////////////////////////////////////////////////////////////////////////
646
647 /*
648 * Called when the image canvas' horizontal scrollbar is selected.
649 */
650 void scrollHorizontally(ScrollBar scrollBar) {
651 if (fImage == null)
652 return;
653 Rectangle canvasBounds = fImageCanvas.getClientArea();
654 int width = (int) Math.round(fImageData.width);
655 if (width > canvasBounds.width) {
656 // Only scroll if the image is bigger than the canvas.
657 int x = scrollBar.getSelection();
658 if (width -x< canvasBounds.width) {
659 // Don't scroll past the end of the image.
660 x = width-canvasBounds.width;
661 }
662 fImageCanvas.scroll(-x, fY, -fX, fY, fImageW, fImageH, false);
663 fX = x;
664 }
665 }
666
667 /*
668 * Called when the image canvas' vertical scrollbar is selected.
669 */
670 void scrollVertically(ScrollBar scrollBar) {
671 if (fImage == null)
672 return;
673 Rectangle canvasBounds = fImageCanvas.getClientArea();
674 int width = (int) Math.round(fImageData.width);
675 int height = (int) Math.round(fImageData.height);
676 if (height > canvasBounds.height) {
677 // Only scroll if the image is bigger than the canvas.
678 int y = -scrollBar.getSelection();
679 if (y + height < canvasBounds.height) {
680 // Don't scroll past the end of the image.
681 y = canvasBounds.height - height;
682 }
683 fImageCanvas.scroll(fX, y, fX, fY, width, height, false);
684 fY = y;
685 }
686 }
687
688 /*
689 * Called when the ScaleX combo selection changes.
690 */
691 void scaleImage() {
692 try {
693 String text = fScaleCombo.getText();
694 int index = text.indexOf(' ');
695 fScale = Float.parseFloat(text.substring(0, index)) / 100;
696 } catch (NumberFormatException e) {
697 fScale = 1;
698 fScaleCombo.select(fScaleCombo.indexOf("100 %"));
699 }
700 if (fImage == null)
701 return;
702 // Scale image to a new buffer.
703 fImageData = fImageDatas[fImageDataIndex];
704 if (fScale != 1) {
705 fImageData =
706 fImageData.scaledTo(
707 (int) Math.round(fImageData.width * fScale),
708 (int) Math.round(fImageData.height * fScale));
709 }
710 resizeScrollBars();
711 fX = fImageCanvas.getHorizontalBar().getSelection();
712 fY = -fImageCanvas.getVerticalBar().getSelection();
713 displayImage(fImageData);
714 }
715
716 // Reset the scale combos to 1.
717 void resetScaleCombo() {
718 fScale = 1;
719 fScaleCombo.select(fScaleCombo.indexOf("100 %"));
720 }
721
722 // Reset the scroll bars to 0.
723 void resetScrollBars() {
724 if (fImageData == null)
725 return;
726 fX = 0;
727 fY = 0;
728 resizeScrollBars();
729 fImageCanvas.getHorizontalBar().setSelection(0);
730 fImageCanvas.getVerticalBar().setSelection(0);
731 }
732
733 void resizeScrollBars() {
734 // Set the max and thumb for the image canvas scroll bars.
735 ScrollBar horizontal = fImageCanvas.getHorizontalBar();
736 ScrollBar vertical = fImageCanvas.getVerticalBar();
737 Rectangle canvasBounds = fImageCanvas.getClientArea();
738 int width = (int) Math.round(fImageData.width);
739 if (width > canvasBounds.width) {
740 // The image is wider than the canvas.
741 // Set selection such as to keep top left corner of the image stationary.
742 int x = horizontal.getSelection() * width / horizontal.getMaximum();
743 horizontal.setEnabled(true);
744 horizontal.setMaximum(width);
745 horizontal.setSelection(x);
746 horizontal.setThumb(canvasBounds.width);
747 horizontal.setPageIncrement(canvasBounds.width / 2);
748 } else {
749 // The canvas is wider than the image.
750 horizontal.setEnabled(false);
751 if (fX != 0) {
752 // Make sure the image is completely visible.
753 fX = 0;
754 fImageCanvas.redraw();
755 }
756 }
757 int height = (int) Math.round(fImageData.height);
758 if (height > canvasBounds.height) {
759 // The image is taller than the canvas.
760 // Set selection such as to keep top left corner of the image stationary.
761 vertical.setEnabled(true);
762 vertical.setMaximum(height);
763 vertical.setThumb(canvasBounds.height);
764 vertical.setPageIncrement(canvasBounds.height / 2);
765 } else {
766 // The canvas is taller than the image.
767 vertical.setEnabled(false);
768 if (fY != 0) {
769 // Make sure the image is completely visible.
770 fY = 0;
771 fImageCanvas.redraw();
772 }
773 }
774 }
775
776 void resizeShell(ControlEvent event) {
777 if (fImage == null || fShell.isDisposed())
778 return;
779 resizeScrollBars();
780 }
781
782 void reset() {
783 fImageDatas = null;
784 fImageData = null;
785 if (fImage != null && !fImage.isDisposed())
786 fImage.dispose();
787 fImage = null;
788 }
789
790 ////////////////////////////////////////////////////////////////////////
791
792 /*
793 * Open an error dialog displaying the specified information.
794 */
795 void showErrorDialog(String operation, String filename, Exception e) {
796 MessageBox box = new MessageBox(fShell, SWT.ICON_ERROR);
797 String message = createMsg(i18n("Error"), new String[] { operation, filename });
798 String errorMessage = "";
799 if (e != null) {
800 if (e instanceof SWTException) {
801 SWTException swte = (SWTException) e;
802 errorMessage = swte.getMessage();
803 if (swte.throwable != null) {
804 errorMessage += ":\n" + swte.throwable.toString();
805 }
806 } else {
807 errorMessage = e.toString();
808 }
809 }
810 box.setMessage(message + errorMessage);
811 box.open();
812 }
813
814 /*
815 * Return a String describing the specified image file type.
816 */
817 static String fileTypeString(int filetype) {
818 if (filetype == SWT.IMAGE_BMP)
819 return "BMP";
820 if (filetype == SWT.IMAGE_GIF)
821 return "GIF";
822 if (filetype == SWT.IMAGE_ICO)
823 return "ICO";
824 if (filetype == SWT.IMAGE_JPEG)
825 return "JPEG";
826 if (filetype == SWT.IMAGE_PNG)
827 return "PNG";
828 return i18n("Unknown_ac") + ": " + filetype;
829 }
830
831 static String createMsg(String msg, Object arg) {
832 MessageFormat formatter = new MessageFormat(msg);
833 return formatter.format(new Object[] { arg });
834 }
835
836 static String i18n(String str) {
837 return str;
838 }
839
840 ////////////////////////////////////////////////////////////////////////
841
842 class TextPrompter extends Dialog {
843 String message = "";
844 String result = null;
845 Shell dialog;
846 Text text;
847 public TextPrompter(Shell parent, int style) {
848 super(parent, style);
849 }
850 public TextPrompter(Shell parent) {
851 this(parent, SWT.APPLICATION_MODAL);
852 }
853 public String getMessage() {
854 return message;
855 }
856 public void setMessage(String string) {
857 message = string;
858 }
859 public String open() {
860 dialog = new Shell(getParent(), getStyle());
861 dialog.setText(getText());
862 dialog.setLayout(new GridLayout());
863 Label label = new Label(dialog, SWT.NULL);
864 label.setText(message);
865 label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
866 text = new Text(dialog, SWT.SINGLE | SWT.BORDER);
867 GridData data = new GridData(GridData.FILL_HORIZONTAL);
868 data.widthHint = 300;
869 text.setLayoutData(data);
870 Composite buttons = new Composite(dialog, SWT.NONE);
871 GridLayout grid = new GridLayout();
872 grid.numColumns = 2;
873 buttons.setLayout(grid);
874 buttons.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
875 Button ok = new Button(buttons, SWT.PUSH);
876 ok.setText(i18n("OK"));
877 data = new GridData();
878 data.widthHint = 75;
879 ok.setLayoutData(data);
880 ok.addSelectionListener(new SelectionAdapter() {
881 public void widgetSelected(SelectionEvent e) {
882 result = text.getText();
883 dialog.dispose();
884 }
885 });
886 Button cancel = new Button(buttons, SWT.PUSH);
887 cancel.setText(i18n("Cancel"));
888 data = new GridData();
889 data.widthHint = 75;
890 cancel.setLayoutData(data);
891 cancel.addSelectionListener(new SelectionAdapter() {
892 public void widgetSelected(SelectionEvent e) {
893 dialog.dispose();
894 }
895 });
896 dialog.setDefaultButton(ok);
897 dialog.pack();
898 dialog.open();
899 while (!dialog.isDisposed()) {
900 if (!getDisplay().readAndDispatch())
901 getDisplay().sleep();
902 }
903 return result;
904 }
905 }
906
907 ////////////////////////////////////////////////////////////////////////
908
909 }