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