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

Quick Search    Search Deep

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 }