1 /*
2  * Copyright © 2004 Noah Levitt
3  * Copyright © 2007, 2008 Christian Persch
4  * Copyright © 2012 Red Hat, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
19  */
20 
21 #include <config.h>
22 
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <glib/gi18n-lib.h>
27 #include <gtk/gtk.h>
28 
29 #include "gucharmap-print-operation.h"
30 #include "gucharmap-search-dialog.h"
31 #include "gucharmap-settings.h"
32 #include "gucharmap-window.h"
33 
34 #define FONT_CHANGE_FACTOR (1.189207115f) /* 2^(0.25) */
35 
36 /* #define ENABLE_PRINTING */
37 
38 static void gucharmap_window_class_init (GucharmapWindowClass *klass);
39 static void gucharmap_window_init       (GucharmapWindow *window);
40 
G_DEFINE_TYPE(GucharmapWindow,gucharmap_window,GTK_TYPE_APPLICATION_WINDOW)41 G_DEFINE_TYPE (GucharmapWindow, gucharmap_window, GTK_TYPE_APPLICATION_WINDOW)
42 
43 static void
44 show_error_dialog (GtkWindow *parent,
45                    GError *error)
46 {
47   GtkWidget *dialog;
48 
49   dialog = gtk_message_dialog_new (parent,
50                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
51                                    GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
52                                    "%s", error->message);
53   g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
54   gtk_window_present (GTK_WINDOW (dialog));
55 }
56 
57 #ifdef ENABLE_PRINTING
58 
59 static void
ensure_print_data(GucharmapWindow * guw)60 ensure_print_data (GucharmapWindow *guw)
61 {
62   if (!guw->page_setup) {
63     guw->page_setup = gtk_page_setup_new ();
64   }
65 
66   if (!guw->print_settings) {
67     guw->print_settings = gtk_print_settings_new ();
68   }
69 }
70 
71 static void
print_operation_done_cb(GtkPrintOperation * operation,GtkPrintOperationResult result,GucharmapWindow * guw)72 print_operation_done_cb (GtkPrintOperation *operation,
73                          GtkPrintOperationResult result,
74                          GucharmapWindow *guw)
75 {
76   if (result == GTK_PRINT_OPERATION_RESULT_ERROR) {
77     GError *error = NULL;
78 
79     gtk_print_operation_get_error (operation, &error);
80     show_error_dialog (GTK_WINDOW (guw), error);
81     g_error_free (error);
82   } else if (result == GTK_PRINT_OPERATION_RESULT_APPLY) {
83     if (guw->print_settings)
84       g_object_unref (guw->print_settings);
85     guw->print_settings = g_object_ref (gtk_print_operation_get_print_settings (operation));
86   }
87 }
88 
89 static void
gucharmap_window_print(GucharmapWindow * guw,GtkPrintOperationAction action)90 gucharmap_window_print (GucharmapWindow *guw,
91                         GtkPrintOperationAction action)
92 {
93   GtkPrintOperation *op;
94   PangoFontDescription *font_desc;
95   GucharmapCodepointList *codepoint_list;
96   GucharmapChartable *chartable;
97   char *chapter, *filename;
98   GtkPrintOperationResult rv;
99   GError *error = NULL;
100 
101   g_object_get (guw->charmap,
102                 "active-codepoint-list", &codepoint_list,
103                 "font-desc", &font_desc,
104                 NULL);
105 
106   op = gucharmap_print_operation_new (codepoint_list, font_desc);
107   if (codepoint_list)
108     g_object_unref (codepoint_list);
109   if (font_desc)
110     pango_font_description_free (font_desc);
111 
112   ensure_print_data (guw);
113   if (guw->page_setup)
114     gtk_print_operation_set_default_page_setup (op, guw->page_setup);
115   if (guw->print_settings)
116     gtk_print_operation_set_print_settings (op, guw->print_settings);
117 
118   chapter = gucharmap_charmap_get_active_chapter (guw->charmap);
119   if (chapter) {
120     filename = g_strconcat (chapter, ".pdf", NULL);
121     gtk_print_operation_set_export_filename (op, filename);
122     g_free (filename);
123     g_free (chapter);
124   }
125 
126   gtk_print_operation_set_allow_async (op, TRUE);
127   gtk_print_operation_set_show_progress (op, TRUE);
128 
129   g_signal_connect (op, "done",
130                     G_CALLBACK (print_operation_done_cb), guw);
131 
132   rv = gtk_print_operation_run (op, action, GTK_WINDOW (guw), &error);
133   if (rv == GTK_PRINT_OPERATION_RESULT_ERROR) {
134     show_error_dialog (GTK_WINDOW (guw), error);
135     g_error_free (error);
136   }
137 
138   g_object_unref (op);
139 }
140 
141 #endif /* ENABLE_PRINTING */
142 
143 static void
status_message(GtkWidget * widget,const gchar * message,GucharmapWindow * guw)144 status_message (GtkWidget       *widget,
145                 const gchar     *message,
146                 GucharmapWindow *guw)
147 {
148   gtk_statusbar_pop (GTK_STATUSBAR (guw->status), 0);
149 
150   if (message)
151     gtk_statusbar_push (GTK_STATUSBAR (guw->status), 0, message);
152 }
153 
154 static void
search_start(GucharmapSearchDialog * search_dialog,GucharmapWindow * guw)155 search_start (GucharmapSearchDialog *search_dialog,
156               GucharmapWindow       *guw)
157 {
158   GdkCursor *cursor;
159   GAction *action;
160 
161   cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (guw)), GDK_WATCH);
162   gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (guw)), cursor);
163   g_object_unref (cursor);
164 
165   action = g_action_map_lookup_action (G_ACTION_MAP (guw), "find");
166   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
167   action = g_action_map_lookup_action (G_ACTION_MAP (guw), "find-next");
168   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
169   action = g_action_map_lookup_action (G_ACTION_MAP (guw), "find-previous");
170   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
171 }
172 
173 static void
search_finish(GucharmapSearchDialog * search_dialog,gunichar found_char,GucharmapWindow * guw)174 search_finish (GucharmapSearchDialog *search_dialog,
175                gunichar               found_char,
176                GucharmapWindow       *guw)
177 {
178   GAction *action;
179 
180   if (found_char != (gunichar)(-1))
181     gucharmap_charmap_set_active_character (guw->charmap, found_char);
182   /* not-found dialog handled by GucharmapSearchDialog */
183 
184   gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (guw)), NULL);
185 
186   action = g_action_map_lookup_action (G_ACTION_MAP (guw), "find");
187   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
188   action = g_action_map_lookup_action (G_ACTION_MAP (guw), "find-next");
189   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
190   action = g_action_map_lookup_action (G_ACTION_MAP (guw), "find-previous");
191   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
192 }
193 
194 static void
ensure_search_dialog(GucharmapWindow * guw)195 ensure_search_dialog (GucharmapWindow *guw)
196 {
197   if (guw->search_dialog == NULL)
198     {
199       guw->search_dialog = gucharmap_search_dialog_new (guw);
200       g_signal_connect (guw->search_dialog, "search-start", G_CALLBACK (search_start), guw);
201       g_signal_connect (guw->search_dialog, "search-finish", G_CALLBACK (search_finish), guw);
202     }
203 }
204 
205 static void
search_find(GSimpleAction * action,GVariant * parameter,gpointer data)206 search_find (GSimpleAction *action,
207              GVariant      *parameter,
208              gpointer       data)
209 {
210   GucharmapWindow *guw = data;
211 
212   g_assert (GUCHARMAP_IS_WINDOW (guw));
213 
214   ensure_search_dialog (guw);
215 
216   gucharmap_search_dialog_present (GUCHARMAP_SEARCH_DIALOG (guw->search_dialog));
217 }
218 
219 
220 static void
search_find_next(GSimpleAction * action,GVariant * parameter,gpointer data)221 search_find_next (GSimpleAction *action,
222                   GVariant      *parameter,
223                   gpointer       data)
224 {
225   GucharmapWindow *guw = data;
226 
227   if (guw->search_dialog)
228     gucharmap_search_dialog_start_search (GUCHARMAP_SEARCH_DIALOG (guw->search_dialog), GUCHARMAP_DIRECTION_FORWARD);
229   else
230     search_find (action, NULL, guw);
231 }
232 
233 static void
search_find_prev(GSimpleAction * action,GVariant * parameter,gpointer data)234 search_find_prev (GSimpleAction *action,
235                   GVariant      *parameter,
236                   gpointer       data)
237 {
238   GucharmapWindow *guw = data;
239 
240   if (guw->search_dialog)
241     gucharmap_search_dialog_start_search (GUCHARMAP_SEARCH_DIALOG (guw->search_dialog), GUCHARMAP_DIRECTION_BACKWARD);
242   else
243     search_find (action, NULL, guw);
244 }
245 
246 #ifdef ENABLE_PRINTING
247 
248 static void
page_setup_done_cb(GtkPageSetup * page_setup,GucharmapWindow * guw)249 page_setup_done_cb (GtkPageSetup *page_setup,
250                     GucharmapWindow *guw)
251 {
252   if (page_setup) {
253     g_object_unref (guw->page_setup);
254     guw->page_setup = page_setup;
255   }
256 }
257 
258 static void
file_page_setup(GSimpleAction * action,GVariant * parameter,gpointer data)259 file_page_setup (GSimpleAction *action,
260                  GVariant      *parameter,
261                  gpointer       data)
262 {
263   GucharmapWindow *guw = data;
264 
265   ensure_print_data (guw);
266 
267   gtk_print_run_page_setup_dialog_async (GTK_WINDOW (guw),
268                                          guw->page_setup,
269                                          guw->print_settings,
270                                          (GtkPageSetupDoneFunc) page_setup_done_cb,
271                                          guw);
272 }
273 
274 #if 0
275 static void
276 file_print_preview (GSimpleAction *action,
277                     GVariant      *parameter,
278                     gpointer       data)
279 {
280   GucharmapWindow *guw = data;
281 
282   gucharmap_window_print (guw, GTK_PRINT_OPERATION_ACTION_PREVIEW);
283 }
284 #endif
285 
286 static void
file_print(GSimpleAction * action,GVariant * parameter,gpointer data)287 file_print (GSimpleAction *action,
288             GVariant      *parameter,
289             gpointer       data)
290 {
291   GucharmapWindow *guw = data;
292 
293   gucharmap_window_print (guw, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
294 }
295 
296 #endif /* ENABLE_PRINTING */
297 
298 static void
close_window(GSimpleAction * action,GVariant * parameter,gpointer data)299 close_window (GSimpleAction *action,
300               GVariant      *parameter,
301               gpointer       data)
302 {
303   GtkWidget *widget = data;
304 
305   gtk_widget_destroy (widget);
306 }
307 
308 static void
font_bigger(GSimpleAction * action,GVariant * parameter,gpointer data)309 font_bigger (GSimpleAction *action,
310              GVariant      *parameter,
311              gpointer       data)
312 {
313   GucharmapWindow *guw = data;
314 
315   gucharmap_mini_font_selection_change_font_size (GUCHARMAP_MINI_FONT_SELECTION (guw->fontsel),
316                                                   FONT_CHANGE_FACTOR);
317 }
318 
319 static void
font_smaller(GSimpleAction * action,GVariant * parameter,gpointer data)320 font_smaller (GSimpleAction *action,
321               GVariant      *parameter,
322               gpointer       data)
323 {
324   GucharmapWindow *guw = data;
325 
326   gucharmap_mini_font_selection_change_font_size (GUCHARMAP_MINI_FONT_SELECTION (guw->fontsel),
327                                                   1.0f / FONT_CHANGE_FACTOR);
328 }
329 
330 static void
font_default(GSimpleAction * action,GVariant * parameter,gpointer data)331 font_default (GSimpleAction *action,
332               GVariant      *parameter,
333               gpointer       data)
334 {
335   GucharmapWindow *guw = data;
336 
337   gucharmap_mini_font_selection_reset_font_size (GUCHARMAP_MINI_FONT_SELECTION (guw->fontsel));
338 }
339 
340 static void
snap_cols_pow2_changed(GSettings * settings,const char * key,gpointer data)341 snap_cols_pow2_changed (GSettings  *settings,
342                         const char *key,
343                         gpointer    data)
344 {
345   GucharmapWindow  *guw = data;
346 
347   gucharmap_charmap_set_snap_pow2 (guw->charmap,
348                                    g_settings_get_boolean (settings, key));
349 }
350 
351 static void
toggle_action_activated(GSimpleAction * action,GVariant * parameter,gpointer data)352 toggle_action_activated (GSimpleAction *action,
353                          GVariant      *parameter,
354                          gpointer       data)
355 {
356   GVariant *state = g_action_get_state (G_ACTION (action));
357   gboolean value = g_variant_get_boolean (state);
358 
359   g_action_change_state (G_ACTION (action), g_variant_new_boolean (!value));
360   g_variant_unref (state);
361 }
362 
363 static void
change_no_font_fallback(GSimpleAction * action,GVariant * state,gpointer data)364 change_no_font_fallback (GSimpleAction *action,
365                          GVariant      *state,
366                          gpointer       data)
367 {
368   GucharmapWindow *guw = data;
369   gboolean is_active = g_variant_get_boolean (state);
370 
371   gucharmap_charmap_set_font_fallback (guw->charmap, !is_active);
372   g_simple_action_set_state (action, state);
373 /*  gucharmap_settings_set_font_fallback (is_active); */
374 }
375 
376 static void
open_url(GtkWindow * parent,const char * uri,guint32 user_time)377 open_url (GtkWindow *parent,
378           const char *uri,
379           guint32 user_time)
380 {
381   GdkScreen *screen;
382   GError *error = NULL;
383 
384   if (parent)
385     screen = gtk_widget_get_screen (GTK_WIDGET (parent));
386   else
387     screen = gdk_screen_get_default ();
388 
389   if (!gtk_show_uri (screen, uri, user_time, &error)) {
390     show_error_dialog (parent, error);
391     g_error_free (error);
392   }
393 }
394 
395 static void
help_contents(GSimpleAction * action,GVariant * parameter,gpointer data)396 help_contents (GSimpleAction *action,
397                GVariant      *parameter,
398                gpointer       data)
399 {
400   GucharmapWindow *window = data;
401   open_url (GTK_WINDOW (window),
402             "help:gucharmap", /* DOC_MODULE */
403             gtk_get_current_event_time ());
404 }
405 
406 static void
help_about(GSimpleAction * action,GVariant * parameter,gpointer data)407 help_about (GSimpleAction *action,
408             GVariant      *parameter,
409             gpointer       data)
410 {
411   GucharmapWindow *guw = data;
412   const gchar *authors[] =
413     {
414       "Noah Levitt <nlevitt@columbia.edu>",
415       "Daniel Elstner <daniel.elstner@gmx.net>",
416       "Padraig O'Briain <Padraig.Obriain@sun.com>",
417       "Christian Persch <" "chpe" "\100" "src.gnome" "." "org" ">",
418       "DaeHyun Sung <sungdh86@gmail.com>",
419       NULL
420     };
421 
422   const gchar *documenters[] =
423     {
424       "Chee Bin HOH <cbhoh@gnome.org>",
425       "Sun Microsystems",
426       NULL
427     };
428 
429   const gchar *license[] = {
430     N_("Gucharmap is free software; you can redistribute it and/or modify "
431        "it under the terms of the GNU General Public License as published by "
432        "the Free Software Foundation; either version 3 of the License, or "
433        "(at your option) any later version."),
434     N_("Permission is hereby granted, free of charge, to any person obtaining "
435        "a copy of the Unicode data files to deal in them without restriction, "
436        "including without limitation the rights to use, copy, modify, merge, "
437        "publish, distribute, and/or sell copies."),
438     N_("Gucharmap and the Unicode data files are distributed in the hope that "
439        "they will be useful, but WITHOUT ANY WARRANTY; without even the implied "
440        "warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See "
441        "the GNU General Public License and Unicode Copyright for more details."),
442     N_("You should have received a copy of the GNU General Public License "
443        "along with Gucharmap; if not, write to the Free Software Foundation, Inc., "
444        "51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA"),
445     N_("Also you should have received a copy of the Unicode Copyright along "
446        "with Gucharmap; you can always find it at Unicode's website: "
447        "https://www.unicode.org/copyright.html")
448   };
449   gchar *license_trans;
450   license_trans = g_strconcat (_(license[0]), "\n\n", _(license[1]), "\n\n",
451 			       _(license[2]), "\n\n", _(license[3]), "\n\n",
452 			       _(license[4]), "\n\n", NULL);
453 
454   gtk_show_about_dialog (GTK_WINDOW (guw),
455 			 "program-name", _("GNOME Character Map"),
456 			 "version", VERSION,
457 			 "comments", _("Based on the Unicode Character Database 14.0.0"),
458 			 "copyright", "Copyright © 2004 Noah Levitt\n"
459 				      "Copyright © 1991–2021 Unicode, Inc.\n"
460 				      "Copyright © 2007–2021 Christian Persch\n"
461                                       "Copyright © 2016, 2020 DaeHyun Sung",
462 			 "documenters", documenters,
463 			 "license", license_trans,
464 			 "wrap-license", TRUE,
465 			 "logo-icon-name", GUCHARMAP_ICON_NAME,
466   			 "authors", authors,
467 			 "translator-credits", _("translator-credits"),
468 			 "website", "https://wiki.gnome.org/Apps/Gucharmap",
469 			 NULL);
470 
471   g_free (license_trans);
472 }
473 
474 static void
next_or_prev_character(GSimpleAction * action,GVariant * parameter,gpointer data)475 next_or_prev_character (GSimpleAction *action,
476                         GVariant      *parameter,
477                         gpointer    data)
478 {
479   GucharmapWindow *guw = data;
480   GucharmapChartable *chartable;
481   GucharmapChartableClass *klass;
482   const char *name;
483   guint keyval = 0;
484 
485   name = g_action_get_name (G_ACTION (action));
486   if (strcmp (name, "next-character") == 0) {
487     keyval = GDK_KEY_Right;
488   } else if (strcmp (name, "previous-character") == 0) {
489     keyval = GDK_KEY_Left;
490   } else {
491     g_assert_not_reached ();
492   }
493 
494   chartable = gucharmap_charmap_get_chartable (guw->charmap);
495   klass = GUCHARMAP_CHARTABLE_GET_CLASS (chartable);
496   gtk_binding_set_activate (gtk_binding_set_by_class (klass),
497                             keyval,
498                             0,
499                             G_OBJECT (chartable));
500 }
501 
502 static void
next_chapter(GSimpleAction * action,GVariant * parameter,gpointer data)503 next_chapter (GSimpleAction *action,
504               GVariant      *parameter,
505               gpointer    data)
506 {
507   GucharmapWindow *guw = data;
508   gucharmap_charmap_next_chapter (guw->charmap);
509 }
510 
511 static void
prev_chapter(GSimpleAction * action,GVariant * parameter,gpointer data)512 prev_chapter (GSimpleAction *action,
513               GVariant      *parameter,
514               gpointer    data)
515 {
516   GucharmapWindow *guw = data;
517   gucharmap_charmap_previous_chapter (guw->charmap);
518 }
519 
520 static void
chapters_set_labels(const gchar * labelnext,const gchar * labelprev,GucharmapWindow * guw)521 chapters_set_labels (const gchar     *labelnext,
522 		     const gchar     *labelprev,
523 		     GucharmapWindow *guw)
524 {
525   GtkApplication *app;
526   GMenuModel *model;
527   int n_items;
528 
529   app = GTK_APPLICATION (g_application_get_default ());
530   g_return_if_fail (app != NULL);
531   model = G_MENU_MODEL (g_object_get_data (G_OBJECT (app), "go-chapter-menu"));
532 
533   n_items = g_menu_model_get_n_items (model);
534   while (n_items--)
535     g_menu_remove (G_MENU (model), 0);
536 
537   g_menu_append (G_MENU (model), labelnext, "win.next-chapter");
538   g_menu_append (G_MENU (model), labelprev, "win.previous-chapter");
539 }
540 
541 enum {
542   VIEW_BY_SCRIPT,
543   VIEW_BY_BLOCK
544 };
545 
546 static void
gucharmap_window_set_chapters_model(GucharmapWindow * guw,GucharmapChaptersMode mode)547 gucharmap_window_set_chapters_model (GucharmapWindow *guw,
548                                      GucharmapChaptersMode mode)
549 {
550   GucharmapChaptersModel *model = NULL;
551 
552   switch (mode)
553     {
554       case GUCHARMAP_CHAPTERS_SCRIPT:
555       	model = gucharmap_script_chapters_model_new ();
556 	chapters_set_labels (_("Next Script"), _("Previous Script"), guw);
557 	break;
558 
559       case GUCHARMAP_CHAPTERS_BLOCK:
560       	model = gucharmap_block_chapters_model_new ();
561 	chapters_set_labels (_("Next Block"), _("Previous Block"), guw);
562 	break;
563 
564       default:
565         g_assert_not_reached ();
566     }
567 
568   gucharmap_charmap_set_chapters_model (guw->charmap, model);
569   g_object_unref (model);
570 }
571 
572 static void
gucharmap_window_group_by_changed(GSettings * settings,const gchar * key,gpointer user_data)573 gucharmap_window_group_by_changed (GSettings   *settings,
574                                    const gchar *key,
575                                    gpointer     user_data)
576 {
577   GucharmapWindow *guw = user_data;
578 
579   gucharmap_window_set_chapters_model (guw, g_settings_get_enum (settings, "group-by"));
580 }
581 
582 #ifdef DEBUG_chpe
583 static void
move_to_next_screen_cb(GSimpleAction * action,GVariant * parameter,gpointer data)584 move_to_next_screen_cb (GSimpleAction *action,
585                         GVariant      *parameter,
586                         gpointer       data)
587 {
588   GtkWidget *widget = data;
589   GdkScreen *screen;
590   GdkDisplay *display;
591   int number_of_screens, screen_num;
592 
593   screen = gtk_widget_get_screen (widget);
594   display = gdk_screen_get_display (screen);
595   screen_num = gdk_screen_get_number (screen);
596   number_of_screens =  gdk_display_get_n_screens (display);
597 
598   if ((screen_num + 1) < number_of_screens) {
599     screen = gdk_display_get_screen (display, screen_num + 1);
600   } else {
601     screen = gdk_display_get_screen (display, 0);
602   }
603 
604   gtk_window_set_screen (GTK_WINDOW (widget), screen);
605 }
606 #endif
607 
608 static void
insert_character_in_text_to_copy(GucharmapChartable * chartable,GucharmapWindow * guw)609 insert_character_in_text_to_copy (GucharmapChartable *chartable,
610                                   GucharmapWindow *guw)
611 {
612   gchar ubuf[7];
613   gint pos = -1;
614   gunichar wc;
615 
616   wc = gucharmap_chartable_get_active_character (chartable);
617   /* Can't copy values that are not valid unicode characters */
618   if (!gucharmap_unichar_validate (wc))
619     return;
620 
621   ubuf[g_unichar_to_utf8 (wc, ubuf)] = '\0';
622   gtk_editable_delete_selection (GTK_EDITABLE (guw->text_to_copy_entry));
623   pos = gtk_editable_get_position (GTK_EDITABLE (guw->text_to_copy_entry));
624   gtk_editable_insert_text (GTK_EDITABLE (guw->text_to_copy_entry), ubuf, -1, &pos);
625   gtk_editable_set_position (GTK_EDITABLE (guw->text_to_copy_entry), pos);
626 }
627 
628 static void
edit_copy(GtkWidget * widget,GucharmapWindow * guw)629 edit_copy (GtkWidget *widget, GucharmapWindow *guw)
630 {
631   /* if nothing is selected, select the whole thing */
632   if (! gtk_editable_get_selection_bounds (
633               GTK_EDITABLE (guw->text_to_copy_entry), NULL, NULL))
634     gtk_editable_select_region (GTK_EDITABLE (guw->text_to_copy_entry), 0, -1);
635 
636   gtk_editable_copy_clipboard (GTK_EDITABLE (guw->text_to_copy_entry));
637 }
638 
639 static void
entry_changed_sensitize_button(GtkEditable * editable,GtkWidget * button)640 entry_changed_sensitize_button (GtkEditable *editable, GtkWidget *button)
641 {
642   const gchar *entry_text = gtk_entry_get_text (GTK_ENTRY (editable));
643   gtk_widget_set_sensitive (button, entry_text[0] != '\0');
644 }
645 
646 static void
status_realize(GtkWidget * status,GucharmapWindow * guw)647 status_realize (GtkWidget       *status,
648                 GucharmapWindow *guw)
649 {
650   GtkAllocation *allocation;
651   GtkAllocation widget_allocation;
652 
653   gtk_widget_get_allocation (guw->status, &widget_allocation);
654   allocation = &widget_allocation;
655 
656   /* FIXMEchpe ewww... */
657   /* increase the height a bit so it doesn't resize itself */
658   gtk_widget_set_size_request (guw->status, -1, allocation->height + 9);
659 }
660 
661 static void
fontsel_sync_font_desc(GucharmapMiniFontSelection * fontsel,GParamSpec * pspec,GucharmapWindow * guw)662 fontsel_sync_font_desc (GucharmapMiniFontSelection *fontsel,
663                         GParamSpec *pspec,
664                         GucharmapWindow *guw)
665 {
666   PangoFontDescription *font_desc;
667   char *font;
668 
669   if (guw->in_notification)
670     return;
671 
672   font_desc = gucharmap_mini_font_selection_get_font_desc (fontsel);
673 
674   guw->in_notification = TRUE;
675   gucharmap_charmap_set_font_desc (guw->charmap, font_desc);
676   guw->in_notification = FALSE;
677 
678   font = pango_font_description_to_string (font_desc);
679   g_settings_set (guw->settings, "font", "ms", font);
680   g_free (font);
681 }
682 
683 static void
charmap_sync_font_desc(GucharmapCharmap * charmap,GParamSpec * pspec,GucharmapWindow * guw)684 charmap_sync_font_desc (GucharmapCharmap *charmap,
685                         GParamSpec *pspec,
686                         GucharmapWindow *guw)
687 {
688   PangoFontDescription *font_desc;
689 
690   if (guw->in_notification)
691     return;
692 
693   font_desc = gucharmap_charmap_get_font_desc (charmap);
694 
695   guw->in_notification = TRUE;
696   gucharmap_mini_font_selection_set_font_desc (GUCHARMAP_MINI_FONT_SELECTION (guw->fontsel),
697                                                font_desc);
698   guw->in_notification = FALSE;
699 }
700 
701 static int last_char_dirty = 0;
702 
703 static void
charmap_dirty_active_character(GtkWidget * widget,GParamSpec * pspec,GucharmapWindow * guw)704 charmap_dirty_active_character (GtkWidget *widget,
705                                 GParamSpec *pspec,
706                                 GucharmapWindow *guw)
707 {
708   last_char_dirty = 1;
709 }
710 
711 static gboolean
charmap_save_active_character(GtkWidget * widget,GucharmapWindow * guw)712 charmap_save_active_character (GtkWidget *widget,
713                                GucharmapWindow *guw)
714 {
715   if (last_char_dirty)
716     g_settings_set_uint (guw->settings, "last-char",
717                          gucharmap_charmap_get_active_character (guw->charmap));
718 
719   return FALSE;
720 }
721 
722 static void
gucharmap_window_init(GucharmapWindow * guw)723 gucharmap_window_init (GucharmapWindow *guw)
724 {
725   GtkWidget *grid, *button, *label;
726   GucharmapChartable *chartable;
727   /* tooltips are NULL because they are never actually shown in the program */
728   const GActionEntry menu_entries[] =
729   {
730 #ifdef ENABLE_PRINTING
731     { "page-setup", file_page_setup, NULL, NULL, NULL },
732 #if 0
733     { "print-preview", file_print_preview, NULL, NULL, NULL },
734 #endif
735     { "print", file_print, NULL, NULL, NULL },
736 #endif /* ENABLE_PRINTING */
737     { "close", close_window, NULL, NULL, NULL },
738 
739     { "zoom-in", font_bigger, NULL, NULL, NULL },
740     { "zoom-out", font_smaller, NULL, NULL, NULL },
741     { "normal-size", font_default, NULL, NULL, NULL },
742 
743     { "find", search_find, NULL, NULL, NULL },
744     { "find-next", search_find_next, NULL, NULL, NULL },
745     { "find-previous", search_find_prev, NULL, NULL, NULL },
746 
747     { "next-character", next_or_prev_character, NULL, NULL, NULL },
748     { "previous-character", next_or_prev_character, NULL, NULL, NULL },
749     { "next-chapter", next_chapter, NULL, NULL, NULL },
750     { "previous-chapter", prev_chapter, NULL, NULL, NULL },
751 
752     { "help", help_contents, NULL, NULL, NULL },
753     { "about", help_about, NULL, NULL, NULL },
754 
755   #ifdef DEBUG_chpe
756     { "move-next-screen", move_to_next_screen_cb, NULL, NULL, NULL },
757   #endif
758 
759     { "show-only-glyphs-in-font", toggle_action_activated, NULL, "false",
760       change_no_font_fallback },
761   };
762   GAction *action;
763   gunichar active;
764   gchar *font;
765 
766   guw->settings = g_settings_new ("org.gnome.Charmap");
767 
768   gtk_window_set_title (GTK_WINDOW (guw), _("Character Map"));
769   gtk_window_set_icon_name (GTK_WINDOW (guw), GUCHARMAP_ICON_NAME);
770 
771   g_action_map_add_action_entries (G_ACTION_MAP (guw),
772                                    menu_entries, G_N_ELEMENTS (menu_entries),
773                                    guw);
774 
775   /* Now the widgets */
776   grid = gtk_grid_new ();
777   gtk_container_add (GTK_CONTAINER (guw), grid);
778 
779   /* The font selector */
780   guw->fontsel = gucharmap_mini_font_selection_new ();
781   gtk_grid_attach (GTK_GRID (grid), guw->fontsel, 0, 1, 3, 1);
782   gtk_widget_show (GTK_WIDGET (guw->fontsel));
783 
784   /* The charmap */
785   guw->charmap = GUCHARMAP_CHARMAP (gucharmap_charmap_new ());
786   g_signal_connect (guw->charmap, "notify::font-desc",
787                     G_CALLBACK (charmap_sync_font_desc), guw);
788 
789   gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (guw->charmap), 0, 2, 3, 1);
790   gtk_widget_show (GTK_WIDGET (guw->charmap));
791 
792   /* Text to copy entry + button */
793   label = gtk_label_new_with_mnemonic (_("_Text to copy:"));
794   g_object_set (label, "margin", 6, NULL);
795   gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 1, 1);
796   gtk_widget_show (label);
797 
798   button = gtk_button_new_from_stock (GTK_STOCK_COPY);
799   g_object_set (button, "margin", 6, NULL);
800   gtk_widget_set_tooltip_text (button, _("Copy to the clipboard."));
801   g_signal_connect (G_OBJECT (button), "clicked",
802                     G_CALLBACK (edit_copy), guw);
803   gtk_grid_attach (GTK_GRID (grid), button, 2, 3, 1, 1);
804   gtk_widget_show (button);
805 
806   gtk_widget_set_sensitive (button, FALSE);
807   guw->text_to_copy_entry = gtk_entry_new ();
808   g_object_set (guw->text_to_copy_entry, "margin", 6, NULL);
809   gtk_widget_set_hexpand (guw->text_to_copy_entry, TRUE);
810   gtk_label_set_mnemonic_widget (GTK_LABEL (label), guw->text_to_copy_entry);
811   g_signal_connect (G_OBJECT (guw->text_to_copy_entry), "changed",
812                     G_CALLBACK (entry_changed_sensitize_button), button);
813 
814   gtk_grid_attach (GTK_GRID (grid), guw->text_to_copy_entry, 1, 3, 1, 1);
815   gtk_widget_show (guw->text_to_copy_entry);
816 
817   /* FIXMEchpe!! */
818   chartable = gucharmap_charmap_get_chartable (guw->charmap);
819   g_signal_connect (chartable, "activate", G_CALLBACK (insert_character_in_text_to_copy), guw);
820 
821   /* Finally the statusbar */
822   guw->status = gtk_statusbar_new ();
823   gtk_grid_attach (GTK_GRID (grid), guw->status, 0, 4, 3, 1);
824   gtk_widget_show (guw->status);
825   g_signal_connect (guw->status, "realize", G_CALLBACK (status_realize), guw);
826 
827   g_signal_connect (guw->charmap, "status-message", G_CALLBACK (status_message), guw);
828 
829   gtk_widget_show (grid);
830 
831   gtk_widget_grab_focus (GTK_WIDGET (gucharmap_charmap_get_chartable (guw->charmap)));
832 
833   gtk_window_set_has_resize_grip (GTK_WINDOW (guw), TRUE);
834 
835   /* snap-to-power-of-two */
836   action = g_settings_create_action (guw->settings, "snap-cols-pow2");
837   g_action_map_add_action (G_ACTION_MAP (guw), action);
838   snap_cols_pow2_changed (guw->settings, "snap-cols-pow2", guw);
839   g_signal_connect (guw->settings, "changed::snap-cols-pow2",
840                     G_CALLBACK (snap_cols_pow2_changed), guw);
841 
842   /* read initial settings */
843   /* font */
844   g_settings_get (guw->settings, "font", "ms", &font);
845   if (font != NULL)
846     {
847       gucharmap_window_set_font (guw, font);
848       g_free (font);
849     }
850 
851   /* group by */
852   g_action_map_add_action (G_ACTION_MAP (guw), g_settings_create_action (guw->settings, "group-by"));
853   g_signal_connect_object (guw->settings, "changed::group-by", G_CALLBACK (gucharmap_window_group_by_changed), guw, 0);
854   gucharmap_window_set_chapters_model (guw, g_settings_get_enum (guw->settings, "group-by"));
855 
856   /* active character */
857   active = g_settings_get_uint (guw->settings, "last-char");
858   gucharmap_charmap_set_active_character (guw->charmap, active);
859 
860   /* window geometry */
861   gucharmap_settings_add_window (GTK_WINDOW (guw));
862 
863   /* connect these only after applying the initial settings in order to
864    * avoid unnecessary writes to GSettings.
865    */
866   g_signal_connect (guw, "destroy",
867                     G_CALLBACK (charmap_save_active_character), guw);
868   g_signal_connect (guw->charmap, "notify::active-character",
869                     G_CALLBACK (charmap_dirty_active_character), guw);
870   g_signal_connect (guw->fontsel, "notify::font-desc",
871                     G_CALLBACK (fontsel_sync_font_desc), guw);
872 }
873 
874 static void
gucharmap_window_finalize(GObject * object)875 gucharmap_window_finalize (GObject *object)
876 {
877   GucharmapWindow *guw = GUCHARMAP_WINDOW (object);
878 
879   if (guw->page_setup)
880     g_object_unref (guw->page_setup);
881 
882   if (guw->print_settings)
883     g_object_unref (guw->print_settings);
884 
885   G_OBJECT_CLASS (gucharmap_window_parent_class)->finalize (object);
886 }
887 
888 static void
gucharmap_window_class_init(GucharmapWindowClass * klass)889 gucharmap_window_class_init (GucharmapWindowClass *klass)
890 {
891   GObjectClass *object_class = G_OBJECT_CLASS (klass);
892 
893   object_class->finalize = gucharmap_window_finalize;
894 }
895 
896 /* Public API */
897 
898 GtkWidget *
gucharmap_window_new(GtkApplication * application)899 gucharmap_window_new (GtkApplication *application)
900 {
901   return GTK_WIDGET (g_object_new (gucharmap_window_get_type (),
902                      "application", application, NULL));
903 }
904 
905 void
gucharmap_window_set_font(GucharmapWindow * guw,const char * font)906 gucharmap_window_set_font (GucharmapWindow *guw,
907                            const char *font)
908 {
909   PangoFontDescription *font_desc;
910 
911   g_return_if_fail (GUCHARMAP_IS_WINDOW (guw));
912 
913   g_assert (!gtk_widget_get_realized (GTK_WIDGET (guw)));
914 
915   if (!font)
916     return;
917 
918   font_desc = pango_font_description_from_string (font);
919   gucharmap_charmap_set_font_desc (guw->charmap, font_desc);
920   pango_font_description_free (font_desc);
921 }
922 
923 void
gucharmap_window_search(GucharmapWindow * guw,const char * str)924 gucharmap_window_search (GucharmapWindow *guw,
925                          const char *str)
926 {
927   g_return_if_fail (GUCHARMAP_IS_WINDOW (guw));
928   g_return_if_fail (str != NULL);
929 
930   ensure_search_dialog (guw);
931   gucharmap_search_dialog_set_search (GUCHARMAP_SEARCH_DIALOG (guw->search_dialog), str);
932   gucharmap_search_dialog_start_search (GUCHARMAP_SEARCH_DIALOG (guw->search_dialog),
933                                         GUCHARMAP_DIRECTION_FORWARD);
934 }
935