~ubuntu-branches/ubuntu/utopic/cairo-dock/utopic

« back to all changes in this revision

Viewing changes to src/gldit/cairo-dock-menu.c

  • Committer: Matthieu Baerts
  • Date: 2014-02-19 22:56:20 UTC
  • mfrom: (1.1.38)
  • Revision ID: matttbe@gmail.com-20140219225620-5j86v0ccfmngbwdm
Tags: 3.3.99.beta1.1~20140219~bzr1717-0ubuntu1
* New upstream snapshot.
* Short Upstream ChangeLog:
  - Style: unified the style for all objects.
  - Menu: added the possibility to customise them
  - Config: updated some sections linked to the style
  - API:
    - Updated functions about the style
    - Added new features about PID monitoring
  - Some bugs have been fixed, other tweaks
* debian/control:
  - bumped plug-ins versions
  - bumped Standard Version (no change needed)

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
*/
19
19
 
20
20
#include <stdlib.h>
 
21
#include <math.h>  // fabs
21
22
 
22
23
#include <cairo.h>
23
24
#include <gtk/gtk.h>
31
32
#include "cairo-dock-desktop-manager.h"  // gldi_desktop_get_height
32
33
#include "cairo-dock-log.h"
33
34
#include "cairo-dock-draw.h"
 
35
#include "cairo-dock-backends-manager.h"  // cairo_dock_get_dialog_decorator
 
36
#include "cairo-dock-dialog-manager.h"  // myDialogsParam
 
37
#include "cairo-dock-style-manager.h"
34
38
#include "cairo-dock-menu.h"
35
39
 
 
40
#if GTK_MAJOR_VERSION > 2
 
41
static gboolean _draw_menu_item (GtkWidget *widget, cairo_t *cr, G_GNUC_UNUSED gpointer data);
 
42
static gboolean _on_select_menu_item (GtkWidget* pMenuItem, G_GNUC_UNUSED gpointer data);
 
43
static gboolean _on_deselect_menu_item (GtkWidget* pMenuItem, G_GNUC_UNUSED gpointer data);
 
44
static void _on_destroy_menu_item (GtkWidget* pMenuItem, G_GNUC_UNUSED gpointer data);
 
45
#endif
36
46
 
37
47
  ////////////
38
48
 /// MENU ///
39
49
/////////////
40
50
 
 
51
#if GTK_MAJOR_VERSION > 2
 
52
 
 
53
static void _init_menu_style (void)
 
