Merge lp:~indicator-applet-developers/ido/add-messaging-menu into lp:ido/13.10

Proposed by Ted Gould
Status: Merged
Approved by: Ted Gould
Approved revision: 148
Merged at revision: 145
Proposed branch: lp:~indicator-applet-developers/ido/add-messaging-menu
Merge into: lp:ido/13.10
Diff against target: 1163 lines (+1044/-2)
11 files modified
debian/libido3-0.1-0.symbols (+10/-0)
src/Makefile.am (+8/-2)
src/idoactionhelper.c (+24/-0)
src/idoactionhelper.h (+3/-0)
src/idoapplicationmenuitem.c (+195/-0)
src/idoapplicationmenuitem.h (+36/-0)
src/idodetaillabel.c (+401/-0)
src/idodetaillabel.h (+59/-0)
src/idomenuitemfactory.c (+8/-0)
src/idosourcemenuitem.c (+264/-0)
src/idosourcemenuitem.h (+36/-0)
To merge this branch: bzr merge lp:~indicator-applet-developers/ido/add-messaging-menu
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Ted Gould (community) Approve
Review via email: mp+179962@code.launchpad.net

Commit message

Add widgets for messaging menu

Description of the change

Adds the widgets needed for messaging menu integration. Might be broken, getting a diff and Jenkins comment right now while doing further testing.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
147. By Ted Gould

Adding new symbols

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:147
http://jenkins.qa.ubuntu.com/job/ido-ci/33/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ido-saucy-amd64-ci/32
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ido-saucy-armhf-ci/15

Click here to trigger a rebuild:
http://s-jenkins:8080/job/ido-ci/33/rebuild

review: Approve (continuous-integration)
148. By Ted Gould

