~cairo-dock-team/cairo-dock-plug-ins/plug-ins

« back to all changes in this revision

Viewing changes to Status-Notifier/src/applet-item.c

  • Committer: Matthieu Baerts
  • Date: 2014-10-19 00:26:10 UTC
  • Revision ID: matttbe@gmail.com-20141019002610-ulf26s9b4c4rw10r
We just switched from BZR to Git.
Follow us on Github: https://github.com/Cairo-Dock

Note: we will only use Github to manage our source code and all pull requests.
Please continue to report your bugs/ideas/messages on our forum or Launchpad! 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
* This file is a part of the Cairo-Dock project
3
 
*
4
 
* Copyright : (C) see the 'copyright' file.
5
 
* E-mail    : see the 'copyright' file.
6
 
*
7
 
* This program is free software; you can redistribute it and/or
8
 
* modify it under the terms of the GNU General Public License
9
 
* as published by the Free Software Foundation; either version 3
10
 
* of the License, or (at your option) any later version.
11
 
*
12
 
* This program is distributed in the hope that it will be useful,
13
 
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
* GNU General Public License for more details.
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
 
 
20
 
#include <stdlib.h>
21
 
#include <string.h>
22
 
#include <sys/types.h>
23
 
#include <unistd.h>
24
 
 
25
 
#include "applet-struct.h"
26
 
#include "applet-host.h"
27
 
#include "applet-draw.h"
28
 
#include "applet-notifications.h"
29
 
#include "applet-item.h"
30
 
 
31
 
#define CD_STATUS_NOTIFIER_ITEM_IFACE "org.kde.StatusNotifierItem"
32
 
#define CD_STATUS_NOTIFIER_ITEM_OBJ "/StatusNotifierItem"
33
 
 
34
 
#define CD_INDICATOR_APPLICATION_ITEM_OBJ "/org/ayatana/NotificationItem"
35
 
 
36
 
 
37
 
static CDCategoryEnum _find_category (const gchar *cCategory)
38
 
{
39
 
        if (!cCategory)
40
 
                return CD_CATEGORY_APPLICATION_STATUS;
41
 
        if (*cCategory == 'A')
42
 
                return CD_CATEGORY_APPLICATION_STATUS;
43
 
        if (*cCategory == 'C')
44
 
                return CD_CATEGORY_COMMUNICATIONS;
45
 
        if (*cCategory == 'S')
46
 
                return CD_CATEGORY_SYSTEM_SERVICES;
47
 
        if (*cCategory == 'H')
48
 
                return CD_CATEGORY_HARDWARE;
49
 
        return CD_CATEGORY_APPLICATION_STATUS;
50
 
}
51
 
 
52
 
static CDStatusEnum _find_status (const gchar *cStatus)
53
 
{
54
 
        cd_debug ("STATUS: %s", cStatus);
55
 
        if (!cStatus)
56
 
                return CD_STATUS_ACTIVE;
57
 
        if (*cStatus == 'N')
58
 
                return CD_STATUS_NEEDS_ATTENTION;
59
 
        if (*cStatus == 'A')
60
 
                return CD_STATUS_ACTIVE;
61
 
        if (*cStatus == 'P')
62
 
                return CD_STATUS_PASSIVE;
63
 
        return CD_STATUS_ACTIVE;
64
 
}
65
 
 
66
 
static void cd_free_tooltip (CDToolTip *pToolTip)
67
 
{
68
 
        if (pToolTip == NULL)
69
 
                return;
70
 
        g_free (pToolTip->cIconName);
71
 
        g_free (pToolTip->cTitle);
72
 
        g_free (pToolTip->cMessage);
73
 
        g_free (pToolTip);
74
 
}
75
 
 
76
 
static CDToolTip *_make_tooltip_from_dbus_struct (GValueArray *pToolTipTab)
77
 
{
78
 
        CDToolTip *pToolTip = NULL;
79
 
        if (pToolTipTab)
80
 
        {
81
 
                pToolTip = g_new0 (CDToolTip, 1);
82
 
                GValue *v = &pToolTipTab->values[0];
83
 
                if (v && G_VALUE_HOLDS_STRING (v))
84
 
                        pToolTip->cIconName = g_strdup (g_value_get_string (v));
85
 
                v = &pToolTipTab->values[2];
86
 
                if (v && G_VALUE_HOLDS_STRING (v))
87
 
                        pToolTip->cTitle = g_strdup (g_value_get_string (v));
88
 
                v = &pToolTipTab->values[3];
89
 
                if (v && G_VALUE_HOLDS_STRING (v))
90
 
                        pToolTip->cMessage = g_strdup (g_value_get_string (v));
91
 
                if (pToolTip->cMessage != NULL)
92
 
                {
93
 
                        if (strncmp (pToolTip->cMessage, "<qt>", 4) == 0)
94
 
                        {
95
 
                                gchar *str = pToolTip->cMessage;
96
 
                                int n = strlen (str);
97
 
                                *(str + n - 5) = '\0';
98
 
                                pToolTip->cMessage = g_strdup (str+4);
99
 
                                g_free (str);
100
 
                        }
101
 
                        /// remplacer <br/> par \n
102
 
                        
103
 
                        /// virer les <nobr> et </nobr>
104
 
                        
105
 
                        /// virer les <img src=...> et </img>
106
 
                        
107
 
                }
108
 
        }
109
 
        return pToolTip;
110
 
}
111
 
/* Not used
112
 
static void _show_item_tooltip (Icon *pIcon, CDStatusNotifierItem *pItem)
113
 
{
114
 
        gchar *cText = g_strdup_printf ("<b>%s</b>\n%s", pItem->pToolTip->cTitle, pItem->pToolTip->cMessage);
115
 
        gldi_dialog_show_temporary_with_icon (cText, pIcon, CAIRO_CONTAINER (myIcon->pSubDock), 4000, pItem->pToolTip->cIconName);
116
 
        g_free (cText);
117
 
}
118
 
*/
119
 