54
{
 
55
        static GtkCssProvider *cssProvider = NULL;
 
56
        static int index = 0;
 
57
        
 
58
        if (index == gldi_style_colors_get_index())
 
59
                return;
 
60
        index = gldi_style_colors_get_index();
 
61
        g_print ("%s (%d)\n", __func__, index);
 
62
        
 
63
        if (myDialogsParam.bUseDefaultColors && myStyleParam.bUseSystemColors)
 
64
        {
 
65
                if (cssProvider != NULL)
 
66
                {
 
67
                        gldi_style_colors_freeze ();
 
68
                        gtk_style_context_remove_provider_for_screen (gdk_screen_get_default(), GTK_STYLE_PROVIDER(cssProvider));
 
69
                        gldi_style_colors_freeze ();
 
70
                        g_object_unref (cssProvider);
 
71
                        cssProvider = NULL;
 
72
                }
 
73
        }
 
74
        else
 
75
        {
 
76
                if (cssProvider == NULL)
 
77
                {
 
78
                        cssProvider = gtk_css_provider_new ();
 
79
                        gldi_style_colors_freeze ();
 
80
                        gtk_style_context_add_provider_for_screen (gdk_screen_get_default(), GTK_STYLE_PROVIDER(cssProvider), GTK_STYLE_PROVIDER_PRIORITY_USER);
 
81
                        gldi_style_colors_freeze ();
 
82
                }
 
83
                
 
84
                double *bg_color = (myDialogsParam.bUseDefaultColors ? myStyleParam.fBgColor : myDialogsParam.fBgColor);
 
85
                double *text_color = (myDialogsParam.bUseDefaultColors ? myStyleParam.textDescription.fColorStart : myDialogsParam.dialogTextDescription.fColorStart);
 
86
                
 
87
                double rgb[4];
 
88
                gldi_style_color_shade (bg_color, .2, rgb);
 
89
                double rgbs[4];
 
90
                gldi_style_color_shade (bg_color, .2, rgbs);
 
91
                double rgbb[4];
 
92
                gldi_style_color_shade (bg_color, .3, rgbb);
 
93
                
 
94
                gchar *css = g_strdup_printf ("@define-color menuitem_bg_color rgb (%d, %d, %d); \
 
95
                @define-color menuitem_text_color rgb (%d, %d, %d); \
 
96
                @define-color menuitem_insensitive_text_color rgba (%d, %d, %d, .5); \
 
97
                @define-color menuitem_separator_color rgb (%d, %d, %d); \
 
98
                @define-color menuitem_bg_color2 rgb (%d, %d, %d); \
 
99
                @define-color menu_bg_color rgba (%d, %d, %d, %d); \
 
100
                .menuitem2 { \
 
101
                        text-shadow: none; \
 
102
                        border-image: none; \
 
103
                        box-shadow: none; \
 
104
                        background: transparent; \
 
105
                        color: @menuitem_text_color; \
 
106
                } \
 
107
                .menuitem2 GtkImage { \
 
108
                        background: transparent; \
 
109
                } \
 
110
                .menuitem2.separator, \
 
111
                .menuitem2 .separator { \
 
112
                        color: @menuitem_separator_color; \
 
113
                        border-width: 1px; \
 
114
                        border-style: solid; \
 
115
                        border-image: none; \
 
116
                        border-color: @menuitem_separator_color; \
 
117
                        border-bottom-color: alpha (@menuitem_separator_color, 0.6); \
 
118
                        border-right-color: alpha (@menuitem_separator_color, 0.6); \
 
119
                } \
 
120
                .menuitem2:hover, \
 
121
                .menuitem2 *:hover { \
 
122
                        background: @menuitem_bg_color; \
 
123
                        background-image: none; \
 
124
                        text-shadow: none; \
 
125
                        border-image: none; \
 
126
                        box-shadow: none; \
 
127
                        color: @menuitem_text_color; \
 
128
                } \
 
129
                .menuitem2 *:insensitive { \
 
130
                        text-shadow: none; \
 
131
                        color: @menuitem_insensitive_text_color; \
 
132
                } \
 
133
                .menuitem2 .entry, \
 
134
                .menuitem2.entry { \
 
135
                        background: @menuitem_bg_color; \
 
136
                        border-image: none; \
 
137
                        border-color: transparent; \
 
138
                        color: @menuitem_text_color; \
 
139
                } \
 
140
                .menuitem2 .button, \
 
141
                .menuitem2.button { \
 
142
                        background: @menuitem_bg_color; \
 
143
                        background-image: none; \
 
144
                        box-shadow: none; \
 
145
                        border-color: transparent; \
 
146
                        padding: 2px; \
 
147
                } \
 
148
                .menuitem2 .scale, \
 
149
                .menuitem2.scale { \
 
150
                        background: @menuitem_bg_color2; \
 
151
                        background-image: none; \
 
152
                        color: @menuitem_text_color; \
 
153
                        border-image: none; \
 
154
                } \
 
155
                .menuitem2 .scale.left, \
 
156
                .menuitem2.scale.left { \
 
157
                        background: @menuitem_bg_color; \
 
158
                        background-image: none; \
 
159
                        border-image: none; \
 
160
                } \
 
161
                .menuitem2 .scale.slider, \
 
162
                .menuitem2.scale.slider { \
 
163
                        background: @menuitem_text_color; \
 
164
                        background-image: none; \
 
165
                        border-image: none; \
 
166
                } \
 
167
                .menuitem2 GtkCalendar, \
 
168
                .menuitem2 GtkCalendar.button, \
 
169
                .menuitem2 GtkCalendar.header, \
 
170
                .menuitem2 GtkCalendar.view { \
 
171
                        background-color: @menuitem_bg_color; \
 
172
                        background-image: none; \
 
173
                        color: @menuitem_text_color; \
 
174
                } \
 
175
                .menuitem2 GtkCalendar { \
 
176
                        background-color: @menuitem_bg_color2; \
 
177
                        background-image: none; \
 
178
                } \
 
179
                .menuitem2 GtkCalendar:inconsistent { \
 
180
                        color: shade (@menuitem_bg_color2, 0.6); \
 
181
                } \
 
182
                .menu2 { \
 
183
                        background: @menu_bg_color; \
 
184
                        background-image: none; \
 
185
                        color: @menuitem_text_color; \
 
186
                }",
 
187
                (int)(rgb[0]*255), (int)(rgb[1]*255), (int)(rgb[2]*255),
 
188
                (int)(text_color[0]*255), (int)(text_color[1]*255), (int)(text_color[2]*255),
 
189
                (int)(text_color[0]*255), (int)(text_color[1]*255), (int)(text_color[2]*255),
 
190
                (int)(rgbs[0]*255), (int)(rgbs[1]*255), (int)(rgbs[2]*255),
 
191
                (int)(rgbb[0]*255), (int)(rgbb[1]*255), (int)(rgbb[2]*255),
 
192
                (int)(bg_color[0]*255), (int)(bg_color[1]*255), (int)(bg_color[2]*255), (int)(bg_color[3]*255));  // we also define ".menu", so that custom widgets (like in the SoundMenu) can get our colors.
 
193
                
 
194
                gldi_style_colors_freeze ();
 
195
                gtk_css_provider_load_from_data (cssProvider,
 
196
                        css, -1, NULL);  // (should) clear any previously loaded information
 
197
                gldi_style_colors_freeze ();
 
198
                g_free (css);
 
199
        }
 
200
}
 
201
 
 
202
static gboolean _draw_menu (GtkWidget *pWidget,
 
203
        cairo_t *pCairoContext,
 
204
        G_GNUC_UNUSED GtkWidget *menu)
 
205
{
 
206
        // erase the default background
 
207
        cairo_dock_erase_cairo_context (pCairoContext);
 
208
        
 
209
        // draw the background/outline and set the clip
 
210
        CairoDialogDecorator *pDecorator = cairo_dock_get_dialog_decorator (myDialogsParam.cDecoratorName);
 
211
        if (pDecorator)
 
212
                pDecorator->render_menu (pWidget, pCairoContext);
 
213
        
 
214
        // draw the items
 
215
        cairo_set_source_rgba (pCairoContext, 0.0, 0.0, 0.0, 1.0);
 
216
        
 
217
        GtkWidgetClass *parent_class = g_type_class_peek (g_type_parent (G_TYPE_FROM_INSTANCE (pWidget)));
 
218
        parent_class = g_type_class_peek_parent (parent_class);  // skip the direct parent (GtkBin, which does anyway nothing usually), because dbusmenu-gtk draws it
 
219
        parent_class->draw (pWidget, pCairoContext);
 
220
        
 
221
        return TRUE;
 
222
}
 
223
 
 
224
static void _set_margin_position (GtkWidget *pMenu, GldiMenuParams *pParams)
 
