2 #include <pango/pangocairo.h>
4 #include <gtk/gtkprintoperation.h>
6 static GtkWidget *main_window;
7 static char *filename = NULL;
8 static GtkPageSetup *page_setup = NULL;
9 static GtkPrintSettings *settings = NULL;
10 static gboolean file_changed = FALSE;
11 static GtkTextBuffer *buffer;
12 static GtkWidget *statusbar;
13 static GList *active_prints = NULL;
22 basename = g_strdup ("Untitled");
24 basename = g_path_get_basename (filename);
26 title = g_strdup_printf ("Simple Editor with printing - %s", basename);
29 gtk_window_set_title (GTK_WINDOW (main_window), title);
34 update_statusbar (void)
39 const char *print_str;
41 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 0);
43 gtk_text_buffer_get_iter_at_mark (buffer,
45 gtk_text_buffer_get_insert (buffer));
47 row = gtk_text_iter_get_line (&iter);
48 col = gtk_text_iter_get_line_offset (&iter);
53 GtkPrintOperation *op = active_prints->data;
54 print_str = gtk_print_operation_get_status_string (op);
57 msg = g_strdup_printf ("%d, %d%s %s",
59 file_changed?" - Modified":"",
62 gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, msg);
77 GtkTextIter start, end;
79 gtk_text_buffer_get_start_iter (buffer, &start);
80 gtk_text_buffer_get_end_iter (buffer, &end);
81 return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
85 set_text (const char *text, gsize len)
87 gtk_text_buffer_set_text (buffer, text, len);
93 do_new (GtkAction *action)
101 load_file (const char *open_filename)
103 GtkWidget *error_dialog;
110 if (g_file_get_contents (open_filename, &contents, &len, &error))
112 if (g_utf8_validate (contents, len, NULL))
114 filename = g_strdup (open_filename);
115 set_text (contents, len);
120 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
121 GTK_DIALOG_DESTROY_WITH_PARENT,
124 "Error loading file %s:\n%s",
131 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
132 GTK_DIALOG_DESTROY_WITH_PARENT,
135 "Error loading file %s:\n%s",
139 g_error_free (error);
143 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
144 gtk_widget_show (error_dialog);
149 do_open (GtkAction *action)
155 dialog = gtk_file_chooser_dialog_new ("Select file",
156 GTK_WINDOW (main_window),
157 GTK_FILE_CHOOSER_ACTION_OPEN,
158 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
159 GTK_STOCK_OPEN, GTK_RESPONSE_OK,
161 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
162 response = gtk_dialog_run (GTK_DIALOG (dialog));
164 if (response == GTK_RESPONSE_OK)
166 open_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
167 load_file (open_filename);
168 g_free (open_filename);
171 gtk_widget_destroy (dialog);
175 save_file (const char *save_filename)
177 char *text = get_text ();
178 GtkWidget *error_dialog;
182 if (g_file_set_contents (save_filename,
185 if (save_filename != filename)
188 filename = g_strdup (save_filename);
190 file_changed = FALSE;
195 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
196 GTK_DIALOG_DESTROY_WITH_PARENT,
199 "Error saving to file %s:\n%s",
203 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
204 gtk_widget_show (error_dialog);
206 g_error_free (error);
211 do_save_as (GtkAction *action)
217 dialog = gtk_file_chooser_dialog_new ("Select file",
218 GTK_WINDOW (main_window),
219 GTK_FILE_CHOOSER_ACTION_SAVE,
220 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
221 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
223 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
224 response = gtk_dialog_run (GTK_DIALOG (dialog));
226 if (response == GTK_RESPONSE_OK)
228 save_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
229 save_file (save_filename);
230 g_free (save_filename);
233 gtk_widget_destroy (dialog);
237 do_save (GtkAction *action)
239 if (filename == NULL)
242 save_file (filename);
249 GtkWidget *font_button;
254 begin_print (GtkPrintOperation *operation,
255 GtkPrintContext *context,
256 PrintData *print_data)
258 PangoFontDescription *desc;
259 PangoLayoutLine *layout_line;
260 double width, height;
266 width = gtk_print_context_get_width (context);
267 height = gtk_print_context_get_height (context);
269 print_data->layout = gtk_print_context_create_pango_layout (context);
271 desc = pango_font_description_from_string (print_data->font);
272 pango_layout_set_font_description (print_data->layout, desc);
273 pango_font_description_free (desc);
275 pango_layout_set_width (print_data->layout, width * PANGO_SCALE);
277 pango_layout_set_text (print_data->layout, print_data->text, -1);
279 num_lines = pango_layout_get_line_count (print_data->layout);
284 for (line = 0; line < num_lines; line++)
286 PangoRectangle ink_rect, logical_rect;
289 layout_line = pango_layout_get_line (print_data->layout, line);
290 pango_layout_line_get_extents (layout_line, &ink_rect, &logical_rect);
292 line_height = logical_rect.height / 1024.0;
294 if (page_height + line_height > height)
296 page_breaks = g_list_prepend (page_breaks, GINT_TO_POINTER (line));
300 page_height += line_height;
303 page_breaks = g_list_reverse (page_breaks);
304 gtk_print_operation_set_n_pages (operation, g_list_length (page_breaks) + 1);
306 print_data->page_breaks = page_breaks;
310 draw_page (GtkPrintOperation *operation,
311 GtkPrintContext *context,
313 PrintData *print_data)
318 PangoLayoutIter *iter;
325 pagebreak = g_list_nth (print_data->page_breaks, page_nr - 1);
326 start = GPOINTER_TO_INT (pagebreak->data);
329 pagebreak = g_list_nth (print_data->page_breaks, page_nr);
330 if (pagebreak == NULL)
331 end = pango_layout_get_line_count (print_data->layout);
333 end = GPOINTER_TO_INT (pagebreak->data);
335 cr = gtk_print_context_get_cairo_context (context);
337 cairo_set_source_rgb (cr, 0, 0, 0);
341 iter = pango_layout_get_iter (print_data->layout);
344 PangoRectangle logical_rect;
345 PangoLayoutLine *line;
350 line = pango_layout_iter_get_line (iter);
352 pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
353 baseline = pango_layout_iter_get_baseline (iter);
356 start_pos = logical_rect.y / 1024.0;
358 cairo_move_to (cr, logical_rect.x / 1024.0, baseline / 1024.0 - start_pos);
360 pango_cairo_show_layout_line (cr, line);
365 pango_layout_iter_next_line (iter));
367 pango_layout_iter_free (iter);
371 do_page_setup (GtkAction *action)
373 GtkPageSetup *new_page_setup;
375 new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (main_window),
376 page_setup, settings);
379 g_object_unref (page_setup);
381 page_setup = new_page_setup;
385 status_changed_cb (GtkPrintOperation *op,
388 if (gtk_print_operation_is_finished (op))
390 active_prints = g_list_remove (active_prints, op);
397 create_custom_widget (GtkPrintOperation *operation,
400 GtkWidget *vbox, *hbox, *font, *label;
402 gtk_print_operation_set_custom_tab_label (operation, "Other");
403 vbox = gtk_vbox_new (FALSE, 0);
404 gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
406 hbox = gtk_hbox_new (FALSE, 8);
407 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
408 gtk_widget_show (hbox);
410 label = gtk_label_new ("Font:");
411 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
412 gtk_widget_show (label);
414 font = gtk_font_button_new_with_font (data->font);
415 gtk_box_pack_start (GTK_BOX (hbox), font, FALSE, FALSE, 0);
416 gtk_widget_show (font);
417 data->font_button = font;
423 custom_widget_apply (GtkPrintOperation *operation,
427 const char *selected_font;
428 selected_font = gtk_font_button_get_font_name (GTK_FONT_BUTTON (data->font_button));
430 data->font = g_strdup (selected_font);
435 GtkPrintOperation *op;
436 GtkPrintOperationPreview *preview;
441 gdouble dpi_x, dpi_y;
445 preview_expose (GtkWidget *widget,
446 GdkEventExpose *event,
449 PreviewOp *pop = data;
451 gdk_window_clear (pop->area->window);
452 gtk_print_operation_preview_render_page (pop->preview,
459 preview_ready (GtkPrintOperationPreview *preview,
460 GtkPrintContext *context,
463 PreviewOp *pop = data;
466 g_object_get (pop->op, "n-pages", &n_pages, NULL);
468 gtk_spin_button_set_range (GTK_SPIN_BUTTON (pop->spin),
471 g_signal_connect (pop->area, "expose_event",
472 G_CALLBACK (preview_expose),
475 gtk_widget_queue_draw (pop->area);
479 preview_got_page_size (GtkPrintOperationPreview *preview,
480 GtkPrintContext *context,
481 GtkPageSetup *page_setup,
484 PreviewOp *pop = data;
485 GtkPaperSize *paper_size;
488 gdouble dpi_x, dpi_y;
490 paper_size = gtk_page_setup_get_paper_size (page_setup);
492 w = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH);
493 h = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH);
495 cr = gdk_cairo_create (pop->area->window);
497 dpi_x = pop->area->allocation.width/w;
498 dpi_y = pop->area->allocation.height/h;
500 if (fabs (dpi_x - pop->dpi_x) > 0.001 ||
501 fabs (dpi_y - pop->dpi_y) > 0.001)
503 gtk_print_context_set_cairo_context (context, cr, dpi_x, dpi_y);
508 pango_cairo_update_layout (cr, pop->data->layout);
513 update_page (GtkSpinButton *widget,
516 PreviewOp *pop = data;
518 pop->page = gtk_spin_button_get_value_as_int (widget);
519 gtk_widget_queue_draw (pop->area);
523 preview_destroy (GtkWindow *window,
526 gtk_print_operation_preview_end_preview (pop->preview);
527 g_object_unref (pop->op);
533 preview_cb (GtkPrintOperation *op,
534 GtkPrintOperationPreview *preview,
535 GtkPrintContext *context,
539 GtkPrintSettings *settings;
540 GtkWidget *window, *close, *page, *hbox, *vbox, *da;
541 gdouble width, height;
544 PrintData *print_data = data;
546 pop = g_new0 (PreviewOp, 1);
548 pop->data = print_data;
549 settings = gtk_print_operation_get_print_settings (op);
553 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
554 gtk_window_set_transient_for (GTK_WINDOW (window),
555 GTK_WINDOW (main_window));
556 vbox = gtk_vbox_new (FALSE, 0);
557 gtk_container_add (GTK_CONTAINER (window), vbox);
558 hbox = gtk_hbox_new (FALSE, 0);
559 gtk_box_pack_start (GTK_BOX (vbox), hbox,
561 page = gtk_spin_button_new_with_range (1, 100, 1);
562 gtk_box_pack_start (GTK_BOX (hbox), page, FALSE, FALSE, 0);
564 close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
565 gtk_box_pack_start (GTK_BOX (hbox), close, FALSE, FALSE, 0);
567 da = gtk_drawing_area_new ();
568 gtk_widget_set_size_request (GTK_WIDGET (da), width, height);
569 gtk_box_pack_start (GTK_BOX (vbox), da, TRUE, TRUE, 0);
571 gtk_widget_set_double_buffered (da, FALSE);
573 gtk_widget_realize (da);
575 cr = gdk_cairo_create (da->window);
577 /* TODO: What dpi to use here? This will be used for pagination.. */
578 gtk_print_context_set_cairo_context (context, cr, 72, 72);
582 pop->preview = preview;
587 g_signal_connect (page, "value-changed",
588 G_CALLBACK (update_page), pop);
589 g_signal_connect_swapped (close, "clicked",
590 G_CALLBACK (gtk_widget_destroy), window);
592 g_signal_connect (preview, "ready",
593 G_CALLBACK (preview_ready), pop);
594 g_signal_connect (preview, "got-page-size",
595 G_CALLBACK (preview_got_page_size), pop);
597 g_signal_connect (window, "destroy",
598 G_CALLBACK (preview_destroy), pop);
600 gtk_widget_show_all (window);
606 print_done (GtkPrintOperation *op,
607 GtkPrintOperationResult res,
608 PrintData *print_data)
610 GError *error = NULL;
612 if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
615 GtkWidget *error_dialog;
617 gtk_print_operation_get_error (op, &error);
619 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
620 GTK_DIALOG_DESTROY_WITH_PARENT,
623 "Error printing file:\n%s",
624 error ? error->message : "no details");
625 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
626 gtk_widget_show (error_dialog);
628 else if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
630 if (settings != NULL)
631 g_object_unref (settings);
632 settings = g_object_ref (gtk_print_operation_get_print_settings (op));
635 g_free (print_data->text);
636 g_free (print_data->font);
637 g_list_free (print_data->page_breaks);
638 g_object_unref (print_data->layout);
641 if (!gtk_print_operation_is_finished (op))
644 active_prints = g_list_append (active_prints, op);
647 /* This ref is unref:ed when we get the final state change */
648 g_signal_connect (op, "status_changed",
649 G_CALLBACK (status_changed_cb), NULL);
654 do_print_or_preview (GtkAction *action, GtkPrintOperationAction print_action)
656 GtkPrintOperation *print;
657 PrintData *print_data;
659 print_data = g_new0 (PrintData, 1);
661 print_data->text = get_text ();
662 print_data->font = g_strdup ("Sans 12");
664 print = gtk_print_operation_new ();
666 gtk_print_operation_set_track_print_status (print, TRUE);
668 if (settings != NULL)
669 gtk_print_operation_set_print_settings (print, settings);
671 if (page_setup != NULL)
672 gtk_print_operation_set_default_page_setup (print, page_setup);
674 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), print_data);
675 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), print_data);
676 g_signal_connect (print, "create_custom_widget", G_CALLBACK (create_custom_widget), print_data);
677 g_signal_connect (print, "custom_widget_apply", G_CALLBACK (custom_widget_apply), print_data);
678 g_signal_connect (print, "preview", G_CALLBACK (preview_cb), print_data);
680 g_signal_connect (print, "done", G_CALLBACK (print_done), print_data);
682 gtk_print_operation_set_export_filename (print, "test.pdf");
685 gtk_print_operation_set_allow_async (print, TRUE);
687 gtk_print_operation_run (print, print_action, GTK_WINDOW (main_window), NULL);
689 g_object_unref (print);
693 do_print (GtkAction *action)
695 do_print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
699 do_preview (GtkAction *action)
701 do_print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PREVIEW);
705 do_about (GtkAction *action)
707 const gchar *authors[] = {
711 gtk_show_about_dialog (GTK_WINDOW (main_window),
712 "name", "print test editor",
714 "copyright", "(C) Red Hat, Inc",
715 "comments", "Program to demonstrate GTK+ printing.",
721 do_quit (GtkAction *action)
726 static GtkActionEntry entries[] = {
727 { "FileMenu", NULL, "_File" }, /* name, stock id, label */
728 { "HelpMenu", NULL, "_Help" }, /* name, stock id, label */
729 { "New", GTK_STOCK_NEW, /* name, stock id */
730 "_New", "<control>N", /* label, accelerator */
731 "Create a new file", /* tooltip */
732 G_CALLBACK (do_new) },
733 { "Open", GTK_STOCK_OPEN, /* name, stock id */
734 "_Open","<control>O", /* label, accelerator */
735 "Open a file", /* tooltip */
736 G_CALLBACK (do_open) },
737 { "Save", GTK_STOCK_SAVE, /* name, stock id */
738 "_Save","<control>S", /* label, accelerator */
739 "Save current file", /* tooltip */
740 G_CALLBACK (do_save) },
741 { "SaveAs", GTK_STOCK_SAVE, /* name, stock id */
742 "Save _As...", NULL, /* label, accelerator */
743 "Save to a file", /* tooltip */
744 G_CALLBACK (do_save_as) },
745 { "Quit", GTK_STOCK_QUIT, /* name, stock id */
746 "_Quit", "<control>Q", /* label, accelerator */
747 "Quit", /* tooltip */
748 G_CALLBACK (do_quit) },
749 { "About", NULL, /* name, stock id */
750 "_About", "<control>A", /* label, accelerator */
751 "About", /* tooltip */
752 G_CALLBACK (do_about) },
753 { "PageSetup", NULL, /* name, stock id */
754 "Page _Setup", NULL, /* label, accelerator */
755 "Set up the page", /* tooltip */
756 G_CALLBACK (do_page_setup) },
757 { "Preview", NULL, /* name, stock id */
758 "Print Preview", NULL, /* label, accelerator */
759 "Preview the printed document", /* tooltip */
760 G_CALLBACK (do_preview) },
761 { "Print", GTK_STOCK_PRINT, /* name, stock id */
762 NULL, NULL, /* label, accelerator */
763 "Print the document", /* tooltip */
764 G_CALLBACK (do_print) }
766 static guint n_entries = G_N_ELEMENTS (entries);
768 static const gchar *ui_info =
770 " <menubar name='MenuBar'>"
771 " <menu action='FileMenu'>"
772 " <menuitem action='New'/>"
773 " <menuitem action='Open'/>"
774 " <menuitem action='Save'/>"
775 " <menuitem action='SaveAs'/>"
776 " <menuitem action='PageSetup'/>"
777 " <menuitem action='Preview'/>"
778 " <menuitem action='Print'/>"
780 " <menuitem action='Quit'/>"
782 " <menu action='HelpMenu'>"
783 " <menuitem action='About'/>"
789 buffer_changed_callback (GtkTextBuffer *buffer)
796 mark_set_callback (GtkTextBuffer *buffer,
797 const GtkTextIter *new_location,
805 update_resize_grip (GtkWidget *widget,
806 GdkEventWindowState *event,
807 GtkStatusbar *statusbar)
809 if (event->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED |
810 GDK_WINDOW_STATE_FULLSCREEN))
814 maximized = event->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED |
815 GDK_WINDOW_STATE_FULLSCREEN);
816 gtk_statusbar_set_has_resize_grip (statusbar, !maximized);
828 GtkActionGroup *actions;
830 GtkWindowGroup *group;
832 main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
834 group = gtk_window_group_new ();
835 gtk_window_group_add_window (group, GTK_WINDOW (main_window));
836 g_object_unref (group);
838 gtk_window_set_default_size (GTK_WINDOW (main_window),
841 g_signal_connect (main_window, "delete-event",
842 G_CALLBACK (gtk_main_quit), NULL);
844 actions = gtk_action_group_new ("Actions");
845 gtk_action_group_add_actions (actions, entries, n_entries, NULL);
847 ui = gtk_ui_manager_new ();
848 gtk_ui_manager_insert_action_group (ui, actions, 0);
849 gtk_window_add_accel_group (GTK_WINDOW (main_window),
850 gtk_ui_manager_get_accel_group (ui));
851 gtk_container_set_border_width (GTK_CONTAINER (main_window), 0);
854 if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
856 g_message ("building menus failed: %s", error->message);
857 g_error_free (error);
860 table = gtk_table_new (1, 3, FALSE);
861 gtk_container_add (GTK_CONTAINER (main_window), table);
863 bar = gtk_ui_manager_get_widget (ui, "/MenuBar");
864 gtk_widget_show (bar);
865 gtk_table_attach (GTK_TABLE (table),
867 /* X direction */ /* Y direction */
869 GTK_EXPAND | GTK_FILL, 0,
872 /* Create document */
873 sw = gtk_scrolled_window_new (NULL, NULL);
875 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
876 GTK_POLICY_AUTOMATIC,
877 GTK_POLICY_AUTOMATIC);
879 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
882 gtk_table_attach (GTK_TABLE (table),
884 /* X direction */ /* Y direction */
886 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
889 contents = gtk_text_view_new ();
890 gtk_widget_grab_focus (contents);
892 gtk_container_add (GTK_CONTAINER (sw),
895 /* Create statusbar */
897 statusbar = gtk_statusbar_new ();
898 gtk_table_attach (GTK_TABLE (table),
900 /* X direction */ /* Y direction */
902 GTK_EXPAND | GTK_FILL, 0,
905 /* Show text widget info in the statusbar */
906 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents));
908 g_signal_connect_object (buffer,
910 G_CALLBACK (buffer_changed_callback),
914 g_signal_connect_object (buffer,
915 "mark_set", /* cursor moved */
916 G_CALLBACK (mark_set_callback),
920 g_signal_connect_object (main_window,
921 "window_state_event",
922 G_CALLBACK (update_resize_grip),
928 gtk_widget_show_all (main_window);
932 main (int argc, char **argv)
934 GError *error = NULL;
936 g_set_application_name ("Print editor");
937 gtk_init (&argc, &argv);
939 settings = gtk_print_settings_new_from_file ("print-settings.ini", &error);
941 g_print ("Failed to load print settings: %s\n", error->message);
942 g_clear_error (&error);
944 settings = gtk_print_settings_new ();
946 g_assert (settings != NULL);
948 page_setup = gtk_page_setup_new_from_file ("page-setup.ini", &error);
950 g_print ("Failed to load page setup: %s\n", error->message);
951 g_clear_error (&error);
961 if (!gtk_print_settings_to_file (settings, "print-settings.ini", &error)) {
962 g_print ("Failed to save print settings: %s\n", error->message);
963 g_clear_error (&error);
966 !gtk_page_setup_to_file (page_setup, "page-setup.ini", &error)) {
967 g_print ("Failed to save page setup: %s\n", error->message);
968 g_clear_error (&error);