1 /* font-manager-character-map.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-character-map.h"
22 
23 /**
24  * SECTION: font-manager-character-map
25  * @short_description: Browse available characters
26  * @title: Character Map
27  * @include: font-manager-character-map.h
28  *
29  * Widget which displays available characters in the selected font.
30  */
31 
32 struct _FontManagerCharacterMap
33 {
34     GtkBox   parent_instance;
35 
36     GtkWidget   *name;
37     GtkWidget   *count;
38     GtkWidget   *codepoint;
39     GtkWidget   *character_map;
40     GtkWidget   *action_area;
41     GtkWidget   *fontscale;
42     GtkWidget   *search;
43 
44     gdouble                     preview_size;
45     FontManagerFont             *font;
46     FontManagerCodepointList    *codepoint_list;
47 };
48 
49 G_DEFINE_TYPE(FontManagerCharacterMap, font_manager_character_map, GTK_TYPE_BOX)
50 
51 enum
52 {
53     PROP_RESERVED,
54     PROP_FONT,
55     PROP_ACTIVE_CHAR,
56     PROP_PREVIEW_SIZE,
57     PROP_SEARCH_MODE,
58     N_PROPERTIES
59 };
60 
61 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
62 
63 void font_manager_character_map_set_active_character(FontManagerCharacterMap *self, gunichar ac);
64 
65 static void
font_manager_character_map_dispose(GObject * gobject)66 font_manager_character_map_dispose (GObject *gobject)
67 {
68     g_return_if_fail(gobject != NULL);
69     FontManagerCharacterMap *self = FONT_MANAGER_CHARACTER_MAP(gobject);
70     g_clear_object(&self->font);
71     g_clear_object(&self->codepoint_list);
72     G_OBJECT_CLASS(font_manager_character_map_parent_class)->dispose(gobject);
73     return;
74 }
75 
76 static void
font_manager_character_map_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)77 font_manager_character_map_get_property (GObject *gobject,
78                                          guint property_id,
79                                          GValue *value,
80                                          GParamSpec *pspec)
81 {
82     g_return_if_fail(gobject != NULL);
83     FontManagerCharacterMap *self = FONT_MANAGER_CHARACTER_MAP(gobject);
84     UnicodeCharacterMap *charmap = UNICODE_CHARACTER_MAP(self->character_map);
85     gunichar ac = -1;
86     GtkWidget *child = NULL;
87     switch (property_id) {
88         case PROP_FONT:
89             g_value_set_object(value, self->font);
90             break;
91         case PROP_ACTIVE_CHAR:
92             ac = unicode_character_map_get_active_character(charmap);
93             g_value_set_uint(value, (guint) ac);
94             break;
95         case PROP_SEARCH_MODE:
96             child = gtk_stack_get_visible_child(GTK_STACK(self->action_area));
97             g_value_set_boolean(value, child == self->search);
98             break;
99         case PROP_PREVIEW_SIZE:
100             g_value_set_double(value, self->preview_size);
101             break;
102         default:
103             G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
104     }
105     return;
106 }
107 
108 static void
font_manager_character_map_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * pspec)109 font_manager_character_map_set_property (GObject *gobject,
110                                         guint property_id,
111                                         const GValue *value,
112                                         GParamSpec *pspec)
113 {
114     g_return_if_fail(gobject != NULL);
115     FontManagerCharacterMap *self = FONT_MANAGER_CHARACTER_MAP(gobject);
116     GtkWidget *child = NULL;
117     switch (property_id) {
118         case PROP_FONT:
119             font_manager_character_map_set_font(self, g_value_get_object(value));
120             break;
121         case PROP_ACTIVE_CHAR:
122             font_manager_character_map_set_active_character(self, g_value_get_uint(value));
123             break;
124         case PROP_SEARCH_MODE:
125             child = g_value_get_boolean(value) ? self->search : self->fontscale;
126             gtk_stack_set_visible_child(GTK_STACK(self->action_area), child);
127             break;
128         case PROP_PREVIEW_SIZE:
129             self->preview_size = g_value_get_double(value);
130             break;
131         default:
132             G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
133     }
134     return;
135 }
136 
137 static void
font_manager_character_map_class_init(FontManagerCharacterMapClass * klass)138 font_manager_character_map_class_init (FontManagerCharacterMapClass *klass)
139 {
140     GObjectClass *object_class = G_OBJECT_CLASS(klass);
141 
142     object_class->dispose = font_manager_character_map_dispose;
143     object_class->get_property = font_manager_character_map_get_property;
144     object_class->set_property = font_manager_character_map_set_property;
145 
146     /**
147      * FontManagerCharacterMap:font:
148      *
149      * #FontManagerFont to display
150      */
151     obj_properties[PROP_FONT] = g_param_spec_object("font",
152                                                     NULL,
153                                                     "Currently selected font",
154                                                     FONT_MANAGER_TYPE_FONT,
155                                                     G_PARAM_STATIC_STRINGS |
156                                                     G_PARAM_READWRITE |
157                                                     G_PARAM_EXPLICIT_NOTIFY);
158 
159     /**
160      * FontManagerCharacterMap:active-character:
161      *
162      * Currently selected character
163      */
164     obj_properties[PROP_ACTIVE_CHAR] = g_param_spec_uint("active-character",
165                                                         NULL,
166                                                         "Active character",
167                                                         0,
168                                                         UNICODE_UNICHAR_MAX,
169                                                         0,
170                                                         G_PARAM_READWRITE |
171                                                         G_PARAM_STATIC_STRINGS |
172                                                         G_PARAM_EXPLICIT_NOTIFY);
173 
174     /**
175      * FontManagerCharacterMap:preview-size:
176      *
177      * Font preview size
178      */
179     obj_properties[PROP_PREVIEW_SIZE] = g_param_spec_double("preview-size",
180                                                             NULL,
181                                                             "Preview size",
182                                                             FONT_MANAGER_MIN_FONT_SIZE,
183                                                             FONT_MANAGER_MAX_FONT_SIZE,
184                                                             FONT_MANAGER_CHARACTER_MAP_PREVIEW_SIZE,
185                                                             G_PARAM_STATIC_STRINGS |
186                                                             G_PARAM_READWRITE);
187 
188     /**
189      * FontManagerCharacterMap:search-mode:
190      *
191      * Whether search mode is active or not
192      */
193     obj_properties[PROP_SEARCH_MODE] = g_param_spec_boolean("search-mode",
194                                                             NULL,
195                                                             "Whether search mode is active or not",
196                                                             FALSE,
197                                                             G_PARAM_STATIC_STRINGS |
198                                                             G_PARAM_READWRITE);
199 
200     g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties);
201     return;
202 }
203 
204 static GtkWidget *
create_info_widget(FontManagerCharacterMap * self)205 create_info_widget (FontManagerCharacterMap *self)
206 {
207     GtkWidget *info = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
208     self->name = gtk_label_new(NULL);
209     self->count = gtk_label_new(NULL);
210     self->codepoint = gtk_label_new(NULL);
211     GtkStyleContext *ctx = gtk_widget_get_style_context(self->count);
212     gtk_style_context_add_class(ctx, "CellRendererPill");
213     gtk_widget_set_opacity(self->name, 0.75);
214     gtk_widget_set_opacity(self->codepoint, 0.75);
215     gtk_widget_set_margin_start(self->codepoint, FONT_MANAGER_DEFAULT_MARGIN);
216     gtk_label_set_selectable(GTK_LABEL(self->name), TRUE);
217     gtk_label_set_selectable(GTK_LABEL(self->codepoint), TRUE);
218     gtk_widget_set_can_default(self->name, FALSE);
219     gtk_widget_set_can_default(self->codepoint, FALSE);
220     gtk_widget_set_can_focus(self->name, FALSE);
221     gtk_widget_set_can_focus(self->codepoint, FALSE);
222     gtk_box_pack_start(GTK_BOX(info), self->codepoint, FALSE, FALSE, 0);
223     gtk_box_set_center_widget(GTK_BOX(info), self->name);
224     gtk_box_pack_end(GTK_BOX(info), self->count, FALSE, FALSE, 0);
225     font_manager_widget_set_margin(info, FONT_MANAGER_DEFAULT_MARGIN);
226     gtk_widget_show_all(info);
227     return info;
228 }
229 
230 GtkWidget *
create_action_area(FontManagerCharacterMap * self)231 create_action_area (FontManagerCharacterMap *self)
232 {
233     self->action_area = gtk_stack_new();
234     self->fontscale = font_manager_font_scale_new();
235     self->search = unicode_search_bar_new();
236     gtk_stack_add_named(GTK_STACK(self->action_area), self->fontscale, gtk_widget_get_name(self->fontscale));
237     gtk_stack_add_named(GTK_STACK(self->action_area), self->search, gtk_widget_get_name(self->search));
238     gtk_widget_show(self->search);
239     gtk_widget_show(self->fontscale);
240     gtk_widget_show(self->action_area);
241     gtk_stack_set_visible_child(GTK_STACK(self->action_area), self->fontscale);
242     gtk_stack_set_transition_type(GTK_STACK(self->action_area), GTK_STACK_TRANSITION_TYPE_CROSSFADE);
243     return self->action_area;
244 }
245 
246 static void
font_manager_character_map_init(FontManagerCharacterMap * self)247 font_manager_character_map_init (FontManagerCharacterMap *self)
248 {
249     g_return_if_fail(self != NULL);
250     gtk_widget_set_name(GTK_WIDGET(self), "FontManagerCharacterMap");
251     gtk_orientable_set_orientation(GTK_ORIENTABLE(self), GTK_ORIENTATION_VERTICAL);
252     self->codepoint_list = font_manager_codepoint_list_new();
253     self->character_map = unicode_character_map_new();
254     font_manager_widget_set_expand(self->character_map, TRUE);
255     gtk_box_pack_start(GTK_BOX(self), create_info_widget(self), FALSE, FALSE, 0);
256     GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
257     gtk_container_add(GTK_CONTAINER(scroll), self->character_map);
258     gtk_box_pack_start(GTK_BOX(self), scroll, TRUE, TRUE, 0);
259     gtk_box_pack_end(GTK_BOX(self), create_action_area(self), FALSE, FALSE, 0);
260     gtk_widget_show(self->character_map);
261     gtk_widget_show(scroll);
262     unicode_search_bar_set_character_map(UNICODE_SEARCH_BAR(self->search),
263                                          UNICODE_CHARACTER_MAP(self->character_map));
264     GBindingFlags flags = G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL;
265     g_object_bind_property(self, "preview-size", self->fontscale, "value", flags);
266     g_object_bind_property(self->character_map, "preview-size", self->fontscale, "value", flags);
267     g_object_bind_property(self->character_map, "active-character", self, "active-character", flags);
268     return;
269 }
270 
271 void
font_manager_character_map_set_active_character(FontManagerCharacterMap * self,gunichar ac)272 font_manager_character_map_set_active_character(FontManagerCharacterMap *self, gunichar ac)
273 {
274     g_return_if_fail(self != NULL);
275     g_autofree gchar *codepoint_str = g_markup_printf_escaped("<b>U+%4.4X</b>", ac);
276     const gchar *name = unicode_get_codepoint_name(ac);
277     g_autofree gchar *name_str = g_markup_printf_escaped("<b>%s</b>", name);
278     gtk_label_set_markup(GTK_LABEL(self->codepoint), codepoint_str);
279     gtk_label_set_markup(GTK_LABEL(self->name), name_str);
280     return;
281 }
282 
283 void
font_manager_character_map_set_count(FontManagerCharacterMap * self)284 font_manager_character_map_set_count (FontManagerCharacterMap *self)
285 {
286     gint count = unicode_codepoint_list_get_last_index(UNICODE_CODEPOINT_LIST(self->codepoint_list));
287     g_autofree gchar *count_str = count >= 0 ? g_strdup_printf("   %i   ", count) : g_strdup("   0   ");
288     gtk_label_set_label(GTK_LABEL(self->count), count_str);
289     return;
290 }
291 
292 static void
font_manager_character_map_update(FontManagerCharacterMap * self)293 font_manager_character_map_update (FontManagerCharacterMap *self)
294 {
295     unicode_character_map_set_codepoint_list(UNICODE_CHARACTER_MAP(self->character_map), NULL);
296     g_autofree gchar *description = NULL;
297     g_autoptr(JsonObject) font = NULL;
298     if (self->font && font_manager_json_proxy_is_valid(FONT_MANAGER_JSON_PROXY(self->font)))
299         g_object_get(G_OBJECT(self->font), "description", &description, "source-object", &font, NULL);
300     else
301         description = g_strdup(FONT_MANAGER_DEFAULT_FONT);
302     PangoFontDescription *font_desc = pango_font_description_from_string(description);
303     font_manager_codepoint_list_set_font(self->codepoint_list, font);
304     UnicodeCharacterMap *charmap = UNICODE_CHARACTER_MAP(self->character_map);
305     unicode_character_map_set_font_desc(charmap, font_desc);
306     unicode_character_map_set_codepoint_list(charmap, UNICODE_CODEPOINT_LIST(self->codepoint_list));
307     pango_font_description_free(font_desc);
308     font_manager_character_map_set_count(self);
309     return;
310 }
311 
312 /**
313  * font_manager_character_map_set_font:
314  * @self:               #FontManagerCharacterMap
315  * @font: (nullable):   #FontManagerFont
316  */
317 void
font_manager_character_map_set_font(FontManagerCharacterMap * self,FontManagerFont * font)318 font_manager_character_map_set_font (FontManagerCharacterMap *self, FontManagerFont *font)
319 {
320     g_return_if_fail(self != NULL);
321     if (g_set_object(&self->font, font))
322         g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_FONT]);
323     font_manager_character_map_update(self);
324     return;
325 }
326 
327 /**
328  * font_manager_character_map_set_filter:
329  * @self:                                       #FontManagerCharacterMap
330  * @orthography: (nullable) (transfer none):    #FontManagerOrthography
331  */
332 void
font_manager_character_map_set_filter(FontManagerCharacterMap * self,FontManagerOrthography * orthography)333 font_manager_character_map_set_filter (FontManagerCharacterMap *self, FontManagerOrthography *orthography)
334 {
335     unicode_character_map_set_codepoint_list(UNICODE_CHARACTER_MAP(self->character_map), NULL);
336     GList *filter = NULL;
337     if (orthography)
338         filter = font_manager_orthography_get_filter(orthography);
339     font_manager_codepoint_list_set_filter(self->codepoint_list, filter);
340     font_manager_character_map_set_count(self);
341     unicode_character_map_set_codepoint_list(UNICODE_CHARACTER_MAP(self->character_map),
342                                              UNICODE_CODEPOINT_LIST(self->codepoint_list));
343     return;
344 }
345 
346 /**
347  * font_manager_character_map_new:
348  *
349  * Returns: (transfer full): A newly created #FontManagerCharacterMap.
350  * Free the returned object using #g_object_unref().
351  */
352 GtkWidget *
font_manager_character_map_new()353 font_manager_character_map_new ()
354 {
355     return g_object_new(FONT_MANAGER_TYPE_CHARACTER_MAP, NULL);
356 }
357 
358