225
{
 
226
        if (pParams == NULL)
 
227
                pParams = g_object_get_data (G_OBJECT (pMenu), "gldi-params");
 
228
        g_return_if_fail (pParams);
 
229
        Icon *pIcon = pParams->pIcon;
 
230
        g_return_if_fail (pIcon);
 
231
        GldiContainer *pContainer = cairo_dock_get_icon_container (pIcon);
 
232
        g_return_if_fail (pContainer);
 
233
        
 
234
        // define where the menu will point
 
235
        int iMarginPosition;  // b, t, r, l
 
236
        int y0 = pContainer->iWindowPositionY + pIcon->fDrawY;
 
237
        if (pContainer->bDirectionUp)
 
238
                y0 += pIcon->fHeight * pIcon->fScale - pIcon->image.iHeight;  // the icon might not be maximised yet
 
239
        int Hs = (pContainer->bIsHorizontal ? gldi_desktop_get_height() : gldi_desktop_get_width());
 
240
        if (pContainer->bIsHorizontal)
 
241
        {
 
242
                iMarginPosition = (y0 > Hs/2 ? 0 : 1);
 
243
        }
 
244
        else
 
245
        {
 
246
                iMarginPosition = (y0 > Hs/2 ? 2 : 3);
 
247
        }
 
248
        
 
249
        // store the result, and allocate some space to draw the arrow
 
250
        if (iMarginPosition != pParams->iMarginPosition)  // margin position is now defined or has changed -> update it on the menu
 
251
        {
 
252
                g_print ("margin position: %d -> %d\n", pParams->iMarginPosition, iMarginPosition);
 
253
                // store the value
 
254
                pParams->iMarginPosition = iMarginPosition;
 
255
                
 
256
                // get/add a css (gtk_widget_set_margin_xxx doesn't work as expected :-/)
 
257
                GtkCssProvider *cssProvider = pParams->cssProvider;
 
258
                if (cssProvider)  // unfortunately, GTK doesn't update correctly a css provider if we load some new data inside (the previous padding values are kept, along with the new ones, although 'gtk_css_provider_load_from_data' is supposed to "clear any previously loaded information"), so we have to remove/add it :-/
 
259
                {
 
260
                        gtk_style_context_remove_provider (gtk_widget_get_style_context(pMenu), GTK_STYLE_PROVIDER(cssProvider));
 
261
                        g_object_unref (cssProvider);
 
262
                        cssProvider = NULL;
 
263
                        pParams->cssProvider = NULL;
 
264
                }
 
265
                if (cssProvider == NULL)
 
266
                {
 
267
                        cssProvider = gtk_css_provider_new ();
 
268
                        gtk_style_context_add_provider (gtk_widget_get_style_context(pMenu), GTK_STYLE_PROVIDER(cssProvider), GTK_STYLE_PROVIDER_PRIORITY_USER);  // this adds a reference on the provider, plus the one we own
 
269
                        pParams->cssProvider = cssProvider;
 
270
                        g_print (" + css\n");
 
271
                }
 
272
                
 
273
                // load the new padding rule into the css
 
274
                gchar *css = NULL;
 
275
                int ah = pParams->iArrowHeight;
 
276
                int b=0, t=0, r=0, l=0;
 
277
                switch (iMarginPosition)
 
278
                {
 
279
                        case 0: b = ah; break;
 
280
                        case 1: t = ah; break;
 
281
                        case 2: r = ah; break;
 
282
                        case 3: l = ah; break;
 
283
                        default: break;
 
284
                }
 
285
                css = g_strdup_printf ("GtkMenu { \
 
286
                        padding-bottom: %dpx; \
 
287
                        padding-top: %dpx; \
 
288
                        padding-right: %dpx; \
 
289
                        padding-left: %dpx; \
 
290
                }", b, t, r, l);  // we must define all the paddings, else if the margin position changes, clearing the css won't make the padding disappear :-/
 
291
                gtk_css_provider_load_from_data (cssProvider,
 
292
                        css, -1, NULL);
 
293
                g_free (css);
 
294
        }
 
295
}
 
296
#endif
 
297
 
41
298
GtkWidget *gldi_menu_new (G_GNUC_UNUSED Icon *pIcon)
42
299
{
43
300
        GtkWidget *pMenu = gtk_menu_new ();
49
306
 
50
307
static gboolean _on_icon_destroyed (GtkWidget *pMenu, G_GNUC_UNUSED Icon *pIcon)
51
308
{
52
 
        g_object_set_data (G_OBJECT (pMenu), "gldi-icon", NULL);
 
309
        GldiMenuParams *pParams = g_object_get_data (G_OBJECT (pMenu), "gldi-params");
 
310
        if (pParams)
 
311
                pParams->pIcon = NULL;
53
312
        return GLDI_NOTIFICATION_LET_PASS;
54
313
}
55
314
 
56
315
static void _on_menu_destroyed (GtkWidget *pMenu, G_GNUC_UNUSED gpointer data)
57
316
{
58
 
        Icon *pIcon = g_object_get_data (G_OBJECT(pMenu), "gldi-icon");
 
317
        GldiMenuParams *pParams = g_object_get_data (G_OBJECT (pMenu), "gldi-params");
 
318
        if (!pParams)
 
319
                return;
 
320
        Icon *pIcon = pParams->pIcon;
59
321
        if (pIcon)
60
322
                gldi_object_remove_notification (pIcon,
61
323
                        NOTIFICATION_DESTROY,
62
324
                        (GldiNotificationFunc) _on_icon_destroyed,
63
325
                        NULL);
64
 
}
65
 
 
66
 
void gldi_menu_init (G_GNUC_UNUSED GtkWidget *pMenu, G_GNUC_UNUSED Icon *pIcon)
 
326
        #if GTK_MAJOR_VERSION > 2
 
327
        if (pParams->cssProvider)
 
328
        {
 
329
                g_object_unref (pParams->cssProvider);  /// need to remove the provider from the style context ?... probably not since the style context will be destroyed and will release its reference on the provider
 
330
        }
 
331
        g_free (pParams);
 
332
        #endif
 
333
}
 
