]> Pileus Git - ~andy/gtk/blob - tests/print-editor.c
Fix build errors.
[~andy/gtk] / tests / print-editor.c
1 #include <pango/pangocairo.h>
2 #include <gtk/gtk.h>
3 #include <gtk/gtkprintoperation.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 } PrintData;
249
250 static void
251 begin_print (GtkPrintOperation *operation,
252              GtkPrintContext *context,
253              PrintData *print_data)
254 {
255   PangoFontDescription *desc;
256   PangoLayoutLine *layout_line;
257   double width, height;
258   double page_height;
259   GList *page_breaks;
260   int num_lines;
261   int line;
262
263   width = gtk_print_context_get_width (context);
264   height = gtk_print_context_get_height (context);
265
266   print_data->layout = gtk_print_context_create_layout (context);
267
268   desc = pango_font_description_from_string ("Sans 12");
269   pango_layout_set_font_description (print_data->layout, desc);
270   pango_font_description_free (desc);
271
272   pango_layout_set_width (print_data->layout, width * PANGO_SCALE);
273   
274   pango_layout_set_text (print_data->layout, print_data->text, -1);
275
276   num_lines = pango_layout_get_line_count (print_data->layout);
277
278   page_breaks = NULL;
279   page_height = 0;
280
281   for (line = 0; line < num_lines; line++)
282     {
283       PangoRectangle ink_rect, logical_rect;
284       double line_height;
285       
286       layout_line = pango_layout_get_line (print_data->layout, line);
287       pango_layout_line_get_extents (layout_line, &ink_rect, &logical_rect);
288
289       line_height = logical_rect.height / 1024.0;
290
291       if (page_height + line_height > height)
292         {
293           page_breaks = g_list_prepend (page_breaks, GINT_TO_POINTER (line));
294           page_height = 0;
295         }
296
297       page_height += line_height;
298     }
299
300   page_breaks = g_list_reverse (page_breaks);
301   gtk_print_operation_set_n_pages (operation, g_list_length (page_breaks) + 1);
302   
303   print_data->page_breaks = page_breaks;
304   
305 }
306
307 static void
308 draw_page (GtkPrintOperation *operation,
309            GtkPrintContext *context,
310            int page_nr,
311            PrintData *print_data)
312 {
313   cairo_t *cr;
314   GList *pagebreak;
315   int start, end, i;
316   PangoLayoutIter *iter;
317   double start_pos;
318   if (page_nr == 0)
319     start = 0;
320   else
321     {
322       pagebreak = g_list_nth (print_data->page_breaks, page_nr - 1);
323       start = GPOINTER_TO_INT (pagebreak->data);
324     }
325
326   pagebreak = g_list_nth (print_data->page_breaks, page_nr);
327   if (pagebreak == NULL)
328     end = pango_layout_get_line_count (print_data->layout);
329   else
330     end = GPOINTER_TO_INT (pagebreak->data);
331     
332   cr = gtk_print_context_get_cairo (context);
333
334   cairo_set_source_rgb (cr, 0, 0, 0);
335   
336   i = 0;
337   start_pos = 0;
338   iter = pango_layout_get_iter (print_data->layout);
339   do
340     {
341       PangoRectangle   logical_rect;
342       PangoLayoutLine *line;
343       int              baseline;
344
345       if (i >= start)
346         {
347           line = pango_layout_iter_get_line (iter);
348
349           pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
350           baseline = pango_layout_iter_get_baseline (iter);
351           
352           if (i == start)
353             start_pos = logical_rect.y / 1024.0;
354           
355           cairo_move_to (cr, logical_rect.x / 1024.0, baseline / 1024.0 - start_pos);
356           
357           pango_cairo_show_layout_line  (cr, line);
358         }
359       i++;
360     }
361   while (i < end &&
362          pango_layout_iter_next_line (iter));
363 }
364
365 static void
366 do_page_setup (GtkAction *action)
367 {
368   GtkPageSetup *new_page_setup;
369
370   if (settings == NULL)
371     settings = gtk_print_settings_new ();
372   
373   new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (main_window),
374                                                     page_setup, settings);
375
376   if (page_setup)
377     g_object_unref (page_setup);
378   
379   page_setup = new_page_setup;
380 }
381
382 static void
383 status_changed_cb (GtkPrintOperation *op,
384                    gpointer user_data)
385 {
386   if (gtk_print_operation_is_finished (op))
387     {
388       active_prints = g_list_remove (active_prints, op);
389       g_object_unref (op);
390     }
391   update_statusbar ();
392 }
393
394 static void
395 do_print (GtkAction *action)
396 {
397   GtkWidget *error_dialog;
398   GtkPrintOperation *print;
399   PrintData print_data;
400   GtkPrintOperationResult res;
401   GError *error;
402
403   print_data.text = get_text ();
404
405   print = gtk_print_operation_new ();
406
407   
408   if (settings != NULL)
409     gtk_print_operation_set_print_settings (print, settings);
410
411   if (page_setup != NULL)
412     gtk_print_operation_set_default_page_setup (print, page_setup);
413   
414   g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), &print_data);
415   g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), &print_data);
416
417   error = NULL;
418   res = gtk_print_operation_run (print, GTK_WINDOW (main_window), &error);
419
420   if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
421     {
422       error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
423                                              GTK_DIALOG_DESTROY_WITH_PARENT,
424                                              GTK_MESSAGE_ERROR,
425                                              GTK_BUTTONS_CLOSE,
426                                              "Error printing file:\n%s",
427                                              error->message);
428       g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
429       gtk_widget_show (error_dialog);
430       g_error_free (error);
431     }
432   else if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
433     {
434       if (settings != NULL)
435         g_object_unref (settings);
436       settings = g_object_ref (gtk_print_operation_get_print_settings (print));
437     }
438
439   if (!gtk_print_operation_is_finished (print))
440     {
441       g_object_ref (print);
442       active_prints = g_list_append (active_prints, print);
443       update_statusbar ();
444       
445       /* This ref is unref:ed when we get the final state change */
446       g_signal_connect (print, "status_changed",
447                         G_CALLBACK (status_changed_cb), NULL);
448     }
449   
450   g_object_unref (print);
451 }
452
453 static void
454 do_about (GtkAction *action)
455 {
456   const gchar *authors[] = {
457     "Alexander Larsson",
458     NULL
459   };
460   gtk_show_about_dialog (GTK_WINDOW (main_window),
461                          "name", "print test editor",
462                          "version", "0.1",
463                          "copyright", "(C) Red Hat, Inc",
464                          "comments", "Program to demonstrate GTK+ printing.",
465                          "authors", authors,
466                          NULL);
467 }
468
469 static void
470 do_quit (GtkAction *action)
471 {
472   gtk_main_quit ();
473 }
474
475 static GtkActionEntry entries[] = {
476   { "FileMenu", NULL, "_File" },               /* name, stock id, label */
477   { "HelpMenu", NULL, "_Help" },               /* name, stock id, label */
478   { "New", GTK_STOCK_NEW,                      /* name, stock id */
479     "_New", "<control>N",                      /* label, accelerator */
480     "Create a new file",                       /* tooltip */ 
481     G_CALLBACK (do_new) },      
482   { "Open", GTK_STOCK_OPEN,                    /* name, stock id */
483     "_Open","<control>O",                      /* label, accelerator */     
484     "Open a file",                             /* tooltip */
485     G_CALLBACK (do_open) }, 
486   { "Save", GTK_STOCK_SAVE,                    /* name, stock id */
487     "_Save","<control>S",                      /* label, accelerator */     
488     "Save current file",                       /* tooltip */
489     G_CALLBACK (do_save) },
490   { "SaveAs", GTK_STOCK_SAVE,                  /* name, stock id */
491     "Save _As...", NULL,                       /* label, accelerator */     
492     "Save to a file",                          /* tooltip */
493     G_CALLBACK (do_save_as) },
494   { "Quit", GTK_STOCK_QUIT,                    /* name, stock id */
495     "_Quit", "<control>Q",                     /* label, accelerator */     
496     "Quit",                                    /* tooltip */
497     G_CALLBACK (do_quit) },
498   { "About", NULL,                             /* name, stock id */
499     "_About", "<control>A",                    /* label, accelerator */     
500     "About",                                   /* tooltip */  
501     G_CALLBACK (do_about) },
502   { "PageSetup", NULL,                         /* name, stock id */
503     "Page _Setup", NULL,                       /* label, accelerator */     
504     "Set up the page",                         /* tooltip */
505     G_CALLBACK (do_page_setup) },
506   { "Print", GTK_STOCK_PRINT,                  /* name, stock id */
507      NULL, NULL,                               /* label, accelerator */     
508     "Print the document",                      /* tooltip */
509     G_CALLBACK (do_print) },
510 };
511 static guint n_entries = G_N_ELEMENTS (entries);
512
513 static const gchar *ui_info = 
514 "<ui>"
515 "  <menubar name='MenuBar'>"
516 "    <menu action='FileMenu'>"
517 "      <menuitem action='New'/>"
518 "      <menuitem action='Open'/>"
519 "      <menuitem action='Save'/>"
520 "      <menuitem action='SaveAs'/>"
521 "      <menuitem action='PageSetup'/>"
522 "      <menuitem action='Print'/>"
523 "      <separator/>"
524 "      <menuitem action='Quit'/>"
525 "    </menu>"
526 "    <menu action='HelpMenu'>"
527 "      <menuitem action='About'/>"
528 "    </menu>"
529 "  </menubar>"
530 "</ui>";
531
532 static void
533 buffer_changed_callback (GtkTextBuffer *buffer)
534 {
535   file_changed = TRUE;
536   update_statusbar ();
537 }
538
539 static void
540 mark_set_callback (GtkTextBuffer     *buffer,
541                    const GtkTextIter *new_location,
542                    GtkTextMark       *mark,
543                    gpointer           data)
544 {
545   update_statusbar ();
546 }
547
548 static void
549 update_resize_grip (GtkWidget           *widget,
550                     GdkEventWindowState *event,
551                     GtkStatusbar        *statusbar)
552 {
553   if (event->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED | 
554                              GDK_WINDOW_STATE_FULLSCREEN))
555     {
556       gboolean maximized;
557
558       maximized = event->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED | 
559                                              GDK_WINDOW_STATE_FULLSCREEN);
560       gtk_statusbar_set_has_resize_grip (statusbar, !maximized);
561     }
562 }
563
564 static void
565 create_window (void)
566 {
567   GtkWidget *bar;
568   GtkWidget *table;
569   GtkWidget *contents;
570   GtkUIManager *ui;
571   GtkWidget *sw;
572   GtkActionGroup *actions;
573   GError *error;
574   
575   main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
576
577   gtk_window_set_default_size (GTK_WINDOW (main_window),
578                                400, 600);
579   
580   g_signal_connect (main_window, "delete-event",
581                     G_CALLBACK (gtk_main_quit), NULL);
582   
583   actions = gtk_action_group_new ("Actions");
584   gtk_action_group_add_actions (actions, entries, n_entries, NULL);
585   
586   ui = gtk_ui_manager_new ();
587   gtk_ui_manager_insert_action_group (ui, actions, 0);
588   gtk_window_add_accel_group (GTK_WINDOW (main_window), 
589                               gtk_ui_manager_get_accel_group (ui));
590   gtk_container_set_border_width (GTK_CONTAINER (main_window), 0);
591
592   error = NULL;
593   if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
594     {
595       g_message ("building menus failed: %s", error->message);
596       g_error_free (error);
597     }
598
599   table = gtk_table_new (1, 3, FALSE);
600   gtk_container_add (GTK_CONTAINER (main_window), table);
601
602   bar = gtk_ui_manager_get_widget (ui, "/MenuBar");
603   gtk_widget_show (bar);
604   gtk_table_attach (GTK_TABLE (table),
605                     bar, 
606                     /* X direction */          /* Y direction */
607                     0, 1,                      0, 1,
608                     GTK_EXPAND | GTK_FILL,     0,
609                     0,                         0);
610
611   /* Create document  */
612   sw = gtk_scrolled_window_new (NULL, NULL);
613
614   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
615                                   GTK_POLICY_AUTOMATIC,
616                                   GTK_POLICY_AUTOMATIC);
617   
618   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
619                                        GTK_SHADOW_IN);
620   
621   gtk_table_attach (GTK_TABLE (table),
622                     sw,
623                     /* X direction */       /* Y direction */
624                     0, 1,                   1, 2,
625                     GTK_EXPAND | GTK_FILL,  GTK_EXPAND | GTK_FILL,
626                     0,                      0);
627   
628   contents = gtk_text_view_new ();
629   gtk_widget_grab_focus (contents);
630       
631   gtk_container_add (GTK_CONTAINER (sw),
632                      contents);
633   
634   /* Create statusbar */
635   
636   statusbar = gtk_statusbar_new ();
637   gtk_table_attach (GTK_TABLE (table),
638                     statusbar,
639                     /* X direction */       /* Y direction */
640                     0, 1,                   2, 3,
641                     GTK_EXPAND | GTK_FILL,  0,
642                     0,                      0);
643
644   /* Show text widget info in the statusbar */
645   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents));
646   
647   g_signal_connect_object (buffer,
648                            "changed",
649                            G_CALLBACK (buffer_changed_callback),
650                            NULL,
651                            0);
652   
653   g_signal_connect_object (buffer,
654                            "mark_set", /* cursor moved */
655                            G_CALLBACK (mark_set_callback),
656                            NULL,
657                            0);
658   
659   g_signal_connect_object (main_window, 
660                            "window_state_event", 
661                            G_CALLBACK (update_resize_grip),
662                            statusbar,
663                            0);
664   
665   update_ui ();
666   
667   gtk_widget_show_all (main_window);
668 }
669
670 int
671 main (int argc, char **argv)
672 {
673   gtk_init (&argc, &argv);
674
675   create_window ();
676
677   if (argc == 2)
678     load_file (argv[1]);
679   
680   gtk_main ();
681   return 0;
682 }