1 /* font-manager-font-preview.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-font-preview.h"
22
23 /**
24 * SECTION: font-manager-font-preview
25 * @short_description: Full featured font preview widget
26 * @title: Font Preview
27 * @include: font-manager-font-preview.h
28 *
29 * This widget allows previewing of font files in various ways.
30 */
31
32 GType
font_manager_font_preview_mode_get_type(void)33 font_manager_font_preview_mode_get_type (void)
34 {
35 static volatile gsize g_define_type_id__volatile = 0;
36
37 if (g_once_init_enter (&g_define_type_id__volatile))
38 {
39 static const GEnumValue values[] = {
40 { FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW, "FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW", "preview" },
41 { FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL, "FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL", "waterfall" },
42 { FONT_MANAGER_FONT_PREVIEW_MODE_LOREM_IPSUM, "FONT_MANAGER_FONT_PREVIEW_MODE_LOREM_IPSUM", "lorem-ipsum" },
43 { 0, NULL, NULL }
44 };
45 GType g_define_type_id =
46 g_enum_register_static (g_intern_static_string ("FontManagerFontPreviewMode"), values);
47 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
48 }
49
50 return g_define_type_id__volatile;
51 }
52
53 /**
54 * font_manager_font_preview_mode_to_string:
55 * @mode: #FontManagerFontPreviewMode
56 *
57 * Returns: (transfer none) (nullable): @mode as a string
58 */
59 const gchar *
font_manager_font_preview_mode_to_string(FontManagerFontPreviewMode mode)60 font_manager_font_preview_mode_to_string (FontManagerFontPreviewMode mode)
61 {
62 switch (mode) {
63 case FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW:
64 return "Preview";
65 case FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL:
66 return "Waterfall";
67 case FONT_MANAGER_FONT_PREVIEW_MODE_LOREM_IPSUM:
68 return "Lorem Ipsum";
69 default:
70 return NULL;
71 }
72 }
73
74 /**
75 * font_manager_font_preview_mode_to_translatable_string:
76 * @mode: #FontManagerFontPreviewMode
77 *
78 * Returns: (transfer none) (nullable): @mode as a localized string, if available.
79 */
80 const gchar *
font_manager_font_preview_mode_to_translatable_string(FontManagerFontPreviewMode mode)81 font_manager_font_preview_mode_to_translatable_string (FontManagerFontPreviewMode mode)
82 {
83 switch (mode) {
84 case FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW:
85 return _("Preview");
86 case FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL:
87 return _("Waterfall");
88 case FONT_MANAGER_FONT_PREVIEW_MODE_LOREM_IPSUM:
89 return "Lorem Ipsum";
90 default:
91 return NULL;
92 }
93 }
94
95
96 #define MIN_FONT_SIZE FONT_MANAGER_MIN_FONT_SIZE
97 #define MAX_FONT_SIZE FONT_MANAGER_MAX_FONT_SIZE
98 #define DEFAULT_PREVIEW_SIZE FONT_MANAGER_DEFAULT_PREVIEW_SIZE
99 #define DEFAULT_WATERFALL_MAX_SIZE 48.0
100
101 struct _FontManagerFontPreview
102 {
103 GtkBox parent_instance;
104
105 gchar *pangram;
106 gchar *default_pangram;
107 gchar *preview;
108 gchar *default_preview;
109 gchar *restore_preview;
110 GtkWidget *controls;
111 GtkWidget *fontscale;
112 GtkWidget *textview;
113 GHashTable *samples;
114
115 gint max_waterfall_size;
116 gdouble preview_size;
117 gboolean allow_edit;
118 GtkJustification justification;
119 FontManagerFontPreviewMode mode;
120 PangoFontDescription *font_desc;
121 };
122
123 G_DEFINE_TYPE(FontManagerFontPreview, font_manager_font_preview, GTK_TYPE_BOX)
124
125 enum
126 {
127 PROP_RESERVED,
128 PROP_PREVIEW_MODE,
129 PROP_PREVIEW_SIZE,
130 PROP_PREVIEW_TEXT,
131 PROP_FONT_DESC,
132 PROP_JUSTIFICATION,
133 PROP_SAMPLES,
134 PROP_WATERFALL_MAX,
135 N_PROPERTIES
136 };
137
138 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
139
140 static void
font_manager_font_preview_dispose(GObject * gobject)141 font_manager_font_preview_dispose (GObject *gobject)
142 {
143 g_return_if_fail(gobject != NULL);
144 FontManagerFontPreview *self = FONT_MANAGER_FONT_PREVIEW(gobject);
145 g_clear_pointer(&self->pangram, g_free);
146 g_clear_pointer(&self->default_pangram, g_free);
147 g_clear_pointer(&self->preview, g_free);
148 g_clear_pointer(&self->default_preview, g_free);
149 g_clear_pointer(&self->restore_preview, g_free);
150 g_clear_pointer(&self->font_desc, pango_font_description_free);
151 g_clear_pointer(&self->samples, g_hash_table_unref);
152 G_OBJECT_CLASS(font_manager_font_preview_parent_class)->dispose(gobject);
153 return;
154 }
155
156 static void
font_manager_font_preview_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)157 font_manager_font_preview_get_property (GObject *gobject,
158 guint property_id,
159 GValue *value,
160 GParamSpec *pspec)
161 {
162 g_return_if_fail(gobject != NULL);
163 FontManagerFontPreview *self = FONT_MANAGER_FONT_PREVIEW(gobject);
164 g_autofree gchar *font = NULL;
165 switch (property_id) {
166 case PROP_PREVIEW_SIZE:
167 g_value_set_double(value, font_manager_font_preview_get_preview_size(self));
168 break;
169 case PROP_PREVIEW_MODE:
170 g_value_set_enum(value, font_manager_font_preview_get_preview_mode(self));
171 break;
172 case PROP_PREVIEW_TEXT:
173 g_value_set_string(value, self->preview);
174 break;
175 case PROP_FONT_DESC:
176 font = font_manager_font_preview_get_font_description(self);
177 g_value_set_string(value, font);
178 break;
179 case PROP_JUSTIFICATION:
180 g_value_set_enum(value, (gint) font_manager_font_preview_get_justification(self));
181 break;
182 case PROP_SAMPLES:
183 g_value_set_boxed(value, self->samples);
184 break;
185 case PROP_WATERFALL_MAX:
186 g_value_set_double(value, self->max_waterfall_size);
187 break;
188 default:
189 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
190 }
191 return;
192 }
193
194 static void
font_manager_font_preview_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * pspec)195 font_manager_font_preview_set_property (GObject *gobject,
196 guint property_id,
197 const GValue *value,
198 GParamSpec *pspec)
199 {
200 g_return_if_fail(gobject != NULL);
201 FontManagerFontPreview *self = FONT_MANAGER_FONT_PREVIEW(gobject);
202 switch (property_id) {
203 case PROP_PREVIEW_SIZE:
204 font_manager_font_preview_set_preview_size(self, g_value_get_double(value));
205 break;
206 case PROP_PREVIEW_MODE:
207 font_manager_font_preview_set_preview_mode(self, g_value_get_enum(value));
208 break;
209 case PROP_PREVIEW_TEXT:
210 font_manager_font_preview_set_preview_text(self, g_value_get_string(value));
211 break;
212 case PROP_FONT_DESC:
213 font_manager_font_preview_set_font_description(self, g_value_get_string(value));
214 break;
215 case PROP_JUSTIFICATION:
216 font_manager_font_preview_set_justification(self, (GtkJustification) g_value_get_enum(value));
217 break;
218 case PROP_SAMPLES:
219 font_manager_font_preview_set_sample_strings(self, g_value_get_boxed(value));
220 break;
221 case PROP_WATERFALL_MAX:
222 font_manager_font_preview_set_max_waterfall_size(self, g_value_get_double(value));
223 break;
224 default:
225 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
226 }
227 return;
228 }
229
230 static void
font_manager_font_preview_class_init(FontManagerFontPreviewClass * klass)231 font_manager_font_preview_class_init (FontManagerFontPreviewClass *klass)
232 {
233 GObjectClass *object_class = G_OBJECT_CLASS(klass);
234
235 object_class->dispose = font_manager_font_preview_dispose;
236 object_class->get_property = font_manager_font_preview_get_property;
237 object_class->set_property = font_manager_font_preview_set_property;
238
239 /**
240 * FontManagerFontPreview:preview-mode:
241 *
242 * The current font preview mode.
243 */
244 obj_properties[PROP_PREVIEW_MODE] = g_param_spec_enum("preview-mode",
245 NULL,
246 "Font preview mode.",
247 FONT_MANAGER_TYPE_FONT_PREVIEW_MODE,
248 (gint) FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL,
249 G_PARAM_STATIC_STRINGS |
250 G_PARAM_READWRITE |
251 G_PARAM_EXPLICIT_NOTIFY);
252
253 /**
254 * FontManagerFontPreview:preview-size:
255 *
256 * The current font preview size.
257 */
258 obj_properties[PROP_PREVIEW_SIZE] = g_param_spec_double("preview-size",
259 NULL,
260 "Font preview size in points.",
261 MIN_FONT_SIZE,
262 MAX_FONT_SIZE,
263 DEFAULT_PREVIEW_SIZE,
264 G_PARAM_STATIC_STRINGS |
265 G_PARAM_READWRITE |
266 G_PARAM_EXPLICIT_NOTIFY);
267
268 /**
269 * FontManagerFontPreview:preview-text:
270 *
271 * Current preview text.
272 */
273 obj_properties[PROP_PREVIEW_TEXT] = g_param_spec_string("preview-text",
274 NULL,
275 "Current preview text.",
276 NULL,
277 G_PARAM_STATIC_STRINGS |
278 G_PARAM_READWRITE |
279 G_PARAM_EXPLICIT_NOTIFY);
280
281 /**
282 * FontManagerFontPreview:font-description:
283 *
284 * Current font dsescription as a string.
285 */
286 obj_properties[PROP_FONT_DESC] = g_param_spec_string("font-description",
287 NULL,
288 "Current font description as a string.",
289 FONT_MANAGER_DEFAULT_FONT,
290 G_PARAM_STATIC_STRINGS |
291 G_PARAM_READWRITE |
292 G_PARAM_EXPLICIT_NOTIFY);
293
294 /**
295 * FontManagerFontPreview:justification:
296 *
297 * Preview text justification.
298 */
299 obj_properties[PROP_JUSTIFICATION] = g_param_spec_enum("justification",
300 NULL,
301 "Preview text justification.",
302 GTK_TYPE_JUSTIFICATION,
303 GTK_JUSTIFY_CENTER,
304 G_PARAM_STATIC_STRINGS |
305 G_PARAM_READWRITE |
306 G_PARAM_EXPLICIT_NOTIFY);
307
308 /**
309 * FontManagerFontPreview:sample-strings:
310 *
311 * Dictionary of sample strings
312 */
313 obj_properties[PROP_SAMPLES] = g_param_spec_boxed("samples",
314 NULL,
315 "Dictionary of sample strings",
316 G_TYPE_HASH_TABLE,
317 G_PARAM_STATIC_STRINGS |
318 G_PARAM_READWRITE |
319 G_PARAM_EXPLICIT_NOTIFY);
320
321 /**
322 * FontManagerFontPreview:max-waterfall-size:
323 *
324 * The current maximum waterfall preview size.
325 */
326 obj_properties[PROP_WATERFALL_MAX] = g_param_spec_double("max-waterfall-size",
327 NULL,
328 "Maximum waterfall preview size in points.",
329 MIN_FONT_SIZE,
330 MAX_FONT_SIZE,
331 DEFAULT_WATERFALL_MAX_SIZE,
332 G_PARAM_STATIC_STRINGS |
333 G_PARAM_READWRITE |
334 G_PARAM_EXPLICIT_NOTIFY);
335
336 g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties);
337 return;
338 }
339
340 static void
update_revealer_state(FontManagerFontPreview * self,FontManagerFontPreviewMode mode)341 update_revealer_state (FontManagerFontPreview *self, FontManagerFontPreviewMode mode)
342 {
343 g_return_if_fail(self != NULL);
344 gboolean controls_visible = gtk_revealer_get_child_revealed(GTK_REVEALER(self->controls));
345 GtkRevealerTransitionType trans_type = controls_visible ?
346 GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP :
347 GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN;
348 gtk_revealer_set_transition_type(GTK_REVEALER(self->controls), trans_type);
349 gboolean fontscale_visible = gtk_revealer_get_child_revealed(GTK_REVEALER(self->controls));
350 trans_type = fontscale_visible ? GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN :
351 GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP;
352 gtk_revealer_set_transition_type(GTK_REVEALER(self->fontscale), trans_type);
353 gtk_revealer_set_reveal_child(GTK_REVEALER(self->fontscale),
354 (mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW ||
355 mode == FONT_MANAGER_FONT_PREVIEW_MODE_LOREM_IPSUM));
356 gtk_revealer_set_reveal_child(GTK_REVEALER(self->controls),
357 (mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW));
358 return;
359 }
360
361 static gint current_line = FONT_MANAGER_MIN_FONT_SIZE;
362
363 static gboolean
generate_waterfall_line(FontManagerFontPreview * self)364 generate_waterfall_line (FontManagerFontPreview *self)
365 {
366 GtkTextIter iter;
367 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(self->textview));
368 GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table(buffer);
369 gint i = current_line;
370 g_autofree gchar *size_point = NULL;
371 g_autofree gchar *line = g_strdup_printf("%i", i);
372 size_point = g_strdup_printf(i < 10 ? " %spt. " : "%spt. ", line);
373 gtk_text_buffer_get_iter_at_line(buffer, &iter, i);
374 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, size_point, -1, "SizePoint", NULL);
375 if (!gtk_text_tag_table_lookup(tag_table, line))
376 gtk_text_buffer_create_tag(buffer, line, "size-points", (gdouble) i, NULL);
377 gtk_text_buffer_get_end_iter(buffer, &iter);
378 g_autofree gchar *pangram = g_strdup_printf("%s\n", self->pangram);
379 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, pangram, -1, line, "FontDescription", NULL);
380 current_line++;
381 return current_line > self->max_waterfall_size ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE;
382 }
383
384 static void
generate_waterfall_preview(FontManagerFontPreview * self)385 generate_waterfall_preview (FontManagerFontPreview *self)
386 {
387 g_return_if_fail(self != NULL);
388 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(self->textview));
389 gtk_text_buffer_set_text(buffer, "", -1);
390 g_idle_remove_by_data(self);
391 current_line = FONT_MANAGER_MIN_FONT_SIZE;
392 g_idle_add((GSourceFunc) generate_waterfall_line, self);
393 return;
394 }
395
396 static void
apply_font_description(FontManagerFontPreview * self)397 apply_font_description (FontManagerFontPreview *self)
398 {
399 g_return_if_fail(self != NULL);
400 if (self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL)
401 return;
402 GtkTextIter start, end;
403 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(self->textview));
404 gtk_text_buffer_get_bounds(buffer, &start, &end);
405 gtk_text_buffer_apply_tag_by_name(buffer, "FontDescription", &start, &end);
406 return;
407 }
408
409 /* Prevent tofu if possible */
410 static void
update_sample_string(FontManagerFontPreview * self)411 update_sample_string (FontManagerFontPreview *self)
412 {
413 g_return_if_fail(self != NULL);
414 g_autofree gchar *description = pango_font_description_to_string(self->font_desc);
415 gboolean pangram_changed = FALSE;
416 if (self->samples && g_hash_table_contains(self->samples, description)) {
417 const gchar *sample = g_hash_table_lookup(self->samples, description);
418 if (sample) {
419 g_free(self->pangram);
420 self->pangram = g_strdup(sample);
421 pangram_changed = TRUE;
422 if (self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW
423 && g_strcmp0(self->preview, self->default_preview) == 0) {
424 self->restore_preview = g_strdup(self->preview);
425 font_manager_font_preview_set_preview_text(self, self->pangram);
426 }
427 }
428 } else {
429 if (g_strcmp0(self->pangram, self->default_pangram) != 0) {
430 g_free(self->pangram);
431 self->pangram = g_strdup(self->default_pangram);
432 pangram_changed = TRUE;
433 }
434 if (self->restore_preview && self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW) {
435 font_manager_font_preview_set_preview_text(self, self->restore_preview);
436 g_clear_pointer(&self->restore_preview, g_free);
437 }
438 }
439 if (pangram_changed && self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL)
440 generate_waterfall_preview(self);
441 return;
442 }
443
444 static void
update_font_description(FontManagerFontPreview * self)445 update_font_description (FontManagerFontPreview *self)
446 {
447 g_return_if_fail(self != NULL && self->font_desc != NULL);
448 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(self->textview));
449 GtkTextTagTable *tag_table = gtk_text_buffer_get_tag_table(buffer);
450 GtkTextTag *font_description = gtk_text_tag_table_lookup(tag_table, "FontDescription");
451 g_return_if_fail(font_description != NULL);
452 g_object_set(G_OBJECT(font_description),
453 "font-desc", self->font_desc,
454 "size-points", self->preview_size,
455 "fallback", FALSE,
456 NULL);
457 return;
458 }
459
460 static void
on_edit_toggled(FontManagerFontPreview * self,gboolean active)461 on_edit_toggled (FontManagerFontPreview *self, gboolean active)
462 {
463 g_return_if_fail(self != NULL);
464 self->allow_edit = active;
465 gtk_text_view_set_editable(GTK_TEXT_VIEW(self->textview), active);
466 return;
467 }
468
469 static void
on_buffer_changed(FontManagerFontPreview * self,GtkTextBuffer * buffer)470 on_buffer_changed (FontManagerFontPreview *self, GtkTextBuffer *buffer)
471 {
472 g_return_if_fail(self != NULL);
473 gboolean undo_available = FALSE;
474 GtkWidget *controls = gtk_bin_get_child(GTK_BIN(self->controls));
475 if (self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW) {
476 GtkTextIter start, end;
477 gtk_text_buffer_get_bounds(buffer, &start, &end);
478 gchar *current_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
479 undo_available = (g_strcmp0(self->default_preview, current_text) != 0);
480 g_free(self->preview);
481 self->preview = current_text;
482 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_PREVIEW_TEXT]);
483 }
484 g_object_set(G_OBJECT(controls), "undo-available", undo_available, NULL);
485 return;
486 }
487
488 static void
on_undo_clicked(FontManagerFontPreview * self,FontManagerPreviewControls * controls)489 on_undo_clicked (FontManagerFontPreview *self, FontManagerPreviewControls *controls)
490 {
491 g_return_if_fail(self != NULL);
492 g_return_if_fail(self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW);
493 font_manager_font_preview_set_preview_text(self, self->default_preview);
494 return;
495 }
496
497 static gboolean
on_event(FontManagerFontPreview * self,GdkEvent * event,GtkWidget * widget)498 on_event (FontManagerFontPreview *self, GdkEvent *event, GtkWidget *widget)
499 {
500 g_return_val_if_fail(self != NULL, GDK_EVENT_PROPAGATE);
501 g_return_val_if_fail(event != NULL, GDK_EVENT_PROPAGATE);
502 if (event->type == GDK_SCROLL)
503 return GDK_EVENT_PROPAGATE;
504 if (self->allow_edit && self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW)
505 return GDK_EVENT_PROPAGATE;
506 GdkWindow *text_window = gtk_text_view_get_window(GTK_TEXT_VIEW(self->textview), GTK_TEXT_WINDOW_TEXT);
507 gdk_window_set_cursor(text_window, NULL);
508 return GDK_EVENT_STOP;
509 }
510
511 static GtkTextTagTable *
font_manager_text_tag_table_new(void)512 font_manager_text_tag_table_new (void)
513 {
514 GtkTextTagTable *tags = gtk_text_tag_table_new();
515 g_autoptr(GtkTextTag) font = gtk_text_tag_new("FontDescription");
516 g_object_set(font, "fallback", FALSE, NULL);
517 if (!gtk_text_tag_table_add(tags, font))
518 g_warning(G_STRLOC" : Failed to add text tag to table: FontDescription");
519 g_autoptr(GtkTextTag) point_size = gtk_text_tag_new("SizePoint");
520 g_object_set(point_size, "family", "Monospace", "rise", 1250, "size-points", 6.5, NULL);
521 if (!gtk_text_tag_table_add(tags, point_size))
522 g_warning(G_STRLOC" : Failed to add text tag to table: size-points");
523 return tags;
524 }
525
526 static void
font_manager_font_preview_init(FontManagerFontPreview * self)527 font_manager_font_preview_init (FontManagerFontPreview *self)
528 {
529 g_return_if_fail(self != NULL);
530 self->allow_edit = FALSE;
531 self->samples = NULL;
532 self->restore_preview = NULL;
533 self->max_waterfall_size = DEFAULT_WATERFALL_MAX_SIZE;
534 GtkStyleContext *ctx = gtk_widget_get_style_context(GTK_WIDGET(self));
535 gtk_style_context_add_class(ctx, GTK_STYLE_CLASS_VIEW);
536 gtk_widget_set_name(GTK_WIDGET(self), "FontManagerFontPreview");
537 gtk_orientable_set_orientation(GTK_ORIENTABLE(self), GTK_ORIENTATION_VERTICAL);
538 g_autoptr(GtkTextTagTable) tag_table = font_manager_text_tag_table_new();
539 self->pangram = font_manager_get_localized_pangram();
540 self->default_pangram = font_manager_get_localized_pangram();
541 self->preview = g_strdup_printf(FONT_MANAGER_DEFAULT_PREVIEW_TEXT, self->pangram);
542 self->default_preview = g_strdup(self->preview);
543 self->justification = GTK_JUSTIFY_CENTER;
544 g_autoptr(GtkTextBuffer) buffer = gtk_text_buffer_new(tag_table);
545 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
546 self->textview = gtk_text_view_new_with_buffer(buffer);
547 gtk_drag_dest_unset(self->textview);
548 GtkWidget *controls = font_manager_preview_controls_new();
549 self->controls = gtk_revealer_new();
550 GtkWidget *fontscale = font_manager_font_scale_new();
551 self->fontscale = gtk_revealer_new();
552 gtk_container_add(GTK_CONTAINER(self->controls), controls);
553 gtk_container_add(GTK_CONTAINER(self->fontscale), fontscale);
554 gtk_container_add(GTK_CONTAINER(scroll), self->textview);
555 gtk_box_pack_start(GTK_BOX(self), self->controls, FALSE, TRUE, 0);
556 font_manager_widget_set_expand(scroll, TRUE);
557 gtk_box_pack_start(GTK_BOX(self), scroll, TRUE, TRUE, 0);
558 gtk_box_pack_end(GTK_BOX(self), self->fontscale, FALSE, TRUE, 0);
559 font_manager_widget_set_margin(self->textview, FONT_MANAGER_DEFAULT_MARGIN * 2);
560 gtk_widget_set_margin_top(self->textview, FONT_MANAGER_DEFAULT_MARGIN * 1.5);
561 gtk_widget_set_margin_bottom(self->textview, FONT_MANAGER_DEFAULT_MARGIN * 1.5);
562 font_manager_widget_set_expand(scroll, TRUE);
563 font_manager_font_preview_set_font_description(self, FONT_MANAGER_DEFAULT_FONT);
564 font_manager_font_preview_set_preview_size(self, FONT_MANAGER_DEFAULT_PREVIEW_SIZE);
565 font_manager_font_preview_set_preview_mode(self, FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL);
566 GtkAdjustment *adjustment = font_manager_font_scale_get_adjustment(FONT_MANAGER_FONT_SCALE(fontscale));
567 GBindingFlags flags = G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE;
568 g_object_bind_property(adjustment, "value", self, "preview-size", flags);
569 flags = G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE;
570 g_object_bind_property(self, "font-description", controls, "description", flags);
571 g_object_bind_property(controls, "justification", self, "justification", flags);
572 font_manager_font_preview_set_justification(self, GTK_JUSTIFY_CENTER);
573 g_signal_connect_swapped(controls, "edit-toggled", G_CALLBACK(on_edit_toggled), self);
574 g_signal_connect_swapped(buffer, "changed", G_CALLBACK(on_buffer_changed), self);
575 g_signal_connect_swapped(controls, "undo-clicked", G_CALLBACK(on_undo_clicked), self);
576 g_signal_connect_swapped(self->textview, "event", G_CALLBACK(on_event), self);
577 gtk_widget_show_all(scroll);
578 gtk_widget_show_all(self->controls);
579 gtk_widget_show_all(self->fontscale);
580 return;
581 }
582
583 /**
584 * font_manager_font_preview_set_preview_mode:
585 * @self: #FontManagerFontPreview
586 * @mode: Preview mode.
587 */
588 void
font_manager_font_preview_set_preview_mode(FontManagerFontPreview * self,FontManagerFontPreviewMode mode)589 font_manager_font_preview_set_preview_mode (FontManagerFontPreview *self,
590 FontManagerFontPreviewMode mode)
591 {
592 g_return_if_fail(self != NULL);
593 g_idle_remove_by_data(self);
594 self->mode = mode;
595 GtkTextIter start;
596 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(self->textview));
597 gtk_text_buffer_get_start_iter(buffer, &start);
598 gtk_text_view_set_editable(GTK_TEXT_VIEW(self->textview), FALSE);
599 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(self->textview), GTK_WRAP_WORD_CHAR);
600 gtk_text_view_set_justification(GTK_TEXT_VIEW(self->textview), GTK_JUSTIFY_FILL);
601 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(self->textview), &start, 0.0, TRUE, 0.0, 0.0);
602 gtk_text_view_set_top_margin(GTK_TEXT_VIEW(self->textview), 0);
603 switch (mode) {
604 case FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW:
605 gtk_text_view_set_top_margin(GTK_TEXT_VIEW(self->textview), FONT_MANAGER_DEFAULT_MARGIN * 6);
606 font_manager_font_preview_set_preview_text(self, NULL);
607 gtk_text_view_set_justification(GTK_TEXT_VIEW(self->textview), self->justification);
608 gtk_text_view_set_editable(GTK_TEXT_VIEW(self->textview), self->allow_edit);
609 break;
610 case FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL:
611 generate_waterfall_preview(self);
612 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(self->textview), GTK_WRAP_NONE);
613 break;
614 case FONT_MANAGER_FONT_PREVIEW_MODE_LOREM_IPSUM:
615 gtk_text_buffer_set_text(buffer, FONT_MANAGER_LOREM_IPSUM, -1);
616 break;
617 default:
618 g_critical("Invalid preview mode : %i", (gint) mode);
619 g_return_if_reached();
620 }
621 update_sample_string(self);
622 apply_font_description(self);
623 update_revealer_state(self, mode);
624 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_PREVIEW_MODE]);
625 return;
626 }
627
628 /**
629 * font_manager_font_preview_set_preview_size:
630 * @self: #FontManagerFontPreview
631 * @size_points: Preview text size.
632 */
633 void
font_manager_font_preview_set_preview_size(FontManagerFontPreview * self,gdouble size_points)634 font_manager_font_preview_set_preview_size (FontManagerFontPreview *self,
635 gdouble size_points)
636 {
637 g_return_if_fail(self != NULL);
638 self->preview_size = CLAMP(size_points, MIN_FONT_SIZE, MAX_FONT_SIZE);
639 update_font_description(self);
640 update_sample_string(self);
641 apply_font_description(self);
642 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_PREVIEW_SIZE]);
643 return;
644 }
645
646 /**
647 * font_manager_font_preview_set_preview_text:
648 * @self: #FontManagerFontPreview
649 * @preview_text: Preview text.
650 */
651 void
font_manager_font_preview_set_preview_text(FontManagerFontPreview * self,const gchar * preview_text)652 font_manager_font_preview_set_preview_text (FontManagerFontPreview *self,
653 const gchar *preview_text)
654 {
655 g_return_if_fail(self != NULL);
656
657 if (preview_text) {
658 gchar *new_preview = g_strdup(preview_text);
659 g_free(self->preview);
660 self->preview = new_preview;
661 }
662
663 if (self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW) {
664 g_return_if_fail(self->preview != NULL);
665 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(self->textview));
666 g_autofree gchar *valid = g_utf8_make_valid(self->preview, -1);
667 gtk_text_buffer_set_text(buffer, valid, -1);
668 }
669 apply_font_description(self);
670 return;
671 }
672
673 /**
674 * font_manager_font_preview_set_font_description:
675 * @self: #FontManagerFontPreview
676 * @font: (nullable): string representation of a font description.
677 *
678 * See #pango_font_description_from_string() for details on what constitutes a
679 * valid font description string.
680 */
681 void
font_manager_font_preview_set_font_description(FontManagerFontPreview * self,const gchar * font)682 font_manager_font_preview_set_font_description (FontManagerFontPreview *self,
683 const gchar *font)
684 {
685 g_return_if_fail(self != NULL);
686 pango_font_description_free(self->font_desc);
687 self->font_desc = pango_font_description_from_string(font ? font : FONT_MANAGER_DEFAULT_FONT);
688 update_font_description(self);
689 update_sample_string(self);
690 apply_font_description(self);
691 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_FONT_DESC]);
692 return;
693 }
694
695 /**
696 * font_manager_font_preview_set_justification:
697 * @self: #FontManagerFontPreview
698 * @justification: #GtkJustification
699 *
700 * Set preview text justification.
701 */
702 void
font_manager_font_preview_set_justification(FontManagerFontPreview * self,GtkJustification justification)703 font_manager_font_preview_set_justification (FontManagerFontPreview *self,
704 GtkJustification justification)
705 {
706 g_return_if_fail(self != NULL);
707 self->justification = justification;
708 if (self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_PREVIEW)
709 gtk_text_view_set_justification(GTK_TEXT_VIEW(self->textview), justification);
710 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_JUSTIFICATION]);
711 return;
712 }
713
714 /**
715 * font_manager_font_preview_set_sample_strings:
716 * @self: #FontManagerFontPreview
717 * @samples: #JsonObject containing sample strings
718 *
719 * @samples is expected to have a dictionary like structure,
720 * with the font description as key and sample string as value.
721 */
722 void
font_manager_font_preview_set_sample_strings(FontManagerFontPreview * self,GHashTable * samples)723 font_manager_font_preview_set_sample_strings (FontManagerFontPreview *self, GHashTable *samples)
724 {
725 g_return_if_fail(self != NULL);
726 g_clear_pointer(&self->samples, g_hash_table_unref);
727 if (samples)
728 self->samples = g_hash_table_ref(samples);
729 update_sample_string(self);
730 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_SAMPLES]);
731 return;
732 }
733
734 /**
735 * font_manager_font_preview_set_max_waterfall_size:
736 * @self: #FontManagerFontPreview
737 * @size_points: Maximum size to use for waterfall previews.
738 */
739 void
font_manager_font_preview_set_max_waterfall_size(FontManagerFontPreview * self,gdouble size_points)740 font_manager_font_preview_set_max_waterfall_size (FontManagerFontPreview *self,
741 gdouble size_points)
742 {
743 g_return_if_fail(self != NULL);
744 self->max_waterfall_size = CLAMP(size_points, MIN_FONT_SIZE * 4, MAX_FONT_SIZE);
745 if (self->mode == FONT_MANAGER_FONT_PREVIEW_MODE_WATERFALL)
746 generate_waterfall_preview(self);
747 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_WATERFALL_MAX]);
748 return;
749 }
750
751 /**
752 * font_manager_font_preview_get_preview_size:
753 * @self: #FontManagerFontPreview
754 *
755 * Returns: Current preview size.
756 */
757 gdouble
font_manager_font_preview_get_preview_size(FontManagerFontPreview * self)758 font_manager_font_preview_get_preview_size (FontManagerFontPreview *self)
759 {
760 g_return_val_if_fail(self != NULL, 0.0);
761 return self->preview_size;
762 }
763
764 /**
765 * font_manager_font_preview_get_preview_text:
766 * @self: #FontManagerFontPreview
767 *
768 * Returns:(transfer full) (nullable):
769 * A newly allocated string that must be freed with #g_free or %NULL
770 */
771 gchar *
font_manager_font_preview_get_preview_text(FontManagerFontPreview * self)772 font_manager_font_preview_get_preview_text (FontManagerFontPreview *self)
773 {
774 g_return_val_if_fail(self != NULL, NULL);
775 return g_strdup(self->preview);
776 }
777
778 /**
779 * font_manager_font_preview_get_font_description:
780 * @self: #FontManagerFontPreview
781 *
782 * Returns:(transfer full) (nullable):
783 * A newly allocated string that must be freed with #g_free or %NULL
784 */
785 gchar *
font_manager_font_preview_get_font_description(FontManagerFontPreview * self)786 font_manager_font_preview_get_font_description (FontManagerFontPreview *self)
787 {
788 g_return_val_if_fail(self != NULL, NULL);
789 return pango_font_description_to_string(self->font_desc);
790 }
791
792 /**
793 * font_manager_font_preview_get_preview_mode:
794 * @self: #FontManagerFontPreview
795 *
796 * Returns: Current preview mode.
797 */
798 FontManagerFontPreviewMode
font_manager_font_preview_get_preview_mode(FontManagerFontPreview * self)799 font_manager_font_preview_get_preview_mode (FontManagerFontPreview *self)
800 {
801 g_return_val_if_fail(self != NULL, 0);
802 return self->mode;
803 }
804
805 /**
806 * font_manager_font_preview_get_justification:
807 * @self: #FontManagerFontPreview
808 *
809 * Returns: Current preview text justification.
810 */
811 GtkJustification
font_manager_font_preview_get_justification(FontManagerFontPreview * self)812 font_manager_font_preview_get_justification (FontManagerFontPreview *self)
813 {
814 g_return_val_if_fail(self != NULL, 0);
815 return self->justification;
816 }
817
818 /**
819 * font_manager_font_preview_new:
820 *
821 * Returns: A newly created #FontManagerFontPreview.
822 * Free the returned object using #g_object_unref().
823 */
824 GtkWidget *
font_manager_font_preview_new(void)825 font_manager_font_preview_new (void)
826 {
827 return g_object_new(FONT_MANAGER_TYPE_FONT_PREVIEW, NULL);
828 }
829