]> Pileus Git - ~andy/gtk/blob - tests/print-editor.c
tests/print-editor.c: Use accessor functions to access GtkWidget
[~andy/gtk] / tests / print-editor.c
1 #include <math.h>
2 #include <pango/pangocairo.h>
3 #include <gtk/gtk.h>
4
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;
13
14 static void
15 update_title (void)
16 {
17   char *basename;
18   char *title;
19   
20   if (filename == NULL)
21     basename = g_strdup ("Untitled");
22   else
23     basename = g_path_get_basename (filename);
24
25   title = g_strdup_printf ("Simple Editor with printing - %s", basename);
26   g_free (basename);
27   
28   gtk_window_set_title (GTK_WINDOW (main_window), title);
29   g_free (title);
30 }
31
32 static void
33 update_statusbar (void)
34 {
35   gchar *msg;
36   gint row, col;
37   GtkTextIter iter;
38   const char *print_str;
39
40   gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 0);
41   
42   gtk_text_buffer_get_iter_at_mark (buffer,
43                                     &iter,
44                                     gtk_text_buffer_get_insert (buffer));
45
46   row = gtk_text_iter_get_line (&iter);
47   col = gtk_text_iter_get_line_offset (&iter);
48
49   print_str = "";
50   if (active_prints)
51     {
52       GtkPrintOperation *op = active_prints->data;
53       print_str = gtk_print_operation_get_status_string (op);
54     }
55   
56   msg = g_strdup_printf ("%d, %d%s %s",
57                          row, col,
58                          file_changed?" - Modified":"",
59                          print_str);
60
61   gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, msg);
62
63   g_free (msg);
64 }
65
66 static void
67 update_ui (void)
68 {
69   update_title ();
70   update_statusbar ();
71 }
72
73 static char *
74 get_text (void)
75 {
76   GtkTextIter start, end;
77
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);
81 }
82
83 static void
84 set_text (const char *text, gsize len)
85 {
86   gtk_text_buffer_set_text (buffer, text, len);
87   file_changed = FALSE;
88   update_ui ();
89 }
90
91 static void
92 do_new (GtkAction *action)
93 {
94   g_free (filename);
95   filename = NULL;
96   set_text ("", 0);
97 }
98
99 static void
100 load_file (const char *open_filename)
101 {
102   GtkWidget *error_dialog;
103   char *contents;
104   GError *error;
105   gsize len;
106   
107   error_dialog = NULL;
108   error = NULL;
109   if (g_file_get_contents (open_filename, &contents, &len, &error))
110     {
111       if (g_utf8_validate (contents, len, NULL))
112         {
113           filename = g_strdup (open_filename);
114           set_text (contents, len);
115           g_free (contents);
116         }
117       else
118         {
119           error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
120                                                  GTK_DIALOG_DESTROY_WITH_PARENT,
121                                                  GTK_MESSAGE_ERROR,
122                                                  GTK_BUTTONS_CLOSE,
123                                                  "Error loading file %s:\n%s",
124                                                  open_filename,
125                                                  "Not valid utf8");
126         }
127     }
128   else
129     {
130       error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
131                                              GTK_DIALOG_DESTROY_WITH_PARENT,
132                                              GTK_MESSAGE_ERROR,
133                                              GTK_BUTTONS_CLOSE,
134                                              "Error loading file %s:\n%s",
135                                              open_filename,
136                                              error->message);
137       
138       g_error_free (error);
139     }
140   if (error_dialog)
141     {
142       g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
143       gtk_widget_show (error_dialog);
144     }
145 }
146
147 static void
148 do_open (GtkAction *action)
149 {
150   GtkWidget *dialog;
151   gint response;
152   char *open_filename;
153   
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,
159                                         NULL);
160   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
161   response = gtk_dialog_run (GTK_DIALOG (dialog));
162
163   if (response == GTK_RESPONSE_OK)
164     {
165       open_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
166       load_file (open_filename);
167       g_free (open_filename);
168     }
169
170   gtk_widget_destroy (dialog);
171 }
172
173 static void
174 save_file (const char *save_filename)
175 {
176   char *text = get_text ();
177   GtkWidget *error_dialog;
178   GError *error;
179
180   error = NULL;
181   if (g_file_set_contents (save_filename,
182                            text, -1, &error))
183     {
184       if (save_filename != filename)
185         {
186           g_free (filename);
187           filename = g_strdup (save_filename);
188         }
189       file_changed = FALSE;
190       update_ui ();
191     }
192   else
193     {
194       error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
195                                              GTK_DIALOG_DESTROY_WITH_PARENT,
196                                              GTK_MESSAGE_ERROR,
197                                              GTK_BUTTONS_CLOSE,
198                                              "Error saving to file %s:\n%s",
199                                              filename,
200                                              error->message);
201       
202       g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
203       gtk_widget_show (error_dialog);
204       
205       g_error_free (error);
206     }
207 }
208
209 static void
210 do_save_as (GtkAction *action)
211 {
212   GtkWidget *dialog;
213   gint response;
214   char *save_filename;
215   
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,
221                                         NULL);
222   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
223   response = gtk_dialog_run (GTK_DIALOG (dialog));
224
225   if (response == GTK_RESPONSE_OK)
226     {
227       save_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
228       save_file (save_filename);
229       g_free (save_filename);
230     }
231   
232   gtk_widget_destroy (dialog);
233 }
234
235 static void
236 do_save (GtkAction *action)
237 {
238   if (filename == NULL)
239     do_save_as (action);
240   else
241     save_file (filename);
242 }
243
244 typedef struct {
245   char *text;
246   PangoLayout *layout;
247   GList *page_breaks;
248   GtkWidget *font_button;
249   char *font;
250 } PrintData;
251
252 static void
253 begin_print (GtkPrintOperation *operation,
254              GtkPrintContext *context,
255              PrintData *print_data)
256 {
257   PangoFontDescription *desc;
258   PangoLayoutLine *layout_line;
259   double width, height;
260   double page_height;
261   GList *page_breaks;
262   int num_lines;
263   int line;
264
265   width = gtk_print_context_get_width (context);
266   height = gtk_print_context_get_height (context);
267
268   print_data->layout = gtk_print_context_create_pango_layout (context);
269
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);
273
274   pango_layout_set_width (print_data->layout, width * PANGO_SCALE);
275   
276   pango_layout_set_text (print_data->layout, print_data->text, -1);
277
278   num_lines = pango_layout_get_line_count (print_data->layout);
279
280   page_breaks = NULL;
281   page_height = 0;
282
283   for (line = 0; line < num_lines; line++)
284     {
285       PangoRectangle ink_rect, logical_rect;
286       double line_height;
287       
288       layout_line = pango_layout_get_line (print_data->layout, line);
289       pango_layout_line_get_extents (layout_line, &ink_rect, &logical_rect);
290
291       line_height = logical_rect.height / 1024.0;
292
293       if (page_height + line_height > height)
294         {
295           page_breaks = g_list_prepend (page_breaks, GINT_TO_POINTER (line));
296           page_height = 0;
297         }
298
299       page_height += line_height;
300     }
301
302   page_breaks = g_list_reverse (page_breaks);
303   gtk_print_operation_set_n_pages (operation, g_list_length (page_breaks) + 1);
304   
305   print_data->page_breaks = page_breaks;
306 }
307
308 static void
309 draw_page (GtkPrintOperation *operation,
310            GtkPrintContext *context,
311            int page_nr,
312            PrintData *print_data)
313 {
314   cairo_t *cr;
315   GList *pagebreak;
316   int start, end, i;
317   PangoLayoutIter *iter;
318   double start_pos;
319
320   if (page_nr == 0)
321     start = 0;
322   else
323     {
324       pagebreak = g_list_nth (print_data->page_breaks, page_nr - 1);
325       start = GPOINTER_TO_INT (pagebreak->data);
326     }
327
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);
331   else
332     end = GPOINTER_TO_INT (pagebreak->data);
333     
334   cr = gtk_print_context_get_cairo_context (context);
335
336   cairo_set_source_rgb (cr, 0, 0, 0);
337   
338   i = 0;
339   start_pos = 0;
340   iter = pango_layout_get_iter (print_data->layout);
341   do
342     {
343       PangoRectangle   logical_rect;
344       PangoLayoutLine *line;
345       int              baseline;
346
347       if (i >= start)
348         {
349           line = pango_layout_iter_get_line (iter);
350
351           pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
352           baseline = pango_layout_iter_get_baseline (iter);
353           
354           if (i == start)
355             start_pos = logical_rect.y / 1024.0;
356           
357           cairo_move_to (cr, logical_rect.x / 1024.0, baseline / 1024.0 - start_pos);
358           
359           pango_cairo_show_layout_line  (cr, line);
360         }
361       i++;
362     }
363   while (i < end &&
364          pango_layout_iter_next_line (iter));
365
366   pango_layout_iter_free (iter);
367 }
368
369 static void
370 do_page_setup (GtkAction *action)
371 {
372   GtkPageSetup *new_page_setup;
373
374   new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (main_window),
375                                                     page_setup, settings);
376
377   if (page_setup)
378     g_object_unref (page_setup);
379   
380   page_setup = new_page_setup;
381 }
382
383 static void
384 status_changed_cb (GtkPrintOperation *op,
385                    gpointer user_data)
386 {
387   if (gtk_print_operation_is_finished (op))
388     {
389       active_prints = g_list_remove (active_prints, op);
390       g_object_unref (op);
391     }
392   update_statusbar ();
393 }
394
395 static GtkWidget *
396 create_custom_widget (GtkPrintOperation *operation,
397                       PrintData *data)
398 {
399   GtkWidget *vbox, *hbox, *font, *label;
400
401   gtk_print_operation_set_custom_tab_label (operation, "Other");
402   vbox = gtk_vbox_new (FALSE, 0);
403   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
404
405   hbox = gtk_hbox_new (FALSE, 8);
406   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
407   gtk_widget_show (hbox);
408
409   label = gtk_label_new ("Font:");
410   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
411   gtk_widget_show (label);
412   
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;
417
418   return vbox;
419 }
420
421 static void
422 custom_widget_apply (GtkPrintOperation *operation,
423                      GtkWidget *widget,
424                      PrintData *data)
425 {
426   const char *selected_font;
427   selected_font = gtk_font_button_get_font_name  (GTK_FONT_BUTTON (data->font_button));
428   g_free (data->font);
429   data->font = g_strdup (selected_font);
430 }
431
432 typedef struct 
433 {
434   GtkPrintOperation *op;
435   GtkPrintOperationPreview *preview;
436   GtkWidget         *spin;
437   GtkWidget         *area;
438   gint               page;
439   PrintData *data;
440   gdouble dpi_x, dpi_y;
441 } PreviewOp;
442
443 static gboolean
444 preview_expose (GtkWidget      *widget,
445                 GdkEventExpose *event,
446                 gpointer        data)
447 {
448   PreviewOp *pop = data;
449
450   gdk_window_clear (gtk_widget_get_window (pop->area));
451   gtk_print_operation_preview_render_page (pop->preview,
452                                            pop->page - 1);
453
454   return TRUE;
455 }
456
457 static void
458 preview_ready (GtkPrintOperationPreview *preview,
459                GtkPrintContext          *context,
460                gpointer                  data)
461 {
462   PreviewOp *pop = data;
463   gint n_pages;
464
465   g_object_get (pop->op, "n-pages", &n_pages, NULL);
466
467   gtk_spin_button_set_range (GTK_SPIN_BUTTON (pop->spin), 
468                              1.0, n_pages);
469
470   g_signal_connect (pop->area, "expose_event",
471                     G_CALLBACK (preview_expose),
472                     pop);
473
474   gtk_widget_queue_draw (pop->area);
475 }
476
477 static void
478 preview_got_page_size (GtkPrintOperationPreview *preview, 
479                        GtkPrintContext          *context,
480                        GtkPageSetup             *page_setup,
481                        gpointer                  data)
482 {
483   PreviewOp *pop = data;
484   GtkAllocation allocation;
485   GtkPaperSize *paper_size;
486   double w, h;
487   cairo_t *cr;
488   gdouble dpi_x, dpi_y;
489
490   paper_size = gtk_page_setup_get_paper_size (page_setup);
491
492   w = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH);
493   h = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH);
494
495   cr = gdk_cairo_create (gtk_widget_get_window (pop->area));
496
497   gtk_widget_get_allocation (pop->area, &allocation);
498   dpi_x = allocation.width/w;
499   dpi_y = allocation.height/h;
500
501   if (fabs (dpi_x - pop->dpi_x) > 0.001 ||
502       fabs (dpi_y - pop->dpi_y) > 0.001)
503     {
504       gtk_print_context_set_cairo_context (context, cr, dpi_x, dpi_y);
505       pop->dpi_x = dpi_x;
506       pop->dpi_y = dpi_y;
507     }
508
509   pango_cairo_update_layout (cr, pop->data->layout);
510   cairo_destroy (cr);
511 }
512
513 static void
514 update_page (GtkSpinButton *widget,
515              gpointer       data)
516 {
517   PreviewOp *pop = data;
518
519   pop->page = gtk_spin_button_get_value_as_int (widget);
520   gtk_widget_queue_draw (pop->area);
521 }
522
523 static void
524 preview_destroy (GtkWindow *window, 
525                  PreviewOp *pop)
526 {
527   gtk_print_operation_preview_end_preview (pop->preview);
528   g_object_unref (pop->op);
529
530   g_free (pop);
531 }
532
533 static gboolean 
534 preview_cb (GtkPrintOperation        *op,
535             GtkPrintOperationPreview *preview,
536             GtkPrintContext          *context,
537             GtkWindow                *parent,
538             gpointer                  data)
539 {
540   GtkPrintSettings *settings;
541   GtkWidget *window, *close, *page, *hbox, *vbox, *da;
542   gdouble width, height;
543   cairo_t *cr;
544   PreviewOp *pop;
545   PrintData *print_data = data;
546
547   pop = g_new0 (PreviewOp, 1);
548
549   pop->data = print_data;
550   settings = gtk_print_operation_get_print_settings (op);
551
552   width = 200;
553   height = 300;
554   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
555   gtk_window_set_transient_for (GTK_WINDOW (window), 
556                                 GTK_WINDOW (main_window));
557   vbox = gtk_vbox_new (FALSE, 0);
558   gtk_container_add (GTK_CONTAINER (window), vbox);
559   hbox = gtk_hbox_new (FALSE, 0);
560   gtk_box_pack_start (GTK_BOX (vbox), hbox,
561                       FALSE, FALSE, 0);
562   page = gtk_spin_button_new_with_range (1, 100, 1);
563   gtk_box_pack_start (GTK_BOX (hbox), page, FALSE, FALSE, 0);
564   
565   close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
566   gtk_box_pack_start (GTK_BOX (hbox), close, FALSE, FALSE, 0);
567
568   da = gtk_drawing_area_new ();
569   gtk_widget_set_size_request (GTK_WIDGET (da), width, height);
570   gtk_box_pack_start (GTK_BOX (vbox), da, TRUE, TRUE, 0);
571
572   gtk_widget_set_double_buffered (da, FALSE);
573
574   gtk_widget_realize (da);
575
576   cr = gdk_cairo_create (gtk_widget_get_window (da));
577
578   /* TODO: What dpi to use here? This will be used for pagination.. */
579   gtk_print_context_set_cairo_context (context, cr, 72, 72);
580   cairo_destroy (cr);
581   
582   pop->op = g_object_ref (op);
583   pop->preview = preview;
584   pop->spin = page;
585   pop->area = da;
586   pop->page = 1;
587
588   g_signal_connect (page, "value-changed", 
589                     G_CALLBACK (update_page), pop);
590   g_signal_connect_swapped (close, "clicked", 
591                             G_CALLBACK (gtk_widget_destroy), window);
592
593   g_signal_connect (preview, "ready",
594                     G_CALLBACK (preview_ready), pop);
595   g_signal_connect (preview, "got-page-size",
596                     G_CALLBACK (preview_got_page_size), pop);
597
598   g_signal_connect (window, "destroy", 
599                     G_CALLBACK (preview_destroy), pop);
600                             
601   gtk_widget_show_all (window);
602   
603   return TRUE;
604 }
605
606 static void
607 print_done (GtkPrintOperation *op,
608             GtkPrintOperationResult res,
609             PrintData *print_data)
610 {
611   GError *error = NULL;
612
613   if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
614     {
615
616       GtkWidget *error_dialog;
617       
618       gtk_print_operation_get_error (op, &error);
619       
620       error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
621                                              GTK_DIALOG_DESTROY_WITH_PARENT,
622                                              GTK_MESSAGE_ERROR,
623                                              GTK_BUTTONS_CLOSE,
624                                              "Error printing file:\n%s",
625                                              error ? error->message : "no details");
626       g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
627       gtk_widget_show (error_dialog);
628     }
629   else if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
630     {
631       if (settings != NULL)
632         g_object_unref (settings);
633       settings = g_object_ref (gtk_print_operation_get_print_settings (op));
634     }
635
636   g_free (print_data->text);
637   g_free (print_data->font);
638   g_free (print_data);
639   
640   if (!gtk_print_operation_is_finished (op))
641     {
642       g_object_ref (op);
643       active_prints = g_list_append (active_prints, op);
644       update_statusbar ();
645       
646       /* This ref is unref:ed when we get the final state change */
647       g_signal_connect (op, "status_changed",
648                         G_CALLBACK (status_changed_cb), NULL);
649     }
650 }
651
652 static void
653 end_print (GtkPrintOperation *op, GtkPrintContext *context, PrintData *print_data)
654 {
655   g_list_free (print_data->page_breaks);
656   print_data->page_breaks = NULL;
657   g_object_unref (print_data->layout);
658   print_data->layout = NULL;
659 }
660
661 static void
662 do_print_or_preview (GtkAction *action, GtkPrintOperationAction print_action)
663 {
664   GtkPrintOperation *print;
665   PrintData *print_data;
666
667   print_data = g_new0 (PrintData, 1);
668
669   print_data->text = get_text ();
670   print_data->font = g_strdup ("Sans 12");
671
672   print = gtk_print_operation_new ();
673
674   gtk_print_operation_set_track_print_status (print, TRUE);
675   
676   if (settings != NULL)
677     gtk_print_operation_set_print_settings (print, settings);
678
679   if (page_setup != NULL)
680     gtk_print_operation_set_default_page_setup (print, page_setup);
681   
682   g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), print_data);
683   g_signal_connect (print, "end-print", G_CALLBACK (end_print), print_data);
684   g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), print_data);
685   g_signal_connect (print, "create_custom_widget", G_CALLBACK (create_custom_widget), print_data);
686   g_signal_connect (print, "custom_widget_apply", G_CALLBACK (custom_widget_apply), print_data);
687   g_signal_connect (print, "preview", G_CALLBACK (preview_cb), print_data);
688
689   g_signal_connect (print, "done", G_CALLBACK (print_done), print_data);
690
691   gtk_print_operation_set_export_filename (print, "test.pdf");
692
693 #if 0
694   gtk_print_operation_set_allow_async (print, TRUE);
695 #endif
696   gtk_print_operation_run (print, print_action, GTK_WINDOW (main_window), NULL);
697
698   g_object_unref (print);
699 }
700
701 static void
702 do_print (GtkAction *action)
703 {
704   do_print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
705 }
706
707 static void
708 do_preview (GtkAction *action)
709 {
710   do_print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PREVIEW);
711 }
712
713 static void
714 do_about (GtkAction *action)
715 {
716   const gchar *authors[] = {
717     "Alexander Larsson",
718     NULL
719   };
720   gtk_show_about_dialog (GTK_WINDOW (main_window),
721                          "name", "print test editor",
722                          "version", "0.1",
723                          "copyright", "(C) Red Hat, Inc",
724                          "comments", "Program to demonstrate GTK+ printing.",
725                          "authors", authors,
726                          NULL);
727 }
728
729 static void
730 do_quit (GtkAction *action)
731 {
732   gtk_main_quit ();
733 }
734
735 static GtkActionEntry entries[] = {
736   { "FileMenu", NULL, "_File" },               /* name, stock id, label */
737   { "HelpMenu", NULL, "_Help" },               /* name, stock id, label */
738   { "New", GTK_STOCK_NEW,                      /* name, stock id */
739     "_New", "<control>N",                      /* label, accelerator */
740     "Create a new file",                       /* tooltip */ 
741     G_CALLBACK (do_new) },      
742   { "Open", GTK_STOCK_OPEN,                    /* name, stock id */
743     "_Open","<control>O",                      /* label, accelerator */     
744     "Open a file",                             /* tooltip */
745     G_CALLBACK (do_open) }, 
746   { "Save", GTK_STOCK_SAVE,                    /* name, stock id */
747     "_Save","<control>S",                      /* label, accelerator */     
748     "Save current file",                       /* tooltip */
749     G_CALLBACK (do_save) },
750   { "SaveAs", GTK_STOCK_SAVE,                  /* name, stock id */
751     "Save _As...", NULL,                       /* label, accelerator */     
752     "Save to a file",                          /* tooltip */
753     G_CALLBACK (do_save_as) },
754   { "Quit", GTK_STOCK_QUIT,                    /* name, stock id */
755     "_Quit", "<control>Q",                     /* label, accelerator */     
756     "Quit",                                    /* tooltip */
757     G_CALLBACK (do_quit) },
758   { "About", NULL,                             /* name, stock id */
759     "_About", "<control>A",                    /* label, accelerator */     
760     "About",                                   /* tooltip */  
761     G_CALLBACK (do_about) },
762   { "PageSetup", NULL,                         /* name, stock id */
763     "Page _Setup", NULL,                       /* label, accelerator */     
764     "Set up the page",                         /* tooltip */
765     G_CALLBACK (do_page_setup) },
766   { "Preview", NULL,                           /* name, stock id */
767     "Print Preview", NULL,                     /* label, accelerator */     
768     "Preview the printed document",            /* tooltip */
769     G_CALLBACK (do_preview) },
770   { "Print", GTK_STOCK_PRINT,                  /* name, stock id */
771      NULL, NULL,                               /* label, accelerator */     
772     "Print the document",                      /* tooltip */
773     G_CALLBACK (do_print) }
774 };
775 static guint n_entries = G_N_ELEMENTS (entries);
776
777 static const gchar *ui_info = 
778 "<ui>"
779 "  <menubar name='MenuBar'>"
780 "    <menu action='FileMenu'>"
781 "      <menuitem action='New'/>"
782 "      <menuitem action='Open'/>"
783 "      <menuitem action='Save'/>"
784 "      <menuitem action='SaveAs'/>"
785 "      <menuitem action='PageSetup'/>"
786 "      <menuitem action='Preview'/>"
787 "      <menuitem action='Print'/>"
788 "      <separator/>"
789 "      <menuitem action='Quit'/>"
790 "    </menu>"
791 "    <menu action='HelpMenu'>"
792 "      <menuitem action='About'/>"
793 "    </menu>"
794 "  </menubar>"
795 "</ui>";
796
797 static void
798 buffer_changed_callback (GtkTextBuffer *buffer)
799 {
800   file_changed = TRUE;
801   update_statusbar ();
802 }
803
804 static void
805 mark_set_callback (GtkTextBuffer     *buffer,
806                    const GtkTextIter *new_location,
807                    GtkTextMark       *mark,
808                    gpointer           data)
809 {
810   update_statusbar ();
811 }
812
813 static void
814 update_resize_grip (GtkWidget           *widget,
815                     GdkEventWindowState *event,
816                     GtkStatusbar        *statusbar)
817 {
818   if (event->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED | 
819                              GDK_WINDOW_STATE_FULLSCREEN))
820     {
821       gboolean maximized;
822
823       maximized = event->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED | 
824                                              GDK_WINDOW_STATE_FULLSCREEN);
825       gtk_statusbar_set_has_resize_grip (statusbar, !maximized);
826     }
827 }
828
829 static void
830 create_window (void)
831 {
832   GtkWidget *bar;
833   GtkWidget *table;
834   GtkWidget *contents;
835   GtkUIManager *ui;
836   GtkWidget *sw;
837   GtkActionGroup *actions;
838   GError *error;
839   GtkWindowGroup *group;
840   
841   main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
842
843   group = gtk_window_group_new ();
844   gtk_window_group_add_window (group, GTK_WINDOW (main_window));
845   g_object_unref (group);
846
847   gtk_window_set_default_size (GTK_WINDOW (main_window),
848                                400, 600);
849   
850   g_signal_connect (main_window, "delete-event",
851                     G_CALLBACK (gtk_main_quit), NULL);
852   
853   actions = gtk_action_group_new ("Actions");
854   gtk_action_group_add_actions (actions, entries, n_entries, NULL);
855   
856   ui = gtk_ui_manager_new ();
857   gtk_ui_manager_insert_action_group (ui, actions, 0);
858   gtk_window_add_accel_group (GTK_WINDOW (main_window), 
859                               gtk_ui_manager_get_accel_group (ui));
860   gtk_container_set_border_width (GTK_CONTAINER (main_window), 0);
861
862   error = NULL;
863   if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
864     {
865       g_message ("building menus failed: %s", error->message);
866       g_error_free (error);
867     }
868
869   table = gtk_table_new (1, 3, FALSE);
870   gtk_container_add (GTK_CONTAINER (main_window), table);
871
872   bar = gtk_ui_manager_get_widget (ui, "/MenuBar");
873   gtk_widget_show (bar);
874   gtk_table_attach (GTK_TABLE (table),
875                     bar, 
876                     /* X direction */          /* Y direction */
877                     0, 1,                      0, 1,
878                     GTK_EXPAND | GTK_FILL,     0,
879                     0,                         0);
880
881   /* Create document  */
882   sw = gtk_scrolled_window_new (NULL, NULL);
883
884   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
885                                   GTK_POLICY_AUTOMATIC,
886                                   GTK_POLICY_AUTOMATIC);
887   
888   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
889                                        GTK_SHADOW_IN);
890   
891   gtk_table_attach (GTK_TABLE (table),
892                     sw,
893                     /* X direction */       /* Y direction */
894                     0, 1,                   1, 2,
895                     GTK_EXPAND | GTK_FILL,  GTK_EXPAND | GTK_FILL,
896                     0,                      0);
897   
898   contents = gtk_text_view_new ();
899   gtk_widget_grab_focus (contents);
900       
901   gtk_container_add (GTK_CONTAINER (sw),
902                      contents);
903   
904   /* Create statusbar */
905   
906   statusbar = gtk_statusbar_new ();
907   gtk_table_attach (GTK_TABLE (table),
908                     statusbar,
909                     /* X direction */       /* Y direction */
910                     0, 1,                   2, 3,
911                     GTK_EXPAND | GTK_FILL,  0,
912                     0,                      0);
913
914   /* Show text widget info in the statusbar */
915   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents));
916   
917   g_signal_connect_object (buffer,
918                            "changed",
919                            G_CALLBACK (buffer_changed_callback),
920                            NULL,
921                            0);
922   
923   g_signal_connect_object (buffer,
924                            "mark_set", /* cursor moved */
925                            G_CALLBACK (mark_set_callback),
926                            NULL,
927                            0);
928   
929   g_signal_connect_object (main_window, 
930                            "window_state_event", 
931                            G_CALLBACK (update_resize_grip),
932                            statusbar,
933                            0);
934   
935   update_ui ();
936   
937   gtk_widget_show_all (main_window);
938 }
939
940 int
941 main (int argc, char **argv)
942 {
943   GError *error = NULL;
944
945   g_set_application_name ("Print editor");
946   gtk_init (&argc, &argv);
947
948   settings = gtk_print_settings_new_from_file ("print-settings.ini", &error);
949   if (error) {
950     g_print ("Failed to load print settings: %s\n", error->message);
951     g_clear_error (&error);
952
953     settings = gtk_print_settings_new ();
954   }
955   g_assert (settings != NULL);
956
957   page_setup = gtk_page_setup_new_from_file ("page-setup.ini", &error);
958   if (error) {
959     g_print ("Failed to load page setup: %s\n", error->message);
960     g_clear_error (&error);
961   }
962
963   create_window ();
964
965   if (argc == 2)
966     load_file (argv[1]);
967   
968   gtk_main ();
969
970   if (!gtk_print_settings_to_file (settings, "print-settings.ini", &error)) {
971     g_print ("Failed to save print settings: %s\n", error->message);
972     g_clear_error (&error);
973   }
974   if (page_setup &&
975       !gtk_page_setup_to_file (page_setup, "page-setup.ini", &error)) {
976     g_print ("Failed to save page setup: %s\n", error->message);
977     g_clear_error (&error);
978   }
979
980   return 0;
981 }