334
 
 
335
#if GTK_MAJOR_VERSION > 2
 
336
static void _on_menu_deactivated (GtkMenuShell *pMenu, G_GNUC_UNUSED gpointer data)
 
337
{
 
338
        GldiMenuParams *pParams = g_object_get_data (G_OBJECT (pMenu), "gldi-params");
 
339
        if (!pParams)
 
340
                return;
 
341
        Icon *pIcon = pParams->pIcon;
 
342
        if (pIcon->iHideLabel > 0)
 
343
        {
 
344
                pIcon->iHideLabel --;
 
345
                GldiContainer *pContainer = cairo_dock_get_icon_container (pIcon);
 
346
                if (pIcon->iHideLabel == 0 && pContainer)
 
347
                        gtk_widget_queue_draw (pContainer->pWidget);
 
348
        }
 
349
}
 
350
#endif
 
351
 
 
352
void gldi_menu_init (G_GNUC_UNUSED GtkWidget *pMenu, Icon *pIcon)
67
353
{
68
354
        #if (CAIRO_DOCK_FORCE_ICON_IN_MENUS == 1)
69
355
        gtk_menu_set_reserve_toggle_size (GTK_MENU(pMenu), TRUE);
70
356
        #endif
71
357
        
 
358
        #if GTK_MAJOR_VERSION > 2
 
359
        // connect to 'draw' event to draw the menu (background and items)
 
360
        GtkWidget *pWindow = gtk_widget_get_toplevel (pMenu);
 
361
        cairo_dock_set_default_rgba_visual (pWindow);
 
362
        
 
363
        g_signal_connect (G_OBJECT (pMenu),
 
364
                "draw",
 
365
                G_CALLBACK (_draw_menu),
 
366
                pMenu);
 
367
        
 
368
        _init_menu_style ();
 
369
        
 
370
        gtk_style_context_add_class (gtk_widget_get_style_context (pMenu), "menu2");
 
371
        #endif
 
372
        
 
373
        // set params
 
374
        GldiMenuParams *pParams = g_new0 (GldiMenuParams, 1);
 
375
        g_object_set_data (G_OBJECT (pMenu), "gldi-params", pParams);
 
376
        g_signal_connect (G_OBJECT (pMenu),
 
377
                "destroy",
 
378
                G_CALLBACK (_on_menu_destroyed),
 
379
                NULL);
 
380
                
 
381
        // init a main menu
72
382
        if (pIcon != NULL)  // the menu points on an icon
73
383
        {
74
384
                // link it to the icon
75
385
                g_object_set_data (G_OBJECT (pMenu), "gldi-icon", pIcon);
 
386
                pParams->pIcon = pIcon;
76
387
                gldi_object_register_notification (pIcon,
77
388
                        NOTIFICATION_DESTROY,
78
389
                        (GldiNotificationFunc) _on_icon_destroyed,
79
 
                        GLDI_RUN_AFTER, pMenu);  // when the icon is destroyed, unlink the menu from it
80
 
                g_signal_connect (G_OBJECT (pMenu),
81
 
                        "destroy",
82
 
                        G_CALLBACK (_on_menu_destroyed),
83
 
                        NULL);  // when the menu is destroyed, unregister the above notification on the icon
 
390
                        GLDI_RUN_AFTER, pMenu);  // when the icon is destroyed, unlink the menu from it; when the menu is destroyed, the above notification will be unregistered on the icon in the "destroy" callback
 
391
                
 
392
                GldiContainer *pContainer = cairo_dock_get_icon_container (pIcon);
 
393
                if (pContainer != NULL)
 
394
                {
 
395
                        #if GTK_MAJOR_VERSION > 2
 
396
                        // init the rendering --> align, margin-height
 
397
                        CairoDialogDecorator *pDecorator = cairo_dock_get_dialog_decorator (myDialogsParam.cDecoratorName);
 
398
                        if (pDecorator)
 
399
                                pDecorator->setup_menu (pMenu);
 
400
                        
 
401
                        // define where the menu will point, and allocate some space to draw the arrow
 
402
                        pParams->iMarginPosition = -1;
 
403
                        _set_margin_position (pMenu, pParams);
 
404
                        
 
405
                        // show the icon's label back when the menu is hidden
 
406
                        g_signal_connect (G_OBJECT (pMenu),
 
407
                                "deactivate",
 
408
                                G_CALLBACK (_on_menu_deactivated),
 
409
                                NULL);
 
410
                        
 
411
                        gchar *css = NULL;
 
412
                        const gchar *orientation = "";
 
413
                        int ah = pParams->iArrowHeight;
 
414
                        switch (pParams->iMarginPosition)
 
415
                        {
 
416
                                case 0: orientation = "bottom";  break;
 
417
                                case 1: orientation = "top";  break;
 
418
                                case 2: orientation = "right";  break;
 
419
                                case 3: orientation = "left";  break;
 
420
                                default: break;
 
421
                        }
 
422
                        
 
423
                        GtkCssProvider *cssProvider = gtk_css_provider_new ();
 
424
                        css = g_strdup_printf ("GtkMenu { \
 
425
                                padding-%s: %dpx; \
 
426
                        }", orientation, ah);
 
427
                        gtk_css_provider_load_from_data (cssProvider,
 
428
                                css, -1, NULL);
 
429
                        gtk_style_context_add_provider (gtk_widget_get_style_context(pMenu), GTK_STYLE_PROVIDER(cssProvider), GTK_STYLE_PROVIDER_PRIORITY_USER);
 
430
                        g_free (css);
 
431
                        #endif
 
432
                }
84
433
        }
