Merge lp:~aauzi/midori/fix-1177553-3 into lp:midori

Proposed by André Auzi
Status: Merged
Approved by: Paweł Forysiuk
Approved revision: 6247
Merged at revision: 6290
Proposed branch: lp:~aauzi/midori/fix-1177553-3
Merge into: lp:midori
Diff against target: 876 lines (+771/-37)
3 files modified
katze/katze-cellrenderercomboboxtext.c (+518/-0)
katze/katze-cellrenderercomboboxtext.h (+58/-0)
midori/midori-browser.c (+195/-37)
To merge this branch: bzr merge lp:~aauzi/midori/fix-1177553-3
Reviewer Review Type Date Requested Status
Paweł Forysiuk Approve
Cris Dywan Approve
Review via email: mp+177272@code.launchpad.net

This proposal supersedes a proposal from 2013-07-18.

Commit message

Show folder tree when editing bookmarks

Description of the change

This last step for Bookmark folder tree reflection addresses the bookmarks edit dialog combo box.

The combo box is reworked to display a tree view of the bookmark folders.

Folder icons are added and give, in the combo box, a display of folders that aims to have consistent aspect with bookmark bar, bookmark menu and bookmark panel.

The combo box population algorithm is reworked to handle the case of sub-folders referring to a previously populated parent folder.

After this process, remaining items of the retrieved folders list are orphaned folder entries.

Opened folder are now displayed with the message 'Select [folder name]'.

To post a comment you must log in.
Revision history for this message
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal

I could live with the "duplicate" folder if need be. If it's feasible I'd consider renaming the "second" menu item to "Choose this folder", a style I've seen in a few places.
For the sizing, sokoke.c has a function, alternatively something screen width based. Hard to say which works best in this case honestly.

review: Needs Fixing
Revision history for this message
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal

Could you provide some reasoning as to what the 2 new classes offer over standard cell renderers?

review: Needs Information
Revision history for this message
André Auzi (aauzi) wrote : Posted in a previous version of this proposal

> Could you provide some reasoning as to what the 2 new classes offer over
> standard cell renderers?

Sure.

The combobox uses the same tree store row for the folder and the top menuitem it presents, before the separator, in the subfolder list view.

That's why we get the same name and icons in the opened folder and the top menuitem of the subfolders list.

I had to figure out a way to distinguish between the two situations:
a/ when the rendered row corresponds to the folder
b/ when the rendered row corresponds to the top of subfolders list

Since the cell renderers are executed on the same row this could not be done by information stored in the row itself.

The cell renderers I've (rapidly) written examine the containing menu to determine if they're called on a the first widget of a menu, just before a separator.

This could had been avoided for pixbufs if the combobox managed the 'expanded' property of the cell renderer, as a treeview does, but, unless I've made a big mistake, it does not seem to do it. (I couldn't make it work :/)

The two new classes are actually tied to the combobox implementation and offer the capability to render different text and pixbufs for the folder when it's listed in a menu or presented at the top of it's containees.

Note: since the detection is the same for the two classes I believe they can be implemented with about half of the code I used here.

Honestly, if combobox on tree store is not used elsewhere, I would be reluctant to keep them.

I'd only provide them if negative user feedback was given on the previous, straight forward, implementation.

Revision history for this message
Cris Dywan (kalikiana) wrote :

I'm fine with the approach, purely technically speaking.

review: Approve
Revision history for this message
Paweł Forysiuk (tuxator) wrote :

