1 /*
2 * FXTrayIcon.cpp
3 *
4 * Copyright (c) 2008, Hendrik Rittich
5 * Copyright (C) 2006-2011 by Sander Jansen
6 * Copyright 2012 David Vachulka <david@konstrukce-cad.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 */
23
24 #include <xincs.h>
25 #include "FXTrayIcon.h"
26 #include "FXTrayApp.h"
27
28 namespace FX {
29
30 #ifdef WIN32
31 FXDEFMAP(FXTrayIcon) FXTrayIconMap[] = {
32 FXMAPFUNC(SEL_TIMEOUT, FXTrayIcon::ID_POPTIMEOUT, FXTrayIcon::onTimeout),
33 FXMAPFUNC(SEL_MOTION, 0, FXTrayIcon::onEvent)
34 };
35 #else
36 enum
37 {
38 SYSTEM_TRAY_REQUEST_DOCK = 0,
39 SYSTEM_TRAY_BEGIN_MESSAGE = 1,
40 SYSTEM_TRAY_CANCEL_MESSAGE = 2,
41 };
42
43 enum
44 {
45 SYSTEM_TRAY_HORIZONTAL = 0,
46 SYSTEM_TRAY_VERTICAL = 1,
47 SYSTEM_TRAY_UNKNOWN = 2
48 };
49
50
51 FXDEFMAP(FXTrayIcon) FXTrayIconMap[] = {
52 FXMAPFUNC(SEL_PAINT, 0, FXTrayIcon::onPaint),
53 FXMAPFUNC(SEL_CONFIGURE, 0, FXTrayIcon::onConfigure),
54 FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, FXTrayIcon::onLeftBtnPress),
55 FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, 0, FXTrayIcon::onRightBtnRelease),
56 FXMAPFUNC(SEL_QUERY_TIP, 0, FXTrayIcon::onQueryTip),
57 FXMAPFUNC(SEL_EMBED_NOTIFY, 0, FXTrayIcon::onEmbedded),
58 FXMAPFUNC(SEL_TIMEOUT, FXTrayIcon::ID_POPTIMEOUT, FXTrayIcon::onTimeout)
59 };
60 #endif
61
62 FXIMPLEMENT(FXTrayIcon, FXTopWindow, FXTrayIconMap, ARRAYNUMBER(FXTrayIconMap))
63
64 #ifdef WIN32
65 DWORD FXTrayIcon::sTrayIconCount = 1;
66
FXTrayIcon(FXApp * app,const FXString & text,FXIcon * icon,FXPopup * popup,FXObject * target,FXSelector sel,FXuint opts)67 FXTrayIcon::FXTrayIcon(FXApp* app, const FXString& text, FXIcon* icon,
68 FXPopup* popup, FXObject* target, FXSelector sel,
69 FXuint opts) :
70 FXTopWindow(app, text, 0, 0, 0, 0,0,0,0, 0,0,0,0, 0,0),
71 mIcon(icon),
72 m_popup(popup),
73 m_opts(opts),
74 mTooltip(text)
75 {
76 mWIcon = 0;
77 mTrayID = sTrayIconCount;
78 sTrayIconCount++;
79
80 setTarget(target);
81 setSelector(sel);
82 }
83
~FXTrayIcon()84 FXTrayIcon::~FXTrayIcon() {
85 if (mWIcon)
86 {
87 // Icon entfernen
88 NOTIFYICONDATA tray_data;
89 ZeroMemory(&tray_data, sizeof(tray_data));
90 tray_data.cbSize = sizeof(tray_data);
91 tray_data.hWnd = static_cast<HWND>(xid);
92 tray_data.uID = mTrayID;
93 Shell_NotifyIcon(NIM_DELETE, &tray_data);
94
95 // Icon loeschen
96 DestroyIcon(mWIcon);
97 }
98 }
99
create()100 void FXTrayIcon::create() {
101 FXTopWindow::create();
102
103 if (mWIcon)
104 return;
105
106 mIcon->create();
107
108 // Windows Icon erzeugen
109 mWIcon = createMswIcon(mIcon);
110
111 mapToManager();
112 }
113
mapToManager()114 void FXTrayIcon::mapToManager()
115 {
116 NOTIFYICONDATA tray_data;
117 setupNotifyData(&tray_data);
118
119 Shell_NotifyIcon(NIM_ADD, &tray_data);
120 }
121
setupNotifyData(NOTIFYICONDATA * data)122 void FXTrayIcon::setupNotifyData(NOTIFYICONDATA* data)
123 {
124 ZeroMemory(data, sizeof(*data));
125 data->cbSize = sizeof(*data);
126 data->hWnd = static_cast<HWND>(xid);
127 data->uID = mTrayID;
128 data->hIcon = mWIcon;
129 data->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
130 data->uCallbackMessage = WM_MOUSEMOVE; // Change ID
131
132 // Tooltip
133 #ifdef UNICODE
134 utf2ncs(data->szTip, mTooltip.text(), 64);
135 #else
136 //SetWindowTextA((HWND)xid,title.text());
137 #error USE DEFINE UNICODE
138 #endif
139 }
140
createMswIcon(FXIcon * icon)141 HICON FXTrayIcon::createMswIcon(FXIcon* icon)
142 {
143 // Windows Icon erzeugen
144 ICONINFO iconinfo;
145 ZeroMemory(&iconinfo, sizeof(iconinfo));
146 iconinfo.fIcon=TRUE;
147 iconinfo.hbmMask=(HBITMAP)icon->shape;
148 iconinfo.hbmColor=(HBITMAP)icon->xid;
149 return CreateIconIndirect(&iconinfo);
150 }
151
onEvent(FXObject * obj,FXSelector,void * ptr)152 long FXTrayIcon::onEvent(FXObject* obj, FXSelector, void* ptr)
153 {
154 if (ptr == 0)
155 return 0;
156
157 FXEvent* event = static_cast<FXEvent*>(ptr);
158 int lParam = event->root_x;
159
160 if ((lParam == WM_LBUTTONUP && (m_opts & TRAY_MENU_ON_LEFT)) ||
161 (lParam == WM_RBUTTONUP && (m_opts & TRAY_MENU_ON_RIGHT)))
162 {
163 POINT p;
164 GetCursorPos(&p);
165 m_popup->popup(0, p.x, p.y);
166 getApp()->addTimeout(this, ID_POPTIMEOUT, 10000);
167
168 return 1;
169 }
170 if (target &&
171 (lParam == WM_LBUTTONUP && (m_opts & TRAY_CMD_ON_LEFT)) ||
172 (lParam == WM_RBUTTONUP && (m_opts & TRAY_CMD_ON_RIGHT)))
173 {
174 return target->tryHandle(obj, FXSEL(SEL_COMMAND, message), ptr);
175 }
176 return 0;
177 }
178
setIcon(FXIcon * icon)179 void FXTrayIcon::setIcon(FXIcon* icon)
180 {
181 mIcon = icon;
182
183 if (!mWIcon)
184 return; // not created
185
186 // Icon loeschen
187 DestroyIcon(mWIcon);
188
189 mWIcon = createMswIcon(icon);
190
191 NOTIFYICONDATA tray_data;
192 setupNotifyData(&tray_data);
193 Shell_NotifyIcon(NIM_MODIFY, &tray_data);
194 }
195
setText(const FXString & text)196 void FXTrayIcon::setText(const FXString& text)
197 {
198 mTooltip = text;
199
200 if (!mWIcon)
201 return; // not created
202
203 NOTIFYICONDATA tray_data;
204 setupNotifyData(&tray_data);
205 Shell_NotifyIcon(NIM_MODIFY, &tray_data);
206 }
207 #else
208 FXTrayIcon::FXTrayIcon()
209 {
210 m_icon = NULL;
211 flags |= FLAG_ENABLED;
212 }
213
214 FXTrayIcon::FXTrayIcon(FXApp *app, const FXString& tip, FXIcon* icon,
215 FXPopup* popup, FXObject* target, FXSelector sel,
216 FXuint opts)
217 : FXTopWindow(app, "test", NULL, NULL, DECOR_NONE, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0), m_xtraywindow(0),
218 m_xtrayopcode(0), m_socket(0), m_icon(icon), m_opaque(false), m_tip(tip), m_popup(popup), m_opts(opts), m_traycolor(getBackColor())
219 {
220 flags |= FLAG_ENABLED;
221 setTarget(target);
222 setSelector(sel);
223 static_cast<FXTrayApp*> (app)->registerIcon(this);
224 }
225
226 FXTrayIcon::~FXTrayIcon()
227 {
228 static_cast<FXTrayApp*> (getApp())->unregisterIcon();
229 }
230
231 bool FXTrayIcon::doesOverrideRedirect() const
232 {
233 return true;
234 }
235
236 FXbool FXTrayIcon::findSystemTray()
237 {
238 FXString systemtray = FXStringFormat("_NET_SYSTEM_TRAY_S%d", DefaultScreen((Display*) getApp()->getDisplay()));
239 Atom xtrayselection = XInternAtom((Display*) getApp()->getDisplay(), systemtray.text(), 0);
240 if (xtrayselection != None)
241 {
242 m_xtraywindow = (FXID) XGetSelectionOwner((Display*) getApp()->getDisplay(), xtrayselection);
243 }
244 return (m_xtraywindow != 0);
245 }
246
247 void FXTrayIcon::requestDock()
248 {
249 if (xid && m_xtraywindow)
250 {
251 XEvent ev;
252 memset(&ev, 0, sizeof (ev));
253 ev.xclient.type = ClientMessage;
254 ev.xclient.window = m_xtraywindow;
255 ev.xclient.message_type = m_xtrayopcode;
256 ev.xclient.format = 32;
257 ev.xclient.data.l[0] = CurrentTime;
258 ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
259 ev.xclient.data.l[2] = xid;
260 XSendEvent((Display*) getApp()->getDisplay(), m_xtraywindow, False, NoEventMask, &ev);
261 XSync((Display*) getApp()->getDisplay(), 0);
262 }
263 }
264
265 FXuint FXTrayIcon::getTrayOrientation()
266 {
267 if (m_xtrayorientation || m_xtrayxfceorientation)
268 {
269 FXuint orientation;
270 Atom returntype;
271 int returnformat;
272 unsigned long nitems;
273 unsigned long nbytes;
274 long * bytes = NULL;
275
276 if (m_xtrayorientation && (XGetWindowProperty((Display*) getApp()->getDisplay(), (Atom) m_xtraywindow, (Atom) m_xtrayorientation, 0, 2, False, XA_CARDINAL, &returntype, &returnformat, &nitems, &nbytes, (unsigned char**) (void*) &bytes) == Success) && returntype == XA_CARDINAL)
277 {
278 orientation = *(long*) bytes;
279 XFree(bytes);
280 return orientation;
281 }
282
283 if (bytes != NULL)
284 {
285 XFree(bytes);
286 bytes = NULL;
287 }
288
289 if (m_xtrayxfceorientation && (XGetWindowProperty((Display*) getApp()->getDisplay(), (Atom) m_xtraywindow, (Atom) m_xtrayxfceorientation, 0, 2, False, XA_CARDINAL, &returntype, &returnformat, &nitems, &nbytes, (unsigned char**) (void*) &bytes) == Success) && returntype == XA_CARDINAL)
290 {
291 orientation = *(long*) bytes;
292 XFree(bytes);
293 return orientation;
294 }
295
296 if (bytes != NULL)
297 {
298 XFree(bytes);
299 bytes = NULL;
300 }
301 }
302 return SYSTEM_TRAY_UNKNOWN;
303 }
304
305 FXuint FXTrayIcon::getTrayVisual()
306 {
307 if (m_xtrayvisual)
308 {
309 FXuint visualid;
310 Atom returntype;
311 int returnformat;
312 unsigned long nitems;
313 unsigned long nbytes;
314 long * bytes = NULL;
315
316 if (m_xtrayvisual && (XGetWindowProperty((Display*) getApp()->getDisplay(), m_xtraywindow, m_xtrayvisual, 0, 2, False, XA_VISUALID, &returntype, &returnformat, &nitems, &nbytes, (unsigned char**) (void*) &bytes) == Success) && returntype == XA_VISUALID)
317 {
318 visualid = *(long*) bytes;
319 XFree(bytes);
320 return visualid;
321 }
322
323 if (bytes != NULL)
324 {
325 XFree(bytes);
326 bytes = NULL;
327 }
328 }
329 return 0;
330 }
331
332 void FXTrayIcon::create()
333 {
334 m_xtrayopcode = (FXID) XInternAtom((Display*) getApp()->getDisplay(), "_NET_SYSTEM_TRAY_OPCODE", 0);
335 m_xtrayorientation = (FXID) XInternAtom((Display*) getApp()->getDisplay(), "_NET_SYSTEM_TRAY_ORIENTATION", 0);
336 m_xtrayxfceorientation = (FXID) XInternAtom((Display*) getApp()->getDisplay(), "_NET_XFCE_TRAY_MANAGER_ORIENTATION", 0);
337 m_xtrayvisual = (FXID) XInternAtom((Display*) getApp()->getDisplay(), "_NET_SYSTEM_TRAY_VISUAL", 0);
338
339 FXTopWindow::create();
340 if (xid)
341 {
342 Atom xembedinfo = XInternAtom((Display*) getApp()->getDisplay(), "_XEMBED_INFO", 0);
343 if (xembedinfo != None)
344 {
345 unsigned long info[2] = {0, (1 << 0)};
346 XChangeProperty((Display*) getApp()->getDisplay(), xid, xembedinfo, xembedinfo, 32, PropModeReplace, (unsigned char*) info, 2);
347 }
348
349 /// Set the size hints...
350 XSizeHints size;
351 size.flags = PMinSize | PMaxSize | PBaseSize | PAspect;
352 size.x = 0;
353 size.y = 0;
354 size.width = 0;
355 size.height = 0;
356 size.width_inc = 0;
357 size.height_inc = 0;
358 size.min_aspect.x = 1;
359 size.min_aspect.y = 1;
360 size.max_aspect.x = 1;
361 size.max_aspect.y = 1;
362 size.win_gravity = 0;
363 size.win_gravity = 0;
364
365 size.min_width = 8;
366 size.min_height = 8;
367 size.max_width = 64;
368 size.max_height = 64;
369 size.base_width = 32;
370 size.base_height = 32;
371 XSetWMNormalHints((Display*) getApp()->getDisplay(), xid, &size);
372 }
373 }
374
375 FXbool FXTrayIcon::dock()
376 {
377 if (findSystemTray())
378 {
379 FXuint id = getTrayVisual();
380 if (id)
381 {
382 if (id != XVisualIDFromVisual((Visual*) getVisual()->getVisual()))
383 m_opaque = true;
384 else
385 m_opaque = false;
386 }
387 if (!m_opaque)
388 {
389 /// Don't draw the background
390 XSetWindowAttributes sattr;
391 sattr.background_pixmap = ParentRelative;
392 XChangeWindowAttributes((Display*) getApp()->getDisplay(), xid, CWBackPixmap, &sattr);
393 }
394 requestDock();
395 return true;
396 }
397 return false;
398 }
399
400 void FXTrayIcon::setFocus()
401 {
402 FXShell::setFocus();
403 if (xid && m_socket)
404 {
405 XEvent ev;
406 memset(&ev, 0, sizeof (ev));
407 ev.xclient.type = ClientMessage;
408 ev.xclient.window = m_socket;
409 ev.xclient.message_type = ((FXTrayApp*) getApp())->mXembed;
410 ev.xclient.format = 32;
411 ev.xclient.data.l[0] = CurrentTime;
412 ev.xclient.data.l[1] = XEMBED_REQUEST_FOCUS;
413 XSendEvent((Display*) getApp()->getDisplay(), m_socket, False, NoEventMask, &ev);
414 }
415 }
416
417 long FXTrayIcon::onConfigure(FXObject*, FXSelector, void*ptr)
418 {
419 FXEvent * event = (FXEvent*) ptr;
420 FXuint orientation = getTrayOrientation();
421 FXint size = 0;
422 switch (orientation)
423 {
424 case SYSTEM_TRAY_HORIZONTAL: size = event->rect.h;
425 break;
426 case SYSTEM_TRAY_VERTICAL: size = event->rect.w;
427 break;
428 default: size = FXMAX(event->rect.h, event->rect.w);
429 break;
430 };
431 resize(size, size);
432 XSizeHints hint;
433 hint.flags = PMinSize | PMaxSize | PBaseSize;
434 hint.min_width = hint.max_width = hint.base_width = size;
435 hint.min_height = hint.max_height = hint.base_height = size;
436 XSetWMNormalHints((Display*) getApp()->getDisplay(), xid, &hint);
437 return 1;
438 }
439
440 long FXTrayIcon::onRightBtnRelease(FXObject *obj, FXSelector, void *ptr)
441 {
442 FXEvent *event = (FXEvent*) ptr;
443 if (event->moved) return 0;
444 if (m_opts & TRAY_MENU_ON_RIGHT)
445 {
446 FXEvent* event = static_cast<FXEvent*> (ptr);
447 popup(event->click_x, event->click_y);
448 return 1;
449 }
450 if (m_opts & TRAY_CMD_ON_RIGHT && target)
451 {
452 return target->tryHandle(obj, FXSEL(SEL_COMMAND, message), ptr);
453 }
454 return 0;
455 }
456
457 long FXTrayIcon::onLeftBtnPress(FXObject *obj, FXSelector, void *ptr)
458 {
459 if (m_opts & TRAY_MENU_ON_LEFT)
460 {
461 FXEvent* event = static_cast<FXEvent*> (ptr);
462 popup(event->click_x, event->click_y);
463 return 1;
464 }
465 if (m_opts & TRAY_CMD_ON_LEFT && target)
466 {
467 return target->tryHandle(obj, FXSEL(SEL_COMMAND, message), ptr);
468 }
469 return 0;
470 }
471
472 long FXTrayIcon::onQueryTip(FXObject*sender, FXSelector sel, void* ptr)
473 {
474 if (FXTopWindow::onQueryTip(sender, sel, ptr)) return 1;
475 if ((flags & FLAG_TIP) && !m_tip.empty() && !grabbed())
476 {
477 sender->handle(this, FXSEL(SEL_COMMAND, ID_SETSTRINGVALUE), (void*) &m_tip);
478 return 1;
479 }
480 return 0;
481 }
482
483 long FXTrayIcon::onPaint(FXObject*, FXSelector, void*)
484 {
485 if (m_icon)
486 {
487 FXDCWindow dc(this);
488 FXint dx = 0;
489 FXint w = getWidth();
490 FXint iw = m_icon->getWidth();
491 if(w>iw) dx = (w-iw)/2;
492 FXint dy = 0;
493 FXint h = getHeight();
494 FXint ih = m_icon->getHeight();
495 if(h>ih) dy = (h-ih)/2;
496 if (m_opaque)
497 {
498 dc.setForeground(m_traycolor);
499 dc.fillRectangle(0, 0, w, h);
500 dc.drawIcon(m_icon, dx, dy);
501 }
502 else
503 {
504 dc.drawIcon(m_icon, 0, 0);
505 //move(dx,dy);
506 }
507 }
508 return 1;
509 }
510
511 long FXTrayIcon::onEmbedded(FXObject*, FXSelector, void*ptr)
512 {
513 flags |= FLAG_SHOWN;
514 m_socket = (FXID) (FXival) ptr;
515 return 1;
516 }
517
518 void FXTrayIcon::popup(FXint x, FXint y)
519 {
520 if(!m_popup) return;
521 translateCoordinatesTo(x, y, getRoot(), x, y);
522
523 if (y > getRoot()->getHeight() / 2)
524 {
525 y -= m_popup->getDefaultHeight();
526 }
527
528 m_popup->popup(0, x, y);
529 getApp()->addTimeout(this, ID_POPTIMEOUT, 10000);
530
531 }
532
533 void FXTrayIcon::setIcon(FXIcon* ic)
534 {
535 if(m_icon != ic && ic)
536 {
537 m_icon = ic;
538 if(!m_opaque) setShape(m_icon);
539 update();
540 }
541 }
542
543 FXIcon* FXTrayIcon::getIcon() const
544 {
545 return m_icon;
546 }
547
548 void FXTrayIcon::setTrayColor(FXColor color)
549 {
550 m_traycolor = color;
551 if(m_opaque) update();
552 }
553 #endif
554
onTimeout(FXObject *,FXSelector,void *)555 long FXTrayIcon::onTimeout(FXObject*, FXSelector, void*)
556 {
557 if(!m_popup->shown()) return 1;
558 FXint x,y, px, py;
559 FXuint button;
560 getCursorPosition(x,y,button);
561 translateCoordinatesTo(px,py,getParent(),x,y);
562 m_popup->contains(px,py) ? getApp()->addTimeout(this, ID_POPTIMEOUT, 10000) : m_popup->popdown();
563 return 1;
564 }
565 }
566