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 | |
9 | static GtkWidget *main_window; |
10 | static GFile *filename = NULL; |
11 | static GtkPageSetup *page_setup = NULL; |
12 | static GtkPrintSettings *settings = NULL; |
13 | static gboolean file_changed = FALSE; |
14 | static GtkTextBuffer *buffer; |
15 | static GtkWidget *statusbar; |
16 | static GList *active_prints = NULL; |
17 | |
18 | static void |
19 | update_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 | |
36 | static void |
37 | update_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 | |
70 | static void |
71 | update_ui (void) |
72 | { |
73 | update_title (GTK_WINDOW (main_window)); |
74 | update_statusbar (); |
75 | } |
76 | |
77 | static char * |
78 | get_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 | |
87 | static void |
88 | set_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 | |
96 | static void |
97 | load_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 | |
153 | static void |
154 | save_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 | |
201 | typedef struct { |
202 | char *text; |
203 | PangoLayout *layout; |
204 | GList *page_breaks; |
205 | GtkWidget *font_button; |
206 | char *font; |
207 | } PrintData; |
208 | |
209 | static void |
210 | begin_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 | |
265 | static void |
266 | draw_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 | |
326 | static void |
327 | status_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 | |
338 | static GtkWidget * |
339 | create_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 | |
363 | static void |
364 | custom_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 | |
375 | static void |
376 | print_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 | |
421 | static void |
422 | end_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 | |
430 | static void |
431 | print_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 | |
469 | static void |
470 | activate_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 | |
485 | static void |
486 | activate_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 | |
493 | static void |
494 | activate_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 | |
501 | static void |
502 | on_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 | |
515 | static void |
516 | activate_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 | |
537 | static void |
538 | activate_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 | |
548 | static void |
549 | on_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 | |
562 | static void |
563 | activate_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 | |
584 | static void |
585 | activate_new (GSimpleAction *action, |
586 | GVariant *parameter, |
587 | gpointer user_data) |
588 | { |
589 | g_clear_object (&filename); |
590 | set_text (text: "" , len: 0); |
591 | } |
592 | |
593 | static void |
594 | activate_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 | |
682 | static void |
683 | activate_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 | |
703 | static 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 | |
715 | static 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 | |
771 | static void |
772 | buffer_changed_callback (GtkTextBuffer *text_buffer) |
773 | { |
774 | file_changed = TRUE; |
775 | update_statusbar (); |
776 | } |
777 | |
778 | static void |
779 | mark_set_callback (GtkTextBuffer *text_buffer, |
780 | const GtkTextIter *new_location, |
781 | GtkTextMark *mark, |
782 | gpointer data) |
783 | { |
784 | update_statusbar (); |
785 | } |
786 | |
787 | static void |
788 | startup (GApplication *app) |
789 | { |
790 | GtkBuilder *builder; |
791 | GMenuModel *; |
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 | |
815 | static void |
816 | activate (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 | |
877 | static void |
878 | open (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 | |
891 | int |
892 | main (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 | |