Merge lp:~stolowski/libunity/libunity.debug-tool-previews into lp:libunity

Proposed by Paweł Stołowski
Status: Merged
Approved by: Michal Hruby
Approved revision: 167
Merged at revision: 162
Proposed branch: lp:~stolowski/libunity/libunity.debug-tool-previews
Merge into: lp:libunity
Diff against target: 2161 lines (+1490/-231)
10 files modified
protocol/Makefile.am (+3/-8)
src/Makefile.am (+7/-10)
test/vala/Makefile.am (+3/-3)
test/vala/Makefile.integration_tests (+8/-11)
tools/Makefile.am (+19/-8)
tools/music-track-model-renderer.vala (+95/-0)
tools/preview-renderer.vala (+457/-0)
tools/unity-tool-dbus-util.vala (+100/-100)
tools/unity-tool-ui.vala (+519/-85)
tools/unity-tool.ui (+279/-6)
To merge this branch: bzr merge lp:~stolowski/libunity/libunity.debug-tool-previews
Reviewer Review Type Date Requested Status
Michal Hruby (community) Approve
Review via email: mp+119549@code.launchpad.net

Commit message

Preview support for libunity-tool and build fixes.

Description of the change

Preview support for libunity-tool.

To post a comment you must log in.
Revision history for this message
Michal Hruby (mhr3) wrote :