Library functions taking GVariant params need to ref_sink() and unref() always. (reason #24 to not like GVariant)

Revision history for this message
Ted Gould (ted) wrote :

Fixed a couple of little things. But this seems sane.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:148
http://jenkins.qa.ubuntu.com/job/ido-ci/34/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ido-saucy-amd64-ci/33
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ido-saucy-armhf-ci/16

Click here to trigger a rebuild:
http://s-jenkins:8080/job/ido-ci/34/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/libido3-0.1-0.symbols'
2--- debian/libido3-0.1-0.symbols 2013-07-31 02:02:53 +0000
3+++ debian/libido3-0.1-0.symbols 2013-08-13 20:56:02 +0000
4@@ -1,11 +1,14 @@
5 libido3-0.1.so.0 libido3-0.1-0 #MINVER#
6 ido_action_helper_activate@Base 13.10.0daily13.06.19
7+ ido_action_helper_activate_with_parameter@Base 0replaceme
8 ido_action_helper_change_action_state@Base 13.10.0daily13.06.19
9 ido_action_helper_get_action_target@Base 13.10.0daily13.06.19
10 ido_action_helper_get_type@Base 13.10.0daily13.06.19
11 ido_action_helper_get_widget@Base 13.10.0daily13.06.19
12 ido_action_helper_new@Base 13.10.0daily13.06.19
13 ido_alarm_menu_item_new_from_model@Base 13.10.0+13.10.20130731
14+ ido_application_menu_item_get_type@Base 0replaceme
15+ ido_application_menu_item_new_from_model@Base 0replaceme
16 ido_appointment_menu_item_new_from_model@Base 13.10.0daily13.06.19
17 ido_basic_menu_item_get_type@Base 13.10.0+13.10.20130731
18 ido_basic_menu_item_new@Base 13.10.0+13.10.20130731
19@@ -24,6 +27,11 @@
20 ido_calendar_menu_item_set_date@Base 0.2.2
21 ido_calendar_menu_item_set_display_options@Base 0.2.1
22 ido_calendar_menu_item_unmark_day@Base 0.2.1
23+ ido_detail_label_get_text@Base 0replaceme
24+ ido_detail_label_get_type@Base 0replaceme
25+ ido_detail_label_new@Base 0replaceme
26+ ido_detail_label_set_count@Base 0replaceme
27+ ido_detail_label_set_text@Base 0replaceme
28 ido_entry_menu_item_get_entry@Base 0.1.0
29 ido_entry_menu_item_get_type@Base 0.1.0
30 ido_entry_menu_item_new@Base 0.1.0
31@@ -57,6 +65,8 @@
32 ido_scale_menu_item_set_secondary_label@Base 0.1.9
33 ido_scale_menu_item_set_style@Base 0.1.9
34 ido_scale_menu_item_style_get_type@Base 0.1.9
35+ ido_source_menu_item_get_type@Base 0replaceme
36+ ido_source_menu_item_new_from_menu_model@Base 0replaceme
37 ido_switch_menu_item_get_content_area@Base 12.10.0
38 ido_switch_menu_item_get_type@Base 12.10.0
39 ido_switch_menu_item_new@Base 12.10.0
40
41=== modified file 'src/Makefile.am'
42--- src/Makefile.am 2013-07-25 23:53:48 +0000
43+++ src/Makefile.am 2013-08-13 20:56:02 +0000
44@@ -27,7 +27,10 @@
45 libido.h \
46 idoactionhelper.h \
47 idomediaplayermenuitem.h \
48- idoplaybackmenuitem.h
49+ idoplaybackmenuitem.h \
50+ idoapplicationmenuitem.h \
51+ idodetaillabel.h \
52+ idosourcemenuitem.h
53
54 EXTRA_DIST = \
55 ido.list \
56@@ -86,7 +89,10 @@
57 idoappointmentmenuitem.c \
58 idobasicmenuitem.c \
59 idotimestampmenuitem.c \
60- idolocationmenuitem.c
61+ idolocationmenuitem.c \
62+ idoapplicationmenuitem.c \
63+ idodetaillabel.c \
64+ idosourcemenuitem.c
65
66 libido3_0_1_la_SOURCES = $(libido_0_1_la_SOURCES)
67
68
69=== modified file 'src/idoactionhelper.c'
70--- src/idoactionhelper.c 2013-05-31 22:50:09 +0000
71+++ src/idoactionhelper.c 2013-08-13 20:56:02 +0000
72@@ -403,6 +403,30 @@
73 }
74
75 /**
76+ * ido_action_helper_activate_with_parameter:
77+ * @helper: an #IdoActionHelper
78+ * @parameter: a #GVariant containing the parameter
79+ *
80+ * Activates the action that is associated with this helper passing
81+ * @parameter instead the "target" associated with the menu item this
82+ * helper is bound to.
83+ */
84+void
85+ido_action_helper_activate_with_parameter (IdoActionHelper *helper,
86+ GVariant *parameter)
87+{
88+ g_return_if_fail (IDO_IS_ACTION_HELPER (helper));
89+ g_return_if_fail (parameter != NULL);
90+
91+ g_variant_ref_sink (parameter);
92+
93+ if (helper->actions && helper->action_name)
94+ g_action_group_activate_action (helper->actions, helper->action_name, parameter);
95+
96+ g_variant_unref (parameter);
97+}
98+
99+/**
100 * ido_action_helper_change_action_state:
101 * @helper: an #IdoActionHelper
102 * @state: the proposed new state of the action
103
104=== modified file 'src/idoactionhelper.h'
105--- src/idoactionhelper.h 2013-05-31 22:50:09 +0000
106+++ src/idoactionhelper.h 2013-08-13 20:56:02 +0000
107@@ -41,6 +41,9 @@
108
109 void ido_action_helper_activate (IdoActionHelper *helper);
110
111+void ido_action_helper_activate_with_parameter (IdoActionHelper *helper,
112+ GVariant *parameter);
113+
114 void ido_action_helper_change_action_state (IdoActionHelper *helper,
115 GVariant *state);
116
117
118=== added file 'src/idoapplicationmenuitem.c'
119--- src/idoapplicationmenuitem.c 1970-01-01 00:00:00 +0000
120+++ src/idoapplicationmenuitem.c 2013-08-13 20:56:02 +0000
121@@ -0,0 +1,195 @@
122+/*
123+ * Copyright 2013 Canonical Ltd.
124+ *
125+ * This program is free software: you can redistribute it and/or modify it
126+ * under the terms of the GNU General Public License version 3, as published
127+ * by the Free Software Foundation.
128+ *
129+ * This program is distributed in the hope that it will be useful, but
130+ * WITHOUT ANY WARRANTY; without even the implied warranties of
131+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
132+ * PURPOSE. See the GNU General Public License for more details.
133+ *
134+ * You should have received a copy of the GNU General Public License along
135+ * with this program. If not, see <http://www.gnu.org/licenses/>.
136+ *
137+ * Authors:
138+ * Lars Uebernickel <lars.uebernickel@canonical.com>
139+ */
140+
141+#include "idoapplicationmenuitem.h"
142+#include "idoactionhelper.h"
143+
144+typedef GtkMenuItemClass IdoApplicationMenuItemClass;
145+
146+struct _IdoApplicationMenuItem
147+{
148+ GtkMenuItem parent;
149+
150+ gboolean is_running;
151+
152+ GtkWidget *icon;
153+ GtkWidget *label;
154+};
155+
156+G_DEFINE_TYPE (IdoApplicationMenuItem, ido_application_menu_item, GTK_TYPE_MENU_ITEM);
157+
158+static void
159+ido_application_menu_item_constructed (GObject *object)
160+{
161+ IdoApplicationMenuItem *item = IDO_APPLICATION_MENU_ITEM (object);
162+ GtkWidget *grid;
163+
164+ item->icon = g_object_ref (gtk_image_new ());
165+ gtk_widget_set_margin_right (item->icon, 6);
166+
167+ item->label = g_object_ref (gtk_label_new (""));
168+
169+ grid = gtk_grid_new ();
170+ gtk_grid_attach (GTK_GRID (grid), item->icon, 0, 0, 1, 1);
171+ gtk_grid_attach (GTK_GRID (grid), item->label, 1, 0, 1, 1);
172+
173+ gtk_container_add (GTK_CONTAINER (object), grid);
174+ gtk_widget_show_all (grid);
175+
176+ G_OBJECT_CLASS (ido_application_menu_item_parent_class)->constructed (object);
177+}
178+
179+static void
180+ido_application_menu_item_dispose (GObject *object)
181+{
182+ IdoApplicationMenuItem *self = IDO_APPLICATION_MENU_ITEM (object);
183+
184+ g_clear_object (&self->icon);
185+ g_clear_object (&self->label);
186+
187+ G_OBJECT_CLASS (ido_application_menu_item_parent_class)->dispose (object);
188+}
189+
190+static gboolean
191+ido_application_menu_item_draw (GtkWidget *widget,
192+ cairo_t *cr)
193+{
194+ IdoApplicationMenuItem *item = IDO_APPLICATION_MENU_ITEM (widget);
195+
196+ GTK_WIDGET_CLASS (ido_application_menu_item_parent_class)->draw (widget, cr);
197+
198+ if (item->is_running)
199+ {
200+ const int arrow_width = 5;
201+ const double half_arrow_height = 4.5;
202+ GtkAllocation alloc;
203+ GdkRGBA color;
204+ double center;
205+
206+ gtk_widget_get_allocation (widget, &alloc);
207+
208+ gtk_style_context_get_color (gtk_widget_get_style_context (widget),
209+ gtk_widget_get_state_flags (widget),
210+ &color);
211+ gdk_cairo_set_source_rgba (cr, &color);
212+
213+ center = alloc.height / 2 + 0.5;
214+
215+ cairo_move_to (cr, 0, center - half_arrow_height);
216+ cairo_line_to (cr, 0, center + half_arrow_height);
217+ cairo_line_to (cr, arrow_width, center);
218+ cairo_close_path (cr);
219+
220+ cairo_fill (cr);
221+ }
222+
223+ return FALSE;
224+}
225+
226+void
227+ido_application_menu_item_class_init (IdoApplicationMenuItemClass *klass)
228+{
229+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
230+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
231+
232+ object_class->constructed = ido_application_menu_item_constructed;
233+ object_class->dispose = ido_application_menu_item_dispose;
234+
235+ widget_class->draw = ido_application_menu_item_draw;
236+}
237+
238+static void
239+ido_application_menu_item_init (IdoApplicationMenuItem *self)
240+{
241+}
242+
243+static void
244+ido_application_menu_item_set_label (IdoApplicationMenuItem *item,
245+ const gchar *label)
246+{
247+ gtk_label_set_label (GTK_LABEL (item->label), label);
248+}
249+
250+static void
251+ido_application_menu_item_set_icon (IdoApplicationMenuItem *item,
252+ GIcon *icon)
253+{
254+ gtk_image_set_from_gicon (GTK_IMAGE (item->icon), icon, GTK_ICON_SIZE_MENU);
255+}
256+
257+static void
258+ido_application_menu_item_state_changed (IdoActionHelper *helper,
259+ GVariant *state,
260+ gpointer user_data)
261+{
262+ IdoApplicationMenuItem *item = user_data;
263+
264+ item->is_running = g_variant_get_boolean (state);
265+ gtk_widget_queue_draw (GTK_WIDGET (item));
266+}
267+
268+GtkMenuItem *
269+ido_application_menu_item_new_from_model (GMenuItem *menuitem,
270+ GActionGroup *actions)
271+{
272+ GtkMenuItem *item;
273+ gchar *label;
274+ GVariant *serialized_icon;
275+ gchar *action;
276+
277+ item = g_object_new (IDO_TYPE_APPLICATION_MENU_ITEM, NULL);
278+
279+ if (g_menu_item_get_attribute (menuitem, "label", "s", &label))
280+ {
281+ ido_application_menu_item_set_label (IDO_APPLICATION_MENU_ITEM (item), label);
282+ g_free (label);
283+ }
284+
285+ serialized_icon = g_menu_item_get_attribute_value (menuitem, "icon", NULL);
286+ if (serialized_icon)
287+ {
288+ GIcon *icon;
289+
290+ icon = g_icon_deserialize (serialized_icon);
291+ if (icon)
292+ {
293+ ido_application_menu_item_set_icon (IDO_APPLICATION_MENU_ITEM (item), icon);
294+ g_object_unref (icon);
295+ }
296+
297+ g_variant_unref (serialized_icon);
298+ }
299+
300+ if (g_menu_item_get_attribute (menuitem, "action", "s", &action))
301+ {
302+ IdoActionHelper *helper;
303+
304+ helper = ido_action_helper_new (GTK_WIDGET (item), actions, action, NULL);
305+ g_signal_connect (helper, "action-state-changed",
306+ G_CALLBACK (ido_application_menu_item_state_changed), item);
307+ g_signal_connect_object (item, "activate",
308+ G_CALLBACK (ido_action_helper_activate), helper,
309+ G_CONNECT_SWAPPED);
310+ g_signal_connect_swapped (item, "destroy", G_CALLBACK (g_object_unref), helper);
311+
312+ g_free (action);
313+ }
314+
315+ return item;
316+}
317
318=== added file 'src/idoapplicationmenuitem.h'
319--- src/idoapplicationmenuitem.h 1970-01-01 00:00:00 +0000
320+++ src/idoapplicationmenuitem.h 2013-08-13 20:56:02 +0000
321@@ -0,0 +1,36 @@
322+/*
323+ * Copyright 2013 Canonical Ltd.
324+ *
325+ * This program is free software: you can redistribute it and/or modify it
326+ * under the terms of the GNU General Public License version 3, as published
327+ * by the Free Software Foundation.
328+ *
329+ * This program is distributed in the hope that it will be useful, but
330+ * WITHOUT ANY WARRANTY; without even the implied warranties of
331+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
332+ * PURPOSE. See the GNU General Public License for more details.
333+ *
334+ * You should have received a copy of the GNU General Public License along
335+ * with this program. If not, see <http://www.gnu.org/licenses/>.
336+ *
337+ * Authors:
338+ * Lars Uebernickel <lars.uebernickel@canonical.com>
339+ */
340+
341+#ifndef __IDO_APPLICATION_MENU_ITEM_H__
342+#define __IDO_APPLICATION_MENU_ITEM_H__
343+
344+#include <gtk/gtk.h>
345+
346+#define IDO_TYPE_APPLICATION_MENU_ITEM (ido_application_menu_item_get_type ())
347+#define IDO_APPLICATION_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_TYPE_APPLICATION_MENU_ITEM, IdoApplicationMenuItem))
348+#define IS_IDO_APPLICATION_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_TYPE_APPLICATION_MENU_ITEM))
349+
350+typedef struct _IdoApplicationMenuItem IdoApplicationMenuItem;
351+
352+GType ido_application_menu_item_get_type (void);
353+
354+GtkMenuItem * ido_application_menu_item_new_from_model (GMenuItem *item,
355+ GActionGroup *actions);
356+
357+#endif
358
359=== added file 'src/idodetaillabel.c'
360--- src/idodetaillabel.c 1970-01-01 00:00:00 +0000
361+++ src/idodetaillabel.c 2013-08-13 20:56:02 +0000
362@@ -0,0 +1,401 @@
363+/*
364+ * Copyright 2012 Canonical Ltd.
365+ *
366+ * This program is free software: you can redistribute it and/or modify it
367+ * under the terms of the GNU General Public License version 3, as published
368+ * by the Free Software Foundation.
369+ *
370+ * This program is distributed in the hope that it will be useful, but
371+ * WITHOUT ANY WARRANTY; without even the implied warranties of
372+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
373+ * PURPOSE. See the GNU General Public License for more details.
374+ *
375+ * You should have received a copy of the GNU General Public License along
376+ * with this program. If not, see <http://www.gnu.org/licenses/>.
377+ *
378+ * Authors:
379+ * Lars Uebernickel <lars.uebernickel@canonical.com>
380+ */
381+
382+#include "idodetaillabel.h"
383+
384+#include <math.h>
385+
386+G_DEFINE_TYPE (IdoDetailLabel, ido_detail_label, GTK_TYPE_WIDGET)
387+
388+struct _IdoDetailLabelPrivate
389+{
390+ gchar *text;
391+ PangoLayout *layout;
392+ gboolean draw_lozenge;
393+};
394+
395+enum
396+{
397+ PROP_0,
398+ PROP_TEXT,
399+ NUM_PROPERTIES
400+};
401+
402+static GParamSpec *properties[NUM_PROPERTIES];
403+
404+static void
405+ido_detail_label_get_property (GObject *object,
406+ guint property_id,
407+ GValue *value,
408+ GParamSpec *pspec)
409+{
410+ IdoDetailLabel *self = IDO_DETAIL_LABEL (object);
411+
412+ switch (property_id)
413+ {
414+ case PROP_TEXT:
415+ g_value_set_string (value, self->priv->text);
416+ break;
417+
418+ default:
419+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
420+ }
421+}
422+
423+static void
424+ido_detail_label_set_property (GObject *object,
425+ guint property_id,
426+ const GValue *value,
427+ GParamSpec *pspec)
428+{
429+ IdoDetailLabel *self = IDO_DETAIL_LABEL (object);
430+
431+ switch (property_id)
432+ {
433+ case PROP_TEXT:
434+ ido_detail_label_set_text (self, g_value_get_string (value));
435+ break;
436+
437+ default:
438+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
439+ }
440+}
441+
442+
443+static void
444+ido_detail_label_finalize (GObject *object)
445+{
446+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv;
447+
448+ g_free (priv->text);
449+
450+ G_OBJECT_CLASS (ido_detail_label_parent_class)->finalize (object);
451+}
452+
453+static void
454+ido_detail_label_dispose (GObject *object)
455+{
456+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv;
457+
458+ g_clear_object (&priv->layout);
459+
460+ G_OBJECT_CLASS (ido_detail_label_parent_class)->dispose (object);
461+}
462+
463+static void
464+ido_detail_label_ensure_layout (IdoDetailLabel *label)
465+{
466+ IdoDetailLabelPrivate *priv = label->priv;
467+
468+ if (priv->layout == NULL)
469+ {
470+ priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), priv->text);
471+ pango_layout_set_alignment (priv->layout, PANGO_ALIGN_CENTER);
472+ pango_layout_set_ellipsize (priv->layout, PANGO_ELLIPSIZE_END);
473+ pango_layout_set_height (priv->layout, -1);
474+
475+ // TODO update layout on "style-updated" and "direction-changed"
476+ }
477+}
478+
479+static void
480+cairo_lozenge (cairo_t *cr,
481+ double x,
482+ double y,
483+ double w,
484+ double h,
485+ double radius)
486+{
487+ double x1 = x + w - radius;
488+ double x2 = x + radius;
489+ double y1 = y + radius;
490+ double y2 = y + h - radius;
491+
492+ cairo_move_to (cr, x + radius, y);
493+ cairo_arc (cr, x1, y1, radius, G_PI * 1.5, G_PI * 2);
494+ cairo_arc (cr, x1, y2, radius, 0, G_PI * 0.5);
495+ cairo_arc (cr, x2, y2, radius, G_PI * 0.5, G_PI);
496+ cairo_arc (cr, x2, y1, radius, G_PI, G_PI * 1.5);
497+}
498+
499+static PangoFontMetrics *
500+gtk_widget_get_font_metrics (GtkWidget *widget,
501+ PangoContext *context)
502+{
503+ PangoFontDescription *font;
504+ PangoFontMetrics *metrics;
505+
506+ gtk_style_context_get (gtk_widget_get_style_context (widget),
507+ gtk_widget_get_state_flags (widget),
508+ "font", &font, NULL);
509+
510+ metrics = pango_context_get_metrics (context,
511+ font,
512+ pango_context_get_language (context));
513+
514+ pango_font_description_free (font);
515+ return metrics;
516+}
517+
518+static gint
519+ido_detail_label_get_minimum_text_width (IdoDetailLabel *label)
520+{
521+ IdoDetailLabelPrivate *priv = label->priv;
522+ PangoContext *context;
523+ PangoFontMetrics *metrics;
524+ gint char_width;
525+ gint w;
526+
527+ context = pango_layout_get_context (priv->layout);
528+ metrics = gtk_widget_get_font_metrics (GTK_WIDGET (label), context);
529+ char_width = pango_font_metrics_get_approximate_digit_width (metrics);
530+
531+ w = 2 * char_width / PANGO_SCALE;
532+ pango_font_metrics_unref (metrics);
533+ return w;
534+}
535+
536+static gboolean
537+ido_detail_label_draw (GtkWidget *widget,
538+ cairo_t *cr)
539+{
540+ IdoDetailLabel *label = IDO_DETAIL_LABEL (widget);
541+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
542+ PangoRectangle extents;
543+ GtkAllocation allocation;
544+ double x, w, h, radius;
545+ GdkRGBA color;
546+
547+ if (!priv->text || !*priv->text)
548+ return TRUE;
549+
550+ gtk_widget_get_allocation (widget, &allocation);
551+
552+ ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
553+
554+ pango_layout_get_extents (priv->layout, NULL, &extents);
555+ pango_extents_to_pixels (&extents, NULL);
556+
557+ h = MIN (allocation.height, extents.height);
558+ radius = floor (h / 2.0);
559+ w = MAX (ido_detail_label_get_minimum_text_width (label), extents.width) + 2.0 * radius;
560+ x = allocation.width - w;
561+
562+ pango_layout_set_width (priv->layout, (allocation.width - 2 * radius) * PANGO_SCALE);
563+ pango_layout_get_extents (priv->layout, NULL, &extents);
564+ pango_extents_to_pixels (&extents, NULL);
565+
566+ gtk_style_context_get_color (gtk_widget_get_style_context (widget),
567+ gtk_widget_get_state_flags (widget),
568+ &color);
569+ gdk_cairo_set_source_rgba (cr, &color);
570+
571+ cairo_set_line_width (cr, 1.0);
572+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
573+
574+ if (priv->draw_lozenge)
575+ cairo_lozenge (cr, x, 0.0, w, h, radius);
576+
577+ cairo_move_to (cr, x + radius, (allocation.height - extents.height) / 2.0);
578+ pango_cairo_layout_path (cr, priv->layout);
579+ cairo_fill (cr);
580+
581+ return TRUE;
582+}
583+
584+static void
585+ido_detail_label_get_preferred_width (GtkWidget *widget,
586+ gint *minimum,
587+ gint *natural)
588+{
589+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
590+ PangoRectangle extents;
591+ double radius;
592+
593+ ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
594+
595+ pango_layout_get_extents (priv->layout, NULL, &extents);
596+ pango_extents_to_pixels (&extents, NULL);
597+
598+ radius = floor (extents.height / 2.0);
599+
600+ *minimum = ido_detail_label_get_minimum_text_width (IDO_DETAIL_LABEL (widget)) + 2.0 * radius;
601+ *natural = MAX (*minimum, extents.width + 2.0 * radius);
602+}
603+
604+static void
605+ido_detail_label_get_preferred_height (GtkWidget *widget,
606+ gint *minimum,
607+ gint *natural)
608+{
609+ IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
610+ PangoContext *context;
611+ PangoFontMetrics *metrics;
612+ PangoRectangle extents;
613+
614+ ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
615+
616+ pango_layout_get_extents (priv->layout, NULL, &extents);
617+ pango_extents_to_pixels (&extents, NULL);
618+ context = pango_layout_get_context (priv->layout);
619+ metrics = gtk_widget_get_font_metrics (widget, context);
620+
621+ *minimum = *natural = (pango_font_metrics_get_ascent (metrics) +
622+ pango_font_metrics_get_descent (metrics)) / PANGO_SCALE;
623+
624+ pango_font_metrics_unref (metrics);
625+}
626+
627+static void
628+ido_detail_label_class_init (IdoDetailLabelClass *klass)
629+{
630+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
631+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
632+
633+ object_class->get_property = ido_detail_label_get_property;
634+ object_class->set_property = ido_detail_label_set_property;
635+ object_class->finalize = ido_detail_label_finalize;
636+ object_class->dispose = ido_detail_label_dispose;
637+
638+ widget_class->draw = ido_detail_label_draw;
639+ widget_class->get_preferred_width = ido_detail_label_get_preferred_width;
640+ widget_class->get_preferred_height = ido_detail_label_get_preferred_height;
641+
642+ g_type_class_add_private (klass, sizeof (IdoDetailLabelPrivate));
643+
644+ properties[PROP_TEXT] = g_param_spec_string ("text",
645+ "Text",
646+ "The text of the label",
647+ NULL,
648+ G_PARAM_READWRITE |
649+ G_PARAM_STATIC_STRINGS);
650+
651+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
652+}
653+
654+static void
655+ido_detail_label_init (IdoDetailLabel *self)
656+{
657+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
658+ IDO_TYPE_DETAIL_LABEL,
659+ IdoDetailLabelPrivate);
660+
661+ gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
662+}
663+
664+GtkWidget *
665+ido_detail_label_new (const gchar *label)
666+{
667+ return g_object_new (IDO_TYPE_DETAIL_LABEL,
668+ "text", label,
669+ NULL);
670+}
671+
672+const gchar *
673+ido_detail_label_get_text (IdoDetailLabel *label)
674+{
675+ g_return_val_if_fail (IDO_IS_DETAIL_LABEL (label), NULL);
676+ return label->priv->text;
677+}
678+
679+/* collapse_whitespace:
680+ * @str: the source string
681+ *
682+ * Collapses all occurences of consecutive whitespace charactes in @str
683+ * into a single space.
684+ *
685+ * Returns: (transfer full): a newly-allocated string
686+ */
687+static gchar *
688+collapse_whitespace (const gchar *str)
689+{
690+ GString *result;
691+ gboolean in_space = FALSE;
692+
693+ if (str == NULL)
694+ return NULL;
695+
696+ result = g_string_new ("");
697+
698+ while (*str)
699+ {
700+ gunichar c = g_utf8_get_char_validated (str, -1);
701+
702+ if (c == (gunichar) -1)
703+ break;
704+
705+ if (!g_unichar_isspace (c))
706+ {
707+ g_string_append_unichar (result, c);
708+ in_space = FALSE;
709+ }
710+ else if (!in_space)
711+ {
712+ g_string_append_c (result, ' ');
713+ in_space = TRUE;
714+ }
715+
716+ str = g_utf8_next_char (str);
717+ }
718+
719+ return g_string_free (result, FALSE);
720+}
721+
722+static void
723+ido_detail_label_set_text_impl (IdoDetailLabel *label,
724+ const gchar *text,
725+ gboolean draw_lozenge)
726+{
727+ IdoDetailLabelPrivate * priv = label->priv;
728+
729+ g_clear_object (&priv->layout);
730+ g_free (priv->text);
731+
732+ priv->text = g_strdup (text);
733+ priv->draw_lozenge = draw_lozenge;
734+
735+ g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]);
736+ gtk_widget_queue_resize (GTK_WIDGET (label));
737+}
738+
739+void
740+ido_detail_label_set_text (IdoDetailLabel *label,
741+ const gchar *text)
742+{
743+ gchar *str;
744+
745+ g_return_if_fail (IDO_IS_DETAIL_LABEL (label));
746+
747+ str = collapse_whitespace (text);
748+ ido_detail_label_set_text_impl (label, str, FALSE);
749+ g_free (str);
750+}
751+
752+void
753+ido_detail_label_set_count (IdoDetailLabel *label,
754+ gint count)
755+{
756+ gchar *text;
757+
758+ g_return_if_fail (IDO_IS_DETAIL_LABEL (label));
759+
760+ text = g_strdup_printf ("%d", count);
761+ ido_detail_label_set_text_impl (label, text, TRUE);
762+ g_free (text);
763+}
764
765=== added file 'src/idodetaillabel.h'
766--- src/idodetaillabel.h 1970-01-01 00:00:00 +0000
767+++ src/idodetaillabel.h 2013-08-13 20:56:02 +0000
768@@ -0,0 +1,59 @@
769+/*
770+ * Copyright 2012 Canonical Ltd.
771+ *
772+ * This program is free software: you can redistribute it and/or modify it
773+ * under the terms of the GNU General Public License version 3, as published
774+ * by the Free Software Foundation.
775+ *
776+ * This program is distributed in the hope that it will be useful, but
777+ * WITHOUT ANY WARRANTY; without even the implied warranties of
778+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
779+ * PURPOSE. See the GNU General Public License for more details.
780+ *
781+ * You should have received a copy of the GNU General Public License along
782+ * with this program. If not, see <http://www.gnu.org/licenses/>.
783+ *
784+ * Authors:
785+ * Lars Uebernickel <lars.uebernickel@canonical.com>
786+ */
787+
788+#ifndef __IDO_DETAIL_LABEL_H__
789+#define __IDO_DETAIL_LABEL_H__
790+
791+#include <gtk/gtk.h>
792+
793+#define IDO_TYPE_DETAIL_LABEL (ido_detail_label_get_type())
794+#define IDO_DETAIL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_TYPE_DETAIL_LABEL, IdoDetailLabel))
795+#define IDO_DETAIL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IDO_TYPE_DETAIL_LABEL, IdoDetailLabelClass))
796+#define IDO_IS_DETAIL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_TYPE_DETAIL_LABEL))
797+#define IDO_IS_DETAIL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IDO_TYPE_DETAIL_LABEL))
798+#define IDO_DETAIL_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IDO_TYPE_DETAIL_LABEL, IdoDetailLabelClass))
799+
800+typedef struct _IdoDetailLabel IdoDetailLabel;
801+typedef struct _IdoDetailLabelClass IdoDetailLabelClass;
802+typedef struct _IdoDetailLabelPrivate IdoDetailLabelPrivate;
803+
804+struct _IdoDetailLabel
805+{
806+ GtkWidget parent;
807+ IdoDetailLabelPrivate *priv;
808+};
809+
810+struct _IdoDetailLabelClass
811+{
812+ GtkWidgetClass parent_class;
813+};
814+
815+GType ido_detail_label_get_type (void) G_GNUC_CONST;
816+
817+GtkWidget * ido_detail_label_new (const gchar *str);
818+
819+const gchar * ido_detail_label_get_text (IdoDetailLabel *label);
820+
821+void ido_detail_label_set_text (IdoDetailLabel *label,
822+ const gchar *text);
823+
824+void ido_detail_label_set_count (IdoDetailLabel *label,
825+ gint count);
826+
827+#endif
828
829=== modified file 'src/idomenuitemfactory.c'
830--- src/idomenuitemfactory.c 2013-07-25 23:53:48 +0000
831+++ src/idomenuitemfactory.c 2013-08-13 20:56:02 +0000
832@@ -29,6 +29,8 @@
833 #include "idousermenuitem.h"
834 #include "idomediaplayermenuitem.h"
835 #include "idoplaybackmenuitem.h"
836+#include "idoapplicationmenuitem.h"
837+#include "idosourcemenuitem.h"
838
839 #define IDO_TYPE_MENU_ITEM_FACTORY (ido_menu_item_factory_get_type ())
840 #define IDO_MENU_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), IDO_TYPE_MENU_ITEM_FACTORY, IdoMenuItemFactory))
841@@ -83,6 +85,12 @@
842 else if (g_str_equal (type, "com.canonical.unity.playback-item"))
843 item = ido_playback_menu_item_new_from_model (menuitem, actions);
844
845+ else if (g_str_equal (type, "com.canonical.application"))
846+ item = ido_application_menu_item_new_from_model (menuitem, actions);
847+
848+ else if (g_str_equal (type, "com.canonical.indicator.messages.source"))
849+ item = ido_source_menu_item_new_from_menu_model (menuitem, actions);
850+
851 return item;
852 }
853
854
855=== added file 'src/idosourcemenuitem.c'
856--- src/idosourcemenuitem.c 1970-01-01 00:00:00 +0000
857+++ src/idosourcemenuitem.c 2013-08-13 20:56:02 +0000
858@@ -0,0 +1,264 @@
859+/*
860+ * Copyright 2013 Canonical Ltd.
861+ *
862+ * This program is free software: you can redistribute it and/or modify it
863+ * under the terms of the GNU General Public License version 3, as published
864+ * by the Free Software Foundation.
865+ *
866+ * This program is distributed in the hope that it will be useful, but
867+ * WITHOUT ANY WARRANTY; without even the implied warranties of
868+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
869+ * PURPOSE. See the GNU General Public License for more details.
870+ *
871+ * You should have received a copy of the GNU General Public License along
872+ * with this program. If not, see <http://www.gnu.org/licenses/>.
873+ *
874+ * Authors:
875+ * Lars Uebernickel <lars.uebernickel@canonical.com>
876+ */
877+
878+#include "idosourcemenuitem.h"
879+
880+#include <libintl.h>
881+#include "idodetaillabel.h"
882+#include "idoactionhelper.h"
883+
884+typedef GtkMenuItemClass IdoSourceMenuItemClass;
885+
886+struct _IdoSourceMenuItem
887+{
888+ GtkMenuItem parent;
889+
890+ GtkWidget *icon;
891+ GtkWidget *label;
892+ GtkWidget *detail;
893+
894+ gint64 time;
895+ guint timer_id;
896+};
897+
898+G_DEFINE_TYPE (IdoSourceMenuItem, ido_source_menu_item, GTK_TYPE_MENU_ITEM);
899+
900+static void
901+ido_source_menu_item_constructed (GObject *object)
902+{
903+ IdoSourceMenuItem *item = IDO_SOURCE_MENU_ITEM (object);
904+ GtkWidget *grid;
905+ gint icon_width;
906+
907+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL);
908+
909+ item->icon = g_object_ref (gtk_image_new ());
910+ gtk_widget_set_margin_left (item->icon, icon_width);
911+ gtk_widget_set_margin_right (item->icon, 6);
912+
913+ item->label = g_object_ref (gtk_label_new (""));
914+ gtk_label_set_max_width_chars (GTK_LABEL (item->label), 40);
915+ gtk_label_set_ellipsize (GTK_LABEL (item->label), PANGO_ELLIPSIZE_END);
916+ gtk_misc_set_alignment (GTK_MISC (item->label), 0.0, 0.5);
917+
918+ item->detail = g_object_ref (ido_detail_label_new (""));
919+ gtk_widget_set_halign (item->detail, GTK_ALIGN_END);
920+ gtk_widget_set_hexpand (item->detail, TRUE);
921+ gtk_style_context_add_class (gtk_widget_get_style_context (item->detail), "accelerator");
922+
923+ grid = gtk_grid_new ();
924+ gtk_grid_attach (GTK_GRID (grid), item->icon, 0, 0, 1, 1);
925+ gtk_grid_attach (GTK_GRID (grid), item->label, 1, 0, 1, 1);
926+ gtk_grid_attach (GTK_GRID (grid), item->detail, 2, 0, 1, 1);
927+
928+ gtk_container_add (GTK_CONTAINER (object), grid);
929+ gtk_widget_show_all (grid);
930+
931+ G_OBJECT_CLASS (ido_source_menu_item_parent_class)->constructed (object);
932+}
933+
934+static gchar *
935+ido_source_menu_item_time_span_string (gint64 timestamp)
936+{
937+ gchar *str;
938+ gint64 span;
939+ gint hours;
940+ gint minutes;
941+
942+ span = MAX (g_get_real_time () - timestamp, 0) / G_USEC_PER_SEC;
943+ hours = span / 3600;
944+ minutes = (span / 60) % 60;
945+
946+ if (hours == 0)
947+ {
948+ /* TRANSLATORS: number of minutes that have passed */
949+ str = g_strdup_printf (ngettext ("%d min", "%d min", minutes), minutes);
950+ }
951+ else
952+ {
953+ /* TRANSLATORS: number of hours that have passed */
954+ str = g_strdup_printf (ngettext ("%d h", "%d h", hours), hours);
955+ }
956+
957+ return str;
958+}
959+
960+static void
961+ido_source_menu_item_set_detail_time (IdoSourceMenuItem *self,
962+ gint64 time)
963+{
964+ gchar *str;
965+
966+ self->time = time;
967+
968+ str = ido_source_menu_item_time_span_string (self->time);
969+ ido_detail_label_set_text (IDO_DETAIL_LABEL (self->detail), str);
970+
971+ g_free (str);
972+}
973+
974+static gboolean
975+ido_source_menu_item_update_time (gpointer data)
976+{
977+ IdoSourceMenuItem *self = data;
978+
979+ ido_source_menu_item_set_detail_time (self, self->time);
980+
981+ return TRUE;
982+}
983+
984+static void
985+ido_source_menu_item_dispose (GObject *object)
986+{
987+ IdoSourceMenuItem *self = IDO_SOURCE_MENU_ITEM (object);
988+
989+ if (self->timer_id != 0)
990+ {
991+ g_source_remove (self->timer_id);
992+ self->timer_id = 0;
993+ }
994+
995+ g_clear_object (&self->icon);
996+ g_clear_object (&self->label);
997+ g_clear_object (&self->detail);
998+
999+ G_OBJECT_CLASS (ido_source_menu_item_parent_class)->dispose (object);
1000+}
1001+
1002+static void
1003+ido_source_menu_item_class_init (IdoSourceMenuItemClass *klass)
1004+{
1005+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
1006+
1007+ object_class->constructed = ido_source_menu_item_constructed;
1008+ object_class->dispose = ido_source_menu_item_dispose;
1009+}
1010+
1011+static void
1012+ido_source_menu_item_init (IdoSourceMenuItem *self)
1013+{
1014+}
1015+
1016+static void
1017+ido_source_menu_item_set_label (IdoSourceMenuItem *item,
1018+ const gchar *label)
1019+{
1020+ gtk_label_set_label (GTK_LABEL (item->label), label ? label : "");
1021+}
1022+
1023+static void
1024+ido_source_menu_item_set_icon (IdoSourceMenuItem *item,
1025+ GIcon *icon)
1026+{
1027+ if (icon)
1028+ gtk_image_set_from_gicon (GTK_IMAGE (item->icon), icon, GTK_ICON_SIZE_MENU);
1029+ else
1030+ gtk_image_clear (GTK_IMAGE (item->icon));
1031+}
1032+
1033+
1034+static void
1035+ido_source_menu_item_activate (GtkMenuItem *item,
1036+ gpointer user_data)
1037+{
1038+ IdoActionHelper *helper = user_data;
1039+
1040+ /* The parameter signifies whether this source was activated (TRUE) or
1041+ * dismissed (FALSE). Since there's no UI to dismiss a gtkmenuitem,
1042+ * this always passes TRUE. */
1043+ ido_action_helper_activate_with_parameter (helper, g_variant_new_boolean (TRUE));
1044+}
1045+
1046+static void
1047+ido_source_menu_item_state_changed (IdoActionHelper *helper,
1048+ GVariant *state,
1049+ gpointer user_data)
1050+{
1051+ IdoSourceMenuItem *item = user_data;
1052+ guint32 count;
1053+ gint64 time;
1054+ const gchar *str;
1055+
1056+ if (item->timer_id != 0)
1057+ {
1058+ g_source_remove (item->timer_id);
1059+ item->timer_id = 0;
1060+ }
1061+
1062+ g_return_val_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)")), FALSE);
1063+
1064+ g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL);
1065+
1066+ if (count != 0)
1067+ ido_detail_label_set_count (IDO_DETAIL_LABEL (item->detail), count);
1068+ else if (time != 0)
1069+ {
1070+ ido_source_menu_item_set_detail_time (item, time);
1071+ item->timer_id = g_timeout_add_seconds (59, ido_source_menu_item_update_time, item);
1072+ }
1073+ else if (str != NULL && *str)
1074+ ido_detail_label_set_text (IDO_DETAIL_LABEL (item->detail), str);
1075+}
1076+
1077+GtkMenuItem *
1078+ido_source_menu_item_new_from_menu_model (GMenuItem *menuitem,
1079+ GActionGroup *actions)
1080+{
1081+ GtkMenuItem *item;
1082+ GVariant *serialized_icon;
1083+ GIcon *icon = NULL;
1084+ gchar *label;
1085+ gchar *action = NULL;
1086+
1087+ item = g_object_new (IDO_TYPE_SOURCE_MENU_ITEM, NULL);
1088+
1089+ if (g_menu_item_get_attribute (menuitem, "label", "s", &label))
1090+ {
1091+ ido_source_menu_item_set_label (IDO_SOURCE_MENU_ITEM (item), label);
1092+ g_free (label);
1093+ }
1094+
1095+ serialized_icon = g_menu_item_get_attribute_value (menuitem, "icon", NULL);
1096+ if (serialized_icon)
1097+ {
1098+ icon = g_icon_deserialize (serialized_icon);
1099+ g_variant_unref (serialized_icon);
1100+ }
1101+ ido_source_menu_item_set_icon (IDO_SOURCE_MENU_ITEM (item), icon);
1102+
1103+ if (g_menu_item_get_attribute (menuitem, "action", "s", &action))
1104+ {
1105+ IdoActionHelper *helper;
1106+
1107+ helper = ido_action_helper_new (GTK_WIDGET (item), actions, action, NULL);
1108+ g_signal_connect (helper, "action-state-changed",
1109+ G_CALLBACK (ido_source_menu_item_state_changed), item);
1110+ g_signal_connect_object (item, "activate",
1111+ G_CALLBACK (ido_source_menu_item_activate), helper,
1112+ 0);
1113+ g_signal_connect_swapped (item, "destroy", G_CALLBACK (g_object_unref), helper);
1114+
1115+ g_free (action);
1116+ }
1117+
1118+ if (icon)
1119+ g_object_unref (icon);
1120+
1121+ return item;
1122+}
1123
1124=== added file 'src/idosourcemenuitem.h'
1125--- src/idosourcemenuitem.h 1970-01-01 00:00:00 +0000
1126+++ src/idosourcemenuitem.h 2013-08-13 20:56:02 +0000
1127@@ -0,0 +1,36 @@
1128+/*
1129+ * Copyright 2013 Canonical Ltd.
1130+ *
1131+ * This program is free software: you can redistribute it and/or modify it
1132+ * under the terms of the GNU General Public License version 3, as published
1133+ * by the Free Software Foundation.
1134+ *
1135+ * This program is distributed in the hope that it will be useful, but
1136+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1137+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1138+ * PURPOSE. See the GNU General Public License for more details.
1139+ *
1140+ * You should have received a copy of the GNU General Public License along
1141+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1142+ *
1143+ * Authors:
1144+ * Lars Uebernickel <lars.uebernickel@canonical.com>
1145+ */
1146+
1147+#ifndef __IDO_SOURCE_MENU_ITEM_H__
1148+#define __IDO_SOURCE_MENU_ITEM_H__
1149+
1150+#include <gtk/gtk.h>
1151+
1152+#define IDO_TYPE_SOURCE_MENU_ITEM (ido_source_menu_item_get_type ())
1153+#define IDO_SOURCE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_TYPE_SOURCE_MENU_ITEM, IdoSourceMenuItem))
1154+#define IDO_IS_SOURCE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_TYPE_SOURCE_MENU_ITEM))
1155+
1156+typedef struct _IdoSourceMenuItem IdoSourceMenuItem;
1157+
1158+GType ido_source_menu_item_get_type (void);
1159+
1160+GtkMenuItem * ido_source_menu_item_new_from_menu_model (GMenuItem *menuitem,
1161+ GActionGroup *actions);
1162+
1163+#endif

Subscribers

People subscribed via source and target branches

to all changes: