1 /////////////////////////////////////////////////////////////////////////
2 // File:        src/gtk/taskbarex.cpp
3 // Purpose:     wxTaskBarIconEx
4 // Author:      Vaclav Slavik
5 // Modified by: Paul Cornett / Rom Walton
6 // Created:     2004/05/29
7 // RCS-ID:      $Id$
8 // Copyright:   (c) Vaclav Slavik, 2004
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////
11 
12 #ifdef __GNUG__
13 #pragma implementation "taskbarex.h"
14 #endif
15 
16 #define GSocket GlibGSocket
17 #include <gtk/gtk.h>
18 #undef GSocket
19 
20 #include "stdwx.h"
21 
22 #include <libnotify/notify.h>
23 #include <glib.h>
24 #include <dlfcn.h>
25 
26 #include "BOINCGUIApp.h"
27 #include "gtk/taskbarex.h"
28 #include "BOINCTaskBar.h"
29 
30 
31 // Old Style
32 typedef NotifyNotification* (*__notify_notification_new_with_status_icon)
33 (
34 	const gchar *summary,
35     const gchar *body,
36 	const gchar *icon,
37     GtkStatusIcon *status_icon
38 );
39 
40 // New Style
41 typedef NotifyNotification* (*__notify_notification_new)
42 (
43      const char *summary,
44      const char *body,
45      const char *icon
46 );
47 
48 static void* notify_lib = NULL;
49 static __notify_notification_new_with_status_icon my_notify_notification_new_with_status_icon = NULL;
50 static __notify_notification_new my_notify_notification_new = NULL;
51 
52 
53 static GtkStatusIcon* g_pStatusIcon = NULL;
54 static NotifyNotification* g_pNotification = NULL;
55 
56 
57 //-----------------------------------------------------------------------------
58 
59 extern "C" {
60     static void
status_icon_activate(GtkStatusIcon *,wxTaskBarIconEx * taskBarIcon)61     status_icon_activate(GtkStatusIcon*, wxTaskBarIconEx* taskBarIcon)
62     {
63         wxTaskBarIconExEvent eventLeftDClick(wxEVT_TASKBAR_LEFT_DCLICK, taskBarIcon);
64         taskBarIcon->AddPendingEvent(eventLeftDClick);
65     }
66 
67     static void
status_icon_popup_menu(GtkStatusIcon *,guint,guint,wxTaskBarIconEx * taskBarIcon)68     status_icon_popup_menu(GtkStatusIcon*, guint, guint, wxTaskBarIconEx* taskBarIcon)
69     {
70         wxTaskBarIconExEvent eventDown(wxEVT_TASKBAR_RIGHT_DOWN, taskBarIcon);
71         taskBarIcon->AddPendingEvent(eventDown);
72         wxTaskBarIconExEvent eventUp(wxEVT_TASKBAR_RIGHT_UP, taskBarIcon);
73         taskBarIcon->AddPendingEvent(eventUp);
74     }
75 
76     static void
status_icon_notification_actions(NotifyNotification * notification,gchar * action,wxTaskBarIconEx * taskBarIcon)77     status_icon_notification_actions(NotifyNotification* notification, gchar *action, wxTaskBarIconEx* taskBarIcon)
78     {
79         if (strcmp(action, "default") == 0) {
80             taskBarIcon->FireUserClickedEvent();
81         }
82     }
83 
84     static void
status_icon_notification_closed(NotifyNotification * notification,wxTaskBarIconEx * taskBarIcon)85     status_icon_notification_closed(NotifyNotification* notification, wxTaskBarIconEx* taskBarIcon)
86     {
87         if (taskBarIcon->IsUserClicked()) {
88             wxTaskBarIconExEvent eventUserClicked(wxEVT_TASKBAR_BALLOON_USERCLICK, taskBarIcon);
89             taskBarIcon->AddPendingEvent(eventUserClicked);
90         } else {
91             wxTaskBarIconExEvent eventTimeout(wxEVT_TASKBAR_BALLOON_USERTIMEOUT, taskBarIcon);
92             taskBarIcon->AddPendingEvent(eventTimeout);
93         }
94 
95         taskBarIcon->ClearEvents();
96     }
97 }
98 
99 
100 //-----------------------------------------------------------------------------
101 
102 
103 static wxChar* wxTaskBarExWindow      = (wxChar*) wxT("wxTaskBarExWindow");
104 
105 
106 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_CREATED )
DEFINE_EVENT_TYPE(wxEVT_TASKBAR_CONTEXT_MENU)107 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_CONTEXT_MENU )
108 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_SELECT )
109 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_KEY_SELECT )
110 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_BALLOON_SHOW )
111 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_BALLOON_HIDE )
112 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_BALLOON_USERTIMEOUT )
113 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_BALLOON_USERCLICK )
114 DEFINE_EVENT_TYPE( wxEVT_TASKBAR_SHUTDOWN )
115 
116 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIconEx, wxEvtHandler)
117 
118 BEGIN_EVENT_TABLE (wxTaskBarIconEx, wxEvtHandler)
119 END_EVENT_TABLE ()
120 
121 
122 wxTaskBarIconEx::wxTaskBarIconEx()
123 {
124     wxTaskBarIconEx((wxChar*)wxTaskBarExWindow, 1);
125 }
126 
wxTaskBarIconEx(wxChar * szWindowTitle,wxInt32 iTaskbarID)127 wxTaskBarIconEx::wxTaskBarIconEx( wxChar* szWindowTitle, wxInt32 iTaskbarID )
128 {
129     m_pWnd = NULL;
130     m_iTaskbarID = iTaskbarID;
131     g_pStatusIcon = NULL;
132     g_pNotification = NULL;
133     m_bUserClicked = false;
134 
135     notify_lib = dlopen("libnotify.so", RTLD_NOW);
136     if (notify_lib) {
137         my_notify_notification_new_with_status_icon = (__notify_notification_new_with_status_icon)dlsym(notify_lib, "notify_notification_new_with_status_icon");
138         my_notify_notification_new = (__notify_notification_new)dlsym(notify_lib, "notify_notification_new");
139     }
140 
141     notify_init((const char*)wxString(szWindowTitle).mb_str());
142 }
143 
~wxTaskBarIconEx()144 wxTaskBarIconEx::~wxTaskBarIconEx()
145 {
146     m_bUserClicked = false;
147 
148     if (m_pWnd)
149     {
150         m_pWnd->PopEventHandler();
151         m_pWnd->Destroy();
152         m_pWnd = NULL;
153     }
154 
155     if (g_pNotification)
156     {
157         notify_notification_close(g_pNotification, NULL);
158         g_pNotification = NULL;
159     }
160 
161     if (g_pStatusIcon)
162     {
163         g_object_unref(g_pStatusIcon);
164         g_pStatusIcon = NULL;
165     }
166 
167     if (notify_lib) {
168         my_notify_notification_new_with_status_icon = NULL;
169         my_notify_notification_new = NULL;
170         dlclose(notify_lib);
171     }
172 }
173 
IsIconInstalled() const174 bool wxTaskBarIconEx::IsIconInstalled() const {
175     return (g_pStatusIcon != NULL);
176 }
177 
ClearEvents()178 void wxTaskBarIconEx::ClearEvents() {
179     m_bUserClicked = false;
180 }
181 
FireUserClickedEvent()182 void wxTaskBarIconEx::FireUserClickedEvent() {
183     m_bUserClicked = true;
184 }
185 
IsUserClicked()186 bool wxTaskBarIconEx::IsUserClicked() {
187     return m_bUserClicked;
188 }
189 
190 // Operations
SetIcon(const wxIcon & icon,const wxString & message)191 bool wxTaskBarIconEx::SetIcon(const wxIcon& icon, const wxString& message)
192 {
193     if (!IsOK())
194         return false;
195 
196     if (!icon.Ok())
197         return false;
198 
199     wxBitmap bitmap = icon;
200 
201     if (!g_pStatusIcon)
202     {
203         g_pStatusIcon = gtk_status_icon_new_from_pixbuf(bitmap.GetPixbuf());
204         g_signal_connect(g_pStatusIcon, "activate", G_CALLBACK(status_icon_activate), this);
205         g_signal_connect(g_pStatusIcon, "popup_menu", G_CALLBACK(status_icon_popup_menu), this);
206     }
207 
208     gtk_status_icon_set_from_pixbuf(g_pStatusIcon, bitmap.GetPixbuf());
209     if (!message.empty())
210     {
211         gtk_status_icon_set_tooltip_text(g_pStatusIcon, message.mb_str());
212     }
213     gtk_status_icon_set_visible(g_pStatusIcon, TRUE);
214 
215     return true;
216 }
217 
SetBalloon__returnIcon(const unsigned int iconballoon)218 static const char* SetBalloon__returnIcon(const unsigned int iconballoon) {
219     switch(iconballoon)
220     {
221         case BALLOONTYPE_INFO:
222             return(GTK_STOCK_DIALOG_INFO);
223             break;
224         case BALLOONTYPE_WARNING:
225             return(GTK_STOCK_DIALOG_WARNING);
226             break;
227         case BALLOONTYPE_ERROR:
228         default:
229             return(GTK_STOCK_DIALOG_ERROR);
230             break;
231     }
232     return(NULL);
233 }
234 
SetBalloon(const wxIcon & icon,const wxString title,const wxString message,unsigned int iconballoon)235 bool wxTaskBarIconEx::SetBalloon(const wxIcon& icon, const wxString title, const wxString message, unsigned int iconballoon)
236 {
237     wxLogTrace(wxT("Function Start/End"), wxT("wxTaskBarIconEx::SetBalloon - Function Begin"));
238 
239     bool retval = false;
240     GError* error = NULL;
241 
242     if (!IsOK())
243         return false;
244 
245     if (!icon.Ok())
246         return false;
247 
248     if (!SetIcon(icon, wxEmptyString))
249         return false;
250 
251     const char* desired_icon = SetBalloon__returnIcon(iconballoon);
252 
253     if (!g_pNotification)
254     {
255         // Old Style
256         if (my_notify_notification_new_with_status_icon) {
257             g_pNotification =
258                 (*my_notify_notification_new_with_status_icon)(
259                     title.mb_str(),
260                     message.mb_str(),
261                     desired_icon,
262                     g_pStatusIcon
263                 );
264         }
265 
266         // New Style
267         if (my_notify_notification_new) {
268             g_pNotification =
269                 (*my_notify_notification_new)(
270                     title.mb_str(),
271                     message.mb_str(),
272                     gtk_status_icon_get_icon_name(g_pStatusIcon)
273             );
274         }
275 
276         g_signal_connect(
277             g_pNotification,
278             "closed",
279             G_CALLBACK(status_icon_notification_closed),
280             this
281         );
282 
283         notify_notification_add_action(
284             g_pNotification,
285             "default",
286             "Do Default Action",
287             NOTIFY_ACTION_CALLBACK(status_icon_notification_actions),
288             this,
289             NULL
290         );
291     }
292     else
293     {
294         notify_notification_update(
295             g_pNotification,
296             title.mb_str(),
297             message.mb_str(),
298             desired_icon
299         );
300     }
301 
302     retval = notify_notification_show(g_pNotification, &error);
303     g_clear_error(&error);
304 
305     wxLogTrace(wxT("Function Start/End"), wxT("wxTaskBarIconEx::SetBalloon - Function End"));
306     return retval;
307 }
308 
QueueBalloon(const wxIcon & icon,const wxString title,const wxString message,unsigned int iconballoon)309 bool wxTaskBarIconEx::QueueBalloon(const wxIcon& icon, const wxString title, const wxString message, unsigned int iconballoon)
310 {
311     // There isn't two classifications of notifications on Linux as there is on Windows
312     return SetBalloon(icon, title, message, iconballoon);
313 }
314 
RemoveIcon()315 bool wxTaskBarIconEx::RemoveIcon()
316 {
317     if (!IsOK())
318         return false;
319 
320     if (g_pNotification)
321     {
322         notify_notification_close(g_pNotification, NULL);
323         g_pNotification = NULL;
324     }
325 
326     if (g_pStatusIcon)
327     {
328         g_object_unref(g_pStatusIcon);
329         g_pStatusIcon = NULL;
330     }
331 
332     return true;
333 }
334 
PopupMenu(wxMenu * menu)335 bool wxTaskBarIconEx::PopupMenu(wxMenu* menu)
336 {
337 #if wxUSE_MENUS
338 
339     if (m_pWnd == NULL)
340     {
341         m_pWnd = new wxTopLevelWindow(NULL, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
342         m_pWnd->PushEventHandler(this);
343     }
344 
345     wxPoint point(-1, -1);
346 #ifdef __WXUNIVERSAL__
347     point = wxGetMousePosition();
348 #endif
349 
350     m_pWnd->PopupMenu(menu, point);
351 
352 #endif // wxUSE_MENUS
353     return true;
354 }
355 
356