2 * Copyright (c) 2014-2016 Bert Burgemeister <trebbu@googlemail.com>
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #include <gtk/gtkunixprint.h>
30 #include <libxml/xpath.h>
35 #include <semaphore.h>
41 #include <sys/select.h>
46 #define VERSION "4.6.0"
48 #define WHITESPACE " \t\n"
49 #define MAIN_WIN "main"
51 "usage: pipeglade [-h] " \
55 "[-u glade-file.ui]\n" \
58 "[--display X-server]\n"
64 __func__, __FILE__, __LINE__); \
71 "Out of memory in %s (%s:%d): ", \
72 __func__, __FILE__, __LINE__); \
76 static FILE *out; /* UI feedback messages */
77 static FILE *save
; /* saving user data */
78 static GtkBuilder
*builder
; /* to be read from .ui file */
80 void (*fn
)(GObject
*, const char *action
,
81 const char *data
, const char *msg
, GType type
);
92 * Print a formatted message to stream s and give up with status
95 bye(int status
, FILE *s
, const char *fmt
, ...)
100 vfprintf(s
, fmt
, ap
);
106 * Check if string s1 and s2 are equal
109 eql(const char *s1
, const char *s2
)
111 return s1
!= NULL
&& s2
!= NULL
&& strcmp(s1
, s2
) == 0;
115 widget_name(void *obj
)
117 return gtk_buildable_get_name(GTK_BUILDABLE(obj
));
121 send_msg_to(FILE* o
, GtkBuildable
*obj
, const char *tag
, va_list ap
)
124 const char *w_name
= widget_name(obj
);
127 struct timeval timeout
= {1, 0};
131 if (select(ofd
+ 1, NULL
, &wfds
, NULL
, &timeout
) == 1) {
132 fprintf(o
, "%s:%s ", w_name
, tag
);
133 while ((data
= va_arg(ap
, char *)) != NULL
) {
137 while ((c
= data
[i
++]) != '\0')
148 "send error; discarding feedback message %s:%s\n",
153 * Send GUI feedback to global stream "out". The message format is
154 * "<origin>:<tag> <data ...>". The variadic arguments are strings;
155 * last argument must be NULL.
158 send_msg(GtkBuildable
*obj
, const char *tag
, ...)
162 send_msg_to(out
, obj
, tag
, ap
);
167 * Send message from GUI to global stream "save". The message format
168 * is "<origin>:<tag> <data ...>". The variadic arguments are strings;
169 * last argument must be NULL.
172 save_msg(GtkBuildable
*obj
, const char *tag
, ...)
176 send_msg_to(save
, obj
, tag
, ap
);
181 * Send message from GUI to global stream "save". The message format
182 * is "<origin>:set <data ...>". The variadic arguments are strings;
183 * last argument must be NULL.
186 save_action_set_msg(GtkBuildable
*obj
, const char *tag
, ...)
190 send_msg_to(save
, obj
, "set", ap
);
195 * Callback that sends user's selection from a file dialog
198 cb_send_file_chooser_dialog_selection(gpointer user_data
)
200 send_msg(user_data
, "file",
201 gtk_file_chooser_get_filename(user_data
), NULL
);
202 send_msg(user_data
, "folder",
203 gtk_file_chooser_get_current_folder(user_data
), NULL
);
207 * Callback that sends in a message the content of the text buffer
208 * passed in user_data
211 cb_send_text(GtkBuildable
*obj
, gpointer user_data
)
215 gtk_text_buffer_get_bounds(user_data
, &a
, &b
);
216 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, TRUE
), NULL
);
220 * Callback that sends in a message the highlighted text from the text
221 * buffer which was passed in user_data
224 cb_send_text_selection(GtkBuildable
*obj
, gpointer user_data
)
228 gtk_text_buffer_get_selection_bounds(user_data
, &a
, &b
);
229 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, TRUE
), NULL
);
233 * Callbacks that send messages about pointer device activity in a
237 cb_event_box_button(GtkBuildable
*obj
, GdkEvent
*e
, gpointer user_data
)
241 snprintf(data
, BUFLEN
, "%d %.1lf %.1lf",
242 e
->button
.button
, e
->button
.x
, e
->button
.y
);
243 send_msg(obj
, user_data
, data
, NULL
);
248 cb_event_box_motion(GtkBuildable
*obj
, GdkEvent
*e
, gpointer user_data
)
252 snprintf(data
, BUFLEN
, "%.1lf %.1lf", e
->button
.x
, e
->button
.y
);
253 send_msg(obj
, user_data
, data
, NULL
);
258 * Callback that sends in a message the name of the key pressed when
259 * a GtkEventBox is focused.
262 cb_event_box_key(GtkBuildable
*obj
, GdkEvent
*e
, gpointer user_data
)
264 send_msg(obj
, user_data
, gdk_keyval_name(e
->key
.keyval
), NULL
);
269 * Use msg_sender() to send a message describing a particular cell.
272 send_tree_cell_msg_by(void msg_sender(GtkBuildable
*, const char *, ...),
273 GtkTreeModel
*model
, const char *path_s
,
274 GtkTreeIter
*iter
, int col
, GtkBuildable
*obj
)
276 GValue value
= G_VALUE_INIT
;
280 gtk_tree_model_get_value(model
, iter
, col
, &value
);
281 col_type
= gtk_tree_model_get_column_type(model
, col
);
284 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_int(&value
));
285 msg_sender(obj
, "gint", path_s
, str
, NULL
);
288 snprintf(str
, BUFLEN
, " %d %ld", col
, g_value_get_long(&value
));
289 msg_sender(obj
, "glong", path_s
, str
, NULL
);
292 snprintf(str
, BUFLEN
, " %d %" PRId64
, col
, g_value_get_int64(&value
));
293 msg_sender(obj
, "gint64", path_s
, str
, NULL
);
296 snprintf(str
, BUFLEN
, " %d %u", col
, g_value_get_uint(&value
));
297 msg_sender(obj
, "guint", path_s
, str
, NULL
);
300 snprintf(str
, BUFLEN
, " %d %lu", col
, g_value_get_ulong(&value
));
301 msg_sender(obj
, "gulong", path_s
, str
, NULL
);
304 snprintf(str
, BUFLEN
, " %d %" PRIu64
, col
, g_value_get_uint64(&value
));
305 msg_sender(obj
, "guint64", path_s
, str
, NULL
);
308 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_boolean(&value
));
309 msg_sender(obj
, "gboolean", path_s
, str
, NULL
);
312 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_float(&value
));
313 msg_sender(obj
, "gfloat", path_s
, str
, NULL
);
316 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_double(&value
));
317 msg_sender(obj
, "gdouble", path_s
, str
, NULL
);
320 snprintf(str
, BUFLEN
, " %d ", col
);
321 msg_sender(obj
, "gchararray", path_s
, str
, g_value_get_string(&value
), NULL
);
324 fprintf(stderr
, "column %d not implemented: %s\n", col
, G_VALUE_TYPE_NAME(&value
));
327 g_value_unset(&value
);
331 * Use msg_sender() to send one message per column for a single row.
334 send_tree_row_msg_by(void msg_sender(GtkBuildable
*, const char *, ...),
335 GtkTreeModel
*model
, char *path_s
,
336 GtkTreeIter
*iter
, GtkBuildable
*obj
)
339 for (col
= 0; col
< gtk_tree_model_get_n_columns(model
); col
++)
340 send_tree_cell_msg_by(msg_sender
, model
, path_s
, iter
, col
, obj
);
344 * send_tree_row_msg serves as an argument for
345 * gtk_tree_selection_selected_foreach()
348 send_tree_row_msg(GtkTreeModel
*model
,
349 GtkTreePath
*path
, GtkTreeIter
*iter
, GtkBuildable
*obj
)
351 char *path_s
= gtk_tree_path_to_string(path
);
353 send_tree_row_msg_by(send_msg
, model
, path_s
, iter
, obj
);
359 * save_tree_row_msg serves as an argument for
360 * gtk_tree_model_foreach().
361 * Send message from GUI to global stream "save".
364 save_tree_row_msg(GtkTreeModel
*model
,
365 GtkTreePath
*path
, GtkTreeIter
*iter
, GtkBuildable
*obj
)
367 char *path_s
= gtk_tree_path_to_string(path
);
370 send_tree_row_msg_by(save_action_set_msg
, model
, path_s
, iter
, obj
);
376 * Callback that sends message(s) whose nature depends on the
377 * arguments passed. A call to this function will also be initiated
378 * by the user command ...:force.
381 cb(GtkBuildable
*obj
, const char *tag
)
386 unsigned int year
= 0, month
= 0, day
= 0;
388 if (GTK_IS_ENTRY(obj
))
389 send_msg(obj
, tag
, gtk_entry_get_text(GTK_ENTRY(obj
)), NULL
);
390 else if (GTK_IS_MENU_ITEM(obj
))
391 send_msg(obj
, tag
, gtk_menu_item_get_label(GTK_MENU_ITEM(obj
)), NULL
);
392 else if (GTK_IS_RANGE(obj
)) {
393 snprintf(str
, BUFLEN
, "%f", gtk_range_get_value(GTK_RANGE(obj
)));
394 send_msg(obj
, tag
, str
, NULL
);
395 } else if (GTK_IS_SWITCH(obj
))
396 send_msg(obj
, gtk_switch_get_active(GTK_SWITCH(obj
)) ? "1" : "0", NULL
);
397 else if (GTK_IS_TOGGLE_BUTTON(obj
))
398 send_msg(obj
, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(obj
)) ? "1" : "0", NULL
);
399 else if (GTK_IS_COLOR_BUTTON(obj
)) {
400 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
401 send_msg(obj
, tag
, gdk_rgba_to_string(&color
), NULL
);
402 } else if (GTK_IS_FONT_BUTTON(obj
))
403 send_msg(obj
, tag
, gtk_font_button_get_font_name(GTK_FONT_BUTTON(obj
)), NULL
);
404 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
405 send_msg(obj
, tag
, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(obj
)), NULL
);
406 else if (GTK_IS_BUTTON(obj
) || GTK_IS_TREE_VIEW_COLUMN(obj
) || GTK_IS_SOCKET(obj
))
407 send_msg(obj
, tag
, NULL
);
408 else if (GTK_IS_CALENDAR(obj
)) {
409 gtk_calendar_get_date(GTK_CALENDAR(obj
), &year
, &month
, &day
);
410 snprintf(str
, BUFLEN
, "%04u-%02u-%02u", year
, ++month
, day
);
411 send_msg(obj
, tag
, str
, NULL
);
412 } else if (GTK_IS_TREE_SELECTION(obj
)) {
413 view
= gtk_tree_selection_get_tree_view(GTK_TREE_SELECTION(obj
));
414 send_msg(GTK_BUILDABLE(view
), tag
, NULL
);
415 gtk_tree_selection_selected_foreach(GTK_TREE_SELECTION(obj
),
416 (GtkTreeSelectionForeachFunc
)send_tree_row_msg
,
419 fprintf(stderr
, "ignoring callback from %s\n", widget_name(obj
));
423 * Callback like cb(), but returning true.
426 cb_true(GtkBuildable
*obj
, const char *tag
)
433 * Store a line from stream s into buf, which should have been malloc'd
434 * to bufsize. Enlarge buf and bufsize if necessary.
437 read_buf(FILE *s
, char **buf
, size_t *bufsize
)
448 select(ifd
+ 1, &rfds
, NULL
, NULL
, NULL
);
450 if (c
== '\n' || feof(s
))
452 if (i
>= *bufsize
- 1)
453 if ((*buf
= realloc(*buf
, *bufsize
= *bufsize
* 2)) == NULL
)
458 case 'n': (*buf
)[i
++] = '\n'; break;
459 case 'r': (*buf
)[i
++] = '\r'; break;
460 default: (*buf
)[i
++] = c
; break;
462 } else if (c
== '\\')
475 ign_cmd(GType type
, const char *msg
)
477 const char *name
, *pad
= " ";
479 if (type
== G_TYPE_INVALID
) {
484 name
= g_type_name(type
);
485 fprintf(stderr
, "ignoring %s%scommand \"%s\"\n", name
, pad
, msg
);
489 * Drawing on a GtkDrawingArea
516 * One single element of a drawing
519 struct draw_op
*next
;
520 struct draw_op
*prev
;
526 struct rectangle_args
{
541 struct curve_to_args
{
550 struct move_to_args
{
555 struct set_source_rgba_args
{
559 struct set_dash_args
{
564 struct set_line_cap_args
{
565 cairo_line_cap_t line_cap
;
568 struct set_line_join_args
{
569 cairo_line_join_t line_join
;
572 struct set_line_width_args
{
576 struct show_text_args
{
581 struct set_font_size_args
{
586 draw(cairo_t
*cr
, enum cairo_fn op
, void *op_args
)
590 struct rectangle_args
*args
= op_args
;
592 cairo_rectangle(cr
, args
->x
, args
->y
, args
->width
, args
->height
);
596 struct arc_args
*args
= op_args
;
598 cairo_arc(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
602 struct arc_args
*args
= op_args
;
604 cairo_arc_negative(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
608 struct curve_to_args
*args
= op_args
;
610 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
614 struct curve_to_args
*args
= op_args
;
616 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
620 struct move_to_args
*args
= op_args
;
622 cairo_line_to(cr
, args
->x
, args
->y
);
626 struct move_to_args
*args
= op_args
;
628 cairo_rel_line_to(cr
, args
->x
, args
->y
);
632 struct move_to_args
*args
= op_args
;
634 cairo_move_to(cr
, args
->x
, args
->y
);
638 struct move_to_args
*args
= op_args
;
640 cairo_rel_move_to(cr
, args
->x
, args
->y
);
644 cairo_close_path(cr
);
646 case SET_SOURCE_RGBA
: {
647 struct set_source_rgba_args
*args
= op_args
;
649 gdk_cairo_set_source_rgba(cr
, &args
->color
);
653 struct set_dash_args
*args
= op_args
;
655 cairo_set_dash(cr
, args
->dashes
, args
->num_dashes
, 0);
659 struct set_line_cap_args
*args
= op_args
;
661 cairo_set_line_cap(cr
, args
->line_cap
);
664 case SET_LINE_JOIN
: {
665 struct set_line_join_args
*args
= op_args
;
667 cairo_set_line_join(cr
, args
->line_join
);
670 case SET_LINE_WIDTH
: {
671 struct set_line_width_args
*args
= op_args
;
673 cairo_set_line_width(cr
, args
->width
);
680 cairo_fill_preserve(cr
);
685 case STROKE_PRESERVE
:
686 cairo_stroke_preserve(cr
);
689 struct show_text_args
*args
= op_args
;
691 cairo_show_text(cr
, args
->text
);
694 case SET_FONT_SIZE
: {
695 struct set_font_size_args
*args
= op_args
;
697 cairo_set_font_size(cr
, args
->size
);
707 set_draw_op(struct draw_op
*op
, const char *action
, const char *data
)
709 if (eql(action
, "rectangle")) {
710 struct rectangle_args
*args
;
712 if ((args
= malloc(sizeof(*args
))) == NULL
)
716 if (sscanf(data
, "%u %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->width
, &args
->height
) != 5)
718 } else if (eql(action
, "arc")) {
719 struct arc_args
*args
;
722 if ((args
= malloc(sizeof(*args
))) == NULL
)
726 if (sscanf(data
, "%u %lf %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->radius
, °1
, °2
) != 6)
728 args
->angle1
= deg1
* (M_PI
/ 180.);
729 args
->angle2
= deg2
* (M_PI
/ 180.);
730 } else if (eql(action
, "arc_negative")) {
731 struct arc_args
*args
;
734 if ((args
= malloc(sizeof(*args
))) == NULL
)
736 op
->op
= ARC_NEGATIVE
;
738 if (sscanf(data
, "%u %lf %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->radius
, °1
, °2
) != 6)
740 args
->angle1
= deg1
* (M_PI
/ 180.);
741 args
->angle2
= deg2
* (M_PI
/ 180.);
742 } else if (eql(action
, "curve_to")) {
743 struct curve_to_args
*args
;
745 if ((args
= malloc(sizeof(*args
))) == NULL
)
749 if (sscanf(data
, "%u %lf %lf %lf %lf %lf %lf", &op
->id
, &args
->x1
, &args
->y1
, &args
->x2
, &args
->y2
, &args
->x3
, &args
->y3
) != 7)
751 } else if (eql(action
, "rel_curve_to")) {
752 struct curve_to_args
*args
;
754 if ((args
= malloc(sizeof(*args
))) == NULL
)
756 op
->op
= REL_CURVE_TO
;
758 if (sscanf(data
, "%u %lf %lf %lf %lf %lf %lf", &op
->id
, &args
->x1
, &args
->y1
, &args
->x2
, &args
->y2
, &args
->x3
, &args
->y3
) != 7)
760 } else if (eql(action
, "line_to")) {
761 struct move_to_args
*args
;
763 if ((args
= malloc(sizeof(*args
))) == NULL
)
767 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
769 } else if (eql(action
, "rel_line_to")) {
770 struct move_to_args
*args
;
772 if ((args
= malloc(sizeof(*args
))) == NULL
)
774 op
->op
= REL_LINE_TO
;
776 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
778 } else if (eql(action
, "move_to")) {
779 struct move_to_args
*args
;
781 if ((args
= malloc(sizeof(*args
))) == NULL
)
785 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
787 } else if (eql(action
, "rel_move_to")) {
788 struct move_to_args
*args
;
790 if ((args
= malloc(sizeof(*args
))) == NULL
)
792 op
->op
= REL_MOVE_TO
;
794 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
796 } else if (eql(action
, "close_path")) {
798 if (sscanf(data
, "%u", &op
->id
) != 1)
801 } else if (eql(action
, "set_source_rgba")) {
802 struct set_source_rgba_args
*args
;
805 if ((args
= malloc(sizeof(*args
))) == NULL
)
807 op
->op
= SET_SOURCE_RGBA
;
809 if ((sscanf(data
, "%u %n", &op
->id
, &c_start
) < 1))
811 gdk_rgba_parse(&args
->color
, data
+ c_start
);
812 } else if (eql(action
, "set_dash")) {
813 struct set_dash_args
*args
;
815 char data1
[strlen(data
) + 1];
819 if (sscanf(data1
, "%u %n", &op
->id
, &d_start
) < 1)
821 next
= end
= data1
+ d_start
;
827 } while (next
!= end
);
828 if ((args
= malloc(sizeof(*args
) + n
* sizeof(args
->dashes
[0]))) == NULL
)
832 args
->num_dashes
= n
;
833 for (i
= 0, next
= data1
+ d_start
; i
< n
; i
++, next
= end
) {
834 args
->dashes
[i
] = strtod(next
, &end
);
836 } else if (eql(action
, "set_line_cap")) {
837 struct set_line_cap_args
*args
;
840 if ((args
= malloc(sizeof(*args
))) == NULL
)
842 op
->op
= SET_LINE_CAP
;
844 if (sscanf(data
, "%u %6s", &op
->id
, str
) != 2)
846 if (eql(str
, "butt"))
847 args
->line_cap
= CAIRO_LINE_CAP_BUTT
;
848 else if (eql(str
, "round"))
849 args
->line_cap
= CAIRO_LINE_CAP_ROUND
;
850 else if (eql(str
, "square"))
851 args
->line_cap
= CAIRO_LINE_CAP_SQUARE
;
854 } else if (eql(action
, "set_line_join")) {
855 struct set_line_join_args
*args
;
858 if ((args
= malloc(sizeof(*args
))) == NULL
)
860 op
->op
= SET_LINE_JOIN
;
862 if (sscanf(data
, "%u %5s", &op
->id
, str
) != 2)
864 if (eql(str
, "miter"))
865 args
->line_join
= CAIRO_LINE_JOIN_MITER
;
866 else if (eql(str
, "round"))
867 args
->line_join
= CAIRO_LINE_JOIN_ROUND
;
868 else if (eql(str
, "bevel"))
869 args
->line_join
= CAIRO_LINE_JOIN_BEVEL
;
872 } else if (eql(action
, "set_line_width")) {
873 struct set_line_width_args
*args
;
875 if ((args
= malloc(sizeof(*args
))) == NULL
)
877 op
->op
= SET_LINE_WIDTH
;
879 if (sscanf(data
, "%u %lf", &op
->id
, &args
->width
) != 2)
881 } else if (eql(action
, "fill")) {
883 if (sscanf(data
, "%u", &op
->id
) != 1)
886 } else if (eql(action
, "fill_preserve")) {
887 op
->op
= FILL_PRESERVE
;
888 if (sscanf(data
, "%u", &op
->id
) != 1)
891 } else if (eql(action
, "stroke")) {
893 if (sscanf(data
, "%u", &op
->id
) != 1)
896 } else if (eql(action
, "stroke_preserve")) {
897 op
->op
= STROKE_PRESERVE
;
898 if (sscanf(data
, "%u", &op
->id
) != 1)
901 } else if (eql(action
, "show_text")) {
902 struct show_text_args
*args
;
905 if (sscanf(data
, "%u %n", &op
->id
, &start
) < 1)
907 len
= strlen(data
+ start
) + 1;
908 if ((args
= malloc(sizeof(*args
) + len
* sizeof(args
->text
[0]))) == NULL
)
912 args
->len
= len
; /* not used */
913 strncpy(args
->text
, (data
+ start
), len
);
914 } else if (eql(action
, "set_font_size")) {
915 struct set_font_size_args
*args
;
917 if ((args
= malloc(sizeof(*args
))) == NULL
)
919 op
->op
= SET_FONT_SIZE
;
921 if (sscanf(data
, "%u %lf", &op
->id
, &args
->size
) != 2)
929 * Add another element to widget's "draw_ops" list
932 ins_draw_op(GObject
*widget
, const char *action
, const char *data
)
934 struct draw_op
*op
, *draw_ops
, *last_op
;
936 if ((op
= malloc(sizeof(*op
))) == NULL
)
939 if (!set_draw_op(op
, action
, data
)) {
944 if ((draw_ops
= g_object_get_data(widget
, "draw_ops")) == NULL
) {
945 g_object_set_data(widget
, "draw_ops", op
);
948 for (last_op
= draw_ops
; last_op
->next
!= NULL
; last_op
= last_op
->next
);
955 * Remove all elements with the given id from widget's "draw_ops" list
958 rem_draw_op(GObject
*widget
, const char *data
)
960 struct draw_op
*op
, *next_op
;
963 if (sscanf(data
, "%u", &id
) != 1)
965 op
= g_object_get_data(widget
, "draw_ops");
969 if (op
->prev
== NULL
)
970 g_object_set_data(widget
, "draw_ops", next_op
);
981 * Callback that draws on a GtkDrawingArea
984 cb_draw(GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
989 for (op
= g_object_get_data(G_OBJECT(widget
), "draw_ops");
992 draw(cr
, op
->op
, op
->op_args
);
997 * Change the style of the widget passed
1000 update_widget_style(GObject
*obj
, const char *name
,
1001 const char *data
, const char *whole_msg
, GType type
)
1003 GtkStyleContext
*context
;
1004 GtkStyleProvider
*style_provider
;
1006 const char *prefix
= "* {", *suffix
= "}";
1012 style_provider
= g_object_get_data(obj
, "style_provider");
1013 sz
= strlen(prefix
) + strlen(suffix
) + strlen(data
) + 1;
1014 context
= gtk_widget_get_style_context(GTK_WIDGET(obj
));
1015 gtk_style_context_remove_provider(context
, style_provider
);
1016 if ((style_decl
= malloc(sz
)) == NULL
)
1018 strcpy(style_decl
, prefix
);
1019 strcat(style_decl
, data
);
1020 strcat(style_decl
, suffix
);
1021 gtk_style_context_add_provider(context
, style_provider
,
1022 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
1023 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(style_provider
),
1024 style_decl
, -1, NULL
);
1029 * Update various kinds of widgets according to the respective action
1033 update_button(GObject
*obj
, const char *action
,
1034 const char *data
, const char *whole_msg
, GType type
)
1036 if (eql(action
, "set_label"))
1037 gtk_button_set_label(GTK_BUTTON(obj
), data
);
1039 ign_cmd(type
, whole_msg
);
1043 update_calendar(GObject
*obj
, const char *action
,
1044 const char *data
, const char *whole_msg
, GType type
)
1046 GtkCalendar
*calendar
= GTK_CALENDAR(obj
);
1047 int year
= 0, month
= 0, day
= 0;
1049 if (eql(action
, "select_date")) {
1050 sscanf(data
, "%d-%d-%d", &year
, &month
, &day
);
1051 if (month
> -1 && month
<= 11 && day
> 0 && day
<= 31) {
1052 gtk_calendar_select_month(calendar
, --month
, year
);
1053 gtk_calendar_select_day(calendar
, day
);
1055 ign_cmd(type
, whole_msg
);
1056 } else if (eql(action
, "mark_day")) {
1057 day
= strtol(data
, NULL
, 10);
1058 if (day
> 0 && day
<= 31)
1059 gtk_calendar_mark_day(calendar
, day
);
1061 ign_cmd(type
, whole_msg
);
1062 } else if (eql(action
, "clear_marks"))
1063 gtk_calendar_clear_marks(calendar
);
1065 ign_cmd(type
, whole_msg
);
1069 update_color_button(GObject
*obj
, const char *action
,
1070 const char *data
, const char *whole_msg
, GType type
)
1074 if (eql(action
, "set_color")) {
1075 gdk_rgba_parse(&color
, data
);
1076 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
1078 ign_cmd(type
, whole_msg
);
1082 update_combo_box_text(GObject
*obj
, const char *action
,
1083 const char *data
, const char *whole_msg
, GType type
)
1085 GtkComboBoxText
*combobox
= GTK_COMBO_BOX_TEXT(obj
);
1086 char data1
[strlen(data
) + 1];
1088 strcpy(data1
, data
);
1089 if (eql(action
, "prepend_text"))
1090 gtk_combo_box_text_prepend_text(combobox
, data1
);
1091 else if (eql(action
, "append_text"))
1092 gtk_combo_box_text_append_text(combobox
, data1
);
1093 else if (eql(action
, "remove"))
1094 gtk_combo_box_text_remove(combobox
, strtol(data1
, NULL
, 10));
1095 else if (eql(action
, "insert_text")) {
1096 char *position
= strtok(data1
, WHITESPACE
);
1097 char *text
= strtok(NULL
, WHITESPACE
);
1098 gtk_combo_box_text_insert_text(combobox
, strtol(position
, NULL
, 10), text
);
1100 ign_cmd(type
, whole_msg
);
1104 update_frame(GObject
*obj
, const char *action
,
1105 const char *data
, const char *whole_msg
, GType type
)
1107 if (eql(action
, "set_label"))
1108 gtk_frame_set_label(GTK_FRAME(obj
), data
);
1110 ign_cmd(type
, whole_msg
);
1114 update_scrolled_window(GObject
*obj
, const char *action
,
1115 const char *data
, const char *whole_msg
, GType type
)
1117 GtkScrolledWindow
*window
= GTK_SCROLLED_WINDOW(obj
);
1118 GtkAdjustment
*hadj
= gtk_scrolled_window_get_hadjustment(window
);
1119 GtkAdjustment
*vadj
= gtk_scrolled_window_get_vadjustment(window
);
1122 if (eql(action
, "hscroll") && sscanf(data
, "%lf", &d0
) == 1)
1123 gtk_adjustment_set_value(hadj
, d0
);
1124 else if (eql(action
, "vscroll") && sscanf(data
, "%lf", &d0
) == 1)
1125 gtk_adjustment_set_value(vadj
, d0
);
1126 else if (eql(action
, "hscroll_to_range") &&
1127 sscanf(data
, "%lf %lf", &d0
, &d1
) == 2)
1128 gtk_adjustment_clamp_page(hadj
, d0
, d1
);
1129 else if (eql(action
, "vscroll_to_range") &&
1130 sscanf(data
, "%lf %lf", &d0
, &d1
) == 2)
1131 gtk_adjustment_clamp_page(vadj
, d0
, d1
);
1133 ign_cmd(type
, whole_msg
);
1137 update_drawing_area(GObject
*obj
, const char *action
,
1138 const char *data
, const char *whole_msg
, GType type
)
1140 if (eql(action
, "remove")) {
1141 if (!rem_draw_op(obj
, data
))
1142 ign_cmd(type
, whole_msg
);
1143 } else if (eql(action
, "refresh")) {
1144 gint width
= gtk_widget_get_allocated_width(GTK_WIDGET(obj
));
1145 gint height
= gtk_widget_get_allocated_height(GTK_WIDGET(obj
));
1147 gtk_widget_queue_draw_area(GTK_WIDGET(obj
), 0, 0, width
, height
);
1148 } else if (ins_draw_op(obj
, action
, data
));
1150 ign_cmd(type
, whole_msg
);
1154 update_entry(GObject
*obj
, const char *action
,
1155 const char *data
, const char *whole_msg
, GType type
)
1157 GtkEntry
*entry
= GTK_ENTRY(obj
);
1159 if (eql(action
, "set_text"))
1160 gtk_entry_set_text(entry
, data
);
1161 else if (eql(action
, "set_placeholder_text"))
1162 gtk_entry_set_placeholder_text(entry
, data
);
1164 ign_cmd(type
, whole_msg
);
1168 update_label(GObject
*obj
, const char *action
,
1169 const char *data
, const char *whole_msg
, GType type
)
1171 if (eql(action
, "set_text"))
1172 gtk_label_set_text(GTK_LABEL(obj
), data
);
1174 ign_cmd(type
, whole_msg
);
1178 update_expander(GObject
*obj
, const char *action
,
1179 const char *data
, const char *whole_msg
, GType type
)
1181 GtkExpander
*expander
= GTK_EXPANDER(obj
);
1183 if (eql(action
, "set_expanded"))
1184 gtk_expander_set_expanded(expander
, strtol(data
, NULL
, 10));
1185 else if (eql(action
, "set_label"))
1186 gtk_expander_set_label(expander
, data
);
1188 ign_cmd(type
, whole_msg
);
1192 update_file_chooser_button(GObject
*obj
, const char *action
,
1193 const char *data
, const char *whole_msg
, GType type
)
1195 if (eql(action
, "set_filename"))
1196 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(obj
), data
);
1198 ign_cmd(type
, whole_msg
);
1202 update_file_chooser_dialog(GObject
*obj
, const char *action
,
1203 const char *data
, const char *whole_msg
, GType type
)
1205 GtkFileChooser
*chooser
= GTK_FILE_CHOOSER(obj
);
1207 if (eql(action
, "set_filename"))
1208 gtk_file_chooser_set_filename(chooser
, data
);
1209 else if (eql(action
, "set_current_name"))
1210 gtk_file_chooser_set_current_name(chooser
, data
);
1212 ign_cmd(type
, whole_msg
);
1216 update_font_button(GObject
*obj
, const char *action
,
1217 const char *data
, const char *whole_msg
, GType type
)
1219 GtkFontButton
*font_button
= GTK_FONT_BUTTON(obj
);
1221 if (eql(action
, "set_font_name"))
1222 gtk_font_button_set_font_name(font_button
, data
);
1224 ign_cmd(type
, whole_msg
);
1228 update_print_dialog(GObject
*obj
, const char *action
,
1229 const char *data
, const char *whole_msg
, GType type
)
1231 GtkPrintUnixDialog
*dialog
= GTK_PRINT_UNIX_DIALOG(obj
);
1233 GtkPrinter
*printer
;
1234 GtkPrintSettings
*settings
;
1235 GtkPageSetup
*page_setup
;
1238 if (eql(action
, "print")) {
1239 response_id
= gtk_dialog_run(GTK_DIALOG(dialog
));
1240 switch (response_id
) {
1241 case GTK_RESPONSE_OK
:
1242 printer
= gtk_print_unix_dialog_get_selected_printer(dialog
);
1243 settings
= gtk_print_unix_dialog_get_settings(dialog
);
1244 page_setup
= gtk_print_unix_dialog_get_page_setup(dialog
);
1245 job
= gtk_print_job_new(data
, printer
, settings
, page_setup
);
1246 if (gtk_print_job_set_source_file(job
, data
, NULL
))
1247 gtk_print_job_send(job
, NULL
, NULL
, NULL
);
1249 ign_cmd(type
, whole_msg
);
1250 g_clear_object(&settings
);
1251 g_clear_object(&job
);
1253 case GTK_RESPONSE_CANCEL
:
1254 case GTK_RESPONSE_DELETE_EVENT
:
1257 fprintf(stderr
, "%s sent an unexpected response id (%d)\n",
1258 widget_name(GTK_WIDGET(dialog
)), response_id
);
1261 gtk_widget_hide(GTK_WIDGET(dialog
));
1263 ign_cmd(type
, whole_msg
);
1267 update_image(GObject
*obj
, const char *action
,
1268 const char *data
, const char *whole_msg
, GType type
)
1270 GtkImage
*image
= GTK_IMAGE(obj
);
1273 gtk_image_get_icon_name(image
, NULL
, &size
);
1274 if (eql(action
, "set_from_file"))
1275 gtk_image_set_from_file(image
, data
);
1276 else if (eql(action
, "set_from_icon_name"))
1277 gtk_image_set_from_icon_name(image
, data
, size
);
1279 ign_cmd(type
, whole_msg
);
1283 update_notebook(GObject
*obj
, const char *action
,
1284 const char *data
, const char *whole_msg
, GType type
)
1286 if (eql(action
, "set_current_page"))
1287 gtk_notebook_set_current_page(GTK_NOTEBOOK(obj
), strtol(data
, NULL
, 10));
1289 ign_cmd(type
, whole_msg
);
1293 update_progress_bar(GObject
*obj
, const char *action
,
1294 const char *data
, const char *whole_msg
, GType type
)
1296 GtkProgressBar
*progressbar
= GTK_PROGRESS_BAR(obj
);
1298 if (eql(action
, "set_text"))
1299 gtk_progress_bar_set_text(progressbar
, *data
== '\0' ? NULL
: data
);
1300 else if (eql(action
, "set_fraction"))
1301 gtk_progress_bar_set_fraction(progressbar
, strtod(data
, NULL
));
1303 ign_cmd(type
, whole_msg
);
1307 update_scale(GObject
*obj
, const char *action
,
1308 const char *data
, const char *whole_msg
, GType type
)
1310 if (eql(action
, "set_value"))
1311 gtk_range_set_value(GTK_RANGE(obj
), strtod(data
, NULL
));
1313 ign_cmd(type
, whole_msg
);
1317 update_spinner(GObject
*obj
, const char *action
,
1318 const char *data
, const char *whole_msg
, GType type
)
1320 GtkSpinner
*spinner
= GTK_SPINNER(obj
);
1323 if (eql(action
, "start"))
1324 gtk_spinner_start(spinner
);
1325 else if (eql(action
, "stop"))
1326 gtk_spinner_stop(spinner
);
1328 ign_cmd(type
, whole_msg
);
1332 update_statusbar(GObject
*obj
, const char *action
,
1333 const char *data
, const char *whole_msg
, GType type
)
1335 GtkStatusbar
*statusbar
= GTK_STATUSBAR(obj
);
1339 if (eql(action
, "push"))
1340 gtk_statusbar_push(statusbar
, 0, data
);
1341 else if (eql(action
, "push_id") &&
1342 sscanf(data
, "%u%zn", &id
, &id_len
) == 1)
1343 gtk_statusbar_push(statusbar
, id
, data
+ id_len
+
1344 (size_t) (id_len
< strlen(data
) ? 1 : 0));
1345 else if (eql(action
, "pop"))
1346 gtk_statusbar_pop(statusbar
, 0);
1347 else if (eql(action
, "pop_id") &&
1348 sscanf(data
, "%u", &id
) == 1)
1349 gtk_statusbar_pop(statusbar
, id
);
1350 else if (eql(action
, "remove_all"))
1351 gtk_statusbar_remove_all(statusbar
, 0);
1353 ign_cmd(type
, whole_msg
);
1357 update_switch(GObject
*obj
, const char *action
,
1358 const char *data
, const char *whole_msg
, GType type
)
1360 if (eql(action
, "set_active"))
1361 gtk_switch_set_active(GTK_SWITCH(obj
), strtol(data
, NULL
, 10));
1363 ign_cmd(type
, whole_msg
);
1367 update_text_view(GObject
*obj
, const char *action
,
1368 const char *data
, const char *whole_msg
, GType type
)
1370 GtkTextView
*view
= GTK_TEXT_VIEW(obj
);
1371 GtkTextBuffer
*textbuf
= gtk_text_view_get_buffer(view
);
1374 if (eql(action
, "set_text"))
1375 gtk_text_buffer_set_text(textbuf
, data
, -1);
1376 else if (eql(action
, "delete")) {
1377 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
1378 gtk_text_buffer_delete(textbuf
, &a
, &b
);
1379 } else if (eql(action
, "insert_at_cursor"))
1380 gtk_text_buffer_insert_at_cursor(textbuf
, data
, -1);
1381 else if (eql(action
, "place_cursor")) {
1382 if (eql(data
, "end"))
1383 gtk_text_buffer_get_end_iter(textbuf
, &a
);
1384 else /* numeric offset */
1385 gtk_text_buffer_get_iter_at_offset(textbuf
, &a
,
1386 strtol(data
, NULL
, 10));
1387 gtk_text_buffer_place_cursor(textbuf
, &a
);
1388 } else if (eql(action
, "place_cursor_at_line")) {
1389 gtk_text_buffer_get_iter_at_line(textbuf
, &a
, strtol(data
, NULL
, 10));
1390 gtk_text_buffer_place_cursor(textbuf
, &a
);
1391 } else if (eql(action
, "scroll_to_cursor"))
1392 gtk_text_view_scroll_to_mark(view
, gtk_text_buffer_get_insert(textbuf
),
1394 else if (eql(action
, "save") && data
!= NULL
&&
1395 (save
= fopen(data
, "w")) != NULL
) {
1396 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
1397 save_msg(GTK_BUILDABLE(view
), "insert_at_cursor",
1398 gtk_text_buffer_get_text(textbuf
, &a
, &b
, TRUE
), NULL
);
1401 ign_cmd(type
, whole_msg
);
1405 update_toggle_button(GObject
*obj
, const char *action
,
1406 const char *data
, const char *whole_msg
, GType type
)
1408 if (eql(action
, "set_label"))
1409 gtk_button_set_label(GTK_BUTTON(obj
), data
);
1410 else if (eql(action
, "set_active"))
1411 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(obj
), strtol(data
, NULL
, 10));
1413 ign_cmd(type
, whole_msg
);
1417 * Check if s is a valid string representation of a GtkTreePath
1420 is_path_string(char *s
)
1423 strlen(s
) == strspn(s
, ":0123456789") &&
1424 strstr(s
, "::") == NULL
&&
1425 strcspn(s
, ":") > 0;
1429 tree_model_insert_before(GtkTreeModel
*model
, GtkTreeIter
*iter
,
1430 GtkTreeIter
*parent
, GtkTreeIter
*sibling
)
1432 if (GTK_IS_TREE_STORE(model
))
1433 gtk_tree_store_insert_before(GTK_TREE_STORE(model
),
1434 iter
, parent
, sibling
);
1435 else if (GTK_IS_LIST_STORE(model
))
1436 gtk_list_store_insert_before(GTK_LIST_STORE(model
),
1443 tree_model_insert_after(GtkTreeModel
*model
, GtkTreeIter
*iter
,
1444 GtkTreeIter
*parent
, GtkTreeIter
*sibling
)
1446 if (GTK_IS_TREE_STORE(model
))
1447 gtk_tree_store_insert_after(GTK_TREE_STORE(model
),
1448 iter
, parent
, sibling
);
1449 else if (GTK_IS_LIST_STORE(model
))
1450 gtk_list_store_insert_after(GTK_LIST_STORE(model
),
1457 tree_model_move_before(GtkTreeModel
*model
, GtkTreeIter
*iter
,
1458 GtkTreeIter
*position
)
1460 if (GTK_IS_TREE_STORE(model
))
1461 gtk_tree_store_move_before(GTK_TREE_STORE(model
), iter
, position
);
1462 else if (GTK_IS_LIST_STORE(model
))
1463 gtk_list_store_move_before(GTK_LIST_STORE(model
), iter
, position
);
1469 tree_model_remove(GtkTreeModel
*model
, GtkTreeIter
*iter
)
1471 if (GTK_IS_TREE_STORE(model
))
1472 gtk_tree_store_remove(GTK_TREE_STORE(model
), iter
);
1473 else if (GTK_IS_LIST_STORE(model
))
1474 gtk_list_store_remove(GTK_LIST_STORE(model
), iter
);
1480 tree_model_clear(GtkTreeModel
*model
)
1482 if (GTK_IS_TREE_STORE(model
))
1483 gtk_tree_store_clear(GTK_TREE_STORE(model
));
1484 else if (GTK_IS_LIST_STORE(model
))
1485 gtk_list_store_clear(GTK_LIST_STORE(model
));
1491 tree_model_set(GtkTreeModel
*model
, GtkTreeIter
*iter
, ...)
1496 if (GTK_IS_TREE_STORE(model
))
1497 gtk_tree_store_set_valist(GTK_TREE_STORE(model
), iter
, ap
);
1498 else if (GTK_IS_LIST_STORE(model
))
1499 gtk_list_store_set_valist(GTK_LIST_STORE(model
), iter
, ap
);
1506 * Create an empty row at path if it doesn't yet exist. Create older
1507 * siblings and parents as necessary.
1510 create_subtree(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
)
1512 GtkTreePath
*path_1
; /* path's predecessor */
1513 GtkTreeIter iter_1
; /* iter's predecessor */
1515 if (gtk_tree_model_get_iter(model
, iter
, path
))
1517 path_1
= gtk_tree_path_copy(path
);
1518 if (gtk_tree_path_prev(path_1
)) { /* need an older sibling */
1519 create_subtree(model
, path_1
, iter
);
1521 tree_model_insert_after(model
, iter
, NULL
, &iter_1
);
1522 } else if (gtk_tree_path_up(path_1
)) { /* need a parent */
1523 create_subtree(model
, path_1
, iter
);
1524 if (gtk_tree_path_get_depth(path_1
) == 0)
1525 /* first toplevel row */
1526 tree_model_insert_after(model
, iter
, NULL
, NULL
);
1527 else { /* first row in a lower level */
1529 tree_model_insert_after(model
, iter
, &iter_1
, NULL
);
1531 } /* neither prev nor up mean we're at the root of an empty tree */
1532 gtk_tree_path_free(path_1
);
1536 set_tree_view_cell(GtkTreeModel
*model
, GtkTreeIter
*iter
,
1537 const char *path_s
, int col
, const char *new_text
)
1539 GType col_type
= gtk_tree_model_get_column_type(model
, col
);
1546 path
= gtk_tree_path_new_from_string(path_s
);
1547 create_subtree(model
, path
, iter
);
1548 gtk_tree_path_free(path
);
1550 case G_TYPE_BOOLEAN
:
1559 n
= strtoll(new_text
, &endptr
, 10);
1560 if (!errno
&& endptr
!= new_text
) {
1561 tree_model_set(model
, iter
, col
, n
, -1);
1569 d
= strtod(new_text
, &endptr
);
1570 if (!errno
&& endptr
!= new_text
) {
1571 tree_model_set(model
, iter
, col
, d
, -1);
1576 tree_model_set(model
, iter
, col
, new_text
, -1);
1580 fprintf(stderr
, "column %d: %s not implemented\n",
1581 col
, g_type_name(col_type
));
1589 tree_view_set_cursor(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*col
)
1591 /* GTK+ 3.14 requires this. For 3.18, path = NULL */
1592 /* is just fine and this function need not exist. */
1594 path
= gtk_tree_path_new();
1595 gtk_tree_view_set_cursor(view
, path
, col
, false);
1599 update_tree_view(GObject
*obj
, const char *action
,
1600 const char *data
, const char *whole_msg
, GType type
)
1602 GtkTreeView
*view
= GTK_TREE_VIEW(obj
);
1603 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
1604 GtkTreeIter iter0
, iter1
;
1605 GtkTreePath
*path
= NULL
;
1606 bool iter0_valid
, iter1_valid
;
1607 char *tokens
, *arg0
, *arg1
, *arg2
;
1608 int col
= -1; /* invalid column number */
1610 if (!GTK_IS_LIST_STORE(model
) && !GTK_IS_TREE_STORE(model
))
1612 fprintf(stderr
, "missing model/");
1613 ign_cmd(type
, whole_msg
);
1616 if ((tokens
= malloc(strlen(data
) + 1)) == NULL
)
1618 strcpy(tokens
, data
);
1619 arg0
= strtok(tokens
, WHITESPACE
);
1620 arg1
= strtok(NULL
, WHITESPACE
);
1621 arg2
= strtok(NULL
, "");
1622 iter0_valid
= is_path_string(arg0
) &&
1623 gtk_tree_model_get_iter_from_string(model
, &iter0
, arg0
);
1624 iter1_valid
= is_path_string(arg1
) &&
1625 gtk_tree_model_get_iter_from_string(model
, &iter1
, arg1
);
1626 if (is_path_string(arg1
))
1627 col
= strtol(arg1
, NULL
, 10);
1628 if (eql(action
, "set")
1629 && col
> -1 && col
< gtk_tree_model_get_n_columns(model
) &&
1630 is_path_string(arg0
)) {
1631 if (set_tree_view_cell(model
, &iter0
, arg0
, col
, arg2
) == false)
1632 ign_cmd(type
, whole_msg
);
1633 } else if (eql(action
, "scroll") && iter0_valid
&& iter1_valid
) {
1634 path
= gtk_tree_path_new_from_string(arg0
);
1635 gtk_tree_view_scroll_to_cell (view
,
1637 gtk_tree_view_get_column(view
, col
),
1639 } else if (eql(action
, "expand") && iter0_valid
) {
1640 path
= gtk_tree_path_new_from_string(arg0
);
1641 gtk_tree_view_expand_row(view
, path
, false);
1642 } else if (eql(action
, "expand_all") && iter0_valid
) {
1643 path
= gtk_tree_path_new_from_string(arg0
);
1644 gtk_tree_view_expand_row(view
, path
, true);
1645 } else if (eql(action
, "expand_all") && arg0
== NULL
)
1646 gtk_tree_view_expand_all(view
);
1647 else if (eql(action
, "collapse") && iter0_valid
) {
1648 path
= gtk_tree_path_new_from_string(arg0
);
1649 gtk_tree_view_collapse_row(view
, path
);
1650 } else if (eql(action
, "collapse") && arg0
== NULL
)
1651 gtk_tree_view_collapse_all(view
);
1652 else if (eql(action
, "set_cursor") && iter0_valid
) {
1653 path
= gtk_tree_path_new_from_string(arg0
);
1654 tree_view_set_cursor(view
, path
, NULL
);
1655 } else if (eql(action
, "set_cursor") && arg0
== NULL
) {
1656 tree_view_set_cursor(view
, NULL
, NULL
);
1657 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(view
));
1658 } else if (eql(action
, "insert_row") && eql(arg0
, "end"))
1659 tree_model_insert_before(model
, &iter1
, NULL
, NULL
);
1660 else if (eql(action
, "insert_row") && iter0_valid
&& eql(arg1
, "as_child"))
1661 tree_model_insert_after(model
, &iter1
, &iter0
, NULL
);
1662 else if (eql(action
, "insert_row") && iter0_valid
)
1663 tree_model_insert_before(model
, &iter1
, NULL
, &iter0
);
1664 else if (eql(action
, "move_row") && iter0_valid
&& eql(arg1
, "end"))
1665 tree_model_move_before(model
, &iter0
, NULL
);
1666 else if (eql(action
, "move_row") && iter0_valid
&& iter1_valid
)
1667 tree_model_move_before(model
, &iter0
, &iter1
);
1668 else if (eql(action
, "remove_row") && iter0_valid
)
1669 tree_model_remove(model
, &iter0
);
1670 else if (eql(action
, "clear") && arg0
== NULL
) {
1671 tree_view_set_cursor(view
, NULL
, NULL
);
1672 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(view
));
1673 tree_model_clear(model
);
1674 } else if (eql(action
, "save") && arg0
!= NULL
&&
1675 (save
= fopen(arg0
, "w")) != NULL
) {
1676 gtk_tree_model_foreach(model
, (GtkTreeModelForeachFunc
)save_tree_row_msg
, view
);
1679 ign_cmd(type
, whole_msg
);
1681 gtk_tree_path_free(path
);
1685 update_socket(GObject
*obj
, const char *action
,
1686 const char *data
, const char *whole_msg
, GType type
)
1688 GtkSocket
*socket
= GTK_SOCKET(obj
);
1693 if (eql(action
, "id")) {
1694 id
= gtk_socket_get_id(socket
);
1695 snprintf(str
, BUFLEN
, "%lu", id
);
1696 send_msg(GTK_BUILDABLE(socket
), "id", str
, NULL
);
1698 ign_cmd(type
, whole_msg
);
1702 update_window(GObject
*obj
, const char *action
,
1703 const char *data
, const char *whole_msg
, GType type
)
1705 GtkWindow
*window
= GTK_WINDOW(obj
);
1708 if (eql(action
, "set_title"))
1709 gtk_window_set_title(window
, data
);
1710 else if (eql(action
, "fullscreen"))
1711 gtk_window_fullscreen(window
);
1712 else if (eql(action
, "unfullscreen"))
1713 gtk_window_unfullscreen(window
);
1714 else if (eql(action
, "resize")) {
1715 if (sscanf(data
, "%d %d", &x
, &y
) != 2)
1716 gtk_window_get_default_size(window
, &x
, &y
);
1717 gtk_window_resize(window
, x
, y
);
1718 } else if (eql(action
, "move")) {
1719 if (sscanf(data
, "%d %d", &x
, &y
) == 2)
1720 gtk_window_move(window
, x
, y
);
1722 ign_cmd(type
, whole_msg
);
1724 ign_cmd(type
, whole_msg
);
1728 update_sensitivity(GObject
*obj
, const char *action
,
1729 const char *data
, const char *whole_msg
, GType type
)
1734 gtk_widget_set_sensitive(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
1738 update_visibility(GObject
*obj
, const char *action
,
1739 const char *data
, const char *whole_msg
, GType type
)
1744 gtk_widget_set_visible(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
1748 update_focus(GObject
*obj
, const char *action
,
1749 const char *data
, const char *whole_msg
, GType type
)
1753 if (gtk_widget_get_can_focus(GTK_WIDGET(obj
)))
1754 gtk_widget_grab_focus(GTK_WIDGET(obj
));
1756 ign_cmd(type
, whole_msg
);
1760 update_size_request(GObject
*obj
, const char *action
,
1761 const char *data
, const char *whole_msg
, GType type
)
1768 if (sscanf(data
, "%d %d", &x
, &y
) == 2)
1769 gtk_widget_set_size_request(GTK_WIDGET(obj
), x
, y
);
1771 gtk_widget_set_size_request(GTK_WIDGET(obj
), -1, -1);
1775 update_tooltip_text(GObject
*obj
, const char *action
,
1776 const char *data
, const char *whole_msg
, GType type
)
1781 gtk_widget_set_tooltip_text(GTK_WIDGET(obj
), data
);
1785 fake_ui_activity(GObject
*obj
, const char *action
,
1786 const char *data
, const char *whole_msg
, GType type
)
1790 if (!GTK_IS_WIDGET(obj
))
1791 ign_cmd(type
, whole_msg
);
1792 else if (GTK_IS_ENTRY(obj
) || GTK_IS_SPIN_BUTTON(obj
))
1793 cb(GTK_BUILDABLE(obj
), "text");
1794 else if (GTK_IS_SCALE(obj
))
1795 cb(GTK_BUILDABLE(obj
), "value");
1796 else if (GTK_IS_CALENDAR(obj
))
1797 cb(GTK_BUILDABLE(obj
), "clicked");
1798 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
1799 cb(GTK_BUILDABLE(obj
), "file");
1800 else if (!gtk_widget_activate(GTK_WIDGET(obj
)))
1801 ign_cmd(type
, whole_msg
);
1805 main_quit(GObject
*obj
, const char *action
,
1806 const char *data
, const char *whole_msg
, GType type
)
1818 complain(GObject
*obj
, const char *action
,
1819 const char *data
, const char *whole_msg
, GType type
)
1825 ign_cmd(type
, whole_msg
);
1829 * Parse command pointed to by ud, and act on ui accordingly
1832 update_ui(struct ui_data
*ud
)
1834 (ud
->fn
)(ud
->obj
, ud
->action
, ud
->data
, ud
->msg
, ud
->type
);
1844 * Keep track of loading files to avoid recursive loading of the same
1845 * file. If filename = NULL, forget the most recently remembered file.
1848 remember_loading_file(char *filename
)
1850 static char *filenames
[BUFLEN
];
1851 static size_t latest
= 0;
1854 if (filename
== NULL
) { /* pop */
1860 for (i
= 1; i
<= latest
; i
++)
1861 if (eql(filename
, filenames
[i
]))
1863 if (latest
> BUFLEN
-2)
1865 filenames
[++latest
] = filename
;
1871 * Parse command pointed to by ud, and act on ui accordingly; post
1872 * semaphore ud.msg_digested if done. Runs once per command inside
1876 update_ui_in(struct ui_data
*ud
)
1879 sem_post(&ud
->msg_digested
);
1880 return G_SOURCE_REMOVE
;
1884 * Read lines from stream cmd and perform appropriate actions on the
1888 digest_msg(FILE *cmd
)
1891 FILE *load
; /* restoring user data */
1894 sem_init(&ud
.msg_digested
, 0, 0);
1896 char first_char
= '\0';
1897 size_t msg_size
= 32;
1898 int name_start
= 0, name_end
= 0;
1899 int action_start
= 0, action_end
= 0;
1902 ud
.type
= G_TYPE_INVALID
;
1905 if ((ud
.msg
= malloc(msg_size
)) == NULL
)
1907 pthread_cleanup_push((void(*)(void *))free_at
, &ud
.msg
);
1908 pthread_testcancel();
1909 read_buf(cmd
, &ud
.msg
, &msg_size
);
1910 data_start
= strlen(ud
.msg
);
1911 if ((ud
.msg_tokens
= malloc(strlen(ud
.msg
) + 1)) == NULL
)
1913 pthread_cleanup_push((void(*)(void *))free_at
, &ud
.msg_tokens
);
1914 strcpy(ud
.msg_tokens
, ud
.msg
);
1915 sscanf(ud
.msg
, " %c", &first_char
);
1916 if (strlen(ud
.msg
) == 0 || first_char
== '#') /* comment */
1918 sscanf(ud
.msg_tokens
,
1919 " %n%*[0-9a-zA-Z_]%n:%n%*[0-9a-zA-Z_]%n%*1[ \t]%n",
1920 &name_start
, &name_end
, &action_start
, &action_end
, &data_start
);
1921 ud
.msg_tokens
[name_end
] = ud
.msg_tokens
[action_end
] = '\0';
1922 name
= ud
.msg_tokens
+ name_start
;
1923 ud
.action
= ud
.msg_tokens
+ action_start
;
1924 if (eql(ud
.action
, "main_quit")) {
1928 ud
.data
= ud
.msg_tokens
+ data_start
;
1929 if (eql(ud
.action
, "load") && strlen(ud
.data
) > 0 &&
1930 (load
= fopen(ud
.data
, "r")) != NULL
&&
1931 remember_loading_file(ud
.data
)) {
1934 remember_loading_file(NULL
);
1937 if ((ud
.obj
= (gtk_builder_get_object(builder
, name
))) == NULL
) {
1941 ud
.type
= G_TYPE_FROM_INSTANCE(ud
.obj
);
1942 if (eql(ud
.action
, "force"))
1943 ud
.fn
= fake_ui_activity
;
1944 else if (eql(ud
.action
, "set_sensitive"))
1945 ud
.fn
= update_sensitivity
;
1946 else if (eql(ud
.action
, "set_visible"))
1947 ud
.fn
= update_visibility
;
1948 else if (eql(ud
.action
, "set_size_request"))
1949 ud
.fn
= update_size_request
;
1950 else if (eql(ud
.action
, "set_tooltip_text"))
1951 ud
.fn
= update_tooltip_text
;
1952 else if (eql(ud
.action
, "grab_focus"))
1953 ud
.fn
= update_focus
;
1954 else if (eql(ud
.action
, "style")) {
1956 ud
.fn
= update_widget_style
;
1957 } else if (ud
.type
== GTK_TYPE_TREE_VIEW
)
1958 ud
.fn
= update_tree_view
;
1959 else if (ud
.type
== GTK_TYPE_DRAWING_AREA
)
1960 ud
.fn
= update_drawing_area
;
1961 else if (ud
.type
== GTK_TYPE_LABEL
)
1962 ud
.fn
= update_label
;
1963 else if (ud
.type
== GTK_TYPE_IMAGE
)
1964 ud
.fn
= update_image
;
1965 else if (ud
.type
== GTK_TYPE_TEXT_VIEW
)
1966 ud
.fn
= update_text_view
;
1967 else if (ud
.type
== GTK_TYPE_NOTEBOOK
)
1968 ud
.fn
= update_notebook
;
1969 else if (ud
.type
== GTK_TYPE_EXPANDER
)
1970 ud
.fn
= update_expander
;
1971 else if (ud
.type
== GTK_TYPE_FRAME
)
1972 ud
.fn
= update_frame
;
1973 else if (ud
.type
== GTK_TYPE_SCROLLED_WINDOW
)
1974 ud
.fn
= update_scrolled_window
;
1975 else if (ud
.type
== GTK_TYPE_BUTTON
)
1976 ud
.fn
= update_button
;
1977 else if (ud
.type
== GTK_TYPE_FILE_CHOOSER_DIALOG
)
1978 ud
.fn
= update_file_chooser_dialog
;
1979 else if (ud
.type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
1980 ud
.fn
= update_file_chooser_button
;
1981 else if (ud
.type
== GTK_TYPE_COLOR_BUTTON
)
1982 ud
.fn
= update_color_button
;
1983 else if (ud
.type
== GTK_TYPE_FONT_BUTTON
)
1984 ud
.fn
= update_font_button
;
1985 else if (ud
.type
== GTK_TYPE_PRINT_UNIX_DIALOG
)
1986 ud
.fn
= update_print_dialog
;
1987 else if (ud
.type
== GTK_TYPE_SWITCH
)
1988 ud
.fn
= update_switch
;
1989 else if (ud
.type
== GTK_TYPE_TOGGLE_BUTTON
|| ud
.type
== GTK_TYPE_RADIO_BUTTON
|| ud
.type
== GTK_TYPE_CHECK_BUTTON
)
1990 ud
.fn
= update_toggle_button
;
1991 else if (ud
.type
== GTK_TYPE_SPIN_BUTTON
|| ud
.type
== GTK_TYPE_ENTRY
)
1992 ud
.fn
= update_entry
;
1993 else if (ud
.type
== GTK_TYPE_SCALE
)
1994 ud
.fn
= update_scale
;
1995 else if (ud
.type
== GTK_TYPE_PROGRESS_BAR
)
1996 ud
.fn
= update_progress_bar
;
1997 else if (ud
.type
== GTK_TYPE_SPINNER
)
1998 ud
.fn
= update_spinner
;
1999 else if (ud
.type
== GTK_TYPE_COMBO_BOX_TEXT
)
2000 ud
.fn
= update_combo_box_text
;
2001 else if (ud
.type
== GTK_TYPE_STATUSBAR
)
2002 ud
.fn
= update_statusbar
;
2003 else if (ud
.type
== GTK_TYPE_CALENDAR
)
2004 ud
.fn
= update_calendar
;
2005 else if (ud
.type
== GTK_TYPE_SOCKET
)
2006 ud
.fn
= update_socket
;
2007 else if (ud
.type
== GTK_TYPE_WINDOW
)
2008 ud
.fn
= update_window
;
2012 pthread_testcancel();
2013 gdk_threads_add_timeout(1, (GSourceFunc
)update_ui_in
, &ud
);
2014 sem_wait(&ud
.msg_digested
);
2016 pthread_cleanup_pop(1); /* free ud.msg_tokens */
2017 pthread_cleanup_pop(1); /* free ud.msg */
2023 * Create a fifo if necessary, and open it. Give up if the file
2024 * exists but is not a fifo
2027 fifo(const char *name
, const char *mode
)
2036 if (S_ISFIFO(sb
.st_mode
)) {
2037 if (chmod(name
, 0600) != 0)
2038 bye(EXIT_FAILURE
, stderr
,
2039 "using pre-existing fifo %s: %s\n",
2040 name
, strerror(errno
));
2041 } else if (mkfifo(name
, 0600) != 0)
2042 bye(EXIT_FAILURE
, stderr
,
2043 "making fifo %s: %s\n", name
, strerror(errno
));
2051 if ((fd
= open(name
, O_RDWR
| O_NONBLOCK
)) < 0)
2052 bye(EXIT_FAILURE
, stderr
,
2053 "opening fifo %s (%s): %s\n",
2054 name
, mode
, strerror(errno
));
2055 s
= fdopen(fd
, "r");
2063 s
= fopen(name
, "w+");
2070 bye(EXIT_FAILURE
, stderr
, "opening fifo %s (%s): %s\n",
2071 name
, mode
, strerror(errno
));
2073 setvbuf(s
, NULL
, bufmode
, 0);
2078 * Remove suffix from name; find the object named like this
2081 obj_sans_suffix(const char *suffix
, const char *name
)
2084 char str
[BUFLEN
+ 1] = {'\0'};
2086 str_l
= suffix
- name
;
2087 strncpy(str
, name
, str_l
< BUFLEN
? str_l
: BUFLEN
);
2088 return gtk_builder_get_object(builder
, str
);
2092 * Callback that forwards a modification of a tree view cell to the
2096 cb_tree_model_edit(GtkCellRenderer
*renderer
, const gchar
*path_s
,
2097 const gchar
*new_text
, gpointer model
)
2103 gtk_tree_model_get_iter_from_string(model
, &iter
, path_s
);
2104 view
= g_object_get_data(G_OBJECT(renderer
), "tree_view");
2105 col
= g_object_get_data(G_OBJECT(renderer
), "col_number");
2106 set_tree_view_cell(model
, &iter
, path_s
, GPOINTER_TO_INT(col
),
2108 send_tree_cell_msg_by(send_msg
, model
, path_s
, &iter
, GPOINTER_TO_INT(col
),
2109 GTK_BUILDABLE(view
));
2113 cb_tree_model_toggle(GtkCellRenderer
*renderer
, gchar
*path_s
, gpointer model
)
2119 gtk_tree_model_get_iter_from_string(model
, &iter
, path_s
);
2120 col
= g_object_get_data(G_OBJECT(renderer
), "col_number");
2121 gtk_tree_model_get(model
, &iter
, col
, &toggle_state
, -1);
2122 set_tree_view_cell(model
, &iter
, path_s
, GPOINTER_TO_INT(col
),
2123 toggle_state
? "0" : "1");
2127 * Attach to renderer key "col_number". Associate "col_number" with
2128 * the corresponding column number in the underlying model.
2129 * Due to what looks like a gap in the GTK API, renderer id and column
2130 * number are taken directly from the XML .ui file.
2133 tree_view_column_get_renderer_column(const char *ui_file
, GtkTreeViewColumn
*t_col
,
2134 int n
, GtkCellRenderer
**renderer
)
2137 xmlXPathContextPtr xpath_ctx
;
2138 xmlXPathObjectPtr xpath_obj
;
2139 xmlNodeSetPtr nodes
;
2142 xmlChar
*xpath
, *renderer_name
= NULL
, *m_col_s
= NULL
;
2143 char *xpath_base1
= "//object[@class=\"GtkTreeViewColumn\" and @id=\"";
2144 const char *xpath_id
= widget_name(t_col
);
2145 char *xpath_base2
= "\"]/child[";
2146 size_t xpath_n_len
= 3; /* Big Enough (TM) */
2147 char *xpath_base3
= "]/object[@class=\"GtkCellRendererText\""
2148 " or @class=\"GtkCellRendererToggle\"]/";
2149 char *xpath_text_col
= "../attributes/attribute[@name=\"text\""
2150 " or @name=\"active\"]";
2151 char *xpath_renderer_id
= "/@id";
2155 if ((doc
= xmlParseFile(ui_file
)) == NULL
)
2157 if ((xpath_ctx
= xmlXPathNewContext(doc
)) == NULL
) {
2161 xpath_len
= 2 * (strlen(xpath_base1
) + strlen(xpath_id
) +
2162 strlen(xpath_base2
) + xpath_n_len
+
2163 strlen(xpath_base3
))
2165 + strlen(xpath_text_col
) + strlen(xpath_renderer_id
)
2167 if ((xpath
= malloc(xpath_len
)) == NULL
) {
2171 snprintf((char *)xpath
, xpath_len
, "%s%s%s%d%s%s|%s%s%s%d%s%s",
2172 xpath_base1
, xpath_id
, xpath_base2
, n
, xpath_base3
, xpath_text_col
,
2173 xpath_base1
, xpath_id
, xpath_base2
, n
, xpath_base3
, xpath_renderer_id
);
2174 if ((xpath_obj
= xmlXPathEvalExpression(xpath
, xpath_ctx
)) == NULL
) {
2175 xmlXPathFreeContext(xpath_ctx
);
2180 if ((nodes
= xpath_obj
->nodesetval
) != NULL
) {
2181 for (i
= 0; i
< nodes
->nodeNr
; ++i
) {
2182 if (nodes
->nodeTab
[i
]->type
== XML_ELEMENT_NODE
) {
2183 cur
= nodes
->nodeTab
[i
];
2184 m_col_s
= xmlNodeGetContent(cur
);
2186 cur
= nodes
->nodeTab
[i
];
2187 renderer_name
= xmlNodeGetContent(cur
);
2191 if (renderer_name
) {
2192 *renderer
= GTK_CELL_RENDERER(
2193 gtk_builder_get_object(builder
, (char *)renderer_name
));
2195 g_object_set_data(G_OBJECT(*renderer
), "col_number",
2196 GINT_TO_POINTER(strtol((char *)m_col_s
,
2201 xmlFree(renderer_name
);
2203 xmlXPathFreeObject(xpath_obj
);
2204 xmlXPathFreeContext(xpath_ctx
);
2211 connect_widget_signals(gpointer
*obj
, char *ui_file
)
2213 const char *name
= NULL
;
2214 char *suffix
= NULL
;
2216 GType type
= G_TYPE_INVALID
;
2218 type
= G_TYPE_FROM_INSTANCE(obj
);
2219 if (GTK_IS_BUILDABLE(obj
))
2220 name
= widget_name(obj
);
2221 if (type
== GTK_TYPE_TREE_VIEW_COLUMN
) {
2222 gboolean editable
= FALSE
;
2224 GtkTreeModel
*model
;
2225 GtkCellRenderer
*renderer
;
2228 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
2229 view
= GTK_TREE_VIEW(gtk_tree_view_column_get_tree_view(GTK_TREE_VIEW_COLUMN(obj
)));
2230 model
= gtk_tree_view_get_model(view
);
2232 if (!tree_view_column_get_renderer_column(ui_file
, GTK_TREE_VIEW_COLUMN(obj
), i
, &renderer
))
2234 g_object_set_data(G_OBJECT(renderer
), "tree_view", view
);
2235 if (GTK_IS_CELL_RENDERER_TEXT(renderer
)) {
2236 g_object_get(renderer
, "editable", &editable
, NULL
);
2238 g_signal_connect(renderer
, "edited", G_CALLBACK(cb_tree_model_edit
), model
);
2239 } else if (GTK_IS_CELL_RENDERER_TOGGLE(renderer
)) {
2240 g_object_get(renderer
, "activatable", &editable
, NULL
);
2242 g_signal_connect(renderer
, "toggled", G_CALLBACK(cb_tree_model_toggle
), model
);
2246 else if (type
== GTK_TYPE_BUTTON
) {
2247 /* Button associated with a GtkTextView. */
2248 if ((suffix
= strstr(name
, "_send_text")) != NULL
&&
2249 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(suffix
, name
)))
2250 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text
),
2251 gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
)));
2252 else if ((suffix
= strstr(name
, "_send_selection")) != NULL
&&
2253 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(suffix
, name
)))
2254 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text_selection
),
2255 gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
)));
2256 /* Buttons associated with (and part of) a GtkDialog.
2257 * (We shun response ids which could be returned from
2258 * gtk_dialog_run() because that would require the
2259 * user to define those response ids in Glade,
2261 else if ((suffix
= strstr(name
, "_cancel")) != NULL
&&
2262 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
)))
2263 if (eql(widget_name(obj2
), MAIN_WIN
))
2264 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_main_quit
), NULL
);
2266 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_widget_hide
), obj2
);
2267 else if ((suffix
= strstr(name
, "_ok")) != NULL
&&
2268 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
))) {
2269 if (GTK_IS_FILE_CHOOSER_DIALOG(obj2
))
2270 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(cb_send_file_chooser_dialog_selection
), GTK_FILE_CHOOSER(obj2
));
2271 else /* generic button */
2272 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
2273 if (eql(widget_name(obj2
), MAIN_WIN
))
2274 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_main_quit
), NULL
);
2276 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_widget_hide
), obj2
);
2277 } else if ((suffix
= strstr(name
, "_apply")) != NULL
&&
2278 GTK_IS_FILE_CHOOSER_DIALOG(obj2
= obj_sans_suffix(suffix
, name
)))
2279 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(cb_send_file_chooser_dialog_selection
), obj2
);
2280 else /* generic button */
2281 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
2282 } else if (GTK_IS_MENU_ITEM(obj
))
2283 if ((suffix
= strstr(name
, "_invoke")) != NULL
&&
2284 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
)))
2285 g_signal_connect_swapped(obj
, "activate", G_CALLBACK(gtk_widget_show
), obj2
);
2287 g_signal_connect(obj
, "activate", G_CALLBACK(cb
), "active");
2288 else if (GTK_IS_WINDOW(obj
))
2289 if (eql(name
, MAIN_WIN
))
2290 g_signal_connect_swapped(obj
, "delete-event", G_CALLBACK(gtk_main_quit
), NULL
);
2292 g_signal_connect(obj
, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
2293 else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
2294 g_signal_connect(obj
, "file-set", G_CALLBACK(cb
), "file");
2295 else if (type
== GTK_TYPE_COLOR_BUTTON
)
2296 g_signal_connect(obj
, "color-set", G_CALLBACK(cb
), "color");
2297 else if (type
== GTK_TYPE_FONT_BUTTON
)
2298 g_signal_connect(obj
, "font-set", G_CALLBACK(cb
), "font");
2299 else if (type
== GTK_TYPE_SWITCH
)
2300 g_signal_connect(obj
, "notify::active", G_CALLBACK(cb
), NULL
);
2301 else if (type
== GTK_TYPE_TOGGLE_BUTTON
|| type
== GTK_TYPE_RADIO_BUTTON
|| type
== GTK_TYPE_CHECK_BUTTON
)
2302 g_signal_connect(obj
, "toggled", G_CALLBACK(cb
), NULL
);
2303 else if (type
== GTK_TYPE_SPIN_BUTTON
|| type
== GTK_TYPE_ENTRY
)
2304 g_signal_connect(obj
, "changed", G_CALLBACK(cb
), "text");
2305 else if (type
== GTK_TYPE_SCALE
)
2306 g_signal_connect(obj
, "value-changed", G_CALLBACK(cb
), "value");
2307 else if (type
== GTK_TYPE_CALENDAR
) {
2308 g_signal_connect(obj
, "day-selected-double-click", G_CALLBACK(cb
), "doubleclicked");
2309 g_signal_connect(obj
, "day-selected", G_CALLBACK(cb
), "clicked");
2310 } else if (type
== GTK_TYPE_TREE_SELECTION
)
2311 g_signal_connect(obj
, "changed", G_CALLBACK(cb
), "clicked");
2312 else if (type
== GTK_TYPE_SOCKET
) {
2313 g_signal_connect(obj
, "plug-added", G_CALLBACK(cb
), "plug-added");
2314 g_signal_connect(obj
, "plug-removed", G_CALLBACK(cb_true
), "plug-removed");
2315 } else if (type
== GTK_TYPE_DRAWING_AREA
)
2316 g_signal_connect(obj
, "draw", G_CALLBACK(cb_draw
), NULL
);
2317 else if (type
== GTK_TYPE_EVENT_BOX
) {
2318 gtk_widget_set_can_focus(GTK_WIDGET(obj
), true);
2319 g_signal_connect(obj
, "button-press-event", G_CALLBACK(cb_event_box_button
), "button_press");
2320 g_signal_connect(obj
, "button-release-event", G_CALLBACK(cb_event_box_button
), "button_release");
2321 g_signal_connect(obj
, "motion-notify-event", G_CALLBACK(cb_event_box_motion
), "motion");
2322 g_signal_connect(obj
, "key-press-event", G_CALLBACK(cb_event_box_key
), "key_press");
2327 * We keep a style provider with each widget
2330 add_widget_style_provider(gpointer
*obj
, void *data
)
2332 GtkStyleContext
*context
;
2333 GtkCssProvider
*style_provider
;
2336 if (!GTK_IS_WIDGET(obj
))
2338 style_provider
= gtk_css_provider_new();
2339 context
= gtk_widget_get_style_context(GTK_WIDGET(obj
));
2340 gtk_style_context_add_provider(context
,
2341 GTK_STYLE_PROVIDER(style_provider
),
2342 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
2343 g_object_set_data(G_OBJECT(obj
), "style_provider", style_provider
);
2347 prepare_widgets(char *ui_file
)
2349 GSList
*objects
= NULL
;
2351 objects
= gtk_builder_get_objects(builder
);
2352 g_slist_foreach(objects
, (GFunc
)connect_widget_signals
, ui_file
);
2353 g_slist_foreach(objects
, (GFunc
)add_widget_style_provider
, NULL
);
2354 g_slist_free(objects
);
2358 main(int argc
, char *argv
[])
2361 char *in_fifo
= NULL
, *out_fifo
= NULL
, *ui_file
= NULL
;
2362 char *xid_s
= NULL
, xid_s2
[BUFLEN
];
2364 GtkWidget
*plug
, *body
;
2366 GError
*error
= NULL
;
2367 GObject
*main_window
= NULL
;
2368 FILE *in
= NULL
; /* command input */
2370 /* Disable runtime GLIB deprecation warnings: */
2371 setenv("G_ENABLE_DIAGNOSTIC", "0", 0);
2374 gtk_init(&argc
, &argv
);
2375 while ((opt
= getopt(argc
, argv
, "he:i:o:u:GV")) != -1) {
2377 case 'e': xid_s
= optarg
; break;
2378 case 'i': in_fifo
= optarg
; break;
2379 case 'o': out_fifo
= optarg
; break;
2380 case 'u': ui_file
= optarg
; break;
2381 case 'G': bye(EXIT_SUCCESS
, stdout
, "GTK+ v%d.%d.%d (running v%d.%d.%d)\n",
2382 GTK_MAJOR_VERSION
, GTK_MINOR_VERSION
, GTK_MICRO_VERSION
,
2383 gtk_get_major_version(), gtk_get_minor_version(),
2384 gtk_get_micro_version());
2386 case 'V': bye(EXIT_SUCCESS
, stdout
, "%s\n", VERSION
); break;
2387 case 'h': bye(EXIT_SUCCESS
, stdout
, USAGE
); break;
2389 default: bye(EXIT_FAILURE
, stderr
, USAGE
); break;
2392 if (argv
[optind
] != NULL
)
2393 bye(EXIT_FAILURE
, stderr
,
2394 "illegal parameter '%s'\n" USAGE
, argv
[optind
]);
2395 in
= fifo(in_fifo
, "r");
2396 out
= fifo(out_fifo
, "w");
2397 if (ui_file
== NULL
)
2398 ui_file
= "pipeglade.ui";
2399 builder
= gtk_builder_new();
2400 if (gtk_builder_add_from_file(builder
, ui_file
, &error
) == 0)
2401 bye(EXIT_FAILURE
, stderr
, "%s\n", error
->message
);
2402 pthread_create(&receiver
, NULL
, (void *(*)(void *))digest_msg
, in
);
2403 main_window
= gtk_builder_get_object(builder
, MAIN_WIN
);
2404 if (!GTK_IS_WINDOW(main_window
))
2405 bye(EXIT_FAILURE
, stderr
,
2406 "no toplevel window named \'" MAIN_WIN
"\'\n");
2408 LIBXML_TEST_VERSION
;
2409 prepare_widgets(ui_file
);
2410 if (xid_s
== NULL
) /* standalone */
2411 gtk_widget_show(GTK_WIDGET(main_window
));
2412 else { /* We're being XEmbedded */
2413 xid
= strtoul(xid_s
, NULL
, 10);
2414 snprintf(xid_s2
, BUFLEN
, "%lu", xid
);
2415 if (!eql(xid_s
, xid_s2
))
2416 bye(EXIT_FAILURE
, stderr
,
2417 "%s is not a valid XEmbed socket id\n", xid_s
);
2418 body
= gtk_bin_get_child(GTK_BIN(main_window
));
2419 gtk_container_remove(GTK_CONTAINER(main_window
), body
);
2420 plug
= gtk_plug_new(xid
);
2421 if (!gtk_plug_get_embedded(GTK_PLUG(plug
)))
2422 bye(EXIT_FAILURE
, stderr
,
2423 "unable to embed into XEmbed socket %s\n", xid_s
);
2424 gtk_container_add(GTK_CONTAINER(plug
), body
);
2425 gtk_widget_show(plug
);
2432 if (out
!= stdout
) {
2436 pthread_cancel(receiver
);
2437 pthread_join(receiver
, NULL
);