85
434
}
86
435
 
87
436
static void _place_menu_on_icon (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, G_GNUC_UNUSED gpointer data)
88
437
{
89
438
        *push_in = FALSE;
90
 
        Icon *pIcon = g_object_get_data (G_OBJECT(menu), "gldi-icon");
 
439
        GldiMenuParams *pParams = g_object_get_data (G_OBJECT(menu), "gldi-params");
 
440
        g_return_if_fail (pParams != NULL);
 
441
        
 
442
        Icon *pIcon = pParams->pIcon;
91
443
        GldiContainer *pContainer = (pIcon ? cairo_dock_get_icon_container (pIcon) : NULL);
92
444
        int x0 = pContainer->iWindowPositionX + pIcon->fDrawX;
93
445
        int y0 = pContainer->iWindowPositionY + pIcon->fDrawY;
104
456
        w = requisition.width;
105
457
        h = requisition.height;
106
458
        
 
459
        /// TODO: use iMarginPosition...
 
460
        #if GTK_MAJOR_VERSION > 2
 
461
        double fAlign = pParams->fAlign;
 
462
        int r = pParams->iRadius;
 
463
        int ah = pParams->iArrowHeight;
 
464
        int w_, h_;
 
465
        #endif
 
466
        int iAimedX, iAimedY;
107
467
        int Hs = (pContainer->bIsHorizontal ? gldi_desktop_get_height() : gldi_desktop_get_width());
108
 
        //g_print ("%d;%d %dx%d\n", x0, y0, w, h);
109
468
        if (pContainer->bIsHorizontal)
110
469
        {
111
 
                *x = x0;
 
470
                iAimedX = x0 + pIcon->image.iWidth/2;
 
471
                #if GTK_MAJOR_VERSION > 2
 
472
                w_ = w - 2 * r;
 
473
                h_ = h - 2 * r - ah;
 
474
                *x = MAX (0, iAimedX - fAlign * w_ - r);
 
475
                #else
 
476
                *x = iAimedX;
 
477
                #endif
112
478
                if (y0 > Hs/2)  // pContainer->bDirectionUp
 
479
                {
113
480
                        *y = y0 - h;
 
481
                        iAimedY = y0;
 
482
                }
114
483
                else
 
484
                {
115
485
                        *y = y0 + pIcon->fHeight * pIcon->fScale;
 
486
                        iAimedY = y0 + pIcon->image.iHeight;
 
487
                }
116
488
        }
117
489
        else
118
490
        {
119
 
                *y = MIN (x0, gldi_desktop_get_height() - h);
 
491
                iAimedY = x0 + pIcon->image.iWidth/2;
 
492
                #if GTK_MAJOR_VERSION > 2
 
493
                w_ = w - 2 * r - ah;
 
494
                h_ = h - 2 * r;
 
495
                *y = MIN (iAimedY - fAlign * h_ - r, gldi_desktop_get_height() - h);
 
496
                #else
 
497
                *y = MIN (iAimedY, gldi_desktop_get_height() - h);
 
498
                #endif
120
499
                if (y0 > Hs/2)  // pContainer->bDirectionUp
 
500
                {
121
501
                        *x = y0 - w;
 
502
                        iAimedX = y0;
 
503
                }
122
504
                else
123
 
                        *x = y0 + pIcon->fHeight * pIcon->fScale;
124
 
        }
125
 
}
 
505
                {
 
506
                        *x = y0 + pIcon->image.iHeight;
 
507
                        iAimedX = y0 + pIcon->image.iHeight;
 
508
                }
 
509
        }
 
510
        pParams->iAimedX = iAimedX;
 
511
        pParams->iAimedY = iAimedY;
 
512
}
 
513
 
 
514
#if GTK_MAJOR_VERSION > 2
 
515
static void _init_menu_item (GtkWidget *pMenuItem)
 
516
{
 
517
        int index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pMenuItem), "gldi-text-color"));
 
518
        if (index == 0)
 
519
        {
 
520
                // the following code is to draw the menu item; this is more like a proof of concept
 
521
                /**g_signal_connect (G_OBJECT (pMenuItem),
 
522
                        "draw",
 
523
                        G_CALLBACK (_draw_menu_item),
 
524
                        NULL);
 
525
                g_signal_connect (G_OBJECT (pMenuItem),
 
526
                        "select",
 
527
                        G_CALLBACK (_on_select_menu_item),
 
528
                        NULL);
 
529
                g_signal_connect (G_OBJECT (pMenuItem),
 
530
                        "deselect",
 
531
                        G_CALLBACK (_on_deselect_menu_item),
 
532
                        NULL);
 
533
                g_signal_connect (G_OBJECT (pMenuItem),
 
534
                        "destroy",
 
535
                        G_CALLBACK (_on_destroy_menu_item),
 
536
                        NULL);*/
 
537
                
 
538
                gtk_style_context_add_class (gtk_widget_get_style_context (pMenuItem), "menuitem2");
 
539
                
 
540
                g_object_set_data (G_OBJECT (pMenuItem), "gldi-text-color", GINT_TO_POINTER(1));
 
541
        }
 
542
        
 
543
        GtkWidget *pSubMenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (pMenuItem));
 
544
        if (pSubMenu != NULL)  /// TODO: if it's a sub-menu not made by us (for instance, the NetworkManager indicator), set the drawing callback...
 
545
                gtk_container_forall (GTK_CONTAINER (pSubMenu), (GtkCallback) _init_menu_item, NULL);
 
546
}
 
547
#endif
 
548
 
