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__); \
82 * ============================================================
84 * ============================================================
88 * Check if s1 and s2 are equal strings
91 eql(const char *s1
, const char *s2
)
93 return s1
!= NULL
&& s2
!= NULL
&& strcmp(s1
, s2
) == 0;
97 * Print a formatted message to stream s and give up with status
100 bye(int status
, FILE *s
, const char *fmt
, ...)
105 vfprintf(s
, fmt
, ap
);
111 show_lib_versions(void)
113 bye(EXIT_SUCCESS
, stdout
,
114 "GTK+ v%d.%d.%d (running v%d.%d.%d)\n"
115 "cairo v%s (running v%s)\n",
116 GTK_MAJOR_VERSION
, GTK_MINOR_VERSION
, GTK_MICRO_VERSION
,
117 gtk_get_major_version(), gtk_get_minor_version(),
118 gtk_get_micro_version(),
119 CAIRO_VERSION_STRING
, cairo_version_string());
123 * XEmbed us if xid_s is given, or show a standalone window; give up
127 xembed_if(char *xid_s
, GObject
*main_window
)
129 GtkWidget
*plug
, *body
;
133 if (xid_s
== NULL
) { /* standalone */
134 gtk_widget_show(GTK_WIDGET(main_window
));
137 /* We're being XEmbedded */
138 xid
= strtoul(xid_s
, NULL
, 10);
139 snprintf(xid_s2
, BUFLEN
, "%lu", xid
);
140 if (!eql(xid_s
, xid_s2
))
141 bye(EXIT_FAILURE
, stderr
,
142 "%s is not a valid XEmbed socket id\n", xid_s
);
143 body
= gtk_bin_get_child(GTK_BIN(main_window
));
144 gtk_container_remove(GTK_CONTAINER(main_window
), body
);
145 plug
= gtk_plug_new(xid
);
146 if (!gtk_plug_get_embedded(GTK_PLUG(plug
)))
147 bye(EXIT_FAILURE
, stderr
,
148 "unable to embed into XEmbed socket %s\n", xid_s
);
149 gtk_container_add(GTK_CONTAINER(plug
), body
);
150 gtk_widget_show(plug
);
154 * If requested, redirect stderr to file name
157 redirect_stderr(const char *name
)
161 if (freopen(name
, "a", stderr
) == NULL
)
162 /* complaining on stdout since stderr is closed now */
163 bye(EXIT_FAILURE
, stdout
, "redirecting stderr to %s: %s\n",
164 name
, strerror(errno
));
165 if (fchmod(fileno(stderr
), 0600) < 0)
166 bye(EXIT_FAILURE
, stdout
, "setting permissions of %s: %s\n",
167 name
, strerror(errno
));
168 setvbuf(stderr
, NULL
, _IOLBF
, 0);
173 * fork() if requested in bg; give up on errors
176 go_bg_if(bool bg
, FILE *in
, FILE *out
, char *err_file
)
182 if (in
== stdin
|| out
== stdout
)
183 bye(EXIT_FAILURE
, stderr
,
184 "parameter -b requires both -i and -o\n");
187 bye(EXIT_FAILURE
, stderr
,
188 "going to background: %s\n", strerror(errno
));
190 bye(EXIT_SUCCESS
, stdout
, "%d\n", pid
);
191 /* We're the child */
192 close(fileno(stdin
)); /* making certain not-so-smart */
193 close(fileno(stdout
)); /* system/run-shell commands happy */
194 if (err_file
== NULL
)
195 freopen("/dev/null", "w", stderr
);
199 * Return the current locale and set it to "C". Should be free()d if
206 char *lc
= setlocale(LC_NUMERIC
, NULL
);
208 if ((lc_orig
= malloc(strlen(lc
) + 1)) == NULL
)
211 setlocale(LC_NUMERIC
, "C");
216 * Set locale (back) to lc; free lc
219 lc_numeric_free(char *lc
)
221 setlocale(LC_NUMERIC
, lc
);
226 * Print a warning about a malformed command to stderr. Runs inside
230 ign_cmd(GType type
, const char *msg
)
232 const char *name
, *pad
= " ";
234 if (type
== G_TYPE_INVALID
) {
238 name
= g_type_name(type
);
239 fprintf(stderr
, "ignoring %s%scommand \"%s\"\n", name
, pad
, msg
);
243 * Check if n is, or can be made, the name of a fifo, and put its
244 * struct stat into sb. Give up if n exists but is not a fifo.
247 find_fifo(const char *n
, struct stat
*sb
)
251 if ((fd
= open(n
, O_RDONLY
| O_NONBLOCK
)) > -1) {
252 if (fstat(fd
, sb
) == 0 &&
253 S_ISFIFO(sb
->st_mode
) &&
254 fchmod(fd
, 0600) == 0) {
259 bye(EXIT_FAILURE
, stderr
, "using pre-existing fifo %s: %s\n",
262 if (mkfifo(n
, 0600) != 0)
263 bye(EXIT_FAILURE
, stderr
, "making fifo %s: %s\n",
269 open_fifo(const char *name
, const char *fmode
, FILE *fallback
, int bmode
)
273 struct stat sb1
, sb2
;
278 find_fifo(name
, &sb1
);
279 /* TODO: O_RDWR on fifo is undefined in POSIX */
280 if (!((fd
= open(name
, O_RDWR
)) > -1 &&
281 fstat(fd
, &sb2
) == 0 &&
282 sb1
.st_mode
== sb2
.st_mode
&&
283 sb1
.st_ino
== sb2
.st_ino
&&
284 sb1
.st_dev
== sb2
.st_dev
&&
285 (s
= fdopen(fd
, fmode
)) != NULL
))
286 bye(EXIT_FAILURE
, stderr
, "opening fifo %s (%s): %s\n",
287 name
, fmode
, strerror(errno
));
289 setvbuf(s
, NULL
, bmode
, 0);
294 * Create a log file if necessary, and open it. A name of "-"
295 * requests use of stderr.
298 open_log(const char *name
)
306 if ((s
= fopen(name
, "a")) == NULL
)
307 bye(EXIT_FAILURE
, stderr
, "opening log file %s: %s\n",
308 name
, strerror(errno
));
309 if (fchmod(fileno(s
), 0600) < 0)
310 bye(EXIT_FAILURE
, stderr
, "setting permissions of %s: %s\n",
311 name
, strerror(errno
));
316 rm_unless(FILE *forbidden
, FILE *s
, char *name
)
325 * Microseconds elapsed since start
328 usec_since(struct timespec
*start
)
332 clock_gettime(CLOCK_MONOTONIC
, &now
);
333 return (now
.tv_sec
- start
->tv_sec
) * 1e6
+
334 (now
.tv_nsec
- start
->tv_nsec
) / 1e3
;
341 log_msg(FILE *l
, char *msg
)
343 static char *old_msg
;
344 static struct timespec start
;
346 if (l
== NULL
) /* no logging */
348 if (msg
== NULL
&& old_msg
== NULL
)
349 fprintf(l
, "##########\t##### (New Pipeglade session) #####\n");
350 else if (msg
== NULL
&& old_msg
!= NULL
) { /* command done; start idle */
351 fprintf(l
, "%10ld\t%s\n", usec_since(&start
), old_msg
);
354 } else if (msg
!= NULL
&& old_msg
== NULL
) { /* idle done; start command */
355 fprintf(l
, "%10ld\t### (Idle) ###\n", usec_since(&start
));
356 if ((old_msg
= malloc(strlen(msg
) + 1)) == NULL
)
358 strcpy(old_msg
, msg
);
361 clock_gettime(CLOCK_MONOTONIC
, &start
);
365 has_suffix(const char *s
, const char *suffix
)
367 int s_suf
= strlen(s
) - strlen(suffix
);
371 return eql(suffix
, s
+ s_suf
);
375 * Remove suffix from name; find the object named like this
378 obj_sans_suffix(GtkBuilder
*builder
, const char *suffix
, const char *name
)
380 char str
[BUFLEN
+ 1] = {'\0'};
383 str_l
= suffix
- name
;
384 strncpy(str
, name
, str_l
< BUFLEN
? str_l
: BUFLEN
);
385 return gtk_builder_get_object(builder
, str
);
389 * Read UI definition from ui_file; give up on errors
392 builder_from_file(char *ui_file
)
394 GError
*error
= NULL
;
397 b
= gtk_builder_new();
398 if (gtk_builder_add_from_file(b
, ui_file
, &error
) == 0)
399 bye(EXIT_FAILURE
, stderr
, "%s\n", error
->message
);
404 widget_name(GtkBuildable
*obj
)
406 return gtk_buildable_get_name(obj
);
410 * Get the main window; give up on errors
413 find_main_window(GtkBuilder
*builder
)
417 if (GTK_IS_WINDOW(mw
= gtk_builder_get_object(builder
, MAIN_WIN
)))
419 bye(EXIT_FAILURE
, stderr
, "no toplevel window named \'" MAIN_WIN
"\'\n");
420 return NULL
; /* NOT REACHED */
424 * Store a line from stream s into buf, which should have been malloc'd
425 * to bufsize. Enlarge buf and bufsize if necessary.
428 read_buf(FILE *s
, char **buf
, size_t *bufsize
)
439 select(ifd
+ 1, &rfds
, NULL
, NULL
, NULL
);
441 if (c
== '\n' || feof(s
))
443 if (i
>= *bufsize
- 1)
444 if ((*buf
= realloc(*buf
, *bufsize
*= 2)) == NULL
)
449 case 'n': (*buf
)[i
++] = '\n'; break;
450 case 'r': (*buf
)[i
++] = '\r'; break;
451 default: (*buf
)[i
++] = c
; break;
453 } else if (c
== '\\')
464 * ============================================================
465 * Receiving feedback from the GUI
466 * ============================================================
470 send_msg_to(FILE* o
, GtkBuildable
*obj
, const char *tag
, va_list ap
)
473 const char *w_name
= widget_name(obj
);
476 struct timeval timeout
= {1, 0};
480 if (select(ofd
+ 1, NULL
, &wfds
, NULL
, &timeout
) == 1) {
481 fprintf(o
, "%s:%s ", w_name
, tag
);
482 while ((data
= va_arg(ap
, char *)) != NULL
) {
486 while ((c
= data
[i
++]) != '\0')
497 "send error; discarding feedback message %s:%s\n",
502 * Send GUI feedback to stream o. The message format is
503 * "<origin>:<tag> <data ...>". The variadic arguments are strings;
504 * last argument must be NULL.
507 send_msg(FILE *o
, GtkBuildable
*obj
, const char *tag
, ...)
512 send_msg_to(o
, obj
, tag
, ap
);
517 * Send message from GUI to stream o. The message format is
518 * "<origin>:set <data ...>", which happens to be a legal command.
519 * The variadic arguments are strings; last argument must be NULL.
522 send_msg_as_cmd(FILE *o
, GtkBuildable
*obj
, const char *tag
, ...)
527 send_msg_to(o
, obj
, "set", ap
);
532 * Stuff to pass around
535 FILE *outstr
; /* UI feedback messages */
536 FILE *instr
; /* command input */
537 FILE *logstr
; /* logging output */
538 GtkBuilder
*builder
; /* to be read from .ui file */
545 * Return pointer to a newly allocated struct info
548 info_new_full(FILE *stream
, GObject
*obj
, GtkTreeModel
*model
, char *txt
)
552 if ((a
= malloc(sizeof(struct info
))) == NULL
)
565 info_txt_new(FILE *stream
, char *txt
)
567 return info_new_full(stream
, NULL
, NULL
, txt
);
571 info_obj_new(FILE *stream
, GObject
*obj
, GtkTreeModel
*model
)
573 return info_new_full(stream
, obj
, model
, NULL
);
577 * Use msg_sender() to send a message describing a particular cell
580 send_tree_cell_msg_by(void msg_sender(FILE *, GtkBuildable
*, const char *, ...),
582 GtkTreeIter
*iter
, int col
, struct info
*a
)
584 GtkBuildable
*obj
= GTK_BUILDABLE(a
->obj
);
585 GtkTreeModel
*model
= a
->model
;
587 GValue value
= G_VALUE_INIT
;
588 char str
[BUFLEN
], *lc
= lc_numeric();
590 gtk_tree_model_get_value(model
, iter
, col
, &value
);
591 col_type
= gtk_tree_model_get_column_type(model
, col
);
594 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_int(&value
));
595 msg_sender(a
->outstr
, obj
, "gint", path_s
, str
, NULL
);
598 snprintf(str
, BUFLEN
, " %d %ld", col
, g_value_get_long(&value
));
599 msg_sender(a
->outstr
, obj
, "glong", path_s
, str
, NULL
);
602 snprintf(str
, BUFLEN
, " %d %" PRId64
, col
, g_value_get_int64(&value
));
603 msg_sender(a
->outstr
, obj
, "gint64", path_s
, str
, NULL
);
606 snprintf(str
, BUFLEN
, " %d %u", col
, g_value_get_uint(&value
));
607 msg_sender(a
->outstr
, obj
, "guint", path_s
, str
, NULL
);
610 snprintf(str
, BUFLEN
, " %d %lu", col
, g_value_get_ulong(&value
));
611 msg_sender(a
->outstr
, obj
, "gulong", path_s
, str
, NULL
);
614 snprintf(str
, BUFLEN
, " %d %" PRIu64
, col
, g_value_get_uint64(&value
));
615 msg_sender(a
->outstr
, obj
, "guint64", path_s
, str
, NULL
);
618 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_boolean(&value
));
619 msg_sender(a
->outstr
, obj
, "gboolean", path_s
, str
, NULL
);
622 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_float(&value
));
623 msg_sender(a
->outstr
, obj
, "gfloat", path_s
, str
, NULL
);
626 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_double(&value
));
627 msg_sender(a
->outstr
, obj
, "gdouble", path_s
, str
, NULL
);
630 snprintf(str
, BUFLEN
, " %d ", col
);
631 msg_sender(a
->outstr
, obj
, "gchararray", path_s
, str
, g_value_get_string(&value
), NULL
);
634 fprintf(stderr
, "column %d not implemented: %s\n", col
, G_VALUE_TYPE_NAME(&value
));
637 g_value_unset(&value
);
642 * Use msg_sender() to send one message per column for a single row
645 send_tree_row_msg_by(void msg_sender(FILE *, GtkBuildable
*, const char *, ...),
646 char *path_s
, GtkTreeIter
*iter
, struct info
*args
)
650 for (col
= 0; col
< gtk_tree_model_get_n_columns(args
->model
); col
++)
651 send_tree_cell_msg_by(msg_sender
, path_s
, iter
, col
, args
);
655 * send_tree_row_msg serves as an argument for
656 * gtk_tree_selection_selected_foreach()
659 send_tree_row_msg(GtkTreeModel
*model
,
660 GtkTreePath
*path
, GtkTreeIter
*iter
, struct info
*args
)
662 char *path_s
= gtk_tree_path_to_string(path
);
665 send_tree_row_msg_by(send_msg
, path_s
, iter
, args
);
671 * save_tree_row_msg serves as an argument for
672 * gtk_tree_model_foreach().
673 * Send message from GUI to global stream "save".
676 save_tree_row_msg(GtkTreeModel
*model
,
677 GtkTreePath
*path
, GtkTreeIter
*iter
, struct info
*args
)
679 char *path_s
= gtk_tree_path_to_string(path
);
682 send_tree_row_msg_by(send_msg_as_cmd
, path_s
, iter
, args
);
688 cb_calendar(GtkBuildable
*obj
, struct info
*a
)
691 unsigned int year
= 0, month
= 0, day
= 0;
693 gtk_calendar_get_date(GTK_CALENDAR(obj
), &year
, &month
, &day
);
694 snprintf(str
, BUFLEN
, "%04u-%02u-%02u", year
, ++month
, day
);
695 send_msg(a
->outstr
, obj
, a
->txt
, str
, NULL
);
699 cb_color_button(GtkBuildable
*obj
, struct info
*a
)
703 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
704 send_msg(a
->outstr
, obj
, a
->txt
, gdk_rgba_to_string(&color
), NULL
);
708 cb_editable(GtkBuildable
*obj
, struct info
*a
)
710 send_msg(a
->outstr
, obj
, a
->txt
, gtk_entry_get_text(GTK_ENTRY(obj
)), NULL
);
714 * Callback that sends a message about a pointer device button press
718 cb_event_box_button(GtkBuildable
*obj
, GdkEvent
*e
, struct info
*a
)
720 char data
[BUFLEN
], *lc
= lc_numeric();
722 snprintf(data
, BUFLEN
, "%d %.1lf %.1lf",
723 e
->button
.button
, e
->button
.x
, e
->button
.y
);
724 send_msg(a
->outstr
, obj
, a
->txt
, data
, NULL
);
730 * Callback that sends in a message the name of the key pressed when
731 * a GtkEventBox is focused
734 cb_event_box_key(GtkBuildable
*obj
, GdkEvent
*e
, struct info
*a
)
736 send_msg(a
->outstr
, obj
, a
->txt
, gdk_keyval_name(e
->key
.keyval
), NULL
);
741 * Callback that sends a message about pointer device motion in a
745 cb_event_box_motion(GtkBuildable
*obj
, GdkEvent
*e
, struct info
*a
)
747 char data
[BUFLEN
], *lc
= lc_numeric();
749 snprintf(data
, BUFLEN
, "%.1lf %.1lf", e
->button
.x
, e
->button
.y
);
750 send_msg(a
->outstr
, obj
, a
->txt
, data
, NULL
);
756 * Callback that only sends "name:tag" and returns false
759 cb_event_simple(GtkBuildable
*obj
, GdkEvent
*e
, struct info
*a
)
762 send_msg(a
->outstr
, obj
, a
->txt
, NULL
);
767 cb_file_chooser_button(GtkBuildable
*obj
, struct info
*a
)
769 send_msg(a
->outstr
, obj
, a
->txt
,
770 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(obj
)), NULL
);
774 cb_font_button(GtkBuildable
*obj
, struct info
*a
)
776 send_msg(a
->outstr
, obj
, a
->txt
,
777 gtk_font_button_get_font_name(GTK_FONT_BUTTON(obj
)), NULL
);
781 cb_menu_item(GtkBuildable
*obj
, struct info
*a
)
783 send_msg(a
->outstr
, obj
, a
->txt
,
784 gtk_menu_item_get_label(GTK_MENU_ITEM(obj
)), NULL
);
788 cb_range(GtkBuildable
*obj
, struct info
*a
)
790 char str
[BUFLEN
], *lc
= lc_numeric();
792 snprintf(str
, BUFLEN
, "%f", gtk_range_get_value(GTK_RANGE(obj
)));
793 send_msg(a
->outstr
, obj
, a
->txt
, str
, NULL
);
798 * Callback that sends user's selection from a file dialog
801 cb_send_file_chooser_dialog_selection(struct info
*a
)
803 send_msg(a
->outstr
, GTK_BUILDABLE(a
->obj
), "file",
804 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(a
->obj
)),
806 send_msg(a
->outstr
, GTK_BUILDABLE(a
->obj
), "folder",
807 gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(a
->obj
)),
812 * Callback that sends in a message the content of the text buffer
813 * passed in user_data
816 cb_send_text(GtkBuildable
*obj
, struct info
*ar
)
820 gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(ar
->obj
), &a
, &b
);
821 send_msg(ar
->outstr
, obj
, "text",
822 gtk_text_buffer_get_text(GTK_TEXT_BUFFER(ar
->obj
), &a
, &b
, TRUE
),
827 * Callback that sends in a message the highlighted text from the text
828 * buffer which was passed in user_data
831 cb_send_text_selection(GtkBuildable
*obj
, struct info
*ar
)
835 gtk_text_buffer_get_selection_bounds(GTK_TEXT_BUFFER(ar
->obj
), &a
, &b
);
836 send_msg(ar
->outstr
, obj
, "text",
837 gtk_text_buffer_get_text(GTK_TEXT_BUFFER(ar
->obj
), &a
, &b
, TRUE
),
842 * Callback that only sends "name:tag" and returns true
845 cb_simple(GtkBuildable
*obj
, struct info
*a
)
847 send_msg(a
->outstr
, obj
, a
->txt
, NULL
);
852 cb_spin_button(GtkBuildable
*obj
, struct info
*a
)
854 char str
[BUFLEN
], *lc
= lc_numeric();
856 snprintf(str
, BUFLEN
, "%f", gtk_spin_button_get_value(GTK_SPIN_BUTTON(obj
)));
857 send_msg(a
->outstr
, obj
, a
->txt
, str
, NULL
);
862 cb_switch(GtkBuildable
*obj
, void *pspec
, struct info
*a
)
865 send_msg(a
->outstr
, obj
,
866 gtk_switch_get_active(GTK_SWITCH(obj
)) ? "1" : "0",
871 cb_toggle_button(GtkBuildable
*obj
, struct info
*a
)
873 send_msg(a
->outstr
, obj
,
874 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(obj
)) ? "1" : "0",
879 cb_tree_selection(GtkBuildable
*obj
, struct info
*a
)
881 GtkTreeSelection
*sel
= GTK_TREE_SELECTION(obj
);
882 GtkTreeView
*view
= gtk_tree_selection_get_tree_view(sel
);
884 a
->obj
= G_OBJECT(view
);
885 send_msg(a
->outstr
, GTK_BUILDABLE(view
), a
->txt
, NULL
);
886 gtk_tree_selection_selected_foreach(
887 sel
, (GtkTreeSelectionForeachFunc
) send_tree_row_msg
, a
);
892 * ============================================================
893 * cb_draw() maintains a drawing on a GtkDrawingArea; it needs a few
895 * ============================================================
899 * The set of supported drawing operations
930 * Text placement mode for rel_move_for()
944 enum draw_op_policy
{
951 * One single element of a drawing
954 struct draw_op
*next
;
955 struct draw_op
*prev
;
956 unsigned long long int id
;
957 unsigned long long int before
;
958 enum draw_op_policy policy
;
964 * Argument sets for the various drawing operations
974 struct curve_to_args
{
983 struct move_to_args
{
988 struct rectangle_args
{
995 struct rel_move_for_args
{
1001 struct set_dash_args
{
1006 struct set_font_face_args
{
1007 cairo_font_slant_t slant
;
1008 cairo_font_weight_t weight
;
1012 struct set_font_size_args
{
1016 struct set_line_cap_args
{
1017 cairo_line_cap_t line_cap
;
1020 struct set_line_join_args
{
1021 cairo_line_join_t line_join
;
1024 struct set_line_width_args
{
1028 struct set_source_rgba_args
{
1032 struct show_text_args
{
1037 struct transform_args
{
1038 cairo_matrix_t matrix
;
1042 draw(cairo_t
*cr
, enum cairo_fn op
, void *op_args
)
1046 struct move_to_args
*args
= op_args
;
1048 cairo_line_to(cr
, args
->x
, args
->y
);
1052 struct move_to_args
*args
= op_args
;
1054 cairo_rel_line_to(cr
, args
->x
, args
->y
);
1058 struct move_to_args
*args
= op_args
;
1060 cairo_move_to(cr
, args
->x
, args
->y
);
1064 struct move_to_args
*args
= op_args
;
1066 cairo_rel_move_to(cr
, args
->x
, args
->y
);
1070 struct arc_args
*args
= op_args
;
1072 cairo_arc(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
1075 case ARC_NEGATIVE
: {
1076 struct arc_args
*args
= op_args
;
1078 cairo_arc_negative(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
1082 struct curve_to_args
*args
= op_args
;
1084 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
1087 case REL_CURVE_TO
: {
1088 struct curve_to_args
*args
= op_args
;
1090 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
1094 struct rectangle_args
*args
= op_args
;
1096 cairo_rectangle(cr
, args
->x
, args
->y
, args
->width
, args
->height
);
1100 cairo_close_path(cr
);
1103 struct show_text_args
*args
= op_args
;
1105 cairo_show_text(cr
, args
->text
);
1108 case REL_MOVE_FOR
: {
1109 cairo_text_extents_t e
;
1110 double dx
= 0.0, dy
= 0.0;
1111 struct rel_move_for_args
*args
= op_args
;
1113 cairo_text_extents(cr
, args
->text
, &e
);
1114 switch (args
->ref
) {
1115 case C
: dx
= -e
.width
/ 2; dy
= e
.height
/ 2; break;
1116 case E
: dx
= -e
.width
; dy
= e
.height
/ 2; break;
1117 case N
: dx
= -e
.width
/ 2; dy
= e
.height
; break;
1118 case NE
: dx
= -e
.width
; dy
= e
.height
; break;
1119 case NW
: dy
= e
.height
; break;
1120 case S
: dx
= -e
.width
/ 2; break;
1121 case SE
: dx
= -e
.width
; break;
1123 case W
: dy
= e
.height
/ 2; break;
1124 default: ABORT
; break;
1126 cairo_rel_move_to(cr
, dx
, dy
);
1130 cairo_identity_matrix(cr
);
1135 case STROKE_PRESERVE
:
1136 cairo_stroke_preserve(cr
);
1142 cairo_fill_preserve(cr
);
1145 struct set_dash_args
*args
= op_args
;
1147 cairo_set_dash(cr
, args
->dashes
, args
->num_dashes
, 0);
1150 case SET_FONT_FACE
: {
1151 struct set_font_face_args
*args
= op_args
;
1153 cairo_select_font_face(cr
, args
->family
, args
->slant
, args
->weight
);
1156 case SET_FONT_SIZE
: {
1157 struct set_font_size_args
*args
= op_args
;
1159 cairo_set_font_size(cr
, args
->size
);
1162 case SET_LINE_CAP
: {
1163 struct set_line_cap_args
*args
= op_args
;
1165 cairo_set_line_cap(cr
, args
->line_cap
);
1168 case SET_LINE_JOIN
: {
1169 struct set_line_join_args
*args
= op_args
;
1171 cairo_set_line_join(cr
, args
->line_join
);
1174 case SET_LINE_WIDTH
: {
1175 struct set_line_width_args
*args
= op_args
;
1177 cairo_set_line_width(cr
, args
->width
);
1180 case SET_SOURCE_RGBA
: {
1181 struct set_source_rgba_args
*args
= op_args
;
1183 gdk_cairo_set_source_rgba(cr
, &args
->color
);
1187 struct transform_args
*args
= op_args
;
1189 cairo_transform(cr
, &args
->matrix
);
1199 * Callback that draws on a GtkDrawingArea
1202 cb_draw(GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
1207 for (op
= g_object_get_data(G_OBJECT(widget
), "draw_ops");
1210 draw(cr
, op
->op
, op
->op_args
);
1216 * ============================================================
1217 * Manipulating the GUI
1218 * ============================================================
1222 update_button(GObject
*obj
, const char *action
, const char *data
,
1223 const char *whole_msg
, GType type
, struct info
*ar
)
1226 if (eql(action
, "set_label"))
1227 gtk_button_set_label(GTK_BUTTON(obj
), data
);
1229 ign_cmd(type
, whole_msg
);
1233 update_calendar(GObject
*obj
, const char *action
, const char *data
,
1234 const char *whole_msg
, GType type
, struct info
*ar
)
1236 GtkCalendar
*calendar
= GTK_CALENDAR(obj
);
1238 int year
= 0, month
= 0, day
= 0;
1241 if (eql(action
, "select_date") &&
1242 sscanf(data
, "%d-%d-%d %c", &year
, &month
, &day
, &dummy
) == 3) {
1243 if (month
> -1 && month
<= 11 && day
> 0 && day
<= 31) {
1244 gtk_calendar_select_month(calendar
, --month
, year
);
1245 gtk_calendar_select_day(calendar
, day
);
1247 ign_cmd(type
, whole_msg
);
1248 } else if (eql(action
, "mark_day") &&
1249 sscanf(data
, "%d %c", &day
, &dummy
) == 1) {
1250 if (day
> 0 && day
<= 31)
1251 gtk_calendar_mark_day(calendar
, day
);
1253 ign_cmd(type
, whole_msg
);
1254 } else if (eql(action
, "clear_marks") && sscanf(data
, " %c", &dummy
) < 1)
1255 gtk_calendar_clear_marks(calendar
);
1257 ign_cmd(type
, whole_msg
);
1261 * Common actions for various kinds of window. Return false if
1262 * command is ignored. Runs inside gtk_main().
1265 update_class_window(GObject
*obj
, const char *action
, const char *data
,
1266 const char *whole_msg
, GType type
, struct info
*ar
)
1268 GtkWindow
*window
= GTK_WINDOW(obj
);
1275 if (eql(action
, "set_title"))
1276 gtk_window_set_title(window
, data
);
1277 else if (eql(action
, "fullscreen") && sscanf(data
, " %c", &dummy
) < 1)
1278 gtk_window_fullscreen(window
);
1279 else if (eql(action
, "unfullscreen") && sscanf(data
, " %c", &dummy
) < 1)
1280 gtk_window_unfullscreen(window
);
1281 else if (eql(action
, "resize") &&
1282 sscanf(data
, "%d %d %c", &x
, &y
, &dummy
) == 2)
1283 gtk_window_resize(window
, x
, y
);
1284 else if (eql(action
, "resize") && sscanf(data
, " %c", &dummy
) < 1) {
1285 gtk_window_get_default_size(window
, &x
, &y
);
1286 gtk_window_resize(window
, x
, y
);
1287 } else if (eql(action
, "move") &&
1288 sscanf(data
, "%d %d %c", &x
, &y
, &dummy
) == 2)
1289 gtk_window_move(window
, x
, y
);
1296 update_color_button(GObject
*obj
, const char *action
, const char *data
,
1297 const char *whole_msg
, GType type
, struct info
*ar
)
1302 if (eql(action
, "set_color")) {
1303 gdk_rgba_parse(&color
, data
);
1304 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
1306 ign_cmd(type
, whole_msg
);
1310 update_combo_box_text(GObject
*obj
, const char *action
, const char *data
,
1311 const char *whole_msg
, GType type
, struct info
*ar
)
1313 GtkComboBoxText
*combobox
= GTK_COMBO_BOX_TEXT(obj
);
1314 char data1
[strlen(data
) + 1];
1319 strcpy(data1
, data
);
1320 if (eql(action
, "prepend_text"))
1321 gtk_combo_box_text_prepend_text(combobox
, data1
);
1322 else if (eql(action
, "append_text"))
1323 gtk_combo_box_text_append_text(combobox
, data1
);
1324 else if (eql(action
, "remove") && sscanf(data
, "%d %c", &val
, &dummy
) == 1)
1325 gtk_combo_box_text_remove(combobox
, strtol(data1
, NULL
, 10));
1326 else if (eql(action
, "insert_text")) {
1327 char *position
= strtok(data1
, WHITESPACE
);
1328 char *text
= strtok(NULL
, WHITESPACE
);
1330 gtk_combo_box_text_insert_text(combobox
,
1331 strtol(position
, NULL
, 10), text
);
1333 ign_cmd(type
, whole_msg
);
1337 * update_drawing_area(), which runs inside gtk_main(), maintains a
1338 * list of drawing operations. It needs a few helper functions. It
1339 * is the responsibility of cb_draw() to actually execute the list.
1349 * Fill structure *op with the drawing operation according to action
1350 * and with the appropriate set of arguments
1352 static enum draw_op_stat
1353 set_draw_op(struct draw_op
*op
, const char *action
, const char *data
)
1356 const char *raw_args
= data
;
1357 enum draw_op_stat result
= SUCCESS
;
1360 if (sscanf(data
, "=%llu %n", &op
->id
, &args_start
) == 1) {
1361 op
->policy
= REPLACE
;
1362 result
= NEED_REDRAW
;
1363 } else if (sscanf(data
, "%llu<%llu %n", &op
->id
, &op
->before
, &args_start
) == 2) {
1364 op
->policy
= BEFORE
;
1365 result
= NEED_REDRAW
;
1366 } else if (sscanf(data
, "%llu %n", &op
->id
, &args_start
) == 1)
1367 op
->policy
= APPEND
;
1370 raw_args
+= args_start
;
1371 if (eql(action
, "line_to")) {
1372 struct move_to_args
*args
;
1374 if ((args
= malloc(sizeof(*args
))) == NULL
)
1378 if (sscanf(raw_args
, "%lf %lf %c", &args
->x
, &args
->y
, &dummy
) != 2)
1380 } else if (eql(action
, "rel_line_to")) {
1381 struct move_to_args
*args
;
1383 if ((args
= malloc(sizeof(*args
))) == NULL
)
1385 op
->op
= REL_LINE_TO
;
1387 if (sscanf(raw_args
, "%lf %lf %c", &args
->x
, &args
->y
, &dummy
) != 2)
1389 } else if (eql(action
, "move_to")) {
1390 struct move_to_args
*args
;
1392 if ((args
= malloc(sizeof(*args
))) == NULL
)
1396 if (sscanf(raw_args
, "%lf %lf %c", &args
->x
, &args
->y
, &dummy
) != 2)
1398 } else if (eql(action
, "rel_move_to")) {
1399 struct move_to_args
*args
;
1401 if ((args
= malloc(sizeof(*args
))) == NULL
)
1403 op
->op
= REL_MOVE_TO
;
1405 if (sscanf(raw_args
, "%lf %lf %c", &args
->x
, &args
->y
, &dummy
) != 2)
1407 } else if (eql(action
, "arc")) {
1408 struct arc_args
*args
;
1411 if ((args
= malloc(sizeof(*args
))) == NULL
)
1415 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %c",
1416 &args
->x
, &args
->y
, &args
->radius
, °1
, °2
, &dummy
) != 5)
1418 args
->angle1
= deg1
* (M_PI
/ 180.L
);
1419 args
->angle2
= deg2
* (M_PI
/ 180.L
);
1420 } else if (eql(action
, "arc_negative")) {
1422 struct arc_args
*args
;
1424 if ((args
= malloc(sizeof(*args
))) == NULL
)
1426 op
->op
= ARC_NEGATIVE
;
1428 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %c",
1429 &args
->x
, &args
->y
, &args
->radius
, °1
, °2
, &dummy
) != 5)
1431 args
->angle1
= deg1
* (M_PI
/ 180.L
);
1432 args
->angle2
= deg2
* (M_PI
/ 180.L
);
1433 } else if (eql(action
, "curve_to")) {
1434 struct curve_to_args
*args
;
1436 if ((args
= malloc(sizeof(*args
))) == NULL
)
1440 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %lf %c",
1441 &args
->x1
, &args
->y1
, &args
->x2
, &args
->y2
, &args
->x3
, &args
->y3
, &dummy
) != 6)
1443 } else if (eql(action
, "rel_curve_to")) {
1444 struct curve_to_args
*args
;
1446 if ((args
= malloc(sizeof(*args
))) == NULL
)
1448 op
->op
= REL_CURVE_TO
;
1450 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %lf %c",
1451 &args
->x1
, &args
->y1
, &args
->x2
, &args
->y2
, &args
->x3
, &args
->y3
, &dummy
) != 6)
1453 } else if (eql(action
, "rectangle")) {
1454 struct rectangle_args
*args
;
1456 if ((args
= malloc(sizeof(*args
))) == NULL
)
1460 if (sscanf(raw_args
, "%lf %lf %lf %lf %c",
1461 &args
->x
, &args
->y
, &args
->width
, &args
->height
, &dummy
) != 4)
1463 } else if (eql(action
, "close_path")) {
1464 op
->op
= CLOSE_PATH
;
1465 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1468 } else if (eql(action
, "show_text")) {
1469 struct show_text_args
*args
;
1472 len
= strlen(raw_args
) + 1;
1473 if ((args
= malloc(sizeof(*args
) + len
* sizeof(args
->text
[0]))) == NULL
)
1477 args
->len
= len
; /* not used */
1478 strncpy(args
->text
, raw_args
, len
);
1479 result
= NEED_REDRAW
;
1480 } else if (eql(action
, "rel_move_for")) {
1481 char ref_point
[2 + 1];
1483 struct rel_move_for_args
*args
;
1485 if (sscanf(raw_args
, "%2s %n", ref_point
, &start
) < 1)
1487 len
= strlen(raw_args
+ start
) + 1;
1488 if ((args
= malloc(sizeof(*args
) + len
* sizeof(args
->text
[0]))) == NULL
)
1490 if (eql(ref_point
, "c"))
1492 else if (eql(ref_point
, "e"))
1494 else if (eql(ref_point
, "n"))
1496 else if (eql(ref_point
, "ne"))
1498 else if (eql(ref_point
, "nw"))
1500 else if (eql(ref_point
, "s"))
1502 else if (eql(ref_point
, "se"))
1504 else if (eql(ref_point
, "sw"))
1506 else if (eql(ref_point
, "w"))
1510 op
->op
= REL_MOVE_FOR
;
1512 args
->len
= len
; /* not used */
1513 strncpy(args
->text
, (raw_args
+ start
), len
);
1514 } else if (eql(action
, "stroke")) {
1516 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1519 result
= NEED_REDRAW
;
1520 } else if (eql(action
, "stroke_preserve")) {
1521 op
->op
= STROKE_PRESERVE
;
1522 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1525 result
= NEED_REDRAW
;
1526 } else if (eql(action
, "fill")) {
1528 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1531 result
= NEED_REDRAW
;
1532 } else if (eql(action
, "fill_preserve")) {
1533 op
->op
= FILL_PRESERVE
;
1534 if (sscanf(raw_args
, " %c", &dummy
) > 0)
1537 result
= NEED_REDRAW
;
1538 } else if (eql(action
, "set_dash")) {
1540 char data1
[strlen(raw_args
) + 1];
1542 struct set_dash_args
*args
;
1544 strcpy(data1
, raw_args
);
1551 } while (next
!= end
);
1552 if ((args
= malloc(sizeof(*args
) + n
* sizeof(args
->dashes
[0]))) == NULL
)
1556 args
->num_dashes
= n
;
1557 for (i
= 0, next
= data1
; i
< n
; i
++, next
= end
) {
1558 args
->dashes
[i
] = strtod(next
, &end
);
1560 } else if (eql(action
, "set_font_face")) {
1561 char slant
[7 + 1]; /* "oblique" */
1562 char weight
[6 + 1]; /* "normal" */
1563 int family_start
, family_len
;
1564 struct set_font_face_args
*args
;
1566 if (sscanf(raw_args
, "%7s %6s %n%*s", slant
, weight
, &family_start
) != 2)
1568 family_len
= strlen(raw_args
+ family_start
) + 1;
1569 if ((args
= malloc(sizeof(*args
) + family_len
* sizeof(args
->family
[0]))) == NULL
)
1571 op
->op
= SET_FONT_FACE
;
1573 strncpy(args
->family
, raw_args
+ family_start
, family_len
);
1574 if (eql(slant
, "normal"))
1575 args
->slant
= CAIRO_FONT_SLANT_NORMAL
;
1576 else if (eql(slant
, "italic"))
1577 args
->slant
= CAIRO_FONT_SLANT_ITALIC
;
1578 else if (eql(slant
, "oblique"))
1579 args
->slant
= CAIRO_FONT_SLANT_OBLIQUE
;
1582 if (eql(weight
, "normal"))
1583 args
->weight
= CAIRO_FONT_WEIGHT_NORMAL
;
1584 else if (eql(weight
, "bold"))
1585 args
->weight
= CAIRO_FONT_WEIGHT_BOLD
;
1588 } else if (eql(action
, "set_font_size")) {
1589 struct set_font_size_args
*args
;
1591 if ((args
= malloc(sizeof(*args
))) == NULL
)
1593 op
->op
= SET_FONT_SIZE
;
1595 if (sscanf(raw_args
, "%lf %c", &args
->size
, &dummy
) != 1)
1597 } else if (eql(action
, "set_line_cap")) {
1598 char str
[6 + 1]; /* "square" */
1599 struct set_line_cap_args
*args
;
1601 if ((args
= malloc(sizeof(*args
))) == NULL
)
1603 op
->op
= SET_LINE_CAP
;
1605 if (sscanf(raw_args
, "%6s %c", str
, &dummy
) != 1)
1607 if (eql(str
, "butt"))
1608 args
->line_cap
= CAIRO_LINE_CAP_BUTT
;
1609 else if (eql(str
, "round"))
1610 args
->line_cap
= CAIRO_LINE_CAP_ROUND
;
1611 else if (eql(str
, "square"))
1612 args
->line_cap
= CAIRO_LINE_CAP_SQUARE
;
1615 } else if (eql(action
, "set_line_join")) {
1616 char str
[5 + 1]; /* "miter" */
1617 struct set_line_join_args
*args
;
1619 if ((args
= malloc(sizeof(*args
))) == NULL
)
1621 op
->op
= SET_LINE_JOIN
;
1623 if (sscanf(raw_args
, "%5s %c", str
, &dummy
) != 1)
1625 if (eql(str
, "miter"))
1626 args
->line_join
= CAIRO_LINE_JOIN_MITER
;
1627 else if (eql(str
, "round"))
1628 args
->line_join
= CAIRO_LINE_JOIN_ROUND
;
1629 else if (eql(str
, "bevel"))
1630 args
->line_join
= CAIRO_LINE_JOIN_BEVEL
;
1633 } else if (eql(action
, "set_line_width")) {
1634 struct set_line_width_args
*args
;
1636 if ((args
= malloc(sizeof(*args
))) == NULL
)
1638 op
->op
= SET_LINE_WIDTH
;
1640 if (sscanf(raw_args
, "%lf %c", &args
->width
, &dummy
) != 1)
1642 } else if (eql(action
, "set_source_rgba")) {
1643 struct set_source_rgba_args
*args
;
1645 if ((args
= malloc(sizeof(*args
))) == NULL
)
1647 op
->op
= SET_SOURCE_RGBA
;
1649 gdk_rgba_parse(&args
->color
, raw_args
);
1650 } else if (eql(action
, "transform")) {
1652 double xx
, yx
, xy
, yy
, x0
, y0
;
1654 if (sscanf(raw_args
, "%lf %lf %lf %lf %lf %lf %c",
1655 &xx
, &yx
, &xy
, &yy
, &x0
, &y0
, &dummy
) == 6) {
1656 struct transform_args
*args
;
1658 if ((args
= malloc(sizeof(*args
))) == NULL
)
1662 cairo_matrix_init(&args
->matrix
, xx
, yx
, xy
, yy
, x0
, y0
);
1663 } else if (sscanf(raw_args
, " %c", &dummy
) < 1) {
1668 } else if (eql(action
, "translate")) {
1670 struct transform_args
*args
;
1672 if ((args
= malloc(sizeof(*args
))) == NULL
)
1676 if (sscanf(raw_args
, "%lf %lf %c", &tx
, &ty
, &dummy
) != 2)
1678 cairo_matrix_init_translate(&args
->matrix
, tx
, ty
);
1679 } else if (eql(action
, "scale")) {
1681 struct transform_args
*args
;
1683 if ((args
= malloc(sizeof(*args
))) == NULL
)
1687 if (sscanf(raw_args
, "%lf %lf %c", &sx
, &sy
, &dummy
) != 2)
1689 cairo_matrix_init_scale(&args
->matrix
, sx
, sy
);
1690 } else if (eql(action
, "rotate")) {
1692 struct transform_args
*args
;
1694 if ((args
= malloc(sizeof(*args
))) == NULL
)
1698 if (sscanf(raw_args
, "%lf %c", &angle
, &dummy
) != 1)
1700 cairo_matrix_init_rotate(&args
->matrix
, angle
* (M_PI
/ 180.L
));
1707 * Add another element to widget's "draw_ops" list
1709 static enum draw_op_stat
1710 ins_draw_op(GObject
*widget
, const char *action
, const char *data
)
1712 enum draw_op_stat result
;
1713 struct draw_op
*new_op
= NULL
, *draw_ops
= NULL
, *prev_op
= NULL
;
1715 if ((new_op
= malloc(sizeof(*new_op
))) == NULL
)
1717 new_op
->op_args
= NULL
;
1718 new_op
->next
= NULL
;
1719 if ((result
= set_draw_op(new_op
, action
, data
)) == FAILURE
) {
1720 free(new_op
->op_args
);
1724 switch (new_op
->policy
) {
1726 if ((draw_ops
= g_object_get_data(widget
, "draw_ops")) == NULL
)
1727 g_object_set_data(widget
, "draw_ops", new_op
);
1729 for (prev_op
= draw_ops
;
1730 prev_op
->next
!= NULL
;
1731 prev_op
= prev_op
->next
);
1732 prev_op
->next
= new_op
;
1736 for (prev_op
= NULL
, draw_ops
= g_object_get_data(widget
, "draw_ops");
1737 draw_ops
!= NULL
&& draw_ops
->id
!= new_op
->before
;
1738 prev_op
= draw_ops
, draw_ops
= draw_ops
->next
);
1739 if (prev_op
== NULL
) { /* prepend a new first element */
1740 g_object_set_data(widget
, "draw_ops", new_op
);
1741 new_op
->next
= draw_ops
;
1742 } else if (draw_ops
== NULL
) /* append */
1743 prev_op
->next
= new_op
;
1745 new_op
->next
= draw_ops
;
1746 prev_op
->next
= new_op
;
1750 for (prev_op
= NULL
, draw_ops
= g_object_get_data(widget
, "draw_ops");
1751 draw_ops
!= NULL
&& draw_ops
->id
!= new_op
->id
;
1752 prev_op
= draw_ops
, draw_ops
= draw_ops
->next
);
1753 if (draw_ops
== NULL
&& prev_op
== NULL
) /* start a new list */
1754 g_object_set_data(widget
, "draw_ops", new_op
);
1755 else if (prev_op
== NULL
) { /* replace the first element */
1756 g_object_set_data(widget
, "draw_ops", new_op
);
1757 new_op
->next
= draw_ops
->next
;
1758 free(draw_ops
->op_args
);
1760 } else if (draw_ops
== NULL
) /* append */
1761 prev_op
->next
= new_op
;
1762 else { /* replace some other element */
1763 new_op
->next
= draw_ops
->next
;
1764 prev_op
->next
= new_op
;
1765 free(draw_ops
->op_args
);
1777 * Remove all elements with the given id from widget's "draw_ops" list
1779 static enum draw_op_stat
1780 rem_draw_op(GObject
*widget
, const char *data
)
1783 struct draw_op
*op
, *next_op
, *prev_op
= NULL
;
1784 unsigned long long int id
;
1786 if (sscanf(data
, "%llu %c", &id
, &dummy
) != 1)
1788 op
= g_object_get_data(widget
, "draw_ops");
1789 while (op
!= NULL
) {
1792 if (prev_op
== NULL
) /* list head */
1793 g_object_set_data(widget
, "draw_ops", op
->next
);
1795 prev_op
->next
= op
->next
;
1806 refresh_widget(GtkWidget
*widget
)
1808 gint height
= gtk_widget_get_allocated_height(widget
);
1809 gint width
= gtk_widget_get_allocated_width(widget
);
1811 gtk_widget_queue_draw_area(widget
, 0, 0, width
, height
);
1812 return G_SOURCE_REMOVE
;
1816 * Write the drawing from the GtkDrawingArea widget in an appropriate
1819 static enum draw_op_stat
1820 save_drawing(GtkWidget
*widget
, const char *fn
)
1822 cairo_surface_t
*sur
;
1824 int height
= gtk_widget_get_allocated_height(widget
);
1825 int width
= gtk_widget_get_allocated_width(widget
);
1827 if (has_suffix(fn
, ".epsf") || has_suffix(fn
, ".eps")) {
1828 sur
= cairo_ps_surface_create(fn
, width
, height
);
1829 cairo_ps_surface_set_eps(sur
, TRUE
);
1830 } else if (has_suffix(fn
, ".pdf"))
1831 sur
= cairo_pdf_surface_create(fn
, width
, height
);
1832 else if (has_suffix(fn
, ".ps"))
1833 sur
= cairo_ps_surface_create(fn
, width
, height
);
1834 else if (has_suffix(fn
, ".svg"))
1835 sur
= cairo_svg_surface_create(fn
, width
, height
);
1838 cr
= cairo_create(sur
);
1839 cb_draw(widget
, cr
, NULL
);
1841 cairo_surface_destroy(sur
);
1846 update_drawing_area(GObject
*obj
, const char *action
, const char *data
,
1847 const char *whole_msg
, GType type
, struct info
*ar
)
1849 enum draw_op_stat dost
;
1852 if (eql(action
, "remove"))
1853 dost
= rem_draw_op(obj
, data
);
1854 else if (eql(action
, "save"))
1855 dost
= save_drawing(GTK_WIDGET(obj
), data
);
1857 dost
= ins_draw_op(obj
, action
, data
);
1860 gdk_threads_add_idle_full(G_PRIORITY_LOW
,
1861 (GSourceFunc
) refresh_widget
,
1862 GTK_WIDGET(obj
), NULL
);
1865 ign_cmd(type
, whole_msg
);
1876 update_entry(GObject
*obj
, const char *action
, const char *data
,
1877 const char *whole_msg
, GType type
, struct info
*ar
)
1879 GtkEntry
*entry
= GTK_ENTRY(obj
);
1882 if (eql(action
, "set_text"))
1883 gtk_entry_set_text(entry
, data
);
1884 else if (eql(action
, "set_placeholder_text"))
1885 gtk_entry_set_placeholder_text(entry
, data
);
1887 ign_cmd(type
, whole_msg
);
1891 update_expander(GObject
*obj
, const char *action
, const char *data
,
1892 const char *whole_msg
, GType type
, struct info
*ar
)
1894 GtkExpander
*expander
= GTK_EXPANDER(obj
);
1899 if (eql(action
, "set_expanded") &&
1900 sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
1901 gtk_expander_set_expanded(expander
, val
);
1902 else if (eql(action
, "set_label"))
1903 gtk_expander_set_label(expander
, data
);
1905 ign_cmd(type
, whole_msg
);
1909 update_file_chooser_button(GObject
*obj
, const char *action
, const char *data
,
1910 const char *whole_msg
, GType type
, struct info
*ar
)
1913 if (eql(action
, "set_filename"))
1914 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(obj
), data
);
1916 ign_cmd(type
, whole_msg
);
1920 update_file_chooser_dialog(GObject
*obj
, const char *action
, const char *data
,
1921 const char *whole_msg
, GType type
, struct info
*ar
)
1923 GtkFileChooser
*chooser
= GTK_FILE_CHOOSER(obj
);
1926 if (eql(action
, "set_filename"))
1927 gtk_file_chooser_set_filename(chooser
, data
);
1928 else if (eql(action
, "set_current_name"))
1929 gtk_file_chooser_set_current_name(chooser
, data
);
1930 else if (update_class_window(obj
, action
, data
, whole_msg
, type
, NULL
));
1932 ign_cmd(type
, whole_msg
);
1936 update_focus(GObject
*obj
, const char *action
, const char *data
,
1937 const char *whole_msg
, GType type
, struct info
*ar
)
1944 if (sscanf(data
, " %c", &dummy
) < 1 &&
1945 gtk_widget_get_can_focus(GTK_WIDGET(obj
)))
1946 gtk_widget_grab_focus(GTK_WIDGET(obj
));
1948 ign_cmd(type
, whole_msg
);
1952 update_font_button(GObject
*obj
, const char *action
, const char *data
,
1953 const char *whole_msg
, GType type
, struct info
*ar
)
1955 GtkFontButton
*font_button
= GTK_FONT_BUTTON(obj
);
1958 if (eql(action
, "set_font_name"))
1959 gtk_font_button_set_font_name(font_button
, data
);
1961 ign_cmd(type
, whole_msg
);
1965 update_frame(GObject
*obj
, const char *action
, const char *data
,
1966 const char *whole_msg
, GType type
, struct info
*ar
)
1969 if (eql(action
, "set_label"))
1970 gtk_frame_set_label(GTK_FRAME(obj
), data
);
1972 ign_cmd(type
, whole_msg
);
1976 update_image(GObject
*obj
, const char *action
, const char *data
,
1977 const char *whole_msg
, GType type
, struct info
*ar
)
1980 GtkImage
*image
= GTK_IMAGE(obj
);
1983 gtk_image_get_icon_name(image
, NULL
, &size
);
1984 if (eql(action
, "set_from_file"))
1985 gtk_image_set_from_file(image
, data
);
1986 else if (eql(action
, "set_from_icon_name"))
1987 gtk_image_set_from_icon_name(image
, data
, size
);
1989 ign_cmd(type
, whole_msg
);
1993 update_label(GObject
*obj
, const char *action
, const char *data
,
1994 const char *whole_msg
, GType type
, struct info
*ar
)
1997 if (eql(action
, "set_text"))
1998 gtk_label_set_text(GTK_LABEL(obj
), data
);
2000 ign_cmd(type
, whole_msg
);
2004 update_notebook(GObject
*obj
, const char *action
, const char *data
,
2005 const char *whole_msg
, GType type
, struct info
*ar
)
2008 int val
, n_pages
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(obj
));
2011 if (eql(action
, "set_current_page") &&
2012 sscanf(data
, "%d %c", &val
, &dummy
) == 1 &&
2013 val
>= 0 && val
< n_pages
)
2014 gtk_notebook_set_current_page(GTK_NOTEBOOK(obj
), val
);
2016 ign_cmd(type
, whole_msg
);
2020 update_nothing(GObject
*obj
, const char *action
, const char *data
,
2021 const char *whole_msg
, GType type
, struct info
*ar
)
2032 update_print_dialog(GObject
*obj
, const char *action
, const char *data
,
2033 const char *whole_msg
, GType type
, struct info
*ar
)
2035 GtkPageSetup
*page_setup
;
2037 GtkPrintSettings
*settings
;
2038 GtkPrintUnixDialog
*dialog
= GTK_PRINT_UNIX_DIALOG(obj
);
2039 GtkPrinter
*printer
;
2043 if (eql(action
, "print")) {
2044 response_id
= gtk_dialog_run(GTK_DIALOG(dialog
));
2045 switch (response_id
) {
2046 case GTK_RESPONSE_OK
:
2047 printer
= gtk_print_unix_dialog_get_selected_printer(dialog
);
2048 settings
= gtk_print_unix_dialog_get_settings(dialog
);
2049 page_setup
= gtk_print_unix_dialog_get_page_setup(dialog
);
2050 job
= gtk_print_job_new(data
, printer
, settings
, page_setup
);
2051 if (gtk_print_job_set_source_file(job
, data
, NULL
))
2052 gtk_print_job_send(job
, NULL
, NULL
, NULL
);
2054 ign_cmd(type
, whole_msg
);
2055 g_clear_object(&settings
);
2056 g_clear_object(&job
);
2058 case GTK_RESPONSE_CANCEL
:
2059 case GTK_RESPONSE_DELETE_EVENT
:
2062 fprintf(stderr
, "%s sent an unexpected response id (%d)\n",
2063 widget_name(GTK_BUILDABLE(dialog
)), response_id
);
2066 gtk_widget_hide(GTK_WIDGET(dialog
));
2068 ign_cmd(type
, whole_msg
);
2072 update_progress_bar(GObject
*obj
, const char *action
, const char *data
,
2073 const char *whole_msg
, GType type
, struct info
*ar
)
2075 GtkProgressBar
*progressbar
= GTK_PROGRESS_BAR(obj
);
2080 if (eql(action
, "set_text"))
2081 gtk_progress_bar_set_text(progressbar
, *data
== '\0' ? NULL
: data
);
2082 else if (eql(action
, "set_fraction") &&
2083 sscanf(data
, "%lf %c", &frac
, &dummy
) == 1)
2084 gtk_progress_bar_set_fraction(progressbar
, frac
);
2086 ign_cmd(type
, whole_msg
);
2090 update_scale(GObject
*obj
, const char *action
, const char *data
,
2091 const char *whole_msg
, GType type
, struct info
*ar
)
2093 GtkRange
*range
= GTK_RANGE(obj
);
2098 if (eql(action
, "set_value") && sscanf(data
, "%lf %c", &val1
, &dummy
) == 1)
2099 gtk_range_set_value(range
, val1
);
2100 else if (eql(action
, "set_fill_level") &&
2101 sscanf(data
, "%lf %c", &val1
, &dummy
) == 1) {
2102 gtk_range_set_fill_level(range
, val1
);
2103 gtk_range_set_show_fill_level(range
, TRUE
);
2104 } else if (eql(action
, "set_fill_level") &&
2105 sscanf(data
, " %c", &dummy
) < 1)
2106 gtk_range_set_show_fill_level(range
, FALSE
);
2107 else if (eql(action
, "set_range") &&
2108 sscanf(data
, "%lf %lf %c", &val1
, &val2
, &dummy
) == 2)
2109 gtk_range_set_range(range
, val1
, val2
);
2110 else if (eql(action
, "set_increments") &&
2111 sscanf(data
, "%lf %lf %c", &val1
, &val2
, &dummy
) == 2)
2112 gtk_range_set_increments(range
, val1
, val2
);
2114 ign_cmd(type
, whole_msg
);
2118 update_scrolled_window(GObject
*obj
, const char *action
, const char *data
,
2119 const char *whole_msg
, GType type
, struct info
*ar
)
2121 GtkScrolledWindow
*window
= GTK_SCROLLED_WINDOW(obj
);
2122 GtkAdjustment
*hadj
= gtk_scrolled_window_get_hadjustment(window
);
2123 GtkAdjustment
*vadj
= gtk_scrolled_window_get_vadjustment(window
);
2128 if (eql(action
, "hscroll") && sscanf(data
, "%lf %c", &d0
, &dummy
) == 1)
2129 gtk_adjustment_set_value(hadj
, d0
);
2130 else if (eql(action
, "vscroll") && sscanf(data
, "%lf %c", &d0
, &dummy
) == 1)
2131 gtk_adjustment_set_value(vadj
, d0
);
2132 else if (eql(action
, "hscroll_to_range") &&
2133 sscanf(data
, "%lf %lf %c", &d0
, &d1
, &dummy
) == 2)
2134 gtk_adjustment_clamp_page(hadj
, d0
, d1
);
2135 else if (eql(action
, "vscroll_to_range") &&
2136 sscanf(data
, "%lf %lf %c", &d0
, &d1
, &dummy
) == 2)
2137 gtk_adjustment_clamp_page(vadj
, d0
, d1
);
2139 ign_cmd(type
, whole_msg
);
2143 update_sensitivity(GObject
*obj
, const char *action
, const char *data
,
2144 const char *whole_msg
, GType type
, struct info
*ar
)
2153 if (sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
2154 gtk_widget_set_sensitive(GTK_WIDGET(obj
), val
);
2156 ign_cmd(type
, whole_msg
);
2160 update_size_request(GObject
*obj
, const char *action
, const char *data
,
2161 const char *whole_msg
, GType type
, struct info
*ar
)
2170 if (sscanf(data
, "%d %d %c", &x
, &y
, &dummy
) == 2)
2171 gtk_widget_set_size_request(GTK_WIDGET(obj
), x
, y
);
2172 else if (sscanf(data
, " %c", &dummy
) < 1)
2173 gtk_widget_set_size_request(GTK_WIDGET(obj
), -1, -1);
2175 ign_cmd(type
, whole_msg
);
2179 update_socket(GObject
*obj
, const char *action
, const char *data
,
2180 const char *whole_msg
, GType type
, struct info
*ar
)
2182 GtkSocket
*socket
= GTK_SOCKET(obj
);
2184 char str
[BUFLEN
], dummy
;
2187 if (eql(action
, "id") && sscanf(data
, " %c", &dummy
) < 1) {
2188 id
= gtk_socket_get_id(socket
);
2189 snprintf(str
, BUFLEN
, "%lu", id
);
2190 send_msg(ar
->outstr
, GTK_BUILDABLE(socket
), "id", str
, NULL
);
2192 ign_cmd(type
, whole_msg
);
2196 update_spin_button(GObject
*obj
, const char *action
, const char *data
,
2197 const char *whole_msg
, GType type
, struct info
*ar
)
2199 GtkSpinButton
*spinbutton
= GTK_SPIN_BUTTON(obj
);
2204 if (eql(action
, "set_text") && /* TODO: rename to "set_value" */
2205 sscanf(data
, "%lf %c", &val1
, &dummy
) == 1)
2206 gtk_spin_button_set_value(spinbutton
, val1
);
2207 else if (eql(action
, "set_range") &&
2208 sscanf(data
, "%lf %lf %c", &val1
, &val2
, &dummy
) == 2)
2209 gtk_spin_button_set_range(spinbutton
, val1
, val2
);
2210 else if (eql(action
, "set_increments") &&
2211 sscanf(data
, "%lf %lf %c", &val1
, &val2
, &dummy
) == 2)
2212 gtk_spin_button_set_increments(spinbutton
, val1
, val2
);
2214 ign_cmd(type
, whole_msg
);
2218 update_spinner(GObject
*obj
, const char *action
, const char *data
,
2219 const char *whole_msg
, GType type
, struct info
*ar
)
2221 GtkSpinner
*spinner
= GTK_SPINNER(obj
);
2226 if (eql(action
, "start") && sscanf(data
, " %c", &dummy
) < 1)
2227 gtk_spinner_start(spinner
);
2228 else if (eql(action
, "stop") && sscanf(data
, " %c", &dummy
) < 1)
2229 gtk_spinner_stop(spinner
);
2231 ign_cmd(type
, whole_msg
);
2235 update_statusbar(GObject
*obj
, const char *action
, const char *data
,
2236 const char *whole_msg
, GType type
, struct info
*ar
)
2238 GtkStatusbar
*statusbar
= GTK_STATUSBAR(obj
);
2239 char *ctx_msg
, dummy
;
2244 /* TODO: remove "push", "pop", "remove_all"; rename "push_id" to "push", etc. */
2245 if ((ctx_msg
= malloc(strlen(data
) + 1)) == NULL
)
2247 t
= sscanf(data
, "%s %n%c", ctx_msg
, &ctx_len
, &dummy
);
2248 msg
= data
+ ctx_len
;
2249 if (eql(action
, "push"))
2250 gtk_statusbar_push(statusbar
,
2251 gtk_statusbar_get_context_id(statusbar
, "0"),
2253 else if (eql(action
, "push_id") && t
>= 1)
2254 gtk_statusbar_push(statusbar
,
2255 gtk_statusbar_get_context_id(statusbar
, ctx_msg
),
2257 else if (eql(action
, "pop") && t
< 1)
2258 gtk_statusbar_pop(statusbar
,
2259 gtk_statusbar_get_context_id(statusbar
, "0"));
2260 else if (eql(action
, "pop_id") && t
== 1)
2261 gtk_statusbar_pop(statusbar
,
2262 gtk_statusbar_get_context_id(statusbar
, ctx_msg
));
2263 else if (eql(action
, "remove_all") && t
< 1)
2264 gtk_statusbar_remove_all(statusbar
,
2265 gtk_statusbar_get_context_id(statusbar
, "0"));
2266 else if (eql(action
, "remove_all_id") && t
== 1)
2267 gtk_statusbar_remove_all(statusbar
,
2268 gtk_statusbar_get_context_id(statusbar
, ctx_msg
));
2270 ign_cmd(type
, whole_msg
);
2275 update_switch(GObject
*obj
, const char *action
, const char *data
,
2276 const char *whole_msg
, GType type
, struct info
*ar
)
2282 if (eql(action
, "set_active") &&
2283 sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
2284 gtk_switch_set_active(GTK_SWITCH(obj
), val
);
2286 ign_cmd(type
, whole_msg
);
2290 update_text_view(GObject
*obj
, const char *action
, const char *data
,
2291 const char *whole_msg
, GType type
, struct info
*ar
)
2294 GtkTextView
*view
= GTK_TEXT_VIEW(obj
);
2295 GtkTextBuffer
*textbuf
= gtk_text_view_get_buffer(view
);
2301 if (eql(action
, "set_text"))
2302 gtk_text_buffer_set_text(textbuf
, data
, -1);
2303 else if (eql(action
, "delete") && sscanf(data
, " %c", &dummy
) < 1) {
2304 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
2305 gtk_text_buffer_delete(textbuf
, &a
, &b
);
2306 } else if (eql(action
, "insert_at_cursor"))
2307 gtk_text_buffer_insert_at_cursor(textbuf
, data
, -1);
2308 else if (eql(action
, "place_cursor") && eql(data
, "end")) {
2309 gtk_text_buffer_get_end_iter(textbuf
, &a
);
2310 gtk_text_buffer_place_cursor(textbuf
, &a
);
2311 } else if (eql(action
, "place_cursor") &&
2312 sscanf(data
, "%d %c", &val
, &dummy
) == 1) {
2313 gtk_text_buffer_get_iter_at_offset(textbuf
, &a
, val
);
2314 gtk_text_buffer_place_cursor(textbuf
, &a
);
2315 } else if (eql(action
, "place_cursor_at_line") &&
2316 sscanf(data
, "%d %c", &val
, &dummy
) == 1) {
2317 gtk_text_buffer_get_iter_at_line(textbuf
, &a
, val
);
2318 gtk_text_buffer_place_cursor(textbuf
, &a
);
2319 } else if (eql(action
, "scroll_to_cursor") &&
2320 sscanf(data
, " %c", &dummy
) < 1)
2321 gtk_text_view_scroll_to_mark(view
, gtk_text_buffer_get_insert(textbuf
),
2323 else if (eql(action
, "save") && data
!= NULL
&&
2324 (sv
= fopen(data
, "w")) != NULL
) {
2325 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
2326 send_msg(sv
, GTK_BUILDABLE(view
), "insert_at_cursor",
2327 gtk_text_buffer_get_text(textbuf
, &a
, &b
, TRUE
), NULL
);
2330 ign_cmd(type
, whole_msg
);
2334 update_toggle_button(GObject
*obj
, const char *action
, const char *data
,
2335 const char *whole_msg
, GType type
, struct info
*ar
)
2341 if (eql(action
, "set_label"))
2342 gtk_button_set_label(GTK_BUTTON(obj
), data
);
2343 else if (eql(action
, "set_active") &&
2344 sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
2345 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(obj
), val
);
2347 ign_cmd(type
, whole_msg
);
2351 update_tooltip_text(GObject
*obj
, const char *action
, const char *data
,
2352 const char *whole_msg
, GType type
, struct info
*ar
)
2358 gtk_widget_set_tooltip_text(GTK_WIDGET(obj
), data
);
2362 * update_tree_view(), which runs inside gtk_main(), needs a few
2367 * Check if s is a valid string representation of a GtkTreePath
2370 is_path_string(char *s
)
2373 strlen(s
) == strspn(s
, ":0123456789") &&
2374 strstr(s
, "::") == NULL
&&
2375 strcspn(s
, ":") > 0;
2379 tree_model_insert_before(GtkTreeModel
*model
, GtkTreeIter
*iter
,
2380 GtkTreeIter
*parent
, GtkTreeIter
*sibling
)
2382 if (GTK_IS_TREE_STORE(model
))
2383 gtk_tree_store_insert_before(GTK_TREE_STORE(model
),
2384 iter
, parent
, sibling
);
2385 else if (GTK_IS_LIST_STORE(model
))
2386 gtk_list_store_insert_before(GTK_LIST_STORE(model
),
2393 tree_model_insert_after(GtkTreeModel
*model
, GtkTreeIter
*iter
,
2394 GtkTreeIter
*parent
, GtkTreeIter
*sibling
)
2396 if (GTK_IS_TREE_STORE(model
))
2397 gtk_tree_store_insert_after(GTK_TREE_STORE(model
),
2398 iter
, parent
, sibling
);
2399 else if (GTK_IS_LIST_STORE(model
))
2400 gtk_list_store_insert_after(GTK_LIST_STORE(model
),
2407 tree_model_move_before(GtkTreeModel
*model
, GtkTreeIter
*iter
,
2408 GtkTreeIter
*position
)
2410 if (GTK_IS_TREE_STORE(model
))
2411 gtk_tree_store_move_before(GTK_TREE_STORE(model
), iter
, position
);
2412 else if (GTK_IS_LIST_STORE(model
))
2413 gtk_list_store_move_before(GTK_LIST_STORE(model
), iter
, position
);
2419 tree_model_remove(GtkTreeModel
*model
, GtkTreeIter
*iter
)
2421 if (GTK_IS_TREE_STORE(model
))
2422 gtk_tree_store_remove(GTK_TREE_STORE(model
), iter
);
2423 else if (GTK_IS_LIST_STORE(model
))
2424 gtk_list_store_remove(GTK_LIST_STORE(model
), iter
);
2430 tree_model_clear(GtkTreeModel
*model
)
2432 if (GTK_IS_TREE_STORE(model
))
2433 gtk_tree_store_clear(GTK_TREE_STORE(model
));
2434 else if (GTK_IS_LIST_STORE(model
))
2435 gtk_list_store_clear(GTK_LIST_STORE(model
));
2441 tree_model_set(GtkTreeModel
*model
, GtkTreeIter
*iter
, ...)
2446 if (GTK_IS_TREE_STORE(model
))
2447 gtk_tree_store_set_valist(GTK_TREE_STORE(model
), iter
, ap
);
2448 else if (GTK_IS_LIST_STORE(model
))
2449 gtk_list_store_set_valist(GTK_LIST_STORE(model
), iter
, ap
);
2456 * Create an empty row at path if it doesn't yet exist. Create older
2457 * siblings and parents as necessary.
2460 create_subtree(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
)
2462 GtkTreeIter iter_1
; /* iter's predecessor */
2463 GtkTreePath
*path_1
; /* path's predecessor */
2465 if (gtk_tree_path_get_depth(path
) > 0 &&
2466 gtk_tree_model_get_iter(model
, iter
, path
))
2468 path_1
= gtk_tree_path_copy(path
);
2469 if (gtk_tree_path_prev(path_1
)) { /* need an older sibling */
2470 create_subtree(model
, path_1
, iter
);
2472 tree_model_insert_after(model
, iter
, NULL
, &iter_1
);
2473 } else if (gtk_tree_path_up(path_1
)) { /* need a parent */
2474 create_subtree(model
, path_1
, iter
);
2475 if (gtk_tree_path_get_depth(path_1
) == 0)
2476 /* first toplevel row */
2477 tree_model_insert_after(model
, iter
, NULL
, NULL
);
2478 else { /* first row in a lower level */
2480 tree_model_insert_after(model
, iter
, &iter_1
, NULL
);
2482 } /* neither prev nor up mean we're at the root of an empty tree */
2483 gtk_tree_path_free(path_1
);
2487 set_tree_view_cell(GtkTreeModel
*model
, GtkTreeIter
*iter
,
2488 const char *path_s
, int col
, const char *new_text
)
2490 GType col_type
= gtk_tree_model_get_column_type(model
, col
);
2497 path
= gtk_tree_path_new_from_string(path_s
);
2499 case G_TYPE_BOOLEAN
:
2506 if (new_text
!= NULL
&&
2507 sscanf(new_text
, "%lld %c", &n
, &dummy
) == 1) {
2508 create_subtree(model
, path
, iter
);
2509 tree_model_set(model
, iter
, col
, n
, -1);
2515 if (new_text
!= NULL
&&
2516 sscanf(new_text
, "%lf %c", &d
, &dummy
) == 1) {
2517 create_subtree(model
, path
, iter
);
2518 tree_model_set(model
, iter
, col
, d
, -1);
2523 create_subtree(model
, path
, iter
);
2524 tree_model_set(model
, iter
, col
, new_text
, -1);
2528 fprintf(stderr
, "column %d: %s not implemented\n",
2529 col
, g_type_name(col_type
));
2533 gtk_tree_path_free(path
);
2538 tree_view_set_cursor(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*col
)
2540 /* GTK+ 3.14 requires this. For 3.18, path = NULL */
2541 /* is just fine and this function need not exist. */
2543 path
= gtk_tree_path_new();
2544 gtk_tree_view_set_cursor(view
, path
, col
, false);
2548 update_tree_view(GObject
*obj
, const char *action
, const char *data
,
2549 const char *whole_msg
, GType type
, struct info
*ar
)
2551 GtkTreeView
*view
= GTK_TREE_VIEW(obj
);
2552 GtkTreeIter iter0
, iter1
;
2553 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
2554 GtkTreePath
*path
= NULL
;
2555 bool iter0_valid
, iter1_valid
;
2556 char *tokens
, *arg0
, *arg1
, *arg2
;
2557 int col
= -1; /* invalid column number */
2561 if (!GTK_IS_LIST_STORE(model
) && !GTK_IS_TREE_STORE(model
))
2563 fprintf(stderr
, "missing model/");
2564 ign_cmd(type
, whole_msg
);
2567 if ((tokens
= malloc(strlen(data
) + 1)) == NULL
)
2569 strcpy(tokens
, data
);
2570 arg0
= strtok(tokens
, WHITESPACE
);
2571 arg1
= strtok(NULL
, WHITESPACE
);
2572 arg2
= strtok(NULL
, "");
2573 iter0_valid
= is_path_string(arg0
) &&
2574 gtk_tree_model_get_iter_from_string(model
, &iter0
, arg0
);
2575 iter1_valid
= is_path_string(arg1
) &&
2576 gtk_tree_model_get_iter_from_string(model
, &iter1
, arg1
);
2577 if (is_path_string(arg1
))
2578 col
= strtol(arg1
, NULL
, 10);
2579 if (eql(action
, "set") &&
2581 col
< gtk_tree_model_get_n_columns(model
) &&
2582 is_path_string(arg0
)) {
2583 if (set_tree_view_cell(model
, &iter0
, arg0
, col
, arg2
) == false)
2584 ign_cmd(type
, whole_msg
);
2585 } else if (eql(action
, "scroll") && iter0_valid
&& iter1_valid
&&
2587 path
= gtk_tree_path_new_from_string(arg0
);
2588 gtk_tree_view_scroll_to_cell (view
,
2590 gtk_tree_view_get_column(view
, col
),
2592 } else if (eql(action
, "expand") && iter0_valid
&& arg1
== NULL
) {
2593 path
= gtk_tree_path_new_from_string(arg0
);
2594 gtk_tree_view_expand_row(view
, path
, false);
2595 } else if (eql(action
, "expand_all") && iter0_valid
&& arg1
== NULL
) {
2596 path
= gtk_tree_path_new_from_string(arg0
);
2597 gtk_tree_view_expand_row(view
, path
, true);
2598 } else if (eql(action
, "expand_all") && arg0
== NULL
)
2599 gtk_tree_view_expand_all(view
);
2600 else if (eql(action
, "collapse") && iter0_valid
&& arg1
== NULL
) {
2601 path
= gtk_tree_path_new_from_string(arg0
);
2602 gtk_tree_view_collapse_row(view
, path
);
2603 } else if (eql(action
, "collapse") && arg0
== NULL
)
2604 gtk_tree_view_collapse_all(view
);
2605 else if (eql(action
, "set_cursor") && iter0_valid
&& arg1
== NULL
) {
2606 path
= gtk_tree_path_new_from_string(arg0
);
2607 tree_view_set_cursor(view
, path
, NULL
);
2608 } else if (eql(action
, "set_cursor") && arg0
== NULL
) {
2609 tree_view_set_cursor(view
, NULL
, NULL
);
2610 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(view
));
2611 } else if (eql(action
, "insert_row") &&
2612 eql(arg0
, "end") && arg1
== NULL
)
2613 tree_model_insert_before(model
, &iter1
, NULL
, NULL
);
2614 else if (eql(action
, "insert_row") && iter0_valid
&&
2615 eql(arg1
, "as_child") && arg2
== NULL
)
2616 tree_model_insert_after(model
, &iter1
, &iter0
, NULL
);
2617 else if (eql(action
, "insert_row") && iter0_valid
&& arg1
== NULL
)
2618 tree_model_insert_before(model
, &iter1
, NULL
, &iter0
);
2619 else if (eql(action
, "move_row") && iter0_valid
&&
2620 eql(arg1
, "end") && arg2
== NULL
)
2621 tree_model_move_before(model
, &iter0
, NULL
);
2622 else if (eql(action
, "move_row") && iter0_valid
&& iter1_valid
&& arg2
== NULL
)
2623 tree_model_move_before(model
, &iter0
, &iter1
);
2624 else if (eql(action
, "remove_row") && iter0_valid
&& arg1
== NULL
)
2625 tree_model_remove(model
, &iter0
);
2626 else if (eql(action
, "clear") && arg0
== NULL
) {
2627 tree_view_set_cursor(view
, NULL
, NULL
);
2628 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(view
));
2629 tree_model_clear(model
);
2630 } else if (eql(action
, "save") && arg0
!= NULL
&&
2631 (args
.outstr
= fopen(arg0
, "w")) != NULL
) {
2633 gtk_tree_model_foreach(model
,
2634 (GtkTreeModelForeachFunc
) save_tree_row_msg
,
2636 fclose(args
.outstr
);
2638 ign_cmd(type
, whole_msg
);
2640 gtk_tree_path_free(path
);
2644 update_visibility(GObject
*obj
, const char *action
, const char *data
,
2645 const char *whole_msg
, GType type
, struct info
*ar
)
2654 if (sscanf(data
, "%u %c", &val
, &dummy
) == 1 && val
< 2)
2655 gtk_widget_set_visible(GTK_WIDGET(obj
), val
);
2657 ign_cmd(type
, whole_msg
);
2661 * Change the style of the widget passed. Runs inside gtk_main().
2664 update_widget_style(GObject
*obj
, const char *name
, const char *data
,
2665 const char *whole_msg
, GType type
, struct info
*ar
)
2667 GtkStyleContext
*context
;
2668 GtkStyleProvider
*style_provider
;
2670 const char *prefix
= "* {", *suffix
= "}";
2677 style_provider
= g_object_get_data(obj
, "style_provider");
2678 sz
= strlen(prefix
) + strlen(suffix
) + strlen(data
) + 1;
2679 context
= gtk_widget_get_style_context(GTK_WIDGET(obj
));
2680 gtk_style_context_remove_provider(context
, style_provider
);
2681 if ((style_decl
= malloc(sz
)) == NULL
)
2683 strcpy(style_decl
, prefix
);
2684 strcat(style_decl
, data
);
2685 strcat(style_decl
, suffix
);
2686 gtk_style_context_add_provider(context
, style_provider
,
2687 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
2688 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(style_provider
),
2689 style_decl
, -1, NULL
);
2694 update_window(GObject
*obj
, const char *action
, const char *data
,
2695 const char *whole_msg
, GType type
, struct info
*ar
)
2698 if (!update_class_window(obj
, action
, data
, whole_msg
, type
, NULL
))
2699 ign_cmd(type
, whole_msg
);
2703 * Simulate user activity on various widgets. Runs inside gtk_main().
2706 fake_ui_activity(GObject
*obj
, const char *action
, const char *data
,
2707 const char *whole_msg
, GType type
, struct info
*ar
)
2713 if (!GTK_IS_WIDGET(obj
) || sscanf(data
, " %c", &dummy
) > 0)
2714 ign_cmd(type
, whole_msg
);
2715 else if (GTK_IS_SPIN_BUTTON(obj
)) {
2717 cb_spin_button(GTK_BUILDABLE(obj
), ar
); /* TODO: rename to "value" */
2718 } else if (GTK_IS_SCALE(obj
)) {
2720 cb_range(GTK_BUILDABLE(obj
), ar
);
2721 } else if (GTK_IS_ENTRY(obj
)) {
2723 cb_editable(GTK_BUILDABLE(obj
), ar
);
2724 } else if (GTK_IS_CALENDAR(obj
)) {
2725 ar
->txt
= "clicked";
2726 cb_calendar(GTK_BUILDABLE(obj
), ar
);
2727 } else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
)) {
2729 cb_file_chooser_button(GTK_BUILDABLE(obj
), ar
);
2730 } else if (!gtk_widget_activate(GTK_WIDGET(obj
)))
2731 ign_cmd(type
, whole_msg
);
2735 * The final UI update. Runs inside gtk_main().
2738 main_quit(GObject
*obj
, const char *action
, const char *data
,
2739 const char *whole_msg
, GType type
, struct info
*ar
)
2749 if (sscanf(data
, " %c", &dummy
) < 1)
2752 ign_cmd(type
, whole_msg
);
2756 * Don't update anything; just complain from inside gtk_main()
2759 complain(GObject
*obj
, const char *action
, const char *data
,
2760 const char *whole_msg
, GType type
, struct info
*ar
)
2766 ign_cmd(type
, whole_msg
);
2770 * Data to be passed to and from the GTK main loop
2773 void (*fn
)(GObject
*, const char *action
, const char *data
,
2774 const char *msg
, GType type
, struct info
*args
);
2785 * Parse command pointed to by ud, and act on ui accordingly. Runs
2786 * once per command inside gtk_main().
2789 update_ui(struct ui_data
*ud
)
2791 char *lc
= lc_numeric();
2793 (ud
->fn
)(ud
->obj
, ud
->action
, ud
->data
, ud
->msg
, ud
->type
, ud
->args
);
2794 free(ud
->msg_tokens
);
2797 lc_numeric_free(lc
);
2798 return G_SOURCE_REMOVE
;
2802 * Keep track of loading files to avoid recursive loading of the same
2803 * file. If filename = NULL, forget the most recently remembered file.
2806 remember_loading_file(char *filename
)
2808 static char *filenames
[BUFLEN
];
2809 static size_t latest
= 0;
2812 if (filename
== NULL
) { /* pop */
2818 for (i
= 1; i
<= latest
; i
++)
2819 if (eql(filename
, filenames
[i
]))
2821 if (latest
> BUFLEN
-2)
2823 filenames
[++latest
] = filename
;
2829 * Read lines from stream cmd and perform appropriate actions on the
2830 * GUI. Runs inside receiver thread.
2833 digest_msg(struct info
*args
)
2835 static int recursion
= -1; /* > 0 means this is a recursive call */
2839 FILE *cmd
= args
->instr
;
2840 struct ui_data
*ud
= NULL
;
2841 char first_char
= '\0', *name
;
2842 size_t msg_size
= 32;
2843 int name_start
= 0, name_end
= 0;
2844 int action_start
= 0, action_end
= 0;
2849 if ((ud
= malloc(sizeof(*ud
))) == NULL
)
2851 if ((ud
->msg
= malloc(msg_size
)) == NULL
)
2854 ud
->type
= G_TYPE_INVALID
;
2855 pthread_testcancel();
2857 log_msg(args
->logstr
, NULL
);
2858 data_start
= read_buf(cmd
, &ud
->msg
, &msg_size
);
2860 log_msg(args
->logstr
, ud
->msg
);
2861 if ((ud
->msg_tokens
= malloc(strlen(ud
->msg
) + 1)) == NULL
)
2863 strcpy(ud
->msg_tokens
, ud
->msg
);
2864 sscanf(ud
->msg
, " %c", &first_char
);
2865 if (strlen(ud
->msg
) == 0 || first_char
== '#') { /* comment */
2866 ud
->fn
= update_nothing
;
2869 sscanf(ud
->msg_tokens
,
2870 " %n%*[0-9a-zA-Z_]%n:%n%*[0-9a-zA-Z_]%n%*1[ \t]%n",
2871 &name_start
, &name_end
, &action_start
, &action_end
, &data_start
);
2872 ud
->msg_tokens
[name_end
] = ud
->msg_tokens
[action_end
] = '\0';
2873 name
= ud
->msg_tokens
+ name_start
;
2874 ud
->action
= ud
->msg_tokens
+ action_start
;
2875 ud
->data
= ud
->msg_tokens
+ data_start
;
2876 if (eql(ud
->action
, "main_quit")) {
2880 if (eql(ud
->action
, "load") && strlen(ud
->data
) > 0 &&
2881 remember_loading_file(ud
->data
)) {
2882 struct info a
= *args
;
2884 if ((a
.instr
= fopen(ud
->data
, "r")) != NULL
) {
2887 ud
->fn
= update_nothing
;
2890 remember_loading_file(NULL
);
2893 if ((ud
->obj
= (gtk_builder_get_object(args
->builder
, name
))) == NULL
) {
2897 ud
->type
= G_TYPE_FROM_INSTANCE(ud
->obj
);
2898 if (eql(ud
->action
, "force"))
2899 ud
->fn
= fake_ui_activity
;
2900 else if (eql(ud
->action
, "set_sensitive"))
2901 ud
->fn
= update_sensitivity
;
2902 else if (eql(ud
->action
, "set_visible"))
2903 ud
->fn
= update_visibility
;
2904 else if (eql(ud
->action
, "set_size_request"))
2905 ud
->fn
= update_size_request
;
2906 else if (eql(ud
->action
, "set_tooltip_text"))
2907 ud
->fn
= update_tooltip_text
;
2908 else if (eql(ud
->action
, "grab_focus"))
2909 ud
->fn
= update_focus
;
2910 else if (eql(ud
->action
, "style")) {
2912 ud
->fn
= update_widget_style
;
2913 } else if (ud
->type
== GTK_TYPE_DRAWING_AREA
)
2914 ud
->fn
= update_drawing_area
;
2915 else if (ud
->type
== GTK_TYPE_TREE_VIEW
)
2916 ud
->fn
= update_tree_view
;
2917 else if (ud
->type
== GTK_TYPE_COMBO_BOX_TEXT
)
2918 ud
->fn
= update_combo_box_text
;
2919 else if (ud
->type
== GTK_TYPE_LABEL
)
2920 ud
->fn
= update_label
;
2921 else if (ud
->type
== GTK_TYPE_IMAGE
)
2922 ud
->fn
= update_image
;
2923 else if (ud
->type
== GTK_TYPE_TEXT_VIEW
)
2924 ud
->fn
= update_text_view
;
2925 else if (ud
->type
== GTK_TYPE_NOTEBOOK
)
2926 ud
->fn
= update_notebook
;
2927 else if (ud
->type
== GTK_TYPE_EXPANDER
)
2928 ud
->fn
= update_expander
;
2929 else if (ud
->type
== GTK_TYPE_FRAME
)
2930 ud
->fn
= update_frame
;
2931 else if (ud
->type
== GTK_TYPE_SCROLLED_WINDOW
)
2932 ud
->fn
= update_scrolled_window
;
2933 else if (ud
->type
== GTK_TYPE_BUTTON
)
2934 ud
->fn
= update_button
;
2935 else if (ud
->type
== GTK_TYPE_FILE_CHOOSER_DIALOG
)
2936 ud
->fn
= update_file_chooser_dialog
;
2937 else if (ud
->type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
2938 ud
->fn
= update_file_chooser_button
;
2939 else if (ud
->type
== GTK_TYPE_COLOR_BUTTON
)
2940 ud
->fn
= update_color_button
;
2941 else if (ud
->type
== GTK_TYPE_FONT_BUTTON
)
2942 ud
->fn
= update_font_button
;
2943 else if (ud
->type
== GTK_TYPE_PRINT_UNIX_DIALOG
)
2944 ud
->fn
= update_print_dialog
;
2945 else if (ud
->type
== GTK_TYPE_SWITCH
)
2946 ud
->fn
= update_switch
;
2947 else if (ud
->type
== GTK_TYPE_TOGGLE_BUTTON
||
2948 ud
->type
== GTK_TYPE_RADIO_BUTTON
||
2949 ud
->type
== GTK_TYPE_CHECK_BUTTON
)
2950 ud
->fn
= update_toggle_button
;
2951 else if (ud
->type
== GTK_TYPE_ENTRY
)
2952 ud
->fn
= update_entry
;
2953 else if (ud
->type
== GTK_TYPE_SPIN_BUTTON
)
2954 ud
->fn
= update_spin_button
;
2955 else if (ud
->type
== GTK_TYPE_SCALE
)
2956 ud
->fn
= update_scale
;
2957 else if (ud
->type
== GTK_TYPE_PROGRESS_BAR
)
2958 ud
->fn
= update_progress_bar
;
2959 else if (ud
->type
== GTK_TYPE_SPINNER
)
2960 ud
->fn
= update_spinner
;
2961 else if (ud
->type
== GTK_TYPE_STATUSBAR
)
2962 ud
->fn
= update_statusbar
;
2963 else if (ud
->type
== GTK_TYPE_CALENDAR
)
2964 ud
->fn
= update_calendar
;
2965 else if (ud
->type
== GTK_TYPE_SOCKET
)
2966 ud
->fn
= update_socket
;
2967 else if (ud
->type
== GTK_TYPE_WINDOW
||
2968 ud
->type
== GTK_TYPE_DIALOG
)
2969 ud
->fn
= update_window
;
2973 pthread_testcancel();
2974 gdk_threads_add_timeout(0, (GSourceFunc
) update_ui
, ud
);
2982 * ============================================================
2984 * ============================================================
2988 * Attach to renderer key "col_number". Associate "col_number" with
2989 * the corresponding column number in the underlying model.
2990 * Due to what looks like a gap in the GTK API, renderer id and column
2991 * number are taken directly from the XML .ui file.
2994 tree_view_column_get_renderer_column(GtkBuilder
*builder
, const char *ui_file
,
2995 GtkTreeViewColumn
*t_col
, int n
,
2996 GtkCellRenderer
**renderer
)
2999 char *xpath_base1
= "//object[@class=\"GtkTreeViewColumn\" and @id=\"";
3000 char *xpath_base2
= "\"]/child[";
3001 char *xpath_base3
= "]/object[@class=\"GtkCellRendererText\""
3002 " or @class=\"GtkCellRendererToggle\"]/";
3003 char *xpath_renderer_id
= "/@id";
3004 char *xpath_text_col
= "../attributes/attribute[@name=\"text\""
3005 " or @name=\"active\"]";
3006 const char *xpath_id
= widget_name(GTK_BUILDABLE(t_col
));
3009 size_t xpath_n_len
= 3; /* Big Enough (TM) */
3010 xmlChar
*xpath
, *renderer_name
= NULL
, *m_col_s
= NULL
;
3013 xmlNodeSetPtr nodes
;
3014 xmlXPathContextPtr xpath_ctx
;
3015 xmlXPathObjectPtr xpath_obj
;
3017 if ((doc
= xmlParseFile(ui_file
)) == NULL
)
3019 if ((xpath_ctx
= xmlXPathNewContext(doc
)) == NULL
) {
3023 xpath_len
= 2 * (strlen(xpath_base1
) + strlen(xpath_id
) +
3024 strlen(xpath_base2
) + xpath_n_len
+
3025 strlen(xpath_base3
)) +
3027 strlen(xpath_text_col
) + strlen(xpath_renderer_id
) +
3029 if ((xpath
= malloc(xpath_len
)) == NULL
)
3031 snprintf((char *) xpath
, xpath_len
, "%s%s%s%d%s%s|%s%s%s%d%s%s",
3032 xpath_base1
, xpath_id
, xpath_base2
, n
, xpath_base3
, xpath_text_col
,
3033 xpath_base1
, xpath_id
, xpath_base2
, n
, xpath_base3
, xpath_renderer_id
);
3034 if ((xpath_obj
= xmlXPathEvalExpression(xpath
, xpath_ctx
)) == NULL
) {
3035 xmlXPathFreeContext(xpath_ctx
);
3040 if ((nodes
= xpath_obj
->nodesetval
) != NULL
) {
3041 for (i
= 0; i
< nodes
->nodeNr
; ++i
) {
3042 if (nodes
->nodeTab
[i
]->type
== XML_ELEMENT_NODE
) {
3043 cur
= nodes
->nodeTab
[i
];
3044 m_col_s
= xmlNodeGetContent(cur
);
3046 cur
= nodes
->nodeTab
[i
];
3047 renderer_name
= xmlNodeGetContent(cur
);
3051 if (renderer_name
) {
3052 *renderer
= GTK_CELL_RENDERER(
3053 gtk_builder_get_object(builder
, (char *) renderer_name
));
3055 g_object_set_data(G_OBJECT(*renderer
), "col_number",
3056 GINT_TO_POINTER(strtol((char *) m_col_s
,
3061 xmlFree(renderer_name
);
3063 xmlXPathFreeObject(xpath_obj
);
3064 xmlXPathFreeContext(xpath_ctx
);
3071 * Callbacks that forward a modification of a tree view cell to the
3075 cb_tree_model_edit(GtkCellRenderer
*renderer
, const gchar
*path_s
,
3076 const gchar
*new_text
, struct info
*args
)
3079 int col
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(renderer
),
3082 gtk_tree_model_get_iter_from_string(args
->model
, &iter
, path_s
);
3083 set_tree_view_cell(args
->model
, &iter
, path_s
, col
,
3085 send_tree_cell_msg_by(send_msg
, path_s
, &iter
, col
, args
);
3089 cb_tree_model_toggle(GtkCellRenderer
*renderer
, gchar
*path_s
, struct info
*args
)
3093 int col
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(renderer
),
3096 gtk_tree_model_get_iter_from_string(args
->model
, &iter
, path_s
);
3097 gtk_tree_model_get(args
->model
, &iter
, col
, &toggle_state
, -1);
3098 set_tree_view_cell(args
->model
, &iter
, path_s
, col
,
3099 toggle_state
? "0" : "1");
3103 connect_widget_signals(gpointer
*obj
, struct info
*ar
)
3106 GType type
= G_TYPE_INVALID
;
3107 char *suffix
= NULL
;
3108 const char *name
= NULL
;
3109 FILE *o
= ar
->outstr
;
3111 type
= G_TYPE_FROM_INSTANCE(obj
);
3112 if (GTK_IS_BUILDABLE(obj
))
3113 name
= widget_name(GTK_BUILDABLE(obj
));
3114 if (type
== GTK_TYPE_TREE_VIEW_COLUMN
) {
3115 GList
*cells
= gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(obj
));
3116 GtkTreeViewColumn
*tv_col
= GTK_TREE_VIEW_COLUMN(obj
);
3117 GObject
*view
= G_OBJECT(
3118 gtk_tree_view_column_get_tree_view(tv_col
));
3119 unsigned int i
, n_cells
= g_list_length(cells
);
3122 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_simple
), info_txt_new(o
, "clicked"));
3123 for (i
= 1; i
<= n_cells
; i
++) {
3124 GtkCellRenderer
*renderer
;
3125 gboolean editable
= FALSE
;
3127 if (!tree_view_column_get_renderer_column(ar
->builder
, ar
->txt
, tv_col
,
3130 if (GTK_IS_CELL_RENDERER_TEXT(renderer
)) {
3131 g_object_get(renderer
, "editable", &editable
, NULL
);
3133 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(view
));
3135 g_signal_connect(renderer
, "edited",
3136 G_CALLBACK(cb_tree_model_edit
),
3137 info_obj_new(o
, view
, model
));
3139 } else if (GTK_IS_CELL_RENDERER_TOGGLE(renderer
)) {
3140 g_object_get(renderer
, "activatable", &editable
, NULL
);
3142 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(view
));
3144 g_signal_connect(renderer
, "toggled",
3145 G_CALLBACK(cb_tree_model_toggle
),
3146 info_obj_new(o
, NULL
, model
));
3150 } else if (type
== GTK_TYPE_BUTTON
)
3151 /* Button associated with a GtkTextView. */
3152 if ((suffix
= strstr(name
, "_send_text")) != NULL
&&
3153 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(ar
->builder
, suffix
, name
)))
3154 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text
),
3155 info_obj_new(o
, G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
))), NULL
));
3156 else if ((suffix
= strstr(name
, "_send_selection")) != NULL
&&
3157 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(ar
->builder
, suffix
, name
)))
3158 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text_selection
),
3159 info_obj_new(o
, G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
))), NULL
));
3161 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_simple
), info_txt_new(o
, "clicked"));
3162 /* Buttons associated with (and part of) a GtkDialog.
3163 * (We shun response ids which could be returned from
3164 * gtk_dialog_run() because that would require the
3165 * user to define those response ids in Glade,
3167 if ((suffix
= strstr(name
, "_cancel")) != NULL
&&
3168 GTK_IS_DIALOG(obj2
= obj_sans_suffix(ar
->builder
, suffix
, name
)))
3169 if (eql(widget_name(GTK_BUILDABLE(obj2
)), MAIN_WIN
))
3170 g_signal_connect_swapped(obj
, "clicked",
3171 G_CALLBACK(gtk_main_quit
), NULL
);
3173 g_signal_connect_swapped(obj
, "clicked",
3174 G_CALLBACK(gtk_widget_hide
), obj2
);
3175 else if ((suffix
= strstr(name
, "_ok")) != NULL
&&
3176 GTK_IS_DIALOG(obj2
= obj_sans_suffix(ar
->builder
, suffix
, name
))) {
3177 if (GTK_IS_FILE_CHOOSER_DIALOG(obj2
))
3178 g_signal_connect_swapped(obj
, "clicked",
3179 G_CALLBACK(cb_send_file_chooser_dialog_selection
),
3180 info_obj_new(o
, obj2
, NULL
));
3181 if (eql(widget_name(GTK_BUILDABLE(obj2
)), MAIN_WIN
))
3182 g_signal_connect_swapped(obj
, "clicked",
3183 G_CALLBACK(gtk_main_quit
), NULL
);
3185 g_signal_connect_swapped(obj
, "clicked",
3186 G_CALLBACK(gtk_widget_hide
), obj2
);
3187 } else if ((suffix
= strstr(name
, "_apply")) != NULL
&&
3188 GTK_IS_FILE_CHOOSER_DIALOG(obj2
= obj_sans_suffix(ar
->builder
, suffix
, name
)))
3189 g_signal_connect_swapped(obj
, "clicked",
3190 G_CALLBACK(cb_send_file_chooser_dialog_selection
),
3191 info_obj_new(o
, obj2
, NULL
));
3193 else if (GTK_IS_MENU_ITEM(obj
))
3194 if ((suffix
= strstr(name
, "_invoke")) != NULL
&&
3195 GTK_IS_DIALOG(obj2
= obj_sans_suffix(ar
->builder
, suffix
, name
)))
3196 g_signal_connect_swapped(obj
, "activate",
3197 G_CALLBACK(gtk_widget_show
), obj2
);
3199 g_signal_connect(obj
, "activate",
3200 G_CALLBACK(cb_menu_item
), info_txt_new(o
, "active"));
3201 else if (GTK_IS_WINDOW(obj
)) {
3202 g_signal_connect(obj
, "delete-event",
3203 G_CALLBACK(cb_event_simple
), info_txt_new(o
, "closed"));
3204 if (eql(name
, MAIN_WIN
))
3205 g_signal_connect_swapped(obj
, "delete-event",
3206 G_CALLBACK(gtk_main_quit
), NULL
);
3208 g_signal_connect(obj
, "delete-event",
3209 G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
3210 } else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
3211 g_signal_connect(obj
, "file-set",
3212 G_CALLBACK(cb_file_chooser_button
), info_txt_new(o
, "file"));
3213 else if (type
== GTK_TYPE_COLOR_BUTTON
)
3214 g_signal_connect(obj
, "color-set",
3215 G_CALLBACK(cb_color_button
), info_txt_new(o
, "color"));
3216 else if (type
== GTK_TYPE_FONT_BUTTON
)
3217 g_signal_connect(obj
, "font-set",
3218 G_CALLBACK(cb_font_button
), info_txt_new(o
, "font"));
3219 else if (type
== GTK_TYPE_SWITCH
)
3220 g_signal_connect(obj
, "notify::active",
3221 G_CALLBACK(cb_switch
), info_txt_new(o
, NULL
));
3222 else if (type
== GTK_TYPE_TOGGLE_BUTTON
||
3223 type
== GTK_TYPE_RADIO_BUTTON
||
3224 type
== GTK_TYPE_CHECK_BUTTON
)
3225 g_signal_connect(obj
, "toggled",
3226 G_CALLBACK(cb_toggle_button
), info_txt_new(o
, NULL
));
3227 else if (type
== GTK_TYPE_ENTRY
)
3228 g_signal_connect(obj
, "changed",
3229 G_CALLBACK(cb_editable
), info_txt_new(o
, "text"));
3230 else if (type
== GTK_TYPE_SPIN_BUTTON
)
3231 g_signal_connect(obj
, "value_changed",
3232 G_CALLBACK(cb_spin_button
), info_txt_new(o
, "text")); /* TODO: rename to "value" */
3233 else if (type
== GTK_TYPE_SCALE
)
3234 g_signal_connect(obj
, "value-changed",
3235 G_CALLBACK(cb_range
), info_txt_new(o
, "value"));
3236 else if (type
== GTK_TYPE_CALENDAR
) {
3237 g_signal_connect(obj
, "day-selected-double-click",
3238 G_CALLBACK(cb_calendar
), info_txt_new(o
, "doubleclicked"));
3239 g_signal_connect(obj
, "day-selected",
3240 G_CALLBACK(cb_calendar
), info_txt_new(o
, "clicked"));
3241 } else if (type
== GTK_TYPE_TREE_SELECTION
)
3242 g_signal_connect(obj
, "changed",
3243 G_CALLBACK(cb_tree_selection
), info_txt_new(o
, "clicked"));
3244 else if (type
== GTK_TYPE_SOCKET
) {
3245 g_signal_connect(obj
, "plug-added",
3246 G_CALLBACK(cb_simple
), info_txt_new(o
, "plug-added"));
3247 g_signal_connect(obj
, "plug-removed",
3248 G_CALLBACK(cb_simple
), info_txt_new(o
, "plug-removed"));
3249 /* TODO: rename to plug_added, plug_removed */
3250 } else if (type
== GTK_TYPE_DRAWING_AREA
)
3251 g_signal_connect(obj
, "draw", G_CALLBACK(cb_draw
), NULL
);
3252 else if (type
== GTK_TYPE_EVENT_BOX
) {
3253 gtk_widget_set_can_focus(GTK_WIDGET(obj
), true);
3254 g_signal_connect(obj
, "button-press-event",
3255 G_CALLBACK(cb_event_box_button
),
3256 info_txt_new(o
, "button_press"));
3257 g_signal_connect(obj
, "button-release-event",
3258 G_CALLBACK(cb_event_box_button
),
3259 info_txt_new(o
, "button_release"));
3260 g_signal_connect(obj
, "motion-notify-event",
3261 G_CALLBACK(cb_event_box_motion
),
3262 info_txt_new(o
, "motion"));
3263 g_signal_connect(obj
, "key-press-event",
3264 G_CALLBACK(cb_event_box_key
),
3265 info_txt_new(o
, "key_press"));
3270 * We keep a style provider with each widget
3273 add_widget_style_provider(gpointer
*obj
, void *data
)
3275 GtkCssProvider
*style_provider
;
3276 GtkStyleContext
*context
;
3279 if (!GTK_IS_WIDGET(obj
))
3281 style_provider
= gtk_css_provider_new();
3282 context
= gtk_widget_get_style_context(GTK_WIDGET(obj
));
3283 gtk_style_context_add_provider(context
,
3284 GTK_STYLE_PROVIDER(style_provider
),
3285 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
3286 g_object_set_data(G_OBJECT(obj
), "style_provider", style_provider
);
3290 prepare_widgets(GtkBuilder
*builder
, char *ui_file
, FILE *out
)
3292 GSList
*objects
= NULL
;
3293 struct info args
= {.builder
= builder
, .outstr
= out
, .txt
= ui_file
};
3295 objects
= gtk_builder_get_objects(builder
);
3296 g_slist_foreach(objects
, (GFunc
) connect_widget_signals
, &args
);
3297 g_slist_foreach(objects
, (GFunc
) add_widget_style_provider
, NULL
);
3298 g_slist_free(objects
);
3302 main(int argc
, char *argv
[])
3304 GObject
*main_window
= NULL
;
3306 char *in_fifo
= NULL
, *out_fifo
= NULL
;
3307 char *ui_file
= "pipeglade.ui", *log_file
= NULL
, *err_file
= NULL
;
3313 /* Disable runtime GLIB deprecation warnings: */
3314 setenv("G_ENABLE_DIAGNOSTIC", "0", 0);
3315 gtk_init(&argc
, &argv
);
3316 while ((opt
= getopt(argc
, argv
, "bGhe:i:l:o:O:u:V")) != -1) {
3318 case 'b': bg
= true; break;
3319 case 'e': xid
= optarg
; break;
3320 case 'G': show_lib_versions(); break;
3321 case 'h': bye(EXIT_SUCCESS
, stdout
, USAGE
); break;
3322 case 'i': in_fifo
= optarg
; break;
3323 case 'l': log_file
= optarg
; break;
3324 case 'o': out_fifo
= optarg
; break;
3325 case 'O': err_file
= optarg
; break;
3326 case 'u': ui_file
= optarg
; break;
3327 case 'V': bye(EXIT_SUCCESS
, stdout
, "%s\n", VERSION
); break;
3329 default: bye(EXIT_FAILURE
, stderr
, USAGE
); break;
3332 if (argv
[optind
] != NULL
)
3333 bye(EXIT_FAILURE
, stderr
,
3334 "illegal parameter '%s'\n" USAGE
, argv
[optind
]);
3335 redirect_stderr(err_file
);
3336 ar
.instr
= open_fifo(in_fifo
, "r", stdin
, _IONBF
);
3337 ar
.outstr
= open_fifo(out_fifo
, "w", stdout
, _IOLBF
);
3338 go_bg_if(bg
, ar
.instr
, ar
.outstr
, err_file
);
3339 ar
.builder
= builder_from_file(ui_file
);
3340 ar
.logstr
= open_log(log_file
);
3341 pthread_create(&receiver
, NULL
, (void *(*)(void *)) digest_msg
, &ar
);
3342 main_window
= find_main_window(ar
.builder
);
3344 LIBXML_TEST_VERSION
;
3345 prepare_widgets(ar
.builder
, ui_file
, ar
.outstr
);
3346 xembed_if(xid
, main_window
);
3348 rm_unless(stdin
, ar
.instr
, in_fifo
);
3349 rm_unless(stdout
, ar
.outstr
, out_fifo
);
3350 pthread_cancel(receiver
);
3351 pthread_join(receiver
, NULL
);