1 /*******************************************************************************
2 * Goggles Music Manager *
3 ********************************************************************************
4 * Copyright (C) 2008-2021 by Sander Jansen. All Rights Reserved *
5 * --- *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see http://www.gnu.org/licenses. *
18 ********************************************************************************/
19 #ifndef _WIN32
20
21 #include "gmdefs.h"
22 #include "gmutils.h"
23
24 #include "xincs.h"
25
26 #include "icons.h"
27 #include "FXPNGIcon.h"
28
29 #include "GMTrack.h"
30 #include "GMPlayerManager.h"
31 #include "GMWindow.h"
32 #include "GMRemote.h"
33
34
35 #include "GMApp.h"
36 #include "GMTrayIcon.h"
37 #include "GMIconTheme.h"
38
39
40 enum {
41 SYSTEM_TRAY_REQUEST_DOCK =0,
42 SYSTEM_TRAY_BEGIN_MESSAGE =1,
43 SYSTEM_TRAY_CANCEL_MESSAGE=2,
44 };
45
46 enum {
47 SYSTEM_TRAY_HORIZONTAL = 0,
48 SYSTEM_TRAY_VERTICAL = 1,
49 SYSTEM_TRAY_UNKNOWN = 2
50 };
51
52
53 FXDEFMAP(GMTrayIcon) GMTrayIconMap[]={
54 FXMAPFUNC(SEL_PAINT,0,GMTrayIcon::onPaint),
55 FXMAPFUNC(SEL_CONFIGURE,0,GMTrayIcon::onConfigure),
56 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,GMTrayIcon::onLeftBtnPress),
57 FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,GMTrayIcon::onMiddleBtnPress),
58 FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,GMTrayIcon::onRightBtnRelease),
59 FXMAPFUNC(SEL_MOUSEWHEEL,0,GMTrayIcon::onMouseWheel),
60 FXMAPFUNC(SEL_QUERY_TIP,0,GMTrayIcon::onQueryTip),
61 };
62
63 FXIMPLEMENT(GMTrayIcon,GMPlug,GMTrayIconMap,ARRAYNUMBER(GMTrayIconMap));
64
65
GMTrayIcon(FXApp * a)66 GMTrayIcon::GMTrayIcon(FXApp * a) : GMPlug(a) {
67 flags|=FLAG_ENABLED;
68 }
69
~GMTrayIcon()70 GMTrayIcon::~GMTrayIcon(){
71 if (icon) delete icon;
72 }
73
updateIcon()74 void GMTrayIcon::updateIcon() {
75 if (icon) {
76 FXint size = icon->getWidth();
77
78 /// Delete the old
79 delete icon;
80 icon=nullptr;
81
82 /// Update
83 if (size<=16) {
84 icon = new FXPNGIcon(getApp(),gogglesmm_16_png,0,IMAGE_ALPHAGUESS);
85 icon->setVisual(getVisual());
86 if (size!=16) icon->scale(size,size,FOX_SCALE_BEST);
87 }
88 else {
89 icon = new FXPNGIcon(getApp(),gogglesmm_32_png,0,IMAGE_ALPHAGUESS);
90 icon->setVisual(getVisual());
91 if (size!=32) icon->scale(size,size,FOX_SCALE_BEST);
92 }
93
94 //icon->blend(GMPlayerManager::instance()->getPreferences().gui_tray_color);
95 icon->create();
96
97 // Mark Dirty
98 update();
99 }
100 }
101
display(const GMTrack & track)102 void GMTrayIcon::display(const GMTrack &track){
103 setToolTip(FXString::value("%s\n%s\n%s (%d)",track.title.text(),track.artist.text(),track.album.text(),track.year));
104 }
105
reset()106 void GMTrayIcon::reset() {
107 setToolTip(FXString::null);
108 }
109
findSystemTray()110 FXbool GMTrayIcon::findSystemTray() {
111 FXString systemtray = FXString::value("_NET_SYSTEM_TRAY_S%d",DefaultScreen((Display*)getApp()->getDisplay()));
112 Atom xtrayselection = XInternAtom((Display*)getApp()->getDisplay(),systemtray.text(),0);
113 if (xtrayselection!=None) {
114 xtraywindow = (FXID)XGetSelectionOwner((Display*)getApp()->getDisplay(),xtrayselection);
115 }
116 return (xtraywindow!=0);
117 }
118
119
requestDock()120 void GMTrayIcon::requestDock() {
121 if (xid && xtraywindow) {
122 XEvent ev;
123 memset(&ev,0,sizeof(ev));
124 ev.xclient.type = ClientMessage;
125 ev.xclient.window = xtraywindow;
126 ev.xclient.message_type = xtrayopcode;
127 ev.xclient.format = 32;
128 ev.xclient.data.l[0] = CurrentTime;
129 ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
130 ev.xclient.data.l[2] = xid;
131 XSendEvent((Display*)getApp()->getDisplay(),xtraywindow,False,NoEventMask,&ev);
132 XSync((Display*)getApp()->getDisplay(),0);
133 }
134 }
135
getTrayOrientation()136 FXuint GMTrayIcon::getTrayOrientation(){
137 if (xtrayorientation || xtrayxfceorientation) {
138 FXuint orientation;
139 Atom returntype;
140 int returnformat;
141 unsigned long nitems;
142 unsigned long nbytes;
143 long * bytes=nullptr;
144
145 if (xtrayorientation && (XGetWindowProperty((Display*)getApp()->getDisplay(),xtraywindow,xtrayorientation,0,2,False,XA_CARDINAL,&returntype,&returnformat,&nitems,&nbytes,(unsigned char**)&bytes)==Success) && returntype==XA_CARDINAL){
146 orientation=*(long*)bytes;
147 XFree(bytes);
148 return orientation;
149 }
150
151 if (bytes!=nullptr) {
152 XFree(bytes);
153 bytes=nullptr;
154 }
155
156 if (xtrayxfceorientation && (XGetWindowProperty((Display*)getApp()->getDisplay(),xtraywindow,xtrayxfceorientation,0,2,False,XA_CARDINAL,&returntype,&returnformat,&nitems,&nbytes,(unsigned char**)&bytes)==Success) && returntype==XA_CARDINAL){
157 orientation=*(long*)bytes;
158 XFree(bytes);
159 return orientation;
160 }
161
162 if (bytes!=nullptr) {
163 XFree(bytes);
164 bytes=nullptr;
165 }
166 }
167 return SYSTEM_TRAY_UNKNOWN;
168 }
169
170
getTrayVisual()171 FXuint GMTrayIcon::getTrayVisual(){
172 if (xtrayvisual) {
173 FXuint visualid;
174 Atom returntype;
175 int returnformat;
176 unsigned long nitems;
177 unsigned long nbytes;
178 long * bytes=nullptr;
179
180 if (xtrayvisual && (XGetWindowProperty((Display*)getApp()->getDisplay(),xtraywindow,xtrayvisual,0,2,False,XA_VISUALID,&returntype,&returnformat,&nitems,&nbytes,(unsigned char**)&bytes)==Success) && returntype==XA_VISUALID){
181 visualid=*(long*)bytes;
182 XFree(bytes);
183 return visualid;
184 }
185
186 if (bytes!=nullptr) {
187 XFree(bytes);
188 bytes=nullptr;
189 }
190 }
191 return 0;
192 }
193
create()194 void GMTrayIcon::create(){
195 xtrayopcode = (FXID)XInternAtom((Display*)getApp()->getDisplay(),"_NET_SYSTEM_TRAY_OPCODE",0);
196 xtrayorientation = (FXID)XInternAtom((Display*)getApp()->getDisplay(),"_NET_SYSTEM_TRAY_ORIENTATION",0);
197 xtrayxfceorientation = (FXID)XInternAtom((Display*)getApp()->getDisplay(),"_NET_XFCE_TRAY_MANAGER_ORIENTATION",0);
198 xtrayvisual = (FXID)XInternAtom((Display*)getApp()->getDisplay(),"_NET_SYSTEM_TRAY_VISUAL",0);
199
200 GMPlug::create();
201 if (xid) {
202
203 /// Set the size hints...
204 XSizeHints size;
205 size.flags=PMinSize|PMaxSize|PBaseSize|PAspect;
206 size.x=0;
207 size.y=0;
208 size.width=0;
209 size.height=0;
210 size.width_inc=0;
211 size.height_inc=0;
212 size.min_aspect.x=1;
213 size.min_aspect.y=1;
214 size.max_aspect.x=1;
215 size.max_aspect.y=1;
216 size.win_gravity=0;
217 size.win_gravity=0;
218
219 size.min_width=8;
220 size.min_height=8;
221 size.max_width=64;
222 size.max_height=64;
223 size.base_width=32;
224 size.base_height=32;
225 XSetWMNormalHints((Display*)getApp()->getDisplay(),xid,&size);
226
227 dock();
228 }
229 }
230
dock()231 void GMTrayIcon::dock() {
232 if (findSystemTray()){
233
234 opaque=false;
235
236 if (!opaque) {
237 /// Don't draw the background
238 XSetWindowAttributes sattr;
239 sattr.background_pixmap = ParentRelative;
240 XChangeWindowAttributes((Display*)getApp()->getDisplay(),xid,CWBackPixmap,&sattr);
241 }
242
243 requestDock();
244 }
245 }
246
onConfigure(FXObject *,FXSelector,void * ptr)247 long GMTrayIcon::onConfigure(FXObject*,FXSelector,void*ptr){
248 FXEvent * event = (FXEvent*)ptr;
249
250 FXuint orientation = getTrayOrientation();
251 FXint size=0;
252 switch(orientation){
253 case SYSTEM_TRAY_HORIZONTAL: size=event->rect.h; break;
254 case SYSTEM_TRAY_VERTICAL : size=event->rect.w; break;
255 default : size=FXMAX(event->rect.h,event->rect.w); break;
256 };
257
258 resize(size,size);
259
260 XSizeHints hint;
261 hint.flags=PMinSize|PMaxSize|PBaseSize;
262 hint.min_width = hint.max_width = hint.base_width = size;
263 hint.min_height = hint.max_height = hint.base_height = size;
264 XSetWMNormalHints((Display*)getApp()->getDisplay(),xid,&hint);
265
266
267 if (icon && icon->getWidth()!=size) {
268 delete icon;
269 icon=nullptr;
270 }
271
272 if (icon==nullptr) {
273 if (size<=16) {
274 icon = new FXPNGIcon(getApp(),gogglesmm_16_png,0,IMAGE_ALPHAGUESS);
275 icon->setVisual(getVisual());
276 if (size!=16) icon->scale(size,size,FOX_SCALE_BEST);
277 }
278 else {
279 icon = new FXPNGIcon(getApp(),gogglesmm_32_png,0,IMAGE_ALPHAGUESS);
280 icon->setVisual(getVisual());
281 if (size!=32) icon->scale(size,size,FOX_SCALE_BEST);
282 }
283 //icon->blend(GMPlayerManager::instance()->getPreferences().gui_tray_color);
284 icon->create();
285 }
286 return 1;
287 }
288
289
onRightBtnRelease(FXObject *,FXSelector,void * ptr)290 long GMTrayIcon::onRightBtnRelease(FXObject*,FXSelector,void*ptr){
291 FXEvent * event=(FXEvent*)ptr;
292 if (event->moved) return 0;
293 GMMenuPane pane(this,POPUP_SHRINKWRAP);
294 new GMMenuCommand(&pane,pane.tr("Play"),GMIconTheme::instance()->icon_play,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_PLAYPAUSEMENU);
295 new GMMenuCommand(&pane,pane.tr("Stop"),GMIconTheme::instance()->icon_stop,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_STOP);
296 new GMMenuCommand(&pane,pane.tr("Previous Track"),GMIconTheme::instance()->icon_prev,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_PREV);
297 new GMMenuCommand(&pane,pane.tr("Next Track"),GMIconTheme::instance()->icon_next,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_NEXT);
298 new FXMenuSeparator(&pane);
299 new GMMenuCommand(&pane,tr("Quit"),GMIconTheme::instance()->icon_exit,GMPlayerManager::instance()->getMainWindow(),GMWindow::ID_QUIT);
300 gm_set_window_cursor(&pane,getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
301 pane.create();
302 ewmh_change_window_type(&pane,WINDOWTYPE_POPUP_MENU);
303 gm_run_popup_menu(&pane,event->root_x+1,event->root_y+1);
304 return 1;
305 }
306
307
onLeftBtnPress(FXObject *,FXSelector,void *)308 long GMTrayIcon::onLeftBtnPress(FXObject*,FXSelector,void*){
309 GMPlayerManager::instance()->cmd_toggle_shown();
310 return 1;
311 }
312
onMiddleBtnPress(FXObject *,FXSelector,void *)313 long GMTrayIcon::onMiddleBtnPress(FXObject*,FXSelector,void*){
314 GMPlayerManager::instance()->cmd_playpause();
315 return 1;
316 }
317
onMouseWheel(FXObject * sender,FXSelector sel,void * ptr)318 long GMTrayIcon::onMouseWheel(FXObject*sender,FXSelector sel,void*ptr){
319 GMPlayerManager::instance()->getMainWindow()->handle(sender,sel,ptr);
320 return 1;
321 }
322
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)323 long GMTrayIcon::onQueryTip(FXObject*sender,FXSelector sel,void* ptr){
324 if(FXTopWindow::onQueryTip(sender,sel,ptr)) return 1;
325 if((flags&FLAG_TIP) && !tip.empty() && !grabbed() ){
326 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip);
327 return 1;
328 }
329 return 0;
330 }
331
332
onPaint(FXObject *,FXSelector,void *)333 long GMTrayIcon::onPaint(FXObject*,FXSelector,void*){
334 if (icon) {
335 FXDCWindow dc(this);
336 dc.drawIcon(icon,0,0);
337 }
338 return 1;
339 }
340 #endif