126
549
static void _popup_menu (GtkWidget *menu, guint32 time)
127
550
{
128
 
        Icon *pIcon = g_object_get_data (G_OBJECT(menu), "gldi-icon");
 
551
        GldiMenuParams *pParams = g_object_get_data (G_OBJECT(menu), "gldi-params");
 
552
        g_return_if_fail (pParams != NULL);
 
553
        
 
554
        Icon *pIcon = pParams->pIcon;
129
555
        GldiContainer *pContainer = (pIcon ? cairo_dock_get_icon_container (pIcon) : NULL);
130
556
        
131
557
        // setup the menu for the container
132
 
        if (pContainer != NULL)
133
 
        {
134
 
                if (pContainer->iface.setup_menu)
135
 
                        pContainer->iface.setup_menu (pContainer, pIcon, menu);
136
 
        }
137
 
        
138
 
        // show the menu
 
558
        if (pContainer && pContainer->iface.setup_menu)
 
559
                pContainer->iface.setup_menu (pContainer, pIcon, menu);
 
560
        
 
561
        #if GTK_MAJOR_VERSION > 2
 
562
        {
 
563
                _init_menu_style ();
 
564
        }
 
565
        
 
566
        if (pIcon && pContainer)
 
567
        {
 
568
                // hide the icon's label, since menus are placed right above the icon (and therefore, the arrow overlaps the label, which makes it hard to see if both colors are similar).
 
569
                if (pIcon->iHideLabel == 0 && pContainer)
 
570
                        gtk_widget_queue_draw (pContainer->pWidget);
 
571
                pIcon->iHideLabel ++;
 
572
                
 
573
                // ensure margin position is still correct
 
574
                _set_margin_position (menu, pParams);
 
575
        }
 
576
        #endif
 
577
        
139
578
        gtk_widget_show_all (GTK_WIDGET (menu));
140
579
        
 
580
        #if GTK_MAJOR_VERSION > 2
 
581
        // to draw the items ourselves
 
582
        gtk_container_forall (GTK_CONTAINER (menu), (GtkCallback) _init_menu_item, NULL);
 
583
        #endif
 
584
        
141
585
        gtk_menu_popup (GTK_MENU (menu),
142
586
                NULL,
143
587
                NULL,
173
617
 /// MENU ITEM ///
174
618
/////////////////
175
619
 
 
620
#if GTK_MAJOR_VERSION > 2
 
621
const int N = 10;
 
622
const int dt1 = 20;
 
623
const int dt2 = 30;
 
624
 
 
625
static void
 
626
get_arrow_size (GtkWidget *widget,
 
627
                GtkWidget *child,
 
628
                gint      *size,
 
629
                gint      *spacing)
 
630
{
 
631
        PangoContext     *context;
 
632
        PangoFontMetrics *metrics;
 
633
        gfloat            arrow_scaling;
 
634
        gint              arrow_spacing;
 
635
 
 
636
        g_assert (size);
 
637
 
 
638
        gtk_widget_style_get (widget,
 
639
                "arrow-scaling", &arrow_scaling,
 
640
                "arrow-spacing", &arrow_spacing,
 
641
                NULL);
 
642
 
 
643
        if (spacing != NULL)
 
644
                *spacing = arrow_spacing;
 
645
 
 
646
        context = gtk_widget_get_pango_context (child);
 
647
 
 
648
        metrics = pango_context_get_metrics (context,
 
649
                                                                           pango_context_get_font_description (context),
 
650
                                                                           pango_context_get_language (context));
 
651
 
 
652
        *size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
 
653
                                                 pango_font_metrics_get_descent (metrics)));
 
654
 
 
655
        pango_font_metrics_unref (metrics);
 
656
 
 
657
        *size = *size * arrow_scaling;
 
658
}
 
659
static gboolean _draw_menu_item (GtkWidget *widget,
 
660
        cairo_t *cr,
 
661
        G_GNUC_UNUSED gpointer data)
 
662
{
 
663
        //GtkStateFlags state;
 
664
        GtkStyleContext *context;
 
665
        GtkWidget *child;
 
666
        gint x, y, w, h, width, height;
 
667
        guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
 
668
        
 
669
        child = gtk_bin_get_child (GTK_BIN (widget));
 
670
        
 
671
        //state = gtk_widget_get_state_flags (widget);
 
672
        context = gtk_widget_get_style_context (widget);
 
673
        width = gtk_widget_get_allocated_width (widget);
 
674
        height = gtk_widget_get_allocated_height (widget);
 
675
        
 
676
        x = border_width;
 
677
        y = border_width;
 
678
        w = width - border_width * 2;
 
679
        h = height - border_width * 2;
 
680
        
 
681
        // draw the background
 
682
        int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "gldi-step"));
 
683
        if (n != 0)  // so we only draw the barkground if the item is or was selected
 
684
        {
 
685
                double a = (double)n/N;
 
686
                
 
687
                cairo_save (cr);
 
688
                
 
689
                int r=6, l=0;
 
690
                cairo_dock_draw_rounded_rectangle (cr, r, l, w - 2*r, h);
 
691
                cairo_clip (cr);
 
692
                
 
693
                gldi_style_colors_set_selected_bg_color (cr);
 
694
                cairo_paint_with_alpha (cr, a);
 
695
                
 
696
                cairo_restore (cr);
 
697
        }
 
698
        
 
699
        // draw the arrow in case of a sub-menu
 
700
        if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)))
 
