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

Quick Search    Search Deep

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 }