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