701
        {
 
702
                gint arrow_x, arrow_y;
 
703
                gint arrow_size, spacing;
 
704
                GtkTextDirection direction;
 
705
                gdouble angle;
 
706
 
 
707
                direction = gtk_widget_get_direction (widget);
 
708
                get_arrow_size (widget, child, &arrow_size, &spacing);
 
709
 
 
710
                if (direction == GTK_TEXT_DIR_LTR)
 
711
                {
 
712
                  arrow_x = x + w - arrow_size - spacing;
 
713
                  angle = G_PI / 2;
 
714
                }
 
715
                else
 
716
                {
 
717
                  arrow_x = x + spacing;
 
718
                  angle = (3 * G_PI) / 2;
 
719
                }
 
720
 
 
721
                arrow_y = y + (h - arrow_size) / 2;
 
722
 
 
723
                gtk_render_arrow (context, cr, angle, arrow_x, arrow_y, arrow_size);
 
724
        }
 
725
        else if (!child)  // separator
 
726
        {
 
727
                gboolean wide_separators;
 
728
                gint     separator_height;
 
729
                GtkBorder padding;
 
730
                
 
731
                g_print ("SEPARATOR\n");
 
732
                gtk_style_context_get_padding (context, 0, &padding);
 
733
                gtk_widget_style_get (widget,
 
734
                        "wide-separators",    &wide_separators,
 
735
                        "separator-height",   &separator_height,
 
736
                        NULL);
 
737
                if (wide_separators)
 
738
                        gtk_render_frame (context, cr,
 
739
                                x + padding.left,
 
740
                                y + padding.top,
 
741
                                w - padding.left - padding.right,
 
742
                                separator_height);
 
743
                else
 
744
                        gtk_render_line (context, cr,
 
745
                                x + padding.left,
 
746
                                y + padding.top,
 
747
                                x + w - padding.right - 1,
 
748
                                y + padding.top);
 
749
        }
 
750
        
 
751
        // draw the item's content
 
752
        GtkWidgetClass *parent_class = g_type_class_peek (g_type_parent (G_TYPE_FROM_INSTANCE (widget)));
 
753
        parent_class = g_type_class_peek_parent (parent_class);
 
754
        
 
755
        cairo_set_source_rgba (cr, 1, 1, 1, 1);
 
756
        
 
757
        parent_class->draw (widget, cr);
 
758
        return TRUE;  // intercept
 
759
}
 
760
 
 
761
static gboolean _update_menu_item (GtkWidget* pMenuItem, G_GNUC_UNUSED gpointer data)
 
762
{
 
763
        int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(pMenuItem), "gldi-step"));
 
764
        gboolean bInside = GPOINTER_TO_INT (g_object_get_data (G_OBJECT(pMenuItem), "gldi-inside"));
 
765
        
 
766
        if (bInside)
 
767
        {
 
768
                if (n < N)
 
769
                        n ++;
 
770
        }
 
771
        else
 
772
        {
 
773
                if (n > 0)
 
774
                        n --;
 
775
        }
 
776
        g_object_set_data (G_OBJECT(pMenuItem), "gldi-step", GINT_TO_POINTER (n));
 
777
        
 
778
        gtk_widget_queue_draw (pMenuItem);
 
779
        
 
780
        if (n == 0 || n == N)
 
781
        {
 
782
                guint iSidAnimation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(pMenuItem), "gldi-animation"));
 
783
                if (iSidAnimation != 0)
 
784
                {
 
785
                        g_source_remove (iSidAnimation);
 
786
                        g_object_set_data (G_OBJECT(pMenuItem), "gldi-animation", NULL);
 
787
                }
 
788
                return FALSE;
 
789
        }
 
790
        return TRUE;
 
791
}
 
792
static gboolean _on_select_menu_item (GtkWidget* pMenuItem, G_GNUC_UNUSED gpointer data)
 
793
{
 
794
        guint iSidAnimation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(pMenuItem), "gldi-animation"));
 
795
        if (iSidAnimation != 0)
 
796
                g_source_remove (iSidAnimation);
 
797
        iSidAnimation = g_timeout_add (dt1, (GSourceFunc)_update_menu_item, pMenuItem);
 
798
        g_object_set_data (G_OBJECT(pMenuItem), "gldi-animation", GUINT_TO_POINTER (iSidAnimation));
 
799
        
 
800
        g_object_set_data (G_OBJECT(pMenuItem), "gldi-inside", GINT_TO_POINTER (TRUE));
 
801
        
 
802
        return FALSE;
 
803
}
 
804
static gboolean _on_deselect_menu_item (GtkWidget* pMenuItem, G_GNUC_UNUSED gpointer data)
 
805
{
 
806
        guint iSidAnimation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(pMenuItem), "gldi-animation"));
 
807
        if (iSidAnimation != 0)
 
808
                g_source_remove (iSidAnimation);
 
809
        iSidAnimation = g_timeout_add (dt2, (GSourceFunc)_update_menu_item, pMenuItem);
 
810
        g_object_set_data (G_OBJECT(pMenuItem), "gldi-animation", GUINT_TO_POINTER (iSidAnimation));
 
811
        
 
812
        g_object_set_data (G_OBJECT(pMenuItem), "gldi-inside", GINT_TO_POINTER (FALSE));
 
813
        
 
814
        return FALSE;
 
815
}
 
816
static void _on_destroy_menu_item (GtkWidget* pMenuItem, G_GNUC_UNUSED gpointer data)
 
817
{
 
818
        guint iSidAnimation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(pMenuItem), "gldi-animation"));
 
819
        if (iSidAnimation != 0)
 
820
        {
 
821
                g_source_remove (iSidAnimation);
 
822
                g_object_set_data (G_OBJECT(pMenuItem), "gldi-animation", NULL);
 
823
        }
 
824
}
 
825
#endif
 
826
 
