Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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