2 #include <pango/pangocairo.h>
5 static GtkWidget *main_window;
6 static char *filename = NULL;
7 static GtkPageSetup *page_setup = NULL;
8 static GtkPrintSettings *settings = NULL;
9 static gboolean file_changed = FALSE;
10 static GtkTextBuffer *buffer;
11 static GtkWidget *statusbar;
12 static GList *active_prints = NULL;
21 basename = g_strdup ("Untitled");
23 basename = g_path_get_basename (filename);
25 title = g_strdup_printf ("Simple Editor with printing - %s", basename);
28 gtk_window_set_title (GTK_WINDOW (main_window), title);
33 update_statusbar (void)
38 const char *print_str;
40 gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 0);
42 gtk_text_buffer_get_iter_at_mark (buffer,
44 gtk_text_buffer_get_insert (buffer));
46 row = gtk_text_iter_get_line (&iter);
47 col = gtk_text_iter_get_line_offset (&iter);
52 GtkPrintOperation *op = active_prints->data;
53 print_str = gtk_print_operation_get_status_string (op);
56 msg = g_strdup_printf ("%d, %d%s %s",
58 file_changed?" - Modified":"",
61 gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, msg);
76 GtkTextIter start, end;
78 gtk_text_buffer_get_start_iter (buffer, &start);
79 gtk_text_buffer_get_end_iter (buffer, &end);
80 return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
84 set_text (const char *text, gsize len)
86 gtk_text_buffer_set_text (buffer, text, len);
92 do_new (GtkAction *action)
100 load_file (const char *open_filename)
102 GtkWidget *error_dialog;
109 if (g_file_get_contents (open_filename, &contents, &len, &error))
111 if (g_utf8_validate (contents, len, NULL))
113 filename = g_strdup (open_filename);
114 set_text (contents, len);
119 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
120 GTK_DIALOG_DESTROY_WITH_PARENT,
123 "Error loading file %s:\n%s",
130 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
131 GTK_DIALOG_DESTROY_WITH_PARENT,
134 "Error loading file %s:\n%s",
138 g_error_free (error);
142 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
143 gtk_widget_show (error_dialog);
148 do_open (GtkAction *action)
154 dialog = gtk_file_chooser_dialog_new ("Select file",
155 GTK_WINDOW (main_window),
156 GTK_FILE_CHOOSER_ACTION_OPEN,
157 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
158 GTK_STOCK_OPEN, GTK_RESPONSE_OK,
160 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
161 response = gtk_dialog_run (GTK_DIALOG (dialog));
163 if (response == GTK_RESPONSE_OK)
165 open_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
166 load_file (open_filename);
167 g_free (open_filename);
170 gtk_widget_destroy (dialog);
174 save_file (const char *save_filename)
176 char *text = get_text ();
177 GtkWidget *error_dialog;
181 if (g_file_set_contents (save_filename,
184 if (save_filename != filename)
187 filename = g_strdup (save_filename);
189 file_changed = FALSE;
194 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
195 GTK_DIALOG_DESTROY_WITH_PARENT,
198 "Error saving to file %s:\n%s",
202 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
203 gtk_widget_show (error_dialog);
205 g_error_free (error);
210 do_save_as (GtkAction *action)
216 dialog = gtk_file_chooser_dialog_new ("Select file",
217 GTK_WINDOW (main_window),
218 GTK_FILE_CHOOSER_ACTION_SAVE,
219 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
220 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
222 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
223 response = gtk_dialog_run (GTK_DIALOG (dialog));
225 if (response == GTK_RESPONSE_OK)
227 save_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
228 save_file (save_filename);
229 g_free (save_filename);
232 gtk_widget_destroy (dialog);
236 do_save (GtkAction *action)
238 if (filename == NULL)
241 save_file (filename);
248 GtkWidget *font_button;
253 begin_print (GtkPrintOperation *operation,
254 GtkPrintContext *context,
255 PrintData *print_data)
257 PangoFontDescription *desc;
258 PangoLayoutLine *layout_line;
259 double width, height;
265 width = gtk_print_context_get_width (context);
266 height = gtk_print_context_get_height (context);
268 print_data->layout = gtk_print_context_create_pango_layout (context);
270 desc = pango_font_description_from_string (print_data->font);
271 pango_layout_set_font_description (print_data->layout, desc);
272 pango_font_description_free (desc);
274 pango_layout_set_width (print_data->layout, width * PANGO_SCALE);
276 pango_layout_set_text (print_data->layout, print_data->text, -1);
278 num_lines = pango_layout_get_line_count (print_data->layout);
283 for (line = 0; line < num_lines; line++)
285 PangoRectangle ink_rect, logical_rect;
288 layout_line = pango_layout_get_line (print_data->layout, line);
289 pango_layout_line_get_extents (layout_line, &ink_rect, &logical_rect);
291 line_height = logical_rect.height / 1024.0;
293 if (page_height + line_height > height)
295 page_breaks = g_list_prepend (page_breaks, GINT_TO_POINTER (line));
299 page_height += line_height;
302 page_breaks = g_list_reverse (page_breaks);
303 gtk_print_operation_set_n_pages (operation, g_list_length (page_breaks) + 1);
305 print_data->page_breaks = page_breaks;
309 draw_page (GtkPrintOperation *operation,
310 GtkPrintContext *context,
312 PrintData *print_data)
317 PangoLayoutIter *iter;
324 pagebreak = g_list_nth (print_data->page_breaks, page_nr - 1);
325 start = GPOINTER_TO_INT (pagebreak->data);
328 pagebreak = g_list_nth (print_data->page_breaks, page_nr);
329 if (pagebreak == NULL)
330 end = pango_layout_get_line_count (print_data->layout);
332 end = GPOINTER_TO_INT (pagebreak->data);
334 cr = gtk_print_context_get_cairo_context (context);
336 cairo_set_source_rgb (cr, 0, 0, 0);
340 iter = pango_layout_get_iter (print_data->layout);
343 PangoRectangle logical_rect;
344 PangoLayoutLine *line;
349 line = pango_layout_iter_get_line (iter);
351 pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
352 baseline = pango_layout_iter_get_baseline (iter);
355 start_pos = logical_rect.y / 1024.0;
357 cairo_move_to (cr, logical_rect.x / 1024.0, baseline / 1024.0 - start_pos);
359 pango_cairo_show_layout_line (cr, line);
364 pango_layout_iter_next_line (iter));
366 pango_layout_iter_free (iter);
370 do_page_setup (GtkAction *action)
372 GtkPageSetup *new_page_setup;
374 new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (main_window),
375 page_setup, settings);
378 g_object_unref (page_setup);
380 page_setup = new_page_setup;
384 status_changed_cb (GtkPrintOperation *op,
387 if (gtk_print_operation_is_finished (op))
389 active_prints = g_list_remove (active_prints, op);
396 create_custom_widget (GtkPrintOperation *operation,
399 GtkWidget *vbox, *hbox, *font, *label;
401 gtk_print_operation_set_custom_tab_label (operation, "Other");
402 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
403 gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
405 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 8);
406 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
407 gtk_widget_show (hbox);
409 label = gtk_label_new ("Font:");
410 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
411 gtk_widget_show (label);
413 font = gtk_font_button_new_with_font (data->font);
414 gtk_box_pack_start (GTK_BOX (hbox), font, FALSE, FALSE, 0);
415 gtk_widget_show (font);
416 data->font_button = font;
422 custom_widget_apply (GtkPrintOperation *operation,
426 const char *selected_font;
427 selected_font = gtk_font_button_get_font_name (GTK_FONT_BUTTON (data->font_button));
429 data->font = g_strdup (selected_font);
434 GtkPrintOperation *op;
435 GtkPrintOperationPreview *preview;
436 GtkPrintContext *context;
441 gdouble dpi_x, dpi_y;
445 preview_draw (GtkWidget *widget,
449 PreviewOp *pop = data;
453 prev_cr = gtk_print_context_get_cairo_context (pop->context);
454 cairo_reference (prev_cr);
455 dpi_x = gtk_print_context_get_dpi_x (pop->context);
456 dpi_y = gtk_print_context_get_dpi_y (pop->context);
458 gtk_print_context_set_cairo_context (pop->context,
460 gtk_print_operation_preview_render_page (pop->preview,
462 gtk_print_context_set_cairo_context (pop->context,
463 prev_cr, dpi_x, dpi_y);
464 cairo_destroy (prev_cr);
470 preview_ready (GtkPrintOperationPreview *preview,
471 GtkPrintContext *context,
474 PreviewOp *pop = data;
477 g_object_get (pop->op, "n-pages", &n_pages, NULL);
479 gtk_spin_button_set_range (GTK_SPIN_BUTTON (pop->spin),
482 g_signal_connect (pop->area, "draw",
483 G_CALLBACK (preview_draw),
486 gtk_widget_queue_draw (pop->area);
490 preview_got_page_size (GtkPrintOperationPreview *preview,
491 GtkPrintContext *context,
492 GtkPageSetup *page_setup,
495 PreviewOp *pop = data;
496 GtkAllocation allocation;
497 GtkPaperSize *paper_size;
500 gdouble dpi_x, dpi_y;
502 paper_size = gtk_page_setup_get_paper_size (page_setup);
504 w = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH);
505 h = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH);
507 cr = gdk_cairo_create (gtk_widget_get_window (pop->area));
509 gtk_widget_get_allocation (pop->area, &allocation);
510 dpi_x = allocation.width/w;
511 dpi_y = allocation.height/h;
513 if (fabs (dpi_x - pop->dpi_x) > 0.001 ||
514 fabs (dpi_y - pop->dpi_y) > 0.001)
516 gtk_print_context_set_cairo_context (context, cr, dpi_x, dpi_y);
521 pango_cairo_update_layout (cr, pop->data->layout);
526 update_page (GtkSpinButton *widget,
529 PreviewOp *pop = data;
531 pop->page = gtk_spin_button_get_value_as_int (widget);
532 gtk_widget_queue_draw (pop->area);
536 preview_destroy (GtkWindow *window,
539 gtk_print_operation_preview_end_preview (pop->preview);
540 g_object_unref (pop->op);
546 preview_cb (GtkPrintOperation *op,
547 GtkPrintOperationPreview *preview,
548 GtkPrintContext *context,
552 GtkPrintSettings *settings;
553 GtkWidget *window, *close, *page, *hbox, *vbox, *da;
554 gdouble width, height;
557 PrintData *print_data = data;
559 pop = g_new0 (PreviewOp, 1);
561 pop->data = print_data;
562 settings = gtk_print_operation_get_print_settings (op);
566 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
567 gtk_window_set_transient_for (GTK_WINDOW (window),
568 GTK_WINDOW (main_window));
569 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
570 gtk_container_add (GTK_CONTAINER (window), vbox);
571 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
572 gtk_box_pack_start (GTK_BOX (vbox), hbox,
574 page = gtk_spin_button_new_with_range (1, 100, 1);
575 gtk_box_pack_start (GTK_BOX (hbox), page, FALSE, FALSE, 0);
577 close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
578 gtk_box_pack_start (GTK_BOX (hbox), close, FALSE, FALSE, 0);
580 da = gtk_drawing_area_new ();
581 gtk_widget_set_size_request (GTK_WIDGET (da), width, height);
582 gtk_box_pack_start (GTK_BOX (vbox), da, TRUE, TRUE, 0);
584 gtk_widget_realize (da);
586 cr = gdk_cairo_create (gtk_widget_get_window (da));
587 /* TODO: What dpi to use here? This will be used for pagination.. */
588 gtk_print_context_set_cairo_context (context, cr, 72, 72);
591 pop->op = g_object_ref (op);
592 pop->preview = preview;
593 pop->context = context;
598 g_signal_connect (page, "value-changed",
599 G_CALLBACK (update_page), pop);
600 g_signal_connect_swapped (close, "clicked",
601 G_CALLBACK (gtk_widget_destroy), window);
603 g_signal_connect (preview, "ready",
604 G_CALLBACK (preview_ready), pop);
605 g_signal_connect (preview, "got-page-size",
606 G_CALLBACK (preview_got_page_size), pop);
608 g_signal_connect (window, "destroy",
609 G_CALLBACK (preview_destroy), pop);
611 gtk_widget_show_all (window);
617 print_done (GtkPrintOperation *op,
618 GtkPrintOperationResult res,
619 PrintData *print_data)
621 GError *error = NULL;
623 if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
626 GtkWidget *error_dialog;
628 gtk_print_operation_get_error (op, &error);
630 error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
631 GTK_DIALOG_DESTROY_WITH_PARENT,
634 "Error printing file:\n%s",
635 error ? error->message : "no details");
636 g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
637 gtk_widget_show (error_dialog);
639 else if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
641 if (settings != NULL)
642 g_object_unref (settings);
643 settings = g_object_ref (gtk_print_operation_get_print_settings (op));
646 g_free (print_data->text);
647 g_free (print_data->font);
650 if (!gtk_print_operation_is_finished (op))
653 active_prints = g_list_append (active_prints, op);
656 /* This ref is unref:ed when we get the final state change */
657 g_signal_connect (op, "status_changed",
658 G_CALLBACK (status_changed_cb), NULL);
663 end_print (GtkPrintOperation *op, GtkPrintContext *context, PrintData *print_data)
665 g_list_free (print_data->page_breaks);
666 print_data->page_breaks = NULL;
667 g_object_unref (print_data->layout);
668 print_data->layout = NULL;
672 do_print_or_preview (GtkAction *action, GtkPrintOperationAction print_action)
674 GtkPrintOperation *print;
675 PrintData *print_data;
677 print_data = g_new0 (PrintData, 1);
679 print_data->text = get_text ();
680 print_data->font = g_strdup ("Sans 12");
682 print = gtk_print_operation_new ();
684 gtk_print_operation_set_track_print_status (print, TRUE);
686 if (settings != NULL)
687 gtk_print_operation_set_print_settings (print, settings);
689 if (page_setup != NULL)
690 gtk_print_operation_set_default_page_setup (print, page_setup);
692 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), print_data);
693 g_signal_connect (print, "end-print", G_CALLBACK (end_print), print_data);
694 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), print_data);
695 g_signal_connect (print, "create_custom_widget", G_CALLBACK (create_custom_widget), print_data);
696 g_signal_connect (print, "custom_widget_apply", G_CALLBACK (custom_widget_apply), print_data);
697 g_signal_connect (print, "preview", G_CALLBACK (preview_cb), print_data);
699 g_signal_connect (print, "done", G_CALLBACK (print_done), print_data);
701 gtk_print_operation_set_export_filename (print, "test.pdf");
704 gtk_print_operation_set_allow_async (print, TRUE);
706 gtk_print_operation_run (print, print_action, GTK_WINDOW (main_window), NULL);
708 g_object_unref (print);
712 do_print (GtkAction *action)
714 do_print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
718 do_preview (GtkAction *action)
720 do_print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PREVIEW);
724 do_about (GtkAction *action)
726 const gchar *authors[] = {
730 gtk_show_about_dialog (GTK_WINDOW (main_window),
731 "name", "print test editor",
733 "copyright", "(C) Red Hat, Inc",
734 "comments", "Program to demonstrate GTK+ printing.",
740 do_quit (GtkAction *action)
745 static GtkActionEntry entries[] = {
746 { "FileMenu", NULL, "_File" }, /* name, stock id, label */
747 { "HelpMenu", NULL, "_Help" }, /* name, stock id, label */
748 { "New", GTK_STOCK_NEW, /* name, stock id */
749 "_New", "<control>N", /* label, accelerator */
750 "Create a new file", /* tooltip */
751 G_CALLBACK (do_new) },
752 { "Open", GTK_STOCK_OPEN, /* name, stock id */
753 "_Open","<control>O", /* label, accelerator */
754 "Open a file", /* tooltip */
755 G_CALLBACK (do_open) },
756 { "Save", GTK_STOCK_SAVE, /* name, stock id */
757 "_Save","<control>S", /* label, accelerator */
758 "Save current file", /* tooltip */
759 G_CALLBACK (do_save) },
760 { "SaveAs", GTK_STOCK_SAVE, /* name, stock id */
761 "Save _As...", NULL, /* label, accelerator */
762 "Save to a file", /* tooltip */
763 G_CALLBACK (do_save_as) },
764 { "Quit", GTK_STOCK_QUIT, /* name, stock id */
765 "_Quit", "<control>Q", /* label, accelerator */
766 "Quit", /* tooltip */
767 G_CALLBACK (do_quit) },
768 { "About", NULL, /* name, stock id */
769 "_About", "<control>A", /* label, accelerator */
770 "About", /* tooltip */
771 G_CALLBACK (do_about) },
772 { "PageSetup", NULL, /* name, stock id */
773 "Page _Setup", NULL, /* label, accelerator */
774 "Set up the page", /* tooltip */
775 G_CALLBACK (do_page_setup) },
776 { "Preview", NULL, /* name, stock id */
777 "Print Preview", NULL, /* label, accelerator */
778 "Preview the printed document", /* tooltip */
779 G_CALLBACK (do_preview) },
780 { "Print", GTK_STOCK_PRINT, /* name, stock id */
781 NULL, NULL, /* label, accelerator */
782 "Print the document", /* tooltip */
783 G_CALLBACK (do_print) }
785 static guint n_entries = G_N_ELEMENTS (entries);
787 static const gchar *ui_info =
789 " <menubar name='MenuBar'>"
790 " <menu action='FileMenu'>"
791 " <menuitem action='New'/>"
792 " <menuitem action='Open'/>"
793 " <menuitem action='Save'/>"
794 " <menuitem action='SaveAs'/>"
795 " <menuitem action='PageSetup'/>"
796 " <menuitem action='Preview'/>"
797 " <menuitem action='Print'/>"
799 " <menuitem action='Quit'/>"
801 " <menu action='HelpMenu'>"
802 " <menuitem action='About'/>"
808 buffer_changed_callback (GtkTextBuffer *buffer)
815 mark_set_callback (GtkTextBuffer *buffer,
816 const GtkTextIter *new_location,
831 GtkActionGroup *actions;
833 GtkWindowGroup *group;
835 main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
837 group = gtk_window_group_new ();
838 gtk_window_group_add_window (group, GTK_WINDOW (main_window));
839 g_object_unref (group);
841 gtk_window_set_default_size (GTK_WINDOW (main_window),
844 g_signal_connect (main_window, "delete-event",
845 G_CALLBACK (gtk_main_quit), NULL);
847 actions = gtk_action_group_new ("Actions");
848 gtk_action_group_add_actions (actions, entries, n_entries, NULL);
850 ui = gtk_ui_manager_new ();
851 gtk_ui_manager_insert_action_group (ui, actions, 0);
852 gtk_window_add_accel_group (GTK_WINDOW (main_window),
853 gtk_ui_manager_get_accel_group (ui));
854 gtk_container_set_border_width (GTK_CONTAINER (main_window), 0);
857 if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
859 g_message ("building menus failed: %s", error->message);
860 g_error_free (error);
863 table = gtk_table_new (1, 3, FALSE);
864 gtk_container_add (GTK_CONTAINER (main_window), table);
866 bar = gtk_ui_manager_get_widget (ui, "/MenuBar");
867 gtk_widget_show (bar);
868 gtk_table_attach (GTK_TABLE (table),
870 /* X direction */ /* Y direction */
872 GTK_EXPAND | GTK_FILL, 0,
875 /* Create document */
876 sw = gtk_scrolled_window_new (NULL, NULL);
878 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
879 GTK_POLICY_AUTOMATIC,
880 GTK_POLICY_AUTOMATIC);
882 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
885 gtk_table_attach (GTK_TABLE (table),
887 /* X direction */ /* Y direction */
889 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL,
892 contents = gtk_text_view_new ();
893 gtk_widget_grab_focus (contents);
895 gtk_container_add (GTK_CONTAINER (sw),
898 /* Create statusbar */
900 statusbar = gtk_statusbar_new ();
901 gtk_table_attach (GTK_TABLE (table),
903 /* X direction */ /* Y direction */
905 GTK_EXPAND | GTK_FILL, 0,
908 /* Show text widget info in the statusbar */
909 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents));
911 g_signal_connect_object (buffer,
913 G_CALLBACK (buffer_changed_callback),
917 g_signal_connect_object (buffer,
918 "mark_set", /* cursor moved */
919 G_CALLBACK (mark_set_callback),
925 gtk_widget_show_all (main_window);
929 main (int argc, char **argv)
931 GError *error = NULL;
933 g_set_application_name ("Print editor");
934 gtk_init (&argc, &argv);
936 settings = gtk_print_settings_new_from_file ("print-settings.ini", &error);
938 g_print ("Failed to load print settings: %s\n", error->message);
939 g_clear_error (&error);
941 settings = gtk_print_settings_new ();
943 g_assert (settings != NULL);
945 page_setup = gtk_page_setup_new_from_file ("page-setup.ini", &error);
947 g_print ("Failed to load page setup: %s\n", error->message);
948 g_clear_error (&error);
958 if (!gtk_print_settings_to_file (settings, "print-settings.ini", &error)) {
959 g_print ("Failed to save print settings: %s\n", error->message);
960 g_clear_error (&error);
963 !gtk_page_setup_to_file (page_setup, "page-setup.ini", &error)) {
964 g_print ("Failed to save page setup: %s\n", error->message);
965 g_clear_error (&error);