Merge lp:~attente/unity-gtk-module/gtk-enable-mnemonics into lp:unity-gtk-module/14.04
- gtk-enable-mnemonics
- Merge into trunk.14.04
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Sebastien Bacher | ||||
Approved revision: | 322 | ||||
Merged at revision: | 315 | ||||
Proposed branch: | lp:~attente/unity-gtk-module/gtk-enable-mnemonics | ||||
Merge into: | lp:unity-gtk-module/14.04 | ||||
Diff against target: |
403 lines (+190/-48) 5 files modified
lib/unity-gtk-menu-item-private.h (+1/-0) lib/unity-gtk-menu-item.c (+92/-19) lib/unity-gtk-menu-shell-private.h (+20/-18) lib/unity-gtk-menu-shell.c (+76/-11) lib/unity-gtk-menu-shell.h (+1/-0) |
||||
To merge this branch: | bzr merge lp:~attente/unity-gtk-module/gtk-enable-mnemonics | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sebastien Bacher | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Allison Karlitskaya (community) | Needs Fixing | ||
Review via email: mp+207752@code.launchpad.net |
Commit message
Filter out mnemonics when the gtk-enable-
Description of the change
Filter out mnemonics when the gtk-enable-
PS Jenkins bot (ps-jenkins) wrote : | # |
Allison Karlitskaya (desrt) wrote : | # |
trying to dynamically handle the settings changing at runtime is maybe a bit insane.... but assuming that indeed you do want to do this:
unity_gtk_
keeping a gtksettings per menuitem is definitely overkill -- it's exceptionally unlikely that we will ever see more than one gtksettings per program. just use the global one. cache it in a static if you really don't trust that it will never change...
since you will just use the global gtksettings, no need to remove/readd the signal handler every time. also: don't bother with handler ids: they don't really make much of an improvement in performance. just disconnect by funcs/data.
finally: you shouldn't try to do this for every single menu item: only for toplevel ones in menubars.
one unrelated note: your underscore removal algorithm's attempt to be correct on non-ascii utf8 strings is not really necessary. all non-ascii utf8 sequences are composed entirely of non-ascii characters (ie: every byte is > 0x80). it is therefore impossible for '_' to appear in the middle of the sequence. just do the naive thing here and it will be correct.
Allison Karlitskaya (desrt) wrote : | # |
about the strdup: the strlen() + 1 on sized new is not necessary with gstring -- it adds the nul internally. maybe skip GString here entirely since you know that the result string will definitely be smaller than the input.
also: with respect to my previous comments, strchr() is your friend.
- 316. By William Hua
-
Pass property name directly.
- 317. By William Hua
-
Simplify mnemonic removal.
- 318. By William Hua
-
Only honour gtk-enable-
mnemonics on top-level shells.
William Hua (attente) wrote : | # |
I added the signal handler connection/
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:318
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Allison Karlitskaya (desrt) wrote : | # |
This is better, but still not exactly what I had in mind. I wouldn't block on further review bickering at this point, though.
What I was thinking of (and did a relative poor job of explaining) is one global default GtkSettings watch (using gtk_settings_
- 319. By William Hua
-
Use only one signal handler for notify:
:gtk-enable- mnemonics.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:319
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 320. By William Hua
-
Reset the label in the label handler.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:320
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Allison Karlitskaya (desrt) wrote : | # |
Keeping the global list is really strange -- particularly considering that in practice there will only ever be exactly one menubar. It would have been better to just hook directly up to each (of the one and only) toplevel shell instead of bothering with all of this register stuff...
This is totally fine as-is, though...
- 321. By William Hua
-
Don't use a global linked list.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:321
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Allison Karlitskaya (desrt) wrote : | # |
Just one more nag: this check will never fail:
+ if (gtk_enable_
282 + gtk_enable_
283 +
284 + if (g_param_
285 + {
because you already have a detailed signal connection.
- 322. By William Hua
-
Don't check pspec name in detailed signal handler.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:322
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Sebastien Bacher (seb128) wrote : | # |
Seems like that's ready to land, thanks guys for the review/iterations!
Preview Diff
1 | === modified file 'lib/unity-gtk-menu-item-private.h' |
2 | --- lib/unity-gtk-menu-item-private.h 2013-11-28 18:29:27 +0000 |
3 | +++ lib/unity-gtk-menu-item-private.h 2014-03-05 17:18:00 +0000 |
4 | @@ -53,6 +53,7 @@ |
5 | guchar child_shell_valid : 1; |
6 | guint item_index; |
7 | UnityGtkAction *action; |
8 | + gchar *label; |
9 | }; |
10 | |
11 | GType unity_gtk_menu_item_get_type (void) G_GNUC_INTERNAL; |
12 | |
13 | === modified file 'lib/unity-gtk-menu-item.c' |
14 | --- lib/unity-gtk-menu-item.c 2013-11-28 18:29:27 +0000 |
15 | +++ lib/unity-gtk-menu-item.c 2014-03-05 17:18:00 +0000 |
16 | @@ -19,6 +19,7 @@ |
17 | |
18 | #include "unity-gtk-menu-item-private.h" |
19 | #include "unity-gtk-action-group-private.h" |
20 | +#include <string.h> |
21 | |
22 | G_DEFINE_TYPE (UnityGtkMenuItem, |
23 | unity_gtk_menu_item, |
24 | @@ -307,7 +308,7 @@ |
25 | g_return_if_fail (parent_shell != NULL); |
26 | g_warn_if_fail (object == menu_item); |
27 | |
28 | - unity_gtk_menu_shell_handle_item_notify (parent_shell, item, pspec); |
29 | + unity_gtk_menu_shell_handle_item_notify (parent_shell, item, g_param_spec_get_name (pspec)); |
30 | } |
31 | |
32 | static void |
33 | @@ -368,11 +369,27 @@ |
34 | } |
35 | |
36 | static void |
37 | +unity_gtk_menu_item_finalize (GObject *object) |
38 | +{ |
39 | + UnityGtkMenuItem *item; |
40 | + |
41 | + g_return_if_fail (UNITY_GTK_IS_MENU_ITEM (object)); |
42 | + |
43 | + item = UNITY_GTK_MENU_ITEM (object); |
44 | + |
45 | + g_free (item->label); |
46 | + item->label = NULL; |
47 | + |
48 | + G_OBJECT_CLASS (unity_gtk_menu_item_parent_class)->finalize (object); |
49 | +} |
50 | + |
51 | +static void |
52 | unity_gtk_menu_item_class_init (UnityGtkMenuItemClass *klass) |
53 | { |
54 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
55 | |
56 | object_class->dispose = unity_gtk_menu_item_dispose; |
57 | + object_class->finalize = unity_gtk_menu_item_finalize; |
58 | } |
59 | |
60 | static void |
61 | @@ -416,7 +433,7 @@ |
62 | GtkWidget *submenu = gtk_menu_item_get_submenu (menu_item); |
63 | |
64 | if (submenu != NULL) |
65 | - item->child_shell = unity_gtk_menu_shell_new (GTK_MENU_SHELL (submenu)); |
66 | + item->child_shell = unity_gtk_menu_shell_new_internal (GTK_MENU_SHELL (submenu)); |
67 | } |
68 | |
69 | item->child_shell_valid = TRUE; |
70 | @@ -461,36 +478,92 @@ |
71 | } |
72 | } |
73 | |
74 | +static gchar * |
75 | +g_strdup_no_mnemonics (const gchar *str) |
76 | +{ |
77 | + if (str != NULL) |
78 | + { |
79 | + gchar *string; |
80 | + gchar *out; |
81 | + const gchar *in; |
82 | + gboolean underscore; |
83 | + |
84 | + string = g_malloc (strlen (str) + 1); |
85 | + out = string; |
86 | + underscore = FALSE; |
87 | + |
88 | + for (in = str; *in != '\0'; in++) |
89 | + { |
90 | + if (*in != '_') |
91 | + { |
92 | + underscore = FALSE; |
93 | + *out++ = *in; |
94 | + } |
95 | + else |
96 | + { |
97 | + if (!underscore) |
98 | + underscore = TRUE; |
99 | + else |
100 | + { |
101 | + /* double underscores are not accelerator markers */ |
102 | + underscore = FALSE; |
103 | + *out++ = '_'; |
104 | + *out++ = '_'; |
105 | + } |
106 | + } |
107 | + } |
108 | + |
109 | + /* trailing underscores are not accelerator markers */ |
110 | + if (underscore) |
111 | + *out++ = '_'; |
112 | + |
113 | + *out++ = '\0'; |
114 | + |
115 | + return string; |
116 | + } |
117 | + |
118 | + return NULL; |
119 | +} |
120 | + |
121 | const gchar * |
122 | unity_gtk_menu_item_get_label (UnityGtkMenuItem *item) |
123 | { |
124 | - const gchar *label; |
125 | - |
126 | g_return_val_if_fail (UNITY_GTK_IS_MENU_ITEM (item), NULL); |
127 | g_return_val_if_fail (item->menu_item != NULL, NULL); |
128 | |
129 | - label = gtk_menu_item_get_label (item->menu_item); |
130 | - |
131 | - if (label != NULL && label[0] != '\0') |
132 | + if (item->label == NULL) |
133 | { |
134 | - if (GTK_IS_IMAGE_MENU_ITEM (item->menu_item)) |
135 | + const gchar *label = gtk_menu_item_get_label (item->menu_item); |
136 | + |
137 | + if (label != NULL && label[0] != '\0') |
138 | { |
139 | - GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (item->menu_item); |
140 | - |
141 | - if (gtk_image_menu_item_get_use_stock (image_menu_item)) |
142 | + if (GTK_IS_IMAGE_MENU_ITEM (item->menu_item)) |
143 | { |
144 | - GtkStockItem stock_item; |
145 | - |
146 | - if (gtk_stock_lookup (label, &stock_item)) |
147 | - label = stock_item.label; |
148 | + GtkImageMenuItem *image_menu_item = GTK_IMAGE_MENU_ITEM (item->menu_item); |
149 | + |
150 | + if (gtk_image_menu_item_get_use_stock (image_menu_item)) |
151 | + { |
152 | + GtkStockItem stock_item; |
153 | + |
154 | + if (gtk_stock_lookup (label, &stock_item)) |
155 | + label = stock_item.label; |
156 | + } |
157 | } |
158 | } |
159 | + |
160 | + if (label == NULL || label[0] == '\0') |
161 | + label = gtk_menu_item_get_nth_label (item->menu_item, 0); |
162 | + |
163 | + if (label != NULL && label[0] != '\0') |
164 | + { |
165 | + if (item->parent_shell == NULL || item->parent_shell->has_mnemonics) |
166 | + item->label = g_strdup (label); |
167 | + else |
168 | + item->label = g_strdup_no_mnemonics (label); |
169 | + } |
170 | } |
171 | |
172 | - if (label == NULL || label[0] == '\0') |
173 | - label = gtk_menu_item_get_nth_label (item->menu_item, 0); |
174 | - |
175 | - return label != NULL && label[0] != '\0' ? label : NULL; |
176 | + return item->label; |
177 | } |
178 | |
179 | GIcon * |
180 | |
181 | === modified file 'lib/unity-gtk-menu-shell-private.h' |
182 | --- lib/unity-gtk-menu-shell-private.h 2013-11-28 18:29:27 +0000 |
183 | +++ lib/unity-gtk-menu-shell-private.h 2014-03-05 17:18:00 +0000 |
184 | @@ -26,24 +26,26 @@ |
185 | |
186 | G_BEGIN_DECLS |
187 | |
188 | -UnityGtkMenuItem * unity_gtk_menu_shell_get_item (UnityGtkMenuShell *shell, |
189 | - guint index) G_GNUC_INTERNAL; |
190 | - |
191 | -GSequence * unity_gtk_menu_shell_get_visible_indices (UnityGtkMenuShell *shell) G_GNUC_INTERNAL; |
192 | - |
193 | -GSequence * unity_gtk_menu_shell_get_separator_indices (UnityGtkMenuShell *shell) G_GNUC_INTERNAL; |
194 | - |
195 | -void unity_gtk_menu_shell_handle_item_notify (UnityGtkMenuShell *shell, |
196 | - UnityGtkMenuItem *item, |
197 | - GParamSpec *pspec) G_GNUC_INTERNAL; |
198 | - |
199 | -void unity_gtk_menu_shell_activate_item (UnityGtkMenuShell *shell, |
200 | - UnityGtkMenuItem *item) G_GNUC_INTERNAL; |
201 | - |
202 | -void unity_gtk_menu_shell_print (UnityGtkMenuShell *shell, |
203 | - guint indent) G_GNUC_INTERNAL; |
204 | - |
205 | -gboolean unity_gtk_menu_shell_is_debug (void) G_GNUC_INTERNAL; |
206 | +UnityGtkMenuShell * unity_gtk_menu_shell_new_internal (GtkMenuShell *menu_shell) G_GNUC_INTERNAL; |
207 | + |
208 | +UnityGtkMenuItem * unity_gtk_menu_shell_get_item (UnityGtkMenuShell *shell, |
209 | + guint index) G_GNUC_INTERNAL; |
210 | + |
211 | +GSequence * unity_gtk_menu_shell_get_visible_indices (UnityGtkMenuShell *shell) G_GNUC_INTERNAL; |
212 | + |
213 | +GSequence * unity_gtk_menu_shell_get_separator_indices (UnityGtkMenuShell *shell) G_GNUC_INTERNAL; |
214 | + |
215 | +void unity_gtk_menu_shell_handle_item_notify (UnityGtkMenuShell *shell, |
216 | + UnityGtkMenuItem *item, |
217 | + const gchar *property) G_GNUC_INTERNAL; |
218 | + |
219 | +void unity_gtk_menu_shell_activate_item (UnityGtkMenuShell *shell, |
220 | + UnityGtkMenuItem *item) G_GNUC_INTERNAL; |
221 | + |
222 | +void unity_gtk_menu_shell_print (UnityGtkMenuShell *shell, |
223 | + guint indent) G_GNUC_INTERNAL; |
224 | + |
225 | +gboolean unity_gtk_menu_shell_is_debug (void) G_GNUC_INTERNAL; |
226 | |
227 | G_END_DECLS |
228 | |
229 | |
230 | === modified file 'lib/unity-gtk-menu-shell.c' |
231 | --- lib/unity-gtk-menu-shell.c 2014-01-13 15:41:42 +0000 |
232 | +++ lib/unity-gtk-menu-shell.c 2014-03-05 17:18:00 +0000 |
233 | @@ -435,6 +435,13 @@ |
234 | unity_gtk_menu_shell_handle_item_label (UnityGtkMenuShell *shell, |
235 | UnityGtkMenuItem *item) |
236 | { |
237 | + g_return_if_fail (UNITY_GTK_IS_MENU_SHELL (shell)); |
238 | + g_return_if_fail (UNITY_GTK_IS_MENU_ITEM (item)); |
239 | + g_warn_if_fail (item->parent_shell == shell); |
240 | + |
241 | + g_free (item->label); |
242 | + item->label = NULL; |
243 | + |
244 | unity_gtk_menu_shell_update_item (shell, item); |
245 | } |
246 | |
247 | @@ -654,6 +661,41 @@ |
248 | } |
249 | } |
250 | |
251 | +static void |
252 | +unity_gtk_menu_shell_set_has_mnemonics (UnityGtkMenuShell *shell, |
253 | + gboolean has_mnemonics) |
254 | +{ |
255 | + g_return_if_fail (UNITY_GTK_IS_MENU_SHELL (shell)); |
256 | + |
257 | + if (has_mnemonics != shell->has_mnemonics) |
258 | + { |
259 | + shell->has_mnemonics = has_mnemonics; |
260 | + |
261 | + if (shell->items != NULL) |
262 | + { |
263 | + guint i; |
264 | + |
265 | + for (i = 0; i < shell->items->len; i++) |
266 | + unity_gtk_menu_shell_handle_item_label (shell, g_ptr_array_index (shell->items, i)); |
267 | + } |
268 | + } |
269 | +} |
270 | + |
271 | +static void |
272 | +unity_gtk_menu_shell_handle_settings_notify (GObject *object, |
273 | + GParamSpec *pspec, |
274 | + gpointer user_data) |
275 | +{ |
276 | + gboolean has_mnemonics; |
277 | + |
278 | + g_return_if_fail (GTK_IS_SETTINGS (object)); |
279 | + g_return_if_fail (UNITY_GTK_IS_MENU_SHELL (user_data)); |
280 | + |
281 | + g_object_get (GTK_SETTINGS (object), "gtk-enable-mnemonics", &has_mnemonics, NULL); |
282 | + |
283 | + unity_gtk_menu_shell_set_has_mnemonics (UNITY_GTK_MENU_SHELL (user_data), has_mnemonics); |
284 | +} |
285 | + |
286 | static void unity_gtk_menu_shell_clear_menu_shell (UnityGtkMenuShell *shell); |
287 | |
288 | static void |
289 | @@ -731,13 +773,18 @@ |
290 | unity_gtk_menu_shell_dispose (GObject *object) |
291 | { |
292 | UnityGtkMenuShell *shell; |
293 | + GtkSettings *settings; |
294 | |
295 | g_return_if_fail (UNITY_GTK_IS_MENU_SHELL (object)); |
296 | |
297 | shell = UNITY_GTK_MENU_SHELL (object); |
298 | + settings = gtk_settings_get_default (); |
299 | |
300 | unity_gtk_menu_shell_set_menu_shell (shell, NULL); |
301 | |
302 | + if (settings != NULL) |
303 | + g_signal_handlers_disconnect_by_func (settings, unity_gtk_menu_shell_handle_settings_notify, shell); |
304 | + |
305 | G_OBJECT_CLASS (unity_gtk_menu_shell_parent_class)->dispose (object); |
306 | } |
307 | |
308 | @@ -806,6 +853,7 @@ |
309 | static void |
310 | unity_gtk_menu_shell_init (UnityGtkMenuShell *self) |
311 | { |
312 | + self->has_mnemonics = TRUE; |
313 | } |
314 | |
315 | /** |
316 | @@ -822,6 +870,23 @@ |
317 | unity_gtk_menu_shell_new (GtkMenuShell *menu_shell) |
318 | { |
319 | UnityGtkMenuShell *shell = g_object_new (UNITY_GTK_TYPE_MENU_SHELL, NULL); |
320 | + GtkSettings *settings = gtk_settings_get_default (); |
321 | + |
322 | + if (settings != NULL) |
323 | + { |
324 | + g_signal_connect (settings, "notify::gtk-enable-mnemonics", G_CALLBACK (unity_gtk_menu_shell_handle_settings_notify), shell); |
325 | + g_object_get (settings, "gtk-enable-mnemonics", &shell->has_mnemonics, NULL); |
326 | + } |
327 | + |
328 | + unity_gtk_menu_shell_set_menu_shell (shell, menu_shell); |
329 | + |
330 | + return shell; |
331 | +} |
332 | + |
333 | +UnityGtkMenuShell * |
334 | +unity_gtk_menu_shell_new_internal (GtkMenuShell *menu_shell) |
335 | +{ |
336 | + UnityGtkMenuShell *shell = g_object_new (UNITY_GTK_TYPE_MENU_SHELL, NULL); |
337 | |
338 | unity_gtk_menu_shell_set_menu_shell (shell, menu_shell); |
339 | |
340 | @@ -899,7 +964,7 @@ |
341 | void |
342 | unity_gtk_menu_shell_handle_item_notify (UnityGtkMenuShell *shell, |
343 | UnityGtkMenuItem *item, |
344 | - GParamSpec *pspec) |
345 | + const gchar *property) |
346 | { |
347 | static const gchar *visible_name; |
348 | static const gchar *sensitive_name; |
349 | @@ -909,7 +974,7 @@ |
350 | static const gchar *parent_name; |
351 | static const gchar *submenu_name; |
352 | |
353 | - const gchar *pspec_name; |
354 | + const gchar *name; |
355 | |
356 | g_return_if_fail (UNITY_GTK_IS_MENU_SHELL (shell)); |
357 | g_return_if_fail (UNITY_GTK_IS_MENU_ITEM (item)); |
358 | @@ -929,24 +994,24 @@ |
359 | if (submenu_name == NULL) |
360 | submenu_name = g_intern_static_string ("submenu"); |
361 | |
362 | - pspec_name = g_param_spec_get_name (pspec); |
363 | + name = g_intern_string (property); |
364 | |
365 | if (unity_gtk_menu_shell_is_debug ()) |
366 | - g_print ("%s ((%s *) %p, (%s *) %p { \"%s\" }, %s)\n", G_STRFUNC, G_OBJECT_TYPE_NAME (shell), shell, G_OBJECT_TYPE_NAME (item), item, unity_gtk_menu_item_get_label (item), pspec_name); |
367 | + g_print ("%s ((%s *) %p, (%s *) %p { \"%s\" }, %s)\n", G_STRFUNC, G_OBJECT_TYPE_NAME (shell), shell, G_OBJECT_TYPE_NAME (item), item, unity_gtk_menu_item_get_label (item), name); |
368 | |
369 | - if (pspec_name == visible_name) |
370 | + if (name == visible_name) |
371 | unity_gtk_menu_shell_handle_item_visible (shell, item); |
372 | - else if (pspec_name == sensitive_name) |
373 | + else if (name == sensitive_name) |
374 | unity_gtk_menu_shell_handle_item_sensitive (shell, item); |
375 | - else if (pspec_name == label_name) |
376 | + else if (name == label_name) |
377 | unity_gtk_menu_shell_handle_item_label (shell, item); |
378 | - else if (pspec_name == accel_path_name) |
379 | + else if (name == accel_path_name) |
380 | unity_gtk_menu_shell_handle_item_accel_path (shell, item); |
381 | - else if (pspec_name == active_name) |
382 | + else if (name == active_name) |
383 | unity_gtk_menu_shell_handle_item_active (shell, item); |
384 | - else if (pspec_name == parent_name) |
385 | + else if (name == parent_name) |
386 | unity_gtk_menu_shell_handle_item_parent (shell, item); |
387 | - else if (pspec_name == submenu_name) |
388 | + else if (name == submenu_name) |
389 | unity_gtk_menu_shell_handle_item_submenu (shell, item); |
390 | } |
391 | |
392 | |
393 | === modified file 'lib/unity-gtk-menu-shell.h' |
394 | --- lib/unity-gtk-menu-shell.h 2013-02-19 11:55:57 +0000 |
395 | +++ lib/unity-gtk-menu-shell.h 2014-03-05 17:18:00 +0000 |
396 | @@ -52,6 +52,7 @@ |
397 | /*< private >*/ |
398 | GtkMenuShell *menu_shell; |
399 | gulong menu_shell_insert_handler_id; |
400 | + gboolean has_mnemonics; |
401 | GPtrArray *items; |
402 | GPtrArray *sections; |
403 | GSequence *visible_indices; |
PASSED: Continuous integration, rev:315 jenkins. qa.ubuntu. com/job/ unity-gtk- module- ci/23/ jenkins. qa.ubuntu. com/job/ unity-gtk- module- trusty- amd64-ci/ 9 jenkins. qa.ubuntu. com/job/ unity-gtk- module- trusty- armhf-ci/ 9 jenkins. qa.ubuntu. com/job/ unity-gtk- module- trusty- armhf-ci/ 9/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- gtk-module- ci/23/rebuild
http://