1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package davmail.ui.tray;
20
21 import davmail.BundleMessage;
22 import davmail.DavGateway;
23 import davmail.Settings;
24 import davmail.ui.AboutFrame;
25 import davmail.ui.SettingsFrame;
26 import org.apache.log4j.Level;
27 import org.apache.log4j.Logger;
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.graphics.GC;
30 import org.eclipse.swt.graphics.Image;
31 import org.eclipse.swt.graphics.ImageData;
32 import org.eclipse.swt.graphics.PaletteData;
33 import org.eclipse.swt.internal.Library;
34 import org.eclipse.swt.widgets.Display;
35 import org.eclipse.swt.widgets.Menu;
36 import org.eclipse.swt.widgets.MenuItem;
37 import org.eclipse.swt.widgets.Shell;
38 import org.eclipse.swt.widgets.ToolTip;
39 import org.eclipse.swt.widgets.Tray;
40 import org.eclipse.swt.widgets.TrayItem;
41
42 import javax.imageio.ImageIO;
43 import javax.swing.*;
44 import java.awt.image.BufferedImage;
45 import java.io.ByteArrayInputStream;
46 import java.io.ByteArrayOutputStream;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.net.URL;
50 import java.util.ArrayList;
51 import java.util.concurrent.locks.Condition;
52 import java.util.concurrent.locks.ReentrantLock;
53
54
55
56
57 public class SwtGatewayTray implements DavGatewayTrayInterface {
58 private static final Logger LOGGER = Logger.getLogger(SwtGatewayTray.class);
59
60 private static final ReentrantLock lock = new ReentrantLock();
61 private static final Condition ready = lock.newCondition();
62
63 protected SwtGatewayTray() {
64 }
65
66 static SettingsFrame settingsFrame;
67 static AboutFrame aboutFrame;
68
69 private static TrayItem trayItem;
70 private static ArrayList<java.awt.Image> frameIcons;
71
72 private static Image image;
73 private static Image image2;
74 private static Image inactiveImage;
75 private static Display display;
76 private static Shell shell;
77 private boolean isActive = true;
78 private static boolean isReady = false;
79 private static boolean isStarting = false;
80 private static Error error;
81 private boolean firstMessage = true;
82
83 public static void initDisplay() {
84 lock.lock();
85 try {
86 if (isReady || isStarting) {
87
88 while (!isReady) {
89 ready.await();
90 if (error != null) {
91 throw error;
92 }
93 }
94 return;
95 }
96 isStarting = true;
97
98 Thread swtThread = new Thread("SWT") {
99 @Override
100 public void run() {
101 try {
102 LOGGER.info("Detected SWT "+ Library.getVersionString()+", init display");
103 Display.setAppName("davmail");
104 display = Display.getDefault();
105 shell = new Shell(display);
106
107
108 lock.lock();
109 try {
110 isReady = true;
111 ready.signalAll();
112 } finally {
113 lock.unlock();
114 }
115
116
117 while (!shell.isDisposed()) {
118 if (!display.readAndDispatch()) {
119 display.sleep();
120 }
121 }
122
123 if (settingsFrame != null) {
124 settingsFrame.dispose();
125 }
126 if (aboutFrame != null) {
127 aboutFrame.dispose();
128 }
129 System.exit(0);
130 } catch (Throwable e) {
131 LOGGER.error("Error in SWT thread", e);
132 lock.lock();
133 try {
134 error = new Error(e);
135 ready.signalAll();
136 } finally {
137 lock.unlock();
138 }
139 }
140 }
141 };
142 swtThread.start();
143
144 while (!isReady) {
145 ready.await();
146 if (error != null) {
147 throw error;
148 }
149 }
150 } catch (InterruptedException e) {
151 Thread.currentThread().interrupt();
152 } finally {
153 lock.unlock();
154 }
155 }
156
157
158
159
160
161
162 @Override
163 public java.util.List<java.awt.Image> getFrameIcons() {
164 return frameIcons;
165 }
166
167
168
169
170 public void switchIcon() {
171 isActive = true;
172 display.asyncExec(() -> {
173 Image currentImage = trayItem.getImage();
174 if (currentImage != null && currentImage.equals(image)) {
175 trayItem.setImage(image2);
176 } else {
177 trayItem.setImage(image);
178 }
179 });
180
181 }
182
183
184
185
186 public void resetIcon() {
187 display.asyncExec(() -> trayItem.setImage(image));
188 }
189
190
191
192
193 public void inactiveIcon() {
194 isActive = false;
195 display.asyncExec(() -> trayItem.setImage(inactiveImage));
196 }
197
198
199
200
201
202
203 public boolean isActive() {
204 return isActive;
205 }
206
207
208
209
210
211
212
213 public void displayMessage(final String message, final Level level) {
214 if (trayItem != null) {
215 display.asyncExec(() -> {
216 int messageType = 0;
217 if (level.equals(Level.INFO)) {
218 messageType = SWT.ICON_INFORMATION;
219 } else if (level.equals(Level.WARN)) {
220 messageType = SWT.ICON_WARNING;
221 } else if (level.equals(Level.ERROR)) {
222 messageType = SWT.ICON_ERROR;
223 }
224 if (messageType != 0) {
225 final ToolTip toolTip = new ToolTip(shell, SWT.BALLOON | messageType);
226 toolTip.setText(BundleMessage.format("UI_DAVMAIL_GATEWAY"));
227 toolTip.setMessage(message);
228 trayItem.setToolTip(toolTip);
229
230 if (firstMessage) {
231 firstMessage = false;
232 try {
233 Thread.sleep(1000);
234 } catch (InterruptedException e) {
235 Thread.currentThread().interrupt();
236 }
237 }
238 toolTip.setVisible(true);
239 }
240 trayItem.setToolTipText(BundleMessage.format("UI_DAVMAIL_GATEWAY") + '\n' + message);
241 });
242 }
243 }
244
245
246
247
248
249
250
251
252 public static Image loadSwtImage(String fileName) {
253 Image result = null;
254 try {
255 ClassLoader classloader = DavGatewayTray.class.getClassLoader();
256 URL imageUrl = classloader.getResource(fileName);
257 if (imageUrl == null) {
258 throw new IOException("fileName");
259 }
260 try (InputStream inputStream = imageUrl.openStream()) {
261 result = new Image(display, inputStream);
262
263 }
264
265 } catch (IOException e) {
266 DavGatewayTray.warn(new BundleMessage("LOG_UNABLE_TO_LOAD_IMAGE"), e);
267 }
268 return result;
269 }
270
271
272
273
274
275
276
277
278
279 public static Image loadSwtImage(String fileName, int targetSize) {
280 int padding = 0;
281 Image result = null;
282 try {
283 ClassLoader classloader = DavGatewayTray.class.getClassLoader();
284 URL imageUrl = classloader.getResource(fileName);
285 if (imageUrl == null) {
286 throw new IOException(fileName);
287 }
288 BufferedImage bufferedImage;
289 if (Settings.getBooleanProperty("davmail.trayGrayscale", false)) {
290 bufferedImage = DavGatewayTray.convertGrayscale(ImageIO.read(imageUrl));
291 } else {
292 bufferedImage = ImageIO.read(imageUrl);
293 }
294 if (bufferedImage == null) {
295 throw new IOException(fileName);
296 }
297 if (bufferedImage.getWidth() != targetSize || bufferedImage.getHeight() != targetSize) {
298 bufferedImage = DavGatewayTray.scaleImage(bufferedImage, targetSize);
299 }
300 byte[] imageBytes;
301 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
302 ImageIO.write(bufferedImage, "png", os);
303 imageBytes = os.toByteArray();
304 }
305
306 try (InputStream inputStream = new ByteArrayInputStream(imageBytes)) {
307 Image loadedImage = new Image(display, inputStream);
308
309 ImageData resultImageData = new ImageData(targetSize, targetSize, 24, new PaletteData(0xff0000, 0x00ff00, 0x0000ff));
310
311 resultImageData.setAlpha(0, 0, 0);
312
313 result = new Image(display, resultImageData);
314
315
316 GC gc = new GC(result);
317 gc.setAntialias(SWT.ON);
318 gc.setInterpolation(SWT.HIGH);
319 gc.drawImage(loadedImage, 0, 0, loadedImage.getBounds().width, loadedImage.getBounds().height,
320 padding, padding, result.getBounds().width - padding, result.getBounds().height - padding);
321 gc.dispose();
322 loadedImage.dispose();
323 }
324
325 } catch (IOException e) {
326 DavGatewayTray.warn(new BundleMessage("LOG_UNABLE_TO_LOAD_IMAGE"), e);
327 }
328 return result;
329 }
330
331 protected static void setDefaultIconImage(Image image) {
332 SwtGatewayTray.image = image;
333 }
334
335 protected static void setActiveIconImage(Image image) {
336 SwtGatewayTray.image2 = image;
337 }
338
339 protected static void setInactiveIconImage(Image image) {
340 SwtGatewayTray.inactiveImage = image;
341 }
342
343
344
345
346 public void init() {
347 try {
348
349 UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
350 } catch (Exception e) {
351 DavGatewayTray.warn(new BundleMessage("LOG_UNABLE_TO_SET_LOOK_AND_FEEL"));
352 }
353
354 initDisplay();
355
356 display.asyncExec(() -> {
357 try {
358 final Tray tray = display.getSystemTray();
359 if (tray != null) {
360
361 trayItem = new TrayItem(tray, SWT.NONE);
362 trayItem.setToolTipText(BundleMessage.format("UI_DAVMAIL_GATEWAY"));
363
364 frameIcons = new ArrayList<>();
365 frameIcons.add(DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(AwtGatewayTray.TRAY128_PNG)));
366 frameIcons.add(DavGatewayTray.adjustTrayIcon(DavGatewayTray.loadImage(AwtGatewayTray.TRAY_PNG)));
367
368
369 int trayIconSize = 24;
370 if (Settings.isWindows()) {
371 trayIconSize = 16;
372 }
373
374 SwtGatewayTray.setDefaultIconImage(loadSwtImage("tray128.png", trayIconSize));
375 SwtGatewayTray.setActiveIconImage(loadSwtImage("tray128active.png", trayIconSize));
376 SwtGatewayTray.setInactiveIconImage(loadSwtImage("tray128inactive.png", trayIconSize));
377
378 trayItem.setImage(image);
379 trayItem.addDisposeListener(e -> {
380 if (image != null && !image.isDisposed()) {
381 image.dispose();
382 }
383 if (image2 != null && !image2.isDisposed()) {
384 image2.dispose();
385 }
386 if (inactiveImage != null && !inactiveImage.isDisposed()) {
387 inactiveImage.dispose();
388 }
389 });
390
391
392 final Menu popup = new Menu(shell, SWT.POP_UP);
393 trayItem.addListener(SWT.MenuDetect, event -> display.asyncExec(
394 () -> popup.setVisible(true)));
395
396 MenuItem aboutItem = new MenuItem(popup, SWT.PUSH);
397 aboutItem.setText(BundleMessage.format("UI_ABOUT"));
398 aboutItem.addListener(SWT.Selection, event -> SwingUtilities.invokeLater(
399 () -> {
400 if (aboutFrame == null) {
401 aboutFrame = new AboutFrame();
402 }
403 aboutFrame.update();
404 aboutFrame.setVisible(true);
405 aboutFrame.toFront();
406 aboutFrame.requestFocus();
407 }));
408
409
410 trayItem.addListener(SWT.DefaultSelection, event -> SwingUtilities.invokeLater(
411 this::openSettingsFrame));
412
413 MenuItem defaultItem = new MenuItem(popup, SWT.PUSH);
414 defaultItem.setText(BundleMessage.format("UI_SETTINGS"));
415 defaultItem.addListener(SWT.Selection, event -> SwingUtilities.invokeLater(
416 this::openSettingsFrame));
417
418 MenuItem exitItem = new MenuItem(popup, SWT.PUSH);
419 exitItem.setText(BundleMessage.format("UI_EXIT"));
420 exitItem.addListener(SWT.Selection, event -> DavGateway.stop());
421
422
423 if (Settings.isFirstStart()) {
424 SwingUtilities.invokeLater(() -> {
425
426 if (settingsFrame == null) {
427 settingsFrame = new SettingsFrame();
428 }
429 settingsFrame.setVisible(true);
430 settingsFrame.toFront();
431 settingsFrame.requestFocus();
432 });
433
434 }
435
436 }
437 } catch (Exception exc) {
438 DavGatewayTray.error(exc);
439 } catch (Error exc) {
440 error = exc;
441 throw exc;
442 }
443 });
444 }
445
446 private void openSettingsFrame() {
447
448 if (settingsFrame == null) {
449 settingsFrame = new SettingsFrame();
450 }
451 settingsFrame.reload();
452 settingsFrame.setVisible(true);
453 settingsFrame.toFront();
454 settingsFrame.requestFocus();
455 }
456
457 public void dispose() {
458 shell.dispose();
459 }
460
461 }