c70b678a43cab8f3e3b36966cf068b0479abfe57
[colloquium.git] / src / stylesheet_editor.c
1 /*
2  * stylesheet_editor.c
3  *
4  * Copyright © 2013-2019 Thomas White <taw@bitwiz.org.uk>
5  *
6  * This file is part of Colloquium.
7  *
8  * Colloquium is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <gtk/gtk.h>
32
33 #include <narrative.h>
34 #include <stylesheet.h>
35
36 #include "stylesheet_editor.h"
37
38 struct _sspriv
39 {
40         Stylesheet *stylesheet;
41         char *style_name;
42 };
43
44
45 G_DEFINE_TYPE_WITH_CODE(StylesheetEditor, stylesheet_editor,
46                         GTK_TYPE_DIALOG, G_ADD_PRIVATE(StylesheetEditor))
47
48
49 enum selector_column
50 {
51         SEL_COL_FRIENDLY_NAME,
52         SEL_COL_PATH
53 };
54
55
56 static enum gradient id_to_gradtype(const gchar *id)
57 {
58         assert(id != NULL);
59         if ( strcmp(id, "flat") == 0 ) return GRAD_NONE;
60         if ( strcmp(id, "horiz") == 0 ) return GRAD_HORIZ;
61         if ( strcmp(id, "vert") == 0 ) return GRAD_VERT;
62         return GRAD_NONE;
63 }
64
65
66 static enum length_unit id_to_units(const char *id)
67 {
68         if ( strcmp(id, "units") == 0 ) return LENGTH_UNIT;
69         if ( strcmp(id, "percent") == 0 ) return LENGTH_FRAC;
70         return LENGTH_UNIT;
71 }
72
73
74 static enum alignment id_to_align(const char *id, int *err)
75 {
76         *err = 0;
77         if ( strcmp(id, "left") == 0 ) return ALIGN_LEFT;
78         if ( strcmp(id, "center") == 0 ) return ALIGN_CENTER;
79         if ( strcmp(id, "right") == 0 ) return ALIGN_RIGHT;
80         *err = 1;
81         return ALIGN_LEFT;
82 }
83
84
85 static void set_font_fgcol_align_from_ss(Stylesheet *ss, const char *style_name,
86                                          GtkWidget *wfont,
87                                          GtkWidget *wfgcol,
88                                          GtkWidget *walign)
89 {
90         const char *font;
91         struct colour fgcol;
92         enum alignment align;
93
94         font = stylesheet_get_font(ss, style_name, &fgcol, &align);
95         if ( font != NULL ) {
96
97                 GdkRGBA rgba;
98
99                 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(wfont), font);
100
101                 rgba.red = fgcol.rgba[0];
102                 rgba.green = fgcol.rgba[1];
103                 rgba.blue = fgcol.rgba[2];
104                 rgba.alpha = fgcol.rgba[3];
105                 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wfgcol), &rgba);
106
107                 switch ( align ) {
108
109                         case ALIGN_LEFT :
110                         gtk_combo_box_set_active_id(GTK_COMBO_BOX(walign), "left");
111                         break;
112
113                         case ALIGN_CENTER :
114                         gtk_combo_box_set_active_id(GTK_COMBO_BOX(walign), "center");
115                         break;
116
117                         case ALIGN_RIGHT :
118                         gtk_combo_box_set_active_id(GTK_COMBO_BOX(walign), "right");
119                         break;
120
121                         default :
122                         gtk_combo_box_set_active_id(GTK_COMBO_BOX(walign), "left");
123                         break;
124
125                 }
126
127         }
128 }
129
130
131 static void set_padding_from_ss(Stylesheet *ss, const char *style_name,
132                                 GtkWidget *wl, GtkWidget *wr,
133                                 GtkWidget *wt, GtkWidget *wb)
134 {
135         struct length padding[4];
136
137         if ( stylesheet_get_padding(ss, style_name, padding) ) return;
138         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wl), padding[0].len);
139         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wr), padding[1].len);
140         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wt), padding[2].len);
141         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wb), padding[3].len);
142 }
143
144
145 static void set_paraspace_from_ss(Stylesheet *ss, const char *style_name,
146                                   GtkWidget *wl, GtkWidget *wr,
147                                   GtkWidget *wt, GtkWidget *wb)
148 {
149         struct length paraspace[4];
150
151         if ( stylesheet_get_paraspace(ss, style_name, paraspace) ) return;
152         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wl), paraspace[0].len);
153         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wr), paraspace[1].len);
154         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wt), paraspace[2].len);
155         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wb), paraspace[3].len);
156 }
157
158
159 static void set_geom_from_ss(Stylesheet *ss, const char *style_name,
160                              GtkWidget *ww, GtkWidget *wh,
161                              GtkWidget *wx, GtkWidget *wy,
162                              GtkWidget *wwu, GtkWidget *whu)
163 {
164         struct frame_geom geom;
165
166         if ( stylesheet_get_geometry(ss, style_name, &geom) ) return;
167
168         if ( geom.w.unit == LENGTH_FRAC ) {
169                 geom.w.len *= 100;
170                 gtk_combo_box_set_active_id(GTK_COMBO_BOX(wwu), "percent");
171         } else {
172                 gtk_combo_box_set_active_id(GTK_COMBO_BOX(wwu), "units");
173         }
174         if ( geom.h.unit == LENGTH_FRAC ) {
175                 geom.h.len *= 100;
176                 gtk_combo_box_set_active_id(GTK_COMBO_BOX(whu), "percent");
177         } else {
178                 gtk_combo_box_set_active_id(GTK_COMBO_BOX(whu), "units");
179         }
180         gtk_spin_button_set_value(GTK_SPIN_BUTTON(ww), geom.w.len);
181         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wh), geom.h.len);
182         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wx), geom.x.len);
183         gtk_spin_button_set_value(GTK_SPIN_BUTTON(wy), geom.y.len);
184 }
185
186
187 static void set_bg_from_ss(Stylesheet *ss, const char *style_name,
188                            GtkWidget *wcol, GtkWidget *wcol2, GtkWidget *wgrad)
189 {
190         struct colour bgcol;
191         struct colour bgcol2;
192         enum gradient bggrad;
193         GdkRGBA rgba;
194
195         if ( stylesheet_get_background(ss, style_name, &bggrad, &bgcol, &bgcol2) ) return;
196
197         rgba.red = bgcol.rgba[0];
198         rgba.green = bgcol.rgba[1];
199         rgba.blue = bgcol.rgba[2];
200         rgba.alpha = bgcol.rgba[3];
201         gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol), &rgba);
202
203         rgba.red = bgcol2.rgba[0];
204         rgba.green = bgcol2.rgba[1];
205         rgba.blue = bgcol2.rgba[2];
206         rgba.alpha = bgcol2.rgba[3];
207         gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(wcol2), &rgba);
208
209         switch ( bggrad ) {
210
211                 case GRAD_NONE:
212                 gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "flat");
213                 gtk_widget_set_sensitive(wcol, TRUE);
214                 gtk_widget_set_sensitive(wcol2, FALSE);
215                 break;
216
217                 case GRAD_HORIZ:
218                 gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "horiz");
219                 gtk_widget_set_sensitive(wcol, TRUE);
220                 gtk_widget_set_sensitive(wcol2, TRUE);
221                 break;
222
223                 case GRAD_VERT:
224                 gtk_combo_box_set_active_id(GTK_COMBO_BOX(wgrad), "vert");
225                 gtk_widget_set_sensitive(wcol, TRUE);
226                 gtk_widget_set_sensitive(wcol2, TRUE);
227                 break;
228
229         }
230
231 }
232
233
234 static void add_style_to_selector(Stylesheet *ss, const char *path,
235                                   GtkTreeStore *ts, GtkTreeIter *parent_iter)
236 {
237         int i, n_substyles;
238
239         n_substyles = stylesheet_get_num_substyles(ss, path);
240         for ( i=0; i<n_substyles; i++ ) {
241
242                 GtkTreeIter iter;
243                 const char *name = stylesheet_get_substyle_name(ss, path, i);
244
245                 /* Calculate the full path for this style */
246                 size_t len = strlen(path) + strlen(name) + 2;
247                 char *new_path = malloc(len);
248
249                 if ( path[0] != '\0' ) {
250                         strcpy(new_path, path);
251                         strcat(new_path, ".");
252                 } else {
253                         new_path[0] = '\0';
254                 }
255                 strcat(new_path, name);
256
257                 /* Add this style */
258                 gtk_tree_store_append(ts, &iter, parent_iter);
259                 gtk_tree_store_set(ts, &iter, SEL_COL_FRIENDLY_NAME,
260                                    stylesheet_get_friendly_name(name),
261                                    SEL_COL_PATH, new_path, -1);
262
263                 /* Add all substyles */
264                 add_style_to_selector(ss, new_path, ts, &iter);
265         }
266 }
267
268
269 static void set_geom_sensitive(StylesheetEditor *se, gboolean val)
270 {
271         gtk_widget_set_sensitive(se->x, val);
272         gtk_widget_set_sensitive(se->y, val);
273         gtk_widget_set_sensitive(se->w, val);
274         gtk_widget_set_sensitive(se->h, val);
275         gtk_widget_set_sensitive(se->w_units, val);
276         gtk_widget_set_sensitive(se->h_units, val);
277 }
278
279
280 static void set_bg_sensitive(StylesheetEditor *se, gboolean val)
281 {
282         gtk_widget_set_sensitive(se->bgcol, val);
283         gtk_widget_set_sensitive(se->bgcol2, val);
284         gtk_widget_set_sensitive(se->bggrad, val);
285 }
286
287
288 static void set_padding_sensitive(StylesheetEditor *se, gboolean val)
289 {
290         gtk_widget_set_sensitive(se->padding_l, val);
291         gtk_widget_set_sensitive(se->padding_r, val);
292         gtk_widget_set_sensitive(se->padding_t, val);
293         gtk_widget_set_sensitive(se->padding_b, val);
294 }
295
296
297 static void set_paraspace_sensitive(StylesheetEditor *se, gboolean val)
298 {
299         gtk_widget_set_sensitive(se->paraspace_l, val);
300         gtk_widget_set_sensitive(se->paraspace_r, val);
301         gtk_widget_set_sensitive(se->paraspace_t, val);
302         gtk_widget_set_sensitive(se->paraspace_b, val);
303 }
304
305
306 static void set_font_fgcol_align_sensitive(StylesheetEditor *se, gboolean val)
307 {
308         gtk_widget_set_sensitive(se->font, val);
309         gtk_widget_set_sensitive(se->fgcol, val);
310         gtk_widget_set_sensitive(se->alignment, val);
311 }
312
313
314 static void set_values_from_presentation(StylesheetEditor *se)
315 {
316         set_geom_sensitive(se, TRUE);
317         set_bg_sensitive(se, TRUE);
318         set_padding_sensitive(se, TRUE);
319         set_font_fgcol_align_sensitive(se, TRUE);
320         set_paraspace_sensitive(se, TRUE);
321         if ( strncmp(se->priv->style_name, "NARRATIVE", 9) == 0 ) {
322                 set_geom_sensitive(se, FALSE);
323                 if ( se->priv->style_name[9] == '.' ) {
324
325                         /* Narrative item */
326                         set_bg_sensitive(se, FALSE);
327                         set_padding_sensitive(se, FALSE);
328
329                 }
330         }
331         if ( strncmp(se->priv->style_name, "SLIDE", 5) == 0 ) {
332                 if ( se->priv->style_name[5] != '.' ) {
333                         /* Top level "slide" */
334                         set_geom_sensitive(se, FALSE);
335                         gtk_widget_set_sensitive(se->w, TRUE);
336                         gtk_widget_set_sensitive(se->h, TRUE);
337                         set_padding_sensitive(se, FALSE);
338                         set_font_fgcol_align_sensitive(se, FALSE);
339                         set_paraspace_sensitive(se, FALSE);
340
341                 }
342         }
343
344         if ( strcmp(se->priv->style_name, "SLIDE.TEXT") == 0 ) {
345                 set_geom_sensitive(se, FALSE);
346         }
347
348         set_geom_from_ss(se->priv->stylesheet, se->priv->style_name,
349                          se->w, se->h, se->x, se->y, se->w_units, se->h_units);
350
351         set_font_fgcol_align_from_ss(se->priv->stylesheet, se->priv->style_name,
352                                      se->font, se->fgcol, se->alignment);
353
354         set_bg_from_ss(se->priv->stylesheet, se->priv->style_name,
355                        se->bgcol, se->bgcol2, se->bggrad);
356
357         set_padding_from_ss(se->priv->stylesheet, se->priv->style_name,
358                             se->padding_l, se->padding_r, se->padding_t, se->padding_b);
359
360         set_paraspace_from_ss(se->priv->stylesheet, se->priv->style_name,
361                               se->paraspace_l, se->paraspace_r, se->paraspace_t, se->paraspace_b);
362 }
363
364
365 static void element_changed(GtkTreeSelection *sel, StylesheetEditor *se)
366 {
367         GtkTreeIter iter;
368         GtkTreeModel *model;
369         gchar *new_path;
370
371         g_free(se->priv->style_name);
372         if ( gtk_tree_selection_get_selected(sel, &model, &iter) ) {
373                 gtk_tree_model_get(model, &iter, SEL_COL_PATH, &new_path, -1);
374                 se->priv->style_name = new_path;
375                 set_values_from_presentation(se);
376         }
377 }
378
379
380 static void revert_sig(GtkButton *button, StylesheetEditor *se)
381 {
382         /* FIXME: implement */
383         set_values_from_presentation(se);
384         g_signal_emit_by_name(se, "changed");
385 }
386
387
388 static void paraspace_sig(GtkSpinButton *widget, StylesheetEditor *se)
389 {
390         struct length paraspace[4];
391
392         if ( !gtk_widget_get_sensitive(GTK_WIDGET(widget)) ) return;
393
394         paraspace[0].len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->paraspace_l));
395         paraspace[0].unit = LENGTH_UNIT;
396         paraspace[1].len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->paraspace_r));
397         paraspace[1].unit = LENGTH_UNIT;
398         paraspace[2].len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->paraspace_t));
399         paraspace[2].unit = LENGTH_UNIT;
400         paraspace[3].len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->paraspace_b));
401         paraspace[3].unit = LENGTH_UNIT;
402
403         stylesheet_set_paraspace(se->priv->stylesheet, se->priv->style_name, paraspace);
404
405         set_values_from_presentation(se);
406         g_signal_emit_by_name(se, "changed");
407 }
408
409
410 static void padding_sig(GtkSpinButton *widget, StylesheetEditor *se)
411 {
412         struct length padding[4];
413
414         if ( !gtk_widget_get_sensitive(GTK_WIDGET(widget)) ) return;
415
416         padding[0].len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->padding_l));
417         padding[0].unit = LENGTH_UNIT;
418         padding[1].len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->padding_r));
419         padding[1].unit = LENGTH_UNIT;
420         padding[2].len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->padding_t));
421         padding[2].unit = LENGTH_UNIT;
422         padding[3].len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->padding_b));
423         padding[3].unit = LENGTH_UNIT;
424
425         stylesheet_set_padding(se->priv->stylesheet, se->priv->style_name, padding);
426
427         set_values_from_presentation(se);
428         g_signal_emit_by_name(se, "changed");
429 }
430
431
432 static void alignment_sig(GtkComboBoxText *widget, StylesheetEditor *se)
433 {
434         const gchar *id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget));
435         int err;
436         enum alignment align;
437
438         if ( !gtk_widget_get_sensitive(GTK_WIDGET(widget)) ) return;
439
440         align = id_to_align(id, &err);
441         if ( !err ) {
442                 stylesheet_set_alignment(se->priv->stylesheet, se->priv->style_name, align);
443                 set_values_from_presentation(se);
444                 g_signal_emit_by_name(se, "changed");
445         }
446 }
447
448
449 static void geometry_sig(GtkSpinButton *widget, StylesheetEditor *se)
450 {
451         struct frame_geom new_geom;
452         const gchar *uid;
453
454         if ( !gtk_widget_get_sensitive(GTK_WIDGET(widget)) ) return;
455         new_geom.w.len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->w));
456         new_geom.h.len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->h));
457         new_geom.x.len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->x));
458         new_geom.y.len = gtk_spin_button_get_value(GTK_SPIN_BUTTON(se->y));
459
460         uid = gtk_combo_box_get_active_id(GTK_COMBO_BOX(se->w_units));
461         new_geom.w.unit = id_to_units(uid);
462         uid = gtk_combo_box_get_active_id(GTK_COMBO_BOX(se->h_units));
463         new_geom.h.unit = id_to_units(uid);
464
465         new_geom.x.unit = LENGTH_UNIT;
466         new_geom.y.unit = LENGTH_UNIT;
467
468         if ( new_geom.w.unit == LENGTH_FRAC ) new_geom.w.len /= 100.0;
469         if ( new_geom.h.unit == LENGTH_FRAC ) new_geom.h.len /= 100.0;
470
471         stylesheet_set_geometry(se->priv->stylesheet, se->priv->style_name, new_geom);
472
473         set_values_from_presentation(se);
474         g_signal_emit_by_name(se, "changed");
475 }
476
477
478 static void font_sig(GtkFontButton *widget, StylesheetEditor *se)
479 {
480         gchar *font = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(widget));
481         if ( !gtk_widget_get_sensitive(GTK_WIDGET(widget)) ) return;
482
483         stylesheet_set_font(se->priv->stylesheet, se->priv->style_name, font);
484         /* Don't free: now owned by stylesheet */
485
486         set_values_from_presentation(se);
487         g_signal_emit_by_name(se, "changed");
488 }
489
490
491 static void fgcol_sig(GtkColorButton *widget, StylesheetEditor *se)
492 {
493         GdkRGBA rgba;
494         struct colour col;
495
496         if ( !gtk_widget_get_sensitive(GTK_WIDGET(widget)) ) return;
497
498         gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &rgba);
499         col.rgba[0] = rgba.red;
500         col.rgba[1] = rgba.green;
501         col.rgba[2] = rgba.blue;
502         col.rgba[3] = rgba.alpha;
503         col.hexcode = 0;
504         stylesheet_set_fgcol(se->priv->stylesheet, se->priv->style_name, col);
505
506         set_values_from_presentation(se);
507         g_signal_emit_by_name(se, "changed");
508 }
509
510
511 static void bg_sig(GtkColorButton *widget, StylesheetEditor *se)
512 {
513         enum gradient g;
514         const gchar *id;
515         GdkRGBA rgba;
516         struct colour bgcol, bgcol2;
517
518         if ( !gtk_widget_get_sensitive(GTK_WIDGET(widget)) ) return;
519
520         id = gtk_combo_box_get_active_id(GTK_COMBO_BOX(se->bggrad));
521         g = id_to_gradtype(id);
522
523         gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(se->bgcol), &rgba);
524         if ( rgba.alpha < 0.000001 ) rgba.alpha = 0.0;
525         bgcol.rgba[0] = rgba.red;
526         bgcol.rgba[1] = rgba.green;
527         bgcol.rgba[2] = rgba.blue;
528         bgcol.rgba[3] = rgba.alpha;
529         bgcol.hexcode = 0;
530
531         gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(se->bgcol2), &rgba);
532         if ( rgba.alpha < 0.000001 ) rgba.alpha = 0.0;
533         bgcol2.rgba[0] = rgba.red;
534         bgcol2.rgba[1] = rgba.green;
535         bgcol2.rgba[2] = rgba.blue;
536         bgcol2.rgba[3] = rgba.alpha;
537         bgcol2.hexcode = 0;
538
539         stylesheet_set_background(se->priv->stylesheet, se->priv->style_name, g, bgcol, bgcol2);
540
541         set_values_from_presentation(se);
542         g_signal_emit_by_name(se, "changed");
543 }
544
545
546 static void stylesheet_editor_finalize(GObject *obj)
547 {
548         G_OBJECT_CLASS(stylesheet_editor_parent_class)->finalize(obj);
549 }
550
551
552 static void stylesheet_editor_init(StylesheetEditor *se)
553 {
554         se->priv = G_TYPE_INSTANCE_GET_PRIVATE(se, COLLOQUIUM_TYPE_STYLESHEET_EDITOR,
555                                                StylesheetEditorPrivate);
556         gtk_widget_init_template(GTK_WIDGET(se));
557 }
558
559
560 #define SE_BIND_CHILD(a, b) \
561         gtk_widget_class_bind_template_child(widget_class, StylesheetEditor, a); \
562         gtk_widget_class_bind_template_callback(widget_class, b);
563
564 void stylesheet_editor_class_init(StylesheetEditorClass *klass)
565 {
566         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
567         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
568
569         gtk_widget_class_set_template_from_resource(widget_class,
570                                             "/uk/me/bitwiz/Colloquium/stylesheeteditor.ui");
571
572         gobject_class->finalize = stylesheet_editor_finalize;
573
574         SE_BIND_CHILD(paraspace_l, paraspace_sig);
575         SE_BIND_CHILD(paraspace_r, paraspace_sig);
576         SE_BIND_CHILD(paraspace_t, paraspace_sig);
577         SE_BIND_CHILD(paraspace_b, paraspace_sig);
578         SE_BIND_CHILD(padding_l, padding_sig);
579         SE_BIND_CHILD(padding_r, padding_sig);
580         SE_BIND_CHILD(padding_t, padding_sig);
581         SE_BIND_CHILD(padding_b, padding_sig);
582         SE_BIND_CHILD(font, font_sig);
583         SE_BIND_CHILD(fgcol, fgcol_sig);
584         SE_BIND_CHILD(bgcol, bg_sig);
585         SE_BIND_CHILD(bgcol2, bg_sig);
586         SE_BIND_CHILD(bggrad, bg_sig);
587         SE_BIND_CHILD(alignment, alignment_sig);
588         SE_BIND_CHILD(w, geometry_sig);
589         SE_BIND_CHILD(h, geometry_sig);
590         SE_BIND_CHILD(x, geometry_sig);
591         SE_BIND_CHILD(y, geometry_sig);
592         SE_BIND_CHILD(w_units, geometry_sig);
593         SE_BIND_CHILD(h_units, geometry_sig);
594
595         gtk_widget_class_bind_template_child(widget_class, StylesheetEditor, selector);
596         gtk_widget_class_bind_template_child(widget_class, StylesheetEditor, element_tree);
597
598         gtk_widget_class_bind_template_callback(widget_class, revert_sig);
599
600         g_signal_new("changed", COLLOQUIUM_TYPE_STYLESHEET_EDITOR,
601                      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
602 }
603
604
605 StylesheetEditor *stylesheet_editor_new(Stylesheet *ss)
606 {
607         StylesheetEditor *se;
608         GtkTreeSelection *sel;
609         GtkCellRenderer *renderer;
610         GtkTreeViewColumn *column;
611
612         se = g_object_new(COLLOQUIUM_TYPE_STYLESHEET_EDITOR, NULL);
613         if ( se == NULL ) return NULL;
614
615         se->priv->stylesheet = ss;
616         se->priv->style_name = NULL;
617
618         renderer = gtk_cell_renderer_text_new();
619         column = gtk_tree_view_column_new_with_attributes("Element", renderer,
620                                                           "text", SEL_COL_FRIENDLY_NAME,
621                                                           NULL);
622         gtk_tree_view_append_column(GTK_TREE_VIEW(se->selector), column);
623
624         gtk_tree_store_clear(se->element_tree);
625         add_style_to_selector(se->priv->stylesheet, "", se->element_tree, NULL);
626
627         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(se->selector));
628         gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
629         g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(element_changed), se);
630
631         return se;
632 }