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