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