Seems to work fine. Approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'katze/katze-cellrenderercomboboxtext.c'
2--- katze/katze-cellrenderercomboboxtext.c 1970-01-01 00:00:00 +0000
3+++ katze/katze-cellrenderercomboboxtext.c 2013-07-27 21:58:26 +0000
4@@ -0,0 +1,518 @@
5+/*
6+ Copyright (C) 2008-2013 Christian Dywan <christian@twotoasts.de>
7+
8+ This library is free software; you can redistribute it and/or
9+ modify it under the terms of the GNU Lesser General Public
10+ License as published by the Free Software Foundation; either
11+ version 2.1 of the License, or (at your option) any later version.
12+
13+ See the file COPYING for the full license text.
14+*/
15+
16+#include "katze-cellrenderercomboboxtext.h"
17+
18+#include "marshal.h"
19+
20+#include <string.h>
21+#include <gdk/gdk.h>
22+
23+#define P_(String) (String)
24+#define I_(String) (String)
25+#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
26+#define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
27+#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
28+
29+
30+static void
31+katze_cell_renderer_combobox_text_finalize (GObject* object);
32+
33+static void
34+katze_cell_renderer_combobox_text_get_property (GObject* object,
35+ guint param_id,
36+ GValue* value,
37+ GParamSpec* pspec);
38+static void
39+katze_cell_renderer_combobox_text_set_property (GObject* object,
40+ guint param_id,
41+ const GValue* value,
42+ GParamSpec* pspec);
43+static void
44+katze_cell_renderer_combobox_text_get_size (GtkCellRenderer* cell,
45+ GtkWidget* widget,
46+ GdkRectangle* cell_area,
47+ gint* x_offset,
48+ gint* y_offset,
49+ gint* width,
50+ gint* height);
51+#if GTK_CHECK_VERSION(3,0,0)
52+static void
53+katze_cell_renderer_combobox_text_render (GtkCellRenderer *cell,
54+ cairo_t* cr,
55+ GtkWidget *widget,
56+ GdkRectangle *background_area,
57+ GdkRectangle *cell_area,
58+ GtkCellRendererState flags);
59+#else
60+static void
61+katze_cell_renderer_combobox_text_render (GtkCellRenderer *cell,
62+ GdkDrawable *window,
63+ GtkWidget *widget,
64+ GdkRectangle *background_area,
65+ GdkRectangle *cell_area,
66+ GdkRectangle *expose_area,
67+ GtkCellRendererState flags);
68+#endif
69+
70+static void
71+(*_cell_renderer_get_size) (GtkCellRenderer* cell,
72+ GtkWidget* widget,
73+ GdkRectangle* cell_area,
74+ gint* x_offset,
75+ gint* y_offset,
76+ gint* width,
77+ gint* height);
78+
79+#if GTK_CHECK_VERSION(3,0,0)
80+static void
81+(*_cell_renderer_render) (GtkCellRenderer *cell,
82+ cairo_t* cr,
83+ GtkWidget *widget,
84+ GdkRectangle *background_area,
85+ GdkRectangle *cell_area,
86+ GtkCellRendererState flags) = NULL;
87+#else
88+static void
89+(*_cell_renderer_render) (GtkCellRenderer *cell,
90+ GdkDrawable *window,
91+ GtkWidget *widget,
92+ GdkRectangle *background_area,
93+ GdkRectangle *cell_area,
94+ GdkRectangle *expose_area,
95+ GtkCellRendererState flags) = NULL;
96+#endif
97+
98+enum {
99+ PROP_0,
100+
101+ PROP_FOLDED_TEXT,
102+ PROP_FOLDED_MARKUP,
103+ PROP_FOLDED_ATTRIBUTES,
104+ PROP_UNFOLDED_TEXT,
105+ PROP_UNFOLDED_MARKUP,
106+ PROP_UNFOLDED_ATTRIBUTES,
107+};
108+
109+#define KATZE_CELL_RENDERER_COMBOBOX_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KATZE_TYPE_CELL_RENDERER_COMBOBOX_TEXT, KatzeCellRendererComboBoxTextPrivate))
110+
111+typedef struct _KatzeCellRendererComboBoxTextPrivate KatzeCellRendererComboBoxTextPrivate;
112+struct _KatzeCellRendererComboBoxTextPrivate
113+{
114+ struct _Properties {
115+ PangoAttrList* extra_attrs;
116+
117+ gchar* text;
118+
119+ guint markup_set : 1;
120+ } props[2];
121+};
122+
123+G_DEFINE_TYPE (KatzeCellRendererComboBoxText, katze_cell_renderer_combobox_text, GTK_TYPE_CELL_RENDERER_TEXT)
124+
125+static void
126+katze_cell_renderer_combobox_text_init (KatzeCellRendererComboBoxText *celltext)
127+{
128+ guint prop_index;
129+ KatzeCellRendererComboBoxTextPrivate *priv;
130+
131+ priv = KATZE_CELL_RENDERER_COMBOBOX_TEXT_GET_PRIVATE (celltext);
132+
133+ for (prop_index = 0 ; prop_index < 2; prop_index++)
134+ {
135+ priv->props[prop_index].text = NULL;
136+ priv->props[prop_index].extra_attrs = NULL;
137+ priv->props[prop_index].markup_set = FALSE;
138+ }
139+}
140+
141+static void
142+katze_cell_renderer_combobox_text_class_init (KatzeCellRendererComboBoxTextClass *class)
143+{
144+ GObjectClass *object_class = G_OBJECT_CLASS (class);
145+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
146+
147+ object_class->finalize = katze_cell_renderer_combobox_text_finalize;
148+
149+ _cell_renderer_get_size = cell_class->get_size;
150+ _cell_renderer_render = cell_class->render;
151+
152+ object_class->get_property = katze_cell_renderer_combobox_text_get_property;
153+ object_class->set_property = katze_cell_renderer_combobox_text_set_property;
154+
155+ cell_class->get_size = katze_cell_renderer_combobox_text_get_size;
156+ cell_class->render = katze_cell_renderer_combobox_text_render;
157+
158+ g_object_class_install_property (object_class,
159+ PROP_FOLDED_TEXT,
160+ g_param_spec_string ("folded-text",
161+ P_("Folded text"),
162+ P_("Text to render if combobox_text is closed. The string [text] is replaced by default text"),
163+ NULL,
164+ GTK_PARAM_READWRITE));
165+
166+ g_object_class_install_property (object_class,
167+ PROP_FOLDED_MARKUP,
168+ g_param_spec_string ("folded-markup",
169+ P_("Folded markup"),
170+ P_("Marked up text to render if combobox_text is closed. The string [text] is replaced by default text"),
171+ NULL,
172+ GTK_PARAM_WRITABLE));
173+
174+ g_object_class_install_property (object_class,
175+ PROP_FOLDED_ATTRIBUTES,
176+ g_param_spec_boxed ("folded-attributes",
177+ P_("Folded attributes"),
178+ P_("A list of style attributes to apply to the text of the renderer if combobox_text is closed"),
179+ PANGO_TYPE_ATTR_LIST,
180+ GTK_PARAM_READWRITE));
181+
182+ g_object_class_install_property (object_class,
183+ PROP_UNFOLDED_TEXT,
184+ g_param_spec_string ("unfolded-text",
185+ P_("Unfolded text"),
186+ P_("Text to render if combobox_text is opened"),
187+ NULL,
188+ GTK_PARAM_READWRITE));
189+
190+ g_object_class_install_property (object_class,
191+ PROP_UNFOLDED_MARKUP,
192+ g_param_spec_string ("unfolded-markup",
193+ P_("Unfolded markup"),
194+ P_("Marked up text to render if combobox_text is opened"),
195+ NULL,
196+ GTK_PARAM_WRITABLE));
197+
198+ g_object_class_install_property (object_class,
199+ PROP_UNFOLDED_ATTRIBUTES,
200+ g_param_spec_boxed ("unfolded-attributes",
201+ P_("Unfolded attributes"),
202+ P_("A list of style attributes to apply to the text of the renderer if combobox_text is opened"),
203+ PANGO_TYPE_ATTR_LIST,
204+ GTK_PARAM_READWRITE));
205+
206+
207+ g_type_class_add_private (object_class, sizeof (KatzeCellRendererComboBoxTextPrivate));
208+}
209+
210+static void
211+katze_cell_renderer_combobox_text_finalize (GObject *object)
212+{
213+ guint prop_index;
214+ KatzeCellRendererComboBoxTextPrivate *priv;
215+
216+ priv = KATZE_CELL_RENDERER_COMBOBOX_TEXT_GET_PRIVATE (object);
217+
218+ for (prop_index = 0 ; prop_index < 2; prop_index++)
219+ {
220+ g_free (priv->props[prop_index].text);
221+ if (priv->props[prop_index].extra_attrs)
222+ pango_attr_list_unref (priv->props[prop_index].extra_attrs);
223+ }
224+
225+ G_OBJECT_CLASS (katze_cell_renderer_combobox_text_parent_class)->finalize (object);
226+}
227+
228+static void
229+katze_cell_renderer_combobox_text_get_property (GObject* object,
230+ guint param_id,
231+ GValue* value,
232+ GParamSpec* pspec)
233+{
234+ KatzeCellRendererComboBoxTextPrivate *priv;
235+
236+ priv = KATZE_CELL_RENDERER_COMBOBOX_TEXT_GET_PRIVATE (object);
237+
238+ switch (param_id)
239+ {
240+ case PROP_FOLDED_TEXT:
241+ g_value_set_string (value, priv->props[0].text);
242+ break;
243+
244+ case PROP_FOLDED_ATTRIBUTES:
245+ g_value_set_boxed (value, priv->props[0].extra_attrs);
246+ break;
247+
248+ case PROP_UNFOLDED_TEXT:
249+ g_value_set_string (value, priv->props[1].text);
250+ break;
251+
252+ case PROP_UNFOLDED_ATTRIBUTES:
253+ g_value_set_boxed (value, priv->props[1].extra_attrs);
254+ break;
255+
256+ case PROP_FOLDED_MARKUP:
257+ case PROP_UNFOLDED_MARKUP:
258+ default:
259+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
260+ break;
261+ }
262+}
263+
264+
265+static void
266+katze_cell_renderer_combobox_text_set_property (GObject* object,
267+ guint param_id,
268+ const GValue* value,
269+ GParamSpec* pspec)
270+{
271+ guint prop_index = 0;
272+ KatzeCellRendererComboBoxTextPrivate *priv;
273+
274+ priv = KATZE_CELL_RENDERER_COMBOBOX_TEXT_GET_PRIVATE (object);
275+
276+ switch (param_id)
277+ {
278+ case PROP_FOLDED_TEXT:
279+ prop_text:
280+ g_free (priv->props[prop_index].text);
281+
282+ if (priv->props[prop_index].markup_set)
283+ {
284+ if (priv->props[prop_index].extra_attrs)
285+ {
286+ pango_attr_list_unref (priv->props[prop_index].extra_attrs);
287+ priv->props[prop_index].extra_attrs = NULL;
288+ }
289+ priv->props[prop_index].markup_set = FALSE;
290+ }
291+
292+ priv->props[prop_index].text = g_value_dup_string (value);
293+ break;
294+
295+ case PROP_FOLDED_ATTRIBUTES:
296+ prop_attributes:
297+ if (priv->props[prop_index].extra_attrs)
298+ pango_attr_list_unref (priv->props[prop_index].extra_attrs);
299+
300+ priv->props[prop_index].extra_attrs = g_value_get_boxed (value);
301+ if (priv->props[prop_index].extra_attrs)
302+ pango_attr_list_ref (priv->props[prop_index].extra_attrs);
303+ break;
304+
305+ case PROP_FOLDED_MARKUP:
306+ prop_markup:
307+ {
308+ const gchar *str;
309+ gchar *text = NULL;
310+ GError *error = NULL;
311+ PangoAttrList *attrs = NULL;
312+
313+ str = g_value_get_string (value);
314+ if (str && !pango_parse_markup (str,
315+ -1,
316+ 0,
317+ &attrs,
318+ &text,
319+ NULL,
320+ &error))
321+ {
322+ g_warning ("Failed to set text from markup due to error parsing markup: %s",
323+ error->message);
324+ g_error_free (error);
325+ return;
326+ }
327+
328+ g_free (priv->props[prop_index].text);
329+
330+ if (priv->props[prop_index].extra_attrs)
331+ pango_attr_list_unref (priv->props[prop_index].extra_attrs);
332+
333+ priv->props[prop_index].text = text;
334+ priv->props[prop_index].extra_attrs = attrs;
335+ priv->props[prop_index].markup_set = TRUE;
336+ }
337+ break;
338+
339+ case PROP_UNFOLDED_TEXT:
340+ prop_index = 1;
341+ goto prop_text;
342+
343+ case PROP_UNFOLDED_ATTRIBUTES:
344+ prop_index = 1;
345+ goto prop_attributes;
346+
347+ case PROP_UNFOLDED_MARKUP:
348+ prop_index = 1;
349+ goto prop_markup;
350+
351+ default:
352+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
353+ break;
354+ }
355+}
356+
357+/**
358+ * katze_cell_renderer_combobox_text_new:
359+ *
360+ * Creates a new #KatzeCellRendererComboBoxText. Adjust how text is drawn using
361+ * object properties. Object properties can be
362+ * set globally (with g_object_set()). Also, with #GtkTreeViewColumn,
363+ * you can bind a property to a value in a #GtkTreeModel. For example,
364+ * you can bind the "text" property on the cell renderer to a string
365+ * value in the model, thus rendering a different string in each row
366+ * of the #GtkTreeView
367+ *
368+ * Return value: the new cell renderer
369+ **/
370+GtkCellRenderer *
371+katze_cell_renderer_combobox_text_new (void)
372+{
373+ return g_object_new (KATZE_TYPE_CELL_RENDERER_COMBOBOX_TEXT, NULL);
374+}
375+
376+static void
377+set_text(KatzeCellRendererComboBoxText* cell,
378+ GtkWidget* widget,
379+ const gchar* repl)
380+{
381+ const gchar *text = NULL;
382+ PangoAttrList* extra_attrs = NULL;
383+ GtkWidget* pwidget = gtk_widget_get_parent (widget);
384+ gboolean unfolded = FALSE;
385+ KatzeCellRendererComboBoxTextPrivate *priv;
386+
387+ priv = KATZE_CELL_RENDERER_COMBOBOX_TEXT_GET_PRIVATE (cell);
388+
389+ if (GTK_IS_MENU_ITEM (pwidget))
390+ {
391+ GtkWidget* menu = gtk_widget_get_parent (pwidget);
392+ GList* items;
393+
394+ if (menu
395+ && (GTK_IS_MENU (menu))
396+ && (items = gtk_container_get_children (GTK_CONTAINER (menu)))
397+ && (GTK_WIDGET (items->data) == pwidget)
398+ && (g_list_length (items) > 1)
399+ && (GTK_IS_SEPARATOR_MENU_ITEM (g_list_next (items)->data)))
400+ {
401+ unfolded = TRUE;
402+ }
403+ }
404+
405+ if (unfolded)
406+ {
407+ text = priv->props[1].text;
408+ extra_attrs = priv->props[1].extra_attrs;
409+ }
410+ else
411+ {
412+ text = priv->props[0].text;
413+ extra_attrs = priv->props[0].extra_attrs;
414+ }
415+
416+ if (!text)
417+ {
418+ text = g_strdup (repl ? repl : "");
419+ }
420+ else
421+ {
422+ GString* string = g_string_new ("");
423+ const gchar* src = text;
424+ const guint skip = sizeof ("[text]") - 1;
425+ guint len;
426+
427+ while (0 != (len = strlen(src)))
428+ {
429+ const gchar* found = strstr (src, "[text]");
430+
431+ if (!found)
432+ {
433+ g_string_append (string, src);
434+ src += len;
435+ }
436+ else
437+ {
438+ g_string_append_len (string, src, found-src);
439+ if (repl)
440+ g_string_append (string, repl);
441+ src = found + skip;
442+ }
443+ }
444+
445+ text = g_string_free (string, FALSE);
446+ }
447+
448+ g_object_set (G_OBJECT (cell),
449+ "text", text,
450+ "attributes", extra_attrs,
451+ NULL);
452+
453+ g_free ((gpointer)text);
454+}
455+
456+static void
457+katze_cell_renderer_combobox_text_get_size (GtkCellRenderer *cell,
458+ GtkWidget *widget,
459+ GdkRectangle *cell_area,
460+ gint *x_offset,
461+ gint *y_offset,
462+ gint *width,
463+ gint *height)
464+{
465+ const gchar *text = NULL;
466+
467+ g_object_get (G_OBJECT (cell), "text", &text, NULL);
468+
469+ set_text (KATZE_CELL_RENDERER_COMBOBOX_TEXT (cell), widget, text);
470+
471+ _cell_renderer_get_size (cell,
472+ widget, cell_area,
473+ x_offset, y_offset, width, height);
474+
475+ g_object_set (G_OBJECT (cell), "text", text, NULL);
476+ g_free ((gpointer)text);
477+}
478+
479+static void
480+#if GTK_CHECK_VERSION(3,0,0)
481+katze_cell_renderer_combobox_text_render (GtkCellRenderer *cell,
482+ cairo_t* cr,
483+ GtkWidget *widget,
484+ GdkRectangle *background_area,
485+ GdkRectangle *cell_area,
486+ GtkCellRendererState flags)
487+#else
488+katze_cell_renderer_combobox_text_render (GtkCellRenderer *cell,
489+ GdkDrawable *window,
490+ GtkWidget *widget,
491+ GdkRectangle *background_area,
492+ GdkRectangle *cell_area,
493+ GdkRectangle *expose_area,
494+ GtkCellRendererState flags)
495+#endif
496+{
497+ const gchar *text = NULL;
498+
499+ g_object_get (G_OBJECT (cell), "text", &text, NULL);
500+
501+ set_text (KATZE_CELL_RENDERER_COMBOBOX_TEXT (cell), widget, text);
502+
503+#if GTK_CHECK_VERSION(3,0,0)
504+ _cell_renderer_render (cell,
505+ cr,
506+ widget,
507+ background_area,
508+ cell_area,
509+ flags);
510+#else
511+ _cell_renderer_render (cell,
512+ window,
513+ widget,
514+ background_area,
515+ cell_area,
516+ expose_area,
517+ flags);
518+#endif
519+
520+ g_object_set (G_OBJECT (cell), "text", text, NULL);
521+ g_free ((gpointer)text);
522+}
523
524=== added file 'katze/katze-cellrenderercomboboxtext.h'
525--- katze/katze-cellrenderercomboboxtext.h 1970-01-01 00:00:00 +0000
526+++ katze/katze-cellrenderercomboboxtext.h 2013-07-27 21:58:26 +0000
527@@ -0,0 +1,58 @@
528+/*
529+ Copyright (C) 2008-2013 Christian Dywan <christian@twotoasts.de>
530+
531+ This library is free software; you can redistribute it and/or
532+ modify it under the terms of the GNU Lesser General Public
533+ License as published by the Free Software Foundation; either
534+ version 2.1 of the License, or (at your option) any later version.
535+
536+ See the file COPYING for the full license text.
537+*/
538+
539+#ifndef __KATZE_CELL_RENDERER_COMBOBOX_TEXT_H__
540+#define __KATZE_CELL_RENDERER_COMBOBOX_TEXT_H__
541+
542+
543+#include <gtk/gtk.h>
544+
545+#ifndef GSEAL
546+#define GSEAL(String) String
547+#endif
548+
549+G_BEGIN_DECLS
550+
551+
552+#define KATZE_TYPE_CELL_RENDERER_COMBOBOX_TEXT (katze_cell_renderer_combobox_text_get_type ())
553+#define KATZE_CELL_RENDERER_COMBOBOX_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), KATZE_TYPE_CELL_RENDERER_COMBOBOX_TEXT, KatzeCellRendererComboBoxText))
554+#define KATZE_CELL_RENDERER_COMBOBOX_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), KATZE_TYPE_CELL_RENDERER_COMBOBOX_TEXT, KatzeCellRendererComboBoxTextClass))
555+#define KATZE_IS_CELL_RENDERER_COMBOBOX_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), KATZE_TYPE_CELL_RENDERER_COMBOBOX_TEXT))
556+#define KATZE_IS_CELL_RENDERER_COMBOBOX_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), KATZE_TYPE_CELL_RENDERER_COMBOBOX_TEXT))
557+#define KATZE_CELL_RENDERER_COMBOBOX_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), KATZE_TYPE_CELL_RENDERER_COMBOBOX_TEXT, KatzeCellRendererComboBoxTextClass))
558+
559+typedef struct _KatzeCellRendererComboBoxText KatzeCellRendererComboBoxText;
560+typedef struct _KatzeCellRendererComboBoxTextClass KatzeCellRendererComboBoxTextClass;
561+
562+struct _KatzeCellRendererComboBoxText
563+{
564+ GtkCellRendererText parent;
565+
566+ /*< private >*/
567+};
568+
569+struct _KatzeCellRendererComboBoxTextClass
570+{
571+ GtkCellRendererTextClass parent_class;
572+
573+ /* Padding for future expansion */
574+ void (*_gtk_reserved1) (void);
575+ void (*_gtk_reserved2) (void);
576+ void (*_gtk_reserved3) (void);
577+ void (*_gtk_reserved4) (void);
578+};
579+
580+GType katze_cell_renderer_combobox_text_get_type (void) G_GNUC_CONST;
581+GtkCellRenderer *katze_cell_renderer_combobox_text_new (void);
582+
583+G_END_DECLS
584+
585+#endif /* __KATZE_CELL_RENDERER_COMBOBOX_TEXT_H__ */
586
587=== modified file 'midori/midori-browser.c'
588--- midori/midori-browser.c 2013-07-15 23:01:23 +0000
589+++ midori/midori-browser.c 2013-07-27 21:58:26 +0000
590@@ -26,6 +26,7 @@
591 #include "midori-privatedata.h"
592 #include "midori-core.h"
593 #include "midori-privatedata.h"
594+#include "katze-cellrenderercomboboxtext.h"
595
596 #include "marshal.h"
597
598@@ -828,52 +829,217 @@
599 }
600 }
601
602+static gboolean
603+midori_bookmark_folder_button_reach_parent (GtkTreeModel* model, GtkTreeIter *iter, gint64 parentid)
604+{
605+ do
606+ {
607+ gint64 id;
608+
609+ gtk_tree_model_get (model, iter, 1, &id, -1);
610+
611+ if (parentid == id)
612+ return TRUE;
613+
614+ if (gtk_tree_model_iter_has_child (model, iter))
615+ {
616+ GtkTreeIter child;
617+ gtk_tree_model_iter_children (model, &child, iter);
618+ if (midori_bookmark_folder_button_reach_parent (model, &child, parentid))
619+ {
620+ *iter = child;
621+ return TRUE;
622+ }
623+ }
624+ }
625+ while (gtk_tree_model_iter_next (model, iter));
626+
627+ return FALSE;
628+}
629+
630+typedef struct _FolderEntry
631+{
632+ const gchar *title;
633+ gint64 id;
634+ gint64 parentid;
635+} FolderEntry;
636+
637+static void
638+midori_bookmark_folder_free_folder_entry (FolderEntry* folder)
639+{
640+ g_free ((gpointer)folder->title);
641+}
642+
643 static GtkWidget*
644 midori_bookmark_folder_button_new (KatzeArray* array,
645- gint64 selected,
646- gint64 parentid)
647+ gint64 selected_parentid)
648 {
649- GtkListStore* model;
650+ GtkTreeStore* model;
651 GtkWidget* combo;
652 GtkCellRenderer* renderer;
653 guint n;
654 sqlite3* db;
655 sqlite3_stmt* statement;
656 gint result;
657- const gchar* sqlcmd = "SELECT title, id FROM bookmarks WHERE uri='' ORDER BY title ASC";
658-
659- model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT64);
660- combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (model));
661- renderer = gtk_cell_renderer_text_new ();
662- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
663- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "text", 0);
664- gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "ellipsize", 1);
665- gtk_list_store_insert_with_values (model, NULL, G_MAXINT,
666- 0, _("Bookmarks"), 1, PANGO_ELLIPSIZE_END, 2, (gint64)0, -1);
667- gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
668+ const gchar* sqlcmd = "SELECT title, id, parentid FROM bookmarks WHERE uri='' ORDER BY parentid, title ASC";
669+ gint64 current_parentid;
670+ GtkTreeIter tree_iter;
671+ GtkTreeIter stock_parent_iter;
672+ GtkTreeIter* parent_iter;
673+ GList *folders = NULL;
674
675 db = g_object_get_data (G_OBJECT (array), "db");
676 g_return_val_if_fail (db != NULL, NULL);
677+
678+ /* folder combo box model content:
679+ ** 0: title
680+ ** 1: id
681+ */
682+ model = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT64);
683+ combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (model));
684+
685+ /* setup combo layout
686+ ** 0: a folder icon
687+ ** 1: the folder name
688+ */
689+
690+ gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
691+
692+ renderer = gtk_cell_renderer_pixbuf_new ();
693+ g_object_set (G_OBJECT (renderer),
694+ "stock-id", GTK_STOCK_DIRECTORY,
695+ "stock-size", GTK_ICON_SIZE_MENU,
696+ NULL);
697+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
698+
699+ renderer = katze_cell_renderer_combobox_text_new ();
700+ g_object_set (G_OBJECT (renderer),
701+ "width-chars", 40, /* FIXME: figure out a way to define an acceptable string length */
702+ "ellipsize", PANGO_ELLIPSIZE_END,
703+ "unfolded-text", _("Select [text]"),
704+ NULL);
705+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
706+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), renderer, "text", 0);
707+
708+ /* read the folders list from the database */
709+ /* FIXME: this should be a service of midori/midori-bookmarks-db */
710+
711+ if ((result = sqlite3_prepare_v2 (db, sqlcmd, -1, &statement, NULL)) == SQLITE_OK)
712+ {
713+ while ((result = sqlite3_step (statement)) == SQLITE_ROW)
714+ {
715+ FolderEntry* folder = g_new (FolderEntry, 1);
716+
717+ folder->title = g_strdup ((const gchar*)sqlite3_column_text (statement, 0));
718+ folder->id = sqlite3_column_int64 (statement, 1);
719+ folder->parentid = sqlite3_column_int64 (statement, 2);
720+
721+ folders = g_list_append (folders, folder);
722+ }
723+
724+ sqlite3_clear_bindings (statement);
725+ sqlite3_reset (statement);
726+ }
727+
728+ /* populate the combo box */
729+ /* FIXME: here we should have the root bookmark array's name and id, not hard encoded values */
730+
731+ gtk_tree_store_insert_with_values (model, &tree_iter, NULL, G_MAXINT,
732+ 0, _("Bookmarks"), 1, (gint64)0, -1);
733+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &tree_iter);
734+
735+ current_parentid = 0;
736+ parent_iter = NULL;
737 n = 1;
738- if ((result = sqlite3_prepare_v2 (db, sqlcmd, -1, &statement, NULL)) == SQLITE_OK)
739- while ((result = sqlite3_step (statement)) == SQLITE_ROW)
740+ while (g_list_first (folders))
741 {
742- const unsigned char* name = sqlite3_column_text (statement, 0);
743- gint64 id = sqlite3_column_int64 (statement, 1);
744+ gboolean something_done = FALSE;
745+ GList* list_iter = g_list_first (folders);
746
747- /* do not show the folder itself */
748- if (id != selected)
749+ do
750 {
751- gtk_list_store_insert_with_values (model, NULL, G_MAXINT,
752- 0, name, 1, PANGO_ELLIPSIZE_END, 2, id, -1);
753-
754- if (id == parentid)
755- gtk_combo_box_set_active (GTK_COMBO_BOX (combo), n);
756+ FolderEntry* folder = list_iter->data;
757+ const gchar* title = folder->title;
758+ gint64 id = folder->id;
759+ gint64 parentid = folder->parentid;
760+
761+ if (parentid != current_parentid) /* optimize case of sub-folders of the same parent */
762+ {
763+ if (!parentid)
764+ {
765+ /* folder's parent is the stree store root */
766+
767+ current_parentid = 0;
768+ parent_iter = NULL;
769+ }
770+ else if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &tree_iter))
771+ {
772+ if (midori_bookmark_folder_button_reach_parent (
773+ GTK_TREE_MODEL (model), &tree_iter, parentid))
774+ {
775+ /* folder's parent found in the tree store */
776+
777+ current_parentid = parentid;
778+ stock_parent_iter = tree_iter;
779+ parent_iter = &stock_parent_iter;
780+ }
781+ else
782+ {
783+ /* folder's parent not found, skip it */
784+
785+ list_iter = g_list_next (list_iter);
786+ continue;
787+ }
788+ }
789+ else
790+ g_assert_not_reached ();
791+ }
792+
793+ /* insert folder in the tree store and remove it from the folders list */
794+
795+ gtk_tree_store_insert_with_values (model, &tree_iter, parent_iter, G_MAXINT,
796+ 0, title, 1, id, -1);
797+
798+ if (id == selected_parentid)
799+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &tree_iter);
800+
801 n++;
802- }
803- }
804+
805+ something_done = TRUE;
806+
807+ g_free ((gpointer)title);
808+ folders = g_list_delete_link (folders, list_iter);
809+
810+ list_iter = g_list_first (folders);
811+ }
812+ while (list_iter);
813+
814+ if (!something_done) /* avoid infinite loop in case of orphan folders */
815+ break;
816+ }
817+
818+ if (g_list_first (folders))
819+ {
820+ GList* iter;
821+ g_printerr ("midori_bookmark_folder_button_new: orphan folder(s) detected in bookmarks db\n");
822+
823+ for (iter = g_list_first (folders) ; iter ; iter = g_list_next (iter))
824+ {
825+ FolderEntry* folder = iter->data;
826+ const gchar* title = folder->title;
827+ gint64 id = folder->id;
828+ gint64 parentid = folder->parentid;
829+
830+ g_printerr (" id=%" G_GINT64_FORMAT ", parentid=%" G_GINT64_FORMAT ", title=%s\n",
831+ id, parentid, title);
832+ }
833+
834+ g_list_free_full (folders, (GDestroyNotify)midori_bookmark_folder_free_folder_entry);
835+ }
836+
837 if (n < 2)
838 gtk_widget_set_sensitive (combo, FALSE);
839+
840 return combo;
841 }
842
843@@ -887,14 +1053,8 @@
844
845 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
846 {
847- gchar* selected = NULL;
848 GtkTreeModel* model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
849- gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &selected, 2, &id, -1);
850-
851- if (g_str_equal (selected, _("Bookmarks")))
852- id = 0;
853-
854- g_free (selected);
855+ gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 1, &id, -1);
856 }
857
858 return id;
859@@ -1021,7 +1181,6 @@
860 }
861
862 combo_folder = midori_bookmark_folder_button_new (browser->bookmarks,
863- katze_item_get_meta_integer (bookmark, "id"),
864 katze_item_get_meta_integer (bookmark, "parentid"));
865 gtk_box_pack_start (GTK_BOX (vbox), combo_folder, FALSE, FALSE, 0);
866
867@@ -4460,8 +4619,7 @@
868 gtk_container_add (GTK_CONTAINER (content_area), hbox);
869 gtk_widget_show_all (hbox);
870
871- combobox_folder = midori_bookmark_folder_button_new (browser->bookmarks,
872- 0, 0);
873+ combobox_folder = midori_bookmark_folder_button_new (browser->bookmarks, 0);
874 gtk_container_add (GTK_CONTAINER (content_area), combobox_folder);
875
876 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);

Subscribers

People subscribed via source and target branches

to all changes: