1 /* font-manager-preview-pane.c
2  *
3  * Copyright (C) 2009 - 2021 Jerry Casiano
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.
17  *
18  * If not, see <http://www.gnu.org/licenses/gpl-3.0.txt>.
19 */
20 
21 #include "font-manager-preview-pane.h"
22 
23 /**
24  * SECTION: font-manager-preview-pane
25  * @short_description: Preview pane widget
26  * @title: Preview Pane
27  * @include: font-manager-preview-pane.h
28  *
29  * Full featured font preview widget.
30  */
31 
32 /**
33  * font_manager_preview_pane_page_to_string:
34  * @page:  #FontManagerPreviewPanePage
35  *
36  * Returns: (transfer none) (nullable): @page as a string
37  */
38 const gchar *
font_manager_preview_pane_page_to_string(FontManagerPreviewPanePage page)39 font_manager_preview_pane_page_to_string (FontManagerPreviewPanePage page)
40 {
41     switch (page) {
42         case FONT_MANAGER_PREVIEW_PANE_PAGE_CHARACTER_MAP:
43             return _("Characters");
44         case FONT_MANAGER_PREVIEW_PANE_PAGE_PROPERTIES:
45             return _("Properties");
46         case FONT_MANAGER_PREVIEW_PANE_PAGE_LICENSE:
47             return _("License");
48         default:
49             return NULL;
50     }
51 }
52 
53 GType
font_manager_preview_pane_page_get_type(void)54 font_manager_preview_pane_page_get_type (void)
55 {
56   static volatile gsize g_define_type_id__volatile = 0;
57 
58   if (g_once_init_enter (&g_define_type_id__volatile))
59     {
60       static const GEnumValue values[] = {
61         { FONT_MANAGER_PREVIEW_PANE_PAGE_PREVIEW, "FONT_MANAGER_PREVIEW_PANE_PAGE_PREVIEW", "preview" },
62         { FONT_MANAGER_PREVIEW_PANE_PAGE_CHARACTER_MAP, "FONT_MANAGER_PREVIEW_PANE_PAGE_CHARACTER_MAP", "character-map" },
63         { FONT_MANAGER_PREVIEW_PANE_PAGE_PROPERTIES, "FONT_MANAGER_PREVIEW_PANE_PAGE_PROPERTIES", "properties" },
64         { FONT_MANAGER_PREVIEW_PANE_PAGE_LICENSE, "FONT_MANAGER_PREVIEW_PANE_PAGE_LICENSE", "license" },
65         { 0, NULL, NULL }
66       };
67       GType g_define_type_id =
68         g_enum_register_static (g_intern_static_string ("FontManagerPreviewPanePage"), values);
69       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
70     }
71 
72   return g_define_type_id__volatile;
73 }
74 
75 struct _FontManagerPreviewPane
76 {
77     GtkNotebook   parent_instance;
78 
79     gboolean                metadata_update_required;
80     gdouble                 preview_size;
81     gdouble                 glyph_preview_size;
82     gchar                   *preview_text;
83     gchar                   *current_uri;
84     GtkWidget               *preview;
85     GtkWidget               *character_map;
86     GtkWidget               *properties;
87     GtkWidget               *license;
88     GtkWidget               *search;
89     GHashTable              *samples;
90 
91     FontManagerFont         *font;
92     FontManagerFontInfo     *metadata;
93     FontManagerFontPreviewMode  mode;
94 };
95 
96 G_DEFINE_TYPE(FontManagerPreviewPane, font_manager_preview_pane, GTK_TYPE_NOTEBOOK)
97 
98 enum
99 {
100     CHANGED,
101     NUM_SIGNALS
102 };
103 
104 static guint signals[NUM_SIGNALS];
105 
106 enum
107 {
108     PROP_RESERVED,
109     PROP_PREVIEW_SIZE,
110     PROP_GLYPH_PREVIEW_SIZE,
111     PROP_PREVIEW_TEXT,
112     PROP_PREVIEW_MODE,
113     PROP_SAMPLES,
114     PROP_FONT,
115     PROP_METADATA,
116     PROP_ORTHOGRAPHY,
117     N_PROPERTIES
118 };
119 
120 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
121 
122 static void update_mode (FontManagerPreviewPane *self);
123 static gboolean font_manager_preview_pane_update_metadata (FontManagerPreviewPane *self);
124 
125 static void
font_manager_preview_pane_dispose(GObject * gobject)126 font_manager_preview_pane_dispose (GObject *gobject)
127 {
128     g_return_if_fail(gobject != NULL);
129     FontManagerPreviewPane *self = FONT_MANAGER_PREVIEW_PANE(gobject);
130     g_clear_object(&self->font);
131     g_clear_object(&self->metadata);
132     g_clear_object(&self->search);
133     g_clear_pointer(&self->preview_text, g_free);
134     g_clear_pointer(&self->current_uri, g_free);
135     g_clear_pointer(&self->samples, g_hash_table_unref);
136     font_manager_clear_application_fonts();
137     G_OBJECT_CLASS(font_manager_preview_pane_parent_class)->dispose(gobject);
138     return;
139 }
140 
141 static void
font_manager_preview_pane_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)142 font_manager_preview_pane_get_property (GObject *gobject,
143                                         guint property_id,
144                                         GValue *value,
145                                         GParamSpec *pspec)
146 {
147     g_return_if_fail(gobject != NULL);
148     FontManagerPreviewPane *self = FONT_MANAGER_PREVIEW_PANE(gobject);
149     switch (property_id) {
150         case PROP_PREVIEW_SIZE:
151             g_value_set_double(value, self->preview_size);
152             break;
153         case PROP_GLYPH_PREVIEW_SIZE:
154             g_value_set_double(value, self->glyph_preview_size);
155             break;
156         case PROP_PREVIEW_MODE:
157             g_value_set_enum(value, self->mode);
158             break;
159         case PROP_PREVIEW_TEXT:
160             g_value_set_string(value, self->preview_text);
161             break;
162         case PROP_FONT:
163             g_value_set_object(value, self->font);
164             break;
165         case PROP_METADATA:
166             font_manager_preview_pane_update_metadata(self);
167             g_value_set_object(value, self->metadata);
168             break;
169         case PROP_SAMPLES:
170             g_value_set_boxed(value, self->samples);
171             break;
172         default:
173             G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
174     }
175     return;
176 }
177 
178 static void
font_manager_preview_pane_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * pspec)179 font_manager_preview_pane_set_property (GObject *gobject,
180                                         guint property_id,
181                                         const GValue *value,
182                                         GParamSpec *pspec)
183 {
184     g_return_if_fail(gobject != NULL);
185     FontManagerPreviewPane *self = FONT_MANAGER_PREVIEW_PANE(gobject);
186     switch (property_id) {
187         case PROP_PREVIEW_SIZE:
188             self->preview_size = g_value_get_double(value);
189             break;
190         case PROP_GLYPH_PREVIEW_SIZE:
191             self->glyph_preview_size = g_value_get_double(value);
192             break;
193         case PROP_PREVIEW_MODE:
194             self->mode = g_value_get_enum(value);
195             update_mode(self);
196             break;
197         case PROP_PREVIEW_TEXT:
198             g_clear_pointer(&self->preview_text, g_free);
199             self->preview_text = g_value_dup_string(value);
200             break;
201         case PROP_FONT:
202             font_manager_preview_pane_set_font(self, g_value_get_object(value));
203             break;
204         case PROP_SAMPLES:
205             if (self->samples)
206                 g_clear_pointer(&self->samples, g_hash_table_unref);
207             GHashTable *samples = g_value_get_boxed(value);
208             if (samples)
209                 self->samples = g_hash_table_ref(samples);
210             break;
211         case PROP_ORTHOGRAPHY:
212             font_manager_preview_pane_set_orthography(self, g_value_get_object(value));
213             break;
214         default:
215             G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
216     }
217     return;
218 }
219 
220 static void
font_manager_preview_pane_class_init(FontManagerPreviewPaneClass * klass)221 font_manager_preview_pane_class_init (FontManagerPreviewPaneClass *klass)
222 {
223     GObjectClass *object_class = G_OBJECT_CLASS(klass);
224 
225     object_class->dispose = font_manager_preview_pane_dispose;
226     object_class->get_property = font_manager_preview_pane_get_property;
227     object_class->set_property = font_manager_preview_pane_set_property;
228 
229     /**
230      * FontManagerPreviewPane::changed:
231      *
232      * Emitted whenever the the preview is updated.
233      */
234     signals[CHANGED] = g_signal_new("changed",
235                                     FONT_MANAGER_TYPE_PREVIEW_PANE,
236                                     G_SIGNAL_RUN_FIRST,
237                                     0, NULL, NULL, NULL, G_TYPE_NONE, 0);
238 
239     /**
240      * FontManagerPreviewPane:preview-size:
241      *
242      * Size to use for font in preview mode.
243      */
244     obj_properties[PROP_PREVIEW_SIZE] = g_param_spec_double("preview-size",
245                                                             NULL,
246                                                             "Font preview size",
247                                                             FONT_MANAGER_MIN_FONT_SIZE,
248                                                             FONT_MANAGER_MAX_FONT_SIZE,
249                                                             FONT_MANAGER_DEFAULT_PREVIEW_SIZE,
250                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
251 
252     /**
253      * FontManagerPreviewPane:character-map-preview-size:
254      *
255      * Size to use for font in character map.
256      */
257     obj_properties[PROP_GLYPH_PREVIEW_SIZE] = g_param_spec_double("character-map-preview-size",
258                                                                   NULL,
259                                                                   "Font preview size",
260                                                                   FONT_MANAGER_MIN_FONT_SIZE,
261                                                                   FONT_MANAGER_MAX_FONT_SIZE,
262                                                                   FONT_MANAGER_CHARACTER_MAP_PREVIEW_SIZE,
263                                                                   G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
264 
265     /**
266      * FontManagerPreviewPane:preview-text:
267      *
268      * Text to display in interactive preview mode.
269      */
270     obj_properties[PROP_PREVIEW_TEXT] = g_param_spec_string("preview-text",
271                                                             NULL,
272                                                             "Preview text",
273                                                             NULL,
274                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
275 
276     /**
277      * FontManagerPreviewPane:preview-mode:
278      */
279     obj_properties[PROP_PREVIEW_MODE] = g_param_spec_enum("preview-mode",
280                                                           NULL,
281                                                           "Preview mode",
282                                                           FONT_MANAGER_TYPE_FONT_PREVIEW_MODE,
283                                                           FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL,
284                                                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
285 
286     /**
287      * FontManagerPreviewPane:sample-strings:
288      *
289      * Dictionary of sample strings
290      */
291     obj_properties[PROP_SAMPLES] = g_param_spec_boxed("samples",
292                                                       NULL,
293                                                       "Dictionary of sample strings",
294                                                       G_TYPE_HASH_TABLE,
295                                                       G_PARAM_STATIC_STRINGS |
296                                                       G_PARAM_READWRITE);
297 
298     /**
299      * FontManagerPreviewPane:font:
300      *
301      * #FontManagerFont to display.
302      */
303     obj_properties[PROP_FONT] = g_param_spec_object("font",
304                                                     NULL,
305                                                     "#FontManagerFont to display",
306                                                     FONT_MANAGER_TYPE_FONT,
307                                                     G_PARAM_READWRITE |
308                                                     G_PARAM_STATIC_STRINGS |
309                                                     G_PARAM_EXPLICIT_NOTIFY);
310 
311     /**
312      * FontManagerPreviewPane:metadata:
313      *
314      * #FontManagerFontInfo for the currently displayed font.
315      */
316     obj_properties[PROP_METADATA] = g_param_spec_object("metadata",
317                                                          NULL,
318                                                          "#FontManagerFontInfo",
319                                                          FONT_MANAGER_TYPE_FONT_INFO,
320                                                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
321 
322     /**
323      * FontManagerPreviewPane:orthography:
324      *
325      * #FontManagerOrthography to display in character map.
326      */
327     obj_properties[PROP_ORTHOGRAPHY] = g_param_spec_object("orthography",
328                                                             NULL,
329                                                             "#FontManagerOrthography to display",
330                                                             FONT_MANAGER_TYPE_ORTHOGRAPHY,
331                                                             G_PARAM_WRITABLE |
332                                                             G_PARAM_STATIC_STRINGS |
333                                                             G_PARAM_EXPLICIT_NOTIFY);
334 
335     g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties);
336     return;
337 }
338 
339 static gboolean
font_manager_preview_pane_update_metadata(FontManagerPreviewPane * self)340 font_manager_preview_pane_update_metadata (FontManagerPreviewPane *self)
341 {
342     g_return_val_if_fail(self != NULL, G_SOURCE_REMOVE);
343     if (!self->metadata_update_required || !font_manager_json_proxy_is_valid(FONT_MANAGER_JSON_PROXY(self->font)))
344         return G_SOURCE_REMOVE;
345     gint index = 0;
346     GError *error = NULL;
347     g_autofree gchar *filepath = NULL;
348     g_autoptr(JsonObject) res = NULL;
349     g_autoptr(FontManagerFontInfo) metadata = font_manager_font_info_new();
350     g_autoptr(FontManagerDatabase) db = font_manager_get_database(FONT_MANAGER_DATABASE_TYPE_BASE, &error);
351     g_object_get(G_OBJECT(self->font), "filepath", &filepath, "findex", &index, NULL);
352     if (error == NULL) {
353         const gchar *select = "SELECT * FROM Metadata WHERE filepath = %s AND findex = '%i'";
354         char *path = sqlite3_mprintf("%Q", filepath);
355         g_autofree gchar *query = g_strdup_printf(select, path, index);
356         res = font_manager_database_get_object(db, query, &error);
357         sqlite3_free(path);
358     }
359     if (error != NULL) {
360         // g_warning("There was an error retrieving metadata from the database for %s : %s", filepath, error->message);
361         g_clear_error(&error);
362     }
363     if (!res) {
364         res = font_manager_get_metadata(filepath, index, &error);
365         if (error != NULL) {
366             g_critical("Failed to get metadata for %s : %s", filepath, error->message);
367             g_clear_error(&error);
368         }
369     }
370     g_object_set(G_OBJECT(metadata), "source-object", res, NULL);
371     g_set_object(&self->metadata, metadata);
372     self->metadata_update_required = FALSE;
373     return G_SOURCE_REMOVE;
374 }
375 
376 static gboolean
font_manager_preview_pane_update(FontManagerPreviewPane * self)377 font_manager_preview_pane_update (FontManagerPreviewPane *self)
378 {
379     g_return_val_if_fail(self != NULL, G_SOURCE_REMOVE);
380     gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(self));
381     GtkWidget *menu = gtk_notebook_get_action_widget(GTK_NOTEBOOK(self), GTK_PACK_START);
382     GtkWidget *search = gtk_notebook_get_action_widget(GTK_NOTEBOOK(self), GTK_PACK_END);
383     gboolean menu_sensitive = (page == FONT_MANAGER_PREVIEW_PANE_PAGE_PREVIEW);
384     gtk_widget_set_sensitive(menu, menu_sensitive);
385     GtkStyleContext *ctx = gtk_widget_get_style_context(menu);
386     if (!menu_sensitive)
387         gtk_style_context_add_class(ctx, GTK_STYLE_CLASS_FLAT);
388     else
389         gtk_style_context_remove_class(ctx, GTK_STYLE_CLASS_FLAT);
390     gtk_widget_set_visible(search, page == FONT_MANAGER_PREVIEW_PANE_PAGE_CHARACTER_MAP);
391     if (page == FONT_MANAGER_PREVIEW_PANE_PAGE_PREVIEW) {
392         g_autofree gchar *description = NULL;
393         if (self->font)
394             g_object_get(G_OBJECT(self->font), "description", &description, NULL);
395         if (!description)
396             description = g_strdup(FONT_MANAGER_DEFAULT_FONT);
397         font_manager_font_preview_set_font_description(FONT_MANAGER_FONT_PREVIEW(self->preview), description);
398     } else if (page == FONT_MANAGER_PREVIEW_PANE_PAGE_CHARACTER_MAP) {
399         font_manager_character_map_set_font(FONT_MANAGER_CHARACTER_MAP(self->character_map), self->font);
400     } else if (page == FONT_MANAGER_PREVIEW_PANE_PAGE_PROPERTIES) {
401         font_manager_preview_pane_update_metadata(self);
402         font_manager_properties_pane_update(FONT_MANAGER_PROPERTIES_PANE(self->properties), self->font, self->metadata);
403     } else if (page == FONT_MANAGER_PREVIEW_PANE_PAGE_LICENSE) {
404         if (self->metadata) {
405             font_manager_preview_pane_update_metadata(self);
406             FontManagerfsType fsType;
407             g_autofree gchar *license_data = NULL;
408             g_autofree gchar *license_url = NULL;
409             g_object_get(G_OBJECT(self->metadata), "fsType", &fsType, "license-data", &license_data, "license-url", &license_url, NULL);
410             g_object_set(G_OBJECT(self->license), "fstype", fsType, "license-data", license_data, "license-url", license_url, NULL);
411         } else {
412             g_object_set(G_OBJECT(self->license), "fstype", FONT_MANAGER_FSTYPE_RESTRICTED_LICENSE, "license-data", NULL, "license-url", NULL, NULL);
413         }
414     }
415     g_signal_emit(self, signals[CHANGED], 0);
416     g_idle_add((GSourceFunc) font_manager_preview_pane_update_metadata, self);
417     return G_SOURCE_REMOVE;
418 }
419 
420 static void
on_search_toggled(GtkToggleButton * button,FontManagerPreviewPane * self)421 on_search_toggled (GtkToggleButton *button, FontManagerPreviewPane *self)
422 {
423     g_return_if_fail(self != NULL && button != NULL);
424     g_object_set(G_OBJECT(self->character_map), "search-mode", gtk_toggle_button_get_active(button), NULL);
425     return;
426 }
427 
428 GtkWidget *
create_search_button(FontManagerPreviewPane * self)429 create_search_button (FontManagerPreviewPane *self)
430 {
431     GtkWidget *search = gtk_toggle_button_new();
432     GtkWidget *search_icon = gtk_image_new_from_icon_name("system-search-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
433     gtk_container_add(GTK_CONTAINER(search), search_icon);
434     gtk_widget_set_tooltip_text(search, _("Search available characters"));
435     g_signal_connect(search, "toggled", G_CALLBACK(on_search_toggled), self);
436     gtk_widget_show(search_icon);
437     gtk_widget_show(search);
438     font_manager_widget_set_margin(search, 2);
439     gtk_widget_set_margin_top(search, 1);
440     gtk_widget_set_margin_bottom(search, 1);
441     return search;
442 }
443 
444 void
on_page_switch(GtkNotebook * notebook,GtkWidget * page,guint page_num,gpointer user_data)445 on_page_switch (GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data)
446 {
447     g_return_if_fail(notebook != NULL);
448     FontManagerPreviewPane *self = FONT_MANAGER_PREVIEW_PANE(notebook);
449     g_idle_add((GSourceFunc) font_manager_preview_pane_update, self);
450     return;
451 }
452 
453 static void
append_page(FontManagerPreviewPane * self,GtkWidget * widget,const gchar * title)454 append_page (FontManagerPreviewPane *self, GtkWidget *widget, const gchar *title)
455 {
456     gint page_added = gtk_notebook_append_page(GTK_NOTEBOOK(self), widget, gtk_label_new(title));
457     g_warn_if_fail(page_added >= 0);
458     return;
459 }
460 
461 static void
update_mode(FontManagerPreviewPane * self)462 update_mode (FontManagerPreviewPane *self)
463 {
464     GtkWidget *widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(self), self->preview);
465     gtk_label_set_text(GTK_LABEL(widget), font_manager_font_preview_mode_to_translatable_string(self->mode));
466     GApplication *application = g_application_get_default();
467     GAction *action = g_action_map_lookup_action(G_ACTION_MAP(application), "preview-mode");
468     GVariant *variant = g_variant_new_string(font_manager_font_preview_mode_to_string(self->mode));
469     g_simple_action_set_state(G_SIMPLE_ACTION(action), variant);
470     return;
471 }
472 
473 static void
on_mode_action_activated(GSimpleAction * action,GVariant * parameter,FontManagerPreviewPane * self)474 on_mode_action_activated (GSimpleAction *action,
475                           GVariant *parameter,
476                           FontManagerPreviewPane *self)
477 {
478 
479     FontManagerFontPreviewMode mode = FONT_MANAGER_FONT_PREVIEW_MODE_LOREM_IPSUM;
480     const gchar *name = g_variant_get_string(parameter, NULL);
481     if (g_strcmp0(name, "Waterfall") == 0)
482         mode = FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL;
483     else if (g_strcmp0(name, "Preview") == 0)
484         mode = FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW;
485     font_manager_font_preview_set_preview_mode(FONT_MANAGER_FONT_PREVIEW(self->preview), mode);
486     update_mode(self);
487     GtkWidget *menu = gtk_notebook_get_action_widget(GTK_NOTEBOOK(self), GTK_PACK_START);
488     GtkPopover *popover = gtk_menu_button_get_popover(GTK_MENU_BUTTON(menu));
489     if (popover)
490         gtk_popover_popdown(popover);
491     return;
492 }
493 
494 static GtkWidget *
create_menu_button(FontManagerPreviewPane * self)495 create_menu_button (FontManagerPreviewPane *self)
496 {
497     GApplication *application = g_application_get_default();
498     GtkWidget *menu_button = gtk_menu_button_new();
499     GtkWidget *menu_icon = gtk_image_new_from_icon_name("view-more-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
500     gtk_container_add(GTK_CONTAINER(menu_button), menu_icon);
501     GMenu *mode_menu = g_menu_new();
502     GVariant *variant = g_variant_new_string("Waterfall");
503     g_autoptr(GSimpleAction) action = g_simple_action_new_stateful("preview-mode", G_VARIANT_TYPE_STRING, variant);
504     g_simple_action_set_enabled(action, TRUE);
505     g_action_map_add_action(G_ACTION_MAP(application), G_ACTION(action));
506     g_signal_connect(action, "activate", G_CALLBACK(on_mode_action_activated), self);
507     for (gint i = 0; i <= FONT_MANAGER_FONT_PREVIEW_MODE_LOREM_IPSUM; i++) {
508         const gchar *action_state = font_manager_font_preview_mode_to_string((FontManagerFontPreviewMode) i);
509         const gchar *display_name = font_manager_font_preview_mode_to_translatable_string((FontManagerFontPreviewMode) i);
510         g_autofree gchar *action_name = g_strdup_printf("app.preview-mode::%s", action_state);
511         g_autofree gchar *accel = g_strdup_printf("<Alt>%i", i + 1);
512         const gchar *accels [] = { accel, NULL };
513         gtk_application_set_accels_for_action(GTK_APPLICATION(application), action_name, accels);
514         g_autoptr(GMenuItem) item = g_menu_item_new(display_name, action_name);
515         g_menu_item_set_attribute(item, "accel", "s", accels[0],
516                                         "action", "preview-mode",
517                                         "target-value", action_name);
518         g_menu_append_item(mode_menu, item);
519     }
520     gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menu_button), G_MENU_MODEL(mode_menu));
521     gtk_widget_show(menu_icon);
522     gtk_widget_show(menu_button);
523     font_manager_widget_set_margin(menu_button, 2);
524     gtk_widget_set_margin_top(menu_button, 1);
525     gtk_widget_set_margin_bottom(menu_button, 1);
526     return menu_button;
527 }
528 
529 static void
on_search_action_activated(GSimpleAction * action,G_GNUC_UNUSED GVariant * parameter,FontManagerPreviewPane * self)530 on_search_action_activated (GSimpleAction *action,
531                             G_GNUC_UNUSED GVariant *parameter,
532                             FontManagerPreviewPane *self)
533 {
534     gboolean current_state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->search));
535     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->search), !current_state);
536     return;
537 }
538 
539 static void
font_manager_preview_pane_init(FontManagerPreviewPane * self)540 font_manager_preview_pane_init (FontManagerPreviewPane *self)
541 {
542     g_return_if_fail(self != NULL);
543     gtk_notebook_set_show_border(GTK_NOTEBOOK(self), FALSE);
544     self->preview = font_manager_font_preview_new();
545     self->character_map = font_manager_character_map_new();
546     self->properties = font_manager_properties_pane_new();
547     self->license = font_manager_license_pane_new();
548     self->metadata_update_required = TRUE;
549     FontManagerFontPreviewMode mode = font_manager_font_preview_get_preview_mode(FONT_MANAGER_FONT_PREVIEW(self->preview));
550     append_page(self, self->preview, font_manager_font_preview_mode_to_translatable_string(mode));
551     append_page(self, self->character_map, _("Characters"));
552     append_page(self, self->properties, _("Properties"));
553     append_page(self, self->license, _("License"));
554     g_autoptr(GSimpleAction) search = g_simple_action_new("character-search", NULL);
555     g_simple_action_set_enabled(search, TRUE);
556     g_signal_connect(search, "activate", G_CALLBACK(on_search_action_activated), self);
557     GtkApplication *application = GTK_APPLICATION(g_application_get_default());
558     g_action_map_add_action(G_ACTION_MAP(application), G_ACTION(search));
559     const gchar *accels [] = { "<Ctrl>f", NULL };
560     gtk_application_set_accels_for_action(application, "app.character-search", accels);
561     gtk_notebook_set_action_widget(GTK_NOTEBOOK(self), create_menu_button(self), GTK_PACK_START);
562     self->search = g_object_ref_sink(create_search_button(self));
563     gtk_notebook_set_action_widget(GTK_NOTEBOOK(self), self->search, GTK_PACK_END);
564     gtk_widget_show(self->preview);
565     gtk_widget_show(self->character_map);
566     gtk_widget_show(self->properties);
567     gtk_widget_show(self->license);
568     GBindingFlags flags = (G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
569     g_object_bind_property(self->preview, "preview-size", self, "preview-size", flags);
570     g_object_bind_property(self->preview, "preview-text", self, "preview-text", flags);
571     g_object_bind_property(self->preview, "preview-mode", self, "preview-mode", flags);
572     g_object_bind_property(self->preview, "samples", self, "samples", flags);
573     g_object_bind_property(self->character_map, "preview-size", self, "character-map-preview-size", flags);
574     g_signal_connect(self, "switch-page", G_CALLBACK(on_page_switch), NULL);
575     return;
576 }
577 
578 /**
579  * font_manager_preview_pane_show_uri:
580  * @self:       #FontManagerPreviewPane
581  * @uri:        filepath to display
582  * @index:      index of face within file
583  */
584 void
font_manager_preview_pane_show_uri(FontManagerPreviewPane * self,const gchar * uri,int index)585 font_manager_preview_pane_show_uri (FontManagerPreviewPane *self, const gchar *uri, int index)
586 {
587     g_return_if_fail(self != NULL);
588     if (self->current_uri && g_strcmp0(self->current_uri, uri) == 0)
589         return;
590     g_clear_pointer(&self->current_uri, g_free);
591     g_autoptr(GFile) file = g_file_new_for_commandline_arg(uri);
592     g_return_if_fail(g_file_is_native(file));
593     GError *error = NULL;
594     g_autoptr(GFileInfo) info = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
595                                                   G_FILE_QUERY_INFO_NONE, NULL, &error);
596     if (error != NULL) {
597         g_critical("Failed to query file info for %s : %s", uri, error->message);
598         g_clear_error(&error);
599         return;
600     }
601     const gchar *content_type = g_file_info_get_content_type(info);
602     if (!g_strrstr(content_type, "font")) {
603         g_warning("Ignoring unsupported filetype : %s", content_type);
604         return;
605     }
606     g_autofree gchar *path = g_file_get_path(file);
607     font_manager_add_application_font(path);
608     font_manager_clear_pango_cache(gtk_widget_get_pango_context((GtkWidget *) self));
609     g_autoptr(FontManagerFont) font = font_manager_font_new();
610     g_autoptr(JsonObject) source = font_manager_get_attributes_from_filepath(path, index, &error);
611     if (error != NULL) {
612         g_critical("%s : %s", error->message, path);
613         g_clear_error(&error);
614         return;
615     }
616     g_autoptr(JsonObject) orthography = font_manager_get_orthography_results(source);
617     if (!json_object_has_member(orthography, "Basic Latin")) {
618         GList *charset = font_manager_get_charset_from_filepath(path, index);
619         if (!self->samples) {
620             self->samples = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
621             g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_SAMPLES]);
622         }
623         g_autofree gchar *sample = font_manager_get_sample_string_for_orthography(orthography, charset);
624         if (sample) {
625             const gchar *description = json_object_get_string_member(source, "description");
626             g_hash_table_insert(self->samples, g_strdup(description), g_strdup(sample));
627         }
628         g_list_free(charset);
629     }
630     g_object_set(font, "source-object", source, NULL);
631     font_manager_preview_pane_set_font(self, font);
632     self->current_uri = g_strdup(uri);
633     return;
634 }
635 
636 /**
637  * font_msnager_preview_pane_set_font:
638  * @self:       #FontManagerPreviewPane
639  * @font:       #FontManagerFont
640  */
641 void
font_manager_preview_pane_set_font(FontManagerPreviewPane * self,FontManagerFont * font)642 font_manager_preview_pane_set_font (FontManagerPreviewPane *self, FontManagerFont *font)
643 {
644     g_return_if_fail(self != NULL);
645     g_clear_pointer(&self->current_uri, g_free);
646     if (g_set_object(&self->font, font))
647         g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_FONT]);
648     self->metadata_update_required = TRUE;
649     font_manager_preview_pane_update(self);
650     return;
651 }
652 
653 /**
654  * font_manager_preview_pane_set_orthography:
655  * @self:                                       #FontManagerPreviewPane
656  * @orthography: (transfer none) (nullable):    #FontManagerOrthography
657  *
658  * Filter character map using provided @orthography
659  */
660 void
font_manager_preview_pane_set_orthography(FontManagerPreviewPane * self,FontManagerOrthography * orthography)661 font_manager_preview_pane_set_orthography (FontManagerPreviewPane *self,
662                                            FontManagerOrthography *orthography)
663 {
664     g_return_if_fail(self != NULL);
665     font_manager_character_map_set_filter(FONT_MANAGER_CHARACTER_MAP(self->character_map), orthography);
666     return;
667 }
668 
669 /**
670  * font_manager_preview_pane_restore_state:
671  * @self:       #FontManagerPreviewPane
672  * @settings:   #GSettings
673  *
674  * Apply any applicable settings in @settings to @self and also bind those settings to
675  * their respective properties so that they are updated when any changes take place.
676  */
677 void
font_manager_preview_pane_restore_state(FontManagerPreviewPane * self,GSettings * settings)678 font_manager_preview_pane_restore_state (FontManagerPreviewPane *self, GSettings *settings)
679 {
680     g_return_if_fail(self != NULL);
681     g_return_if_fail(settings != NULL);
682     g_autofree gchar *preview_text = g_settings_get_string(settings, "preview-text");
683     g_object_set(self,
684                  "page",  g_settings_get_int(settings, "preview-page"),
685                  "preview-mode", g_settings_get_enum(settings, "preview-mode"),
686                  "preview-text", preview_text,
687                  "preview-size", g_settings_get_double(settings, "preview-font-size"),
688                  "character-map-preview-size", g_settings_get_double(settings, "charmap-font-size"),
689                  NULL);
690     g_settings_bind(settings, "preview-page", self, "page", G_SETTINGS_BIND_DEFAULT);
691     g_settings_bind(settings, "preview-mode", self, "preview-mode", G_SETTINGS_BIND_DEFAULT);
692     g_settings_bind(settings, "preview-text", self, "preview-text", G_SETTINGS_BIND_DEFAULT);
693     g_settings_bind(settings, "preview-font-size", self, "preview-size", G_SETTINGS_BIND_DEFAULT);
694     g_settings_bind(settings, "charmap-font-size", self, "character-map-preview-size", G_SETTINGS_BIND_DEFAULT);
695     return;
696 }
697 
698 /**
699  * font_manager_preview_pane_set_max_waterfall_size:
700  * @self:           #FontManagerFontPreview
701  * @size_points:    Maximum size to use for waterfall previews.
702  */
703 void
font_manager_preview_pane_set_max_waterfall_size(FontManagerPreviewPane * self,gdouble size_points)704 font_manager_preview_pane_set_max_waterfall_size (FontManagerPreviewPane *self,
705                                                   gdouble size_points)
706 {
707     font_manager_font_preview_set_max_waterfall_size(FONT_MANAGER_FONT_PREVIEW(self->preview), size_points);
708     return;
709 }
710 
711 /**
712  * font_manager_preview_pane_new:
713  *
714  * Returns: (transfer full): A newly created #FontManagerPreviewPane.
715  * Free the returned object using #g_object_unref().
716  */
717 GtkWidget *
font_manager_preview_pane_new()718 font_manager_preview_pane_new ()
719 {
720     return g_object_new(FONT_MANAGER_TYPE_PREVIEW_PANE, NULL);
721 }
722