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