176
827
GtkWidget *gldi_menu_item_new_full (const gchar *cLabel, const gchar *cImage, gboolean bUseMnemonic, GtkIconSize iSize)
177
828
{
178
829
        if (iSize == 0)
182
833
        if (! cImage)
183
834
        {
184
835
                if (! cLabel)
185
 
                        return gtk_menu_item_new ();
 
836
                        pMenuItem = gtk_menu_item_new ();
186
837
                else
187
 
                        return (bUseMnemonic ? gtk_menu_item_new_with_mnemonic (cLabel) : gtk_menu_item_new_with_label (cLabel));
 
838
                        pMenuItem =  (bUseMnemonic ? gtk_menu_item_new_with_mnemonic (cLabel) : gtk_menu_item_new_with_label (cLabel));
188
839
        }
189
 
        
190
 
        GtkWidget *image = NULL;
 
840
        else
 
841
        {
 
842
                GtkWidget *image = NULL;
191
843
#if (! GTK_CHECK_VERSION (3, 10, 0)) || (CAIRO_DOCK_FORCE_ICON_IN_MENUS == 1)
192
 
        if (*cImage == '/')
193
 
        {
194
 
                int size;
195
 
                gtk_icon_size_lookup (iSize, &size, NULL);
196
 
                GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size (cImage, size, size, NULL);
197
 
                image = gtk_image_new_from_pixbuf (pixbuf);
198
 
                g_object_unref (pixbuf);
199
 
        }
200
 
        else if (*cImage != '\0')
201
 
        {
202
 
                #if GTK_CHECK_VERSION (3, 10, 0)
203
 
                image = gtk_image_new_from_icon_name (cImage, iSize);  /// actually, this will not work until we replace all the gtk-stock names by standard icon names... which is a PITA, and will be done do later
 
844
                if (*cImage == '/')
 
845
                {
 
846
                        int size;
 
847
                        gtk_icon_size_lookup (iSize, &size, NULL);
 
848
                        GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size (cImage, size, size, NULL);
 
849
                        image = gtk_image_new_from_pixbuf (pixbuf);
 
850
                        g_object_unref (pixbuf);
 
851
                }
 
852
                else if (*cImage != '\0')
 
853
                {
 
854
                        #if GTK_CHECK_VERSION (3, 10, 0)
 
855
                        image = gtk_image_new_from_icon_name (cImage, iSize);  /// actually, this will not work until we replace all the gtk-stock names by standard icon names... which is a PITA, and will be done do later
 
856
                        #else
 
857
                        image = gtk_image_new_from_stock (cImage, iSize);
 
858
                        #endif
 
859
                }
 
860
#endif
 
861
                
 
862
#if GTK_CHECK_VERSION (3, 10, 0)
 
863
                #if (CAIRO_DOCK_FORCE_ICON_IN_MENUS == 1)
 
864
                if (! cLabel)
 
865
                        pMenuItem = gtk3_image_menu_item_new ();
 
866
                else
 
867
                        pMenuItem = (bUseMnemonic ? gtk3_image_menu_item_new_with_mnemonic (cLabel) : gtk3_image_menu_item_new_with_label (cLabel));
 
868
                gtk3_image_menu_item_set_image (GTK3_IMAGE_MENU_ITEM (pMenuItem), image);
204
869
                #else
205
 
                image = gtk_image_new_from_stock (cImage, iSize);
 
870
                if (! cLabel)
 
871
                        pMenuItem = gtk_menu_item_new ();
 
872
                else
 
873
                        pMenuItem = (bUseMnemonic ? gtk_menu_item_new_with_mnemonic (cLabel) : gtk_menu_item_new_with_label (cLabel));
206
874
                #endif
207
 
        }
208
 
#endif
209
 
        
210
 
#if GTK_CHECK_VERSION (3, 10, 0)
211
 
        #if (CAIRO_DOCK_FORCE_ICON_IN_MENUS == 1)
212
 
        if (! cLabel)
213
 
                pMenuItem = gtk3_image_menu_item_new ();
214
 
        else
215
 
                pMenuItem = (bUseMnemonic ? gtk3_image_menu_item_new_with_mnemonic (cLabel) : gtk3_image_menu_item_new_with_label (cLabel));
216
 
        gtk3_image_menu_item_set_image (GTK3_IMAGE_MENU_ITEM (pMenuItem), image);
217
 
        #else
218
 
        if (! cLabel)
219
 
                pMenuItem = gtk_menu_item_new ();
220
 
        else
221
 
                pMenuItem = (bUseMnemonic ? gtk_menu_item_new_with_mnemonic (cLabel) : gtk_menu_item_new_with_label (cLabel));
222
 
        #endif
223
875
#else
224
 
        if (! cLabel)
225
 
                pMenuItem = gtk_image_menu_item_new ();
226
 
        else
227
 
                pMenuItem = (bUseMnemonic ? gtk_image_menu_item_new_with_mnemonic (cLabel) : gtk_image_menu_item_new_with_label (cLabel));
228
 
        gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (pMenuItem), image);
229
 
        #if ((CAIRO_DOCK_FORCE_ICON_IN_MENUS == 1) && (GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION >= 16))
230
 
        gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (pMenuItem), TRUE);
231
 
        #endif
 
876
                if (! cLabel)
 
877
                        pMenuItem = gtk_image_menu_item_new ();
 
878
                else
 
879
                        pMenuItem = (bUseMnemonic ? gtk_image_menu_item_new_with_mnemonic (cLabel) : gtk_image_menu_item_new_with_label (cLabel));
 
880
                gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (pMenuItem), image);
 
881
                #if ((CAIRO_DOCK_FORCE_ICON_IN_MENUS == 1) && (GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION >= 16))
 
882
                gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (pMenuItem), TRUE);
 
883
                #endif
232
884
#endif
233
 
        
 
885
        }
234
886
        gtk_widget_show_all (pMenuItem);  // show immediately, so that the menu-item is realized when the menu is popped up
235
887
        
236
888
        return pMenuItem;