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 }