/*static void _show_item_status (Icon *pIcon, CDStatusNotifierItem *pItem)
120
 
{
121
 
        switch (pItem->iStatus)
122
 
        {
123
 
                case CD_STATUS_PASSIVE :
124
 
                        pIcon->fAlpha = 0.5;
125
 
                        gldi_icon_stop_attention (pIcon, myIcon->pSubDock);
126
 
                        cairo_dock_redraw_icon (pIcon, CAIRO_CONTAINER (myIcon->pSubDock));
127
 
                break;
128
 
                case CD_STATUS_ACTIVE :
129
 
                default:
130
 
                        pIcon->fAlpha = 1.;
131
 
                        gldi_icon_stop_attention (pIcon, myIcon->pSubDock);
132
 
                        cairo_dock_redraw_icon (pIcon, CAIRO_CONTAINER (myIcon->pSubDock));
133
 
                break;
134
 
                case CD_STATUS_NEEDS_ATTENTION:
135
 
                        pIcon->fAlpha = 1.;
136
 
                        gldi_icon_request_attention (pIcon, "rotate", 60);
137
 
                break;
138
 
        }
139
 
}*/
140
 
 
141
 
 
142
 
static void on_new_item_icon (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
143
 
{
144
 
        CD_APPLET_ENTER;
145
 
        cd_debug ("=== %s ()", __func__);
146
 
        
147
 
        g_free (pItem->cIconName);
148
 
        pItem->cIconName = cairo_dock_dbus_get_property_as_string (pItem->pProxyProps, CD_STATUS_NOTIFIER_ITEM_IFACE, "IconName");
149
 
        g_free (pItem->cAccessibleDesc);
150
 
        pItem->cAccessibleDesc = cairo_dock_dbus_get_property_as_string (pItem->pProxyProps, CD_STATUS_NOTIFIER_ITEM_IFACE, "IconAccessibleDesc");
151
 
        cd_debug ("===  new icon : %s", pItem->cIconName);
152
 
        
153
 
        if (pItem->iStatus != CD_STATUS_NEEDS_ATTENTION)
154
 
        {
155
 
                cd_satus_notifier_update_item_image (pItem);
156
 
        }
157
 
        CD_APPLET_LEAVE ();
158
 
}
159
 
 
160
 
static void on_new_item_attention_icon (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
161
 
{
162
 
        CD_APPLET_ENTER;
163
 
        cd_debug ("=== %s ()", __func__);
164
 
        
165
 
        g_free (pItem->cAttentionIconName);
166
 
        pItem->cAttentionIconName = cairo_dock_dbus_get_property_as_string (pItem->pProxyProps, CD_STATUS_NOTIFIER_ITEM_IFACE, "AttentionIconName");
167
 
        cd_debug ("===  new attention icon : %s", pItem->cAttentionIconName);
168
 
        
169
 
        if (pItem->iStatus == CD_STATUS_NEEDS_ATTENTION)
170
 
        {
171
 
                cd_satus_notifier_update_item_image (pItem);
172
 
        }
173
 
        CD_APPLET_LEAVE ();
174
 
}
175
 
 
176
 
static void on_new_item_status (DBusGProxy *proxy_item, const gchar *cStatus, CDStatusNotifierItem *pItem)
177
 
{
178
 
        CD_APPLET_ENTER;
179
 
        //g_print ("=== %s (%s)\n", __func__, cStatus);
180
 
        
181
 
        // get the new status
182
 
        CDStatusEnum iPrevStatus = pItem->iStatus;
183
 
        pItem->iStatus = _find_status (cStatus);
184
 
        if (pItem->iStatus == iPrevStatus)
185
 
                CD_APPLET_LEAVE ();
186
 
        
187
 
        // update the item
188
 
        if ((iPrevStatus == CD_STATUS_PASSIVE || pItem->iStatus == CD_STATUS_PASSIVE)
189
 
        && myConfig.bHideInactive)  // status was/is passive => hide/show the item.
190
 
        {
191
 
                if (myConfig.bCompactMode)
192
 
                {
193
 
                        cd_satus_notifier_reload_compact_mode ();
194
 
                }
195
 
                else
196
 
                {
197
 
                        if (pItem->iStatus == CD_STATUS_PASSIVE)  // remove passive item
198
 
                        {
199
 
                                Icon *pIcon = cd_satus_notifier_get_icon_from_item (pItem);
200
 
                                CD_APPLET_REMOVE_ICON_FROM_MY_ICONS_LIST (pIcon);
201
 
                        }
202
 
                        else  // add newly active item
203
 
                        {
204
 
                                Icon *pIcon = cd_satus_notifier_create_icon_for_item (pItem);
205
 
                                CD_APPLET_ADD_ICON_IN_MY_ICONS_LIST (pIcon);
206
 
                        }
207
 
                }
208
 
        }
209
 
        else  // status has changed => image has changed too.
210
 
        {
211
 
                cd_satus_notifier_update_item_image (pItem);
212
 
        }
213
 
        
214
 
        CD_APPLET_LEAVE ();
215
 
}
216
 
 
217
 
 
218
 
static void on_new_item_label (DBusGProxy *proxy_item, const gchar *cLabel, const gchar *cLabelGuide, CDStatusNotifierItem *pItem)
219
 
{
220
 
        CD_APPLET_ENTER;
221
 
        cd_debug ("=== %s (%s, %s)", __func__, cLabel, cLabelGuide);
222
 
        
223
 
        g_free (pItem->cLabel);
224
 
        pItem->cLabel = g_strdup (cLabel);
225
 
        g_free (pItem->cLabelGuide);
226
 
        pItem->cLabelGuide = g_strdup (cLabelGuide);
227
 
        
228
 
        CD_APPLET_LEAVE ();
229
 
}
230
 
 
231
 
static void on_new_item_theme_path (DBusGProxy *proxy_item, const gchar *cNewThemePath, CDStatusNotifierItem *pItem)
232
 
{
233
 
        CD_APPLET_ENTER;
234
 
        //g_print ("=== %s (%s)\n", __func__, cNewThemePath);
235
 
        
236
 
        if (g_strcmp0 (cNewThemePath, pItem->cIconThemePath) != 0)
237
 
        {
238
 
                if (pItem->cIconThemePath != NULL)  // if the item previously provided a theme, remove it first.
239
 
                        cd_satus_notifier_remove_theme_path (pItem->cIconThemePath);
240
 
                g_free (pItem->cIconThemePath);
241
 
                pItem->cIconThemePath = g_strdup (cNewThemePath);
242
 
                
243
 
                cd_satus_notifier_update_item_image (pItem);
244
 
        }
245
 
        
246
 
        CD_APPLET_LEAVE ();
247
 
}
248
 
 
249
 
 
250
 
static void on_new_item_title (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
251
 
{
252
 
        CD_APPLET_ENTER;
253
 
        //g_print ("=== %s ()\n", __func__);
254
 
        
255
 
        g_free (pItem->cTitle);
256
 
        pItem->cTitle = cairo_dock_dbus_get_property_as_string (pItem->pProxyProps, CD_STATUS_NOTIFIER_ITEM_IFACE, "Title");
257
 
        cd_debug ("===  new title : %s", pItem->cTitle);
258
 
        
259
 
        //gldi_icon_set_name (pIcon, cTitle);
260
 
        
261
 
        CD_APPLET_LEAVE ();
262
 
}
263
 
 
264
 
static void on_new_item_overlay_icon (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
265
 
{
266
 
        CD_APPLET_ENTER;
267
 
        //g_print ("=== %s ()\n", __func__);
268
 
        
269
 
        g_free (pItem->cOverlayIconName);
270
 
        pItem->cOverlayIconName = cairo_dock_dbus_get_property_as_string (pItem->pProxyProps, CD_STATUS_NOTIFIER_ITEM_IFACE, "OverlayIconName");
271
 
        //g_print ("===  new overlay : %s\n", pItem->cOverlayIconName);
272
 
        
273
 
        /*if (pIcon->pIconBuffer)
274
 
        {
275
 
                cairo_t *pIconContext = cairo_create (pIcon->pIconBuffer);
276
 
                cairo_dock_set_image_on_icon (pIconContext, pIcon->cFileName, pIcon, CAIRO_CONTAINER (myIcon->pSubDock));
277
 
                cairo_destroy (pIconContext);
278
 
                
279
 
                if (pItem->cOverlayIconName != NULL)
280
 
                {
281
 
                        CairoEmblem *pEmblem = cairo_dock_make_emblem (pItem->cOverlayIconName, pIcon);
282
 
                        cairo_dock_set_emblem_position (pEmblem, CAIRO_DOCK_EMBLEM_MIDDLE);
283
 
                        cairo_dock_draw_emblem_on_icon (pEmblem, pIcon, CAIRO_CONTAINER (myIcon->pSubDock));
284
 
                        cairo_dock_free_emblem (pEmblem);
285
 
                }
286
 
        }*/
287
 
        
288
 
        CD_APPLET_LEAVE ();
289
 
}
290
 
 
291
 
 
292
 
 
293
 
static void on_new_item_tooltip (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
294
 
{
295
 
        CD_APPLET_ENTER;
296
 
        //g_print ("=== %s ()\n", __func__);
297
 
        
298
 
        cd_free_tooltip (pItem->pToolTip);
299
 
        pItem->pToolTip = NULL;
300
 
        
301
 
        //gldi_dialogs_remove_on_icon (pIcon);
302
 
        
303
 
        GValueArray *pToolTipTab = cairo_dock_dbus_get_property_as_boxed (pItem->pProxyProps, CD_STATUS_NOTIFIER_ITEM_IFACE, "ToolTip");
304
 
        if (pToolTipTab)
305
 
        {
306
 
                pItem->pToolTip = _make_tooltip_from_dbus_struct (pToolTipTab);
307
 
                
308
 
                //if (pItem->pToolTip && pItem->pToolTip->cMessage != NULL)
309
 
                //      _show_item_tooltip (pIcon, pItem);
310
 
        }
311
 
        
312
 
        CD_APPLET_LEAVE ();
313
 
}
314
 
 
315
 
static void _on_item_proxy_destroyed (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
316
 
{
317
 
        if (pItem->bInvalid)
318
 
                return;
319
 
        CD_APPLET_ENTER;
320
 
        //g_print ("=== this item (%s) was suddenly removed\n", __func__, pItem->cService);
321
 
        
322
 
        cd_status_notifier_remove_item_in_list (pItem);
323
 
        
324
 
        if (myConfig.bCompactMode)
325
 
        {
326
 
                cd_satus_notifier_reload_compact_mode ();
327
 
        }
328
 
        else
329
 
        {
330
 
                Icon *pIcon = cd_satus_notifier_get_icon_from_item (pItem);
331
 
                CD_APPLET_REMOVE_ICON_FROM_MY_ICONS_LIST (pIcon);
332
 
        }
333
 
        
334
 
        cd_free_item (pItem);
335
 
        CD_APPLET_LEAVE ();
336
 
}
337
 
 
338
 
static gboolean _update_icon_delayed (CDStatusNotifierItem *pItem)
339
 
{
340
 
        cd_debug ("");
341
 
        if (pItem->cIconName != NULL)
342
 
        {
343
 
                cd_satus_notifier_update_item_image (pItem);
344
 
        }
345
 
        pItem->iSidUpdateIcon = 0;
346
 
        return FALSE;
347
 
}
348
 
gchar *cd_satus_notifier_search_item_icon_s_path (CDStatusNotifierItem *pItem, gint iSize)
349
 
{
350
 
        g_return_val_if_fail (pItem != NULL, NULL);
351
 
        gchar *cImageName = (pItem->iStatus == CD_STATUS_NEEDS_ATTENTION ? pItem->cAttentionIconName: pItem->cIconName);
352
 
        
353
 
        gchar *cIconPath = NULL;
354
 
        if (pItem->cIconThemePath != NULL)  // workaround pour des applis telles que dropbox qui trouvent malin de specifier des icones avec des noms hyper generiques (idle.png).
355
 
        {
356
 
                cIconPath = g_strdup_printf ("%s/%s", pItem->cIconThemePath, cImageName);
357
 
                if (! g_file_test (cIconPath, G_FILE_TEST_EXISTS))
358
 
                {
359
 
                        g_free (cIconPath);
360
 
                        cIconPath = NULL;
361
 
                }
362
 
        }
363
 
        
364
 
        if (cIconPath == NULL)
365
 
        {
366
 
                cIconPath = cairo_dock_search_icon_s_path (cImageName, iSize);
367
 
                if (cIconPath == NULL)  // in case we have a buggy app, try some heuristic
368
 
                {
369
 
                        cIconPath = cairo_dock_search_icon_s_path (pItem->cId, iSize);
370
 
                        if (cIconPath == NULL && pItem->pSurface == NULL)  // only use the fallback icon if the item is still empty (to not have an invisible item).
371
 
                        {
372
 
                                cIconPath = g_strdup (MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE);
373
 
                        }
374
 
                        
375
 
                        // skype strikes again ! on startup, it indicates its icon theme path (a temporary folder in /tmp); BUT it copies the icons after, so for a few seconds, the icons it tells us don't exist.
376
 
                        // so we trigger an update in a few seconds.
377
 
                        if (pItem->iSidUpdateIcon == 0)
378
 
                                pItem->iSidUpdateIcon = g_timeout_add_seconds (7, (GSourceFunc)_update_icon_delayed, pItem);
379
 
                }
380
 
        }
381
 
        else if (pItem->iSidUpdateIcon != 0)  // we found an icon, discard any pending update.
382
 
        {
383
 
                g_source_remove (pItem->iSidUpdateIcon);
384
 
                pItem->iSidUpdateIcon = 0;
385
 
        }
386
 
        
387
 
        return cIconPath;
388
 
}
389
 
 
390
 
static void _cd_cclosure_marshal_VOID__STRING_STRING (GClosure *closure,
391
 
        GValue *return_value G_GNUC_UNUSED,
392
 
        guint n_param_values,
393
 
        const GValue *param_values,
394
 
        gpointer invocation_hint G_GNUC_UNUSED,
395
 
        gpointer marshal_data)
396
 
{
397
 
        typedef void (*GMarshalFunc_VOID__STRING_STRING) (
398
 
                gpointer   data1,
399
 
                gchar      *arg_1,
400
 
                gchar      *arg_2,
401
 
                gpointer   data2);
402
 
        register GMarshalFunc_VOID__STRING_STRING callback;
403
 
        register GCClosure *cc = (GCClosure*) closure;
404
 
        register gpointer data1, data2;
405
 
        g_return_if_fail (n_param_values == 3);  // return_value est NULL ici, car la callback ne renvoit rien.
406
 
 
407
 
        if (G_CCLOSURE_SWAP_DATA (closure))
408
 
        {
409
 
                data1 = closure->data;
410
 
                data2 = g_value_peek_pointer (param_values + 0);
411
 
        }
412
 
        else
413
 
        {
414
 
                data1 = g_value_peek_pointer (param_values + 0);
415
 
                data2 = closure->data;
416
 
        }
417
 
        callback = (GMarshalFunc_VOID__STRING_STRING) (marshal_data ? marshal_data : cc->callback);
418
 
        
419
 
        g_return_if_fail (callback != NULL);
420
 
        g_return_if_fail (G_VALUE_HOLDS_STRING (param_values + 1));
421
 
        g_return_if_fail (G_VALUE_HOLDS_STRING (param_values + 2));
422
 
        
423
 
        callback (data1,
424
 
                (char*) g_value_get_string (param_values + 1),
425
 
                (char*) g_value_get_string (param_values + 2),
426
 
                data2);
427
 
}
428
 
 
429
 
CDStatusNotifierItem *cd_satus_notifier_create_item (const gchar *cService, const gchar *cObjectPath)
430
 
{
431
 
        g_return_val_if_fail  (cService != NULL, NULL);
432
 
        cd_debug ("=== %s (%s, %s)", __func__, cService, cObjectPath);
433
 
        
434
 
        // avoid creating an item that already exists. This can happen in the following case (skype):
435
 
        // watcher starts -> dock registers to it   -> dock asks the items - - - - - - - - - - - - - - - - - -> dock receives the items -> skype item is already here !
436
 
        //                -> skype creates its item -> 'new-item' is emitted -> dock receives the signal -> creates the item
437
 
        if (cd_satus_notifier_find_item_from_service (cService) != NULL)
438
 
        {
439
 
                cd_debug ("The service %s / %s is already listed, skip it", cService, cObjectPath);
440
 
                return NULL;
441
 
        }
442
 
        
443
 
        gchar *str = strchr (cService, '/');  // just to be sure.
444
 
        if (str)
445
 
                *str = '\0';
446
 
        
447
 
        // special case for Ubuntu indicators: we don't know their object path.
448
 
        gchar *cRealObjectPath = NULL;
449
 
        if (cObjectPath != NULL && strncmp (cObjectPath, CD_INDICATOR_APPLICATION_ITEM_OBJ, strlen (CD_INDICATOR_APPLICATION_ITEM_OBJ)) == 0 && g_str_has_suffix (cObjectPath, "/Menu"))
450
 
        {
451
 
                // I think this is because this path is actually the menu path, and fortunately it's just under the item object's path.
452
 
                const gchar *str = strrchr (cObjectPath, '/');
453
 
                if (str)
454
 
                {
455
 
                        cRealObjectPath = g_strndup (cObjectPath, str - cObjectPath);
456
 
                }
457
 
        }
458
 
        else if (cObjectPath == NULL || *cObjectPath == '\0')  // no path, let's assume it's the common one.
459
 
        {
460
 
                cObjectPath = CD_STATUS_NOTIFIER_ITEM_OBJ;
461
 
        }
462
 
        
463
 
        //g_print ("=== %s (cObjectPath: %s)\n", __func__, cRealObjectPath ? cRealObjectPath : cObjectPath);
464
 
        
465
 
        //\_________________ get the properties of the item.
466
 
        DBusGProxy *pProxyItemProp = cairo_dock_create_new_session_proxy (
467
 
                cService,
468
 
                cRealObjectPath ? cRealObjectPath : cObjectPath,
469
 
                DBUS_INTERFACE_PROPERTIES);
470
 
        if (pProxyItemProp == NULL)
471
 
                return NULL;
472
 
        //g_print ("=== owner : %s\n", dbus_g_proxy_get_bus_name (pProxyItemProp));
473
 
 
474
 
        //cd_debug ("%s, %s, %s", cService, cObjectPath, dbus_g_proxy_get_bus_name (pProxyItemProp));
475
 
 
476
 
        //g_print ("=== getting properties ...\n");
477
 
        GHashTable *hProps = cairo_dock_dbus_get_all_properties (pProxyItemProp, CD_STATUS_NOTIFIER_ITEM_IFACE);
478
 
        if (hProps == NULL)
479
 
                return NULL;
480
 
        
481
 
        // properties supported by KDE and Ubuntu.
482
 
        GValue *v;
483
 
        const gchar *cId = NULL;
484
 
        v = g_hash_table_lookup (hProps, "Id");
485
 
        if (v && G_VALUE_HOLDS_STRING (v))
486
 
        {
487
 
                cId = g_value_get_string (v);
488
 
        }
489
 
        cd_debug ("===   ID '%s'", cId);
490
 
        
491
 
        const gchar *cCategory = NULL;
492
 
        v = g_hash_table_lookup (hProps, "Category");  // (ApplicationStatus, Communications, SystemServices, Hardware) -> fOrder
493
 
        if (v && G_VALUE_HOLDS_STRING (v))
494
 
        {
495
 
                cCategory = g_value_get_string (v);
496
 
        }
497
 
        //g_print ("===   Category '%s'\n", cCategory);
498
 
        
499
 
        const gchar *cStatus = NULL;
500
 
        v = g_hash_table_lookup (hProps, "Status");  // (Passive, Active, NeedsAttention) -> demands attention
501
 
        if (v && G_VALUE_HOLDS_STRING (v))
502
 
        {
503
 
                cStatus = g_value_get_string (v);
504
 
        }
505
 
        //g_print ("===   Status '%s'\n", cStatus);
506
 
        
507
 
        const gchar *cIconName = NULL;
508
 
        v = g_hash_table_lookup (hProps, "IconName");  // -> cIFileName
509
 
        if (v && G_VALUE_HOLDS_STRING (v))
510
 
        {
511
 
                cIconName = g_value_get_string (v);
512
 
        }
513
 
        cd_debug ("===   IconName '%s'", cIconName);
514
 
        
515
 
        const gchar *cIconThemePath = NULL;
516
 
        v = g_hash_table_lookup (hProps, "IconThemePath");
517
 
        if (v && G_VALUE_HOLDS_STRING (v))
518
 
        {
519
 
                cIconThemePath = g_value_get_string (v);
520
 
        }
521
 
        cd_debug ("===   IconThemePath '%s'", cIconThemePath);
522
 
        
523
 
        const gchar *cAttentionIconName = NULL;
524
 
        v = g_hash_table_lookup (hProps, "AttentionIconName");  // -> keep for demands of attention
525
 
        if (v && G_VALUE_HOLDS_STRING (v))
526
 
        {
527
 
                cAttentionIconName = g_value_get_string (v);
528
 
        }
529
 
        //g_print ("===   AttentionIconName '%s'\n", cAttentionIconName);
530
 
        
531
 
        const gchar *cMenuPath = NULL;
532
 
        v = g_hash_table_lookup (hProps, "Menu");  // object path to a dbus-menu
533
 
        if (v && G_VALUE_HOLDS_BOXED(v))
534
 
        {
535
 
                cMenuPath = (gchar*) g_value_get_boxed (v);
536
 
        }
537
 
        cd_debug ("===   cMenuPath '%s'", cMenuPath);
538
 
        
539
 
        // properties supported by Ubuntu.
540
 
        gint iPosition = -1;
541
 
        v = g_hash_table_lookup (hProps, "XAyatanaOrderingIndex");
542
 
        if (v && G_VALUE_HOLDS_UINT(v))
543
 
        {
544
 
                iPosition = g_value_get_uint (v);
545
 
        }
546
 
        cd_debug ("===   iPosition '%d'", iPosition);
547
 
        // wrong values from the service !
548
 
        iPosition = -1;
549
 
        
550
 
        const gchar *cLabel = NULL;
551
 
        v = g_hash_table_lookup (hProps, "XAyatanaLabel");
552
 
        if (v && G_VALUE_HOLDS_STRING(v))
553
 
        {
554
 
                cLabel = g_value_get_string (v);
555
 
        }
556
 
        cd_debug ("===   cLabel '%s'", cLabel);
557
 
        
558
 
        const gchar *cLabelGuide = NULL;
559
 
        v = g_hash_table_lookup (hProps, "XAyatanaLabelGuide");
560
 
        if (v && G_VALUE_HOLDS_STRING(v))
561
 
        {
562
 
                cLabelGuide = g_value_get_string (v);
563
 
        }
564
 
        //g_print ("===   cLabelGuide '%s'\n", cLabelGuide);
565
 
 
566
 
        const gchar *cAccessibleDesc = NULL;
567
 
        v = g_hash_table_lookup (hProps, "IconAccessibleDesc");
568
 
        if (v && G_VALUE_HOLDS_STRING(v))
569
 
        {
570
 
                cAccessibleDesc = g_value_get_string (v);
571
 
        } // Updated with ApplicationIconChanged
572
 
        
573
 
        // properties supported by KDE.
574
 
        const gchar *cTitle = NULL;
575
 
        v = g_hash_table_lookup (hProps, "Title");  // -> cName
576
 
        if (v && G_VALUE_HOLDS_STRING (v))
577
 
        {
578
 
                cTitle = g_value_get_string (v);
579
 
        }
580
 
        cd_debug ("===   Title '%s'", cTitle);
581
 
        
582
 
        v = g_hash_table_lookup (hProps, "WindowId");
583
 
        guint iWindowId = 0;
584
 
        if (v && G_VALUE_HOLDS_UINT(v))
585
 
        {
586
 
                iWindowId = g_value_get_uint (v);
587
 
        }
588
 
        //g_print ("===   WindowId '%d'\n", iWindowId);
589
 
        
590
 
        const gchar *cOverlayIconName = NULL;
591
 
        v = g_hash_table_lookup (hProps, "OverlayIconName");  // -> emblem
592
 
        if (v && G_VALUE_HOLDS_STRING (v))
593
 
        {
594
 
                cOverlayIconName = g_value_get_string (v);
595
 
        }
596
 
        //g_print ("===   OverlayIconName '%s'\n", cOverlayIconName);
597
 
        
598
 
        const gchar *cAttentionMovieName = NULL;
599
 
        v = g_hash_table_lookup (hProps, "AttentionMovieName");  // -> idem
600
 
        if (v && G_VALUE_HOLDS_STRING (v))
601
 
        {
602
 
                cAttentionMovieName = g_value_get_string (v);
603
 
        }
604
 
        //g_print ("===   AttentionMovieName '%s'\n", cAttentionMovieName);
605
 
        
606
 
        GValueArray *pToolTipTab = NULL;
607
 
        v = g_hash_table_lookup (hProps, "ToolTip");
608
 
        if (v && G_VALUE_HOLDS_BOXED (v))
609
 
        {
610
 
                pToolTipTab = g_value_get_boxed (v);
611
 
        }
612
 
        
613
 
        gboolean bItemIsMenu = FALSE;  // "when is true the dbusmenu will be shown instead of emitting Activate()"
614
 
        v = g_hash_table_lookup (hProps, "ItemIsMenu");
615
 
        if (v && G_VALUE_HOLDS_BOOLEAN (v))
616
 
        {
617
 
                bItemIsMenu = g_value_get_boolean (v);
618
 
        }
619
 
        
620
 
        DBusGProxy *pProxyItem = cairo_dock_create_new_session_proxy (
621
 
                cService,
622
 
                cRealObjectPath ? cRealObjectPath : cObjectPath,
623
 
                CD_STATUS_NOTIFIER_ITEM_IFACE);
624
 
        if (pProxyItem == NULL)
625
 
                return NULL;
626
 
        
627
 
        //\_________________ create a new item.
628
 
        CDStatusNotifierItem *pItem = g_new0 (CDStatusNotifierItem, 1);
629
 
        pItem->cService = g_strdup (cService);
630
 
        pItem->pProxyProps = pProxyItemProp;
631
 
        pItem->pProxy = pProxyItem;
632
 
        pItem->cId = g_strdup (cId);
633
 
        pItem->iPosition = iPosition;
634
 
        pItem->cTitle = g_strdup (cTitle);
635
 
        pItem->cLabel = g_strdup (cLabel);
636
 
        pItem->cLabelGuide = g_strdup (cLabelGuide);
637
 
        pItem->cAccessibleDesc = g_strdup (cAccessibleDesc);
638
 
        ///pItem->cMenuPath = (cMenuPath ? g_strdup (cMenuPath) : g_strdup (cObjectPath));
639
 
        pItem->cMenuPath = g_strdup (cMenuPath);  // if NULL, we'll just send the ContextMenu() signal.
640
 
        pItem->iWindowId = iWindowId;
641
 
        pItem->iCategory = _find_category (cCategory);
642
 
        pItem->iStatus = _find_status (cStatus);
643
 
        pItem->cIconName = g_strdup (cIconName);
644
 
        pItem->cIconThemePath = g_strdup (cIconThemePath);
645
 
        pItem->cAttentionIconName = g_strdup (cAttentionIconName);
646
 
        pItem->cAttentionMovieName = g_strdup (cAttentionMovieName);
647
 
        pItem->cOverlayIconName = g_strdup (cOverlayIconName);
648
 
        pItem->bItemIsMenu = bItemIsMenu;
649
 
        if (pToolTipTab)
650
 
        {
651
 
                pItem->pToolTip = _make_tooltip_from_dbus_struct (pToolTipTab);
652
 
        }
653
 
        if (pItem->cIconThemePath && *pItem->cIconThemePath != '\0')  // on le rajoute au theme d'icones par defaut; comme le launcher-manager va deja chercher dedans pour charger l'icone, on n'a rien d'autre a faire.
654
 
        {
655
 
                cd_satus_notifier_add_theme_path (pItem->cIconThemePath);
656
 
        }
657
 
        
658
 
        // add it in the list already (must be done before we build its dbusmenu, otherwise the applet's icon might be hidden if it's the first item (=> not in its container), which prevents from initializing the menu correctly
659
 
        cd_status_notifier_add_item_in_list (pItem);
660
 
        
661
 
        // build the dbusmenu right now, so that the menu is complete when the user first clicks on the item (otherwise, the menu is not placed correctly).
662
 
        cd_satus_notifier_build_item_dbusmenu (pItem);
663
 
        
664
 
        //\_________________ track any changes in the item.
665
 
        // signals supported by both.
666
 
        dbus_g_proxy_add_signal(pProxyItem, "NewStatus",
667
 
                G_TYPE_STRING, G_TYPE_INVALID);
668
 
        dbus_g_proxy_connect_signal(pProxyItem, "NewStatus",
669
 
                G_CALLBACK(on_new_item_status), pItem, NULL);
670
 
        
671
 
        dbus_g_proxy_add_signal(pProxyItem, "NewIcon",
672
 
                G_TYPE_INVALID);
673
 
        dbus_g_proxy_connect_signal(pProxyItem, "NewIcon",
674
 
                G_CALLBACK(on_new_item_icon), pItem, NULL);
675
 
        
676
 
        dbus_g_proxy_add_signal(pProxyItem, "NewAttentionIcon",
677
 
                G_TYPE_INVALID);
678
 
        dbus_g_proxy_connect_signal(pProxyItem, "NewAttentionIcon",
679
 
                G_CALLBACK(on_new_item_attention_icon), pItem, NULL);
680
 
        
681
 
        // signals supported by Ubuntu.
682
 
        dbus_g_object_register_marshaller(_cd_cclosure_marshal_VOID__STRING_STRING,
683
 
                G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);     
684
 
        dbus_g_proxy_add_signal(pProxyItem, "XAyatanaNewLabel",
685
 
                G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
686
 
        dbus_g_proxy_connect_signal(pProxyItem, "XAyatanaNewLabel",
687
 
                G_CALLBACK(on_new_item_label), pItem, NULL);
688
 
        
689
 
        dbus_g_proxy_add_signal(pProxyItem, "NewIconThemePath",
690
 
                G_TYPE_INVALID);
691
 
        dbus_g_proxy_connect_signal(pProxyItem, "NewIconThemePath",
692
 
                G_CALLBACK(on_new_item_theme_path), pItem, NULL);
693
 
        
694
 
        // signals supported by KDE.
695
 
        dbus_g_proxy_add_signal(pProxyItem, "NewOverlayIcon",
696
 
                G_TYPE_INVALID);
697
 
        dbus_g_proxy_connect_signal(pProxyItem, "NewOverlayIcon",
698
 
                G_CALLBACK(on_new_item_overlay_icon), pItem, NULL);
699
 
        
700
 
        dbus_g_proxy_add_signal(pProxyItem, "NewTitle",
701
 
                G_TYPE_INVALID);
702
 
        dbus_g_proxy_connect_signal(pProxyItem, "NewTitle",
703
 
                G_CALLBACK(on_new_item_title), pItem, NULL);
704
 
        
705
 
        dbus_g_proxy_add_signal(pProxyItem, "NewToolTip",
706
 
                G_TYPE_INVALID);
707
 
        dbus_g_proxy_connect_signal(pProxyItem, "NewToolTip",
708
 
                G_CALLBACK(on_new_item_tooltip), pItem, NULL);
709
 
        
710
 
        g_signal_connect (G_OBJECT(pProxyItem), "destroy", G_CALLBACK (_on_item_proxy_destroyed), pItem);  // attention, dangereux car on va etre appele lorsqu'on detruit un item.
711
 
        
712
 
        g_hash_table_destroy (hProps);
713
 
        g_free (cRealObjectPath);
714
 
        return pItem;
715
 
}
716
 
 
717
 
void cd_free_item (CDStatusNotifierItem *pItem)
718
 
{
719
 
        if (pItem == NULL)
720
 
                return;
721
 
        pItem->bInvalid = TRUE;
722
 
        if (pItem->iSidPopupTooltip != 0)
723
 
                g_source_remove (pItem->iSidPopupTooltip);
724
 
        if (pItem->iSidUpdateIcon != 0)
725
 
                g_source_remove (pItem->iSidUpdateIcon);
726
 
        if (pItem->cIconThemePath)
727
 
                cd_satus_notifier_remove_theme_path (pItem->cIconThemePath);
728
 
        if (pItem->pMenu != NULL)
729
 
                g_object_unref (pItem->pMenu);  // will remove the 'reposition' callback too.
730
 
        g_object_unref (pItem->pProxy);
731
 
        g_object_unref (pItem->pProxyProps);
732
 
        g_free (pItem->cService);
733
 
        g_free (pItem->cId);
734
 
        g_free (pItem->cIconName);
735
 
        g_free (pItem->cAttentionIconName);
736
 
        g_free (pItem->cLabel);
737
 
        g_free (pItem->cLabelGuide);
738
 
        g_free (pItem->cAccessibleDesc);
739
 
        g_free (pItem->cTitle);
740
 
        g_free (pItem->cAttentionMovieName);
741
 
        g_free (pItem->cOverlayIconName);
742
 
        cd_free_tooltip (pItem->pToolTip);
743
 
        cairo_surface_destroy (pItem->pSurface);
744
 
        g_free (pItem);
745
 
}
746
 
 
747
 
 
748
 
static void _load_item_image (Icon *icon)
749
 
{
750
 
        int iWidth = cairo_dock_icon_get_allocated_width (icon);
751
 
        int iHeight = cairo_dock_icon_get_allocated_height (icon);
752
 
        
753
 
        CDStatusNotifierItem *pItem = cd_satus_notifier_get_item_from_icon (icon);
754
 
        gchar *cIconPath = cd_satus_notifier_search_item_icon_s_path (pItem, MAX (iWidth, iHeight));
755
 
        if (cIconPath != NULL && *cIconPath != '\0')
756
 
        {
757
 
                cairo_surface_t *pSurface = cairo_dock_create_surface_from_image_simple (cIconPath,
758
 
                        iWidth,
759
 
                        iHeight);
760
 
                cairo_dock_load_image_buffer_from_surface (&icon->image, pSurface, iWidth, iHeight);
761
 
        }
762
 
        g_free (cIconPath);
763
 
}
764
 
Icon *cd_satus_notifier_create_icon_for_item (CDStatusNotifierItem *pItem)
765
 
{
766
 
        g_return_val_if_fail (pItem != NULL, NULL);
767
 
        Icon *pIcon = cairo_dock_create_dummy_launcher (g_strdup (pItem->cTitle?pItem->cTitle:pItem->cId),
768
 
                g_strdup (pItem->cIconName),
769
 
                g_strdup (pItem->cService),
770
 
                NULL,
771
 
                pItem->iPosition > -1 ? pItem->iPosition : (int)pItem->iCategory);
772
 
        pIcon->iface.load_image = _load_item_image;  /// a voir...
773
 
        return pIcon;
774
 
}
775
 
 
776
 
 
777
 
CDStatusNotifierItem *cd_satus_notifier_get_item_from_icon (Icon *pIcon)
778
 
{
779
 
        CDStatusNotifierItem *pItem;
780
 
        GList *it;
781
 
        for (it = myData.pItems; it != NULL; it = it->next)
782
 
        {
783
 
                pItem = it->data;
784
 
                if (pIcon->cCommand && strcmp (pIcon->cCommand, pItem->cService) == 0)
785
 
                        return pItem;
786
 
        }
787
 
        return NULL;
788
 
}
789
 
 
790
 
Icon *cd_satus_notifier_get_icon_from_item (CDStatusNotifierItem *pItem)
791
 
{
792
 
        //g_print ("=== %s (%s)\n", __func__, pItem->cService);
793
 
        GList *ic, *pIcons = CD_APPLET_MY_ICONS_LIST;
794
 
        Icon *pIcon;
795
 
        for (ic = pIcons; ic != NULL; ic = ic->next)
796
 
        {
797
 
                pIcon = ic->data;
798
 
                //g_print ("===   %s \n", pIcon->cCommand);
799
 
                if (pIcon->cCommand && strcmp (pIcon->cCommand, pItem->cService) == 0)
800
 
                {
801
 
                        return pIcon;
802
 
                }
803
 
        }
804
 
        return NULL;
805
 
}
806
 
 
807
 
 
808
 
static gboolean _on_draw_menu_reposition (GtkWidget *pWidget, G_GNUC_UNUSED gpointer useless, CDStatusNotifierItem *pItem)
809
 
{
810
 
        g_return_val_if_fail (pItem != NULL, FALSE);
811
 
 
812
 
        int iMenuWidth = gtk_widget_get_allocated_width (pWidget);
813
 
 
814
 
        if (pItem->iMenuWidth != iMenuWidth)  // if the width has changed, reposition the menu to be sure it won't out of the screen.
815
 
        {
816
 
                pItem->iMenuWidth = iMenuWidth;
817
 
                gtk_menu_reposition (GTK_MENU (pWidget));
818
 
        }
819
 
        
820
 
        return FALSE; // FALSE to propagate the event further.
821
 
}
822
 
void cd_satus_notifier_build_item_dbusmenu (CDStatusNotifierItem *pItem)
823
 
{
824
 
        if (pItem->pMenu == NULL)  // menu not yet built
825
 
        {
826
 
                if (pItem->cMenuPath != NULL && *pItem->cMenuPath != '\0' && strcmp (pItem->cMenuPath, "/NO_DBUSMENU") != 0)  // hopefully, if the item doesn't provide a dbusmenu, it will not set something different as these 2 choices  (ex.: Klipper).
827
 
                {
828
 
                        pItem->pMenu = dbusmenu_gtkmenu_new ((gchar *)pItem->cService, (gchar *)pItem->cMenuPath);
829
 
                        if (g_object_is_floating (pItem->pMenu))  // claim ownership on the menu.
830
 
                                g_object_ref_sink (pItem->pMenu);
831
 
                        gldi_menu_init (GTK_WIDGET(pItem->pMenu), myIcon);
832
 
                        /* Position of the menu: GTK doesn't do its job :-/
833
 
                         * e.g. with Dropbox: the menu is out of the screen every time
834
 
                         * something has changed in this menu (it displays 'connecting',
835
 
                         * free space available, etc.) -> we need to reposition it.
836
 
                         * (maybe it's due to a delay because Python and DBus are slower...)
837
 
                         * We can't watch the 'configure' event (which should be triggered
838
 
                         * each time the menu is resized) because it seems this notification
839
 
                         * is not sent...
840
 
                         * This is why we need to watch the 'draw' event...
841
 
                         */
842
 
                        g_signal_connect (G_OBJECT (pItem->pMenu),
843
 
                                "draw",
844
 
                                G_CALLBACK (_on_draw_menu_reposition),
845
 
                                pItem);
846
 
                }
847
 
        }
848
 
}