2 * Copyright (c) 2014, 2015 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.
41 #define VERSION "3.0.1"
43 #define WHITESPACE " \t\n"
44 #define MAIN_WIN "main"
49 "out of memory: %s (%s:%d)\n", \
50 __func__, __FILE__, __LINE__); \
71 "[-u glade-builder-file.ui] "
88 printf("GTK+ v%d.%d.%d (running v%d.%d.%d)\n",
89 GTK_MAJOR_VERSION
, GTK_MINOR_VERSION
, GTK_MICRO_VERSION
,
90 gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version());
95 eql(const char *s1
, const char *s2
)
97 return s1
!= NULL
&& s2
!= NULL
&& strcmp(s1
, s2
) == 0;
101 widget_name(void *obj
)
103 return gtk_buildable_get_name(GTK_BUILDABLE(obj
));
107 * Send GUI feedback to global stream "out". The message format is
108 * "<origin>:<section> <data ...>". The variadic arguments are
109 * strings; last argument must be NULL. We're being patient with
110 * receivers which may intermittently close their end of the fifo, and
111 * make a couple of retries if an error occurs.
114 send_msg(GtkBuildable
*obj
, const char *section
, ...)
120 for (nsec
= 1e6
; nsec
< 1e9
; nsec
<<= 3) {
121 va_start(ap
, section
);
122 fprintf(out
, "%s:%s ", widget_name(obj
), section
);
123 while ((data
= va_arg(ap
, char *)) != NULL
) {
127 while ((c
= data
[i
++]) != '\0')
129 fprintf(out
, "\\\\");
138 fprintf(stderr
, "send error; retrying\n");
140 nanosleep(&(struct timespec
){0, nsec
}, NULL
);
148 * Callback that sends user's selection from a file dialog
151 cb_send_file_chooser_dialog_selection(gpointer user_data
)
153 send_msg(user_data
, "file",
154 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(user_data
)), NULL
);
155 send_msg(user_data
, "folder",
156 gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(user_data
)), NULL
);
160 * Callback that sends in a message the content of the text buffer
161 * passed in user_data
164 cb_send_text(GtkBuildable
*obj
, gpointer user_data
)
168 gtk_text_buffer_get_bounds(user_data
, &a
, &b
);
169 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
173 * Callback that sends in a message the highlighted text from the text
174 * buffer which was passed in user_data
177 cb_send_text_selection(GtkBuildable
*obj
, gpointer user_data
)
181 gtk_text_buffer_get_selection_bounds(user_data
, &a
, &b
);
182 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
186 * send_tree_row_msg serves as an argument for
187 * gtk_tree_selection_selected_foreach()
190 send_tree_row_msg(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, GtkBuildable
*obj
)
192 char *path_s
= gtk_tree_path_to_string(path
);
195 for (col
= 0; col
< gtk_tree_model_get_n_columns(model
); col
++) {
196 GValue value
= G_VALUE_INIT
;
200 gtk_tree_model_get_value(model
, iter
, col
, &value
);
201 col_type
= gtk_tree_model_get_column_type(model
, col
);
204 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_int(&value
));
205 send_msg(obj
, "gint", path_s
, str
, NULL
);
208 snprintf(str
, BUFLEN
, " %d %ld", col
, g_value_get_long(&value
));
209 send_msg(obj
, "glong", path_s
, str
, NULL
);
212 snprintf(str
, BUFLEN
, " %d %" PRId64
, col
, g_value_get_int64(&value
));
213 send_msg(obj
, "gint64", path_s
, str
, NULL
);
216 snprintf(str
, BUFLEN
, " %d %u", col
, g_value_get_uint(&value
));
217 send_msg(obj
, "guint", path_s
, str
, NULL
);
220 snprintf(str
, BUFLEN
, " %d %lu", col
, g_value_get_ulong(&value
));
221 send_msg(obj
, "gulong", path_s
, str
, NULL
);
224 snprintf(str
, BUFLEN
, " %d %" PRIu64
, col
, g_value_get_uint64(&value
));
225 send_msg(obj
, "guint64", path_s
, str
, NULL
);
228 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_boolean(&value
));
229 send_msg(obj
, "gboolean", path_s
, str
, NULL
);
232 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_float(&value
));
233 send_msg(obj
, "gfloat", path_s
, str
, NULL
);
236 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_double(&value
));
237 send_msg(obj
, "gdouble", path_s
, str
, NULL
);
240 snprintf(str
, BUFLEN
, " %d ", col
);
241 send_msg(obj
, "gchararray", path_s
, str
, g_value_get_string(&value
), NULL
);
244 fprintf(stderr
, "column %d not implemented: %s\n", col
, G_VALUE_TYPE_NAME(&value
));
247 g_value_unset(&value
);
253 * Callback that sends message(s) whose nature depends on the
254 * arguments passed. A call to this function will also be initiated
255 * by the user command ...:force.
258 cb(GtkBuildable
*obj
, gpointer user_data
)
262 GtkTreeView
*tree_view
;
263 unsigned int year
= 0, month
= 0, day
= 0;
265 if (GTK_IS_ENTRY(obj
))
266 send_msg(obj
, user_data
, gtk_entry_get_text(GTK_ENTRY(obj
)), NULL
);
267 else if (GTK_IS_MENU_ITEM(obj
))
268 send_msg(obj
, user_data
, gtk_menu_item_get_label(GTK_MENU_ITEM(obj
)), NULL
);
269 else if (GTK_IS_RANGE(obj
)) {
270 snprintf(str
, BUFLEN
, "%f", gtk_range_get_value(GTK_RANGE(obj
)));
271 send_msg(obj
, user_data
, str
, NULL
);
272 } else if (GTK_IS_SWITCH(obj
))
273 send_msg(obj
, gtk_switch_get_active(GTK_SWITCH(obj
)) ? "1" : "0", NULL
);
274 else if (GTK_IS_TOGGLE_BUTTON(obj
))
275 send_msg(obj
, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(obj
)) ? "1" : "0", NULL
);
276 else if (GTK_IS_COLOR_BUTTON(obj
)) {
277 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
278 send_msg(obj
, user_data
, gdk_rgba_to_string(&color
), NULL
);
279 } else if (GTK_IS_FONT_BUTTON(obj
))
280 send_msg(obj
, user_data
, gtk_font_button_get_font_name(GTK_FONT_BUTTON(obj
)), NULL
);
281 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
282 send_msg(obj
, user_data
, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(obj
)), NULL
);
283 else if (GTK_IS_BUTTON(obj
) || GTK_IS_TREE_VIEW_COLUMN(obj
))
284 send_msg(obj
, user_data
, NULL
);
285 else if (GTK_IS_CALENDAR(obj
)) {
286 gtk_calendar_get_date(GTK_CALENDAR(obj
), &year
, &month
, &day
);
287 snprintf(str
, BUFLEN
, "%04u-%02u-%02u", year
, ++month
, day
);
288 send_msg(obj
, user_data
, str
, NULL
);
289 } else if (GTK_IS_TREE_SELECTION(obj
)) {
290 tree_view
= gtk_tree_selection_get_tree_view(GTK_TREE_SELECTION(obj
));
291 send_msg(GTK_BUILDABLE(tree_view
), user_data
, NULL
);
292 gtk_tree_selection_selected_foreach(GTK_TREE_SELECTION(obj
),
293 (GtkTreeSelectionForeachFunc
)send_tree_row_msg
,
294 GTK_BUILDABLE(tree_view
));
296 fprintf(stderr
, "ignoring callback from %s\n", widget_name(obj
));
300 * Store a line from stream into buf, which should have been malloc'd
301 * to bufsize. Enlarge buf and bufsize if necessary.
304 read_buf(FILE *stream
, char **buf
, size_t *bufsize
)
313 nanosleep(&(struct timespec
){0, 1e7
}, NULL
);
318 if (i
>= *bufsize
- 1)
319 if ((*buf
= realloc(*buf
, *bufsize
= *bufsize
* 2)) == NULL
)
322 switch (c
= getc(stream
)) {
323 case 'n': (*buf
)[i
++] = '\n'; break;
324 case 'r': (*buf
)[i
++] = '\r'; break;
325 default: (*buf
)[i
++] = c
; break;
338 ign_cmd(GType type
, const char *msg
)
340 const char *name
, *pad
= " ";
342 if (type
== G_TYPE_INVALID
) {
347 name
= g_type_name(type
);
348 fprintf(stderr
, "ignoring %s%scommand \"%s\"\n", name
, pad
, msg
);
352 * Drawing on a GtkDrawingArea
379 * One single element of a drawing
382 struct draw_op
*next
;
383 struct draw_op
*prev
;
390 * The content of all GtkDrawingAreas
392 static struct drawing
{
393 struct drawing
*next
;
394 struct drawing
*prev
;
396 struct draw_op
*draw_ops
;
400 * Sets of arguments for various drawing functions
402 struct rectangle_args
{
417 struct curve_to_args
{
426 struct move_to_args
{
431 struct set_source_rgba_args
{
435 struct set_dash_args
{
440 struct set_line_cap_args
{
441 cairo_line_cap_t line_cap
;
444 struct set_line_join_args
{
445 cairo_line_join_t line_join
;
448 struct set_line_width_args
{
452 struct show_text_args
{
457 struct set_font_size_args
{
462 draw(cairo_t
*cr
, enum cairo_fn op
, void *op_args
)
466 struct rectangle_args
*args
= op_args
;
468 cairo_rectangle(cr
, args
->x
, args
->y
, args
->width
, args
->height
);
472 struct arc_args
*args
= op_args
;
474 cairo_arc(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
478 struct arc_args
*args
= op_args
;
480 cairo_arc_negative(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
484 struct curve_to_args
*args
= op_args
;
486 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
490 struct curve_to_args
*args
= op_args
;
492 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
496 struct move_to_args
*args
= op_args
;
498 cairo_line_to(cr
, args
->x
, args
->y
);
502 struct move_to_args
*args
= op_args
;
504 cairo_rel_line_to(cr
, args
->x
, args
->y
);
508 struct move_to_args
*args
= op_args
;
510 cairo_move_to(cr
, args
->x
, args
->y
);
514 struct move_to_args
*args
= op_args
;
516 cairo_rel_move_to(cr
, args
->x
, args
->y
);
520 cairo_close_path(cr
);
522 case SET_SOURCE_RGBA
: {
523 struct set_source_rgba_args
*args
= op_args
;
525 gdk_cairo_set_source_rgba(cr
, &args
->color
);
529 struct set_dash_args
*args
= op_args
;
531 cairo_set_dash(cr
, args
->dashes
, args
->num_dashes
, 0);
535 struct set_line_cap_args
*args
= op_args
;
537 cairo_set_line_cap(cr
, args
->line_cap
);
540 case SET_LINE_JOIN
: {
541 struct set_line_join_args
*args
= op_args
;
543 cairo_set_line_join(cr
, args
->line_join
);
546 case SET_LINE_WIDTH
: {
547 struct set_line_width_args
*args
= op_args
;
549 cairo_set_line_width(cr
, args
->width
);
556 cairo_fill_preserve(cr
);
561 case STROKE_PRESERVE
:
562 cairo_stroke_preserve(cr
);
565 struct show_text_args
*args
= op_args
;
567 cairo_show_text(cr
, args
->text
);
570 case SET_FONT_SIZE
: {
571 struct set_font_size_args
*args
= op_args
;
573 cairo_set_font_size(cr
, args
->size
);
583 set_draw_op(struct draw_op
*op
, char* action
, char *data
)
585 if (eql(action
, "rectangle")) {
586 struct rectangle_args
*args
;
588 if ((args
= malloc(sizeof(*args
))) == NULL
)
592 if (sscanf(data
, "%u %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->width
, &args
->height
) != 5)
594 } else if (eql(action
, "arc")) {
595 struct arc_args
*args
;
598 if ((args
= malloc(sizeof(*args
))) == NULL
)
602 if (sscanf(data
, "%u %lf %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->radius
, °1
, °2
) != 6)
604 args
->angle1
= deg1
* (M_PI
/ 180.);
605 args
->angle2
= deg2
* (M_PI
/ 180.);
606 } else if (eql(action
, "arc_negative")) {
607 struct arc_args
*args
;
610 if ((args
= malloc(sizeof(*args
))) == NULL
)
612 op
->op
= ARC_NEGATIVE
;
614 if (sscanf(data
, "%u %lf %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->radius
, °1
, °2
) != 6)
616 args
->angle1
= deg1
* (M_PI
/ 180.);
617 args
->angle2
= deg2
* (M_PI
/ 180.);
618 } else if (eql(action
, "curve_to")) {
619 struct curve_to_args
*args
;
621 if ((args
= malloc(sizeof(*args
))) == NULL
)
625 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)
627 } else if (eql(action
, "rel_curve_to")) {
628 struct curve_to_args
*args
;
630 if ((args
= malloc(sizeof(*args
))) == NULL
)
632 op
->op
= REL_CURVE_TO
;
634 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)
636 } else if (eql(action
, "line_to")) {
637 struct move_to_args
*args
;
639 if ((args
= malloc(sizeof(*args
))) == NULL
)
643 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
645 } else if (eql(action
, "rel_line_to")) {
646 struct move_to_args
*args
;
648 if ((args
= malloc(sizeof(*args
))) == NULL
)
650 op
->op
= REL_LINE_TO
;
652 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
654 } else if (eql(action
, "move_to")) {
655 struct move_to_args
*args
;
657 if ((args
= malloc(sizeof(*args
))) == NULL
)
661 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
663 } else if (eql(action
, "rel_move_to")) {
664 struct move_to_args
*args
;
666 if ((args
= malloc(sizeof(*args
))) == NULL
)
668 op
->op
= REL_MOVE_TO
;
670 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
672 } else if (eql(action
, "close_path")) {
674 if (sscanf(data
, "%u", &op
->id
) != 1)
677 } else if (eql(action
, "set_source_rgba")) {
678 struct set_source_rgba_args
*args
;
681 if ((args
= malloc(sizeof(*args
))) == NULL
)
683 op
->op
= SET_SOURCE_RGBA
;
685 if ((sscanf(data
, "%u %n", &op
->id
, &c_start
) < 1))
687 gdk_rgba_parse(&args
->color
, data
+ c_start
);
688 } else if (eql(action
, "set_dash")) {
689 struct set_dash_args
*args
;
693 if (sscanf(data
, "%u %n", &op
->id
, &d_start
) < 1)
695 next
= end
= data
+ d_start
;
701 } while (next
!= end
);
702 if ((args
= malloc(sizeof(*args
) + n
* sizeof(args
->dashes
[0]))) == NULL
)
706 args
->num_dashes
= n
;
707 for (i
= 0, next
= data
+ d_start
; i
< n
; i
++, next
= end
) {
708 args
->dashes
[i
] = strtod(next
, &end
);
710 } else if (eql(action
, "set_line_cap")) {
711 struct set_line_cap_args
*args
;
714 if ((args
= malloc(sizeof(*args
))) == NULL
)
716 op
->op
= SET_LINE_CAP
;
718 if (sscanf(data
, "%u %6s", &op
->id
, str
) != 2)
720 if (eql(str
, "butt"))
721 args
->line_cap
= CAIRO_LINE_CAP_BUTT
;
722 else if (eql(str
, "round"))
723 args
->line_cap
= CAIRO_LINE_CAP_ROUND
;
724 else if (eql(str
, "square"))
725 args
->line_cap
= CAIRO_LINE_CAP_SQUARE
;
728 } else if (eql(action
, "set_line_join")) {
729 struct set_line_join_args
*args
;
732 if ((args
= malloc(sizeof(*args
))) == NULL
)
734 op
->op
= SET_LINE_JOIN
;
736 if (sscanf(data
, "%u %5s", &op
->id
, str
) != 2)
738 if (eql(str
, "miter"))
739 args
->line_join
= CAIRO_LINE_JOIN_MITER
;
740 else if (eql(str
, "round"))
741 args
->line_join
= CAIRO_LINE_JOIN_ROUND
;
742 else if (eql(str
, "bevel"))
743 args
->line_join
= CAIRO_LINE_JOIN_BEVEL
;
746 } else if (eql(action
, "set_line_width")) {
747 struct set_line_width_args
*args
;
749 if ((args
= malloc(sizeof(*args
))) == NULL
)
751 op
->op
= SET_LINE_WIDTH
;
753 if (sscanf(data
, "%u %lf", &op
->id
, &args
->width
) != 2)
755 } else if (eql(action
, "fill")) {
757 if (sscanf(data
, "%u", &op
->id
) != 1)
760 } else if (eql(action
, "fill_preserve")) {
761 op
->op
= FILL_PRESERVE
;
762 if (sscanf(data
, "%u", &op
->id
) != 1)
765 } else if (eql(action
, "stroke")) {
767 if (sscanf(data
, "%u", &op
->id
) != 1)
770 } else if (eql(action
, "stroke_preserve")) {
771 op
->op
= STROKE_PRESERVE
;
772 if (sscanf(data
, "%u", &op
->id
) != 1)
775 } else if (eql(action
, "show_text")) {
776 struct show_text_args
*args
;
779 if (sscanf(data
, "%u %n", &op
->id
, &start
) < 1)
781 len
= strlen(data
+ start
) + 1;
782 if ((args
= malloc(sizeof(*args
) + len
* sizeof(args
->text
[0]))) == NULL
)
786 args
->len
= len
; /* not used */
787 strncpy(args
->text
, (data
+ start
), len
);
788 } else if (eql(action
, "set_font_size")) {
789 struct set_font_size_args
*args
;
791 if ((args
= malloc(sizeof(*args
))) == NULL
)
793 op
->op
= SET_FONT_SIZE
;
795 if (sscanf(data
, "%u %lf", &op
->id
, &args
->size
) != 2)
803 * Add another element to widget's list of drawing operations
806 ins_draw_op(GtkWidget
*widget
, char *action
, char *data
)
808 struct draw_op
*op
, *last_op
;
811 if ((op
= malloc(sizeof(*op
))) == NULL
)
814 if (!set_draw_op(op
, action
, data
)) {
819 for (d
= drawings
; d
!= NULL
; d
= d
->next
)
820 if (d
->widget
== widget
)
823 if ((d
= malloc(sizeof(*d
))) == NULL
)
825 if (drawings
== NULL
) {
833 } else if (d
->draw_ops
== NULL
) {
837 for (last_op
= d
->draw_ops
; last_op
->next
!= NULL
; last_op
= last_op
->next
);
844 * Remove all elements with the given id from widget's list of drawing
848 rem_draw_op(GtkWidget
*widget
, char *data
)
850 struct draw_op
*op
, *next_op
;
854 if (sscanf(data
, "%u", &id
) != 1)
856 for (d
= drawings
; d
!= NULL
; d
= d
->next
)
857 if (d
->widget
== widget
)
864 if (op
->prev
== NULL
)
865 d
->draw_ops
= next_op
;
877 * Callback that draws on a GtkDrawingArea
880 cb_draw(GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
886 for (p
= drawings
; p
!= NULL
; p
= p
->next
)
887 if (p
->widget
== widget
)
888 for (op
= p
->draw_ops
; op
!= NULL
; op
= op
->next
)
889 draw(cr
, op
->op
, op
->op_args
);
894 * Perform various kinds of actions on the widget passed
897 update_widget_font(GtkWidget
*widget
, char *data
)
900 PangoFontDescription
*font
= pango_font_description_from_string(data
);
902 gtk_widget_override_font(widget
, font
);
903 pango_font_description_free(font
);
905 gtk_widget_override_font(widget
, NULL
);
909 update_widget_color(GtkWidget
*widget
, char *data
)
914 gdk_rgba_parse(&color
, data
);
915 gtk_widget_override_color(widget
, GTK_STATE_FLAG_NORMAL
, &color
);
917 gtk_widget_override_color(widget
, GTK_STATE_FLAG_NORMAL
, NULL
);
921 update_widget_background_color(GtkWidget
*widget
, char *data
)
926 gdk_rgba_parse(&color
, data
);
927 gtk_widget_override_background_color(widget
, GTK_STATE_FLAG_NORMAL
, &color
);
929 gtk_widget_override_background_color(widget
, GTK_STATE_FLAG_NORMAL
, NULL
);
933 * Update various kinds of widgets according to the respective action
937 update_button(GtkButton
*button
, char *action
, char *data
, char *whole_msg
)
939 if (eql(action
, "set_label"))
940 gtk_button_set_label(button
, data
);
942 ign_cmd(GTK_TYPE_BUTTON
, whole_msg
);
946 update_calendar(GtkCalendar
*calendar
, char *action
, char *data
, char *whole_msg
)
948 int year
= 0, month
= 0, day
= 0;
950 if (eql(action
, "select_date")) {
951 sscanf(data
, "%d-%d-%d", &year
, &month
, &day
);
952 if (month
> -1 && month
<= 11 && day
> 0 && day
<= 31) {
953 gtk_calendar_select_month(calendar
, --month
, year
);
954 gtk_calendar_select_day(calendar
, day
);
956 ign_cmd(GTK_TYPE_CALENDAR
, whole_msg
);
957 } else if (eql(action
, "mark_day")) {
958 day
= strtol(data
, NULL
, 10);
959 if (day
> 0 && day
<= 31)
960 gtk_calendar_mark_day(calendar
, day
);
962 ign_cmd(GTK_TYPE_CALENDAR
, whole_msg
);
963 } else if (eql(action
, "clear_marks"))
964 gtk_calendar_clear_marks(calendar
);
966 ign_cmd(GTK_TYPE_CALENDAR
, whole_msg
);
970 update_color_button(GtkColorChooser
*chooser
, char *action
, char *data
, char *whole_msg
)
974 if (eql(action
, "set_color")) {
975 gdk_rgba_parse(&color
, data
);
976 gtk_color_chooser_set_rgba(chooser
, &color
);
978 ign_cmd(GTK_TYPE_COLOR_BUTTON
, whole_msg
);
982 update_combo_box_text(GtkComboBoxText
*combobox
, char *action
, char *data
, char *whole_msg
)
984 if (eql(action
, "prepend_text"))
985 gtk_combo_box_text_prepend_text(combobox
, data
);
986 else if (eql(action
, "append_text"))
987 gtk_combo_box_text_append_text(combobox
, data
);
988 else if (eql(action
, "remove"))
989 gtk_combo_box_text_remove(combobox
, strtol(data
, NULL
, 10));
990 else if (eql(action
, "insert_text")) {
991 char *position
= strtok(data
, WHITESPACE
);
992 char *text
= strtok(NULL
, WHITESPACE
);
994 gtk_combo_box_text_insert_text(combobox
, strtol(position
, NULL
, 10), text
);
996 ign_cmd(GTK_TYPE_COMBO_BOX_TEXT
, whole_msg
);
1000 update_frame(GtkFrame
*frame
, char *action
, char *data
, char *whole_msg
)
1002 if (eql(action
, "set_label"))
1003 gtk_frame_set_label(frame
, data
);
1005 ign_cmd(GTK_TYPE_FRAME
, whole_msg
);
1009 update_drawing_area(GtkWidget
*widget
, char *action
, char *data
, char *whole_msg
)
1011 if (eql(action
, "remove")) {
1012 if (!rem_draw_op(widget
, data
))
1013 ign_cmd(GTK_TYPE_DRAWING_AREA
, whole_msg
);
1014 } else if (eql(action
, "refresh")) {
1015 gint width
= gtk_widget_get_allocated_width (widget
);
1016 gint height
= gtk_widget_get_allocated_height (widget
);
1018 gtk_widget_queue_draw_area(widget
, 0, 0, width
, height
);
1019 } else if (ins_draw_op(widget
, action
, data
));
1021 ign_cmd(GTK_TYPE_DRAWING_AREA
, whole_msg
);
1025 update_entry(GtkEntry
*entry
, char *action
, char *data
, char *whole_msg
, GType type
)
1027 if (eql(action
, "set_text"))
1028 gtk_entry_set_text(entry
, data
);
1030 ign_cmd(type
, whole_msg
);
1034 update_label(GtkLabel
*label
, char *action
, char *data
, char *whole_msg
)
1036 if (eql(action
, "set_text"))
1037 gtk_label_set_text(label
, data
);
1039 ign_cmd(GTK_TYPE_LABEL
, whole_msg
);
1043 update_expander(GtkExpander
*expander
, char *action
, char *data
, char *whole_msg
)
1045 if (eql(action
, "set_expanded"))
1046 gtk_expander_set_expanded(expander
, strtol(data
, NULL
, 10));
1047 else if (eql(action
, "set_label"))
1048 gtk_expander_set_label(expander
, data
);
1050 ign_cmd(GTK_TYPE_EXPANDER
, whole_msg
);
1054 update_file_chooser_button(GtkFileChooser
*chooser
, char *action
, char *data
, char *whole_msg
)
1056 if (eql(action
, "set_filename"))
1057 gtk_file_chooser_set_filename(chooser
, data
);
1059 ign_cmd(GTK_TYPE_FILE_CHOOSER_BUTTON
, whole_msg
);
1063 update_file_chooser_dialog(GtkFileChooser
*chooser
, char *action
, char *data
, char *whole_msg
)
1065 if (eql(action
, "set_filename"))
1066 gtk_file_chooser_set_filename(chooser
, data
);
1067 else if (eql(action
, "set_current_name"))
1068 gtk_file_chooser_set_current_name(chooser
, data
);
1070 ign_cmd(GTK_TYPE_FILE_CHOOSER_DIALOG
, whole_msg
);
1074 update_font_button(GtkFontButton
*font_button
, char *action
, char *data
, char *whole_msg
)
1076 if (eql(action
, "set_font_name"))
1077 gtk_font_button_set_font_name(font_button
, data
);
1079 ign_cmd(GTK_TYPE_FONT_BUTTON
, whole_msg
);
1083 update_image(GtkImage
*image
, char *action
, char *data
, char *whole_msg
)
1087 gtk_image_get_icon_name(image
, NULL
, &size
);
1088 if (eql(action
, "set_from_file"))
1089 gtk_image_set_from_file(image
, data
);
1090 else if (eql(action
, "set_from_icon_name"))
1091 gtk_image_set_from_icon_name(image
, data
, size
);
1093 ign_cmd(GTK_TYPE_IMAGE
, whole_msg
);
1097 update_notebook(GtkNotebook
*notebook
, char *action
, char *data
, char *whole_msg
)
1099 if (eql(action
, "set_current_page"))
1100 gtk_notebook_set_current_page(notebook
, strtol(data
, NULL
, 10));
1102 ign_cmd(GTK_TYPE_NOTEBOOK
, whole_msg
);
1106 update_progress_bar(GtkProgressBar
*progressbar
, char *action
, char *data
, char *whole_msg
)
1108 if (eql(action
, "set_text"))
1109 gtk_progress_bar_set_text(progressbar
, *data
== '\0' ? NULL
: data
);
1110 else if (eql(action
, "set_fraction"))
1111 gtk_progress_bar_set_fraction(progressbar
, strtod(data
, NULL
));
1113 ign_cmd(GTK_TYPE_PROGRESS_BAR
, whole_msg
);
1117 update_scale(GtkRange
*range
, char *action
, char *data
, char *whole_msg
)
1119 if (eql(action
, "set_value"))
1120 gtk_range_set_value(range
, strtod(data
, NULL
));
1122 ign_cmd(GTK_TYPE_SCALE
, whole_msg
);
1126 update_spinner(GtkSpinner
*spinner
, char *action
, char *whole_msg
)
1128 if (eql(action
, "start"))
1129 gtk_spinner_start(spinner
);
1130 else if (eql(action
, "stop"))
1131 gtk_spinner_stop(spinner
);
1133 ign_cmd(GTK_TYPE_SPINNER
, whole_msg
);
1137 update_statusbar(GtkStatusbar
*statusbar
, char *action
, char *data
, char *whole_msg
)
1139 if (eql(action
, "push"))
1140 gtk_statusbar_push(statusbar
, 0, data
);
1141 else if (eql(action
, "pop"))
1142 gtk_statusbar_pop(statusbar
, 0);
1143 else if (eql(action
, "remove_all"))
1144 gtk_statusbar_remove_all(statusbar
, 0);
1146 ign_cmd(GTK_TYPE_STATUSBAR
, whole_msg
);
1150 update_switch(GtkSwitch
*switcher
, char *action
, char *data
, char *whole_msg
)
1152 if (eql(action
, "set_active"))
1153 gtk_switch_set_active(switcher
, strtol(data
, NULL
, 10));
1155 ign_cmd(GTK_TYPE_SWITCH
, whole_msg
);
1159 update_text_view(GtkTextView
*view
, char *action
, char *data
, char *whole_msg
)
1161 GtkTextBuffer
*textbuf
= gtk_text_view_get_buffer(view
);
1164 if (eql(action
, "set_text"))
1165 gtk_text_buffer_set_text(textbuf
, data
, -1);
1166 else if (eql(action
, "delete")) {
1167 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
1168 gtk_text_buffer_delete(textbuf
, &a
, &b
);
1169 } else if (eql(action
, "insert_at_cursor"))
1170 gtk_text_buffer_insert_at_cursor(textbuf
, data
, -1);
1171 else if (eql(action
, "place_cursor")) {
1172 if (eql(data
, "end"))
1173 gtk_text_buffer_get_end_iter(textbuf
, &a
);
1174 else /* numeric offset */
1175 gtk_text_buffer_get_iter_at_offset(textbuf
, &a
, strtol(data
, NULL
, 10));
1176 gtk_text_buffer_place_cursor(textbuf
, &a
);
1177 } else if (eql(action
, "place_cursor_at_line")) {
1178 gtk_text_buffer_get_iter_at_line(textbuf
, &a
, strtol(data
, NULL
, 10));
1179 gtk_text_buffer_place_cursor(textbuf
, &a
);
1180 } else if (eql(action
, "scroll_to_cursor"))
1181 gtk_text_view_scroll_to_mark(view
, gtk_text_buffer_get_insert(textbuf
), 0., 0, 0., 0.);
1183 ign_cmd(GTK_TYPE_TEXT_VIEW
, whole_msg
);
1187 update_toggle_button(GtkToggleButton
*toggle
, char *action
, char *data
, char *whole_msg
, GType type
)
1189 if (eql(action
, "set_label"))
1190 gtk_button_set_label(GTK_BUTTON(toggle
), data
);
1191 else if (eql(action
, "set_active"))
1192 gtk_toggle_button_set_active(toggle
, strtol(data
, NULL
, 10));
1194 ign_cmd(type
, whole_msg
);
1198 update_tree_view(GtkTreeView
*view
, char *action
, char *data
, char *whole_msg
)
1200 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
1201 GtkListStore
*store
= GTK_LIST_STORE(model
);
1202 GtkTreeIter iter0
, iter1
;
1203 char *tokens
, *arg0_s
, *arg1_s
, *arg2_s
, *endptr
;
1204 int arg0_n
= 0, arg1_n
= 0;
1205 bool arg0_n_valid
= false, arg1_n_valid
= false;
1206 bool iter0_valid
= false, iter1_valid
= false;
1208 if ((tokens
= malloc(strlen(data
) + 1)) == NULL
)
1210 strcpy(tokens
, data
);
1211 arg0_s
= strtok(tokens
, WHITESPACE
);
1212 arg1_s
= strtok(NULL
, WHITESPACE
);
1213 arg2_s
= strtok(NULL
, "\n");
1218 (arg0_n_valid
= (arg0_n
= strtol(arg0_s
, &endptr
, 10)) >= 0 &&
1219 errno
== 0 && endptr
!= arg0_s
) &&
1220 gtk_tree_model_get_iter_from_string(model
, &iter0
, arg0_s
);
1225 (arg1_n_valid
= (arg1_n
= strtol(arg1_s
, &endptr
, 10)) >= 0 &&
1226 errno
== 0 && endptr
!= arg1_s
) &&
1227 gtk_tree_model_get_iter_from_string(model
, &iter1
, arg1_s
);
1228 if (eql(action
, "set") && iter0_valid
&& arg1_n_valid
&&
1229 arg1_n
< gtk_tree_model_get_n_columns(model
)) {
1230 GType col_type
= gtk_tree_model_get_column_type(model
, arg1_n
);
1235 case G_TYPE_BOOLEAN
:
1244 n
= strtoll(arg2_s
, &endptr
, 10);
1245 if (!errno
&& endptr
!= arg2_s
)
1246 gtk_list_store_set(store
, &iter0
, arg1_n
, n
, -1);
1248 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1254 d
= strtod(arg2_s
, &endptr
);
1255 if (!errno
&& endptr
!= arg2_s
)
1256 gtk_list_store_set(store
, &iter0
, arg1_n
, d
, -1);
1258 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1259 gtk_list_store_set(store
, &iter0
, arg1_n
, strtod(arg2_s
, NULL
), -1);
1262 gtk_list_store_set(store
, &iter0
, arg1_n
, arg2_s
, -1);
1265 fprintf(stderr
, "column %d: %s not implemented\n", arg1_n
, g_type_name(col_type
));
1268 } else if (eql(action
, "scroll") && arg0_n_valid
&& arg1_n_valid
)
1269 gtk_tree_view_scroll_to_cell (view
,
1270 gtk_tree_path_new_from_string(arg0_s
),
1271 gtk_tree_view_get_column(view
, arg1_n
),
1273 else if (eql(action
, "insert_row"))
1274 if (eql(arg0_s
, "end"))
1275 gtk_list_store_insert_before(store
, &iter1
, NULL
);
1276 else if (iter0_valid
)
1277 gtk_list_store_insert_before(store
, &iter1
, &iter0
);
1279 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1280 else if (eql(action
, "move_row") && iter0_valid
)
1281 if (eql(arg1_s
, "end"))
1282 gtk_list_store_move_before(store
, &iter0
, NULL
);
1283 else if (iter1_valid
)
1284 gtk_list_store_move_before(store
, &iter0
, &iter1
);
1286 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1287 else if (eql(action
, "remove_row") && iter0_valid
)
1288 gtk_list_store_remove(store
, &iter0
);
1290 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1295 update_window(GtkWindow
*window
, char *action
, char *data
, char *whole_msg
)
1297 if (eql(action
, "set_title"))
1298 gtk_window_set_title(window
, data
);
1300 ign_cmd(GTK_TYPE_WINDOW
, whole_msg
);
1304 fake_ui_activity(GObject
*obj
, char *whole_msg
, GType type
)
1306 if (!GTK_IS_WIDGET(obj
))
1307 ign_cmd(type
, whole_msg
);
1308 else if (GTK_IS_ENTRY(obj
) || GTK_IS_SPIN_BUTTON(obj
))
1309 cb(GTK_BUILDABLE(obj
), "text");
1310 else if (GTK_IS_SCALE(obj
))
1311 cb(GTK_BUILDABLE(obj
), "value");
1312 else if (GTK_IS_CALENDAR(obj
))
1313 cb(GTK_BUILDABLE(obj
), "clicked");
1314 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
1315 cb(GTK_BUILDABLE(obj
), "file");
1316 else if (!gtk_widget_activate(GTK_WIDGET(obj
)))
1317 ign_cmd(type
, whole_msg
);
1321 * Parse command pointed to by ud, and act on ui accordingly. Set
1322 * ud->digested = true if done. Runs once per command inside
1326 update_ui(struct ui_data
*ud
)
1328 char name
[ud
->msg_size
], action
[ud
->msg_size
];
1330 int data_start
= strlen(ud
->msg
);
1331 GObject
*obj
= NULL
;
1332 GType type
= G_TYPE_INVALID
;
1334 name
[0] = action
[0] = '\0';
1336 " %[0-9a-zA-Z_]:%[0-9a-zA-Z_]%*1[ \t]%n",
1337 name
, action
, &data_start
);
1338 if (eql(action
, "main_quit")) {
1342 if ((obj
= (gtk_builder_get_object(ud
->builder
, name
))) == NULL
) {
1343 ign_cmd(type
, ud
->msg
);
1346 type
= G_TYPE_FROM_INSTANCE(obj
);
1347 data
= ud
->msg
+ data_start
;
1348 if (eql(action
, "force"))
1349 fake_ui_activity(obj
, ud
->msg
, type
);
1350 else if (eql(action
, "set_sensitive"))
1351 gtk_widget_set_sensitive(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
1352 else if (eql(action
, "set_visible"))
1353 gtk_widget_set_visible(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
1354 else if (eql(action
, "override_font"))
1355 update_widget_font(GTK_WIDGET(obj
), data
);
1356 else if (eql(action
, "override_color"))
1357 update_widget_color(GTK_WIDGET(obj
), data
);
1358 else if (eql(action
, "override_background_color"))
1359 update_widget_background_color(GTK_WIDGET(obj
), data
);
1360 else if (type
== GTK_TYPE_LABEL
)
1361 update_label(GTK_LABEL(obj
), action
, data
, ud
->msg
);
1362 else if (type
== GTK_TYPE_IMAGE
)
1363 update_image(GTK_IMAGE(obj
), action
, data
, ud
->msg
);
1364 else if (type
== GTK_TYPE_TEXT_VIEW
)
1365 update_text_view(GTK_TEXT_VIEW(obj
), action
, data
, ud
->msg
);
1366 else if (type
== GTK_TYPE_NOTEBOOK
)
1367 update_notebook(GTK_NOTEBOOK(obj
), action
, data
, ud
->msg
);
1368 else if (type
== GTK_TYPE_EXPANDER
)
1369 update_expander(GTK_EXPANDER(obj
), action
, data
, ud
->msg
);
1370 else if (type
== GTK_TYPE_FRAME
)
1371 update_frame(GTK_FRAME(obj
), action
, data
, ud
->msg
);
1372 else if (type
== GTK_TYPE_BUTTON
)
1373 update_button(GTK_BUTTON(obj
), action
, data
, ud
->msg
);
1374 else if (type
== GTK_TYPE_FILE_CHOOSER_DIALOG
)
1375 update_file_chooser_dialog(GTK_FILE_CHOOSER(obj
), action
, data
, ud
->msg
);
1376 else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
1377 update_file_chooser_button(GTK_FILE_CHOOSER(obj
), action
, data
, ud
->msg
);
1378 else if (type
== GTK_TYPE_COLOR_BUTTON
)
1379 update_color_button(GTK_COLOR_CHOOSER(obj
), action
, data
, ud
->msg
);
1380 else if (type
== GTK_TYPE_FONT_BUTTON
)
1381 update_font_button(GTK_FONT_BUTTON(obj
), action
, data
, ud
->msg
);
1382 else if (type
== GTK_TYPE_SWITCH
)
1383 update_switch(GTK_SWITCH(obj
), action
, data
, ud
->msg
);
1384 else if (type
== GTK_TYPE_TOGGLE_BUTTON
|| type
== GTK_TYPE_RADIO_BUTTON
|| type
== GTK_TYPE_CHECK_BUTTON
)
1385 update_toggle_button(GTK_TOGGLE_BUTTON(obj
), action
, data
, ud
->msg
, type
);
1386 else if (type
== GTK_TYPE_SPIN_BUTTON
|| type
== GTK_TYPE_ENTRY
)
1387 update_entry(GTK_ENTRY(obj
), action
, data
, ud
->msg
, type
);
1388 else if (type
== GTK_TYPE_SCALE
)
1389 update_scale(GTK_RANGE(obj
), action
, data
, ud
->msg
);
1390 else if (type
== GTK_TYPE_PROGRESS_BAR
)
1391 update_progress_bar(GTK_PROGRESS_BAR(obj
), action
, data
, ud
->msg
);
1392 else if (type
== GTK_TYPE_SPINNER
)
1393 update_spinner(GTK_SPINNER(obj
), action
, ud
->msg
);
1394 else if (type
== GTK_TYPE_COMBO_BOX_TEXT
)
1395 update_combo_box_text(GTK_COMBO_BOX_TEXT(obj
), action
, data
, ud
->msg
);
1396 else if (type
== GTK_TYPE_STATUSBAR
)
1397 update_statusbar(GTK_STATUSBAR(obj
), action
, data
, ud
->msg
);
1398 else if (type
== GTK_TYPE_CALENDAR
)
1399 update_calendar(GTK_CALENDAR(obj
), action
, data
, ud
->msg
);
1400 else if (type
== GTK_TYPE_TREE_VIEW
)
1401 update_tree_view(GTK_TREE_VIEW(obj
), action
, data
, ud
->msg
);
1402 else if (type
== GTK_TYPE_DRAWING_AREA
)
1403 update_drawing_area(GTK_WIDGET(obj
), action
, data
, ud
->msg
);
1404 else if (type
== GTK_TYPE_WINDOW
)
1405 update_window(GTK_WINDOW(obj
), action
, data
, ud
->msg
);
1407 ign_cmd(type
, ud
->msg
);
1409 ud
->msg_digested
= true;
1410 return G_SOURCE_REMOVE
;
1420 * Read lines from global stream "in" and perform the appropriate
1421 * actions on the GUI
1424 digest_msg(void *builder
)
1427 char first_char
= '\0';
1430 if ((ud
.msg
= malloc(ud
.msg_size
= 32)) == NULL
)
1432 pthread_cleanup_push((void(*)(void *))free_at
, &ud
.msg
);
1433 pthread_testcancel();
1434 read_buf(in
, &ud
.msg
, &ud
.msg_size
);
1435 sscanf(ud
.msg
, " %c", &first_char
);
1436 ud
.builder
= builder
;
1437 if (first_char
!= '#') {
1438 ud
.msg_digested
= false;
1439 pthread_testcancel();
1440 gdk_threads_add_timeout(1, (GSourceFunc
)update_ui
, &ud
);
1441 while (!ud
.msg_digested
)
1442 nanosleep(&(struct timespec
){0, 1e6
}, NULL
);
1444 pthread_cleanup_pop(1);
1450 * Create a fifo if necessary, and open it. Give up if the file
1451 * exists but is not a fifo
1454 fifo(const char *name
, const char *mode
)
1460 if (name
!= NULL
&& (stat(name
, &sb
), !S_ISFIFO(sb
.st_mode
)))
1461 if (mkfifo(name
, 0666) != 0) {
1462 perror("making fifo");
1470 fd
= open(name
, O_RDWR
| O_NONBLOCK
);
1472 perror("opening fifo");
1475 stream
= fdopen(fd
, "r");
1482 /* fopen blocks if there is no reader, so here is one */
1483 fd
= open(name
, O_RDONLY
| O_NONBLOCK
);
1485 perror("opening fifo");
1488 stream
= fopen(name
, "w");
1489 /* unblocking done */
1497 if (stream
== NULL
) {
1498 perror("opening fifo");
1501 setvbuf(stream
, NULL
, _IOLBF
, 0);
1507 obj_sans_suffix(char *suffix
, const char *name
, gpointer
*builder
)
1510 char str
[BUFLEN
+ 1] = {'\0'};
1512 str_l
= suffix
- name
;
1513 strncpy(str
, name
, str_l
< BUFLEN
? str_l
: BUFLEN
);
1514 return gtk_builder_get_object(GTK_BUILDER(builder
), str
);
1518 connect_widget_signals(gpointer
*obj
, gpointer
*builder
)
1520 const char *name
= NULL
;
1521 char *suffix
= NULL
;
1523 GType type
= G_TYPE_INVALID
;
1525 type
= G_TYPE_FROM_INSTANCE(obj
);
1526 if (GTK_IS_BUILDABLE(obj
))
1527 name
= widget_name(obj
);
1528 if (type
== GTK_TYPE_TREE_VIEW_COLUMN
)
1529 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
1530 if (type
== GTK_TYPE_BUTTON
) {
1531 /* button associated with a GtkTextView */
1532 if ((suffix
= strstr(name
, "_send_text")) != NULL
&&
1533 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1534 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text
),
1535 gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
)));
1536 else if ((suffix
= strstr(name
, "_send_selection")) != NULL
&&
1537 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1538 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text_selection
),
1539 gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
)));
1540 /* button associated with (and part of) a GtkDialog */
1541 else if ((suffix
= strstr(name
, "_cancel")) != NULL
&&
1542 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1543 if (eql(widget_name(obj2
), MAIN_WIN
))
1544 g_signal_connect(obj
, "clicked", G_CALLBACK(gtk_main_quit
), NULL
);
1546 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_widget_hide
), obj2
);
1547 else if ((suffix
= strstr(name
, "_ok")) != NULL
&&
1548 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
, builder
))) {
1549 if (GTK_IS_FILE_CHOOSER_DIALOG(obj2
))
1550 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(cb_send_file_chooser_dialog_selection
), obj2
);
1551 else /* generic button */
1552 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
1553 if (eql(widget_name(obj2
), MAIN_WIN
))
1554 g_signal_connect(obj
, "clicked", G_CALLBACK(gtk_main_quit
), NULL
);
1556 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_widget_hide
), obj2
);
1557 } else if ((suffix
= strstr(name
, "_apply")) != NULL
&&
1558 GTK_IS_FILE_CHOOSER_DIALOG(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1559 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(cb_send_file_chooser_dialog_selection
), obj2
);
1560 else /* generic button */
1561 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
1563 else if (GTK_IS_MENU_ITEM(obj
))
1564 if ((suffix
= strstr(name
, "_invoke")) != NULL
&&
1565 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1566 g_signal_connect_swapped(obj
, "activate", G_CALLBACK(gtk_widget_show
), obj2
);
1568 g_signal_connect(obj
, "activate", G_CALLBACK(cb
), "active");
1569 else if (GTK_IS_WINDOW(obj
))
1570 if (eql(name
, MAIN_WIN
))
1571 g_signal_connect(obj
, "delete-event", G_CALLBACK(gtk_main_quit
), NULL
);
1573 g_signal_connect(obj
, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
1574 else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
1575 g_signal_connect(obj
, "file-set", G_CALLBACK(cb
), "file");
1576 else if (type
== GTK_TYPE_COLOR_BUTTON
)
1577 g_signal_connect(obj
, "color-set", G_CALLBACK(cb
), "color");
1578 else if (type
== GTK_TYPE_FONT_BUTTON
)
1579 g_signal_connect(obj
, "font-set", G_CALLBACK(cb
), "font");
1580 else if (type
== GTK_TYPE_SWITCH
)
1581 g_signal_connect(obj
, "notify::active", G_CALLBACK(cb
), NULL
);
1582 else if (type
== GTK_TYPE_TOGGLE_BUTTON
|| type
== GTK_TYPE_RADIO_BUTTON
|| type
== GTK_TYPE_CHECK_BUTTON
)
1583 g_signal_connect(obj
, "toggled", G_CALLBACK(cb
), NULL
);
1584 else if (type
== GTK_TYPE_SPIN_BUTTON
|| type
== GTK_TYPE_ENTRY
)
1585 g_signal_connect(obj
, "changed", G_CALLBACK(cb
), "text");
1586 else if (type
== GTK_TYPE_SCALE
)
1587 g_signal_connect(obj
, "value-changed", G_CALLBACK(cb
), "value");
1588 else if (type
== GTK_TYPE_CALENDAR
) {
1589 g_signal_connect(obj
, "day-selected-double-click", G_CALLBACK(cb
), "doubleclicked");
1590 g_signal_connect(obj
, "day-selected", G_CALLBACK(cb
), "clicked");
1591 } else if (type
== GTK_TYPE_TREE_SELECTION
)
1592 g_signal_connect(obj
, "changed", G_CALLBACK(cb
), "clicked");
1593 else if (type
== GTK_TYPE_DRAWING_AREA
)
1594 g_signal_connect(obj
, "draw", G_CALLBACK(cb_draw
), NULL
);
1598 connect_signals(GtkBuilder
*builder
)
1600 GSList
*objects
= NULL
;
1602 objects
= gtk_builder_get_objects(builder
);
1603 g_slist_foreach(objects
, (GFunc
)connect_widget_signals
, builder
);
1604 g_slist_free(objects
);
1608 main(int argc
, char *argv
[])
1611 char *in_fifo
= NULL
, *out_fifo
= NULL
, *ui_file
= NULL
;
1612 GtkBuilder
*builder
;
1614 GError
*error
= NULL
;
1615 GObject
*main_window
= NULL
;
1619 while ((opt
= getopt(argc
, argv
, "hi:o:u:GV")) != -1) {
1621 case 'i': in_fifo
= optarg
; break;
1622 case 'o': out_fifo
= optarg
; break;
1623 case 'u': ui_file
= optarg
; break;
1624 case 'G': gtk_versions(); break;
1625 case 'V': version(); break;
1628 default: usage(argv
); break;
1631 if (argv
[optind
] != NULL
) {
1632 fprintf(stderr
, "illegal parameter '%s'\n", argv
[optind
]);
1635 if (ui_file
== NULL
)
1636 ui_file
= "pipeglade.ui";
1638 builder
= gtk_builder_new();
1639 if (gtk_builder_add_from_file(builder
, ui_file
, &error
) == 0) {
1640 fprintf(stderr
, "%s\n", error
->message
);
1643 in
= fifo(in_fifo
, "r");
1644 out
= fifo(out_fifo
, "w");
1645 pthread_create(&receiver
, NULL
, digest_msg
, (void*)builder
);
1646 main_window
= gtk_builder_get_object(builder
, MAIN_WIN
);
1647 if (!GTK_IS_WINDOW(main_window
)) {
1648 fprintf(stderr
, "no toplevel window named \'" MAIN_WIN
"\'\n");
1651 connect_signals(builder
);
1652 gtk_widget_show(GTK_WIDGET(main_window
));
1658 if (out
!= stdout
) {
1662 pthread_cancel(receiver
);
1663 pthread_join(receiver
, NULL
);