2
* This file is a part of the Cairo-Dock project
4
* Copyright : (C) see the 'copyright' file.
5
* E-mail : see the 'copyright' file.
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.
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/>.
22
#include <sys/types.h>
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"
31
#define CD_STATUS_NOTIFIER_ITEM_IFACE "org.kde.StatusNotifierItem"
32
#define CD_STATUS_NOTIFIER_ITEM_OBJ "/StatusNotifierItem"
34
#define CD_INDICATOR_APPLICATION_ITEM_OBJ "/org/ayatana/NotificationItem"
37
static CDCategoryEnum _find_category (const gchar *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;
52
static CDStatusEnum _find_status (const gchar *cStatus)
54
cd_debug ("STATUS: %s", cStatus);
56
return CD_STATUS_ACTIVE;
58
return CD_STATUS_NEEDS_ATTENTION;
60
return CD_STATUS_ACTIVE;
62
return CD_STATUS_PASSIVE;
63
return CD_STATUS_ACTIVE;
66
static void cd_free_tooltip (CDToolTip *pToolTip)
70
g_free (pToolTip->cIconName);
71
g_free (pToolTip->cTitle);
72
g_free (pToolTip->cMessage);
76
static CDToolTip *_make_tooltip_from_dbus_struct (GValueArray *pToolTipTab)
78
CDToolTip *pToolTip = NULL;
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)
93
if (strncmp (pToolTip->cMessage, "<qt>", 4) == 0)
95
gchar *str = pToolTip->cMessage;
97
*(str + n - 5) = '\0';
98
pToolTip->cMessage = g_strdup (str+4);
101
/// remplacer <br/> par \n
103
/// virer les <nobr> et </nobr>
105
/// virer les <img src=...> et </img>
112
static void _show_item_tooltip (Icon *pIcon, CDStatusNotifierItem *pItem)
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);
119
/*static void _show_item_status (Icon *pIcon, CDStatusNotifierItem *pItem)
121
switch (pItem->iStatus)
123
case CD_STATUS_PASSIVE :
125
gldi_icon_stop_attention (pIcon, myIcon->pSubDock);
126
cairo_dock_redraw_icon (pIcon, CAIRO_CONTAINER (myIcon->pSubDock));
128
case CD_STATUS_ACTIVE :
131
gldi_icon_stop_attention (pIcon, myIcon->pSubDock);
132
cairo_dock_redraw_icon (pIcon, CAIRO_CONTAINER (myIcon->pSubDock));
134
case CD_STATUS_NEEDS_ATTENTION:
136
gldi_icon_request_attention (pIcon, "rotate", 60);
142
static void on_new_item_icon (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
145
cd_debug ("=== %s ()", __func__);
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);
153
if (pItem->iStatus != CD_STATUS_NEEDS_ATTENTION)
155
cd_satus_notifier_update_item_image (pItem);
160
static void on_new_item_attention_icon (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
163
cd_debug ("=== %s ()", __func__);
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);
169
if (pItem->iStatus == CD_STATUS_NEEDS_ATTENTION)
171
cd_satus_notifier_update_item_image (pItem);
176
static void on_new_item_status (DBusGProxy *proxy_item, const gchar *cStatus, CDStatusNotifierItem *pItem)
179
//g_print ("=== %s (%s)\n", __func__, cStatus);
181
// get the new status
182
CDStatusEnum iPrevStatus = pItem->iStatus;
183
pItem->iStatus = _find_status (cStatus);
184
if (pItem->iStatus == iPrevStatus)
188
if ((iPrevStatus == CD_STATUS_PASSIVE || pItem->iStatus == CD_STATUS_PASSIVE)
189
&& myConfig.bHideInactive) // status was/is passive => hide/show the item.
191
if (myConfig.bCompactMode)
193
cd_satus_notifier_reload_compact_mode ();
197
if (pItem->iStatus == CD_STATUS_PASSIVE) // remove passive item
199
Icon *pIcon = cd_satus_notifier_get_icon_from_item (pItem);
200
CD_APPLET_REMOVE_ICON_FROM_MY_ICONS_LIST (pIcon);
202
else // add newly active item
204
Icon *pIcon = cd_satus_notifier_create_icon_for_item (pItem);
205
CD_APPLET_ADD_ICON_IN_MY_ICONS_LIST (pIcon);
209
else // status has changed => image has changed too.
211
cd_satus_notifier_update_item_image (pItem);
218
static void on_new_item_label (DBusGProxy *proxy_item, const gchar *cLabel, const gchar *cLabelGuide, CDStatusNotifierItem *pItem)
221
cd_debug ("=== %s (%s, %s)", __func__, cLabel, cLabelGuide);
223
g_free (pItem->cLabel);
224
pItem->cLabel = g_strdup (cLabel);
225
g_free (pItem->cLabelGuide);
226
pItem->cLabelGuide = g_strdup (cLabelGuide);
231
static void on_new_item_theme_path (DBusGProxy *proxy_item, const gchar *cNewThemePath, CDStatusNotifierItem *pItem)
234
//g_print ("=== %s (%s)\n", __func__, cNewThemePath);
236
if (g_strcmp0 (cNewThemePath, pItem->cIconThemePath) != 0)
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);
243
cd_satus_notifier_update_item_image (pItem);
250
static void on_new_item_title (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
253
//g_print ("=== %s ()\n", __func__);
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);
259
//gldi_icon_set_name (pIcon, cTitle);
264
static void on_new_item_overlay_icon (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
267
//g_print ("=== %s ()\n", __func__);
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);
273
/*if (pIcon->pIconBuffer)
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);
279
if (pItem->cOverlayIconName != NULL)
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);
293
static void on_new_item_tooltip (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
296
//g_print ("=== %s ()\n", __func__);
298
cd_free_tooltip (pItem->pToolTip);
299
pItem->pToolTip = NULL;
301
//gldi_dialogs_remove_on_icon (pIcon);
303
GValueArray *pToolTipTab = cairo_dock_dbus_get_property_as_boxed (pItem->pProxyProps, CD_STATUS_NOTIFIER_ITEM_IFACE, "ToolTip");
306
pItem->pToolTip = _make_tooltip_from_dbus_struct (pToolTipTab);
308
//if (pItem->pToolTip && pItem->pToolTip->cMessage != NULL)
309
// _show_item_tooltip (pIcon, pItem);
315
static void _on_item_proxy_destroyed (DBusGProxy *proxy_item, CDStatusNotifierItem *pItem)
320
//g_print ("=== this item (%s) was suddenly removed\n", __func__, pItem->cService);
322
cd_status_notifier_remove_item_in_list (pItem);
324
if (myConfig.bCompactMode)
326
cd_satus_notifier_reload_compact_mode ();
330
Icon *pIcon = cd_satus_notifier_get_icon_from_item (pItem);
331
CD_APPLET_REMOVE_ICON_FROM_MY_ICONS_LIST (pIcon);
334
cd_free_item (pItem);
338
static gboolean _update_icon_delayed (CDStatusNotifierItem *pItem)
341
if (pItem->cIconName != NULL)
343
cd_satus_notifier_update_item_image (pItem);
345
pItem->iSidUpdateIcon = 0;
348
gchar *cd_satus_notifier_search_item_icon_s_path (CDStatusNotifierItem *pItem, gint iSize)
350
g_return_val_if_fail (pItem != NULL, NULL);
351
gchar *cImageName = (pItem->iStatus == CD_STATUS_NEEDS_ATTENTION ? pItem->cAttentionIconName: pItem->cIconName);
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).
356
cIconPath = g_strdup_printf ("%s/%s", pItem->cIconThemePath, cImageName);
357
if (! g_file_test (cIconPath, G_FILE_TEST_EXISTS))
364
if (cIconPath == NULL)
366
cIconPath = cairo_dock_search_icon_s_path (cImageName, iSize);
367
if (cIconPath == NULL) // in case we have a buggy app, try some heuristic
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).
372
cIconPath = g_strdup (MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE);
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);
381
else if (pItem->iSidUpdateIcon != 0) // we found an icon, discard any pending update.
383
g_source_remove (pItem->iSidUpdateIcon);
384
pItem->iSidUpdateIcon = 0;
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)
397
typedef void (*GMarshalFunc_VOID__STRING_STRING) (
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.
407
if (G_CCLOSURE_SWAP_DATA (closure))
409
data1 = closure->data;
410
data2 = g_value_peek_pointer (param_values + 0);
414
data1 = g_value_peek_pointer (param_values + 0);
415
data2 = closure->data;
417
callback = (GMarshalFunc_VOID__STRING_STRING) (marshal_data ? marshal_data : cc->callback);
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));
424
(char*) g_value_get_string (param_values + 1),
425
(char*) g_value_get_string (param_values + 2),
429
CDStatusNotifierItem *cd_satus_notifier_create_item (const gchar *cService, const gchar *cObjectPath)
431
g_return_val_if_fail (cService != NULL, NULL);
432
cd_debug ("=== %s (%s, %s)", __func__, cService, cObjectPath);
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)
439
cd_debug ("The service %s / %s is already listed, skip it", cService, cObjectPath);
443
gchar *str = strchr (cService, '/'); // just to be sure.
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"))
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, '/');
455
cRealObjectPath = g_strndup (cObjectPath, str - cObjectPath);
458
else if (cObjectPath == NULL || *cObjectPath == '\0') // no path, let's assume it's the common one.
460
cObjectPath = CD_STATUS_NOTIFIER_ITEM_OBJ;
463
//g_print ("=== %s (cObjectPath: %s)\n", __func__, cRealObjectPath ? cRealObjectPath : cObjectPath);
465
//\_________________ get the properties of the item.
466
DBusGProxy *pProxyItemProp = cairo_dock_create_new_session_proxy (
468
cRealObjectPath ? cRealObjectPath : cObjectPath,
469
DBUS_INTERFACE_PROPERTIES);
470
if (pProxyItemProp == NULL)
472
//g_print ("=== owner : %s\n", dbus_g_proxy_get_bus_name (pProxyItemProp));
474
//cd_debug ("%s, %s, %s", cService, cObjectPath, dbus_g_proxy_get_bus_name (pProxyItemProp));
476
//g_print ("=== getting properties ...\n");
477
GHashTable *hProps = cairo_dock_dbus_get_all_properties (pProxyItemProp, CD_STATUS_NOTIFIER_ITEM_IFACE);
481
// properties supported by KDE and Ubuntu.
483
const gchar *cId = NULL;
484
v = g_hash_table_lookup (hProps, "Id");
485
if (v && G_VALUE_HOLDS_STRING (v))
487
cId = g_value_get_string (v);
489
cd_debug ("=== ID '%s'", cId);
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))
495
cCategory = g_value_get_string (v);
497
//g_print ("=== Category '%s'\n", cCategory);
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))
503
cStatus = g_value_get_string (v);
505
//g_print ("=== Status '%s'\n", cStatus);
507
const gchar *cIconName = NULL;
508
v = g_hash_table_lookup (hProps, "IconName"); // -> cIFileName
509
if (v && G_VALUE_HOLDS_STRING (v))
511
cIconName = g_value_get_string (v);
513
cd_debug ("=== IconName '%s'", cIconName);
515
const gchar *cIconThemePath = NULL;
516
v = g_hash_table_lookup (hProps, "IconThemePath");
517
if (v && G_VALUE_HOLDS_STRING (v))
519
cIconThemePath = g_value_get_string (v);
521
cd_debug ("=== IconThemePath '%s'", cIconThemePath);
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))
527
cAttentionIconName = g_value_get_string (v);
529
//g_print ("=== AttentionIconName '%s'\n", cAttentionIconName);
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))
535
cMenuPath = (gchar*) g_value_get_boxed (v);
537
cd_debug ("=== cMenuPath '%s'", cMenuPath);
539
// properties supported by Ubuntu.
541
v = g_hash_table_lookup (hProps, "XAyatanaOrderingIndex");
542
if (v && G_VALUE_HOLDS_UINT(v))
544
iPosition = g_value_get_uint (v);
546
cd_debug ("=== iPosition '%d'", iPosition);
547
// wrong values from the service !
550
const gchar *cLabel = NULL;
551
v = g_hash_table_lookup (hProps, "XAyatanaLabel");
552
if (v && G_VALUE_HOLDS_STRING(v))
554
cLabel = g_value_get_string (v);
556
cd_debug ("=== cLabel '%s'", cLabel);
558
const gchar *cLabelGuide = NULL;
559
v = g_hash_table_lookup (hProps, "XAyatanaLabelGuide");
560
if (v && G_VALUE_HOLDS_STRING(v))
562
cLabelGuide = g_value_get_string (v);
564
//g_print ("=== cLabelGuide '%s'\n", cLabelGuide);
566
const gchar *cAccessibleDesc = NULL;
567
v = g_hash_table_lookup (hProps, "IconAccessibleDesc");
568
if (v && G_VALUE_HOLDS_STRING(v))
570
cAccessibleDesc = g_value_get_string (v);
571
} // Updated with ApplicationIconChanged
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))
578
cTitle = g_value_get_string (v);
580
cd_debug ("=== Title '%s'", cTitle);
582
v = g_hash_table_lookup (hProps, "WindowId");
584
if (v && G_VALUE_HOLDS_UINT(v))
586
iWindowId = g_value_get_uint (v);
588
//g_print ("=== WindowId '%d'\n", iWindowId);
590
const gchar *cOverlayIconName = NULL;
591
v = g_hash_table_lookup (hProps, "OverlayIconName"); // -> emblem
592
if (v && G_VALUE_HOLDS_STRING (v))
594
cOverlayIconName = g_value_get_string (v);
596
//g_print ("=== OverlayIconName '%s'\n", cOverlayIconName);
598
const gchar *cAttentionMovieName = NULL;
599
v = g_hash_table_lookup (hProps, "AttentionMovieName"); // -> idem
600
if (v && G_VALUE_HOLDS_STRING (v))
602
cAttentionMovieName = g_value_get_string (v);
604
//g_print ("=== AttentionMovieName '%s'\n", cAttentionMovieName);
606
GValueArray *pToolTipTab = NULL;
607
v = g_hash_table_lookup (hProps, "ToolTip");
608
if (v && G_VALUE_HOLDS_BOXED (v))
610
pToolTipTab = g_value_get_boxed (v);
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))
617
bItemIsMenu = g_value_get_boolean (v);
620
DBusGProxy *pProxyItem = cairo_dock_create_new_session_proxy (
622
cRealObjectPath ? cRealObjectPath : cObjectPath,
623
CD_STATUS_NOTIFIER_ITEM_IFACE);
624
if (pProxyItem == NULL)
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;
651
pItem->pToolTip = _make_tooltip_from_dbus_struct (pToolTipTab);
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.
655
cd_satus_notifier_add_theme_path (pItem->cIconThemePath);
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);
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);
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);
671
dbus_g_proxy_add_signal(pProxyItem, "NewIcon",
673
dbus_g_proxy_connect_signal(pProxyItem, "NewIcon",
674
G_CALLBACK(on_new_item_icon), pItem, NULL);
676
dbus_g_proxy_add_signal(pProxyItem, "NewAttentionIcon",
678
dbus_g_proxy_connect_signal(pProxyItem, "NewAttentionIcon",
679
G_CALLBACK(on_new_item_attention_icon), pItem, NULL);
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);
689
dbus_g_proxy_add_signal(pProxyItem, "NewIconThemePath",
691
dbus_g_proxy_connect_signal(pProxyItem, "NewIconThemePath",
692
G_CALLBACK(on_new_item_theme_path), pItem, NULL);
694
// signals supported by KDE.
695
dbus_g_proxy_add_signal(pProxyItem, "NewOverlayIcon",
697
dbus_g_proxy_connect_signal(pProxyItem, "NewOverlayIcon",
698
G_CALLBACK(on_new_item_overlay_icon), pItem, NULL);
700
dbus_g_proxy_add_signal(pProxyItem, "NewTitle",
702
dbus_g_proxy_connect_signal(pProxyItem, "NewTitle",
703
G_CALLBACK(on_new_item_title), pItem, NULL);
705
dbus_g_proxy_add_signal(pProxyItem, "NewToolTip",
707
dbus_g_proxy_connect_signal(pProxyItem, "NewToolTip",
708
G_CALLBACK(on_new_item_tooltip), pItem, NULL);
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.
712
g_hash_table_destroy (hProps);
713
g_free (cRealObjectPath);
717
void cd_free_item (CDStatusNotifierItem *pItem)
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);
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);
748
static void _load_item_image (Icon *icon)
750
int iWidth = cairo_dock_icon_get_allocated_width (icon);
751
int iHeight = cairo_dock_icon_get_allocated_height (icon);
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')
757
cairo_surface_t *pSurface = cairo_dock_create_surface_from_image_simple (cIconPath,
760
cairo_dock_load_image_buffer_from_surface (&icon->image, pSurface, iWidth, iHeight);
764
Icon *cd_satus_notifier_create_icon_for_item (CDStatusNotifierItem *pItem)
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),
771
pItem->iPosition > -1 ? pItem->iPosition : (int)pItem->iCategory);
772
pIcon->iface.load_image = _load_item_image; /// a voir...
777
CDStatusNotifierItem *cd_satus_notifier_get_item_from_icon (Icon *pIcon)
779
CDStatusNotifierItem *pItem;
781
for (it = myData.pItems; it != NULL; it = it->next)
784
if (pIcon->cCommand && strcmp (pIcon->cCommand, pItem->cService) == 0)
790
Icon *cd_satus_notifier_get_icon_from_item (CDStatusNotifierItem *pItem)
792
//g_print ("=== %s (%s)\n", __func__, pItem->cService);
793
GList *ic, *pIcons = CD_APPLET_MY_ICONS_LIST;
795
for (ic = pIcons; ic != NULL; ic = ic->next)
798
//g_print ("=== %s \n", pIcon->cCommand);
799
if (pIcon->cCommand && strcmp (pIcon->cCommand, pItem->cService) == 0)
808
static gboolean _on_draw_menu_reposition (GtkWidget *pWidget, G_GNUC_UNUSED gpointer useless, CDStatusNotifierItem *pItem)
810
g_return_val_if_fail (pItem != NULL, FALSE);
812
int iMenuWidth = gtk_widget_get_allocated_width (pWidget);
814
if (pItem->iMenuWidth != iMenuWidth) // if the width has changed, reposition the menu to be sure it won't out of the screen.
816
pItem->iMenuWidth = iMenuWidth;
817
gtk_menu_reposition (GTK_MENU (pWidget));
820
return FALSE; // FALSE to propagate the event further.
822
void cd_satus_notifier_build_item_dbusmenu (CDStatusNotifierItem *pItem)
824
if (pItem->pMenu == NULL) // menu not yet built
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).
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
840
* This is why we need to watch the 'draw' event...
842
g_signal_connect (G_OBJECT (pItem->pMenu),
844
G_CALLBACK (_on_draw_menu_reposition),