Merge lp:~aauzi/midori/fix-1177553-3 into lp:midori
- fix-1177553-3
- Merge into trunk
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 |
Related bugs: |
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]'.
Cris Dywan (kalikiana) wrote : Posted in a previous version of this proposal | # |
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?
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.
Cris Dywan (kalikiana) wrote : | # |
I'm fine with the approach, purely technically speaking.
Paweł Forysiuk (tuxator) wrote : | # |
Seems to work fine. Approve
Preview Diff
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); |
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.