1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * Massively updated for Pango by Owen Taylor, May 2000
5  * GtkFontSelection widget for Gtk+, by Damon Chaplin, May 1998.
6  * Based on the GnomeFontSelector widget, by Elliot Lee, but major changes.
7  * The GnomeFontSelector was derived from app/text_tool.c in the GIMP.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24 
25 /*
26  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
27  * file for a list of people on the GTK+ Team.  See the ChangeLog
28  * files for a list of changes.  These files are distributed with
29  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
30  */
31 
32 /*
33  * Modified to allow filtering of fonts for gretl, Allin Cottrell, 2002
34  */
35 
36 #include "gretl.h" /* this must provide standard and GTK headers and
37 		      deal with gettext */
38 #include "dlgutils.h"
39 #include "gtkfontselhack.h"
40 #include "fontfilter.h"
41 
42 #define FONT_DEBUG 0
43 
44 #define GTK_TYPE_FNTHACK              (gtk_fontsel_hack_get_type ())
45 #define GTK_FNTHACK(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FNTHACK, GtkFontselHack))
46 #define GTK_FNTHACK_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FNTHACK, GtkFontselHackClass))
47 #define GTK_IS_FNTHACK(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FNTHACK))
48 #define GTK_IS_FNTHACK_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FNTHACK))
49 #define GTK_FNTHACK_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FNTHACK, GtkFontselHackClass))
50 
51 
52 #define GTK_TYPE_FNTHACK_DIALOG              (gtk_fontsel_hack_dialog_get_type ())
53 #define GTK_FNTHACK_DIALOG(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FNTHACK_DIALOG, GtkFontselHackDialog))
54 #define GTK_FNTHACK_DIALOG_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FNTHACK_DIALOG, GtkFontselHackDialogClass))
55 #define GTK_IS_FNTHACK_DIALOG(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FNTHACK_DIALOG))
56 #define GTK_IS_FNTHACK_DIALOG_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FNTHACK_DIALOG))
57 #define GTK_FNTHACK_DIALOG_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FNTHACK_DIALOG, GtkFontselHackDialogClass))
58 
59 #define GTK_TYPE_FONT_FILTER (gtk_font_filter_get_type())
60 
61 struct _GtkFontselHack
62 {
63     GtkVBox parent_instance;
64 
65     GtkWidget *font_entry;
66     GtkWidget *family_list;
67     GtkWidget *font_style_entry;
68     GtkWidget *face_list;
69     GtkWidget *size_entry;
70     GtkWidget *size_list;
71     GtkWidget *pixels_button;
72     GtkWidget *points_button;
73     GtkWidget *filter_button;
74     GtkWidget *preview_entry;
75 
76     PangoFontFamily *family;	/* Current family */
77     PangoFontFace *face;        /* Current face */
78 
79     gint size;
80     FontFilterType filter;
81 };
82 
83 struct _GtkFontselHackClass
84 {
85     GtkVBoxClass parent_class;
86 
87     /* Padding for future expansion */
88     void (*_gtk_reserved1) (void);
89     void (*_gtk_reserved2) (void);
90     void (*_gtk_reserved3) (void);
91     void (*_gtk_reserved4) (void);
92 };
93 
94 struct _GtkFontselHackDialog
95 {
96     GtkDialog parent_instance;
97     GtkWidget *fontsel;
98     GtkWidget *main_vbox;
99     GtkWidget *action_area;
100     GtkWidget *ok_button;
101     GtkWidget *cancel_button;
102     /* If the user changes the width of the dialog, we turn auto-shrink off. */
103     gint dialog_width;
104     gboolean auto_resize;
105 };
106 
107 struct _GtkFontselHackDialogClass
108 {
109     GtkDialogClass parent_class;
110 
111     /* Padding for future expansion */
112     void (*_gtk_reserved1) (void);
113     void (*_gtk_reserved2) (void);
114     void (*_gtk_reserved3) (void);
115     void (*_gtk_reserved4) (void);
116 };
117 
118 /* This is the default text shown in the preview entry, though the user
119    can set it. Remember that some fonts only have capital letters. */
120 #define PREVIEW_TEXT N_("abcdefghijk ABCDEFGHIJK")
121 
122 /* This is the initial and maximum height of the preview entry (it expands
123    when large font sizes are selected). Initial height is also the minimum. */
124 #define INITIAL_PREVIEW_HEIGHT 44
125 #define MAX_PREVIEW_HEIGHT 300
126 
127 /* These are the sizes of the font, style & size lists. */
128 #define FONT_LIST_HEIGHT	136
129 #define FONT_LIST_WIDTH		190
130 #define FONT_STYLE_LIST_WIDTH	170
131 #define FONT_SIZE_LIST_WIDTH	60
132 
133 /* These are what we use as the standard font sizes, for the size list.
134  */
135 static const guint16 font_sizes[] = {
136     6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28,
137     32, 36, 40, 48, 56, 64, 72
138 };
139 
140 enum {
141     PROP_0,
142     PROP_FONT_NAME,
143     PROP_PREVIEW_TEXT,
144     PROP_FILTER
145 };
146 
147 enum {
148     FAMILY_COLUMN,
149     FAMILY_NAME_COLUMN
150 };
151 
152 enum {
153     FACE_COLUMN,
154     FACE_NAME_COLUMN
155 };
156 
157 enum {
158     SIZE_COLUMN
159 };
160 
161 static void    gtk_fontsel_hack_class_init	   (GtkFontselHackClass *klass);
162 static void    gtk_fontsel_hack_set_property       (GObject         *object,
163 						    guint            prop_id,
164 						    const GValue    *value,
165 						    GParamSpec      *pspec);
166 static void    gtk_fontsel_hack_get_property       (GObject         *object,
167 						    guint            prop_id,
168 						    GValue          *value,
169 						    GParamSpec      *pspec);
170 static void    gtk_fontsel_hack_init		     (GtkFontselHack      *fontsel);
171 static void    gtk_fontsel_hack_finalize	     (GObject               *object);
172 
173 /* These are the callbacks & related functions. */
174 static void     gtk_fontsel_hack_select_font           (GtkTreeSelection *selection,
175 							gpointer          data);
176 static void     gtk_fontsel_hack_show_available_fonts  (GtkFontselHack *fs);
177 
178 static void     gtk_fontsel_hack_show_available_styles (GtkFontselHack *fs);
179 static void     gtk_fontsel_hack_select_best_style     (GtkFontselHack *fs,
180 							gboolean          use_first);
181 static void     gtk_fontsel_hack_select_style          (GtkTreeSelection *selection,
182 							gpointer          data);
183 
184 static void     gtk_fontsel_hack_select_best_size      (GtkFontselHack *fs);
185 static void     gtk_fontsel_hack_show_available_sizes  (GtkFontselHack *fs,
186 							gboolean          first_time);
187 static void     gtk_fontsel_hack_size_activate         (GtkWidget        *w,
188 							gpointer          data);
189 static gboolean gtk_fontsel_hack_size_focus_out        (GtkWidget        *w,
190 							GdkEventFocus    *event,
191 							gpointer          data);
192 static void     gtk_fontsel_hack_select_size           (GtkTreeSelection *selection,
193 							gpointer          data);
194 
195 static void     gtk_fontsel_hack_scroll_on_map         (GtkWidget        *w,
196 							gpointer          data);
197 
198 static void     gtk_fontsel_hack_screen_changed        (GtkWidget *widget,
199 							GdkScreen *previous_screen);
200 
201 static void     gtk_fontsel_hack_preview_changed       (GtkWidget        *entry,
202 							GtkFontselHack *fontsel);
203 
204 /* Misc. utility functions. */
205 static void gtk_fontsel_hack_load_font          (GtkFontselHack *fs);
206 static void gtk_fontsel_hack_update_preview     (GtkFontselHack *fs);
207 
208 /* FontselHackDialog */
209 static void gtk_fontsel_hack_dialog_class_init  (GtkFontselHackDialogClass *klass);
210 static void gtk_fontsel_hack_dialog_init        (GtkFontselHackDialog *fontseldiag);
211 
212 static gint gtk_fontsel_hack_dialog_on_configure (GtkWidget      *widget,
213 						  GdkEventConfigure *event,
214 						  GtkFontselHackDialog *fsd);
215 
gtk_font_filter_get_type(void)216 GType gtk_font_filter_get_type (void)
217 {
218     static GType etype = 0;
219 
220     if (etype == 0) {
221 	static const GEnumValue values[] = {
222 	    { FONT_HACK_NONE, "FONT_HACK_NONE", "use no font filter" },
223 	    { FONT_HACK_LATIN, "FONT_HACK_LATIN", "latin text fonts" },
224 	    { FONT_HACK_LATIN_MONO, "FONT_HACK_LATIN_MONO", "monospaced latin text fonts" },
225 	    { 0, NULL, NULL }
226 	};
227 
228 	etype = g_enum_register_static("GtkFontFilter", values);
229     }
230 
231     return etype;
232 }
233 
G_DEFINE_TYPE(GtkFontselHack,gtk_fontsel_hack,GTK_TYPE_VBOX)234 G_DEFINE_TYPE (GtkFontselHack, gtk_fontsel_hack, GTK_TYPE_VBOX)
235 
236 static void
237 gtk_fontsel_hack_class_init (GtkFontselHackClass *klass)
238 {
239     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
240     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
241 
242     gobject_class->set_property = gtk_fontsel_hack_set_property;
243     gobject_class->get_property = gtk_fontsel_hack_get_property;
244 
245     widget_class->screen_changed = gtk_fontsel_hack_screen_changed;
246 
247     g_object_class_install_property(gobject_class,
248 				    PROP_FONT_NAME,
249 				    g_param_spec_string("font-name",
250 							_("Font name"),
251 							_("The X string that represents this font"),
252 							NULL,
253 							G_PARAM_READWRITE));
254     g_object_class_install_property(gobject_class,
255 				    PROP_PREVIEW_TEXT,
256 				    g_param_spec_string("preview-text",
257 							_("Preview text"),
258 							_("The text to display in order to demonstrate the selected font"),
259 							PREVIEW_TEXT,
260 							G_PARAM_READWRITE));
261     g_object_class_install_property(gobject_class,
262 				    PROP_FILTER,
263 				    g_param_spec_enum("filter",
264 						      _("Filter"),
265 						      _("The filter for acceptable fonts."),
266 						      GTK_TYPE_FONT_FILTER,
267 						      FONT_HACK_NONE,
268 						      G_PARAM_READWRITE));
269 
270     gobject_class->finalize = gtk_fontsel_hack_finalize;
271 }
272 
273 static void
gtk_fontsel_hack_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)274 gtk_fontsel_hack_set_property (GObject *object,
275 			       guint prop_id,
276 			       const GValue *value,
277 			       GParamSpec *pspec)
278 {
279     GtkFontselHack *fontsel;
280 
281     fontsel = GTK_FNTHACK(object);
282 
283     switch (prop_id) {
284     case PROP_FONT_NAME:
285 	gtk_fontsel_hack_set_font_name(fontsel, g_value_get_string(value));
286 	break;
287     case PROP_PREVIEW_TEXT:
288 	gtk_fontsel_hack_set_preview_text(fontsel, g_value_get_string(value));
289 	break;
290     default:
291 	G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
292 	break;
293     }
294 }
295 
gtk_fontsel_hack_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)296 static void gtk_fontsel_hack_get_property (GObject *object,
297 					   guint prop_id,
298 					   GValue *value,
299 					   GParamSpec *pspec)
300 {
301     GtkFontselHack *fontsel;
302 
303     fontsel = GTK_FNTHACK(object);
304 
305     switch (prop_id) {
306     case PROP_FONT_NAME:
307 	g_value_set_string(value, gtk_fontsel_hack_get_font_name(fontsel));
308 	break;
309     case PROP_PREVIEW_TEXT:
310 	g_value_set_string(value, gtk_fontsel_hack_get_preview_text(fontsel));
311 	break;
312     case PROP_FILTER:
313 	g_value_set_int(value, gtk_fontsel_hack_get_filter(fontsel));
314 	break;
315     default:
316 	G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
317 	break;
318     }
319 }
320 
321 static void
gtk_fontsel_hack_init(GtkFontselHack * fontsel)322 gtk_fontsel_hack_init (GtkFontselHack *fontsel)
323 {
324     GtkWidget *scrolled_win;
325     GtkWidget *text_frame;
326     GtkWidget *text_box;
327     GtkWidget *table, *label;
328     GtkWidget *font_label, *style_label;
329     GtkListStore *model;
330     GtkTreeViewColumn *column;
331     GList *focus_chain = NULL;
332 
333     gtk_widget_push_composite_child();
334 
335     fontsel->size = 12 * PANGO_SCALE;
336     fontsel->filter = FONT_HACK_NONE;
337 
338     /* Create the table of font, style & size */
339     table = gtk_table_new(3, 3, FALSE);
340     gtk_widget_show(table);
341     gtk_table_set_col_spacings(GTK_TABLE (table), 8);
342     gtk_box_pack_start(GTK_BOX(fontsel), table, TRUE, TRUE, 0);
343 
344     fontsel->size_entry = gtk_entry_new();
345     gtk_widget_set_size_request(fontsel->size_entry, 20, -1);
346     gtk_widget_show(fontsel->size_entry);
347     gtk_table_attach(GTK_TABLE(table), fontsel->size_entry, 2, 3, 1, 2,
348 		     GTK_FILL, 0, 0, 0);
349     g_signal_connect(G_OBJECT(fontsel->size_entry), "activate",
350 		     (GCallback) gtk_fontsel_hack_size_activate,
351 		     fontsel);
352     g_signal_connect_after(G_OBJECT(fontsel->size_entry), "focus-out-event",
353 			   (GCallback) gtk_fontsel_hack_size_focus_out,
354 			   fontsel);
355 
356     font_label = gtk_label_new_with_mnemonic(_("_Family:"));
357     gtk_misc_set_alignment(GTK_MISC(font_label), 0.0, 0.5);
358     gtk_widget_show(font_label);
359     gtk_table_attach(GTK_TABLE(table), font_label, 0, 1, 0, 1,
360 		     GTK_FILL, 0, 0, 0);
361 
362     style_label = gtk_label_new_with_mnemonic(_("_Style:"));
363     gtk_misc_set_alignment(GTK_MISC(style_label), 0.0, 0.5);
364     gtk_widget_show(style_label);
365     gtk_table_attach(GTK_TABLE(table), style_label, 1, 2, 0, 1,
366 		     GTK_FILL, 0, 0, 0);
367 
368     label = gtk_label_new_with_mnemonic(_("Si_ze:"));
369     gtk_label_set_mnemonic_widget(GTK_LABEL(label),
370 				  fontsel->size_entry);
371     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
372     gtk_widget_show(label);
373     gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1,
374 		     GTK_FILL, 0, 0, 0);
375 
376 
377     /* Create the lists  */
378 
379     model = gtk_list_store_new(2,
380 			       G_TYPE_OBJECT,  /* FAMILY_COLUMN */
381 			       G_TYPE_STRING); /* FAMILY_NAME_COLUMN */
382     fontsel->family_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
383     g_object_unref(model);
384 
385     column = gtk_tree_view_column_new_with_attributes("Family",
386 						      gtk_cell_renderer_text_new(),
387 						      "text", FAMILY_NAME_COLUMN,
388 						      NULL);
389     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
390     gtk_tree_view_append_column(GTK_TREE_VIEW(fontsel->family_list), column);
391 
392     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fontsel->family_list), FALSE);
393     gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(fontsel->family_list)),
394 				GTK_SELECTION_BROWSE);
395 
396     gtk_label_set_mnemonic_widget(GTK_LABEL(font_label), fontsel->family_list);
397 
398     scrolled_win = gtk_scrolled_window_new(NULL, NULL);
399     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_win), GTK_SHADOW_IN);
400     gtk_widget_set_size_request(scrolled_win, FONT_LIST_WIDTH, FONT_LIST_HEIGHT);
401     gtk_container_add(GTK_CONTAINER(scrolled_win), fontsel->family_list);
402     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
403 				   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
404     gtk_widget_show(fontsel->family_list);
405     gtk_widget_show(scrolled_win);
406 
407     gtk_table_attach(GTK_TABLE(table), scrolled_win, 0, 1, 1, 3,
408 		     GTK_EXPAND | GTK_FILL,
409 		     GTK_EXPAND | GTK_FILL, 0, 0);
410     focus_chain = g_list_append(focus_chain, scrolled_win);
411 
412     model = gtk_list_store_new(2,
413 			       G_TYPE_OBJECT,  /* FACE_COLUMN */
414 			       G_TYPE_STRING); /* FACE_NAME_COLUMN */
415     fontsel->face_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
416     g_object_unref(model);
417 
418     gtk_label_set_mnemonic_widget(GTK_LABEL(style_label), fontsel->face_list);
419 
420     column = gtk_tree_view_column_new_with_attributes("Face",
421 						      gtk_cell_renderer_text_new(),
422 						      "text", FACE_NAME_COLUMN,
423 						      NULL);
424     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
425     gtk_tree_view_append_column(GTK_TREE_VIEW(fontsel->face_list), column);
426 
427     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fontsel->face_list), FALSE);
428     gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(fontsel->face_list)),
429 				GTK_SELECTION_BROWSE);
430 
431     scrolled_win = gtk_scrolled_window_new(NULL, NULL);
432     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_win), GTK_SHADOW_IN);
433     gtk_widget_set_size_request(scrolled_win, FONT_STYLE_LIST_WIDTH, FONT_LIST_HEIGHT);
434     gtk_container_add(GTK_CONTAINER(scrolled_win), fontsel->face_list);
435     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
436 				   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
437     gtk_widget_show(fontsel->face_list);
438     gtk_widget_show(scrolled_win);
439     gtk_table_attach(GTK_TABLE(table), scrolled_win, 1, 2, 1, 3,
440 		     GTK_EXPAND | GTK_FILL,
441 		     GTK_EXPAND | GTK_FILL, 0, 0);
442     focus_chain = g_list_append(focus_chain, scrolled_win);
443 
444     focus_chain = g_list_append(focus_chain, fontsel->size_entry);
445 
446     model = gtk_list_store_new(1, G_TYPE_INT);
447     fontsel->size_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
448     g_object_unref(model);
449 
450     column = gtk_tree_view_column_new_with_attributes("Size",
451 						      gtk_cell_renderer_text_new(),
452 						      "text", SIZE_COLUMN,
453 						      NULL);
454     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
455     gtk_tree_view_append_column(GTK_TREE_VIEW(fontsel->size_list), column);
456 
457     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fontsel->size_list), FALSE);
458     gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(fontsel->size_list)),
459 				GTK_SELECTION_BROWSE);
460 
461     scrolled_win = gtk_scrolled_window_new(NULL, NULL);
462     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_win), GTK_SHADOW_IN);
463     gtk_container_add(GTK_CONTAINER(scrolled_win), fontsel->size_list);
464     gtk_widget_set_size_request(scrolled_win, -1, FONT_LIST_HEIGHT);
465     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
466 				   GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
467     gtk_widget_show(fontsel->size_list);
468     gtk_widget_show(scrolled_win);
469     gtk_table_attach(GTK_TABLE(table), scrolled_win, 2, 3, 2, 3,
470 		     GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
471     focus_chain = g_list_append(focus_chain, scrolled_win);
472 
473     gtk_container_set_focus_chain(GTK_CONTAINER(table), focus_chain);
474     g_list_free(focus_chain);
475 
476     /* Insert the fonts. */
477     gtk_fontsel_hack_show_available_fonts(fontsel);
478 
479     g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(fontsel->family_list)), "changed",
480 		     G_CALLBACK(gtk_fontsel_hack_select_font), fontsel);
481 
482     g_signal_connect_after(G_OBJECT(fontsel->family_list), "map",
483 			   G_CALLBACK(gtk_fontsel_hack_scroll_on_map),
484 			   fontsel);
485 
486     gtk_fontsel_hack_show_available_styles(fontsel);
487 
488     g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(fontsel->face_list)), "changed",
489 		     G_CALLBACK(gtk_fontsel_hack_select_style), fontsel);
490 
491     gtk_fontsel_hack_show_available_sizes(fontsel, TRUE);
492 
493     g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(fontsel->size_list)), "changed",
494 		     G_CALLBACK(gtk_fontsel_hack_select_size), fontsel);
495 
496     /* create the text entry widget */
497     label = gtk_label_new_with_mnemonic(_("_Preview:"));
498     gtk_widget_show(label);
499 
500     text_frame = gtk_frame_new(NULL);
501     gtk_frame_set_label_widget(GTK_FRAME(text_frame), label);
502 
503     gtk_widget_show(text_frame);
504     gtk_frame_set_shadow_type(GTK_FRAME(text_frame), GTK_SHADOW_ETCHED_IN);
505     gtk_box_pack_start(GTK_BOX(fontsel), text_frame,
506 		       FALSE, TRUE, 0);
507 
508     /* This is just used to get a 4-pixel space around the preview entry. */
509     text_box = gtk_hbox_new(FALSE, 0);
510     gtk_widget_show(text_box);
511     gtk_container_add(GTK_CONTAINER(text_frame), text_box);
512     gtk_container_set_border_width(GTK_CONTAINER(text_box), 4);
513 
514     fontsel->preview_entry = gtk_entry_new();
515     gtk_label_set_mnemonic_widget(GTK_LABEL(label), fontsel->preview_entry);
516 
517     gtk_widget_show(fontsel->preview_entry);
518     g_signal_connect(G_OBJECT(fontsel->preview_entry), "changed",
519 		     (GCallback) gtk_fontsel_hack_preview_changed,
520 		     fontsel);
521     gtk_widget_set_size_request(fontsel->preview_entry, -1, INITIAL_PREVIEW_HEIGHT);
522     gtk_box_pack_start(GTK_BOX(text_box), fontsel->preview_entry,
523 		       TRUE, TRUE, 0);
524 
525     gtk_fontsel_hack_update_preview(fontsel);
526 
527     gtk_widget_pop_composite_child();
528 }
529 
530 static void
gtk_fontsel_hack_display_fonts(GtkFontselHack * fontsel)531 gtk_fontsel_hack_display_fonts (GtkFontselHack *fontsel)
532 {
533     /* Insert the (possibly revised) list of fonts. */
534     gtk_fontsel_hack_show_available_fonts(fontsel);
535     gtk_fontsel_hack_show_available_styles(fontsel);
536     gtk_fontsel_hack_show_available_sizes(fontsel, TRUE);
537 }
538 
539 GtkWidget *
gtk_fontsel_hack_new(void)540 gtk_fontsel_hack_new (void)
541 {
542     GtkFontselHack *fontsel;
543 
544     fontsel = g_object_new(GTK_TYPE_FNTHACK, NULL);
545 
546     return GTK_WIDGET(fontsel);
547 }
548 
549 static void
gtk_fontsel_hack_finalize(GObject * object)550 gtk_fontsel_hack_finalize (GObject *object)
551 {
552     g_return_if_fail(GTK_IS_FNTHACK(object));
553 
554     (* G_OBJECT_CLASS (gtk_fontsel_hack_parent_class)->finalize) (object);
555 }
556 
557 static void
gtk_fontsel_hack_screen_changed(GtkWidget * widget,GdkScreen * previous_screen)558 gtk_fontsel_hack_screen_changed (GtkWidget *widget,
559 				 GdkScreen *previous_screen)
560 {
561     GtkFontselHack *fontsel = GTK_FNTHACK(widget);
562 
563     if (gtk_widget_has_screen(GTK_WIDGET(fontsel))) {
564 	gtk_fontsel_hack_show_available_fonts(fontsel);
565 	gtk_fontsel_hack_show_available_sizes(fontsel, TRUE);
566 	gtk_fontsel_hack_show_available_styles(fontsel);
567     }
568 }
569 
570 static void
gtk_fontsel_hack_preview_changed(GtkWidget * entry,GtkFontselHack * fontsel)571 gtk_fontsel_hack_preview_changed (GtkWidget *entry,
572 				  GtkFontselHack *fontsel)
573 {
574     g_object_notify(G_OBJECT(fontsel), "preview_text");
575 }
576 
577 static void
scroll_to_selection(GtkTreeView * tree_view)578 scroll_to_selection (GtkTreeView *tree_view)
579 {
580     GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view);
581     GtkTreeModel *model;
582     GtkTreeIter iter;
583 
584     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
585 	GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
586 
587 	gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, 0.5, 0.5);
588 	gtk_tree_path_free(path);
589     }
590 }
591 
592 static void
set_cursor_to_iter(GtkTreeView * view,GtkTreeIter * iter)593 set_cursor_to_iter (GtkTreeView *view,
594 		    GtkTreeIter *iter)
595 {
596     GtkTreeModel *model = gtk_tree_view_get_model(view);
597     GtkTreePath *path = gtk_tree_model_get_path(model, iter);
598 
599     if (path != NULL) {
600 	gtk_tree_view_set_cursor(view, path, 0, FALSE);
601 	gtk_tree_path_free(path);
602     }
603 }
604 
605 /* This is called when the list is mapped. Here we scroll to the current
606    font if necessary. */
607 static void
gtk_fontsel_hack_scroll_on_map(GtkWidget * widget,gpointer data)608 gtk_fontsel_hack_scroll_on_map (GtkWidget *widget,
609 				gpointer data)
610 {
611     GtkFontselHack *fontsel;
612 
613     fontsel = GTK_FNTHACK (data);
614 
615     /* Try to scroll the font family list to the selected item */
616     scroll_to_selection(GTK_TREE_VIEW(fontsel->family_list));
617 
618     /* Try to scroll the font family list to the selected item */
619     scroll_to_selection(GTK_TREE_VIEW(fontsel->face_list));
620 
621     /* Try to scroll the font family list to the selected item */
622     scroll_to_selection(GTK_TREE_VIEW(fontsel->size_list));
623 }
624 
625 /* This is called when a family is selected in the list. */
626 static void
gtk_fontsel_hack_select_font(GtkTreeSelection * selection,gpointer data)627 gtk_fontsel_hack_select_font (GtkTreeSelection *selection,
628 			      gpointer data)
629 {
630     GtkFontselHack *fontsel;
631     GtkTreeModel *model;
632     GtkTreeIter iter;
633 
634     fontsel = GTK_FNTHACK(data);
635 
636     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
637 	PangoFontFamily *family;
638 
639 	gtk_tree_model_get(model, &iter, FAMILY_COLUMN, &family, -1);
640 	if (fontsel->family != family) {
641 	    fontsel->family = family;
642 	    gtk_fontsel_hack_show_available_styles(fontsel);
643 	    gtk_fontsel_hack_select_best_style(fontsel, TRUE);
644 	}
645 
646 	g_object_unref(family);
647     }
648 }
649 
650 static int
cmp_families(const void * a,const void * b)651 cmp_families (const void *a, const void *b)
652 {
653     const char *a_name = pango_font_family_get_name (*(PangoFontFamily **)a);
654     const char *b_name = pango_font_family_get_name (*(PangoFontFamily **)b);
655 
656     return g_utf8_collate(a_name, b_name);
657 }
658 
659 static void
gtk_fontsel_hack_show_available_fonts(GtkFontselHack * fontsel)660 gtk_fontsel_hack_show_available_fonts (GtkFontselHack *fontsel)
661 {
662     GtkTreeModel *model;
663     GtkListStore *store;
664     PangoContext *context;
665     PangoFontFamily **families;
666     const char *famname;
667     gint nf, i, got_ok;
668     GtkTreeIter iter, match_iter;
669     gint err = 0;
670 
671     model = gtk_tree_view_get_model(GTK_TREE_VIEW(fontsel->family_list));
672     store = GTK_LIST_STORE(model);
673     gtk_list_store_clear(store);
674 
675     context = gtk_widget_get_pango_context(GTK_WIDGET(fontsel));
676     pango_context_list_families(context, &families, &nf);
677     qsort(families, nf, sizeof *families, cmp_families);
678 
679 #if FONT_DEBUG
680     fprintf(stderr, "Font selector: got %d families\n", nf);
681 #endif
682 
683     fontsel->family = NULL;
684     got_ok = 0;
685 
686     gtk_tree_model_get_iter_first(model, &iter);
687 
688     for (i=0; i<nf && !err; i++) {
689 	famname = pango_font_family_get_name(families[i]);
690 	if (famname == NULL) {
691 	    /* bullet-proofing */
692 	    continue;
693 	}
694 
695 #if FONT_DEBUG
696 	fprintf(stderr, "Examining font family '%s'\n", famname);
697 #endif
698 	if (!validate_font_family(families[i], famname, i, nf, fontsel->filter, &err)) {
699 	    continue;
700 	}
701 
702 	gtk_list_store_append(store, &iter);
703 	gtk_list_store_set(store, &iter,
704 			   FAMILY_COLUMN, families[i],
705 			   FAMILY_NAME_COLUMN, famname,
706 			   -1);
707 
708 	if (!got_ok) {
709 	    if (fontsel->filter != FONT_HACK_NONE ||
710 		i == 0 || !g_ascii_strcasecmp(famname, "sans")) {
711 		got_ok = 1;
712 		fontsel->family = families[i];
713 		match_iter = iter;
714 	    }
715 	}
716     }
717 
718     if (fontsel->family != NULL) {
719 	set_cursor_to_iter(GTK_TREE_VIEW(fontsel->family_list), &match_iter);
720     }
721 
722     g_free(families);
723 }
724 
725 static int
compare_font_descriptions(const PangoFontDescription * a,const PangoFontDescription * b)726 compare_font_descriptions (const PangoFontDescription *a,
727 			   const PangoFontDescription *b)
728 {
729     int val = strcmp(pango_font_description_get_family(a),
730 		     pango_font_description_get_family(b));
731 
732     if (val != 0) {
733 	return val;
734     }
735 
736     if (pango_font_description_get_weight(a) !=
737 	pango_font_description_get_weight(b))
738 	return pango_font_description_get_weight(a) -
739 	    pango_font_description_get_weight(b);
740 
741     if (pango_font_description_get_style(a) !=
742 	pango_font_description_get_style(b))
743 	return pango_font_description_get_style(a) -
744 	    pango_font_description_get_style(b);
745 
746     if (pango_font_description_get_stretch(a) !=
747 	pango_font_description_get_stretch(b))
748 	return pango_font_description_get_stretch(a) -
749 	    pango_font_description_get_stretch(b);
750 
751     if (pango_font_description_get_variant(a) !=
752 	pango_font_description_get_variant(b))
753 	return pango_font_description_get_variant(a) -
754 	    pango_font_description_get_variant(b);
755 
756     return 0;
757 }
758 
759 static int
faces_sort_func(const void * a,const void * b)760 faces_sort_func (const void *a, const void *b)
761 {
762     PangoFontDescription *desc_a = pango_font_face_describe(*(PangoFontFace **)a);
763     PangoFontDescription *desc_b = pango_font_face_describe(*(PangoFontFace **)b);
764 
765     int ord = compare_font_descriptions(desc_a, desc_b);
766 
767     pango_font_description_free(desc_a);
768     pango_font_description_free(desc_b);
769 
770     return ord;
771 }
772 
773 static gboolean
font_description_style_equal(const PangoFontDescription * a,const PangoFontDescription * b)774 font_description_style_equal (const PangoFontDescription *a,
775 			      const PangoFontDescription *b)
776 {
777     return (pango_font_description_get_weight(a) ==
778 	    pango_font_description_get_weight(b) &&
779 	    pango_font_description_get_style(a) ==
780 	    pango_font_description_get_style(b) &&
781 	    pango_font_description_get_stretch(a) ==
782 	    pango_font_description_get_stretch(b) &&
783 	    pango_font_description_get_variant(a) ==
784 	    pango_font_description_get_variant(b));
785 }
786 
787 /* This fills the font style list with all the possible style combinations
788    for the current font family. */
789 
790 static void
gtk_fontsel_hack_show_available_styles(GtkFontselHack * fontsel)791 gtk_fontsel_hack_show_available_styles (GtkFontselHack *fontsel)
792 {
793     gint n_faces, i;
794     PangoFontFace **faces;
795     PangoFontDescription *old_desc;
796     GtkListStore *model;
797     GtkTreeIter iter, match_row;
798     PangoFontFace *match_face = NULL;
799 
800     model = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(fontsel->face_list)));
801 
802     if (fontsel->face) {
803 	old_desc = pango_font_face_describe(fontsel->face);
804     } else {
805 	old_desc= NULL;
806     }
807 
808     pango_font_family_list_faces(fontsel->family, &faces, &n_faces);
809     qsort(faces, n_faces, sizeof *faces, faces_sort_func);
810 
811     gtk_list_store_clear(model);
812     gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
813 
814     for (i=0; i<n_faces; i++) {
815 	const gchar *str = pango_font_face_get_face_name(faces[i]);
816 
817 	gtk_list_store_append(model, &iter);
818 	gtk_list_store_set(model, &iter,
819 			   FACE_COLUMN, faces[i],
820 			   FACE_NAME_COLUMN, str,
821 			   -1);
822 
823 	if (i == 0) {
824 	    match_row = iter;
825 	    match_face = faces[i];
826 	} else if (old_desc) {
827 	    PangoFontDescription *tmp_desc = pango_font_face_describe(faces[i]);
828 
829 	    if (font_description_style_equal(tmp_desc, old_desc)) {
830 		match_row = iter;
831 		match_face = faces[i];
832 	    }
833 	    pango_font_description_free(tmp_desc);
834 	}
835     }
836 
837     if (old_desc != NULL) {
838 	pango_font_description_free(old_desc);
839     }
840 
841     fontsel->face = match_face;
842     if (match_face) {
843 	set_cursor_to_iter(GTK_TREE_VIEW(fontsel->face_list), &match_row);
844     }
845 
846     g_free(faces);
847 }
848 
849 /* This selects a style when the user selects a font. It just uses the first
850    available style at present. I was thinking of trying to maintain the
851    selected style, e.g. bold italic, when the user selects different fonts.
852    However, the interface is so easy to use now I'm not sure it's worth it.
853    Note: This will load a font. */
854 
855 static void
gtk_fontsel_hack_select_best_style(GtkFontselHack * fontsel,gboolean use_first)856 gtk_fontsel_hack_select_best_style (GtkFontselHack *fontsel,
857 				    gboolean use_first)
858 {
859     GtkTreeIter iter;
860     GtkTreeModel *model;
861 
862     model = gtk_tree_view_get_model (GTK_TREE_VIEW (fontsel->face_list));
863 
864     if (gtk_tree_model_get_iter_first (model, &iter)) {
865 	set_cursor_to_iter(GTK_TREE_VIEW(fontsel->face_list), &iter);
866 	scroll_to_selection(GTK_TREE_VIEW(fontsel->face_list));
867     }
868 
869     gtk_fontsel_hack_show_available_sizes(fontsel, FALSE);
870     gtk_fontsel_hack_select_best_size(fontsel);
871 }
872 
873 
874 /* This is called when a style is selected in the list. */
875 
876 static void
gtk_fontsel_hack_select_style(GtkTreeSelection * selection,gpointer data)877 gtk_fontsel_hack_select_style (GtkTreeSelection *selection,
878 			       gpointer          data)
879 {
880     GtkFontselHack *fontsel = GTK_FNTHACK(data);
881     GtkTreeModel *model;
882     GtkTreeIter iter;
883 
884     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
885 	PangoFontFace *face;
886 
887 	gtk_tree_model_get(model, &iter, FACE_COLUMN, &face, -1);
888 	fontsel->face = face;
889 
890 	g_object_unref(face);
891     }
892 
893     gtk_fontsel_hack_show_available_sizes(fontsel, FALSE);
894     gtk_fontsel_hack_select_best_size(fontsel);
895 }
896 
897 static void
gtk_fontsel_hack_show_available_sizes(GtkFontselHack * fontsel,gboolean first_time)898 gtk_fontsel_hack_show_available_sizes (GtkFontselHack *fontsel,
899 				       gboolean first_time)
900 {
901     size_t i;
902     GtkListStore *model;
903     gchar buffer[128];
904     gchar *p;
905 
906     model = GTK_LIST_STORE(gtk_tree_view_get_model (GTK_TREE_VIEW (fontsel->size_list)));
907 
908     /* Insert the standard font sizes */
909     if (first_time) {
910 	GtkTreeIter iter;
911 
912 	gtk_list_store_clear (model);
913 	gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
914 
915 	for (i = 0; i < G_N_ELEMENTS (font_sizes); i++) {
916 	    gtk_list_store_append (model, &iter);
917 	    gtk_list_store_set (model, &iter, SIZE_COLUMN, font_sizes[i], -1);
918 
919 	    if (font_sizes[i] * PANGO_SCALE == fontsel->size)
920 		set_cursor_to_iter (GTK_TREE_VIEW (fontsel->size_list), &iter);
921 	}
922     } else {
923 	GtkTreeIter iter;
924 	gboolean found = FALSE;
925 
926 	gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
927 	for (i = 0; i < G_N_ELEMENTS (font_sizes) && !found; i++) {
928 	    if (font_sizes[i] * PANGO_SCALE == fontsel->size) {
929 		set_cursor_to_iter (GTK_TREE_VIEW (fontsel->size_list), &iter);
930 		found = TRUE;
931 	    }
932 
933 	    gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
934 	}
935 
936 	if (!found) {
937 	    GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (fontsel->size_list));
938 
939 	    gtk_tree_selection_unselect_all (selection);
940 	}
941     }
942 
943     /* Set the entry to the new size, rounding to 1 digit,
944      * trimming of trailing 0's and a trailing period
945      */
946     sprintf (buffer, "%.1f", fontsel->size / (1.0 * PANGO_SCALE));
947     if (strchr (buffer, '.')) {
948 	p = buffer + strlen (buffer) - 1;
949 	while (*p == '0')
950 	    p--;
951 	if (*p == '.')
952 	    p--;
953 	p[1] = '\0';
954     }
955 
956     /* Compare, to avoid moving the cursor unecessarily */
957     if (strcmp (gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry)), buffer) != 0)
958 	gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
959 }
960 
961 static void
gtk_fontsel_hack_select_best_size(GtkFontselHack * fontsel)962 gtk_fontsel_hack_select_best_size (GtkFontselHack *fontsel)
963 {
964     gtk_fontsel_hack_load_font(fontsel);
965 }
966 
967 static void
gtk_fontsel_hack_set_size(GtkFontselHack * fontsel,gint new_size)968 gtk_fontsel_hack_set_size (GtkFontselHack *fontsel,
969 			   gint new_size)
970 {
971     if (fontsel->size != new_size) {
972 	fontsel->size = new_size;
973 
974 	gtk_fontsel_hack_show_available_sizes (fontsel, FALSE);
975 	gtk_fontsel_hack_load_font (fontsel);
976     }
977 }
978 
979 /* If the user hits return in the font size entry, we change to the new font
980    size. */
981 
982 static void
gtk_fontsel_hack_size_activate(GtkWidget * w,gpointer data)983 gtk_fontsel_hack_size_activate (GtkWidget *w,
984 				gpointer data)
985 {
986     GtkFontselHack *fontsel;
987     gint new_size;
988     const gchar *text;
989 
990     fontsel = GTK_FNTHACK(data);
991 
992     text = gtk_entry_get_text(GTK_ENTRY(fontsel->size_entry));
993     new_size = (int) MAX(0.1, atof(text) * PANGO_SCALE + 0.5);
994 
995     gtk_fontsel_hack_set_size(fontsel, new_size);
996 }
997 
998 static gboolean
gtk_fontsel_hack_size_focus_out(GtkWidget * w,GdkEventFocus * event,gpointer data)999 gtk_fontsel_hack_size_focus_out (GtkWidget*w,
1000 				 GdkEventFocus *event,
1001 				 gpointer data)
1002 {
1003     gtk_fontsel_hack_size_activate(w, data);
1004 
1005     return TRUE;
1006 }
1007 
1008 /* This is called when a size is selected in the list. */
1009 
1010 static void
gtk_fontsel_hack_select_size(GtkTreeSelection * selection,gpointer data)1011 gtk_fontsel_hack_select_size (GtkTreeSelection *selection,
1012 			      gpointer data)
1013 {
1014     GtkFontselHack *fontsel;
1015     GtkTreeModel *model;
1016     GtkTreeIter iter;
1017     gint new_size;
1018 
1019     fontsel = GTK_FNTHACK(data);
1020 
1021     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
1022 	gtk_tree_model_get(model, &iter, SIZE_COLUMN, &new_size, -1);
1023 	gtk_fontsel_hack_set_size(fontsel, new_size * PANGO_SCALE);
1024     }
1025 }
1026 
1027 static void
gtk_fontsel_hack_load_font(GtkFontselHack * fontsel)1028 gtk_fontsel_hack_load_font (GtkFontselHack *fontsel)
1029 {
1030     gtk_fontsel_hack_update_preview(fontsel);
1031 }
1032 
1033 static PangoFontDescription *
gtk_fontsel_hack_get_font_description(GtkFontselHack * fontsel)1034 gtk_fontsel_hack_get_font_description (GtkFontselHack *fontsel)
1035 {
1036     PangoFontDescription *font_desc = pango_font_face_describe(fontsel->face);
1037 
1038     pango_font_description_set_size(font_desc, fontsel->size);
1039 
1040     return font_desc;
1041 }
1042 
1043 /* This sets the font in the preview entry to the selected font, and
1044    tries to make sure that the preview entry is a reasonable size,
1045    i.e. so that the text can be seen with a bit of space to spare. But
1046    it tries to avoid resizing the entry every time the font changes.
1047    This is also used to shrink the preview if the font size was
1048    decreased, but that made it awkward if the users wanted to resize
1049    the window themselves. */
1050 
1051 static void
gtk_fontsel_hack_update_preview(GtkFontselHack * fontsel)1052 gtk_fontsel_hack_update_preview (GtkFontselHack *fontsel)
1053 {
1054 #if GTK_MAJOR_VERSION < 3
1055     GtkRcStyle *rc_style;
1056 #endif
1057     gint new_height;
1058     GtkRequisition req, old_requisition;
1059     GtkWidget *preview_entry = fontsel->preview_entry;
1060     const gchar *text;
1061 
1062     gtk_widget_get_child_requisition(preview_entry, &old_requisition);
1063 
1064 #if GTK_MAJOR_VERSION >= 3
1065     gtk_widget_modify_font(preview_entry, gtk_fontsel_hack_get_font_description(fontsel));
1066 #else
1067     rc_style = gtk_rc_style_new();
1068     rc_style->font_desc = gtk_fontsel_hack_get_font_description(fontsel);
1069     gtk_widget_modify_style(preview_entry, rc_style);
1070     g_object_unref(rc_style);
1071 #endif
1072 
1073     gtk_widget_size_request(preview_entry, NULL);
1074     gtk_widget_get_requisition(preview_entry, &req);
1075 
1076     /* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
1077     new_height = CLAMP(req.height,
1078 		       INITIAL_PREVIEW_HEIGHT,
1079 		       MAX_PREVIEW_HEIGHT);
1080 
1081     if (new_height > old_requisition.height || new_height < old_requisition.height - 30)
1082 	gtk_widget_set_size_request(preview_entry, -1, new_height);
1083 
1084     /* This sets the preview text, if it hasn't been set already. */
1085     text = gtk_entry_get_text(GTK_ENTRY (preview_entry));
1086     if (*text == '\0') {
1087 	gtk_entry_set_text(GTK_ENTRY(preview_entry), _(PREVIEW_TEXT));
1088     }
1089     gtk_editable_set_position(GTK_EDITABLE(preview_entry), 0);
1090 }
1091 
1092 /*****************************************************************************
1093  * These functions are the main public interface for getting/setting the font.
1094  *****************************************************************************/
1095 
1096 gchar *
gtk_fontsel_hack_get_font_name(GtkFontselHack * fontsel)1097 gtk_fontsel_hack_get_font_name (GtkFontselHack *fontsel)
1098 {
1099     PangoFontDescription *font_desc =
1100 	gtk_fontsel_hack_get_font_description(fontsel);
1101     gchar *result;
1102 
1103     result = pango_font_description_to_string(font_desc);
1104     pango_font_description_free(font_desc);
1105 
1106     return result;
1107 }
1108 
1109 /* This sets the current font, selecting the appropriate list rows.
1110    First we check the fontname is valid and try to find the font
1111    family - i.e. the name in the main list. If we can't find that,
1112    then just return.  Next we try to set each of the properties
1113    according to the fontname.  Finally we select the font family &
1114    style in the lists. */
1115 
1116 gboolean
gtk_fontsel_hack_set_font_name(GtkFontselHack * fontsel,const gchar * fontname)1117 gtk_fontsel_hack_set_font_name (GtkFontselHack *fontsel,
1118 				const gchar *fontname)
1119 {
1120     PangoFontFamily *new_family = NULL;
1121     PangoFontFace *new_face = NULL;
1122     PangoFontFace *fallback_face = NULL;
1123     PangoFontDescription *new_desc;
1124     GtkTreeModel *model;
1125     GtkTreeIter iter;
1126     GtkTreeIter match_iter;
1127     gboolean valid;
1128 
1129     g_return_val_if_fail(GTK_IS_FNTHACK(fontsel), FALSE);
1130 
1131     new_desc = pango_font_description_from_string(fontname);
1132 
1133     /* Check to make sure that this is in the list of allowed fonts */
1134 
1135     model = gtk_tree_view_get_model(GTK_TREE_VIEW(fontsel->family_list));
1136     for (valid = gtk_tree_model_get_iter_first (model, &iter);
1137 	 valid;
1138 	 valid = gtk_tree_model_iter_next(model, &iter)) {
1139 	PangoFontFamily *family;
1140 
1141 	gtk_tree_model_get(model, &iter, FAMILY_COLUMN, &family, -1);
1142 
1143 	if (g_ascii_strcasecmp(pango_font_family_get_name(family),
1144 			       pango_font_description_get_family(new_desc)) == 0)
1145 	    new_family = family;
1146 
1147 	g_object_unref(family);
1148 
1149 	if (new_family)
1150 	    break;
1151     }
1152 
1153     if (!new_family)
1154 	return FALSE;
1155 
1156     fontsel->family = new_family;
1157     set_cursor_to_iter(GTK_TREE_VIEW(fontsel->family_list), &iter);
1158     gtk_fontsel_hack_show_available_styles(fontsel);
1159 
1160     model = gtk_tree_view_get_model (GTK_TREE_VIEW (fontsel->face_list));
1161     for (valid = gtk_tree_model_get_iter_first(model, &iter);
1162 	 valid;
1163 	 valid = gtk_tree_model_iter_next(model, &iter)) {
1164 	PangoFontFace *face;
1165 	PangoFontDescription *tmp_desc;
1166 
1167 	gtk_tree_model_get(model, &iter, FACE_COLUMN, &face, -1);
1168 	tmp_desc = pango_font_face_describe(face);
1169 
1170 	if (font_description_style_equal(tmp_desc, new_desc))
1171 	    new_face = face;
1172 
1173 	if (!fallback_face) {
1174 	    fallback_face = face;
1175 	    match_iter = iter;
1176 	}
1177 
1178 	pango_font_description_free(tmp_desc);
1179 	g_object_unref(face);
1180 
1181 	if (new_face) {
1182 	    match_iter = iter;
1183 	    break;
1184 	}
1185     }
1186 
1187     if (!new_face)
1188 	new_face = fallback_face;
1189 
1190     fontsel->face = new_face;
1191     set_cursor_to_iter(GTK_TREE_VIEW(fontsel->face_list), &match_iter);
1192 
1193     gtk_fontsel_hack_set_size(fontsel, pango_font_description_get_size(new_desc));
1194 
1195     g_object_freeze_notify(G_OBJECT(fontsel));
1196     g_object_notify(G_OBJECT(fontsel), "font_name");
1197     g_object_thaw_notify(G_OBJECT(fontsel));
1198 
1199     pango_font_description_free(new_desc);
1200 
1201     return TRUE;
1202 }
1203 
1204 gint
gtk_fontsel_hack_get_filter(GtkFontselHack * fontsel)1205 gtk_fontsel_hack_get_filter (GtkFontselHack *fontsel)
1206 {
1207     return fontsel->filter;
1208 }
1209 
1210 void
gtk_fontsel_hack_set_filter(GtkFontselHack * fontsel,FontFilterType filter)1211 gtk_fontsel_hack_set_filter (GtkFontselHack *fontsel,
1212 			     FontFilterType filter)
1213 {
1214     fontsel->filter = filter;
1215     gtk_fontsel_hack_display_fonts(fontsel);
1216 }
1217 
1218 /* This returns the text in the preview entry. You should copy the returned
1219    text if you need it. */
1220 
1221 G_CONST_RETURN gchar*
gtk_fontsel_hack_get_preview_text(GtkFontselHack * fontsel)1222 gtk_fontsel_hack_get_preview_text (GtkFontselHack *fontsel)
1223 {
1224     return gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
1225 }
1226 
1227 
1228 /* This sets the text in the preview entry. */
1229 
1230 void
gtk_fontsel_hack_set_preview_text(GtkFontselHack * fontsel,const gchar * text)1231 gtk_fontsel_hack_set_preview_text (GtkFontselHack *fontsel,
1232 				   const gchar *text)
1233 {
1234     gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), text);
1235 }
1236 
1237 /*****************************************************************************
1238  * GtkFontselHackDialog
1239  *****************************************************************************/
1240 
1241 static void gtk_fontsel_hack_dialog_buildable_interface_init     (GtkBuildableIface *iface);
1242 static GObject * gtk_fontsel_hack_dialog_buildable_get_internal_child (GtkBuildable *buildable,
1243 								       GtkBuilder   *builder,
1244 								       const gchar  *childname);
1245 
1246 G_DEFINE_TYPE_WITH_CODE (GtkFontselHackDialog, gtk_fontsel_hack_dialog,
1247 			 GTK_TYPE_DIALOG,
1248 			 G_IMPLEMENT_INTERFACE(GTK_TYPE_BUILDABLE,
1249 					       gtk_fontsel_hack_dialog_buildable_interface_init))
1250 
1251 static GtkBuildableIface *parent_buildable_iface;
1252 
1253 static void
gtk_fontsel_hack_dialog_buildable_interface_init(GtkBuildableIface * iface)1254 gtk_fontsel_hack_dialog_buildable_interface_init (GtkBuildableIface *iface)
1255 {
1256     parent_buildable_iface = g_type_interface_peek_parent(iface);
1257     iface->get_internal_child = gtk_fontsel_hack_dialog_buildable_get_internal_child;
1258 }
1259 
1260 static GObject *
gtk_fontsel_hack_dialog_buildable_get_internal_child(GtkBuildable * buildable,GtkBuilder * builder,const gchar * childname)1261 gtk_fontsel_hack_dialog_buildable_get_internal_child (GtkBuildable *buildable,
1262 						      GtkBuilder   *builder,
1263 						      const gchar  *childname)
1264 {
1265     if (strcmp(childname, "ok_button") == 0)
1266 	return G_OBJECT(GTK_FNTHACK_DIALOG(buildable)->ok_button);
1267     else if (strcmp(childname, "cancel_button") == 0)
1268 	return G_OBJECT(GTK_FNTHACK_DIALOG(buildable)->cancel_button);
1269     else if (strcmp(childname, "font_selection") == 0)
1270 	return G_OBJECT(GTK_FNTHACK_DIALOG(buildable)->fontsel);
1271 
1272     return parent_buildable_iface->get_internal_child(buildable, builder, childname);
1273 }
1274 
1275 static void
gtk_fontsel_hack_dialog_class_init(GtkFontselHackDialogClass * klass)1276 gtk_fontsel_hack_dialog_class_init (GtkFontselHackDialogClass *klass)
1277 {
1278 }
1279 
1280 static void
gtk_fontsel_hack_dialog_init(GtkFontselHackDialog * fontseldiag)1281 gtk_fontsel_hack_dialog_init (GtkFontselHackDialog *fontseldiag)
1282 {
1283     GtkDialog *dialog;
1284 
1285     gtk_widget_push_composite_child();
1286 
1287     dialog = GTK_DIALOG(fontseldiag);
1288 
1289     fontseldiag->dialog_width = -1;
1290     fontseldiag->auto_resize = TRUE;
1291 
1292     gtk_widget_set_events(GTK_WIDGET(fontseldiag), GDK_STRUCTURE_MASK);
1293     g_signal_connect(G_OBJECT(fontseldiag), "configure-event",
1294 		     (GCallback) gtk_fontsel_hack_dialog_on_configure,
1295 		     fontseldiag);
1296 
1297     gtk_container_set_border_width(GTK_CONTAINER(fontseldiag), 4);
1298     gtk_window_set_resizable(GTK_WINDOW(fontseldiag), FALSE);
1299 
1300     fontseldiag->main_vbox = gtk_dialog_get_content_area(dialog);
1301 
1302     fontseldiag->fontsel = gtk_fontsel_hack_new();
1303     gtk_container_set_border_width(GTK_CONTAINER(fontseldiag->fontsel), 4);
1304     gtk_widget_show(fontseldiag->fontsel);
1305     gtk_box_pack_start(GTK_BOX(fontseldiag->main_vbox),
1306 		       fontseldiag->fontsel, TRUE, TRUE, 0);
1307 
1308     /* Create the action area */
1309     fontseldiag->action_area = gtk_dialog_get_action_area(dialog);
1310 
1311     fontseldiag->cancel_button = gtk_dialog_add_button(dialog,
1312 						       GTK_STOCK_CANCEL,
1313 						       GTK_RESPONSE_CANCEL);
1314     fontseldiag->ok_button = gtk_dialog_add_button(dialog,
1315 						   GTK_STOCK_OK,
1316 						   GTK_RESPONSE_OK);
1317     gtk_widget_grab_default(fontseldiag->ok_button);
1318 
1319     gtk_window_set_title(GTK_WINDOW(fontseldiag), _("Font Selection"));
1320 #if GTK_MAJOR_VERSION < 3
1321     gtk_dialog_set_has_separator(GTK_DIALOG (dialog), FALSE);
1322 #endif
1323     gtk_widget_pop_composite_child();
1324 }
1325 
1326 GtkWidget*
gtk_fontsel_hack_dialog_new(const gchar * title)1327 gtk_fontsel_hack_dialog_new (const gchar *title)
1328 {
1329     GtkFontselHackDialog *fontseldiag;
1330 
1331     fontseldiag = g_object_new(GTK_TYPE_FNTHACK_DIALOG, NULL);
1332 
1333     if (title)
1334 	gtk_window_set_title(GTK_WINDOW(fontseldiag), title);
1335 
1336     return GTK_WIDGET(fontseldiag);
1337 }
1338 
1339 gchar*
gtk_fontsel_hack_dialog_get_font_name(GtkFontselHackDialog * fsd)1340 gtk_fontsel_hack_dialog_get_font_name (GtkFontselHackDialog *fsd)
1341 {
1342     return gtk_fontsel_hack_get_font_name(GTK_FNTHACK(fsd->fontsel));
1343 }
1344 
1345 gboolean
gtk_fontsel_hack_dialog_set_font_name(GtkFontselHackDialog * fsd,const gchar * fontname)1346 gtk_fontsel_hack_dialog_set_font_name (GtkFontselHackDialog *fsd,
1347 				       const gchar *fontname)
1348 {
1349     return gtk_fontsel_hack_set_font_name(GTK_FNTHACK(fsd->fontsel), fontname);
1350 }
1351 
1352 gint
gtk_fontsel_hack_dialog_get_filter(GtkFontselHackDialog * fsd)1353 gtk_fontsel_hack_dialog_get_filter (GtkFontselHackDialog *fsd)
1354 {
1355     return gtk_fontsel_hack_get_filter (GTK_FNTHACK (fsd->fontsel));
1356 }
1357 
1358 void
gtk_fontsel_hack_dialog_set_filter(GtkFontselHackDialog * fsd,FontFilterType filter)1359 gtk_fontsel_hack_dialog_set_filter (GtkFontselHackDialog *fsd,
1360 				    FontFilterType	  filter)
1361 {
1362     gtk_fontsel_hack_set_filter(GTK_FNTHACK(fsd->fontsel), filter);
1363 }
1364 
1365 G_CONST_RETURN gchar*
gtk_fontsel_hack_dialog_get_preview_text(GtkFontselHackDialog * fsd)1366 gtk_fontsel_hack_dialog_get_preview_text (GtkFontselHackDialog *fsd)
1367 {
1368     return gtk_fontsel_hack_get_preview_text(GTK_FNTHACK(fsd->fontsel));
1369 }
1370 
1371 void
gtk_fontsel_hack_dialog_set_preview_text(GtkFontselHackDialog * fsd,const gchar * text)1372 gtk_fontsel_hack_dialog_set_preview_text (GtkFontselHackDialog *fsd,
1373 					  const gchar *text)
1374 {
1375     gtk_fontsel_hack_set_preview_text(GTK_FNTHACK(fsd->fontsel), text);
1376 }
1377 
gtk_fontsel_hack_dialog_ok_button(GtkWidget * fsd)1378 GtkWidget *gtk_fontsel_hack_dialog_ok_button (GtkWidget *fsd)
1379 {
1380     return GTK_FNTHACK_DIALOG(fsd)->ok_button;
1381 }
1382 
gtk_fontsel_hack_dialog_cancel_button(GtkWidget * fsd)1383 GtkWidget *gtk_fontsel_hack_dialog_cancel_button (GtkWidget *fsd)
1384 {
1385     return GTK_FNTHACK_DIALOG(fsd)->cancel_button;
1386 }
1387 
1388 /* This turns auto-shrink off if the user resizes the width of the dialog.
1389    It also turns it back on again if the user resizes it back to its normal
1390    width. */
1391 
1392 static gint
gtk_fontsel_hack_dialog_on_configure(GtkWidget * widget,GdkEventConfigure * event,GtkFontselHackDialog * fsd)1393 gtk_fontsel_hack_dialog_on_configure (GtkWidget *widget,
1394 				      GdkEventConfigure *event,
1395 				      GtkFontselHackDialog *fsd)
1396 {
1397     /* This sets the initial width. */
1398     if (fsd->dialog_width == -1) {
1399 	fsd->dialog_width = event->width;
1400     } else if (fsd->auto_resize && fsd->dialog_width != event->width) {
1401 	fsd->auto_resize = FALSE;
1402 	gtk_window_set_resizable(GTK_WINDOW(fsd), FALSE);
1403     } else if (!fsd->auto_resize && fsd->dialog_width == event->width) {
1404 	fsd->auto_resize = TRUE;
1405 	gtk_window_set_resizable(GTK_WINDOW(fsd), FALSE);
1406     }
1407 
1408     return FALSE;
1409 }
1410