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.
24 #include <cairo-pdf.h>
26 #include <cairo-svg.h>
30 #include <gtk/gtkunixprint.h>
33 #include <libxml/xpath.h>
42 #include <sys/select.h>
47 #define VERSION "4.7.0"
49 #define WHITESPACE " \t\n"
50 #define MAIN_WIN "main"
52 "usage: pipeglade [[-i in-fifo] " \
55 "[-u glade-file.ui] " \
59 "[--display X-server]] | " \
68 __func__, __FILE__, __LINE__); \
75 "Out of memory in %s (%s:%d): ", \
76 __func__, __FILE__, __LINE__); \
80 static FILE *out
; /* UI feedback messages */
81 static FILE *save
; /* saving user data */
82 static FILE *log_out
; /* logging output */
83 static GtkBuilder
*builder
; /* to be read from .ui file */
87 * ============================================================
89 * ============================================================
93 * Check if s1 and s2 are equal strings
96 eql(const char *s1
, const char *s2
)
98 return s1
!= NULL
&& s2
!= NULL
&& strcmp(s1
, s2
) == 0;
102 * Print a formatted message to stream s and give up with status
105 bye(int status
, FILE *s
, const char *fmt
, ...)
110 vfprintf(s
, fmt
, ap
);
116 show_lib_versions(void)
118 bye(EXIT_SUCCESS
, stdout
,
119 "GTK+ v%d.%d.%d (running v%d.%d.%d)\n"
120 "cairo v%s (running v%s)\n",
121 GTK_MAJOR_VERSION
, GTK_MINOR_VERSION
, GTK_MICRO_VERSION
,
122 gtk_get_major_version(), gtk_get_minor_version(),
123 gtk_get_micro_version(),
124 CAIRO_VERSION_STRING
, cairo_version_string());
128 * XEmbed us if xid_s is given, or show a standalone window; give up
132 xembed_if(char *xid_s
, GObject
*main_window
)
134 GtkWidget
*plug
, *body
;
138 if (xid_s
== NULL
) { /* standalone */
139 gtk_widget_show(GTK_WIDGET(main_window
));
142 /* We're being XEmbedded */
143 xid
= strtoul(xid_s
, NULL
, 10);
144 snprintf(xid_s2
, BUFLEN
, "%lu", xid
);
145 if (!eql(xid_s
, xid_s2
))
146 bye(EXIT_FAILURE
, stderr
,
147 "%s is not a valid XEmbed socket id\n", xid_s
);
148 body
= gtk_bin_get_child(GTK_BIN(main_window
));
149 gtk_container_remove(GTK_CONTAINER(main_window
), body
);
150 plug
= gtk_plug_new(xid
);
151 if (!gtk_plug_get_embedded(GTK_PLUG(plug
)))
152 bye(EXIT_FAILURE
, stderr
,
153 "unable to embed into XEmbed socket %s\n", xid_s
);
154 gtk_container_add(GTK_CONTAINER(plug
), body
);
155 gtk_widget_show(plug
);
159 * If requested, redirect stderr to file name
162 redirect_stderr(const char *name
)
166 if (freopen(name
, "a", stderr
) == NULL
)
167 /* complaining on stdout since stderr is closed now */
168 bye(EXIT_FAILURE
, stdout
, "redirecting stderr to %s: %s\n",
169 name
, strerror(errno
));
170 if (fchmod(fileno(stderr
), 0600) < 0)
171 bye(EXIT_FAILURE
, stdout
, "setting permissions of %s: %s\n",
172 name
, strerror(errno
));
177 * fork() if requested in bg; give up on errors
180 go_bg_if(bool bg
, FILE *in
, FILE *out
, char *err_file
)
186 if (in
== stdin
|| out
== stdout
)
187 bye(EXIT_FAILURE
, stderr
,
188 "parameter -b requires both -i and -o\n");
191 bye(EXIT_FAILURE
, stderr
,
192 "going to background: %s\n", strerror(errno
));
194 bye(EXIT_SUCCESS
, stdout
, "%d\n", pid
);
195 /* We're the child */
196 close(fileno(stdin
)); /* making certain not-so-smart */
197 close(fileno(stdout
)); /* system/run-shell commands happy */
198 if (err_file
== NULL
)
199 freopen("/dev/null", "w", stderr
);
203 * Return the current locale and set it to "C". Should be free()d if
210 char *lc
= setlocale(LC_NUMERIC
, NULL
);
212 if ((lc_orig
= malloc(strlen(lc
) + 1)) == NULL
)
215 setlocale(LC_NUMERIC
, "C");
220 * Set locale (back) to lc; free lc
223 lc_numeric_free(char *lc
)
225 setlocale(LC_NUMERIC
, lc
);
230 * Print a warning about a malformed command to stderr
233 ign_cmd(GType type
, const char *msg
)
235 const char *name
, *pad
= " ";
237 if (type
== G_TYPE_INVALID
) {
241 name
= g_type_name(type
);
242 fprintf(stderr
, "ignoring %s%scommand \"%s\"\n", name
, pad
, msg
);
246 * Check if n is, or can be made, the name of a fifo. Give up if n
247 * exists but is not a fifo.
250 find_fifo(const char *n
)
255 if (S_ISFIFO(sb
.st_mode
)) {
256 if (chmod(n
, 0600) != 0)
257 bye(EXIT_FAILURE
, stderr
, "using pre-existing fifo %s: %s\n",
259 } else if (mkfifo(n
, 0600) != 0)
260 bye(EXIT_FAILURE
, stderr
, "making fifo %s: %s\n",
265 * Create a fifo if necessary, and open it. Give up if the file
266 * exists but is not a fifo
269 open_in_fifo(const char *name
)
278 if ((fd
= open(name
, O_RDWR
| O_NONBLOCK
)) < 0)
279 bye(EXIT_FAILURE
, stderr
, "opening fifo %s (r): %s\n",
280 name
, strerror(errno
));
281 if ((s
= fdopen(fd
, "r")) == NULL
)
282 bye(EXIT_FAILURE
, stderr
, "opening fifo %s (r): %s\n",
283 name
, strerror(errno
));
285 setvbuf(s
, NULL
, _IONBF
, 0);
290 open_out_fifo(const char *name
)
298 if ((s
= fopen(name
, "w+")) == NULL
)
299 bye(EXIT_FAILURE
, stderr
, "opening fifo %s (w): %s\n",
300 name
, strerror(errno
));
302 setvbuf(s
, NULL
, _IOLBF
, 0);
307 * Create a log file if necessary, and open it. A name of "-"
308 * requests use of stderr.
311 open_log(const char *name
)
319 if ((s
= fopen(name
, "a")) == NULL
)
320 bye(EXIT_FAILURE
, stderr
, "opening log file %s: %s\n",
321 name
, strerror(errno
));
322 if (fchmod(fileno(s
), 0600) < 0)
323 bye(EXIT_FAILURE
, stderr
, "setting permissions of %s: %s\n",
324 name
, strerror(errno
));
329 rm_unless(FILE *forbidden
, FILE *s
, char *name
)
338 * Microseconds elapsed since start
341 usec_since(struct timespec
*start
)
345 clock_gettime(CLOCK_MONOTONIC
, &now
);
346 return (now
.tv_sec
- start
->tv_sec
) * 1e6
+
347 (now
.tv_nsec
- start
->tv_nsec
) / 1e3
;
356 static char *old_msg
;
357 static struct timespec start
;
359 if (log_out
== NULL
) /* no logging */
361 if (msg
== NULL
&& old_msg
== NULL
)
363 "##########\t##### (New Pipeglade session) #####\n");
364 else if (msg
== NULL
&& old_msg
!= NULL
) { /* command done; start idle */
366 "%10ld\t%s\n", usec_since(&start
), old_msg
);
369 } else if (msg
!= NULL
&& old_msg
== NULL
) { /* idle done; start command */
371 "%10ld\t### (Idle) ###\n", usec_since(&start
));
372 if ((old_msg
= malloc(strlen(msg
) + 1)) == NULL
)
374 strcpy(old_msg
, msg
);
377 clock_gettime(CLOCK_MONOTONIC
, &start
);
381 has_suffix(const char *s
, const char *suffix
)
383 int s_suf
= strlen(s
) - strlen(suffix
);
387 return eql(suffix
, s
+ s_suf
);
391 * Remove suffix from name; find the object named like this
394 obj_sans_suffix(const char *suffix
, const char *name
)
396 char str
[BUFLEN
+ 1] = {'\0'};
399 str_l
= suffix
- name
;
400 strncpy(str
, name
, str_l
< BUFLEN
? str_l
: BUFLEN
);
401 return gtk_builder_get_object(builder
, str
);
405 * Read UI definition from ui_file; give up on errors
408 builder_from_file(char *ui_file
)
410 GError
*error
= NULL
;
413 b
= gtk_builder_new();
414 if (gtk_builder_add_from_file(b
, ui_file
, &error
) == 0)
415 bye(EXIT_FAILURE
, stderr
, "%s\n", error
->message
);
420 widget_name(GtkBuildable
*obj
)
422 return gtk_buildable_get_name(obj
);
426 * Get the main window; give up on errors
429 find_main_window(void)
433 if (GTK_IS_WINDOW(mw
= gtk_builder_get_object(builder
, MAIN_WIN
)))
435 bye(EXIT_FAILURE
, stderr
, "no toplevel window named \'" MAIN_WIN
"\'\n");
436 return NULL
; /* NOT REACHED */
440 * Store a line from stream s into buf, which should have been malloc'd
441 * to bufsize. Enlarge buf and bufsize if necessary.
444 read_buf(FILE *s
, char **buf
, size_t *bufsize
)
455 select(ifd
+ 1, &rfds
, NULL
, NULL
, NULL
);
457 if (c
== '\n' || feof(s
))
459 if (i
>= *bufsize
- 1)
460 if ((*buf
= realloc(*buf
, *bufsize
*= 2)) == NULL
)
465 case 'n': (*buf
)[i
++] = '\n'; break;
466 case 'r': (*buf
)[i
++] = '\r'; break;
467 default: (*buf
)[i
++] = c
; break;
469 } else if (c
== '\\')
480 * ============================================================
481 * Receiving feedback from the GUI
482 * ============================================================
486 send_msg_to(FILE* o
, GtkBuildable
*obj
, const char *tag
, va_list ap
)
489 const char *w_name
= widget_name(obj
);
492 struct timeval timeout
= {1, 0};
496 if (select(ofd
+ 1, NULL
, &wfds
, NULL
, &timeout
) == 1) {
497 fprintf(o
, "%s:%s ", w_name
, tag
);
498 while ((data
= va_arg(ap
, char *)) != NULL
) {
502 while ((c
= data
[i
++]) != '\0')
513 "send error; discarding feedback message %s:%s\n",
518 * Send GUI feedback to global stream "out". The message format is
519 * "<origin>:<tag> <data ...>". The variadic arguments are strings;
520 * last argument must be NULL.
523 send_msg(GtkBuildable
*obj
, const char *tag
, ...)
528 send_msg_to(out
, obj
, tag
, ap
);
533 * Send message from GUI to global stream "save". The message format
534 * is "<origin>:<tag> <data ...>". The variadic arguments are strings;
535 * last argument must be NULL.
538 save_msg(GtkBuildable
*obj
, const char *tag
, ...)
543 send_msg_to(save
, obj
, tag
, ap
);
548 * Send message from GUI to global stream "save". The message format
549 * is "<origin>:set <data ...>". The variadic arguments are strings;
550 * last argument must be NULL.
553 save_action_set_msg(GtkBuildable
*obj
, const char *tag
, ...)
558 send_msg_to(save
, obj
, "set", ap
);
563 * Use msg_sender() to send a message describing a particular cell
566 send_tree_cell_msg_by(void msg_sender(GtkBuildable
*, const char *, ...),
567 GtkTreeModel
*model
, const char *path_s
,
568 GtkTreeIter
*iter
, int col
, GtkBuildable
*obj
)
571 GValue value
= G_VALUE_INIT
;
572 char str
[BUFLEN
], *lc
= lc_numeric();
574 gtk_tree_model_get_value(model
, iter
, col
, &value
);
575 col_type
= gtk_tree_model_get_column_type(model
, col
);
578 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_int(&value
));
579 msg_sender(obj
, "gint", path_s
, str
, NULL
);
582 snprintf(str
, BUFLEN
, " %d %ld", col
, g_value_get_long(&value
));
583 msg_sender(obj
, "glong", path_s
, str
, NULL
);
586 snprintf(str
, BUFLEN
, " %d %" PRId64
, col
, g_value_get_int64(&value
));
587 msg_sender(obj
, "gint64", path_s
, str
, NULL
);
590 snprintf(str
, BUFLEN
, " %d %u", col
, g_value_get_uint(&value
));
591 msg_sender(obj
, "guint", path_s
, str
, NULL
);
594 snprintf(str
, BUFLEN
, " %d %lu", col
, g_value_get_ulong(&value
));
595 msg_sender(obj
, "gulong", path_s
, str
, NULL
);
598 snprintf(str
, BUFLEN
, " %d %" PRIu64
, col
, g_value_get_uint64(&value
));
599 msg_sender(obj
, "guint64", path_s
, str
, NULL
);
602 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_boolean(&value
));
603 msg_sender(obj
, "gboolean", path_s
, str
, NULL
);
606 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_float(&value
));
607 msg_sender(obj
, "gfloat", path_s
, str
, NULL
);
610 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_double(&value
));
611 msg_sender(obj
, "gdouble", path_s
, str
, NULL
);
614 snprintf(str
, BUFLEN
, " %d ", col
);
615 msg_sender(obj
, "gchararray", path_s
, str
, g_value_get_string(&value
), NULL
);
618 fprintf(stderr
, "column %d not implemented: %s\n", col
, G_VALUE_TYPE_NAME(&value
));
621 g_value_unset(&value
);
626 * Use msg_sender() to send one message per column for a single row
629 send_tree_row_msg_by(void msg_sender(GtkBuildable
*, const char *, ...),
630 GtkTreeModel
*model
, char *path_s
,
631 GtkTreeIter
*iter
, GtkBuildable
*obj
)
635 for (col
= 0; col
< gtk_tree_model_get_n_columns(model
); col
++)
636 send_tree_cell_msg_by(msg_sender
, model
, path_s
, iter
, col
, obj
);
640 * send_tree_row_msg serves as an argument for
641 * gtk_tree_selection_selected_foreach()
644 send_tree_row_msg(GtkTreeModel
*model
,
645 GtkTreePath
*path
, GtkTreeIter
*iter
, GtkBuildable
*obj
)
647 char *path_s
= gtk_tree_path_to_string(path
);
649 send_tree_row_msg_by(send_msg
, model
, path_s
, iter
, obj
);
655 * save_tree_row_msg serves as an argument for
656 * gtk_tree_model_foreach().
657 * Send message from GUI to global stream "save".
660 save_tree_row_msg(GtkTreeModel
*model
,
661 GtkTreePath
*path
, GtkTreeIter
*iter
, GtkBuildable
*obj
)
663 char *path_s
= gtk_tree_path_to_string(path
);
666 send_tree_row_msg_by(save_action_set_msg
, model
, path_s
, iter
, obj
);
672 cb_calendar(GtkBuildable
*obj
, const char *tag
)
675 unsigned int year
= 0, month
= 0, day
= 0;
677 gtk_calendar_get_date(GTK_CALENDAR(obj
), &year
, &month
, &day
);
678 snprintf(str
, BUFLEN
, "%04u-%02u-%02u", year
, ++month
, day
);
679 send_msg(obj
, tag
, str
, NULL
);
683 cb_color_button(GtkBuildable
*obj
, const char *tag
)
687 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
688 send_msg(obj
, tag
, gdk_rgba_to_string(&color
), NULL
);
692 cb_editable(GtkBuildable
*obj
, const char *tag
)
694 send_msg(obj
, tag
, gtk_entry_get_text(GTK_ENTRY(obj
)), NULL
);
698 * Callback that sends a message about a pointer device button press
702 cb_event_box_button(GtkBuildable
*obj
, GdkEvent
*e
, gpointer user_data
)
704 char data
[BUFLEN
], *lc
= lc_numeric();
706 snprintf(data
, BUFLEN
, "%d %.1lf %.1lf",
707 e
->button
.button
, e
->button
.x
, e
->button
.y
);
708 send_msg(obj
, user_data
, data
, NULL
);
714 * Callback that sends in a message the name of the key pressed when
715 * a GtkEventBox is focused
718 cb_event_box_key(GtkBuildable
*obj
, GdkEvent
*e
, gpointer user_data
)
720 send_msg(obj
, user_data
, gdk_keyval_name(e
->key
.keyval
), NULL
);
725 * Callback that sends a message about pointer device motion in a
729 cb_event_box_motion(GtkBuildable
*obj
, GdkEvent
*e
, gpointer user_data
)
731 char data
[BUFLEN
], *lc
= lc_numeric();
733 snprintf(data
, BUFLEN
, "%.1lf %.1lf", e
->button
.x
, e
->button
.y
);
734 send_msg(obj
, user_data
, data
, NULL
);
740 * Callback that only sends "name:tag" and returns false
743 cb_event_simple(GtkBuildable
*obj
, GdkEvent
*e
, const char *tag
)
746 send_msg(obj
, tag
, NULL
);
751 cb_file_chooser_button(GtkBuildable
*obj
, const char *tag
)
753 send_msg(obj
, tag
, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(obj
)), NULL
);
757 cb_font_button(GtkBuildable
*obj
, const char *tag
)
759 send_msg(obj
, tag
, gtk_font_button_get_font_name(GTK_FONT_BUTTON(obj
)), NULL
);
763 cb_menu_item(GtkBuildable
*obj
, const char *tag
)
765 send_msg(obj
, tag
, gtk_menu_item_get_label(GTK_MENU_ITEM(obj
)), NULL
);
769 cb_range(GtkBuildable
*obj
, const char *tag
)
771 char str
[BUFLEN
], *lc
= lc_numeric();
773 snprintf(str
, BUFLEN
, "%f", gtk_range_get_value(GTK_RANGE(obj
)));
774 send_msg(obj
, tag
, str
, NULL
);
779 * Callback that sends user's selection from a file dialog
782 cb_send_file_chooser_dialog_selection(gpointer user_data
)
784 send_msg(user_data
, "file",
785 gtk_file_chooser_get_filename(user_data
), NULL
);
786 send_msg(user_data
, "folder",
787 gtk_file_chooser_get_current_folder(user_data
), NULL
);
791 * Callback that sends in a message the content of the text buffer
792 * passed in user_data
795 cb_send_text(GtkBuildable
*obj
, gpointer user_data
)
799 gtk_text_buffer_get_bounds(user_data
, &a
, &b
);
800 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, TRUE
), NULL
);
804 * Callback that sends in a message the highlighted text from the text
805 * buffer which was passed in user_data
808 cb_send_text_selection(GtkBuildable
*obj
, gpointer user_data
)
812 gtk_text_buffer_get_selection_bounds(user_data
, &a
, &b
);
813 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, TRUE
), NULL
);
817 * Callback that only sends "name:tag" and returns true
820 cb_simple(GtkBuildable
*obj
, const char *tag
)
822 send_msg(obj
, tag
, NULL
);
827 cb_spin_button(GtkBuildable
*obj
, const char *tag
)
829 char str
[BUFLEN
], *lc
= lc_numeric();
831 snprintf(str
, BUFLEN
, "%f", gtk_spin_button_get_value(GTK_SPIN_BUTTON(obj
)));
832 send_msg(obj
, tag
, str
, NULL
);
837 cb_switch(GtkBuildable
*obj
, void *pspec
, void *user_data
)
841 send_msg(obj
, gtk_switch_get_active(GTK_SWITCH(obj
)) ? "1" : "0", NULL
);
845 cb_toggle_button(GtkBuildable
*obj
, const char *tag
)
848 send_msg(obj
, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(obj
)) ? "1" : "0", NULL
);
852 cb_tree_selection(GtkBuildable
*obj
, const char *tag
)
854 GtkTreeSelection
*sel
= GTK_TREE_SELECTION(obj
);
855 GtkTreeView
*view
= gtk_tree_selection_get_tree_view(sel
);
858 send_msg(GTK_BUILDABLE(view
), tag
, NULL
);
859 gtk_tree_selection_selected_foreach(
860 sel
, (GtkTreeSelectionForeachFunc
) send_tree_row_msg
, view
);
865 * ============================================================
866 * cb_draw() maintains a drawing on a GtkDrawingArea; it needs a few
868 * ============================================================
872 * The set of supported drawing operations
903 * Text placement mode for rel_move_for()
917 enum draw_op_policy
{
924 * One single element of a drawing
927 struct draw_op
*next
;
928 struct draw_op
*prev
;
929 unsigned long long int id
;
930 unsigned long long int before
;
931 enum draw_op_policy policy
;
937 * Argument sets for the various drawing operations
947 struct curve_to_args
{
956 struct move_to_args
{
961 struct rectangle_args
{
968 struct rel_move_for_args
{
974 struct set_dash_args
{
979 struct set_font_face_args
{
980 cairo_font_slant_t slant
;
981 cairo_font_weight_t weight
;
985 struct set_font_size_args
{
989 struct set_line_cap_args
{
990 cairo_line_cap_t line_cap
;
993 struct set_line_join_args
{
994 cairo_line_join_t line_join
;
997 struct set_line_width_args
{
1001 struct set_source_rgba_args
{
1005 struct show_text_args
{
1010 struct transform_args
{
1011 cairo_matrix_t matrix
;
1015 draw(cairo_t
*cr
, enum cairo_fn op
, void *op_args
)
1019 struct move_to_args
*args
= op_args
;
1021 cairo_line_to(cr
, args
->x
, args
->y
);
1025 struct move_to_args
*args
= op_args
;
1027 cairo_rel_line_to(cr
, args
->x
, args
->y
);
1031 struct move_to_args
*args
= op_args
;
1033 cairo_move_to(cr
, args
->x
, args
->y
);
1037 struct move_to_args
*args
= op_args
;
1039 cairo_rel_move_to(cr
, args
->x
, args
->y
);
1043 struct arc_args
*args
= op_args
;
1045 cairo_arc(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
1048 case ARC_NEGATIVE
: {
1049 struct arc_args
*args
= op_args
;
1051 cairo_arc_negative(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
1055 struct curve_to_args
*args
= op_args
;
1057 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
1060 case REL_CURVE_TO
: {
1061 struct curve_to_args
*args
= op_args
;
1063 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
1067 struct rectangle_args
*args
= op_args
;
1069 cairo_rectangle(cr
, args
->x
, args
->y
, args
->width
, args
->height
);
1073 cairo_close_path(cr
);
1076 struct show_text_args
*args
= op_args
;
1078 cairo_show_text(cr
, args
->text
);
1081 case REL_MOVE_FOR
: {
1082 cairo_text_extents_t e
;
1083 double dx
= 0.0, dy
= 0.0;
1084 struct rel_move_for_args
*args
= op_args
;
1086 cairo_text_extents(cr
, args
->text
, &e
);
1087 switch (args
->ref
) {
1088 case C
: dx
= -e
.width
/ 2; dy
= e
.height
/ 2; break;
1089 case E
: dx
= -e
.width
; dy
= e
.height
/ 2; break;
1090 case N
: dx
= -e
.width
/ 2; dy
= e
.height
; break;
1091 case NE
: dx
= -e
.width
; dy
= e
.height
; break;
1092 case NW
: dy
= e
.height
; break;
1093 case S
: dx
= -e
.width
/ 2; break;
1094 case SE
: dx
= -e
.width
; break;
1096 case W
: dy
= e
.height
/ 2; break;
1097 default: ABORT
; break;
1099 cairo_rel_move_to(cr
, dx
, dy
);
1103 cairo_identity_matrix(cr
);
1108 case STROKE_PRESERVE
:
1109 cairo_stroke_preserve(cr
);
1115 cairo_fill_preserve(cr
);
1118 struct set_dash_args
*args
= op_args
;
1120 cairo_set_dash(cr
, args
->dashes
, args
->num_dashes
, 0);
1123 case SET_FONT_FACE
: {
1124 struct set_font_face_args
*args
= op_args
;
1126 cairo_select_font_face(cr
, args
->family
, args
->slant
, args
->weight
);
1129 case SET_FONT_SIZE
: {
1130 struct set_font_size_args
*args
= op_args
;
1132 cairo_set_font_size(cr
, args
->size
);
1135 case SET_LINE_CAP
: {
1136 struct set_line_cap_args
*args
= op_args
;
1138 cairo_set_line_cap(cr
, args
->line_cap
);
1141 case SET_LINE_JOIN
: {
1142 struct set_line_join_args
*args
= op_args
;
1144 cairo_set_line_join(cr
, args
->line_join
);
1147 case SET_LINE_WIDTH
: {
1148 struct set_line_width_args
*args
= op_args
;
1150 cairo_set_line_width(cr
, args
->width
);
1153 case SET_SOURCE_RGBA
: {
1154 struct set_source_rgba_args
*args
= op_args
;
1156 gdk_cairo_set_source_rgba(cr
, &args
->color
);
1160 struct transform_args
*args
= op_args
;
1162 cairo_transform(cr
, &args
->matrix
);
1172 * Callback that draws on a GtkDrawingArea
1175 cb_draw(GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
1180 for (op
= g_object_get_data(G_OBJECT(widget
), "draw_ops");
1183 draw(cr
, op
->op
, op
->op_args
);
1189 * ============================================================
1190 * Manipulating the GUI
1191 * ============================================================
1195 update_button(GObject
*obj
, const char *action
,
1196 const char *data
, const char *whole_msg
, GType type
)
1198 if (eql(action
, "set_label"))
1199 gtk_button_set_label(GTK_BUTTON(obj
), data
);
1201 ign_cmd(type
, whole_msg
);
1205 update_calendar(GObject
*obj
, const char *action
,
1206 const char *data
, const char *whole_msg
, GType type
)
1208 GtkCalendar
*calendar
= GTK_CALENDAR(obj
);
1210 int year
= 0, month
= 0, day
= 0;
1212 if (eql(action
, "select_date") &&
1213 sscanf(data
, "%d-%d-%d %c", &year
, &month
, &day
, &dummy
) == 3) {
1214 if (month
> -1 && month
<= 11 && day
> 0 && day
<= 31) {
1215 gtk_calendar_select_month(calendar
, --month
, year
);
1216 gtk_calendar_select_day(calendar
, day
);
1218 ign_cmd(type
, whole_msg
);
1219 } else if (eql(action
, "mark_day") &&
1220 sscanf(data
, "%d %c", &day
, &dummy
) == 1) {
1221 if (day
> 0 && day
<= 31)
1222 gtk_calendar_mark_day(calendar
, day
);
1224 ign_cmd(type
, whole_msg
);
1225 } else if (eql(action
, "clear_marks") && sscanf(data
, " %c", &dummy
) < 1)
1226 gtk_calendar_clear_marks(calendar
);
1228 ign_cmd(type
, whole_msg
);
1232 * Common actions for various kinds of window. Return false if
1233 * command is ignored
1236 update_class_window(GObject
*obj
, const char *action
,
1237 const char *data
, const char *whole_msg
, GType type
)
1239 GtkWindow
*window
= GTK_WINDOW(obj
);
1245 if (eql(action
, "set_title"))
1246 gtk_window_set_title(window
, data
);
1247 else if (eql(action
, "fullscreen") && sscanf(data
, " %c", &dummy
) < 1)
1248 gtk_window_fullscreen(window
);
1249 else if (eql(action
, "unfullscreen") && sscanf(data
, " %c", &dummy
) < 1)
1250 gtk_window_unfullscreen(window
);
1251 else if (eql(action
, "resize") &&
1252 sscanf(data
, "%d %d %c", &x
, &y
, &dummy
) == 2)
1253 gtk_window_resize(window
, x
, y
);
1254 else if (eql(action
, "resize") && sscanf(data
, " %c", &dummy
) < 1) {
1255 gtk_window_get_default_size(window
, &x
, &y
);
1256 gtk_window_resize(window
, x
, y
);
1257 } else if (eql(action
, "move") &&
1258 sscanf(data
, "%d %d %c", &x
, &y
, &dummy
) == 2)
1259 gtk_window_move(window
, x
, y
);
1266 update_color_button(GObject
*obj
, const char *action
,
1267 const char *data
, const char *whole_msg
, GType type
)
1271 if (eql(action
, "set_color")) {
1272 gdk_rgba_parse(&color
, data
);
1273 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
1275 ign_cmd(type
, whole_msg
);
1279 update_combo_box_text(GObject
*obj
, const char *action
,
1280 const char *data
, const char *whole_msg
, GType type
)
1282 GtkComboBoxText
*combobox
= GTK_COMBO_BOX_TEXT(obj
);
1283 char data1
[strlen(data
) + 1];
1287 strcpy(data1
, data
);
1288 if (eql(action
, "prepend_text"))
1289 gtk_combo_box_text_prepend_text(combobox
, data1
);
1290 else if (eql(action
, "append_text"))
1291 gtk_combo_box_text_append_text(combobox
, data1
);
1292 else if (eql(action
, "remove") && sscanf(data
, "%d %c", &val
, &dummy
) == 1)
1293 gtk_combo_box_text_remove(combobox
, strtol(data1
, NULL
, 10));
1294 else if (eql(action
, "insert_text")) {
1295 char *position
= strtok(data1
, WHITESPACE
);
1296 char *text
= strtok(NULL
, WHITESPACE
);
1298 gtk_combo_box_text_insert_text(combobox
,
1299 strtol(position
, NULL
, 10), text
);
1301 ign_cmd(type
, whole_msg
);
1305 * Maintaining a list of drawing operations. It is the responsibility
1306 * of cb_draw() to actually draw them. update_drawing_area() needs a
1307 * few helper functions.
1317 * Fill structure *op with the drawing operation according to action
1318 * and with the appropriate set of arguments
1320 static enum draw_op_stat
1321 set_draw_op(struct draw_op
*op
, const char *action
, const char *data
)
1324 const char *raw_args
= data
;
1325 enum draw_op_stat result
= SUCCESS
;
1328 if (sscanf(data
, "=%llu %n", &op
->id
, &args_start
) == 1) {
1329 op
->policy
= REPLACE
;
1330 result
= NEED_REDRAW
;
1331 } else if (sscanf(data
, "%llu<%llu %n", &op
->id
, &op
->before
, &args_start
) == 2) {
1332 op
->policy
= BEFORE
;
1333 result
= NEED_REDRAW
;
1334 } else if (sscanf(data
, "%llu %n", &op
->id
, &args_start
) == 1)
1335 op
->policy
= APPEND
;
1338 raw_args
+= args_start
;
1339 if (eql(action
, "line_to")) {
1340 struct move_to_args
*args
;
1342 if ((args
= malloc(sizeof(*args
))) == NULL
)
1346 if (sscanf(raw_args
, "%lf %lf %c", &args
->x
, &args
->y
, &dummy
) != 2)
1348 } else if (eql(action
, "rel_line_to")) {
1349 struct move_to_args
*args
;
1351 if ((args
= malloc(sizeof(*args
))) == NULL
)
1353 op
->op
= REL_LINE_TO
;
1355 if (sscanf(raw_args
, "%lf %lf %c", &args
->x
, &args
->y
, &dummy
) != 2)
1357 } else if (eql(action
, "move_to")) {
1358 struct move_to_args
*args
;
1360 if ((args
= malloc(sizeof(*args
))) == NULL
)
1364 if (sscanf(raw_args
, "%lf %lf %c", &args
->x
, &args
->y
, &dummy
) != 2)
1366 } else if (eql(action
, "rel_move_to")) {
1367 struct move_to_args
*args
;
1369 if ((args
= malloc(sizeof(*args
))) == NULL
)
1371 op
->op
= REL_MOVE_TO
;
1373 if (sscanf(raw_args
, "%lf %lf %c", &args
->x
, &args
->y
, &dummy
) != 2)
1375 } else if (eql(action
, "arc")) {
1376 struct arc_args
*args
;
1379 if ((args
= malloc(sizeof(*args
))) == NULL
)
1383 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %c",
1384 &args
->x
, &args
->y
, &args
->radius
, °1
, °2
, &dummy
) != 5)
1386 args
->angle1
= deg1
* (M_PI
/ 180.L
);
1387 args
->angle2
= deg2
* (M_PI
/ 180.L
);
1388 } else if (eql(action
, "arc_negative")) {
1390 struct arc_args
*args
;
1392 if ((args
= malloc(sizeof(*args
))) == NULL
)
1394 op
->op
= ARC_NEGATIVE
;
1396 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %c",
1397 &args
->x
, &args
->y
, &args
->radius
, °1
, °2
, &dummy
) != 5)
1399 args
->angle1
= deg1
* (M_PI
/ 180.L
);
1400 args
->angle2
= deg2
* (M_PI
/ 180.L
);
1401 } else if (eql(action
, "curve_to")) {
1402 struct curve_to_args
*args
;
1404 if ((args
= malloc(sizeof(*args
))) == NULL
)
1408 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %lf %c",
1409 &args
->x1
, &args
->y1
, &args
->x2
, &args
->y2
, &args
->x3
, &args
->y3
, &dummy
) != 6)
1411 } else if (eql(action
, "rel_curve_to")) {
1412 struct curve_to_args
*args
;
1414 if ((args
= malloc(sizeof(*args
))) == NULL
)
1416 op
->op
= REL_CURVE_TO
;
1418 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %lf %c",
1419 &args
->x1
, &args
->y1
, &args
->x2
, &args
->y2
, &args
->x3
, &args
->y3
, &dummy
) != 6)
1421 } else if (eql(action
, "rectangle")) {
1422 struct rectangle_args
*args
;
1424 if ((args
= malloc(sizeof(*args
))) == NULL
)
1428 if (sscanf(raw_args
, "%lf %lf %lf %lf %c",
1429 &args
->x
, &args
->y
, &args
->width
, &args
->height
, &dummy
) != 4)
1431 } else if (eql(action
, "close_path")) {
1432 op
->op
= CLOSE_PATH
;
1433 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1436 } else if (eql(action
, "show_text")) {
1437 struct show_text_args
*args
;
1440 len
= strlen(raw_args
) + 1;
1441 if ((args
= malloc(sizeof(*args
) + len
* sizeof(args
->text
[0]))) == NULL
)
1445 args
->len
= len
; /* not used */
1446 strncpy(args
->text
, raw_args
, len
);
1447 result
= NEED_REDRAW
;
1448 } else if (eql(action
, "rel_move_for")) {
1449 char ref_point
[2 + 1];
1451 struct rel_move_for_args
*args
;
1453 if (sscanf(raw_args
, "%2s %n", ref_point
, &start
) < 1)
1455 len
= strlen(raw_args
+ start
) + 1;
1456 if ((args
= malloc(sizeof(*args
) + len
* sizeof(args
->text
[0]))) == NULL
)
1458 if (eql(ref_point
, "c"))
1460 else if (eql(ref_point
, "e"))
1462 else if (eql(ref_point
, "n"))
1464 else if (eql(ref_point
, "ne"))
1466 else if (eql(ref_point
, "nw"))
1468 else if (eql(ref_point
, "s"))
1470 else if (eql(ref_point
, "se"))
1472 else if (eql(ref_point
, "sw"))
1474 else if (eql(ref_point
, "w"))
1478 op
->op
= REL_MOVE_FOR
;
1480 args
->len
= len
; /* not used */
1481 strncpy(args
->text
, (raw_args
+ start
), len
);
1482 } else if (eql(action
, "stroke")) {
1484 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1487 result
= NEED_REDRAW
;
1488 } else if (eql(action
, "stroke_preserve")) {
1489 op
->op
= STROKE_PRESERVE
;
1490 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1493 result
= NEED_REDRAW
;
1494 } else if (eql(action
, "fill")) {
1496 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1499 result
= NEED_REDRAW
;
1500 } else if (eql(action
, "fill_preserve")) {
1501 op
->op
= FILL_PRESERVE
;
1502 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1505 result
= NEED_REDRAW
;
1506 } else if (eql(action
, "set_dash")) {
1508 char data1
[strlen(raw_args
) + 1];
1510 struct set_dash_args
*args
;
1512 strcpy(data1
, raw_args
);
1519 } while (next
!= end
);
1520 if ((args
= malloc(sizeof(*args
) + n
* sizeof(args
->dashes
[0]))) == NULL
)
1524 args
->num_dashes
= n
;
1525 for (i
= 0, next
= data1
; i
< n
; i
++, next
= end
) {
1526 args
->dashes
[i
] = strtod(next
, &end
);
1528 } else if (eql(action
, "set_font_face")) {
1529 char slant
[7 + 1]; /* "oblique" */
1530 char weight
[6 + 1]; /* "normal" */
1531 int family_start
, family_len
;
1532 struct set_font_face_args
*args
;
1534 if (sscanf(raw_args
, "%7s %6s %n%*s", slant
, weight
, &family_start
) != 2)
1536 family_len
= strlen(raw_args
+ family_start
) + 1;
1537 if ((args
= malloc(sizeof(*args
) + family_len
* sizeof(args
->family
[0]))) == NULL
)
1539 op
->op
= SET_FONT_FACE
;
1541 strncpy(args
->family
, raw_args
+ family_start
, family_len
);
1542 if (eql(slant
, "normal"))
1543 args
->slant
= CAIRO_FONT_SLANT_NORMAL
;
1544 else if (eql(slant
, "italic"))
1545 args
->slant
= CAIRO_FONT_SLANT_ITALIC
;
1546 else if (eql(slant
, "oblique"))
1547 args
->slant
= CAIRO_FONT_SLANT_OBLIQUE
;
1550 if (eql(weight
, "normal"))
1551 args
->weight
= CAIRO_FONT_WEIGHT_NORMAL
;
1552 else if (eql(weight
, "bold"))
1553 args
->weight
= CAIRO_FONT_WEIGHT_BOLD
;
1556 } else if (eql(action
, "set_font_size")) {
1557 struct set_font_size_args
*args
;
1559 if ((args
= malloc(sizeof(*args
))) == NULL
)
1561 op
->op
= SET_FONT_SIZE
;
1563 if (sscanf(raw_args
, "%lf %c", &args
->size
, &dummy
) != 1)
1565 } else if (eql(action
, "set_line_cap")) {
1566 char str
[6 + 1]; /* "square" */
1567 struct set_line_cap_args
*args
;
1569 if ((args
= malloc(sizeof(*args
))) == NULL
)
1571 op
->op
= SET_LINE_CAP
;
1573 if (sscanf(raw_args
, "%6s %c", str
, &dummy
) != 1)
1575 if (eql(str
, "butt"))
1576 args
->line_cap
= CAIRO_LINE_CAP_BUTT
;
1577 else if (eql(str
, "round"))
1578 args
->line_cap
= CAIRO_LINE_CAP_ROUND
;
1579 else if (eql(str
, "square"))
1580 args
->line_cap
= CAIRO_LINE_CAP_SQUARE
;
1583 } else if (eql(action
, "set_line_join")) {
1584 char str
[5 + 1]; /* "miter" */
1585 struct set_line_join_args
*args
;
1587 if ((args
= malloc(sizeof(*args
))) == NULL
)
1589 op
->op
= SET_LINE_JOIN
;
1591 if (sscanf(raw_args
, "%5s %c", str
, &dummy
) != 1)
1593 if (eql(str
, "miter"))
1594 args
->line_join
= CAIRO_LINE_JOIN_MITER
;
1595 else if (eql(str
, "round"))
1596 args
->line_join
= CAIRO_LINE_JOIN_ROUND
;
1597 else if (eql(str
, "bevel"))
1598 args
->line_join
= CAIRO_LINE_JOIN_BEVEL
;
1601 } else if (eql(action
, "set_line_width")) {
1602 struct set_line_width_args
*args
;
1604 if ((args
= malloc(sizeof(*args
))) == NULL
)
1606 op
->op
= SET_LINE_WIDTH
;
1608 if (sscanf(raw_args
, "%lf %c", &args
->width
, &dummy
) != 1)
1610 } else if (eql(action
, "set_source_rgba")) {
1611 struct set_source_rgba_args
*args
;
1613 if ((args
= malloc(sizeof(*args
))) == NULL
)
1615 op
->op
= SET_SOURCE_RGBA
;
1617 gdk_rgba_parse(&args
->color
, raw_args
);
1618 } else if (eql(action
, "transform")) {
1620 double xx
, yx
, xy
, yy
, x0
, y0
;
1622 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %lf %c",
1623 &xx
, &yx
, &xy
, &yy
, &x0
, &y0
, &dummy
) == 6) {
1624 struct transform_args
*args
;
1626 if ((args
= malloc(sizeof(*args
))) == NULL
)
1630 cairo_matrix_init(&args
->matrix
, xx
, yx
, xy
, yy
, x0
, y0
);
1631 } else if (sscanf(raw_args
, " %c", &dummy
) < 1) {
1636 } else if (eql(action
, "translate")) {
1638 struct transform_args
*args
;
1640 if ((args
= malloc(sizeof(*args
))) == NULL
)
1644 if (sscanf(raw_args
, "%lf %lf %c", &tx
, &ty
, &dummy
) != 2)
1646 cairo_matrix_init_translate(&args
->matrix
, tx
, ty
);
1647 } else if (eql(action
, "scale")) {
1649 struct transform_args
*args
;
1651 if ((args
= malloc(sizeof(*args
))) == NULL
)
1655 if (sscanf(raw_args
, "%lf %lf %c", &sx
, &sy
, &dummy
) != 2)
1657 cairo_matrix_init_scale(&args
->matrix
, sx
, sy
);
1658 } else if (eql(action
, "rotate")) {
1660 struct transform_args
*args
;
1662 if ((args
= malloc(sizeof(*args
))) == NULL
)
1666 if (sscanf(raw_args
, "%lf %c", &angle
, &dummy
) != 1)
1668 cairo_matrix_init_rotate(&args
->matrix
, angle
* (M_PI
/ 180.L
));
1675 * Add another element to widget's "draw_ops" list
1677 static enum draw_op_stat
1678 ins_draw_op(GObject
*widget
, const char *action
, const char *data
)
1680 enum draw_op_stat result
;
1681 struct draw_op
*new_op
= NULL
, *draw_ops
= NULL
, *prev_op
= NULL
;
1683 if ((new_op
= malloc(sizeof(*new_op
))) == NULL
)
1685 new_op
->op_args
= NULL
;
1686 new_op
->next
= NULL
;
1687 if ((result
= set_draw_op(new_op
, action
, data
)) == FAILURE
) {
1688 free(new_op
->op_args
);
1692 switch (new_op
->policy
) {
1694 if ((draw_ops
= g_object_get_data(widget
, "draw_ops")) == NULL
)
1695 g_object_set_data(widget
, "draw_ops", new_op
);
1697 for (prev_op
= draw_ops
;
1698 prev_op
->next
!= NULL
;
1699 prev_op
= prev_op
->next
);
1700 prev_op
->next
= new_op
;
1704 for (prev_op
= NULL
, draw_ops
= g_object_get_data(widget
, "draw_ops");
1705 draw_ops
!= NULL
&& draw_ops
->id
!= new_op
->before
;
1706 prev_op
= draw_ops
, draw_ops
= draw_ops
->next
);
1707 if (prev_op
== NULL
) { /* prepend a new first element */
1708 g_object_set_data(widget
, "draw_ops", new_op
);
1709 new_op
->next
= draw_ops
;
1710 } else if (draw_ops
== NULL
) /* append */
1711 prev_op
->next
= new_op
;
1713 new_op
->next
= draw_ops
;
1714 prev_op
->next
= new_op
;
1718 for (prev_op
= NULL
, draw_ops
= g_object_get_data(widget
, "draw_ops");
1719 draw_ops
!= NULL
&& draw_ops
->id
!= new_op
->id
;
1720 prev_op
= draw_ops
, draw_ops
= draw_ops
->next
);
1721 if (draw_ops
== NULL
&& prev_op
== NULL
) /* start a new list */
1722 g_object_set_data(widget
, "draw_ops", new_op
);
1723 else if (prev_op
== NULL
) { /* replace the first element */
1724 g_object_set_data(widget
, "draw_ops", new_op
);
1725 new_op
->next
= draw_ops
->next
;
1726 free(draw_ops
->op_args
);
1728 } else if (draw_ops
== NULL
) /* append */
1729 prev_op
->next
= new_op
;
1730 else { /* replace some other element */
1731 new_op
->next
= draw_ops
->next
;
1732 prev_op
->next
= new_op
;
1733 free(draw_ops
->op_args
);
1745 * Remove all elements with the given id from widget's "draw_ops" list
1747 static enum draw_op_stat
1748 rem_draw_op(GObject
*widget
, const char *data
)
1751 struct draw_op
*op
, *next_op
, *prev_op
= NULL
;
1752 unsigned long long int id
;
1754 if (sscanf(data
, "%llu %c", &id
, &dummy
) != 1)
1756 op
= g_object_get_data(widget
, "draw_ops");
1757 while (op
!= NULL
) {
1760 if (prev_op
== NULL
) /* list head */
1761 g_object_set_data(widget
, "draw_ops", op
->next
);
1763 prev_op
->next
= op
->next
;
1774 refresh_widget(GtkWidget
*widget
)
1776 gint height
= gtk_widget_get_allocated_height(widget
);
1777 gint width
= gtk_widget_get_allocated_width(widget
);
1779 gtk_widget_queue_draw_area(widget
, 0, 0, width
, height
);
1780 return G_SOURCE_REMOVE
;
1784 * Write the drawing from the GtkDrawingArea widget in an appropriate
1787 static enum draw_op_stat
1788 save_drawing(GtkWidget
*widget
, const char *fn
)
1790 cairo_surface_t
*sur
;
1792 int height
= gtk_widget_get_allocated_height(widget
);
1793 int width
= gtk_widget_get_allocated_width(widget
);
1795 if (has_suffix(fn
, ".epsf") || has_suffix(fn
, ".eps")) {
1796 sur
= cairo_ps_surface_create(fn
, width
, height
);
1797 cairo_ps_surface_set_eps(sur
, TRUE
);
1798 } else if (has_suffix(fn
, ".pdf"))
1799 sur
= cairo_pdf_surface_create(fn
, width
, height
);
1800 else if (has_suffix(fn
, ".ps"))
1801 sur
= cairo_ps_surface_create(fn
, width
, height
);
1802 else if (has_suffix(fn
, ".svg"))
1803 sur
= cairo_svg_surface_create(fn
, width
, height
);
1806 cr
= cairo_create(sur
);
1807 cb_draw(widget
, cr
, NULL
);
1809 cairo_surface_destroy(sur
);
1814 update_drawing_area(GObject
*obj
, const char *action
,
1815 const char *data
, const char *whole_msg
, GType type
)
1817 enum draw_op_stat dost
;
1819 if (eql(action
, "remove"))
1820 dost
= rem_draw_op(obj
, data
);
1821 else if (eql(action
, "save"))
1822 dost
= save_drawing(GTK_WIDGET(obj
), data
);
1824 dost
= ins_draw_op(obj
, action
, data
);
1827 gdk_threads_add_idle_full(G_PRIORITY_LOW
,
1828 (GSourceFunc
) refresh_widget
,
1829 GTK_WIDGET(obj
), NULL
);
1832 ign_cmd(type
, whole_msg
);
1843 update_entry(GObject
*obj
, const char *action
,
1844 const char *data
, const char *whole_msg
, GType type
)
1846 GtkEntry
*entry
= GTK_ENTRY(obj
);
1848 if (eql(action
, "set_text"))
1849 gtk_entry_set_text(entry
, data
);
1850 else if (eql(action
, "set_placeholder_text"))
1851 gtk_entry_set_placeholder_text(entry
, data
);
1853 ign_cmd(type
, whole_msg
);
1857 update_expander(GObject
*obj
, const char *action
,
1858 const char *data
, const char *whole_msg
, GType type
)
1860 GtkExpander
*expander
= GTK_EXPANDER(obj
);
1864 if (eql(action
, "set_expanded") &&
1865 sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
1866 gtk_expander_set_expanded(expander
, val
);
1867 else if (eql(action
, "set_label"))
1868 gtk_expander_set_label(expander
, data
);
1870 ign_cmd(type
, whole_msg
);
1874 update_file_chooser_button(GObject
*obj
, const char *action
,
1875 const char *data
, const char *whole_msg
, GType type
)
1877 if (eql(action
, "set_filename"))
1878 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(obj
), data
);
1880 ign_cmd(type
, whole_msg
);
1884 update_file_chooser_dialog(GObject
*obj
, const char *action
,
1885 const char *data
, const char *whole_msg
, GType type
)
1887 GtkFileChooser
*chooser
= GTK_FILE_CHOOSER(obj
);
1889 if (eql(action
, "set_filename"))
1890 gtk_file_chooser_set_filename(chooser
, data
);
1891 else if (eql(action
, "set_current_name"))
1892 gtk_file_chooser_set_current_name(chooser
, data
);
1893 else if (update_class_window(obj
, action
, data
, whole_msg
, type
));
1895 ign_cmd(type
, whole_msg
);
1899 update_focus(GObject
*obj
, const char *action
,
1900 const char *data
, const char *whole_msg
, GType type
)
1906 if (sscanf(data
, " %c", &dummy
) < 1 &&
1907 gtk_widget_get_can_focus(GTK_WIDGET(obj
)))
1908 gtk_widget_grab_focus(GTK_WIDGET(obj
));
1910 ign_cmd(type
, whole_msg
);
1914 update_font_button(GObject
*obj
, const char *action
,
1915 const char *data
, const char *whole_msg
, GType type
)
1917 GtkFontButton
*font_button
= GTK_FONT_BUTTON(obj
);
1919 if (eql(action
, "set_font_name"))
1920 gtk_font_button_set_font_name(font_button
, data
);
1922 ign_cmd(type
, whole_msg
);
1926 update_frame(GObject
*obj
, const char *action
,
1927 const char *data
, const char *whole_msg
, GType type
)
1929 if (eql(action
, "set_label"))
1930 gtk_frame_set_label(GTK_FRAME(obj
), data
);
1932 ign_cmd(type
, whole_msg
);
1936 update_image(GObject
*obj
, const char *action
,
1937 const char *data
, const char *whole_msg
, GType type
)
1940 GtkImage
*image
= GTK_IMAGE(obj
);
1942 gtk_image_get_icon_name(image
, NULL
, &size
);
1943 if (eql(action
, "set_from_file"))
1944 gtk_image_set_from_file(image
, data
);
1945 else if (eql(action
, "set_from_icon_name"))
1946 gtk_image_set_from_icon_name(image
, data
, size
);
1948 ign_cmd(type
, whole_msg
);
1952 update_label(GObject
*obj
, const char *action
,
1953 const char *data
, const char *whole_msg
, GType type
)
1955 if (eql(action
, "set_text"))
1956 gtk_label_set_text(GTK_LABEL(obj
), data
);
1958 ign_cmd(type
, whole_msg
);
1962 update_notebook(GObject
*obj
, const char *action
,
1963 const char *data
, const char *whole_msg
, GType type
)
1966 int val
, n_pages
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(obj
));
1968 if (eql(action
, "set_current_page") &&
1969 sscanf(data
, "%d %c", &val
, &dummy
) == 1 &&
1970 val
>= 0 && val
< n_pages
)
1971 gtk_notebook_set_current_page(GTK_NOTEBOOK(obj
), val
);
1973 ign_cmd(type
, whole_msg
);
1977 update_nothing(GObject
*obj
, const char *action
,
1978 const char *data
, const char *whole_msg
, GType type
)
1988 update_print_dialog(GObject
*obj
, const char *action
,
1989 const char *data
, const char *whole_msg
, GType type
)
1991 GtkPageSetup
*page_setup
;
1993 GtkPrintSettings
*settings
;
1994 GtkPrintUnixDialog
*dialog
= GTK_PRINT_UNIX_DIALOG(obj
);
1995 GtkPrinter
*printer
;
1998 if (eql(action
, "print")) {
1999 response_id
= gtk_dialog_run(GTK_DIALOG(dialog
));
2000 switch (response_id
) {
2001 case GTK_RESPONSE_OK
:
2002 printer
= gtk_print_unix_dialog_get_selected_printer(dialog
);
2003 settings
= gtk_print_unix_dialog_get_settings(dialog
);
2004 page_setup
= gtk_print_unix_dialog_get_page_setup(dialog
);
2005 job
= gtk_print_job_new(data
, printer
, settings
, page_setup
);
2006 if (gtk_print_job_set_source_file(job
, data
, NULL
))
2007 gtk_print_job_send(job
, NULL
, NULL
, NULL
);
2009 ign_cmd(type
, whole_msg
);
2010 g_clear_object(&settings
);
2011 g_clear_object(&job
);
2013 case GTK_RESPONSE_CANCEL
:
2014 case GTK_RESPONSE_DELETE_EVENT
:
2017 fprintf(stderr
, "%s sent an unexpected response id (%d)\n",
2018 widget_name(GTK_BUILDABLE(dialog
)), response_id
);
2021 gtk_widget_hide(GTK_WIDGET(dialog
));
2023 ign_cmd(type
, whole_msg
);
2027 update_progress_bar(GObject
*obj
, const char *action
,
2028 const char *data
, const char *whole_msg
, GType type
)
2030 GtkProgressBar
*progressbar
= GTK_PROGRESS_BAR(obj
);
2034 if (eql(action
, "set_text"))
2035 gtk_progress_bar_set_text(progressbar
, *data
== '\0' ? NULL
: data
);
2036 else if (eql(action
, "set_fraction") &&
2037 sscanf(data
, "%lf %c", &frac
, &dummy
) == 1)
2038 gtk_progress_bar_set_fraction(progressbar
, frac
);
2040 ign_cmd(type
, whole_msg
);
2044 update_scale(GObject
*obj
, const char *action
,
2045 const char *data
, const char *whole_msg
, GType type
)
2047 GtkRange
*range
= GTK_RANGE(obj
);
2051 if (eql(action
, "set_value") && sscanf(data
, "%lf %c", &val1
, &dummy
) == 1)
2052 gtk_range_set_value(range
, val1
);
2053 else if (eql(action
, "set_fill_level") &&
2054 sscanf(data
, "%lf %c", &val1
, &dummy
) == 1) {
2055 gtk_range_set_fill_level(range
, val1
);
2056 gtk_range_set_show_fill_level(range
, TRUE
);
2057 } else if (eql(action
, "set_fill_level") &&
2058 sscanf(data
, " %c", &dummy
) < 1)
2059 gtk_range_set_show_fill_level(range
, FALSE
);
2060 else if (eql(action
, "set_range") &&
2061 sscanf(data
, "%lf %lf %c", &val1
, &val2
, &dummy
) == 2)
2062 gtk_range_set_range(range
, val1
, val2
);
2063 else if (eql(action
, "set_increments") &&
2064 sscanf(data
, "%lf %lf %c", &val1
, &val2
, &dummy
) == 2)
2065 gtk_range_set_increments(range
, val1
, val2
);
2067 ign_cmd(type
, whole_msg
);
2071 update_scrolled_window(GObject
*obj
, const char *action
,
2072 const char *data
, const char *whole_msg
, GType type
)
2074 GtkScrolledWindow
*window
= GTK_SCROLLED_WINDOW(obj
);
2075 GtkAdjustment
*hadj
= gtk_scrolled_window_get_hadjustment(window
);
2076 GtkAdjustment
*vadj
= gtk_scrolled_window_get_vadjustment(window
);
2080 if (eql(action
, "hscroll") && sscanf(data
, "%lf %c", &d0
, &dummy
) == 1)
2081 gtk_adjustment_set_value(hadj
, d0
);
2082 else if (eql(action
, "vscroll") && sscanf(data
, "%lf %c", &d0
, &dummy
) == 1)
2083 gtk_adjustment_set_value(vadj
, d0
);
2084 else if (eql(action
, "hscroll_to_range") &&
2085 sscanf(data
, "%lf %lf %c", &d0
, &d1
, &dummy
) == 2)
2086 gtk_adjustment_clamp_page(hadj
, d0
, d1
);
2087 else if (eql(action
, "vscroll_to_range") &&
2088 sscanf(data
, "%lf %lf %c", &d0
, &d1
, &dummy
) == 2)
2089 gtk_adjustment_clamp_page(vadj
, d0
, d1
);
2091 ign_cmd(type
, whole_msg
);
2095 update_sensitivity(GObject
*obj
, const char *action
,
2096 const char *data
, const char *whole_msg
, GType type
)
2104 if (sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
2105 gtk_widget_set_sensitive(GTK_WIDGET(obj
), val
);
2107 ign_cmd(type
, whole_msg
);
2111 update_size_request(GObject
*obj
, const char *action
,
2112 const char *data
, const char *whole_msg
, GType type
)
2120 if (sscanf(data
, "%d %d %c", &x
, &y
, &dummy
) == 2)
2121 gtk_widget_set_size_request(GTK_WIDGET(obj
), x
, y
);
2122 else if (sscanf(data
, " %c", &dummy
) < 1)
2123 gtk_widget_set_size_request(GTK_WIDGET(obj
), -1, -1);
2125 ign_cmd(type
, whole_msg
);
2129 update_socket(GObject
*obj
, const char *action
,
2130 const char *data
, const char *whole_msg
, GType type
)
2132 GtkSocket
*socket
= GTK_SOCKET(obj
);
2134 char str
[BUFLEN
], dummy
;
2137 if (eql(action
, "id") && sscanf(data
, " %c", &dummy
) < 1) {
2138 id
= gtk_socket_get_id(socket
);
2139 snprintf(str
, BUFLEN
, "%lu", id
);
2140 send_msg(GTK_BUILDABLE(socket
), "id", str
, NULL
);
2142 ign_cmd(type
, whole_msg
);
2146 update_spin_button(GObject
*obj
, const char *action
,
2147 const char *data
, const char *whole_msg
, GType type
)
2149 GtkSpinButton
*spinbutton
= GTK_SPIN_BUTTON(obj
);
2153 if (eql(action
, "set_text") && /* TODO: rename to "set_value" */
2154 sscanf(data
, "%lf %c", &val1
, &dummy
) == 1)
2155 gtk_spin_button_set_value(spinbutton
, val1
);
2156 else if (eql(action
, "set_range") &&
2157 sscanf(data
, "%lf %lf %c", &val1
, &val2
, &dummy
) == 2)
2158 gtk_spin_button_set_range(spinbutton
, val1
, val2
);
2159 else if (eql(action
, "set_increments") &&
2160 sscanf(data
, "%lf %lf %c", &val1
, &val2
, &dummy
) == 2)
2161 gtk_spin_button_set_increments(spinbutton
, val1
, val2
);
2163 ign_cmd(type
, whole_msg
);
2167 update_spinner(GObject
*obj
, const char *action
,
2168 const char *data
, const char *whole_msg
, GType type
)
2170 GtkSpinner
*spinner
= GTK_SPINNER(obj
);
2174 if (eql(action
, "start") && sscanf(data
, " %c", &dummy
) < 1)
2175 gtk_spinner_start(spinner
);
2176 else if (eql(action
, "stop") && sscanf(data
, " %c", &dummy
) < 1)
2177 gtk_spinner_stop(spinner
);
2179 ign_cmd(type
, whole_msg
);
2183 update_statusbar(GObject
*obj
, const char *action
,
2184 const char *data
, const char *whole_msg
, GType type
)
2186 GtkStatusbar
*statusbar
= GTK_STATUSBAR(obj
);
2187 char *ctx_msg
, dummy
;
2191 /* TODO: remove "push", "pop", "remove_all"; rename "push_id" to "push", etc. */
2192 if ((ctx_msg
= malloc(strlen(data
) + 1)) == NULL
)
2194 t
= sscanf(data
, "%s %n%c", ctx_msg
, &ctx_len
, &dummy
);
2195 msg
= data
+ ctx_len
;
2196 if (eql(action
, "push"))
2197 gtk_statusbar_push(statusbar
,
2198 gtk_statusbar_get_context_id(statusbar
, "0"),
2200 else if (eql(action
, "push_id") && t
>= 1)
2201 gtk_statusbar_push(statusbar
,
2202 gtk_statusbar_get_context_id(statusbar
, ctx_msg
),
2204 else if (eql(action
, "pop") && t
< 1)
2205 gtk_statusbar_pop(statusbar
,
2206 gtk_statusbar_get_context_id(statusbar
, "0"));
2207 else if (eql(action
, "pop_id") && t
== 1)
2208 gtk_statusbar_pop(statusbar
,
2209 gtk_statusbar_get_context_id(statusbar
, ctx_msg
));
2210 else if (eql(action
, "remove_all") && t
< 1)
2211 gtk_statusbar_remove_all(statusbar
,
2212 gtk_statusbar_get_context_id(statusbar
, "0"));
2213 else if (eql(action
, "remove_all_id") && t
== 1)
2214 gtk_statusbar_remove_all(statusbar
,
2215 gtk_statusbar_get_context_id(statusbar
, ctx_msg
));
2217 ign_cmd(type
, whole_msg
);
2222 update_switch(GObject
*obj
, const char *action
,
2223 const char *data
, const char *whole_msg
, GType type
)
2228 if (eql(action
, "set_active") &&
2229 sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
2230 gtk_switch_set_active(GTK_SWITCH(obj
), val
);
2232 ign_cmd(type
, whole_msg
);
2236 update_text_view(GObject
*obj
, const char *action
,
2237 const char *data
, const char *whole_msg
, GType type
)
2239 GtkTextView
*view
= GTK_TEXT_VIEW(obj
);
2240 GtkTextBuffer
*textbuf
= gtk_text_view_get_buffer(view
);
2245 if (eql(action
, "set_text"))
2246 gtk_text_buffer_set_text(textbuf
, data
, -1);
2247 else if (eql(action
, "delete") && sscanf(data
, " %c", &dummy
) < 1) {
2248 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
2249 gtk_text_buffer_delete(textbuf
, &a
, &b
);
2250 } else if (eql(action
, "insert_at_cursor"))
2251 gtk_text_buffer_insert_at_cursor(textbuf
, data
, -1);
2252 else if (eql(action
, "place_cursor") && eql(data
, "end")) {
2253 gtk_text_buffer_get_end_iter(textbuf
, &a
);
2254 gtk_text_buffer_place_cursor(textbuf
, &a
);
2255 } else if (eql(action
, "place_cursor") &&
2256 sscanf(data
, "%d %c", &val
, &dummy
) == 1) {
2257 gtk_text_buffer_get_iter_at_offset(textbuf
, &a
, val
);
2258 gtk_text_buffer_place_cursor(textbuf
, &a
);
2259 } else if (eql(action
, "place_cursor_at_line") &&
2260 sscanf(data
, "%d %c", &val
, &dummy
) == 1) {
2261 gtk_text_buffer_get_iter_at_line(textbuf
, &a
, val
);
2262 gtk_text_buffer_place_cursor(textbuf
, &a
);
2263 } else if (eql(action
, "scroll_to_cursor") &&
2264 sscanf(data
, " %c", &dummy
) < 1)
2265 gtk_text_view_scroll_to_mark(view
, gtk_text_buffer_get_insert(textbuf
),
2267 else if (eql(action
, "save") && data
!= NULL
&&
2268 (save
= fopen(data
, "w")) != NULL
) {
2269 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
2270 save_msg(GTK_BUILDABLE(view
), "insert_at_cursor",
2271 gtk_text_buffer_get_text(textbuf
, &a
, &b
, TRUE
), NULL
);
2274 ign_cmd(type
, whole_msg
);
2278 update_toggle_button(GObject
*obj
, const char *action
,
2279 const char *data
, const char *whole_msg
, GType type
)
2284 if (eql(action
, "set_label"))
2285 gtk_button_set_label(GTK_BUTTON(obj
), data
);
2286 else if (eql(action
, "set_active") &&
2287 sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
2288 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(obj
), val
);
2290 ign_cmd(type
, whole_msg
);
2294 update_tooltip_text(GObject
*obj
, const char *action
,
2295 const char *data
, const char *whole_msg
, GType type
)
2300 gtk_widget_set_tooltip_text(GTK_WIDGET(obj
), data
);
2304 * update_tree_view() needs a few helper functions
2308 * Check if s is a valid string representation of a GtkTreePath
2311 is_path_string(char *s
)
2314 strlen(s
) == strspn(s
, ":0123456789") &&
2315 strstr(s
, "::") == NULL
&&
2316 strcspn(s
, ":") > 0;
2320 tree_model_insert_before(GtkTreeModel
*model
, GtkTreeIter
*iter
,
2321 GtkTreeIter
*parent
, GtkTreeIter
*sibling
)
2323 if (GTK_IS_TREE_STORE(model
))
2324 gtk_tree_store_insert_before(GTK_TREE_STORE(model
),
2325 iter
, parent
, sibling
);
2326 else if (GTK_IS_LIST_STORE(model
))
2327 gtk_list_store_insert_before(GTK_LIST_STORE(model
),
2334 tree_model_insert_after(GtkTreeModel
*model
, GtkTreeIter
*iter
,
2335 GtkTreeIter
*parent
, GtkTreeIter
*sibling
)
2337 if (GTK_IS_TREE_STORE(model
))
2338 gtk_tree_store_insert_after(GTK_TREE_STORE(model
),
2339 iter
, parent
, sibling
);
2340 else if (GTK_IS_LIST_STORE(model
))
2341 gtk_list_store_insert_after(GTK_LIST_STORE(model
),
2348 tree_model_move_before(GtkTreeModel
*model
, GtkTreeIter
*iter
,
2349 GtkTreeIter
*position
)
2351 if (GTK_IS_TREE_STORE(model
))
2352 gtk_tree_store_move_before(GTK_TREE_STORE(model
), iter
, position
);
2353 else if (GTK_IS_LIST_STORE(model
))
2354 gtk_list_store_move_before(GTK_LIST_STORE(model
), iter
, position
);
2360 tree_model_remove(GtkTreeModel
*model
, GtkTreeIter
*iter
)
2362 if (GTK_IS_TREE_STORE(model
))
2363 gtk_tree_store_remove(GTK_TREE_STORE(model
), iter
);
2364 else if (GTK_IS_LIST_STORE(model
))
2365 gtk_list_store_remove(GTK_LIST_STORE(model
), iter
);
2371 tree_model_clear(GtkTreeModel
*model
)
2373 if (GTK_IS_TREE_STORE(model
))
2374 gtk_tree_store_clear(GTK_TREE_STORE(model
));
2375 else if (GTK_IS_LIST_STORE(model
))
2376 gtk_list_store_clear(GTK_LIST_STORE(model
));
2382 tree_model_set(GtkTreeModel
*model
, GtkTreeIter
*iter
, ...)
2387 if (GTK_IS_TREE_STORE(model
))
2388 gtk_tree_store_set_valist(GTK_TREE_STORE(model
), iter
, ap
);
2389 else if (GTK_IS_LIST_STORE(model
))
2390 gtk_list_store_set_valist(GTK_LIST_STORE(model
), iter
, ap
);
2397 * Create an empty row at path if it doesn't yet exist. Create older
2398 * siblings and parents as necessary.
2401 create_subtree(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
)
2403 GtkTreeIter iter_1
; /* iter's predecessor */
2404 GtkTreePath
*path_1
; /* path's predecessor */
2406 if (gtk_tree_model_get_iter(model
, iter
, path
))
2408 path_1
= gtk_tree_path_copy(path
);
2409 if (gtk_tree_path_prev(path_1
)) { /* need an older sibling */
2410 create_subtree(model
, path_1
, iter
);
2412 tree_model_insert_after(model
, iter
, NULL
, &iter_1
);
2413 } else if (gtk_tree_path_up(path_1
)) { /* need a parent */
2414 create_subtree(model
, path_1
, iter
);
2415 if (gtk_tree_path_get_depth(path_1
) == 0)
2416 /* first toplevel row */
2417 tree_model_insert_after(model
, iter
, NULL
, NULL
);
2418 else { /* first row in a lower level */
2420 tree_model_insert_after(model
, iter
, &iter_1
, NULL
);
2422 } /* neither prev nor up mean we're at the root of an empty tree */
2423 gtk_tree_path_free(path_1
);
2427 set_tree_view_cell(GtkTreeModel
*model
, GtkTreeIter
*iter
,
2428 const char *path_s
, int col
, const char *new_text
)
2430 GType col_type
= gtk_tree_model_get_column_type(model
, col
);
2437 path
= gtk_tree_path_new_from_string(path_s
);
2438 create_subtree(model
, path
, iter
);
2439 gtk_tree_path_free(path
);
2441 case G_TYPE_BOOLEAN
:
2450 n
= strtoll(new_text
, &endptr
, 10);
2451 if (!errno
&& endptr
!= new_text
) {
2452 tree_model_set(model
, iter
, col
, n
, -1);
2460 d
= strtod(new_text
, &endptr
);
2461 if (!errno
&& endptr
!= new_text
) {
2462 tree_model_set(model
, iter
, col
, d
, -1);
2467 tree_model_set(model
, iter
, col
, new_text
, -1);
2471 fprintf(stderr
, "column %d: %s not implemented\n",
2472 col
, g_type_name(col_type
));
2480 tree_view_set_cursor(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*col
)
2482 /* GTK+ 3.14 requires this. For 3.18, path = NULL */
2483 /* is just fine and this function need not exist. */
2485 path
= gtk_tree_path_new();
2486 gtk_tree_view_set_cursor(view
, path
, col
, false);
2490 update_tree_view(GObject
*obj
, const char *action
,
2491 const char *data
, const char *whole_msg
, GType type
)
2493 GtkTreeView
*view
= GTK_TREE_VIEW(obj
);
2494 GtkTreeIter iter0
, iter1
;
2495 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
2496 GtkTreePath
*path
= NULL
;
2497 bool iter0_valid
, iter1_valid
;
2498 char *tokens
, *arg0
, *arg1
, *arg2
;
2499 int col
= -1; /* invalid column number */
2501 if (!GTK_IS_LIST_STORE(model
) && !GTK_IS_TREE_STORE(model
))
2503 fprintf(stderr
, "missing model/");
2504 ign_cmd(type
, whole_msg
);
2507 if ((tokens
= malloc(strlen(data
) + 1)) == NULL
)
2509 strcpy(tokens
, data
);
2510 arg0
= strtok(tokens
, WHITESPACE
);
2511 arg1
= strtok(NULL
, WHITESPACE
);
2512 arg2
= strtok(NULL
, "");
2513 iter0_valid
= is_path_string(arg0
) &&
2514 gtk_tree_model_get_iter_from_string(model
, &iter0
, arg0
);
2515 iter1_valid
= is_path_string(arg1
) &&
2516 gtk_tree_model_get_iter_from_string(model
, &iter1
, arg1
);
2517 if (is_path_string(arg1
))
2518 col
= strtol(arg1
, NULL
, 10);
2519 if (eql(action
, "set") &&
2521 col
< gtk_tree_model_get_n_columns(model
) &&
2522 is_path_string(arg0
)) {
2523 if (set_tree_view_cell(model
, &iter0
, arg0
, col
, arg2
) == false)
2524 ign_cmd(type
, whole_msg
);
2525 } else if (eql(action
, "scroll") && iter0_valid
&& iter1_valid
&&
2527 path
= gtk_tree_path_new_from_string(arg0
);
2528 gtk_tree_view_scroll_to_cell (view
,
2530 gtk_tree_view_get_column(view
, col
),
2532 } else if (eql(action
, "expand") && iter0_valid
&& arg1
== NULL
) {
2533 path
= gtk_tree_path_new_from_string(arg0
);
2534 gtk_tree_view_expand_row(view
, path
, false);
2535 } else if (eql(action
, "expand_all") && iter0_valid
&& arg1
== NULL
) {
2536 path
= gtk_tree_path_new_from_string(arg0
);
2537 gtk_tree_view_expand_row(view
, path
, true);
2538 } else if (eql(action
, "expand_all") && arg0
== NULL
)
2539 gtk_tree_view_expand_all(view
);
2540 else if (eql(action
, "collapse") && iter0_valid
&& arg1
== NULL
) {
2541 path
= gtk_tree_path_new_from_string(arg0
);
2542 gtk_tree_view_collapse_row(view
, path
);
2543 } else if (eql(action
, "collapse") && arg0
== NULL
)
2544 gtk_tree_view_collapse_all(view
);
2545 else if (eql(action
, "set_cursor") && iter0_valid
&& arg1
== NULL
) {
2546 path
= gtk_tree_path_new_from_string(arg0
);
2547 tree_view_set_cursor(view
, path
, NULL
);
2548 } else if (eql(action
, "set_cursor") && arg0
== NULL
) {
2549 tree_view_set_cursor(view
, NULL
, NULL
);
2550 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(view
));
2551 } else if (eql(action
, "insert_row") &&
2552 eql(arg0
, "end") && arg1
== NULL
)
2553 tree_model_insert_before(model
, &iter1
, NULL
, NULL
);
2554 else if (eql(action
, "insert_row") && iter0_valid
&&
2555 eql(arg1
, "as_child") && arg2
== NULL
)
2556 tree_model_insert_after(model
, &iter1
, &iter0
, NULL
);
2557 else if (eql(action
, "insert_row") && iter0_valid
&& arg1
== NULL
)
2558 tree_model_insert_before(model
, &iter1
, NULL
, &iter0
);
2559 else if (eql(action
, "move_row") && iter0_valid
&&
2560 eql(arg1
, "end") && arg2
== NULL
)
2561 tree_model_move_before(model
, &iter0
, NULL
);
2562 else if (eql(action
, "move_row") && iter0_valid
&& iter1_valid
&& arg2
== NULL
)
2563 tree_model_move_before(model
, &iter0
, &iter1
);
2564 else if (eql(action
, "remove_row") && iter0_valid
&& arg1
== NULL
)
2565 tree_model_remove(model
, &iter0
);
2566 else if (eql(action
, "clear") && arg0
== NULL
) {
2567 tree_view_set_cursor(view
, NULL
, NULL
);
2568 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(view
));
2569 tree_model_clear(model
);
2570 } else if (eql(action
, "save") && arg0
!= NULL
&&
2571 (save
= fopen(arg0
, "w")) != NULL
) {
2572 gtk_tree_model_foreach(model
, (GtkTreeModelForeachFunc
) save_tree_row_msg
, view
);
2575 ign_cmd(type
, whole_msg
);
2577 gtk_tree_path_free(path
);
2581 update_visibility(GObject
*obj
, const char *action
,
2582 const char *data
, const char *whole_msg
, GType type
)
2590 if (sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
2591 gtk_widget_set_visible(GTK_WIDGET(obj
), val
);
2593 ign_cmd(type
, whole_msg
);
2597 * Change the style of the widget passed
2600 update_widget_style(GObject
*obj
, const char *name
,
2601 const char *data
, const char *whole_msg
, GType type
)
2603 GtkStyleContext
*context
;
2604 GtkStyleProvider
*style_provider
;
2606 const char *prefix
= "* {", *suffix
= "}";
2612 style_provider
= g_object_get_data(obj
, "style_provider");
2613 sz
= strlen(prefix
) + strlen(suffix
) + strlen(data
) + 1;
2614 context
= gtk_widget_get_style_context(GTK_WIDGET(obj
));
2615 gtk_style_context_remove_provider(context
, style_provider
);
2616 if ((style_decl
= malloc(sz
)) == NULL
)
2618 strcpy(style_decl
, prefix
);
2619 strcat(style_decl
, data
);
2620 strcat(style_decl
, suffix
);
2621 gtk_style_context_add_provider(context
, style_provider
,
2622 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
2623 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(style_provider
),
2624 style_decl
, -1, NULL
);
2629 update_window(GObject
*obj
, const char *action
,
2630 const char *data
, const char *whole_msg
, GType type
)
2632 if (!update_class_window(obj
, action
, data
, whole_msg
, type
))
2633 ign_cmd(type
, whole_msg
);
2637 * Simulate user activity on various widgets
2640 fake_ui_activity(GObject
*obj
, const char *action
,
2641 const char *data
, const char *whole_msg
, GType type
)
2647 if (!GTK_IS_WIDGET(obj
) || sscanf(data
, " %c", &dummy
) > 0)
2648 ign_cmd(type
, whole_msg
);
2649 else if (GTK_IS_SPIN_BUTTON(obj
))
2650 cb_spin_button(GTK_BUILDABLE(obj
), "text"); /* TODO: rename to "value" */
2651 else if (GTK_IS_SCALE(obj
))
2652 cb_range(GTK_BUILDABLE(obj
), "value");
2653 else if (GTK_IS_ENTRY(obj
))
2654 cb_editable(GTK_BUILDABLE(obj
), "text");
2655 else if (GTK_IS_CALENDAR(obj
))
2656 cb_calendar(GTK_BUILDABLE(obj
), "clicked");
2657 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
2658 cb_file_chooser_button(GTK_BUILDABLE(obj
), "file");
2659 else if (!gtk_widget_activate(GTK_WIDGET(obj
)))
2660 ign_cmd(type
, whole_msg
);
2664 * The final UI update
2667 main_quit(GObject
*obj
, const char *action
,
2668 const char *data
, const char *whole_msg
, GType type
)
2677 if (sscanf(data
, " %c", &dummy
) < 1)
2680 ign_cmd(type
, whole_msg
);
2684 * Don't update anything; just complain
2687 complain(GObject
*obj
, const char *action
,
2688 const char *data
, const char *whole_msg
, GType type
)
2693 ign_cmd(type
, whole_msg
);
2697 * Data to be passed to and from the GTK main loop
2700 void (*fn
)(GObject
*, const char *action
,
2701 const char *data
, const char *msg
, GType type
);
2711 * Parse command pointed to by ud, and act on ui accordingly. Runs
2712 * once per command inside gtk_main_loop().
2715 update_ui(struct ui_data
*ud
)
2717 char *lc
= lc_numeric();
2719 (ud
->fn
)(ud
->obj
, ud
->action
, ud
->data
, ud
->msg
, ud
->type
);
2720 free(ud
->msg_tokens
);
2723 lc_numeric_free(lc
);
2724 return G_SOURCE_REMOVE
;
2728 * Keep track of loading files to avoid recursive loading of the same
2729 * file. If filename = NULL, forget the most recently remembered file.
2732 remember_loading_file(char *filename
)
2734 static char *filenames
[BUFLEN
];
2735 static size_t latest
= 0;
2738 if (filename
== NULL
) { /* pop */
2744 for (i
= 1; i
<= latest
; i
++)
2745 if (eql(filename
, filenames
[i
]))
2747 if (latest
> BUFLEN
-2)
2749 filenames
[++latest
] = filename
;
2755 * Read lines from stream cmd and perform appropriate actions on the
2759 digest_msg(FILE *cmd
)
2761 FILE *load
; /* restoring user data */
2763 static int recursion
= -1; /* > 0 means this is a recursive call */
2768 char first_char
= '\0';
2769 size_t msg_size
= 32;
2770 int name_start
= 0, name_end
= 0;
2771 int action_start
= 0, action_end
= 0;
2776 if ((ud
= malloc(sizeof(*ud
))) == NULL
)
2778 if ((ud
->msg
= malloc(msg_size
)) == NULL
)
2780 ud
->type
= G_TYPE_INVALID
;
2781 pthread_testcancel();
2784 read_buf(cmd
, &ud
->msg
, &msg_size
);
2787 data_start
= strlen(ud
->msg
);
2788 if ((ud
->msg_tokens
= malloc(strlen(ud
->msg
) + 1)) == NULL
)
2790 strcpy(ud
->msg_tokens
, ud
->msg
);
2791 sscanf(ud
->msg
, " %c", &first_char
);
2792 if (strlen(ud
->msg
) == 0 || first_char
== '#') { /* comment */
2793 ud
->fn
= update_nothing
;
2796 sscanf(ud
->msg_tokens
,
2797 " %n%*[0-9a-zA-Z_]%n:%n%*[0-9a-zA-Z_]%n%*1[ \t]%n",
2798 &name_start
, &name_end
, &action_start
, &action_end
, &data_start
);
2799 ud
->msg_tokens
[name_end
] = ud
->msg_tokens
[action_end
] = '\0';
2800 name
= ud
->msg_tokens
+ name_start
;
2801 ud
->action
= ud
->msg_tokens
+ action_start
;
2802 ud
->data
= ud
->msg_tokens
+ data_start
;
2803 if (eql(ud
->action
, "main_quit")) {
2807 if (eql(ud
->action
, "load") && strlen(ud
->data
) > 0 &&
2808 (load
= fopen(ud
->data
, "r")) != NULL
&&
2809 remember_loading_file(ud
->data
)) {
2812 remember_loading_file(NULL
);
2813 ud
->fn
= update_nothing
;
2816 if ((ud
->obj
= (gtk_builder_get_object(builder
, name
))) == NULL
) {
2820 ud
->type
= G_TYPE_FROM_INSTANCE(ud
->obj
);
2821 if (eql(ud
->action
, "force"))
2822 ud
->fn
= fake_ui_activity
;
2823 else if (eql(ud
->action
, "set_sensitive"))
2824 ud
->fn
= update_sensitivity
;
2825 else if (eql(ud
->action
, "set_visible"))
2826 ud
->fn
= update_visibility
;
2827 else if (eql(ud
->action
, "set_size_request"))
2828 ud
->fn
= update_size_request
;
2829 else if (eql(ud
->action
, "set_tooltip_text"))
2830 ud
->fn
= update_tooltip_text
;
2831 else if (eql(ud
->action
, "grab_focus"))
2832 ud
->fn
= update_focus
;
2833 else if (eql(ud
->action
, "style")) {
2835 ud
->fn
= update_widget_style
;
2836 } else if (ud
->type
== GTK_TYPE_DRAWING_AREA
)
2837 ud
->fn
= update_drawing_area
;
2838 else if (ud
->type
== GTK_TYPE_TREE_VIEW
)
2839 ud
->fn
= update_tree_view
;
2840 else if (ud
->type
== GTK_TYPE_COMBO_BOX_TEXT
)
2841 ud
->fn
= update_combo_box_text
;
2842 else if (ud
->type
== GTK_TYPE_LABEL
)
2843 ud
->fn
= update_label
;
2844 else if (ud
->type
== GTK_TYPE_IMAGE
)
2845 ud
->fn
= update_image
;
2846 else if (ud
->type
== GTK_TYPE_TEXT_VIEW
)
2847 ud
->fn
= update_text_view
;
2848 else if (ud
->type
== GTK_TYPE_NOTEBOOK
)
2849 ud
->fn
= update_notebook
;
2850 else if (ud
->type
== GTK_TYPE_EXPANDER
)
2851 ud
->fn
= update_expander
;
2852 else if (ud
->type
== GTK_TYPE_FRAME
)
2853 ud
->fn
= update_frame
;
2854 else if (ud
->type
== GTK_TYPE_SCROLLED_WINDOW
)
2855 ud
->fn
= update_scrolled_window
;
2856 else if (ud
->type
== GTK_TYPE_BUTTON
)
2857 ud
->fn
= update_button
;
2858 else if (ud
->type
== GTK_TYPE_FILE_CHOOSER_DIALOG
)
2859 ud
->fn
= update_file_chooser_dialog
;
2860 else if (ud
->type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
2861 ud
->fn
= update_file_chooser_button
;
2862 else if (ud
->type
== GTK_TYPE_COLOR_BUTTON
)
2863 ud
->fn
= update_color_button
;
2864 else if (ud
->type
== GTK_TYPE_FONT_BUTTON
)
2865 ud
->fn
= update_font_button
;
2866 else if (ud
->type
== GTK_TYPE_PRINT_UNIX_DIALOG
)
2867 ud
->fn
= update_print_dialog
;
2868 else if (ud
->type
== GTK_TYPE_SWITCH
)
2869 ud
->fn
= update_switch
;
2870 else if (ud
->type
== GTK_TYPE_TOGGLE_BUTTON
||
2871 ud
->type
== GTK_TYPE_RADIO_BUTTON
||
2872 ud
->type
== GTK_TYPE_CHECK_BUTTON
)
2873 ud
->fn
= update_toggle_button
;
2874 else if (ud
->type
== GTK_TYPE_ENTRY
)
2875 ud
->fn
= update_entry
;
2876 else if (ud
->type
== GTK_TYPE_SPIN_BUTTON
)
2877 ud
->fn
= update_spin_button
;
2878 else if (ud
->type
== GTK_TYPE_SCALE
)
2879 ud
->fn
= update_scale
;
2880 else if (ud
->type
== GTK_TYPE_PROGRESS_BAR
)
2881 ud
->fn
= update_progress_bar
;
2882 else if (ud
->type
== GTK_TYPE_SPINNER
)
2883 ud
->fn
= update_spinner
;
2884 else if (ud
->type
== GTK_TYPE_STATUSBAR
)
2885 ud
->fn
= update_statusbar
;
2886 else if (ud
->type
== GTK_TYPE_CALENDAR
)
2887 ud
->fn
= update_calendar
;
2888 else if (ud
->type
== GTK_TYPE_SOCKET
)
2889 ud
->fn
= update_socket
;
2890 else if (ud
->type
== GTK_TYPE_WINDOW
||
2891 ud
->type
== GTK_TYPE_DIALOG
)
2892 ud
->fn
= update_window
;
2896 pthread_testcancel();
2897 gdk_threads_add_timeout(0, (GSourceFunc
) update_ui
, ud
);
2905 * ============================================================
2907 * ============================================================
2911 * Attach to renderer key "col_number". Associate "col_number" with
2912 * the corresponding column number in the underlying model.
2913 * Due to what looks like a gap in the GTK API, renderer id and column
2914 * number are taken directly from the XML .ui file.
2917 tree_view_column_get_renderer_column(const char *ui_file
, GtkTreeViewColumn
*t_col
,
2918 int n
, GtkCellRenderer
**renderer
)
2921 char *xpath_base1
= "//object[@class=\"GtkTreeViewColumn\" and @id=\"";
2922 char *xpath_base2
= "\"]/child[";
2923 char *xpath_base3
= "]/object[@class=\"GtkCellRendererText\""
2924 " or @class=\"GtkCellRendererToggle\"]/";
2925 char *xpath_renderer_id
= "/@id";
2926 char *xpath_text_col
= "../attributes/attribute[@name=\"text\""
2927 " or @name=\"active\"]";
2928 const char *xpath_id
= widget_name(GTK_BUILDABLE(t_col
));
2931 size_t xpath_n_len
= 3; /* Big Enough (TM) */
2932 xmlChar
*xpath
, *renderer_name
= NULL
, *m_col_s
= NULL
;
2935 xmlNodeSetPtr nodes
;
2936 xmlXPathContextPtr xpath_ctx
;
2937 xmlXPathObjectPtr xpath_obj
;
2939 if ((doc
= xmlParseFile(ui_file
)) == NULL
)
2941 if ((xpath_ctx
= xmlXPathNewContext(doc
)) == NULL
) {
2945 xpath_len
= 2 * (strlen(xpath_base1
) + strlen(xpath_id
) +
2946 strlen(xpath_base2
) + xpath_n_len
+
2947 strlen(xpath_base3
)) +
2949 strlen(xpath_text_col
) + strlen(xpath_renderer_id
) +
2951 if ((xpath
= malloc(xpath_len
)) == NULL
)
2953 snprintf((char *) xpath
, xpath_len
, "%s%s%s%d%s%s|%s%s%s%d%s%s",
2954 xpath_base1
, xpath_id
, xpath_base2
, n
, xpath_base3
, xpath_text_col
,
2955 xpath_base1
, xpath_id
, xpath_base2
, n
, xpath_base3
, xpath_renderer_id
);
2956 if ((xpath_obj
= xmlXPathEvalExpression(xpath
, xpath_ctx
)) == NULL
) {
2957 xmlXPathFreeContext(xpath_ctx
);
2962 if ((nodes
= xpath_obj
->nodesetval
) != NULL
) {
2963 for (i
= 0; i
< nodes
->nodeNr
; ++i
) {
2964 if (nodes
->nodeTab
[i
]->type
== XML_ELEMENT_NODE
) {
2965 cur
= nodes
->nodeTab
[i
];
2966 m_col_s
= xmlNodeGetContent(cur
);
2968 cur
= nodes
->nodeTab
[i
];
2969 renderer_name
= xmlNodeGetContent(cur
);
2973 if (renderer_name
) {
2974 *renderer
= GTK_CELL_RENDERER(
2975 gtk_builder_get_object(builder
, (char *) renderer_name
));
2977 g_object_set_data(G_OBJECT(*renderer
), "col_number",
2978 GINT_TO_POINTER(strtol((char *) m_col_s
,
2983 xmlFree(renderer_name
);
2985 xmlXPathFreeObject(xpath_obj
);
2986 xmlXPathFreeContext(xpath_ctx
);
2993 * Callbacks that forward a modification of a tree view cell to the
2997 cb_tree_model_edit(GtkCellRenderer
*renderer
, const gchar
*path_s
,
2998 const gchar
*new_text
, gpointer view
)
3001 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
3002 int col
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(renderer
),
3005 gtk_tree_model_get_iter_from_string(model
, &iter
, path_s
);
3006 set_tree_view_cell(model
, &iter
, path_s
, col
,
3008 send_tree_cell_msg_by(send_msg
, model
, path_s
, &iter
, col
,
3009 GTK_BUILDABLE(view
));
3013 cb_tree_model_toggle(GtkCellRenderer
*renderer
, gchar
*path_s
, gpointer view
)
3016 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
3018 int col
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(renderer
),
3021 gtk_tree_model_get_iter_from_string(model
, &iter
, path_s
);
3022 gtk_tree_model_get(model
, &iter
, col
, &toggle_state
, -1);
3023 set_tree_view_cell(model
, &iter
, path_s
, col
,
3024 toggle_state
? "0" : "1");
3028 connect_widget_signals(gpointer
*obj
, char *ui_file
)
3031 GType type
= G_TYPE_INVALID
;
3032 char *suffix
= NULL
;
3033 const char *name
= NULL
;
3035 type
= G_TYPE_FROM_INSTANCE(obj
);
3036 if (GTK_IS_BUILDABLE(obj
))
3037 name
= widget_name(GTK_BUILDABLE(obj
));
3038 if (type
== GTK_TYPE_TREE_VIEW_COLUMN
) {
3039 GList
*cells
= gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(obj
));
3040 GtkTreeViewColumn
*tv_col
= GTK_TREE_VIEW_COLUMN(obj
);
3041 unsigned int i
, n_cells
= g_list_length(cells
);
3044 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_simple
), "clicked");
3045 for (i
= 1; i
<= n_cells
; i
++) {
3046 GtkCellRenderer
*renderer
;
3047 GtkTreeView
*view
= GTK_TREE_VIEW(
3048 gtk_tree_view_column_get_tree_view(tv_col
));
3049 gboolean editable
= FALSE
;
3051 if (!tree_view_column_get_renderer_column(ui_file
, tv_col
,
3054 if (GTK_IS_CELL_RENDERER_TEXT(renderer
)) {
3055 g_object_get(renderer
, "editable", &editable
, NULL
);
3057 g_signal_connect(renderer
, "edited",
3058 G_CALLBACK(cb_tree_model_edit
), view
);
3059 } else if (GTK_IS_CELL_RENDERER_TOGGLE(renderer
)) {
3060 g_object_get(renderer
, "activatable", &editable
, NULL
);
3062 g_signal_connect(renderer
, "toggled",
3063 G_CALLBACK(cb_tree_model_toggle
), view
);
3066 } else if (type
== GTK_TYPE_BUTTON
) {
3067 /* Button associated with a GtkTextView. */
3068 if ((suffix
= strstr(name
, "_send_text")) != NULL
&&
3069 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(suffix
, name
)))
3070 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text
),
3071 gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
)));
3072 else if ((suffix
= strstr(name
, "_send_selection")) != NULL
&&
3073 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(suffix
, name
)))
3074 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text_selection
),
3075 gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
)));
3077 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_simple
), "clicked");
3078 /* Buttons associated with (and part of) a GtkDialog.
3079 * (We shun response ids which could be returned from
3080 * gtk_dialog_run() because that would require the
3081 * user to define those response ids in Glade,
3083 if ((suffix
= strstr(name
, "_cancel")) != NULL
&&
3084 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
)))
3085 if (eql(widget_name(GTK_BUILDABLE(obj2
)), MAIN_WIN
))
3086 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_main_quit
), NULL
);
3088 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_widget_hide
), obj2
);
3089 else if ((suffix
= strstr(name
, "_ok")) != NULL
&&
3090 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
))) {
3091 if (GTK_IS_FILE_CHOOSER_DIALOG(obj2
))
3092 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(cb_send_file_chooser_dialog_selection
), GTK_FILE_CHOOSER(obj2
));
3093 if (eql(widget_name(GTK_BUILDABLE(obj2
)), MAIN_WIN
))
3094 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_main_quit
), NULL
);
3096 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_widget_hide
), obj2
);
3097 } else if ((suffix
= strstr(name
, "_apply")) != NULL
&&
3098 GTK_IS_FILE_CHOOSER_DIALOG(obj2
= obj_sans_suffix(suffix
, name
)))
3099 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(cb_send_file_chooser_dialog_selection
), obj2
);
3101 } else if (GTK_IS_MENU_ITEM(obj
))
3102 if ((suffix
= strstr(name
, "_invoke")) != NULL
&&
3103 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
)))
3104 g_signal_connect_swapped(obj
, "activate", G_CALLBACK(gtk_widget_show
), obj2
);
3106 g_signal_connect(obj
, "activate", G_CALLBACK(cb_menu_item
), "active");
3107 else if (GTK_IS_WINDOW(obj
)) {
3108 g_signal_connect(obj
, "delete-event", G_CALLBACK(cb_event_simple
), "closed");
3109 if (eql(name
, MAIN_WIN
))
3110 g_signal_connect_swapped(obj
, "delete-event", G_CALLBACK(gtk_main_quit
), NULL
);
3112 g_signal_connect(obj
, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
3113 } else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
3114 g_signal_connect(obj
, "file-set", G_CALLBACK(cb_file_chooser_button
), "file");
3115 else if (type
== GTK_TYPE_COLOR_BUTTON
)
3116 g_signal_connect(obj
, "color-set", G_CALLBACK(cb_color_button
), "color");
3117 else if (type
== GTK_TYPE_FONT_BUTTON
)
3118 g_signal_connect(obj
, "font-set", G_CALLBACK(cb_font_button
), "font");
3119 else if (type
== GTK_TYPE_SWITCH
)
3120 g_signal_connect(obj
, "notify::active", G_CALLBACK(cb_switch
), NULL
);
3121 else if (type
== GTK_TYPE_TOGGLE_BUTTON
|| type
== GTK_TYPE_RADIO_BUTTON
|| type
== GTK_TYPE_CHECK_BUTTON
)
3122 g_signal_connect(obj
, "toggled", G_CALLBACK(cb_toggle_button
), NULL
);
3123 else if (type
== GTK_TYPE_ENTRY
)
3124 g_signal_connect(obj
, "changed", G_CALLBACK(cb_editable
), "text");
3125 else if (type
== GTK_TYPE_SPIN_BUTTON
)
3126 g_signal_connect(obj
, "value_changed", G_CALLBACK(cb_spin_button
), "text"); /* TODO: rename to "value" */
3127 else if (type
== GTK_TYPE_SCALE
)
3128 g_signal_connect(obj
, "value-changed", G_CALLBACK(cb_range
), "value");
3129 else if (type
== GTK_TYPE_CALENDAR
) {
3130 g_signal_connect(obj
, "day-selected-double-click", G_CALLBACK(cb_calendar
), "doubleclicked");
3131 g_signal_connect(obj
, "day-selected", G_CALLBACK(cb_calendar
), "clicked");
3132 } else if (type
== GTK_TYPE_TREE_SELECTION
)
3133 g_signal_connect(obj
, "changed", G_CALLBACK(cb_tree_selection
), "clicked");
3134 else if (type
== GTK_TYPE_SOCKET
) {
3135 g_signal_connect(obj
, "plug-added", G_CALLBACK(cb_simple
), "plug-added");
3136 g_signal_connect(obj
, "plug-removed", G_CALLBACK(cb_simple
), "plug-removed");
3137 } else if (type
== GTK_TYPE_DRAWING_AREA
)
3138 g_signal_connect(obj
, "draw", G_CALLBACK(cb_draw
), NULL
);
3139 else if (type
== GTK_TYPE_EVENT_BOX
) {
3140 gtk_widget_set_can_focus(GTK_WIDGET(obj
), true);
3141 g_signal_connect(obj
, "button-press-event", G_CALLBACK(cb_event_box_button
), "button_press");
3142 g_signal_connect(obj
, "button-release-event", G_CALLBACK(cb_event_box_button
), "button_release");
3143 g_signal_connect(obj
, "motion-notify-event", G_CALLBACK(cb_event_box_motion
), "motion");
3144 g_signal_connect(obj
, "key-press-event", G_CALLBACK(cb_event_box_key
), "key_press");
3149 * We keep a style provider with each widget
3152 add_widget_style_provider(gpointer
*obj
, void *data
)
3154 GtkCssProvider
*style_provider
;
3155 GtkStyleContext
*context
;
3158 if (!GTK_IS_WIDGET(obj
))
3160 style_provider
= gtk_css_provider_new();
3161 context
= gtk_widget_get_style_context(GTK_WIDGET(obj
));
3162 gtk_style_context_add_provider(context
,
3163 GTK_STYLE_PROVIDER(style_provider
),
3164 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
3165 g_object_set_data(G_OBJECT(obj
), "style_provider", style_provider
);
3169 prepare_widgets(char *ui_file
)
3171 GSList
*objects
= NULL
;
3173 objects
= gtk_builder_get_objects(builder
);
3174 g_slist_foreach(objects
, (GFunc
) connect_widget_signals
, ui_file
);
3175 g_slist_foreach(objects
, (GFunc
) add_widget_style_provider
, NULL
);
3176 g_slist_free(objects
);
3180 main(int argc
, char *argv
[])
3182 FILE *in
= NULL
; /* command input */
3183 GObject
*main_window
= NULL
;
3185 char *in_fifo
= NULL
, *out_fifo
= NULL
;
3186 char *ui_file
= "pipeglade.ui", *log_file
= NULL
, *err_file
= NULL
;
3191 /* Disable runtime GLIB deprecation warnings: */
3192 setenv("G_ENABLE_DIAGNOSTIC", "0", 0);
3196 gtk_init(&argc
, &argv
);
3197 while ((opt
= getopt(argc
, argv
, "bGhe:i:l:o:O:u:V")) != -1) {
3199 case 'b': bg
= true; break;
3200 case 'e': xid
= optarg
; break;
3201 case 'G': show_lib_versions(); break;
3202 case 'h': bye(EXIT_SUCCESS
, stdout
, USAGE
); break;
3203 case 'i': in_fifo
= optarg
; break;
3204 case 'l': log_file
= optarg
; break;
3205 case 'o': out_fifo
= optarg
; break;
3206 case 'O': err_file
= optarg
; break;
3207 case 'u': ui_file
= optarg
; break;
3208 case 'V': bye(EXIT_SUCCESS
, stdout
, "%s\n", VERSION
); break;
3210 default: bye(EXIT_FAILURE
, stderr
, USAGE
); break;
3213 if (argv
[optind
] != NULL
)
3214 bye(EXIT_FAILURE
, stderr
,
3215 "illegal parameter '%s'\n" USAGE
, argv
[optind
]);
3216 redirect_stderr(err_file
);
3217 in
= open_in_fifo(in_fifo
);
3218 out
= open_out_fifo(out_fifo
);
3219 go_bg_if(bg
, in
, out
, err_file
);
3220 builder
= builder_from_file(ui_file
);
3221 log_out
= open_log(log_file
);
3222 pthread_create(&receiver
, NULL
, (void *(*)(void *)) digest_msg
, in
);
3223 main_window
= find_main_window();
3225 LIBXML_TEST_VERSION
;
3226 prepare_widgets(ui_file
);
3227 xembed_if(xid
, main_window
);
3229 rm_unless(stdin
, in
, in_fifo
);
3230 rm_unless(stdout
, out
, out_fifo
);
3231 pthread_cancel(receiver
);
3232 pthread_join(receiver
, NULL
);