Works well, let's get it in...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'protocol/Makefile.am'
2--- protocol/Makefile.am 2012-07-27 09:32:20 +0000
3+++ protocol/Makefile.am 2012-08-14 14:17:21 +0000
4@@ -12,12 +12,10 @@
5 # Headers, vapi, and gir
6 ##
7 unityincludedir = $(includedir)/unity/unity
8-unityinclude_HEADERS = \
9- unity-protocol.h \
10- $(NULL)
11+nodist_unityinclude_HEADERS = unity-protocol.h
12
13 unityvapidir = $(datadir)/vala/vapi
14-unityvapi_DATA = \
15+nodist_unityvapi_DATA = \
16 unity-protocol.vapi \
17 $(NULL)
18
19@@ -55,7 +53,7 @@
20 $(LIBUNITY_PACKAGES) \
21 $(MAINTAINER_VALAFLAGS)
22
23-libunity_protocol_private_la_SOURCES = \
24+nodist_libunity_protocol_private_la_SOURCES = \
25 $(libunity_protocol_private_la_VALASOURCES:.vala=.c) \
26 $(NULL)
27
28@@ -88,6 +86,3 @@
29 $(libunity_protocol_private_la_VALASOURCES:.vala=.c) \
30 $(NULL)
31
32-dist-hook:
33- rm -f $(addprefix $(distdir)/,$(libunity_protocol_private_la_VALASOURCES:.vala=.c))
34-
35
36=== modified file 'src/Makefile.am'
37--- src/Makefile.am 2012-07-27 09:32:20 +0000
38+++ src/Makefile.am 2012-08-14 14:17:21 +0000
39@@ -12,15 +12,15 @@
40 # Headers, vapi, and gir
41 ##
42 unityincludedir = $(includedir)/unity/unity
43-unityinclude_HEADERS = \
44- unity.h \
45- unity-trace.h \
46- $(NULL)
47+nodist_unityinclude_HEADERS = unity.h
48+dist_unityinclude_HEADERS = unity-trace.h
49
50 unityvapidir = $(datadir)/vala/vapi
51-unityvapi_DATA = \
52+nodist_unityvapi_DATA = \
53 unity.vapi \
54 unity.deps \
55+ $(NULL)
56+dist_unityvapi_DATA = \
57 unity-trace.vapi \
58 unity-trace.deps \
59 $(NULL)
60@@ -80,8 +80,8 @@
61 $(LIBUNITY_PACKAGES) \
62 $(MAINTAINER_VALAFLAGS)
63
64-libunity_la_SOURCES = \
65- $(libunity_la_VALASOURCES:.vala=.c) \
66+nodist_libunity_la_SOURCES = $(libunity_la_VALASOURCES:.vala=.c)
67+dist_libunity_la_SOURCES = \
68 unity-trace.c \
69 unity-trace.h
70
71@@ -142,9 +142,6 @@
72 $(libunity_la_VALASOURCES:.vala=.c) \
73 unity.deps
74
75-dist-hook:
76- rm -f $(addprefix $(distdir)/,$(libunity_la_VALASOURCES:.vala=.c))
77-
78 ##
79 # Compile .typelib from .gir
80 ##
81
82=== modified file 'test/vala/Makefile.am'
83--- test/vala/Makefile.am 2012-07-27 09:32:20 +0000
84+++ test/vala/Makefile.am 2012-08-14 14:17:21 +0000
85@@ -51,15 +51,15 @@
86 test-scope-signals.vala \
87 test-vala.vala \
88 $(NULL)
89-test_vala_SOURCES = $(test_vala_VALASOURCES:.vala=.c)
90+nodist_test_vala_SOURCES = $(test_vala_VALASOURCES:.vala=.c)
91
92 test_lens_LDADD = $(test_libs)
93 test_lens_VALASOURCES = test-lens.vala
94-test_lens_SOURCES = $(test_lens_VALASOURCES:.vala=.c)
95+nodist_test_lens_SOURCES = $(test_lens_VALASOURCES:.vala=.c)
96
97 test_remote_scope_LDADD = $(test_libs)
98 test_remote_scope_VALASOURCES = test-remote-scope.vala
99-test_remote_scope_SOURCES = $(test_remote_scope_VALASOURCES:.vala=.c)
100+nodist_test_remote_scope_SOURCES = $(test_remote_scope_VALASOURCES:.vala=.c)
101
102 BUILT_SOURCES = \
103 test-vala.vala.stamp \
104
105=== modified file 'test/vala/Makefile.integration_tests'
106--- test/vala/Makefile.integration_tests 2012-07-27 09:32:20 +0000
107+++ test/vala/Makefile.integration_tests 2012-08-14 14:17:21 +0000
108@@ -1,3 +1,5 @@
109+if ENABLE_INTEGRATION_TESTS
110+
111 check_PROGRAMS += \
112 test-launcher-integration \
113 test-sound-menu \
114@@ -6,11 +8,6 @@
115 test-mpris-backend-prop-updates-client \
116 test-mpris-backend-prop-updates-server
117
118-# We must still build all the .c and .stamp files to distribute,
119-# so we only disable the actual test runs when integration tests
120-# are switched off
121-if ENABLE_INTEGRATION_TESTS
122-
123 TEST_PROGS += \
124 test-lens-scope-interactions \
125 test-launcher-integration \
126@@ -43,7 +40,7 @@
127 #########################################
128 test_launcher_integration_VALASOURCES = \
129 test-launcher-integration.vala
130-test_launcher_integration_SOURCES = \
131+nodist_test_launcher_integration_SOURCES = \
132 $(test_launcher_integration_VALASOURCES:.vala=.c)
133
134 test_launcher_integration_LDADD = $(test_libs)
135@@ -53,7 +50,7 @@
136 #########################################
137 test_sound_menu_VALASOURCES = \
138 test-sound-menu.vala
139-test_sound_menu_SOURCES = \
140+nodist_test_sound_menu_SOURCES = \
141 $(test_sound_menu_VALASOURCES:.vala=.c)
142
143 test_sound_menu_LDADD = $(test_libs)
144@@ -63,7 +60,7 @@
145 #########################################
146 test_mpris_backend_client_VALASOURCES = \
147 test-mpris-backend-client.vala
148-test_mpris_backend_client_SOURCES = \
149+nodist_test_mpris_backend_client_SOURCES = \
150 $(test_mpris_backend_client_VALASOURCES:.vala=.c)
151
152 test_mpris_backend_client_LDADD = $(test_libs)
153@@ -73,7 +70,7 @@
154 ########################################
155 test_mpris_backend_server_VALASOURCES = \
156 test-mpris-backend-server.vala
157-test_mpris_backend_server_SOURCES = \
158+nodist_test_mpris_backend_server_SOURCES = \
159 $(test_mpris_backend_server_VALASOURCES:.vala=.c)
160
161 test_mpris_backend_server_LDADD = $(test_libs)
162@@ -88,7 +85,7 @@
163 #########################################
164 test_mpris_backend_prop_updates_client_VALASOURCES = \
165 test-mpris-backend-prop-updates-client.vala
166-test_mpris_backend_prop_updates_client_SOURCES = \
167+nodist_test_mpris_backend_prop_updates_client_SOURCES = \
168 $(test_mpris_backend_prop_updates_client_VALASOURCES:.vala=.c)
169
170 test_mpris_backend_prop_updates_client_LDADD = $(test_libs)
171@@ -98,7 +95,7 @@
172 ########################################
173 test_mpris_backend_prop_updates_server_VALASOURCES = \
174 test-mpris-backend-prop-updates-server.vala
175-test_mpris_backend_prop_updates_server_SOURCES = \
176+nodist_test_mpris_backend_prop_updates_server_SOURCES = \
177 $(test_mpris_backend_prop_updates_server_VALASOURCES:.vala=.c)
178
179 test_mpris_backend_prop_updates_server_LDADD = $(test_libs)
180
181=== modified file 'tools/Makefile.am'
182--- tools/Makefile.am 2012-07-27 10:03:20 +0000
183+++ tools/Makefile.am 2012-08-14 14:17:21 +0000
184@@ -9,6 +9,8 @@
185 libunity_tool_CPPFLAGS = \
186 -DG_LOG_DOMAIN=\"libunity-tool\" \
187 -I$(srcdir) \
188+ -I$(top_builddir)/src \
189+ -I$(top_builddir)/protocol \
190 $(LIBUNITY_CFLAGS) \
191 $(UNITYTOOL_CFLAGS)
192
193@@ -21,36 +23,44 @@
194 endif
195
196 libunity_tool_LDADD = \
197+ $(top_builddir)/src/libunity.la \
198+ $(top_builddir)/protocol/libunity-protocol-private.la \
199 $(LIBUNITY_LIBS) \
200 $(UNITYTOOL_LIBS)
201
202 libunity_tool_VALAFLAGS = \
203 -C \
204 --vapidir $(top_srcdir)/vapi \
205- --pkg config --pkg gtk+-3.0 --pkg gmodule-2.0 \
206- $(LIBUNITY_PACKAGES)
207+ --vapidir=$(top_builddir)/protocol \
208+ --vapidir=$(top_builddir)/src \
209+ --pkg config \
210+ --pkg gtk+-3.0 \
211+ --pkg gmodule-2.0 \
212+ --pkg unity-internal \
213+ --pkg unity-protocol \
214+ $(LIBUNITY_PACKAGES) \
215 $(MAINTAINER_VALAFLAGS)
216
217 libunity_tool_VALASOURCES = \
218 unity-tool.vala \
219 unity-tool-dbus-util.vala \
220 unity-tool-ui.vala \
221+ preview-renderer.vala \
222+ music-track-model-renderer.vala \
223 $(NULL)
224
225-
226 libunity_tool_UISOURCES = unity-tool-res.gresource.xml unity-tool.ui dbus-lens-connect.ui
227
228 unity-tool-res.c: $(libunity_tool_UISOURCES)
229- $(AM_V_GEN)$(GLIB_RESCOMPILE) --sourcedir $(srcdir) --generate-source $<
230+ $(AM_V_GEN)$(GLIB_RESCOMPILE) --sourcedir $(srcdir) --target=$@ --generate-source $(filter %.xml,$^)
231
232-libunity_tool_SOURCES = \
233+nodist_libunity_tool_SOURCES = \
234 $(libunity_tool_VALASOURCES:.vala=.c) \
235 unity-tool-res.c \
236 $(NULL)
237
238-BUILT_SOURCES += unity_tool_vala.stamp
239+BUILT_SOURCES += unity-tool-res.c unity_tool_vala.stamp
240 EXTRA_DIST += \
241- $(BUILT_SOURCES) \
242 $(libunity_tool_VALASOURCES) \
243 $(libunity_tool_UISOURCES) \
244 $(NULL)
245@@ -60,7 +70,8 @@
246 @touch $@
247
248 CLEANFILES += \
249- *.stamp \
250+ unity_tool_vala.stamp \
251+ unity-tool-res.c \
252 $(libunity_tool_VALASOURCES:.vala=.c) \
253 $(NULL)
254
255
256=== added file 'tools/music-track-model-renderer.vala'
257--- tools/music-track-model-renderer.vala 1970-01-01 00:00:00 +0000
258+++ tools/music-track-model-renderer.vala 2012-08-14 14:17:21 +0000
259@@ -0,0 +1,95 @@
260+/*
261+ * Copyright (C) 2012 Canonical Ltd
262+ *
263+ * This program is free software: you can redistribute it and/or modify
264+ * it under the terms of the GNU General Public License version 3 as
265+ * published by the Free Software Foundation.
266+ *
267+ * This program is distributed in the hope that it will be useful,
268+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
269+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
270+ * GNU General Public License for more details.
271+ *
272+ * You should have received a copy of the GNU General Public License
273+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
274+ *
275+ * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
276+ *
277+ */
278+
279+using Gtk;
280+
281+namespace Unity.Tester {
282+
283+public class MusicTrackModelRenderer: Object
284+{
285+ public string dbus_model { get; construct; }
286+ private Dee.SharedModel? track_model;
287+ private Dee.ModelTag<int> track_model_tag;
288+ private int row_counter = 0;
289+ private ulong model_sync_sig_id = 0;
290+ public ListStore track_view_model { get; construct; }
291+
292+ public signal void track_list_synchronized();
293+
294+ construct
295+ {
296+ track_view_model = new ListStore(6, typeof(string), typeof(int), typeof(string), typeof(uint), typeof(uint), typeof(double));
297+ }
298+
299+ public MusicTrackModelRenderer(string dbus_model)
300+ {
301+ Object(dbus_model: dbus_model);
302+ }
303+
304+ public void sync()
305+ {
306+ track_model = new Dee.SharedModel(dbus_model);
307+ track_model_tag = new Dee.ModelTag<int>(track_model);
308+
309+ track_model.row_added.connect(track_added_cb);
310+ track_model.row_changed.connect(track_changed_cb);
311+
312+ model_sync_sig_id = track_model.notify["synchronized"].connect(track_model_synchronized_cb);
313+ }
314+
315+ private void track_added_cb(Dee.Model model, Dee.ModelIter iter)
316+ {
317+ var row = model.get_row(iter);
318+
319+ track_model_tag.set(track_model, iter, row_counter++);
320+
321+ TreeIter tm_iter;
322+ track_view_model.append(out tm_iter);
323+ track_view_model.set(tm_iter, 0, row[0].get_string(), 1, row[1].get_int32(), 2, row[2].get_string(), 3, row[3].get_uint32(), 4, row[4].get_uint32(), 5, row[5].get_double(), -1);
324+ }
325+
326+ private void track_changed_cb(Dee.Model model, Dee.ModelIter iter)
327+ {
328+ int index = track_model_tag.get(track_model, iter);
329+ TreeIter tm_iter;
330+ if (track_view_model.get_iter_first(out tm_iter)) {
331+ while (index > 0)
332+ {
333+ if (!track_view_model.iter_next(ref tm_iter)) {
334+ break;
335+ }
336+ --index;
337+ }
338+ if (index == 0) {
339+ var row = model.get_row(iter);
340+ track_view_model.set(tm_iter, 0, row[0].get_string(), 1, row[1].get_int32(), 2, row[2].get_string(), 3, row[3].get_uint32(), 4, row[4].get_uint32(), 5, row[5].get_double(), -1);
341+ } else {
342+ stderr.printf("can't update row");
343+ }
344+ }
345+ }
346+
347+ private void track_model_synchronized_cb()
348+ {
349+ SignalHandler.disconnect (track_model, model_sync_sig_id);
350+ track_list_synchronized();
351+ }
352+}
353+
354+}
355\ No newline at end of file
356
357=== added file 'tools/preview-renderer.vala'
358--- tools/preview-renderer.vala 1970-01-01 00:00:00 +0000
359+++ tools/preview-renderer.vala 2012-08-14 14:17:21 +0000
360@@ -0,0 +1,457 @@
361+/*
362+ * Copyright (C) 2012 Canonical Ltd
363+ *
364+ * This program is free software: you can redistribute it and/or modify
365+ * it under the terms of the GNU General Public License version 3 as
366+ * published by the Free Software Foundation.
367+ *
368+ * This program is distributed in the hope that it will be useful,
369+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
370+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
371+ * GNU General Public License for more details.
372+ *
373+ * You should have received a copy of the GNU General Public License
374+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
375+ *
376+ * Authored by Pawel Stolowski <pawel.stolowski@canonical.com>
377+ *
378+ */
379+using Gtk;
380+
381+namespace Unity.Tester {
382+
383+ public abstract class PreviewRenderer: Object
384+ {
385+ public signal void preview_action_clicked(string uri);
386+
387+ public abstract Gtk.Widget get_widget();
388+ public abstract Gtk.Widget get_buttons();
389+ public abstract Gtk.Widget get_extra_buttons();
390+
391+ internal abstract void render_buttons();
392+ internal abstract void render_extra_buttons();
393+ internal abstract void render();
394+
395+ public static PreviewRenderer? create(Unity.Protocol.Preview preview, string scope_uri)
396+ {
397+ if (preview is Unity.Protocol.GenericPreview) {
398+ return new GenericPreviewRenderer(preview as Unity.Protocol.GenericPreview, scope_uri);
399+ }
400+ if (preview is Unity.Protocol.ApplicationPreview) {
401+ return new ApplicationPreviewRenderer(preview as Unity.Protocol.ApplicationPreview, scope_uri);
402+ }
403+ if (preview is Unity.Protocol.MusicPreview) {
404+ return new MusicPreviewRenderer(preview as Unity.Protocol.MusicPreview, scope_uri);
405+ }
406+ if (preview is Unity.Protocol.MoviePreview) {
407+ return new MoviePreviewRenderer(preview as Unity.Protocol.MoviePreview, scope_uri);
408+ }
409+ if (preview is Unity.Protocol.SeriesPreview) {
410+ return new SeriesPreviewRenderer(preview as Unity.Protocol.SeriesPreview, scope_uri);
411+ }
412+ return null;
413+ }
414+
415+ public Unity.Protocol.Preview preview { get; construct; }
416+ public string scope_uri { get; construct; }
417+ }
418+
419+ /**
420+ * Render preview in Gtk.Grid with 2 columns (name and value).
421+ */
422+ public abstract class GridRenderer: PreviewRenderer
423+ {
424+ public GridRenderer()
425+ {
426+ Object();
427+ }
428+
429+ internal void add_standard_attributes(Unity.Protocol.Preview preview)
430+ {
431+ add_text_row("<b>Title</b>", preview.title);
432+ add_text_row("<b>Subtitle</b>", preview.subtitle);
433+ add_text_row("<b>Description</b>", preview.description);
434+ add_text_row("<b>Image source</b>", preview.image_source_uri);
435+ add_text_icon_row("<b>Image</b>", preview.image, preview.image.to_string(), 512);
436+ }
437+
438+ internal void add_info_hints(Unity.Protocol.Preview preview)
439+ {
440+ Unity.Protocol.InfoHintRaw[] hints = preview.get_info_hints();
441+ add_headline("<b><u>Info Hint</u></b>");
442+ foreach (Unity.Protocol.InfoHintRaw hint in hints) {
443+ add_text_row("<b>Id</b>", hint.id);
444+ add_text_row("<b>Display Name</b>", hint.display_name);
445+ add_text_row("<b>Icon hint</b>", hint.icon_hint);
446+ add_text_row("<b>Value</b>", hint.value.print(true));
447+ }
448+ }
449+
450+ /**
451+ * Renders name and value in two adjacent cells of grid row
452+ */
453+ public void add_text_row(string name, string? value)
454+ {
455+ var name_label = new Gtk.Label(null);
456+ name_label.set_markup(name);
457+ grid.attach(name_label, 0, row, 1, 1);
458+ var value_label = new Gtk.Label(value);
459+ value_label.selectable = true;
460+ grid.attach(value_label, 1, row, 1, 1);
461+
462+ ++row;
463+ }
464+
465+ /**
466+ * Renders text in two joined cells of grid row
467+ */
468+ public void add_headline(string text)
469+ {
470+ var label = new Gtk.Label(null);
471+ label.set_markup(text);
472+ grid.attach(label, 0, row, 2, 1);
473+
474+ ++row;
475+ }
476+
477+ /**
478+ * Renders name and arbitrary widget in two adjacent cells of grid row
479+ */
480+ public void add_widget(string name, Gtk.Widget widget)
481+ {
482+ var name_label = new Gtk.Label(null);
483+ name_label.set_markup(name);
484+ grid.attach(name_label, 0, row, 1, 1);
485+ grid.attach(widget, 1, row, 1, 1);
486+ ++row;
487+ }
488+
489+ public void on_preview_action_clicked(Gtk.Button button)
490+ {
491+ string uri = preview_actions.get(button);
492+ preview_action_clicked(uri);
493+ }
494+
495+ /**
496+ * Renders name and icon in two adjacent cells of grid row
497+ */
498+ public void add_text_icon_row(string name, GLib.Icon? icon, string? tooltip, int size=32)
499+ {
500+ var icon_label = new Gtk.Label(null);
501+ icon_label.set_markup(name);
502+ grid.attach(icon_label, 0, row, 1, 1);
503+ var themed_icon = Gtk.IconTheme.get_default().lookup_by_gicon(icon, size, 0);
504+ var pixbuf = themed_icon.load_icon();
505+ Gtk.Image image = new Gtk.Image.from_pixbuf(pixbuf);
506+ if (tooltip != null) {
507+ image.set_tooltip_text(tooltip);
508+ }
509+ grid.attach(image, 1, row, 1, 1);
510+
511+ ++row;
512+ }
513+
514+ public override Gtk.Widget get_widget()
515+ {
516+ render();
517+ grid.foreach((obj) => { obj.set_halign(Gtk.Align.START); });
518+ return grid;
519+ }
520+
521+ public override Gtk.Widget get_buttons()
522+ {
523+ render_buttons();
524+ return preview_actions_box;
525+ }
526+
527+ public override Gtk.Widget get_extra_buttons()
528+ {
529+ render_extra_buttons();
530+ return preview_extra_buttons_box;
531+ }
532+
533+ public override void render_buttons()
534+ {
535+ preview_actions = new GLib.HashTable<Gtk.Button, string>(null, null);
536+ Unity.Protocol.PreviewActionRaw[] actions = preview.get_actions();
537+ for (int i=0; i<actions.length; i++) {
538+ unowned Unity.Protocol.PreviewActionRaw action = actions[i];
539+ Gtk.Button btn = new Gtk.Button.with_label(action.display_name);
540+ btn.clicked.connect(on_preview_action_clicked);
541+ preview_actions_box.pack_start(btn, false, false);
542+ string action_uri = "%s:%s".printf(action.id, scope_uri);
543+ btn.set_tooltip_text(action_uri);
544+ preview_actions.insert(btn, action_uri);
545+ btn.show_all();
546+ }
547+ }
548+
549+ internal override void render_extra_buttons()
550+ {
551+ }
552+
553+ construct {
554+ grid = new Gtk.Grid();
555+ grid.set_column_spacing(5);
556+ grid.set_row_spacing(5);
557+ grid.border_width = 5;
558+ grid.set_halign(Gtk.Align.FILL);
559+ grid.set_valign(Gtk.Align.FILL);
560+ grid.hexpand = true;
561+ grid.vexpand = true;
562+
563+ preview_actions_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 5);
564+ preview_actions_box.set_halign(Gtk.Align.FILL);
565+ preview_actions_box.set_valign(Gtk.Align.FILL);
566+ preview_actions_box.hexpand = false;
567+ preview_actions_box.vexpand = false;
568+
569+ preview_extra_buttons_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 5);
570+ preview_extra_buttons_box.set_halign(Gtk.Align.FILL);
571+ preview_extra_buttons_box.set_valign(Gtk.Align.FILL);
572+ preview_extra_buttons_box.hexpand = false;
573+ preview_extra_buttons_box.vexpand = false;
574+ }
575+
576+ private int row = 0;
577+ public Gtk.Grid grid { get; construct; }
578+ public Gtk.Box preview_actions_box { get; construct; }
579+ public Gtk.Box preview_extra_buttons_box { get; construct; }
580+ private GLib.HashTable<Gtk.Button, string> preview_actions = null;
581+ }
582+
583+ public class GenericPreviewRenderer: GridRenderer
584+ {
585+ public GenericPreviewRenderer(Unity.Protocol.GenericPreview preview, string scope_uri)
586+ {
587+ Object(preview: preview, scope_uri: scope_uri);
588+ }
589+
590+ internal override void render()
591+ {
592+ assert(preview != null);
593+
594+ base.add_standard_attributes(preview as Unity.Protocol.GenericPreview);
595+ base.add_info_hints(preview as Unity.Protocol.GenericPreview);
596+ }
597+ }
598+
599+ public class ApplicationPreviewRenderer: GridRenderer
600+ {
601+ public ApplicationPreviewRenderer(Unity.Protocol.ApplicationPreview preview, string scope_uri)
602+ {
603+ Object(preview: preview, scope_uri: scope_uri);
604+ }
605+
606+ internal override void render()
607+ {
608+ assert(preview != null);
609+
610+ var app_preview = preview as Unity.Protocol.ApplicationPreview;
611+
612+ base.add_standard_attributes(preview);
613+ base.add_text_row("<b>License</b>", app_preview.license);
614+ base.add_text_row("<b>Copyright</b>", app_preview.copyright);
615+ base.add_text_row("<b>Last update</b>", app_preview.last_update);
616+ base.add_text_row("<b>Rating</b>", "%.2f".printf(app_preview.rating));
617+ base.add_text_row("<b>Number of ratings</b>", "%u".printf(app_preview.num_ratings));
618+ base.add_text_icon_row("<b>Application icon</b>", app_preview.app_icon, app_preview.app_icon.to_string());
619+ base.add_info_hints(app_preview);
620+ }
621+ }
622+
623+ public class MusicPreviewRenderer: GridRenderer
624+ {
625+ private MusicTrackModelRenderer track_model_renderer;
626+ private Gtk.TreeView track_view;
627+ private Gtk.Menu track_view_popup_menu;
628+
629+ public signal void play_music_track_clicked(string uri);
630+ public signal void pause_music_track_clicked(string uri);
631+
632+ public MusicPreviewRenderer(Unity.Protocol.MusicPreview preview, string scope_uri)
633+ {
634+ Object(preview: preview, scope_uri: scope_uri);
635+ }
636+
637+ internal override void render()
638+ {
639+ assert(preview != null);
640+ var music_preview = preview as Unity.Protocol.MusicPreview;
641+
642+ base.add_standard_attributes(preview);
643+ base.add_text_row("<b>Track data swarm name</b>", music_preview.track_data_swarm_name);
644+ base.add_text_row("<b>Track data address</b>", music_preview.track_data_address);
645+ base.add_info_hints(music_preview);
646+
647+ if (music_preview.track_data_swarm_name != null && music_preview.track_data_swarm_name != "")
648+ {
649+ track_model_renderer = new MusicTrackModelRenderer(music_preview.track_data_swarm_name);
650+ track_view = new TreeView();
651+ var track_view_viewport = new Viewport(null, null);
652+
653+ track_view.set_model(track_model_renderer.track_view_model);
654+
655+ track_view.insert_column_with_attributes(-1, "uri", new CellRendererText (), "text", 0);
656+ track_view.insert_column_with_attributes(-1, "track no", new CellRendererText (), "text", 1);
657+ track_view.insert_column_with_attributes(-1, "title", new CellRendererText (), "text", 2);
658+ track_view.insert_column_with_attributes(-1, "length", new CellRendererText (), "text", 3);
659+ track_view.insert_column_with_attributes(-1, "playing", new CellRendererText (), "text", 4);
660+ track_view.insert_column_with_attributes(-1, "progress", new CellRendererText (), "text", 5);
661+
662+ track_view_viewport.add_with_properties(track_view);
663+ add_widget("Track model", track_view_viewport);
664+
665+ track_view_popup_menu = new Gtk.Menu();
666+ var play_item = new Gtk.MenuItem.with_label("Play");
667+ play_item.activate.connect(on_play_item_clicked);
668+ track_view_popup_menu.append(play_item);
669+ play_item.show();
670+ var pause_item = new Gtk.MenuItem.with_label("Pause");
671+ pause_item.activate.connect(on_pause_item_clicked);
672+ track_view_popup_menu.append(pause_item);
673+ pause_item.show();
674+
675+ track_view.button_press_event.connect(on_track_view_right_click);
676+ track_model_renderer.sync();
677+ }
678+ }
679+
680+ public bool on_track_view_right_click(Gtk.Widget widget, Gdk.EventButton event)
681+ {
682+ if (event.type == Gdk.EventType.BUTTON_PRESS && event.button == 3 /* right mouse button */) {
683+ track_view_popup_menu.popup(null, null, null, event.button, event.time);
684+ }
685+ return false;
686+ }
687+
688+ internal string get_selected_track_uri()
689+ {
690+ TreeModel model;
691+ TreeIter iter;
692+ var selection = track_view.get_selection();
693+ if (selection.get_selected(out model, out iter)) {
694+ Value val;
695+ // get uri column
696+ model.get_value(iter, 0, out val);
697+ return val.get_string();
698+ }
699+ return "";
700+ }
701+
702+ internal void on_play_item_clicked(Gtk.MenuItem item)
703+ {
704+ string uri = get_selected_track_uri();
705+ if (uri != "")
706+ {
707+ play_music_track_clicked(uri);
708+ }
709+ }
710+
711+ internal void on_pause_item_clicked(Gtk.MenuItem item)
712+ {
713+ string uri = get_selected_track_uri();
714+ if (uri != "")
715+ {
716+ pause_music_track_clicked(uri);
717+ }
718+ }
719+ }
720+
721+ public class MoviePreviewRenderer: GridRenderer
722+ {
723+ public MoviePreviewRenderer(Unity.Protocol.MoviePreview preview, string scope_uri)
724+ {
725+ Object(preview: preview, scope_uri: scope_uri);
726+ }
727+
728+ internal override void render()
729+ {
730+ assert(preview != null);
731+ var movie_preview= preview as Unity.Protocol.MoviePreview;
732+
733+ base.add_standard_attributes(preview);
734+ base.add_text_row("<b>Rating</b>", "%.2f".printf(movie_preview.rating));
735+ base.add_text_row("<b>Number of ratings</b>", "%u".printf(movie_preview.num_ratings));
736+ base.add_info_hints(movie_preview);
737+ }
738+ }
739+
740+ public class SeriesPreviewRenderer: GridRenderer
741+ {
742+ public signal void change_selected_series_item_clicked(string uri, int index);
743+
744+ public SeriesPreviewRenderer(Unity.Protocol.SeriesPreview preview, string scope_uri)
745+ {
746+ Object(preview: preview, scope_uri: scope_uri);
747+ }
748+
749+ public void update_child_preview(Unity.Protocol.Preview child_preview)
750+ {
751+ (preview as Unity.Protocol.SeriesPreview).child_preview = child_preview;
752+ }
753+
754+ private void on_change_selected_item_clicked(Gtk.ComboBox combo)
755+ {
756+ int index = int.parse(combo.active_id);
757+ var series_preview = preview as Unity.Protocol.SeriesPreview;
758+ if (index != series_preview.selected_item) {
759+ change_selected_series_item_clicked(scope_uri, index);
760+ }
761+ }
762+
763+ internal override void render_extra_buttons()
764+ {
765+ var series_preview = preview as Unity.Protocol.SeriesPreview;
766+
767+ Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5);
768+
769+ Gtk.ComboBoxText items_combo = new Gtk.ComboBoxText();
770+ items_combo.changed.connect(on_change_selected_item_clicked);
771+ Protocol.SeriesItemRaw[] items = series_preview.get_items();
772+ for (int i=0; i<items.length; i++) {
773+ items_combo.append("%u".printf(i), "Series item #%u".printf(i));
774+ }
775+ items_combo.set_active_id("%u".printf(series_preview.selected_item));
776+
777+ var label = new Gtk.Label("Set active:");
778+ box.pack_start(label);
779+ box.pack_start(items_combo, false, false);
780+ box.show_all();
781+
782+ preview_extra_buttons_box.pack_start(box, false, false);
783+ }
784+
785+ internal override void render()
786+ {
787+ assert(preview != null);
788+ var series_preview = preview as Unity.Protocol.SeriesPreview;
789+
790+ base.add_standard_attributes(preview);
791+ base.add_text_row("<b>Selected item</b>", "%d".printf(series_preview.selected_item));
792+ Protocol.SeriesItemRaw[] items = series_preview.get_items();
793+
794+ PreviewRenderer? child_preview = PreviewRenderer.create(series_preview.child_preview, scope_uri);
795+ if (child_preview != null) {
796+ base.add_widget("<b>Child preview</b>", child_preview.get_widget());
797+ }
798+
799+ for (int i=0; i<items.length; i++) {
800+ base.add_headline("<u><b>Series item #%u</b></u>".printf(i));
801+ base.add_text_row("<b>Title</b>", items[i].title);
802+ base.add_text_row("<b>Uri</b>", items[i].uri);
803+
804+ if (items[i].icon_hint != null) {
805+ try {
806+ var icon = GLib.Icon.new_for_string(items[i].icon_hint);
807+ base.add_text_icon_row("<b>Icon</b>", icon, items[i].icon_hint.to_string());
808+ }
809+ catch (GLib.Error e) {
810+ stderr.printf("Series Item icon couldn't be loaded: %s\n", e.message);
811+ }
812+ }
813+ }
814+ base.add_info_hints(series_preview);
815+ }
816+ }
817+}
818
819=== modified file 'tools/unity-tool-dbus-util.vala'
820--- tools/unity-tool-dbus-util.vala 2012-06-13 15:17:44 +0000
821+++ tools/unity-tool-dbus-util.vala 2012-08-14 14:17:21 +0000
822@@ -17,12 +17,12 @@
823
824 public DBusLensUtil()
825 {
826- try {
827- lens_dbusname_regex = new Regex("^([a-zA-Z-]+(\\.[a-zA-Z-]+)+)\\.T[\\d]+.[a-zA-Z]+$");
828- }
829- catch (Error e) {
830- stderr.printf("Error parsing lens_dbusname_regex");
831- }
832+ try {
833+ lens_dbusname_regex = new Regex("^([a-zA-Z-]+(\\.[a-zA-Z-]+)+)\\.T[\\d]+.[a-zA-Z]+$");
834+ }
835+ catch (Error e) {
836+ stderr.printf("Error parsing lens_dbusname_regex");
837+ }
838 }
839
840 /**
841@@ -32,22 +32,22 @@
842 {
843 List<string> services = new List<string>();
844
845- var vt = new VariantType ("(as)");
846- var bus = Bus.get_sync (BusType.SESSION);
847- Variant v = bus.call_sync("org.freedesktop.DBus",
848- "/org/freedesktop/DBus",
849- "org.freedesktop.DBus",
850- "ListNames",
851- null,
852- vt,
853- 0,
854- -1,
855- null);
856+ var vt = new VariantType ("(as)");
857+ var bus = Bus.get_sync (BusType.SESSION);
858+ Variant v = bus.call_sync("org.freedesktop.DBus",
859+ "/org/freedesktop/DBus",
860+ "org.freedesktop.DBus",
861+ "ListNames",
862+ null,
863+ vt,
864+ 0,
865+ -1,
866+ null);
867
868- string *[]names = v.get_child_value(0).get_strv();
869- foreach (string *s in names) {
870- services.append(s);
871- }
872+ string *[]names = v.get_child_value(0).get_strv();
873+ foreach (string *s in names) {
874+ services.append(s);
875+ }
876
877 return services;
878 }
879@@ -89,85 +89,85 @@
880 */
881 public async unowned List<DBusObjectAddress?> findLenses() throws GLib.Error
882 {
883- if (lens_dbusname_regex == null) {
884- stderr.printf("Invalid lens_dbusname_regex");
885- return lenses;
886- }
887-
888- var bus = Bus.get_sync (BusType.SESSION);
889- var vt = new VariantType ("(s)");
890-
891- //
892- // Service filtering - potential lenses must match this regexp;
893- // e.g. com.canonical.Unity.Lens.applications.T1338793992370.Results
894- foreach (string srv in getServices()) {
895- if (lens_dbusname_regex.match(srv)) {
896- try {
897- var vb = new VariantBuilder(new VariantType("(s)"));
898- vb.add_value(srv);
899-
900- Variant v = bus.call_sync(
901- "org.freedesktop.DBus",
902- "/",
903- "org.freedesktop.DBus",
904- "GetNameOwner",
905- vb.end(),
906- vt,
907- 0,
908- 10,
909- null);
910-
911- if (v != null) {
912- string owner = v.get_child_value(0).get_string();
913- if (owner != null) {
914- DBusObjectAddress obj = DBusObjectAddress() {
915- dbus_name = owner,
916- dbus_path = "/"
917- };
918- nodes.push_tail(obj);
919- }
920- }
921- }
922- catch (Error e) {
923- // silently ignore
924- }
925-
926- }
927- }
928-
929- //
930- // introspect all dbus paths from nodes queue.
931- // queue may grow as new paths are discovered.
932- while (nodes.length > 0) {
933- DBusObjectAddress node = nodes.pop_head();
934-
935- current_dbus_name = node.dbus_name;
936- current_dbus_path = node.dbus_path;
937-
938- try {
939- Variant v = bus.call_sync(current_dbus_name,
940- current_dbus_path,
941- "org.freedesktop.DBus.Introspectable",
942- "Introspect",
943- null,
944- vt,
945- 0,
946- 10,
947- null);
948-
949- if (v != null) {
950- string xmldata = v.get_child_value(0).get_string();
951- var context = new MarkupParseContext (parser, MarkupParseFlags.TREAT_CDATA_AS_TEXT, this, null);
952- context.parse (xmldata, xmldata.length);
953- }
954- }
955- catch (Error e) {
956- // silently ignore
957- }
958-
959- Idle.add(findLenses.callback);
960- yield;
961- }
962+ if (lens_dbusname_regex == null) {
963+ stderr.printf("Invalid lens_dbusname_regex");
964+ return lenses;
965+ }
966+
967+ var bus = Bus.get_sync (BusType.SESSION);
968+ var vt = new VariantType ("(s)");
969+
970+ //
971+ // Service filtering - potential lenses must match this regexp;
972+ // e.g. com.canonical.Unity.Lens.applications.T1338793992370.Results
973+ foreach (string srv in getServices()) {
974+ if (lens_dbusname_regex.match(srv)) {
975+ try {
976+ var vb = new VariantBuilder(new VariantType("(s)"));
977+ vb.add_value(srv);
978+
979+ Variant v = bus.call_sync(
980+ "org.freedesktop.DBus",
981+ "/",
982+ "org.freedesktop.DBus",
983+ "GetNameOwner",
984+ vb.end(),
985+ vt,
986+ 0,
987+ 10,
988+ null);
989+
990+ if (v != null) {
991+ string owner = v.get_child_value(0).get_string();
992+ if (owner != null) {
993+ DBusObjectAddress obj = DBusObjectAddress() {
994+ dbus_name = owner,
995+ dbus_path = "/"
996+ };
997+ nodes.push_tail(obj);
998+ }
999+ }
1000+ }
1001+ catch (Error e) {
1002+ // silently ignore
1003+ }
1004+
1005+ }
1006+ }
1007+
1008+ //
1009+ // introspect all dbus paths from nodes queue.
1010+ // queue may grow as new paths are discovered.
1011+ while (nodes.length > 0) {
1012+ DBusObjectAddress node = nodes.pop_head();
1013+
1014+ current_dbus_name = node.dbus_name;
1015+ current_dbus_path = node.dbus_path;
1016+
1017+ try {
1018+ Variant v = bus.call_sync(current_dbus_name,
1019+ current_dbus_path,
1020+ "org.freedesktop.DBus.Introspectable",
1021+ "Introspect",
1022+ null,
1023+ vt,
1024+ 0,
1025+ 10,
1026+ null);
1027+
1028+ if (v != null) {
1029+ string xmldata = v.get_child_value(0).get_string();
1030+ var context = new MarkupParseContext (parser, MarkupParseFlags.TREAT_CDATA_AS_TEXT, this, null);
1031+ context.parse (xmldata, xmldata.length);
1032+ }
1033+ }
1034+ catch (Error e) {
1035+ // silently ignore
1036+ }
1037+
1038+ Idle.add(findLenses.callback);
1039+ yield;
1040+ }
1041
1042 return lenses;
1043 }
1044
1045=== modified file 'tools/unity-tool-ui.vala'
1046--- tools/unity-tool-ui.vala 2012-06-13 15:32:55 +0000
1047+++ tools/unity-tool-ui.vala 2012-08-14 14:17:21 +0000
1048@@ -43,34 +43,48 @@
1049 uimodel = builder.get_object("results_model") as ListStore;
1050 ui_filter_model = builder.get_object("filters_model") as ListStore;
1051
1052+ notebook = builder.get_object("notebook") as Notebook;
1053 search_entry = builder.get_object("search_entry") as Entry;
1054 search_type_global_rbutton = builder.get_object("search_type_global") as RadioButton;
1055 search_button = builder.get_object("search_button") as Button;
1056+ results_button = builder.get_object("results_button") as Button;
1057+ prev_preview_button = builder.get_object("prev_preview_btn") as Button;
1058+ next_preview_button = builder.get_object("next_preview_btn") as Button;
1059 statusbar = builder.get_object("statusbar") as Statusbar;
1060 log_buffer = builder.get_object("log_buffer") as TextBuffer;
1061+ preview_raw_data = builder.get_object("preview_raw_data") as TextBuffer;
1062+ preview_buttons_container = builder.get_object("preview_buttons_container") as Alignment;
1063+ preview_extra_buttons_container = builder.get_object("preview_extra_buttons_container") as Alignment;
1064+ preview_viewport = builder.get_object("preview_viewport") as Viewport;
1065+ assert(preview_viewport != null);
1066+
1067+ results_view = builder.get_object("results_view") as TreeView;
1068+ results_view_selection = builder.get_object("results_view_selection") as TreeSelection;
1069+ results_popup_menu = builder.get_object("results_popup_menu") as Gtk.Menu;
1070
1071 statusbar_info_ctx = statusbar.get_context_id("Info");
1072 statusbar_error_ctx = statusbar.get_context_id("Error");
1073
1074+ show_no_preview();
1075 show_connect_dialog();
1076 }
1077 catch (GLib.Error e) {
1078 ui_load_error(e.message);
1079- return false;
1080+ return false;
1081 }
1082 return true;
1083 }
1084
1085- private void ui_load_error(string message)
1086- {
1087- Gtk.Dialog dlg = new Gtk.MessageDialog(null,
1088+ private void ui_load_error(string message)
1089+ {
1090+ Gtk.Dialog dlg = new Gtk.MessageDialog(null,
1091 Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL,
1092 Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
1093 "Error loading UI file:\n%s".printf(message));
1094- dlg.title = "Error creating UI";
1095- dlg.run();
1096- dlg.destroy();
1097- }
1098+ dlg.title = "Error creating UI";
1099+ dlg.run();
1100+ dlg.destroy();
1101+ }
1102
1103 private void discover_lens()
1104 {
1105@@ -94,7 +108,7 @@
1106 stderr.printf("DBus Lens auto-discovery failed %s\n", e.message);
1107 lens_discovery_spinner.stop();
1108 }
1109- });
1110+ });
1111 }
1112
1113 [CCode (instance_pos = -1)]
1114@@ -113,37 +127,50 @@
1115 }
1116
1117 //
1118- // 'New' menu action handler
1119+ // Handler for File > New menu item.
1120 [CCode (instance_pos = -1)]
1121 public void on_connect_clicked(Gtk.MenuItem item)
1122 {
1123 show_connect_dialog();
1124 }
1125
1126+ /**
1127+ * Handler for Edit > Clear Log menu item.
1128+ */
1129+ [CCode (instance_pos = -1)]
1130+ public void on_clear_log_clicked(Gtk.MenuItem item)
1131+ {
1132+ Gtk.TextIter start;
1133+ Gtk.TextIter end;
1134+ log_buffer.get_start_iter(out start);
1135+ log_buffer.get_end_iter(out end);
1136+ log_buffer.delete(ref start, ref end);
1137+ }
1138+
1139 private void show_connect_dialog()
1140 {
1141 var builder = new Builder ();
1142- try {
1143- builder.add_from_resource("/com/canonical/Unity/unity-tool/dbus-lens-connect.ui");
1144- builder.connect_signals(this);
1145- lens_discovery_spinner = builder.get_object("lens_discovery_spinner") as Spinner;
1146- lens_list_model = builder.get_object("lens_list_model") as ListStore;
1147- lens_list_combobox = builder.get_object("lens_list_combobox") as ComboBox;
1148- dbus_name_entry = builder.get_object("dbus_name_entry") as Entry;
1149- dbus_path_entry = builder.get_object("dbus_path_entry") as Entry;
1150- lens_connect_dlg = builder.get_object("lens_connect_dialog") as Dialog;
1151+ try {
1152+ builder.add_from_resource("/com/canonical/Unity/unity-tool/dbus-lens-connect.ui");
1153+ builder.connect_signals(this);
1154+ lens_discovery_spinner = builder.get_object("lens_discovery_spinner") as Spinner;
1155+ lens_list_model = builder.get_object("lens_list_model") as ListStore;
1156+ lens_list_combobox = builder.get_object("lens_list_combobox") as ComboBox;
1157+ dbus_name_entry = builder.get_object("dbus_name_entry") as Entry;
1158+ dbus_path_entry = builder.get_object("dbus_path_entry") as Entry;
1159+ lens_connect_dlg = builder.get_object("lens_connect_dialog") as Dialog;
1160
1161- discover_lens();
1162-
1163- if (Options.lens_dbus_path != null && Options.lens_dbus_path != "" && Options.lens_dbus_name != null && Options.lens_dbus_name != "") {
1164- dbus_name_entry.text = Options.lens_dbus_name;
1165- dbus_path_entry.text = Options.lens_dbus_path;
1166- }
1167- lens_connect_dlg.show_all();
1168- }
1169- catch (GLib.Error e) {
1170- ui_load_error(e.message);
1171- }
1172+ discover_lens();
1173+
1174+ if (Options.lens_dbus_path != null && Options.lens_dbus_path != "" && Options.lens_dbus_name != null && Options.lens_dbus_name != "") {
1175+ dbus_name_entry.text = Options.lens_dbus_name;
1176+ dbus_path_entry.text = Options.lens_dbus_path;
1177+ }
1178+ lens_connect_dlg.show_all();
1179+ }
1180+ catch (GLib.Error e) {
1181+ ui_load_error(e.message);
1182+ }
1183 }
1184
1185 private void results_row_added_cb(Dee.Model model, Dee.ModelIter iter)
1186@@ -153,7 +180,7 @@
1187 uimodel.append(out uiiter);
1188
1189 uimodel.set(uiiter, 0, row[0].get_string(), 1, row[1].get_string(), 2, row[2].get_uint32(), 3, row[3].get_string(), 4, row[4].get_string(), 5,
1190- row[5].get_string(), 6, row[6].get_string(), -1);
1191+ row[5].get_string(), 6, row[6].get_string(), -1);
1192 }
1193
1194 private void filters_row_added_cb(Dee.Model model, Dee.ModelIter iter)
1195@@ -163,14 +190,14 @@
1196 ui_filter_model.append(out uiiter);
1197
1198 ui_filter_model.set(uiiter, 0, row[0].get_string(), 1, row[1].get_string (), 2, row[2].get_string (), 3, row[3].get_string (), 4, row[4].print(true),
1199- 5, row[5].get_boolean (), 6, row[6].get_boolean (), 7, row[7].get_boolean(), -1);
1200+ 5, row[5].get_boolean (), 6, row[6].get_boolean (), 7, row[7].get_boolean(), -1);
1201 }
1202
1203 private void update_status()
1204 {
1205 statusbar.pop(statusbar_info_ctx);
1206 statusbar.push(statusbar_info_ctx, "%u records".printf(dee_results_model.get_n_rows()) + ", DBus name: " + Options.lens_dbus_name + " path: " +
1207- Options.lens_dbus_path);
1208+ Options.lens_dbus_path);
1209 }
1210
1211 private void model_synchronized_cb()
1212@@ -186,8 +213,35 @@
1213 dee_filters_model = null;
1214 }
1215
1216+ private void on_lens_service_vanished(GLib.DBusConnection connection, string name)
1217+ {
1218+ handle_error("Disconnected from %s".printf(name));
1219+ GLib.Bus.unwatch_name(dbus_watcher_id);
1220+ dbus_watcher_id = 0;
1221+ lens_proxy = null;
1222+
1223+ clear_data();
1224+ remove_preview();
1225+ show_no_preview();
1226+
1227+ disable_ui_actions_on_error_condition();
1228+ }
1229+
1230+ private void disable_ui_actions_on_error_condition()
1231+ {
1232+ if (lens_proxy == null || Options.lens_dbus_path == null || Options.lens_dbus_path.length == 0 || Options.lens_dbus_name == null || Options.lens_dbus_name.length == 0) {
1233+ search_entry.sensitive = false;
1234+ results_button.sensitive = false;
1235+ search_button.sensitive = false;
1236+ } else {
1237+ search_entry.sensitive = true;
1238+ results_button.sensitive = true;
1239+ search_button.sensitive = true;
1240+ }
1241+ }
1242+
1243 /**
1244- * Triggered clicking 'Ok' in the connection dialog.
1245+ * Triggered by clicking 'Ok' in the connection dialog.
1246 */
1247 [CCode (instance_pos = -1)]
1248 public void on_lens_connect(Gtk.Dialog dlg, int response)
1249@@ -195,16 +249,43 @@
1250 //
1251 // user clicked 'Ok' in the Lens Connect dialog
1252 if (response == 1) {
1253+ clear_data();
1254+ remove_preview();
1255+ show_no_preview();
1256+
1257 Options.lens_dbus_name = dbus_name_entry.text;
1258 Options.lens_dbus_path = dbus_path_entry.text;
1259- }
1260- if (Options.lens_dbus_path == null || Options.lens_dbus_path.length == 0 || Options.lens_dbus_name == null || Options.lens_dbus_name.length == 0) {
1261- search_entry.sensitive = false;
1262- search_button.sensitive = false;
1263- } else {
1264- search_entry.sensitive = true;
1265- search_button.sensitive = true;
1266- }
1267+
1268+ lens_proxy = null;
1269+ DBusConnection? bus = null;
1270+
1271+ try
1272+ {
1273+ bus = Bus.get_sync(BusType.SESSION);
1274+ lens_proxy = bus.get_proxy_sync<Protocol.LensService>(Options.lens_dbus_name, Options.lens_dbus_path);
1275+
1276+ if (dbus_watcher_id > 0) {
1277+ GLib.Bus.unwatch_name(dbus_watcher_id);
1278+ }
1279+ dbus_watcher_id = GLib.Bus.watch_name(GLib.BusType.SESSION, Options.lens_dbus_name, GLib.BusNameWatcherFlags.AUTO_START, null, on_lens_service_vanished);
1280+
1281+ lens_proxy.changed.connect((info) => {
1282+ string model_name = search_type_global_rbutton.get_active() ? info.global_results_model_name : info.results_model_name;
1283+ dee_results_model = new Dee.SharedModel(model_name);
1284+ model_sync_sig_id = dee_results_model.notify["synchronized"].connect(model_synchronized_cb);
1285+ dee_results_model.row_added.connect(results_row_added_cb);
1286+
1287+ dee_filters_model = new Dee.SharedModel(info.filters_model_name);
1288+ filters_model_sync_sig_id = dee_filters_model.notify["synchronized"].connect(filter_model_synchronized_cb);
1289+ dee_filters_model.row_added.connect(filters_row_added_cb);
1290+ });
1291+ append_log_message("Connected to: %s, %s\n".printf(Options.lens_dbus_name, Options.lens_dbus_path));
1292+ }
1293+ catch (GLib.IOError e) {
1294+ handle_error(e.message);
1295+ }
1296+ }
1297+ disable_ui_actions_on_error_condition();
1298 dlg.destroy();
1299 }
1300
1301@@ -222,20 +303,20 @@
1302 var file_chooser = new FileChooserDialog("Open Lens file", null, Gtk.FileChooserAction.OPEN, Gtk.Stock.CANCEL, 0, Gtk.Stock.OPEN, 1);
1303 file_chooser.set_filter(filter);
1304 if (file_chooser.run() == 1) {
1305- try {
1306- get_lens_params_from_file(file_chooser.get_filename());
1307- dbus_name_entry.text = Options.lens_dbus_name;
1308- dbus_path_entry.text = Options.lens_dbus_path;
1309- }
1310- catch (Error e) {
1311- Gtk.Dialog dlg = new Gtk.MessageDialog(null,
1312- Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL,
1313- Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
1314- "Error loading lens parameters from file:\n%s".printf(e.message));
1315- dlg.title = "Error loading lens file";
1316- dlg.run();
1317- dlg.destroy();
1318- }
1319+ try {
1320+ get_lens_params_from_file(file_chooser.get_filename());
1321+ dbus_name_entry.text = Options.lens_dbus_name;
1322+ dbus_path_entry.text = Options.lens_dbus_path;
1323+ }
1324+ catch (Error e) {
1325+ Gtk.Dialog dlg = new Gtk.MessageDialog(null,
1326+ Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL,
1327+ Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
1328+ "Error loading lens parameters from file:\n%s".printf(e.message));
1329+ dlg.title = "Error loading lens file";
1330+ dlg.run();
1331+ dlg.destroy();
1332+ }
1333 }
1334 file_chooser.destroy();
1335 }
1336@@ -251,12 +332,19 @@
1337 }
1338
1339 /**
1340- * Request lens info via debus, creates dee models.
1341+ * Request lens info via dbus, creates dee models.
1342 */
1343 private void request_lens_info()
1344 {
1345 spinner.start();
1346-
1347+ lens_proxy.info_request();
1348+ }
1349+
1350+ /**
1351+ * Clears all models, removing all search results.
1352+ */
1353+ private void clear_data()
1354+ {
1355 if (dee_filters_model != null) {
1356 dee_filters_model = null;
1357 }
1358@@ -265,21 +353,13 @@
1359 }
1360 uimodel.clear();
1361 ui_filter_model.clear();
1362-
1363- LensInfo info = get_lens_info((msg) => { handle_error(msg); });
1364- string model_name = search_type_global_rbutton.get_active() ? info.global_results_model_name : info.results_model_name;
1365- dee_results_model = new Dee.SharedModel(model_name);
1366- model_sync_sig_id = dee_results_model.notify["synchronized"].connect(model_synchronized_cb);
1367- dee_results_model.row_added.connect(results_row_added_cb);
1368-
1369- dee_filters_model = new Dee.SharedModel(info.filters_model_name);
1370- filters_model_sync_sig_id = dee_filters_model.notify["synchronized"].connect(filter_model_synchronized_cb);
1371- dee_filters_model.row_added.connect(filters_row_added_cb);
1372 }
1373
1374 [CCode (instance_pos = -1)]
1375 public void on_results_button_clicked(Gtk.Button btn)
1376 {
1377+ clear_data();
1378+ remove_preview();
1379 request_lens_info();
1380 }
1381
1382@@ -290,29 +370,71 @@
1383 public void on_search_button_clicked(Gtk.Button btn)
1384 {
1385 string text = search_entry.text;
1386- if (text != "") {
1387- spinner.start();
1388+ spinner.start();
1389
1390- if (DBus.is_name(Options.lens_dbus_name) && GLib.Variant.is_object_path(Options.lens_dbus_path)) {
1391- append_log_message("Query: '%s' (%s), DBus name: %s, DBus path: %s\n".printf(
1392- text,
1393- search_type_global_rbutton.get_active() ? "global" : "local",
1394- Options.lens_dbus_name,
1395- Options.lens_dbus_path));
1396- call_lens_search(text, search_type_global_rbutton.get_active() ? 1 : 0, (result) =>
1397- {
1398- append_log_message(result.print(true) + "\n");
1399- request_lens_info();
1400- }, (msg) => {
1401- handle_error(msg);
1402- }
1403- );
1404+ if (DBus.is_name(Options.lens_dbus_name) && GLib.Variant.is_object_path(Options.lens_dbus_path)) {
1405+ append_log_message("Query: '%s' (%s), DBus name: %s, DBus path: %s\n".printf(
1406+ text,
1407+ search_type_global_rbutton.get_active() ? "global" : "local",
1408+ Options.lens_dbus_name,
1409+ Options.lens_dbus_path));
1410+
1411+ remove_preview();
1412+ show_no_preview();
1413+ clear_data();
1414+
1415+ if (search_type_global_rbutton.get_active()) {
1416+ lens_proxy.global_search.begin(text, new HashTable<string, Variant>(null, null),
1417+ (obj, res) => {
1418+ try {
1419+ var result = lens_proxy.global_search.end(res);
1420+ append_log_message("Search reply: " + dump_ht_reply(result) + "\n");
1421+ request_lens_info();
1422+ }
1423+ catch (Error e) {
1424+ handle_error(e.message);
1425+ }
1426+ });
1427 } else {
1428- handle_error("Invalid DBus name/path");
1429+ lens_proxy.search.begin(text, new HashTable<string, Variant>(null, null),
1430+ (obj, res) => {
1431+ try {
1432+ var result = lens_proxy.search.end(res);
1433+ append_log_message("Search reply: " + dump_ht_reply(result) + "\n");
1434+ request_lens_info();
1435+ }
1436+ catch (Error e) {
1437+ handle_error(e.message);
1438+ }
1439+ });
1440 }
1441+ } else {
1442+ handle_error("Invalid DBus name/path");
1443 }
1444 }
1445
1446+ private static string dump_ht_reply(HashTable<string, Variant> reply)
1447+ {
1448+ var bld = new StringBuilder("{\n");
1449+ reply.foreach((k, v) => {
1450+ bld.append_printf("\t%s = %s", k, v.print(true));
1451+ });
1452+ bld.append("\n}");
1453+ return bld.str;
1454+ }
1455+
1456+ private static string dump_activation_reply(Unity.Protocol.ActivationReplyRaw reply)
1457+ {
1458+ var bld = new StringBuilder();
1459+ string handled_str = ((EnumClass) typeof (Unity.HandledType).class_ref()).get_value((int)reply.handled).value_name;
1460+ bld.append_printf("ActivationReplyRaw: {\n\turi=%s,\n\thandled=%s,\n\thints={\n\t", reply.uri, handled_str);
1461+ reply.hints.foreach((k, v) => {
1462+ bld.append_printf("\t\t%s = %s", k, v.print(true));
1463+ });
1464+ bld.append("\n\t}\n}");
1465+ return bld.str;
1466+ }
1467+
1468 /**
1469 * Helper method that stops spinner and puts error message on statusbar.
1470 */
1471@@ -324,11 +446,321 @@
1472 append_log_message(message + "\n");
1473 }
1474
1475+ /**
1476+ * Helper method to workaround vala-0.16 & vala-0.17 bug -
1477+ * fix is coming to vala - see http://git.gnome.org/browse/vala/commit/?id=79925e1174d62d740ca8f360f489dd1660ea5881
1478+ */
1479+ private async void send_activate (string uri, uint action_type, out Unity.Protocol.ActivationReplyRaw reply) throws GLib.IOError
1480+ {
1481+ reply = yield lens_proxy.activate (uri, action_type);
1482+ }
1483+
1484+ private async void send_update (string uri, HashTable<string, Variant> props, out HashTable<string, Variant> reply) throws GLib.IOError
1485+ {
1486+ reply = yield lens_proxy.update_preview_property (preview_scope_uri, props);
1487+ }
1488+
1489+ private void activate_preview(string preview_uri)
1490+ {
1491+ // call lens activate over dbus
1492+ Unity.Protocol.ActivationReplyRaw? reply_struct = null;
1493+ send_activate.begin(preview_uri,
1494+ Unity.Protocol.ActionType.PREVIEW_RESULT,
1495+ (obj, res) => {
1496+ try {
1497+ send_activate.end(res, out reply_struct);
1498+ preview_scope_uri = reply_struct.uri;
1499+ handle_activation_reply(reply_struct);
1500+ notebook.set_current_page(2); //activate 'Preview' tab
1501+ }
1502+ catch (GLib.IOError e) {
1503+ handle_error(e.message);
1504+ }
1505+ });
1506+ }
1507+
1508+ private void update_next_prev_buttons(TreeModel model, TreeIter cur_iter)
1509+ {
1510+ TreeIter iter;
1511+
1512+ iter = cur_iter;
1513+ prev_preview_button.sensitive = model.iter_previous(ref iter);
1514+ iter = cur_iter;
1515+ next_preview_button.sensitive = model.iter_next(ref iter);
1516+ }
1517+
1518+ /**
1519+ * Handler for 'Request preview' context menu item.
1520+ */
1521+ [CCode (instance_pos = -1)]
1522+ public void on_request_preview(Gtk.MenuItem item)
1523+ {
1524+ TreeModel model;
1525+ TreeIter iter;
1526+ if (results_view_selection.get_selected(out model, out iter)) {
1527+ Value val;
1528+ last_active_model = model;
1529+ last_active_iter = iter;
1530+ // get value from uri column
1531+ model.get_value(iter, 0, out val);
1532+ activate_preview(val.get_string());
1533+ update_next_prev_buttons(model, iter);
1534+ }
1535+ }
1536+
1537+ /**
1538+ * Handler for 'Request preview' context menu item.
1539+ */
1540+ [CCode (instance_pos = -1)]
1541+ public void on_prev_preview_clicked(Gtk.Button button)
1542+ requires (last_active_model != null)
1543+ {
1544+ TreeModel model = last_active_model;
1545+ TreeIter iter = last_active_iter;
1546+ if (last_active_model.iter_previous (ref iter)) {
1547+ Value val;
1548+ last_active_iter = iter;
1549+ // get value from uri column
1550+ model.get_value(iter, 0, out val);
1551+ activate_preview(val.get_string());
1552+ update_next_prev_buttons(model, iter);
1553+ }
1554+ }
1555+ /**
1556+ * Handler for 'Request preview' context menu item.
1557+ */
1558+ [CCode (instance_pos = -1)]
1559+ public void on_next_preview_clicked(Gtk.MenuItem item)
1560+ requires (last_active_model != null)
1561+ {
1562+ TreeModel model = last_active_model;
1563+ TreeIter iter = last_active_iter;
1564+ if (last_active_model.iter_next (ref iter)) {
1565+ Value val;
1566+ last_active_iter = iter;
1567+ // get value from uri column
1568+ model.get_value(iter, 0, out val);
1569+ activate_preview(val.get_string());
1570+ update_next_prev_buttons(model, iter);
1571+ }
1572+ }
1573+ /**
1574+ * Handler for 'Activate result' context menu item.
1575+ */
1576+ [CCode (instance_pos = -1)]
1577+ public void on_activate_result(Gtk.MenuItem item)
1578+ {
1579+ TreeModel model;
1580+ TreeIter iter;
1581+ if (results_view_selection.get_selected(out model, out iter)) {
1582+ Value val;
1583+ // get value from uri column
1584+ model.get_value(iter, 0, out val);
1585+
1586+ //
1587+ // call lens activate over dbus
1588+ //
1589+ Unity.Protocol.ActivationReplyRaw? reply_struct = null;
1590+ send_activate.begin(val.get_string(), Unity.Protocol.ActionType.ACTIVATE_RESULT, (obj, res) => {
1591+ try {
1592+ send_activate.end(res, out reply_struct);
1593+ handle_activation_reply(reply_struct);
1594+ }
1595+ catch (GLib.IOError e) {
1596+ handle_error(e.message);
1597+ }
1598+ });
1599+ }
1600+ }
1601+
1602+ /**
1603+ * Render preview and action buttons depending on preview type (if applicable); log reply.
1604+ */
1605+ private void handle_activation_reply(Unity.Protocol.ActivationReplyRaw reply_struct)
1606+ {
1607+ append_log_message("Activate reply: " + dump_activation_reply(reply_struct) + "\n");
1608+
1609+ if (reply_struct.handled == Unity.HandledType.SHOW_PREVIEW) {
1610+ if (reply_struct.hints.contains("preview")) {
1611+ handle_preview(reply_struct.hints["preview"]);
1612+ } else {
1613+ handle_error("Reply hints don't contain preview element");
1614+ }
1615+ } else {
1616+ remove_preview();
1617+ show_no_preview();
1618+ }
1619+ }
1620+
1621+ private void handle_preview(Variant preview_var)
1622+ {
1623+ Unity.Protocol.Preview? reconstructed = Unity.Protocol.Preview.parse(preview_var);
1624+ preview_raw_data.set_text(preview_var.print(true));
1625+ preview_renderer = PreviewRenderer.create(reconstructed, preview_scope_uri);
1626+ update_preview();
1627+ }
1628+
1629+ private void update_preview()
1630+ {
1631+ remove_preview();
1632+
1633+ if (preview_renderer != null) {
1634+ preview_renderer.preview_action_clicked.connect(on_preview_action_clicked);
1635+ if (preview_renderer is SeriesPreviewRenderer) {
1636+ ((SeriesPreviewRenderer)preview_renderer).change_selected_series_item_clicked.connect(on_change_selected_series_item_clicked);
1637+ }
1638+ else if (preview_renderer is MusicPreviewRenderer) {
1639+ var renderer = preview_renderer as MusicPreviewRenderer;
1640+ renderer.play_music_track_clicked.connect(on_play_music_track_clicked);
1641+ renderer.pause_music_track_clicked.connect(on_pause_music_track_clicked);
1642+ }
1643+ preview_viewport.add_with_properties(preview_renderer.get_widget());
1644+ preview_viewport.show_all();
1645+
1646+ preview_buttons_container.add_with_properties(preview_renderer.get_buttons());
1647+ preview_buttons_container.show_all();
1648+
1649+ preview_extra_buttons_container.add_with_properties(preview_renderer.get_extra_buttons());
1650+ preview_extra_buttons_container.show_all();
1651+ } else {
1652+ handle_error("Unknown preview type");
1653+ show_no_preview();
1654+ }
1655+ }
1656+
1657+ private void show_no_preview()
1658+ {
1659+ preview_renderer = null;
1660+
1661+ preview_raw_data.set_text("");
1662+ var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
1663+ var label = new Gtk.Label("No preview");
1664+ box.pack_start(label);
1665+ preview_viewport.add_with_properties(box);
1666+ box.show_all();
1667+ }
1668+
1669+ /**
1670+ * Destroys all preview objects in the 'Render' tab.
1671+ */
1672+ private void remove_preview()
1673+ {
1674+ preview_viewport.foreach((obj) => { obj.destroy(); });
1675+
1676+ // remove preview buttons
1677+ preview_buttons_container.foreach((btn) => { btn.destroy(); });
1678+ preview_extra_buttons_container.foreach((btn) => { btn.destroy(); });
1679+ }
1680+
1681+ private void on_preview_action_clicked(PreviewRenderer renderer, string uri)
1682+ {
1683+ Unity.Protocol.ActivationReplyRaw? reply_struct = null;
1684+ send_activate.begin(uri, Unity.Protocol.ActionType.PREVIEW_ACTION, (obj, res) => {
1685+ try {
1686+ send_activate.end(res, out reply_struct);
1687+ handle_activation_reply(reply_struct);
1688+ }
1689+ catch (GLib.IOError e) {
1690+ handle_error(e.message);
1691+ }
1692+ });
1693+ }
1694+
1695+ private void on_play_music_track_clicked(MusicPreviewRenderer renderer, string uri)
1696+ {
1697+ renderer.preview.begin_updates();
1698+ (renderer.preview as Unity.Protocol.MusicPreview).play_uri(uri);
1699+ handle_music_track_signals(renderer.preview.end_updates_as_hashtable());
1700+ }
1701+
1702+ private void on_pause_music_track_clicked(MusicPreviewRenderer renderer, string uri)
1703+ {
1704+ renderer.preview.begin_updates();
1705+ (renderer.preview as Unity.Protocol.MusicPreview).pause_uri(uri);
1706+ handle_music_track_signals(renderer.preview.end_updates_as_hashtable());
1707+ }
1708+
1709+ private void handle_music_track_signals(HashTable<string, Variant> props)
1710+ {
1711+ HashTable<string, Variant>? ht = null;
1712+ send_update.begin(preview_scope_uri, props, (obj, res) => {
1713+ try {
1714+ send_update.end(res, out ht);
1715+ if (ht != null) {
1716+ append_log_message("UpdatePreviewProperty reply: " + dump_ht_reply(ht) + "\n"); //TODO: do we expect any reply?
1717+ }
1718+ }
1719+ catch (GLib.IOError e) {
1720+ handle_error(e.message);
1721+ }
1722+ });
1723+ }
1724+
1725+ private void on_change_selected_series_item_clicked(SeriesPreviewRenderer renderer, string uri, int index)
1726+ {
1727+ var props = new HashTable<string, Variant>(str_hash, str_equal);
1728+ props["series-active-index"] = new Variant.int32(index);
1729+
1730+ HashTable<string, Variant>? ht = null;
1731+ send_update.begin(uri, props, (obj, res) => {
1732+ try {
1733+ send_update.end(res, out ht);
1734+ if (ht != null) {
1735+ append_log_message("UpdatePreviewProperty reply: " + dump_ht_reply(ht) + "\n");
1736+ if (ht.contains("preview")) {
1737+ if (preview_renderer is SeriesPreviewRenderer) {
1738+ (preview_renderer as SeriesPreviewRenderer).update_child_preview(Unity.Protocol.Preview.parse(ht["preview"]));
1739+ update_preview();
1740+ }
1741+ }
1742+ }
1743+ }
1744+ catch (GLib.IOError e) {
1745+ handle_error(e.message);
1746+ }
1747+ });
1748+ }
1749+
1750+ /**
1751+ * Handle 'Request preview' context menu action.
1752+ */
1753+ [CCode (instance_pos = -1)]
1754+ public void on_results_popup_request(Gtk.Widget widget)
1755+ {
1756+ }
1757+
1758+ /**
1759+ * Handles right mouse button click event in 'Results' tab.
1760+ */
1761+ [CCode (instance_pos = -1)]
1762+ public bool on_results_right_click(Gtk.Widget widget, Gdk.EventButton event)
1763+ {
1764+ if (event.type == Gdk.EventType.BUTTON_PRESS && event.button == 3 /* right mouse button */) {
1765+ results_popup_menu.popup(null, null, null, event.button, event.time);
1766+ }
1767+ return false;
1768+ }
1769+
1770+ private PreviewRenderer preview_renderer = null;
1771+ private Protocol.LensService lens_proxy = null;
1772+ private uint dbus_watcher_id = 0;
1773+ private string preview_scope_uri;
1774+ private Gtk.Notebook notebook = null;
1775+ private Gtk.Viewport preview_viewport = null;
1776+ private Gtk.TreeSelection results_view_selection = null;
1777+ private Gtk.Alignment preview_buttons_container = null;
1778+ private Gtk.Alignment preview_extra_buttons_container = null;
1779+ private Gtk.TextBuffer preview_raw_data = null;
1780+ private Gtk.Menu results_popup_menu = null;
1781+ private Gtk.TreeView results_view = null;
1782 private Gtk.RadioButton search_type_global_rbutton = null;
1783 private Gtk.Dialog lens_connect_dlg = null;
1784 private Gtk.Spinner spinner = null;
1785 private Gtk.Spinner lens_discovery_spinner = null;
1786 private Gtk.Button search_button = null;
1787+ private Gtk.Button results_button = null;
1788+ private Gtk.Button prev_preview_button = null;
1789+ private Gtk.Button next_preview_button = null;
1790 private Gtk.Statusbar statusbar = null;
1791 private Gtk.TextBuffer log_buffer = null;
1792 private uint statusbar_info_ctx;
1793@@ -344,6 +776,8 @@
1794 private ulong filters_model_sync_sig_id;
1795 private Dee.SharedModel? dee_results_model = null;
1796 private Dee.SharedModel? dee_filters_model = null;
1797+ private TreeModel? last_active_model = null;
1798+ private TreeIter? last_active_iter = null;
1799 }
1800
1801 }
1802
1803=== modified file 'tools/unity-tool.ui'
1804--- tools/unity-tool.ui 2012-06-05 10:02:21 +0000
1805+++ tools/unity-tool.ui 2012-08-14 14:17:21 +0000
1806@@ -22,6 +22,7 @@
1807 </columns>
1808 </object>
1809 <object class="GtkTextBuffer" id="log_buffer"/>
1810+ <object class="GtkTextBuffer" id="preview_raw_data"/>
1811 <object class="GtkListStore" id="results_model">
1812 <columns>
1813 <!-- column-name col1 -->
1814@@ -40,6 +41,30 @@
1815 <column type="gchararray"/>
1816 </columns>
1817 </object>
1818+ <object class="GtkMenu" id="results_popup_menu">
1819+ <property name="visible">True</property>
1820+ <property name="can_focus">False</property>
1821+ <child>
1822+ <object class="GtkMenuItem" id="activate_result">
1823+ <property name="use_action_appearance">False</property>
1824+ <property name="visible">True</property>
1825+ <property name="can_focus">False</property>
1826+ <property name="label" translatable="yes">Activate result</property>
1827+ <property name="use_underline">True</property>
1828+ <signal name="activate" handler="unity_tester_unity_tool_ui_on_activate_result" swapped="no"/>
1829+ </object>
1830+ </child>
1831+ <child>
1832+ <object class="GtkMenuItem" id="request_preview">
1833+ <property name="use_action_appearance">False</property>
1834+ <property name="visible">True</property>
1835+ <property name="can_focus">False</property>
1836+ <property name="label" translatable="yes">Request preview</property>
1837+ <property name="use_underline">True</property>
1838+ <signal name="activate" handler="unity_tester_unity_tool_ui_on_request_preview" swapped="no"/>
1839+ </object>
1840+ </child>
1841+ </object>
1842 <object class="GtkWindow" id="window">
1843 <property name="can_focus">False</property>
1844 <property name="title" translatable="yes">Libunity Tool</property>
1845@@ -99,6 +124,31 @@
1846 </child>
1847 </object>
1848 </child>
1849+ <child>
1850+ <object class="GtkMenuItem" id="menuitem2">
1851+ <property name="use_action_appearance">False</property>
1852+ <property name="visible">True</property>
1853+ <property name="can_focus">False</property>
1854+ <property name="label" translatable="yes">_Edit</property>
1855+ <property name="use_underline">True</property>
1856+ <child type="submenu">
1857+ <object class="GtkMenu" id="menu2">
1858+ <property name="visible">True</property>
1859+ <property name="can_focus">False</property>
1860+ <child>
1861+ <object class="GtkMenuItem" id="menuitem3">
1862+ <property name="use_action_appearance">False</property>
1863+ <property name="visible">True</property>
1864+ <property name="can_focus">False</property>
1865+ <property name="label" translatable="yes">Clear Log</property>
1866+ <property name="use_underline">True</property>
1867+ <signal name="activate" handler="unity_tester_unity_tool_ui_on_clear_log_clicked" swapped="no"/>
1868+ </object>
1869+ </child>
1870+ </object>
1871+ </child>
1872+ </object>
1873+ </child>
1874 </object>
1875 <packing>
1876 <property name="expand">False</property>
1877@@ -110,6 +160,7 @@
1878 <object class="GtkBox" id="box2">
1879 <property name="visible">True</property>
1880 <property name="can_focus">False</property>
1881+ <property name="border_width">6</property>
1882 <child>
1883 <object class="GtkBox" id="box3">
1884 <property name="visible">True</property>
1885@@ -281,16 +332,18 @@
1886 </packing>
1887 </child>
1888 <child>
1889- <object class="GtkNotebook" id="notebook1">
1890+ <object class="GtkNotebook" id="notebook">
1891 <property name="visible">True</property>
1892 <property name="can_focus">True</property>
1893+ <property name="margin_left">6</property>
1894+ <property name="margin_right">6</property>
1895 <child>
1896 <object class="GtkScrolledWindow" id="scrolledwindow1">
1897 <property name="visible">True</property>
1898- <property name="can_focus">True</property>
1899+ <property name="can_focus">False</property>
1900 <property name="shadow_type">in</property>
1901 <child>
1902- <object class="GtkTreeView" id="treeview3">
1903+ <object class="GtkTreeView" id="results_view">
1904 <property name="visible">True</property>
1905 <property name="can_focus">True</property>
1906 <property name="model">results_model</property>
1907@@ -298,8 +351,10 @@
1908 <property name="enable_search">False</property>
1909 <property name="search_column">0</property>
1910 <property name="show_expanders">False</property>
1911+ <signal name="button-press-event" handler="unity_tester_unity_tool_ui_on_results_right_click" swapped="no"/>
1912+ <signal name="popup-menu" handler="unity_tester_unity_tool_ui_on_results_popup_request" swapped="no"/>
1913 <child internal-child="selection">
1914- <object class="GtkTreeSelection" id="treeview-selection1"/>
1915+ <object class="GtkTreeSelection" id="results_view_selection"/>
1916 </child>
1917 <child>
1918 <object class="GtkTreeViewColumn" id="treeviewcolumn1">
1919@@ -539,6 +594,224 @@
1920 </packing>
1921 </child>
1922 <child>
1923+ <object class="GtkBox" id="box6">
1924+ <property name="visible">True</property>
1925+ <property name="can_focus">False</property>
1926+ <child>
1927+ <object class="GtkButton" id="prev_preview_btn">
1928+ <property name="label" translatable="yes">&lt;&lt;</property>
1929+ <property name="use_action_appearance">False</property>
1930+ <property name="visible">True</property>
1931+ <property name="sensitive">False</property>
1932+ <property name="can_focus">True</property>
1933+ <property name="receives_default">True</property>
1934+ <property name="use_action_appearance">False</property>
1935+ <signal name="clicked" handler="unity_tester_unity_tool_ui_on_prev_preview_clicked" swapped="no"/>
1936+ </object>
1937+ <packing>
1938+ <property name="expand">False</property>
1939+ <property name="fill">True</property>
1940+ <property name="position">0</property>
1941+ </packing>
1942+ </child>
1943+ <child>
1944+ <object class="GtkBox" id="box7">
1945+ <property name="visible">True</property>
1946+ <property name="can_focus">False</property>
1947+ <property name="vexpand">True</property>
1948+ <property name="orientation">vertical</property>
1949+ <property name="spacing">10</property>
1950+ <child>
1951+ <object class="GtkFrame" id="frame1">
1952+ <property name="visible">True</property>
1953+ <property name="can_focus">False</property>
1954+ <property name="label_xalign">0</property>
1955+ <property name="shadow_type">none</property>
1956+ <child>
1957+ <object class="GtkAlignment" id="preview_buttons_container">
1958+ <property name="visible">True</property>
1959+ <property name="can_focus">False</property>
1960+ <property name="left_padding">12</property>
1961+ <child>
1962+ <placeholder/>
1963+ </child>
1964+ </object>
1965+ </child>
1966+ <child type="label">
1967+ <object class="GtkLabel" id="label6">
1968+ <property name="visible">True</property>
1969+ <property name="can_focus">False</property>
1970+ <property name="label" translatable="yes">&lt;b&gt;Preview actions&lt;/b&gt;</property>
1971+ <property name="use_markup">True</property>
1972+ </object>
1973+ </child>
1974+ </object>
1975+ <packing>
1976+ <property name="expand">False</property>
1977+ <property name="fill">True</property>
1978+ <property name="position">0</property>
1979+ </packing>
1980+ </child>
1981+ <child>
1982+ <object class="GtkSeparator" id="separator1">
1983+ <property name="visible">True</property>
1984+ <property name="can_focus">False</property>
1985+ </object>
1986+ <packing>
1987+ <property name="expand">False</property>
1988+ <property name="fill">True</property>
1989+ <property name="position">1</property>
1990+ </packing>
1991+ </child>
1992+ <child>
1993+ <object class="GtkFrame" id="frame2">
1994+ <property name="visible">True</property>
1995+ <property name="can_focus">False</property>
1996+ <property name="label_xalign">0</property>
1997+ <property name="shadow_type">none</property>
1998+ <child>
1999+ <object class="GtkAlignment" id="preview_extra_buttons_container">
2000+ <property name="visible">True</property>
2001+ <property name="can_focus">False</property>
2002+ <property name="left_padding">12</property>
2003+ <child>
2004+ <placeholder/>
2005+ </child>
2006+ </object>
2007+ </child>
2008+ <child type="label">
2009+ <object class="GtkLabel" id="label9">
2010+ <property name="visible">True</property>
2011+ <property name="can_focus">False</property>
2012+ <property name="label" translatable="yes">&lt;b&gt;Preview signals&lt;/b&gt;</property>
2013+ <property name="use_markup">True</property>
2014+ </object>
2015+ </child>
2016+ </object>
2017+ <packing>
2018+ <property name="expand">False</property>
2019+ <property name="fill">True</property>
2020+ <property name="position">2</property>
2021+ </packing>
2022+ </child>
2023+ </object>
2024+ <packing>
2025+ <property name="expand">False</property>
2026+ <property name="fill">True</property>
2027+ <property name="position">1</property>
2028+ </packing>
2029+ </child>
2030+ <child>
2031+ <object class="GtkNotebook" id="notebook2">
2032+ <property name="visible">True</property>
2033+ <property name="can_focus">True</property>
2034+ <property name="hexpand">True</property>
2035+ <property name="vexpand">True</property>
2036+ <property name="tab_pos">bottom</property>
2037+ <child>
2038+ <object class="GtkScrolledWindow" id="scrolledwindow5">
2039+ <property name="visible">True</property>
2040+ <property name="can_focus">True</property>
2041+ <property name="shadow_type">in</property>
2042+ <child>
2043+ <object class="GtkViewport" id="preview_viewport">
2044+ <property name="visible">True</property>
2045+ <property name="can_focus">False</property>
2046+ <child>
2047+ <placeholder/>
2048+ </child>
2049+ </object>
2050+ </child>
2051+ </object>
2052+ </child>
2053+ <child type="tab">
2054+ <object class="GtkLabel" id="label7">
2055+ <property name="visible">True</property>
2056+ <property name="can_focus">False</property>
2057+ <property name="label" translatable="yes">Rendered</property>
2058+ </object>
2059+ <packing>
2060+ <property name="tab_fill">False</property>
2061+ </packing>
2062+ </child>
2063+ <child>
2064+ <object class="GtkScrolledWindow" id="scrolledwindow4">
2065+ <property name="visible">True</property>
2066+ <property name="can_focus">True</property>
2067+ <property name="hscrollbar_policy">never</property>
2068+ <property name="shadow_type">in</property>
2069+ <child>
2070+ <object class="GtkTextView" id="preview_raw_view">
2071+ <property name="visible">True</property>
2072+ <property name="can_focus">True</property>
2073+ <property name="editable">False</property>
2074+ <property name="wrap_mode">char</property>
2075+ <property name="buffer">preview_raw_data</property>
2076+ </object>
2077+ </child>
2078+ </object>
2079+ <packing>
2080+ <property name="position">1</property>
2081+ </packing>
2082+ </child>
2083+ <child type="tab">
2084+ <object class="GtkLabel" id="label8">
2085+ <property name="visible">True</property>
2086+ <property name="can_focus">False</property>
2087+ <property name="label" translatable="yes">Raw data</property>
2088+ </object>
2089+ <packing>
2090+ <property name="position">1</property>
2091+ <property name="tab_fill">False</property>
2092+ </packing>
2093+ </child>
2094+ <child>
2095+ <placeholder/>
2096+ </child>
2097+ <child type="tab">
2098+ <placeholder/>
2099+ </child>
2100+ </object>
2101+ <packing>
2102+ <property name="expand">False</property>
2103+ <property name="fill">True</property>
2104+ <property name="position">2</property>
2105+ </packing>
2106+ </child>
2107+ <child>
2108+ <object class="GtkButton" id="next_preview_btn">
2109+ <property name="label" translatable="yes">&gt;&gt;</property>
2110+ <property name="use_action_appearance">False</property>
2111+ <property name="visible">True</property>
2112+ <property name="sensitive">False</property>
2113+ <property name="can_focus">True</property>
2114+ <property name="receives_default">True</property>
2115+ <property name="use_action_appearance">False</property>
2116+ <signal name="clicked" handler="unity_tester_unity_tool_ui_on_next_preview_clicked" swapped="no"/>
2117+ </object>
2118+ <packing>
2119+ <property name="expand">False</property>
2120+ <property name="fill">True</property>
2121+ <property name="position">3</property>
2122+ </packing>
2123+ </child>
2124+ </object>
2125+ <packing>
2126+ <property name="position">2</property>
2127+ </packing>
2128+ </child>
2129+ <child type="tab">
2130+ <object class="GtkLabel" id="label5">
2131+ <property name="visible">True</property>
2132+ <property name="can_focus">False</property>
2133+ <property name="label" translatable="yes">Preview</property>
2134+ </object>
2135+ <packing>
2136+ <property name="position">2</property>
2137+ <property name="tab_fill">False</property>
2138+ </packing>
2139+ </child>
2140+ <child>
2141 <object class="GtkScrolledWindow" id="scrolledwindow3">
2142 <property name="visible">True</property>
2143 <property name="can_focus">True</property>
2144@@ -554,7 +827,7 @@
2145 </child>
2146 </object>
2147 <packing>
2148- <property name="position">2</property>
2149+ <property name="position">3</property>
2150 </packing>
2151 </child>
2152 <child type="tab">
2153@@ -564,7 +837,7 @@
2154 <property name="label" translatable="yes">Log</property>
2155 </object>
2156 <packing>
2157- <property name="position">2</property>
2158+ <property name="position">3</property>
2159 <property name="tab_fill">False</property>
2160 </packing>
2161 </child>

Subscribers

People subscribed via source and target branches