1 /*
2 * Copyright © 2004 Noah Levitt
3 * Copyright © 2008 Christian Persch
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 3 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
18 */
19
20 #include <config.h>
21
22 #include <string.h>
23
24 #include <glib/gi18n-lib.h>
25 #include <gtk/gtk.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "gucharmap-mini-fontsel.h"
29
30 #define I_(string) g_intern_static_string (string)
31
32 enum
33 {
34 MIN_FONT_SIZE = 5,
35 MAX_FONT_SIZE = 400,
36 };
37
38 enum
39 {
40 PROP_0,
41 PROP_FONT_DESC
42 };
43
44 enum
45 {
46 COL_FAMILIY
47 };
48
49 static void gucharmap_mini_font_selection_class_init (GucharmapMiniFontSelectionClass *klass);
50 static void gucharmap_mini_font_selection_init (GucharmapMiniFontSelection *fontsel);
51 static void gucharmap_mini_font_selection_finalize (GObject *object);
52
G_DEFINE_TYPE(GucharmapMiniFontSelection,gucharmap_mini_font_selection,GTK_TYPE_HBOX)53 G_DEFINE_TYPE (GucharmapMiniFontSelection, gucharmap_mini_font_selection, GTK_TYPE_HBOX)
54
55 static void
56 fill_font_families_combo (GucharmapMiniFontSelection *fontsel)
57 {
58 PangoFontFamily **families;
59 int n_families, i;
60
61 pango_context_list_families (
62 gtk_widget_get_pango_context (GTK_WIDGET (fontsel)),
63 &families, &n_families);
64
65 for (i = 0; i < n_families; i++)
66 {
67 PangoFontFamily *family = families[i];
68 GtkTreeIter iter;
69
70 gtk_list_store_insert_with_values (fontsel->family_store,
71 &iter,
72 -1,
73 COL_FAMILIY, pango_font_family_get_name (family),
74 -1);
75 }
76
77 g_free (families);
78
79 /* Now turn on sorting in the combo box */
80 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (fontsel->family_store),
81 COL_FAMILIY,
82 GTK_SORT_ASCENDING);
83 }
84
85 static void
update_font_family_combo(GucharmapMiniFontSelection * fontsel)86 update_font_family_combo (GucharmapMiniFontSelection *fontsel)
87 {
88 GtkTreeModel *model = GTK_TREE_MODEL (fontsel->family_store);
89 GtkTreeIter iter;
90 const char *font_family;
91 gboolean found = FALSE;
92
93 font_family = pango_font_description_get_family (fontsel->font_desc);
94 if (!font_family || !font_family[0]) {
95 gtk_combo_box_set_active (GTK_COMBO_BOX (fontsel->family), -1);
96 return;
97 }
98
99 if (!gtk_tree_model_get_iter_first (model, &iter))
100 return;
101
102 do {
103 char *family;
104
105 gtk_tree_model_get (model, &iter, COL_FAMILIY, &family, -1);
106 found = family && strcmp (family, font_family) == 0;
107 g_free (family);
108 } while (!found && gtk_tree_model_iter_next (model, &iter));
109
110 if (found) {
111 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (fontsel->family), &iter);
112 } else {
113 gtk_combo_box_set_active (GTK_COMBO_BOX (fontsel->family), -1);
114 }
115 }
116
117 static void
family_combo_changed(GtkComboBox * combo,GucharmapMiniFontSelection * fontsel)118 family_combo_changed (GtkComboBox *combo,
119 GucharmapMiniFontSelection *fontsel)
120 {
121 GtkTreeIter iter;
122 char *family;
123
124 if (!gtk_combo_box_get_active_iter (combo, &iter))
125 return;
126
127 gtk_tree_model_get (GTK_TREE_MODEL (fontsel->family_store),
128 &iter,
129 COL_FAMILIY, &family,
130 -1);
131 if (!family)
132 return;
133
134 pango_font_description_set_family (fontsel->font_desc, family);
135 g_free (family);
136
137 g_object_notify (G_OBJECT (fontsel), "font-desc");
138 }
139
140 static gboolean
match_function(GtkEntryCompletion * completion,const gchar * key,GtkTreeIter * iter,gpointer user_data)141 match_function (GtkEntryCompletion *completion,
142 const gchar *key,
143 GtkTreeIter *iter,
144 gpointer user_data)
145 {
146 GucharmapMiniFontSelection *fontsel = GUCHARMAP_MINI_FONT_SELECTION (user_data);
147 char *family, *family_fold;
148 gchar **sub_match, **p;
149 gboolean all_matches = TRUE;
150
151 gtk_tree_model_get (GTK_TREE_MODEL (fontsel->family_store),
152 iter,
153 COL_FAMILIY, &family,
154 -1);
155 family_fold = g_utf8_casefold (family, -1);
156
157 /* Match if all space separated substrings are part of family string */
158 sub_match = g_strsplit (key, " ", -1);
159 p = sub_match;
160 while (*p) {
161 gchar *match_fold = g_utf8_casefold (*p++, -1);
162 if (!g_strstr_len (family_fold, -1, match_fold))
163 all_matches = FALSE;
164 g_free (match_fold);
165 if (!all_matches)
166 break;
167 }
168
169 g_free (family);
170 g_free (family_fold);
171 g_strfreev (sub_match);
172
173 return all_matches;
174 }
175
176 static gboolean
completion_match_selected(GtkEntryCompletion * widget,GtkTreeModel * model,GtkTreeIter * iter,GucharmapMiniFontSelection * fontsel)177 completion_match_selected(GtkEntryCompletion *widget,
178 GtkTreeModel *model,
179 GtkTreeIter *iter,
180 GucharmapMiniFontSelection *fontsel)
181 {
182 char *family;
183
184 gtk_tree_model_get (GTK_TREE_MODEL (fontsel->family_store),
185 iter,
186 COL_FAMILIY, &family,
187 -1);
188 pango_font_description_set_family (fontsel->font_desc, family);
189 g_free (family);
190
191 g_object_notify (G_OBJECT (fontsel), "font-desc");
192
193 return FALSE;
194 }
195
196
197 /* returns font size in points */
198 static int
get_font_size(GucharmapMiniFontSelection * fontsel)199 get_font_size (GucharmapMiniFontSelection *fontsel)
200 {
201 return PANGO_PIXELS (pango_font_description_get_size (fontsel->font_desc));
202 }
203
204 /* size is in points */
205 static void
set_font_size(GucharmapMiniFontSelection * fontsel,int size)206 set_font_size (GucharmapMiniFontSelection *fontsel,
207 int size)
208 {
209 size = CLAMP (size, MIN_FONT_SIZE, MAX_FONT_SIZE);
210 pango_font_description_set_size (fontsel->font_desc, PANGO_SCALE * size);
211
212 gtk_adjustment_set_value (GTK_ADJUSTMENT (fontsel->size_adj), size);
213
214 g_object_notify (G_OBJECT (fontsel), "font-desc");
215 }
216
217 static void
font_size_changed(GtkAdjustment * adjustment,GucharmapMiniFontSelection * fontsel)218 font_size_changed (GtkAdjustment *adjustment,
219 GucharmapMiniFontSelection *fontsel)
220 {
221 int new_size;
222
223 new_size = gtk_adjustment_get_value (adjustment);
224 if (new_size != get_font_size (fontsel))
225 set_font_size (fontsel, new_size);
226 }
227
228 static void
gucharmap_mini_font_selection_finalize(GObject * object)229 gucharmap_mini_font_selection_finalize (GObject *object)
230 {
231 GucharmapMiniFontSelection *fontsel = GUCHARMAP_MINI_FONT_SELECTION (object);
232 pango_font_description_free (fontsel->font_desc);
233
234 G_OBJECT_CLASS (gucharmap_mini_font_selection_parent_class)->finalize (object);
235 }
236
237 static void
gucharmap_mini_font_selection_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)238 gucharmap_mini_font_selection_set_property (GObject *object,
239 guint prop_id,
240 const GValue *value,
241 GParamSpec *pspec)
242 {
243 GucharmapMiniFontSelection *mini_fontsel = GUCHARMAP_MINI_FONT_SELECTION (object);
244
245 switch (prop_id) {
246 case PROP_FONT_DESC:
247 gucharmap_mini_font_selection_set_font_desc (mini_fontsel, g_value_get_boxed (value));
248 break;
249 default:
250 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251 break;
252 }
253 }
254
255 static void
gucharmap_mini_font_selection_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)256 gucharmap_mini_font_selection_get_property (GObject *object,
257 guint prop_id,
258 GValue *value,
259 GParamSpec *pspec)
260 {
261 GucharmapMiniFontSelection*mini_fontsel = GUCHARMAP_MINI_FONT_SELECTION (object);
262
263 switch (prop_id) {
264 case PROP_FONT_DESC:
265 g_value_set_boxed (value, gucharmap_mini_font_selection_get_font_desc (mini_fontsel));
266 break;
267 default:
268 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
269 break;
270 }
271 }
272
273 static void
gucharmap_mini_font_selection_class_init(GucharmapMiniFontSelectionClass * klass)274 gucharmap_mini_font_selection_class_init (GucharmapMiniFontSelectionClass *klass)
275 {
276 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
277
278 gobject_class->finalize = gucharmap_mini_font_selection_finalize;
279 gobject_class->get_property = gucharmap_mini_font_selection_get_property;
280 gobject_class->set_property = gucharmap_mini_font_selection_set_property;
281
282 g_object_class_install_property
283 (gobject_class,
284 PROP_FONT_DESC,
285 g_param_spec_boxed ("font-desc", NULL, NULL,
286 PANGO_TYPE_FONT_DESCRIPTION,
287 G_PARAM_READWRITE |
288 G_PARAM_STATIC_NAME |
289 G_PARAM_STATIC_NICK |
290 G_PARAM_STATIC_BLURB));
291 }
292
293 static void
bold_toggled(GtkToggleButton * toggle,GucharmapMiniFontSelection * fontsel)294 bold_toggled (GtkToggleButton *toggle,
295 GucharmapMiniFontSelection *fontsel)
296 {
297 if (gtk_toggle_button_get_active (toggle))
298 pango_font_description_set_weight (fontsel->font_desc, PANGO_WEIGHT_BOLD);
299 else
300 pango_font_description_set_weight (fontsel->font_desc, PANGO_WEIGHT_NORMAL);
301
302 g_object_notify (G_OBJECT (fontsel), "font-desc");
303 }
304
305 static void
italic_toggled(GtkToggleButton * toggle,GucharmapMiniFontSelection * fontsel)306 italic_toggled (GtkToggleButton *toggle,
307 GucharmapMiniFontSelection *fontsel)
308 {
309 if (gtk_toggle_button_get_active (toggle))
310 pango_font_description_set_style (fontsel->font_desc, PANGO_STYLE_ITALIC);
311 else
312 pango_font_description_set_style (fontsel->font_desc, PANGO_STYLE_NORMAL);
313
314 g_object_notify (G_OBJECT (fontsel), "font-desc");
315 }
316
317 static void
gucharmap_mini_font_selection_init(GucharmapMiniFontSelection * fontsel)318 gucharmap_mini_font_selection_init (GucharmapMiniFontSelection *fontsel)
319 {
320 GtkStyle *style;
321 AtkObject *accessib;
322
323 gtk_widget_ensure_style (GTK_WIDGET (fontsel));
324 style = gtk_widget_get_style (GTK_WIDGET (fontsel));
325 fontsel->font_desc = pango_font_description_copy (style->font_desc);
326 fontsel->default_size = -1;
327
328 fontsel->size_adj = gtk_adjustment_new (MIN_FONT_SIZE,
329 MIN_FONT_SIZE, MAX_FONT_SIZE, 1, 8, 0);
330
331 accessib = gtk_widget_get_accessible (GTK_WIDGET (fontsel));
332 atk_object_set_name (accessib, _("Font"));
333
334 gtk_box_set_spacing (GTK_BOX (fontsel), 6);
335
336 fontsel->family_store = gtk_list_store_new (1, G_TYPE_STRING);
337
338 fontsel->family = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL (fontsel->family_store));
339 gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX(fontsel->family), COL_FAMILIY);
340 fontsel->completion = gtk_entry_completion_new();
341 gtk_entry_completion_set_text_column (fontsel->completion, COL_FAMILIY);
342 gtk_entry_completion_set_model (fontsel->completion, GTK_TREE_MODEL (fontsel->family_store));
343 gtk_entry_completion_set_match_func(fontsel->completion, match_function, fontsel, NULL);
344 g_signal_connect (G_OBJECT (fontsel->completion), "match-selected",
345 G_CALLBACK (completion_match_selected), fontsel);
346
347 gtk_entry_set_completion (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (fontsel->family))),
348 fontsel->completion);
349 gtk_widget_show (fontsel->family);
350 accessib = gtk_widget_get_accessible (fontsel->family);
351 atk_object_set_name (accessib, _("Font Family"));
352
353 fontsel->bold = gtk_toggle_button_new_with_mnemonic (GTK_STOCK_BOLD);
354 gtk_button_set_use_stock (GTK_BUTTON (fontsel->bold), TRUE);
355 gtk_widget_show (fontsel->bold);
356 g_signal_connect (fontsel->bold, "toggled",
357 G_CALLBACK (bold_toggled), fontsel);
358
359 fontsel->italic = gtk_toggle_button_new_with_mnemonic (GTK_STOCK_ITALIC);
360 gtk_button_set_use_stock (GTK_BUTTON (fontsel->italic), TRUE);
361 gtk_widget_show (fontsel->italic);
362 g_signal_connect (fontsel->italic, "toggled",
363 G_CALLBACK (italic_toggled), fontsel);
364
365 fontsel->size = gtk_spin_button_new (GTK_ADJUSTMENT (fontsel->size_adj),
366 0, 0);
367 gtk_widget_show (fontsel->size);
368 accessib = gtk_widget_get_accessible (fontsel->size);
369 atk_object_set_name (accessib, _("Font Size"));
370 g_signal_connect (fontsel->size_adj, "value-changed",
371 G_CALLBACK (font_size_changed), fontsel);
372
373 fill_font_families_combo (fontsel);
374
375 gtk_combo_box_set_active (GTK_COMBO_BOX (fontsel->family), -1);
376 g_signal_connect (fontsel->family, "changed",
377 G_CALLBACK (family_combo_changed), fontsel);
378
379 gtk_box_pack_start (GTK_BOX (fontsel), fontsel->family, FALSE, FALSE, 0);
380 gtk_box_pack_start (GTK_BOX (fontsel), fontsel->bold, FALSE, FALSE, 0);
381 gtk_box_pack_start (GTK_BOX (fontsel), fontsel->italic, FALSE, FALSE, 0);
382 gtk_box_pack_start (GTK_BOX (fontsel), fontsel->size, FALSE, FALSE, 0);
383
384 gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (fontsel->family), FALSE);
385 gtk_button_set_focus_on_click (GTK_BUTTON (fontsel->bold), FALSE);
386 gtk_button_set_focus_on_click (GTK_BUTTON (fontsel->italic), FALSE);
387
388 gtk_container_set_border_width (GTK_CONTAINER (fontsel), 6);
389
390 gtk_widget_show_all (GTK_WIDGET (fontsel));
391 }
392
393 GtkWidget *
gucharmap_mini_font_selection_new(void)394 gucharmap_mini_font_selection_new (void)
395 {
396 return GTK_WIDGET (g_object_new (gucharmap_mini_font_selection_get_type (),
397 NULL));
398 }
399
400 void
gucharmap_mini_font_selection_set_font_desc(GucharmapMiniFontSelection * fontsel,PangoFontDescription * font_desc)401 gucharmap_mini_font_selection_set_font_desc (GucharmapMiniFontSelection *fontsel,
402 PangoFontDescription *font_desc)
403 {
404 GObject *object = G_OBJECT (fontsel);
405 PangoFontDescription *new_font_desc;
406 const char *new_font_family;
407
408 g_return_if_fail (GUCHARMAP_IS_MINI_FONT_SELECTION (fontsel));
409 g_return_if_fail (font_desc != NULL);
410
411 g_object_freeze_notify (object);
412
413 new_font_desc = pango_font_description_copy (font_desc);
414 new_font_family = pango_font_description_get_family (new_font_desc);
415 if (!new_font_family) {
416 pango_font_description_set_family (new_font_desc, "Sans");
417 new_font_family = pango_font_description_get_family (new_font_desc);
418 }
419
420 if ((!fontsel->font_desc ||
421 strcmp (pango_font_description_get_family (fontsel->font_desc), new_font_family) != 0) &&
422 pango_font_description_get_size (new_font_desc) > 0)
423 fontsel->default_size = pango_font_description_get_size (new_font_desc) / PANGO_SCALE;
424
425 if (fontsel->font_desc)
426 pango_font_description_free (fontsel->font_desc);
427
428 fontsel->font_desc = new_font_desc;
429
430 update_font_family_combo (fontsel);
431
432 /* treat oblique and italic both as italic */
433 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->italic), pango_font_description_get_style (fontsel->font_desc) == PANGO_STYLE_ITALIC || pango_font_description_get_style (fontsel->font_desc) == PANGO_STYLE_OBLIQUE);
434
435 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->bold), pango_font_description_get_weight (fontsel->font_desc) > PANGO_WEIGHT_NORMAL);
436
437 gtk_adjustment_set_value (
438 GTK_ADJUSTMENT (fontsel->size_adj),
439 pango_font_description_get_size (fontsel->font_desc) / PANGO_SCALE);
440
441 g_object_notify (G_OBJECT (fontsel), "font-desc");
442
443 g_object_thaw_notify (object);
444 }
445
446 PangoFontDescription *
gucharmap_mini_font_selection_get_font_desc(GucharmapMiniFontSelection * fontsel)447 gucharmap_mini_font_selection_get_font_desc (GucharmapMiniFontSelection *fontsel)
448 {
449 g_return_val_if_fail (GUCHARMAP_IS_MINI_FONT_SELECTION (fontsel), NULL);
450
451 return fontsel->font_desc;
452 }
453
454 void
gucharmap_mini_font_selection_change_font_size(GucharmapMiniFontSelection * fontsel,float factor)455 gucharmap_mini_font_selection_change_font_size (GucharmapMiniFontSelection *fontsel,
456 float factor)
457 {
458 int size, new_size;
459
460 g_return_if_fail (factor > 0.0f);
461
462 size = get_font_size (fontsel);
463 new_size = (float) size * factor;
464
465 if (factor > 1.0f)
466 new_size = MAX (new_size, size + 1);
467 else if (factor < 1.0f)
468 new_size = MIN (new_size, size - 1);
469
470 set_font_size (fontsel, new_size);
471 }
472
473 void
gucharmap_mini_font_selection_reset_font_size(GucharmapMiniFontSelection * fontsel)474 gucharmap_mini_font_selection_reset_font_size (GucharmapMiniFontSelection *fontsel)
475 {
476 if (fontsel->default_size > 0) {
477 set_font_size (fontsel, fontsel->default_size);
478 } else {
479 GtkStyle *style;
480
481 style = gtk_widget_get_style (GTK_WIDGET (fontsel));
482 set_font_size (fontsel, pango_font_description_get_size (style->font_desc) * 2.0f / PANGO_SCALE);
483 }
484 }
485