1#include <config.h>
2#include <math.h>
3#include <pango/pangocairo.h>
4#include <glib/gi18n.h>
5#include <gtk/gtk.h>
6
7#include "demo_conf.h"
8
9static GtkWidget *main_window;
10static GFile *filename = NULL;
11static GtkPageSetup *page_setup = NULL;
12static GtkPrintSettings *settings = NULL;
13static gboolean file_changed = FALSE;
14static GtkTextBuffer *buffer;
15static GtkWidget *statusbar;
16static GList *active_prints = NULL;
17
18static void
19update_title (GtkWindow *window)
20{
21 char *basename;
22 char *title;
23
24 if (filename == NULL)
25 basename = g_strdup (str: "Untitled");
26 else
27 basename = g_file_get_basename (file: filename);
28
29 title = g_strdup_printf (format: "GTK Print Editor — %s", basename);
30 g_free (mem: basename);
31
32 gtk_window_set_title (window, title);
33 g_free (mem: title);
34}
35
36static void
37update_statusbar (void)
38{
39 char *msg;
40 int row, col;
41 GtkTextIter iter;
42 const char *print_str;
43
44 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), context_id: 0);
45
46 gtk_text_buffer_get_iter_at_mark (buffer,
47 iter: &iter,
48 mark: gtk_text_buffer_get_insert (buffer));
49
50 row = gtk_text_iter_get_line (iter: &iter);
51 col = gtk_text_iter_get_line_offset (iter: &iter);
52
53 print_str = "";
54 if (active_prints)
55 {
56 GtkPrintOperation *op = active_prints->data;
57 print_str = gtk_print_operation_get_status_string (op);
58 }
59
60 msg = g_strdup_printf (format: "%d, %d%s %s",
61 row, col,
62 file_changed?" - Modified":"",
63 print_str);
64
65 gtk_statusbar_push (GTK_STATUSBAR (statusbar), context_id: 0, text: msg);
66
67 g_free (mem: msg);
68}
69
70static void
71update_ui (void)
72{
73 update_title (GTK_WINDOW (main_window));
74 update_statusbar ();
75}
76
77static char *
78get_text (void)
79{
80 GtkTextIter start, end;
81
82 gtk_text_buffer_get_start_iter (buffer, iter: &start);
83 gtk_text_buffer_get_end_iter (buffer, iter: &end);
84 return gtk_text_buffer_get_text (buffer, start: &start, end: &end, FALSE);
85}
86
87static void
88set_text (const char *text,
89 gsize len)
90{
91 gtk_text_buffer_set_text (buffer, text, len);
92 file_changed = FALSE;
93 update_ui ();
94}
95
96static void
97load_file (GFile *open_filename)
98{
99 GtkWidget *error_dialog;
100 char *contents;
101 GError *error;
102 gsize len;
103
104 error_dialog = NULL;
105 error = NULL;
106 g_file_load_contents (file: open_filename, NULL, contents: &contents, length: &len, NULL, error: &error);
107 if (error == NULL)
108 {
109 if (g_utf8_validate (str: contents, max_len: len, NULL))
110 {
111 g_clear_object (&filename);
112 filename = g_object_ref (open_filename);
113 set_text (text: contents, len);
114 g_free (mem: contents);
115 }
116 else
117 {
118 GFileInfo *info = g_file_query_info (file: open_filename, attributes: "standard::display-name", flags: 0, NULL, error: &error);
119 const char *display_name = g_file_info_get_display_name (info);
120 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
121 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
122 type: GTK_MESSAGE_ERROR,
123 buttons: GTK_BUTTONS_CLOSE,
124 message_format: "Error loading file %s:\n%s",
125 display_name,
126 "Not valid utf8");
127 g_object_unref (object: info);
128 }
129 }
130 else
131 {
132 GFileInfo *info = g_file_query_info (file: open_filename, attributes: "standard::display-name", flags: 0, NULL, error: &error);
133 const char *display_name = g_file_info_get_display_name (info);
134 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
135 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
136 type: GTK_MESSAGE_ERROR,
137 buttons: GTK_BUTTONS_CLOSE,
138 message_format: "Error loading file %s:\n%s",
139 display_name,
140 error->message);
141 g_object_unref (object: info);
142 g_error_free (error);
143 }
144
145 if (error_dialog)
146 {
147 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
148 gtk_widget_show (widget: error_dialog);
149 }
150}
151
152
153static void
154save_file (GFile *save_filename)
155{
156 char *text = get_text ();
157 GtkWidget *error_dialog;
158 GError *error;
159
160 error = NULL;
161 g_file_replace_contents (file: save_filename,
162 contents: text, length: strlen (s: text),
163 NULL, FALSE,
164 flags: G_FILE_CREATE_NONE,
165 NULL,
166 NULL,
167 error: &error);
168
169 if (error == NULL)
170 {
171 if (save_filename != filename)
172 {
173 g_clear_object (&filename);
174 filename = g_object_ref (save_filename);
175 }
176 file_changed = FALSE;
177 update_ui ();
178 }
179 else
180 {
181 GFileInfo *info = g_file_query_info (file: save_filename, attributes: "standard::display-name", flags: 0, NULL, NULL);
182 const char *display_name = g_file_info_get_display_name (info);
183
184 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
185 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
186 type: GTK_MESSAGE_ERROR,
187 buttons: GTK_BUTTONS_CLOSE,
188 message_format: "Error saving to file %s:\n%s",
189 display_name,
190 error->message);
191
192 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
193 gtk_widget_show (widget: error_dialog);
194
195 g_error_free (error);
196 g_object_unref (object: info);
197 }
198}
199
200
201typedef struct {
202 char *text;
203 PangoLayout *layout;
204 GList *page_breaks;
205 GtkWidget *font_button;
206 char *font;
207} PrintData;
208
209static void
210begin_print (GtkPrintOperation *operation,
211 GtkPrintContext *context,
212 PrintData *print_data)
213{
214 PangoFontDescription *desc;
215 PangoLayoutLine *layout_line;
216 double width, height;
217 double page_height;
218 GList *page_breaks;
219 int num_lines;
220 int line;
221
222 width = gtk_print_context_get_width (context);
223 height = gtk_print_context_get_height (context);
224
225 print_data->layout = gtk_print_context_create_pango_layout (context);
226
227 desc = pango_font_description_from_string (str: print_data->font);
228 pango_layout_set_font_description (layout: print_data->layout, desc);
229 pango_font_description_free (desc);
230
231 pango_layout_set_width (layout: print_data->layout, width: width * PANGO_SCALE);
232
233 pango_layout_set_text (layout: print_data->layout, text: print_data->text, length: -1);
234
235 num_lines = pango_layout_get_line_count (layout: print_data->layout);
236
237 page_breaks = NULL;
238 page_height = 0;
239
240 for (line = 0; line < num_lines; line++)
241 {
242 PangoRectangle ink_rect, logical_rect;
243 double line_height;
244
245 layout_line = pango_layout_get_line (layout: print_data->layout, line);
246 pango_layout_line_get_extents (line: layout_line, ink_rect: &ink_rect, logical_rect: &logical_rect);
247
248 line_height = logical_rect.height / 1024.0;
249
250 if (page_height + line_height > height)
251 {
252 page_breaks = g_list_prepend (list: page_breaks, GINT_TO_POINTER (line));
253 page_height = 0;
254 }
255
256 page_height += line_height;
257 }
258
259 page_breaks = g_list_reverse (list: page_breaks);
260 gtk_print_operation_set_n_pages (op: operation, n_pages: g_list_length (list: page_breaks) + 1);
261
262 print_data->page_breaks = page_breaks;
263}
264
265static void
266draw_page (GtkPrintOperation *operation,
267 GtkPrintContext *context,
268 int page_nr,
269 PrintData *print_data)
270{
271 cairo_t *cr;
272 GList *pagebreak;
273 int start, end, i;
274 PangoLayoutIter *iter;
275 double start_pos;
276
277 if (page_nr == 0)
278 start = 0;
279 else
280 {
281 pagebreak = g_list_nth (list: print_data->page_breaks, n: page_nr - 1);
282 start = GPOINTER_TO_INT (pagebreak->data);
283 }
284
285 pagebreak = g_list_nth (list: print_data->page_breaks, n: page_nr);
286 if (pagebreak == NULL)
287 end = pango_layout_get_line_count (layout: print_data->layout);
288 else
289 end = GPOINTER_TO_INT (pagebreak->data);
290
291 cr = gtk_print_context_get_cairo_context (context);
292
293 cairo_set_source_rgb (cr, red: 0, green: 0, blue: 0);
294
295 i = 0;
296 start_pos = 0;
297 iter = pango_layout_get_iter (layout: print_data->layout);
298 do
299 {
300 PangoRectangle logical_rect;
301 PangoLayoutLine *line;
302 int baseline;
303
304 if (i >= start)
305 {
306 line = pango_layout_iter_get_line (iter);
307
308 pango_layout_iter_get_line_extents (iter, NULL, logical_rect: &logical_rect);
309 baseline = pango_layout_iter_get_baseline (iter);
310
311 if (i == start)
312 start_pos = logical_rect.y / 1024.0;
313
314 cairo_move_to (cr, x: logical_rect.x / 1024.0, y: baseline / 1024.0 - start_pos);
315
316 pango_cairo_show_layout_line (cr, line);
317 }
318 i++;
319 }
320 while (i < end &&
321 pango_layout_iter_next_line (iter));
322
323 pango_layout_iter_free (iter);
324}
325
326static void
327status_changed_cb (GtkPrintOperation *op,
328 gpointer user_data)
329{
330 if (gtk_print_operation_is_finished (op))
331 {
332 active_prints = g_list_remove (list: active_prints, data: op);
333 g_object_unref (object: op);
334 }
335 update_statusbar ();
336}
337
338static GtkWidget *
339create_custom_widget (GtkPrintOperation *operation,
340 PrintData *data)
341{
342 GtkWidget *vbox, *hbox, *font, *label;
343
344 gtk_print_operation_set_custom_tab_label (op: operation, label: "Other");
345 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
346
347 hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 8);
348 gtk_box_append (GTK_BOX (vbox), child: hbox);
349 gtk_widget_show (widget: hbox);
350
351 label = gtk_label_new (str: "Font:");
352 gtk_box_append (GTK_BOX (hbox), child: label);
353 gtk_widget_show (widget: label);
354
355 font = gtk_font_button_new_with_font (fontname: data->font);
356 gtk_box_append (GTK_BOX (hbox), child: font);
357 gtk_widget_show (widget: font);
358 data->font_button = font;
359
360 return vbox;
361}
362
363static void
364custom_widget_apply (GtkPrintOperation *operation,
365 GtkWidget *widget,
366 PrintData *data)
367{
368 const char *selected_font;
369 selected_font = gtk_font_chooser_get_font (GTK_FONT_CHOOSER (data->font_button));
370
371 g_free (mem: data->font);
372 data->font = g_strdup (str: selected_font);
373}
374
375static void
376print_done (GtkPrintOperation *op,
377 GtkPrintOperationResult res,
378 PrintData *print_data)
379{
380 GError *error = NULL;
381
382 if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
383 {
384
385 GtkWidget *error_dialog;
386
387 gtk_print_operation_get_error (op, error: &error);
388
389 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
390 flags: GTK_DIALOG_DESTROY_WITH_PARENT,
391 type: GTK_MESSAGE_ERROR,
392 buttons: GTK_BUTTONS_CLOSE,
393 message_format: "Error printing file:\n%s",
394 error ? error->message : "no details");
395 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
396 gtk_widget_show (widget: error_dialog);
397 }
398 else if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
399 {
400 if (settings != NULL)
401 g_object_unref (object: settings);
402 settings = g_object_ref (gtk_print_operation_get_print_settings (op));
403 }
404
405 g_free (mem: print_data->text);
406 g_free (mem: print_data->font);
407 g_free (mem: print_data);
408
409 if (!gtk_print_operation_is_finished (op))
410 {
411 g_object_ref (op);
412 active_prints = g_list_append (list: active_prints, data: op);
413 update_statusbar ();
414
415 /* This ref is unref:ed when we get the final state change */
416 g_signal_connect (op, "status_changed",
417 G_CALLBACK (status_changed_cb), NULL);
418 }
419}
420
421static void
422end_print (GtkPrintOperation *op, GtkPrintContext *context, PrintData *print_data)
423{
424 g_list_free (list: print_data->page_breaks);
425 print_data->page_breaks = NULL;
426 g_object_unref (object: print_data->layout);
427 print_data->layout = NULL;
428}
429
430static void
431print_or_preview (GSimpleAction *action, GtkPrintOperationAction print_action)
432{
433 GtkPrintOperation *print;
434 PrintData *print_data;
435
436 print_data = g_new0 (PrintData, 1);
437
438 print_data->text = get_text ();
439 print_data->font = g_strdup (str: "Sans 12");
440
441 print = gtk_print_operation_new ();
442
443 gtk_print_operation_set_track_print_status (op: print, TRUE);
444
445 if (settings != NULL)
446 gtk_print_operation_set_print_settings (op: print, print_settings: settings);
447
448 if (page_setup != NULL)
449 gtk_print_operation_set_default_page_setup (op: print, default_page_setup: page_setup);
450
451 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), print_data);
452 g_signal_connect (print, "end-print", G_CALLBACK (end_print), print_data);
453 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), print_data);
454 g_signal_connect (print, "create_custom_widget", G_CALLBACK (create_custom_widget), print_data);
455 g_signal_connect (print, "custom_widget_apply", G_CALLBACK (custom_widget_apply), print_data);
456
457 g_signal_connect (print, "done", G_CALLBACK (print_done), print_data);
458
459 gtk_print_operation_set_export_filename (op: print, filename: "test.pdf");
460
461#if 0
462 gtk_print_operation_set_allow_async (print, TRUE);
463#endif
464 gtk_print_operation_run (op: print, action: print_action, GTK_WINDOW (main_window), NULL);
465
466 g_object_unref (object: print);
467}
468
469static void
470activate_page_setup (GSimpleAction *action,
471 GVariant *parameter,
472 gpointer user_data)
473{
474 GtkPageSetup *new_page_setup;
475
476 new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (main_window),
477 page_setup, settings);
478
479 if (page_setup)
480 g_object_unref (object: page_setup);
481
482 page_setup = new_page_setup;
483}
484
485static void
486activate_print (GSimpleAction *action,
487 GVariant *parameter,
488 gpointer user_data)
489{
490 print_or_preview (action, print_action: GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
491}
492
493static void
494activate_preview (GSimpleAction *action,
495 GVariant *parameter,
496 gpointer user_data)
497{
498 print_or_preview (action, print_action: GTK_PRINT_OPERATION_ACTION_PREVIEW);
499}
500
501static void
502on_save_response (GtkWidget *dialog,
503 int response)
504{
505 if (response == GTK_RESPONSE_OK)
506 {
507 GFile *save_filename = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
508 save_file (save_filename);
509 g_object_unref (object: save_filename);
510 }
511
512 gtk_window_destroy (GTK_WINDOW (dialog));
513}
514
515static void
516activate_save_as (GSimpleAction *action,
517 GVariant *parameter,
518 gpointer user_data)
519{
520 GtkWidget *dialog;
521
522 dialog = gtk_file_chooser_dialog_new (title: "Select file",
523 GTK_WINDOW (main_window),
524 action: GTK_FILE_CHOOSER_ACTION_SAVE,
525 first_button_text: "_Cancel", GTK_RESPONSE_CANCEL,
526 "_Save", GTK_RESPONSE_OK,
527 NULL);
528 gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id: GTK_RESPONSE_OK);
529 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
530 gtk_widget_show (widget: dialog);
531
532 g_signal_connect (dialog, "response",
533 G_CALLBACK (on_save_response),
534 NULL);
535}
536
537static void
538activate_save (GSimpleAction *action,
539 GVariant *parameter,
540 gpointer user_data)
541{
542 if (filename == NULL)
543 activate_save_as (action, NULL, NULL);
544 else
545 save_file (save_filename: filename);
546}
547
548static void
549on_open_response (GtkWidget *dialog,
550 int response)
551{
552 if (response == GTK_RESPONSE_OK)
553 {
554 GFile *open_filename = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
555 load_file (open_filename);
556 g_object_unref (object: open_filename);
557 }
558
559 gtk_window_destroy (GTK_WINDOW (dialog));
560}
561
562static void
563activate_open (GSimpleAction *action,
564 GVariant *parameter,
565 gpointer user_data)
566{
567 GtkWidget *dialog;
568
569 dialog = gtk_file_chooser_dialog_new (title: "Select file",
570 GTK_WINDOW (main_window),
571 action: GTK_FILE_CHOOSER_ACTION_OPEN,
572 first_button_text: "_Cancel", GTK_RESPONSE_CANCEL,
573 "_Open", GTK_RESPONSE_OK,
574 NULL);
575 gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id: GTK_RESPONSE_OK);
576 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
577 gtk_widget_show (widget: dialog);
578
579 g_signal_connect (dialog, "response",
580 G_CALLBACK (on_open_response),
581 NULL);
582}
583
584static void
585activate_new (GSimpleAction *action,
586 GVariant *parameter,
587 gpointer user_data)
588{
589 g_clear_object (&filename);
590 set_text (text: "", len: 0);
591}
592
593static void
594activate_about (GSimpleAction *action,
595 GVariant *parameter,
596 gpointer user_data)
597{
598 char *version;
599 GString *sysinfo;
600 char *setting;
601 char **backends;
602 int i;
603 char *os_name;
604 char *os_version;
605 const char *authors[] = {
606 "Alexander Larsson",
607 NULL
608 };
609 const char *artists[] = {
610 "Jakub Steiner",
611 NULL
612 };
613 const char *maintainers[] = {
614 "The GTK Team",
615 NULL
616 };
617 GtkWidget *dialog;
618
619 os_name = g_get_os_info (G_OS_INFO_KEY_NAME);
620 os_version = g_get_os_info (G_OS_INFO_KEY_VERSION_ID);
621 sysinfo = g_string_new (init: "");
622 if (os_name && os_version)
623 g_string_append_printf (string: sysinfo, format: "OS\t%s %s\n\n", os_name, os_version);
624 g_string_append (string: sysinfo, val: "System libraries\n");
625 g_string_append_printf (string: sysinfo, format: "\tGLib\t%d.%d.%d\n",
626 glib_major_version,
627 glib_minor_version,
628 glib_micro_version);
629 g_string_append_printf (string: sysinfo, format: "\tPango\t%s\n",
630 pango_version_string ());
631 g_string_append_printf (string: sysinfo, format: "\tGTK \t%d.%d.%d\n",
632 gtk_get_major_version (),
633 gtk_get_minor_version (),
634 gtk_get_micro_version ());
635
636 g_string_append (string: sysinfo, val: "\nPrint backends\n");
637
638 g_object_get (object: gtk_settings_get_default (), first_property_name: "gtk-print-backends", &setting, NULL);
639 backends = g_strsplit (string: setting, delimiter: ",", max_tokens: -1);
640 g_string_append (string: sysinfo, val: "\t");
641 for (i = 0; backends[i]; i++)
642 g_string_append_printf (string: sysinfo, format: "%s ", backends[i]);
643 g_strfreev (str_array: backends);
644 g_free (mem: setting);
645
646 version = g_strdup_printf (format: "%s%s%s\nRunning against GTK %d.%d.%d",
647 PACKAGE_VERSION,
648 g_strcmp0 (str1: PROFILE, str2: "devel") == 0 ? "-" : "",
649 g_strcmp0 (str1: PROFILE, str2: "devel") == 0 ? VCS_TAG : "",
650 gtk_get_major_version (),
651 gtk_get_minor_version (),
652 gtk_get_micro_version ());
653
654 dialog = g_object_new (GTK_TYPE_ABOUT_DIALOG,
655 first_property_name: "transient-for", main_window,
656 "program-name", g_strcmp0 (str1: PROFILE, str2: "devel") == 0
657 ? "GTK Print Editor (Development)"
658 : "GTK Print Editor",
659 "version", version,
660 "copyright", "© 2006-2021 Red Hat, Inc",
661 "license-type", GTK_LICENSE_LGPL_2_1,
662 "website", "http://www.gtk.org",
663 "comments", "Program to demonstrate GTK printing",
664 "authors", authors,
665 "logo-icon-name", "org.gtk.PrintEditor4",
666 "title", "About GTK Print Editor",
667 "system-information", sysinfo->str,
668 NULL);
669 gtk_about_dialog_add_credit_section (GTK_ABOUT_DIALOG (dialog),
670 _("Artwork by"), people: artists);
671 gtk_about_dialog_add_credit_section (GTK_ABOUT_DIALOG (dialog),
672 _("Maintained by"), people: maintainers);
673
674 gtk_window_present (GTK_WINDOW (dialog));
675
676 g_string_free (string: sysinfo, TRUE);
677 g_free (mem: version);
678 g_free (mem: os_name);
679 g_free (mem: os_version);
680}
681
682static void
683activate_quit (GSimpleAction *action,
684 GVariant *parameter,
685 gpointer user_data)
686{
687 GtkApplication *app = user_data;
688 GtkWidget *win;
689 GList *list, *next;
690
691 list = gtk_application_get_windows (application: app);
692 while (list)
693 {
694 win = list->data;
695 next = list->next;
696
697 gtk_window_destroy (GTK_WINDOW (win));
698
699 list = next;
700 }
701}
702
703static GActionEntry app_entries[] = {
704 { "new", activate_new, NULL, NULL, NULL },
705 { "open", activate_open, NULL, NULL, NULL },
706 { "save", activate_save, NULL, NULL, NULL },
707 { "save-as", activate_save_as, NULL, NULL, NULL },
708 { "quit", activate_quit, NULL, NULL, NULL },
709 { "about", activate_about, NULL, NULL, NULL },
710 { "page-setup", activate_page_setup, NULL, NULL, NULL },
711 { "preview", activate_preview, NULL, NULL, NULL },
712 { "print", activate_print, NULL, NULL, NULL }
713};
714
715static const char ui_info[] =
716 "<interface>"
717 " <menu id='menubar'>"
718 " <submenu>"
719 " <attribute name='label'>_File</attribute>"
720 " <section>"
721 " <item>"
722 " <attribute name='label'>_New</attribute>"
723 " <attribute name='action'>app.new</attribute>"
724 " </item>"
725 " <item>"
726 " <attribute name='label'>_Open</attribute>"
727 " <attribute name='action'>app.open</attribute>"
728 " </item>"
729 " <item>"
730 " <attribute name='label'>_Save</attribute>"
731 " <attribute name='action'>app.save</attribute>"
732 " </item>"
733 " <item>"
734 " <attribute name='label'>Save _As...</attribute>"
735 " <attribute name='action'>app.save-as</attribute>"
736 " </item>"
737 " </section>"
738 " <section>"
739 " <item>"
740 " <attribute name='label'>Page Setup</attribute>"
741 " <attribute name='action'>app.page-setup</attribute>"
742 " </item>"
743 " <item>"
744 " <attribute name='label'>Preview</attribute>"
745 " <attribute name='action'>app.preview</attribute>"
746 " </item>"
747 " <item>"
748 " <attribute name='label'>Print</attribute>"
749 " <attribute name='action'>app.print</attribute>"
750 " </item>"
751 " </section>"
752 " <section>"
753 " <item>"
754 " <attribute name='label'>_Quit</attribute>"
755 " <attribute name='action'>app.quit</attribute>"
756 " </item>"
757 " </section>"
758 " </submenu>"
759 " <submenu>"
760 " <attribute name='label'>_Help</attribute>"
761 " <section>"
762 " <item>"
763 " <attribute name='label'>_About Print Editor</attribute>"
764 " <attribute name='action'>app.about</attribute>"
765 " </item>"
766 " </section>"
767 " </submenu>"
768 " </menu>"
769 "</interface>";
770
771static void
772buffer_changed_callback (GtkTextBuffer *text_buffer)
773{
774 file_changed = TRUE;
775 update_statusbar ();
776}
777
778static void
779mark_set_callback (GtkTextBuffer *text_buffer,
780 const GtkTextIter *new_location,
781 GtkTextMark *mark,
782 gpointer data)
783{
784 update_statusbar ();
785}
786
787static void
788startup (GApplication *app)
789{
790 GtkBuilder *builder;
791 GMenuModel *menubar;
792 struct {
793 const char *action_and_target;
794 const char *accelerators[2];
795 } accels[] = {
796 { "app.new", { "<Control>n", NULL } },
797 { "app.quit", { "<Control>q", NULL } },
798 { "app.save", { "<Control>s", NULL } },
799 { "app.about", { "<Control>a", NULL } },
800 };
801
802 builder = gtk_builder_new ();
803 gtk_builder_add_from_string (builder, buffer: ui_info, length: -1, NULL);
804
805 menubar = (GMenuModel *)gtk_builder_get_object (builder, name: "menubar");
806
807 gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
808
809 for (int i = 0; i < G_N_ELEMENTS (accels); i++)
810 gtk_application_set_accels_for_action (GTK_APPLICATION (app), detailed_action_name: accels[i].action_and_target, accels: accels[i].accelerators);
811
812 g_object_unref (object: builder);
813}
814
815static void
816activate (GApplication *app)
817{
818 GtkWidget *box;
819 GtkWidget *sw;
820 GtkWidget *contents;
821
822 main_window = gtk_application_window_new (GTK_APPLICATION (app));
823
824 if (g_strcmp0 (str1: PROFILE, str2: "devel") == 0)
825 gtk_widget_add_css_class (GTK_WIDGET (main_window), css_class: "devel");
826
827 gtk_window_set_icon_name (GTK_WINDOW (main_window), name: "text-editor");
828 gtk_window_set_default_size (GTK_WINDOW (main_window), width: 400, height: 600);
829 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (main_window), TRUE);
830 update_title (GTK_WINDOW (main_window));
831
832 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
833 gtk_window_set_child (GTK_WINDOW (main_window), child: box);
834
835 /* Create document */
836 sw = gtk_scrolled_window_new ();
837
838 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
839 hscrollbar_policy: GTK_POLICY_AUTOMATIC,
840 vscrollbar_policy: GTK_POLICY_AUTOMATIC);
841
842 gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
843
844 gtk_widget_set_vexpand (widget: sw, TRUE);
845 gtk_box_append (GTK_BOX (box), child: sw);
846
847 contents = gtk_text_view_new ();
848 gtk_widget_grab_focus (widget: contents);
849
850 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw),
851 child: contents);
852
853 /* Create statusbar */
854 statusbar = gtk_statusbar_new ();
855 gtk_box_append (GTK_BOX (box), child: statusbar);
856
857 /* Show text widget info in the statusbar */
858 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents));
859
860 g_signal_connect_object (instance: buffer,
861 detailed_signal: "changed",
862 G_CALLBACK (buffer_changed_callback),
863 NULL,
864 connect_flags: 0);
865
866 g_signal_connect_object (instance: buffer,
867 detailed_signal: "mark_set", /* cursor moved */
868 G_CALLBACK (mark_set_callback),
869 NULL,
870 connect_flags: 0);
871
872 update_ui ();
873
874 gtk_window_present (GTK_WINDOW (main_window));
875}
876
877static void
878open (GApplication *application,
879 GFile **files,
880 int n_files,
881 const char *hint)
882{
883 if (n_files > 1)
884 g_warning ("Can only open a single file");
885
886 activate (app: application);
887
888 load_file (open_filename: files[0]);
889}
890
891int
892main (int argc, char **argv)
893{
894 GtkApplication *app;
895 GError *error = NULL;
896
897 gtk_init ();
898
899 settings = gtk_print_settings_new_from_file (file_name: "print-settings.ini", error: &error);
900 if (error) {
901 g_print (format: "Failed to load print settings: %s\n", error->message);
902 g_clear_error (err: &error);
903
904 settings = gtk_print_settings_new ();
905 }
906 g_assert (settings != NULL);
907
908 page_setup = gtk_page_setup_new_from_file (file_name: "page-setup.ini", error: &error);
909 if (error) {
910 g_print (format: "Failed to load page setup: %s\n", error->message);
911 g_clear_error (err: &error);
912 }
913
914 app = gtk_application_new (application_id: "org.gtk.PrintEditor4", flags: G_APPLICATION_HANDLES_OPEN);
915
916 g_action_map_add_action_entries (G_ACTION_MAP (app),
917 entries: app_entries, G_N_ELEMENTS (app_entries),
918 user_data: app);
919
920 g_signal_connect (app, "startup", G_CALLBACK (startup), NULL);
921 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
922 g_signal_connect (app, "open", G_CALLBACK (open), NULL);
923
924 g_application_run (G_APPLICATION (app), argc, argv);
925
926 if (!gtk_print_settings_to_file (settings, file_name: "print-settings.ini", error: &error)) {
927 g_print (format: "Failed to save print settings: %s\n", error->message);
928 g_clear_error (err: &error);
929 }
930 if (page_setup &&
931 !gtk_page_setup_to_file (setup: page_setup, file_name: "page-setup.ini", error: &error)) {
932 g_print (format: "Failed to save page setup: %s\n", error->message);
933 g_clear_error (err: &error);
934 }
935
936 return 0;
937}
938

source code of gtk/demos/print-editor/print-editor.c