]> Pileus Git - ~andy/gtk/blob - gtk/gtkfontsel.c
Added properties. Based on patch by Lee Mallabone.
[~andy/gtk] / gtk / gtkfontsel.c
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 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
36
37 #include "gdk/gdk.h"
38 #include "gdk/gdkkeysyms.h"
39
40 #include "gtkfontsel.h"
41
42 #include "gtkbutton.h"
43 #include "gtkclist.h"
44 #include "gtkentry.h"
45 #include "gtkframe.h"
46 #include "gtkhbbox.h"
47 #include "gtkhbox.h"
48 #include "gtklabel.h"
49 #include "gtkrc.h"
50 #include "gtksignal.h"
51 #include "gtkstock.h"
52 #include "gtktable.h"
53 #include "gtkvbox.h"
54 #include "gtkscrolledwindow.h"
55 #include "gtkintl.h"
56
57 /* This is the default text shown in the preview entry, though the user
58    can set it. Remember that some fonts only have capital letters. */
59 #define PREVIEW_TEXT "abcdefghijk ABCDEFGHIJK"
60
61 /* This is the initial and maximum height of the preview entry (it expands
62    when large font sizes are selected). Initial height is also the minimum. */
63 #define INITIAL_PREVIEW_HEIGHT 44
64 #define MAX_PREVIEW_HEIGHT 300
65
66 /* These are the sizes of the font, style & size clists. */
67 #define FONT_LIST_HEIGHT        136
68 #define FONT_LIST_WIDTH         190
69 #define FONT_STYLE_LIST_WIDTH   170
70 #define FONT_SIZE_LIST_WIDTH    60
71
72 /* These are what we use as the standard font sizes, for the size clist.
73  */
74 static const guint16 font_sizes[] = {
75   8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28,
76   32, 36, 40, 48, 56, 64, 72
77 };
78
79 enum {
80    PROP_0,
81    PROP_FONT_NAME,
82    PROP_FONT,
83    PROP_PREVIEW_TEXT
84 };
85
86 static void    gtk_font_selection_class_init         (GtkFontSelectionClass *klass);
87 static void    gtk_font_selection_set_property       (GObject         *object,
88                                                       guint            prop_id,
89                                                       const GValue    *value,
90                                                       GParamSpec      *pspec);
91 static void    gtk_font_selection_get_property       (GObject         *object,
92                                                       guint            prop_id,
93                                                       GValue          *value,
94                                                       GParamSpec      *pspec);
95 static void    gtk_font_selection_init               (GtkFontSelection      *fontsel);
96 static void    gtk_font_selection_finalize           (GObject               *object);
97
98 /* These are the callbacks & related functions. */
99 static void     gtk_font_selection_select_font           (GtkWidget        *w,
100                                                           gint              row,
101                                                           gint              column,
102                                                           GdkEventButton   *bevent,
103                                                           gpointer          data);
104 static void     gtk_font_selection_show_available_fonts  (GtkFontSelection *fs);
105
106 static void     gtk_font_selection_show_available_styles (GtkFontSelection *fs);
107 static void     gtk_font_selection_select_best_style     (GtkFontSelection *fs,
108                                                           gboolean          use_first);
109 static void     gtk_font_selection_select_style          (GtkWidget        *w,
110                                                           gint              row,
111                                                           gint              column,
112                                                           GdkEventButton   *bevent,
113                                                           gpointer          data);
114
115 static void     gtk_font_selection_select_best_size      (GtkFontSelection *fs);
116 static void     gtk_font_selection_show_available_sizes  (GtkFontSelection *fs);
117 static void     gtk_font_selection_size_activate         (GtkWidget        *w,
118                                                           gpointer          data);
119 static void     gtk_font_selection_select_size           (GtkWidget        *w,
120                                                           gint              row,
121                                                           gint              column,
122                                                           GdkEventButton   *bevent,
123                                                           gpointer          data);
124
125 static void     gtk_font_selection_expose_list           (GtkWidget        *w,
126                                                           GdkEventExpose   *event,
127                                                           gpointer          data);
128
129 static void     gtk_font_selection_preview_changed       (GtkWidget        *entry,
130                                                           GtkFontSelection *fontsel);
131
132 /* Misc. utility functions. */
133 static void     gtk_font_selection_load_font         (GtkFontSelection *fs);
134 static void    gtk_font_selection_update_preview     (GtkFontSelection *fs);
135
136 /* FontSelectionDialog */
137 static void    gtk_font_selection_dialog_class_init  (GtkFontSelectionDialogClass *klass);
138 static void    gtk_font_selection_dialog_init        (GtkFontSelectionDialog *fontseldiag);
139
140 static gint    gtk_font_selection_dialog_on_configure(GtkWidget      *widget,
141                                                       GdkEventConfigure *event,
142                                                       GtkFontSelectionDialog *fsd);
143
144 static GtkWindowClass *font_selection_parent_class = NULL;
145 static GtkVBoxClass *font_selection_dialog_parent_class = NULL;
146
147 GtkType
148 gtk_font_selection_get_type()
149 {
150   static GtkType font_selection_type = 0;
151   
152   if(!font_selection_type)
153     {
154       static const GtkTypeInfo fontsel_type_info =
155       {
156         "GtkFontSelection",
157         sizeof (GtkFontSelection),
158         sizeof (GtkFontSelectionClass),
159         (GtkClassInitFunc) gtk_font_selection_class_init,
160         (GtkObjectInitFunc) gtk_font_selection_init,
161         /* reserved_1 */ NULL,
162         /* reserved_2 */ NULL,
163         (GtkClassInitFunc) NULL,
164       };
165       
166       font_selection_type = gtk_type_unique (GTK_TYPE_VBOX,
167                                              &fontsel_type_info);
168     }
169   
170   return font_selection_type;
171 }
172
173 static void
174 gtk_font_selection_class_init(GtkFontSelectionClass *klass)
175 {
176   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
177   
178   font_selection_parent_class = gtk_type_class (GTK_TYPE_VBOX);
179   
180   gobject_class->set_property = gtk_font_selection_set_property;
181   gobject_class->get_property = gtk_font_selection_get_property;
182    
183   g_object_class_install_property (gobject_class,
184                                    PROP_FONT_NAME,
185                                    g_param_spec_string ("font_name",
186                                                         _("Font name"),
187                                                         _("The X string that represents this font."),
188                                                         NULL,
189                                                         G_PARAM_READWRITE));
190   g_object_class_install_property (gobject_class,
191                                    PROP_FONT,
192                                    g_param_spec_boxed ("font",
193                                                        _("Font"),
194                                                        _("The GdkFont that is currently selected."),
195                                                        GDK_TYPE_FONT,
196                                                        G_PARAM_READABLE));
197   g_object_class_install_property (gobject_class,
198                                    PROP_PREVIEW_TEXT,
199                                    g_param_spec_string ("preview_text",
200                                                         _("Preview text"),
201                                                         _("The text to display in order to demonstrate the selected font."),
202                                                         PREVIEW_TEXT,
203                                                         G_PARAM_READWRITE));
204   gobject_class->finalize = gtk_font_selection_finalize;
205 }
206
207 static void 
208 gtk_font_selection_set_property (GObject         *object,
209                                  guint            prop_id,
210                                  const GValue    *value,
211                                  GParamSpec      *pspec)
212 {
213   GtkFontSelection *fontsel;
214
215   fontsel = GTK_FONT_SELECTION (object);
216
217   switch (prop_id)
218     {
219     case PROP_FONT_NAME:
220       gtk_font_selection_set_font_name (fontsel, g_value_get_string (value));
221       break;
222     case PROP_PREVIEW_TEXT:
223       gtk_font_selection_set_preview_text (fontsel, g_value_get_string (value));
224       break;
225     default:
226       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
227       break;
228     }
229 }
230
231 static void gtk_font_selection_get_property (GObject         *object,
232                                              guint            prop_id,
233                                              GValue          *value,
234                                              GParamSpec      *pspec)
235 {
236   GtkFontSelection *fontsel;
237
238   fontsel = GTK_FONT_SELECTION (object);
239
240   switch (prop_id)
241     {
242     case PROP_FONT_NAME:
243       g_value_set_string (value, gtk_font_selection_get_font_name (fontsel));
244       break;
245     case PROP_FONT:
246       g_value_set_object (value, G_OBJECT (gtk_font_selection_get_font (fontsel)));
247       break;
248     case PROP_PREVIEW_TEXT:
249       g_value_set_string (value, gtk_font_selection_get_preview_text (fontsel));
250       break;
251     default:
252       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253       break;
254     }
255 }
256
257
258 static void
259 gtk_font_selection_init(GtkFontSelection *fontsel)
260 {
261   GtkWidget *scrolled_win;
262   GtkWidget *text_frame;
263   GtkWidget *text_box;
264   GtkWidget *table, *label;
265
266   fontsel->font_desc = pango_font_description_from_string ("sans 12");
267   
268   /* Create the table of font, style & size. */
269   table = gtk_table_new (3, 3, FALSE);
270   gtk_widget_show (table);
271   gtk_table_set_col_spacings(GTK_TABLE(table), 8);
272   gtk_box_pack_start (GTK_BOX (fontsel), table, TRUE, TRUE, 0);
273
274   fontsel->font_entry = gtk_entry_new ();
275   gtk_entry_set_editable(GTK_ENTRY(fontsel->font_entry), FALSE);
276   gtk_widget_set_usize (fontsel->font_entry, 20, -1);
277   gtk_widget_show (fontsel->font_entry);
278   gtk_table_attach (GTK_TABLE (table), fontsel->font_entry, 0, 1, 1, 2,
279                     GTK_FILL, 0, 0, 0);
280   
281   fontsel->font_style_entry = gtk_entry_new ();
282   gtk_entry_set_editable (GTK_ENTRY(fontsel->font_style_entry), FALSE);
283   gtk_widget_set_usize (fontsel->font_style_entry, 20, -1);
284   gtk_widget_show (fontsel->font_style_entry);
285   gtk_table_attach (GTK_TABLE (table), fontsel->font_style_entry, 1, 2, 1, 2,
286                     GTK_FILL, 0, 0, 0);
287   
288   fontsel->size_entry = gtk_entry_new ();
289   gtk_widget_set_usize (fontsel->size_entry, 20, -1);
290   gtk_widget_show (fontsel->size_entry);
291   gtk_table_attach (GTK_TABLE (table), fontsel->size_entry, 2, 3, 1, 2,
292                     GTK_FILL, 0, 0, 0);
293   gtk_signal_connect (GTK_OBJECT (fontsel->size_entry), "activate",
294                       (GtkSignalFunc) gtk_font_selection_size_activate,
295                       fontsel);
296   
297   fontsel->font_label = gtk_label_new_with_mnemonic (_("_Family:"));
298   gtk_label_set_mnemonic_widget (GTK_LABEL (fontsel->font_label),
299                                  fontsel->font_entry);
300   gtk_misc_set_alignment (GTK_MISC (fontsel->font_label), 0.0, 0.5);
301   gtk_widget_show (fontsel->font_label);
302   gtk_table_attach (GTK_TABLE (table), fontsel->font_label, 0, 1, 0, 1,
303                     GTK_FILL, 0, 0, 0);  
304   label = gtk_label_new_with_mnemonic (_("_Style:"));
305   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
306                                  fontsel->font_style_entry);
307   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
308   gtk_widget_show (label);
309   gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
310                     GTK_FILL, 0, 0, 0);
311   label = gtk_label_new_with_mnemonic (_("Si_ze:"));
312   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
313                                  fontsel->size_entry);
314   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
315   gtk_widget_show (label);
316   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
317                     GTK_FILL, 0, 0, 0);
318   
319   
320   /* Create the clists  */
321   fontsel->font_clist = gtk_clist_new (1);
322   gtk_clist_column_titles_hide (GTK_CLIST (fontsel->font_clist));
323   gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_clist), 0, TRUE);
324   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
325   gtk_widget_set_usize (scrolled_win, FONT_LIST_WIDTH, FONT_LIST_HEIGHT);
326   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->font_clist);
327   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
328                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
329   gtk_widget_show(fontsel->font_clist);
330   gtk_widget_show(scrolled_win);
331
332   gtk_table_attach (GTK_TABLE (table), scrolled_win, 0, 1, 2, 3,
333                     GTK_EXPAND | GTK_FILL,
334                     GTK_EXPAND | GTK_FILL, 0, 0);
335   
336   fontsel->font_style_clist = gtk_clist_new (1);
337   gtk_clist_column_titles_hide (GTK_CLIST (fontsel->font_style_clist));
338   gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_style_clist),
339                                     0, TRUE);
340   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
341   gtk_widget_set_usize (scrolled_win, FONT_STYLE_LIST_WIDTH, -1);
342   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->font_style_clist);
343   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
344                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
345   gtk_widget_show(fontsel->font_style_clist);
346   gtk_widget_show(scrolled_win);
347   gtk_table_attach (GTK_TABLE (table), scrolled_win, 1, 2, 2, 3,
348                     GTK_EXPAND | GTK_FILL,
349                     GTK_EXPAND | GTK_FILL, 0, 0);
350   
351   fontsel->size_clist = gtk_clist_new (1);
352   gtk_clist_column_titles_hide (GTK_CLIST(fontsel->size_clist));
353   gtk_clist_set_column_width (GTK_CLIST(fontsel->size_clist), 0, 20);
354   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
355   gtk_widget_set_usize (scrolled_win, FONT_SIZE_LIST_WIDTH, -1);
356   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->size_clist);
357   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
358                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
359   gtk_widget_show (fontsel->size_clist);
360   gtk_widget_show (scrolled_win);
361   gtk_table_attach (GTK_TABLE (table), scrolled_win, 2, 3, 2, 3,
362                     GTK_FILL, GTK_FILL, 0, 0);
363   
364   /* Insert the fonts. If there exist fonts with the same family but
365      different foundries, then the foundry name is appended in brackets. */
366   gtk_font_selection_show_available_fonts(fontsel);
367   
368   gtk_signal_connect (GTK_OBJECT (fontsel->font_clist), "select_row",
369                       GTK_SIGNAL_FUNC(gtk_font_selection_select_font),
370                       fontsel);
371   GTK_WIDGET_SET_FLAGS (fontsel->font_clist, GTK_CAN_FOCUS);
372
373   gtk_signal_connect_after (GTK_OBJECT (fontsel->font_clist), "expose_event",
374                             GTK_SIGNAL_FUNC(gtk_font_selection_expose_list),
375                             fontsel);
376   
377   gtk_font_selection_show_available_styles (fontsel);
378   
379   gtk_signal_connect (GTK_OBJECT (fontsel->font_style_clist), "select_row",
380                       GTK_SIGNAL_FUNC(gtk_font_selection_select_style),
381                       fontsel);
382   GTK_WIDGET_SET_FLAGS (fontsel->font_style_clist, GTK_CAN_FOCUS);
383
384   gtk_font_selection_show_available_sizes (fontsel);
385   
386   gtk_signal_connect (GTK_OBJECT (fontsel->size_clist), "select_row",
387                       GTK_SIGNAL_FUNC(gtk_font_selection_select_size),
388                       fontsel);
389   GTK_WIDGET_SET_FLAGS (fontsel->size_clist, GTK_CAN_FOCUS);
390   
391   /* create the text entry widget */
392   text_frame = gtk_frame_new (_("Preview:"));
393   gtk_widget_show (text_frame);
394   gtk_frame_set_shadow_type(GTK_FRAME(text_frame), GTK_SHADOW_ETCHED_IN);
395   gtk_box_pack_start (GTK_BOX (fontsel), text_frame,
396                       FALSE, TRUE, 0);
397   
398   /* This is just used to get a 4-pixel space around the preview entry. */
399   text_box = gtk_hbox_new (FALSE, 0);
400   gtk_widget_show (text_box);
401   gtk_container_add (GTK_CONTAINER (text_frame), text_box);
402   gtk_container_set_border_width (GTK_CONTAINER (text_box), 4);
403   
404   fontsel->preview_entry = gtk_entry_new ();
405   gtk_widget_show (fontsel->preview_entry);
406   gtk_signal_connect (GTK_OBJECT (fontsel->preview_entry), "changed",
407                       (GtkSignalFunc) gtk_font_selection_preview_changed,
408                       fontsel);
409   gtk_widget_set_usize (fontsel->preview_entry, -1, INITIAL_PREVIEW_HEIGHT);
410   gtk_box_pack_start (GTK_BOX (text_box), fontsel->preview_entry,
411                       TRUE, TRUE, 0);
412
413   gtk_font_selection_update_preview (fontsel);
414 }
415
416 GtkWidget *
417 gtk_font_selection_new()
418 {
419   GtkFontSelection *fontsel;
420   
421   fontsel = gtk_type_new (GTK_TYPE_FONT_SELECTION);
422   
423   return GTK_WIDGET (fontsel);
424 }
425
426 static void
427 gtk_font_selection_finalize (GObject *object)
428 {
429   GtkFontSelection *fontsel;
430   
431   g_return_if_fail (object != NULL);
432   g_return_if_fail (GTK_IS_FONT_SELECTION (object));
433   
434   fontsel = GTK_FONT_SELECTION (object);
435
436   pango_font_description_free (fontsel->font_desc);
437
438   if (fontsel->font)
439     gdk_font_unref (fontsel->font);
440   
441   if (G_OBJECT_CLASS (font_selection_parent_class)->finalize)
442     (* G_OBJECT_CLASS (font_selection_parent_class)->finalize) (object);
443 }
444
445 static void
446 gtk_font_selection_preview_changed (GtkWidget        *entry,
447                                     GtkFontSelection *fontsel)
448 {
449   g_object_notify (G_OBJECT (fontsel), "preview_text");
450 }
451
452 /* This is called when the clist is exposed. Here we scroll to the current
453    font if necessary. */
454 static void
455 gtk_font_selection_expose_list (GtkWidget               *widget,
456                                 GdkEventExpose          *event,
457                                 gpointer                 data)
458 {
459   GtkFontSelection *fontsel;
460   GList *selection;
461   gint index;
462   
463 #ifdef FONTSEL_DEBUG
464   g_message("In expose_list\n");
465 #endif
466   fontsel = GTK_FONT_SELECTION(data);
467   
468   /* Try to scroll the font family clist to the selected item */
469   selection = GTK_CLIST(fontsel->font_clist)->selection;
470   if (selection)
471     {
472       index = GPOINTER_TO_INT (selection->data);
473       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_clist), index)
474           != GTK_VISIBILITY_FULL)
475         gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
476     }
477       
478   /* Try to scroll the font style clist to the selected item */
479   selection = GTK_CLIST(fontsel->font_style_clist)->selection;
480   if (selection)
481     {
482       index = GPOINTER_TO_INT (selection->data);
483       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist), index)
484           != GTK_VISIBILITY_FULL)
485         gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), index, -1,
486                          0.5, 0);
487     }
488       
489   /* Try to scroll the font size clist to the selected item */
490   selection = GTK_CLIST(fontsel->size_clist)->selection;
491   if (selection)
492     {
493       index = GPOINTER_TO_INT (selection->data);
494       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->size_clist), index)
495           != GTK_VISIBILITY_FULL)
496       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), index, -1, 0.5, 0);
497     }
498 }
499
500 /* This is called when a family is selected in the list. */
501 static void
502 gtk_font_selection_select_font (GtkWidget      *w,
503                                 gint            row,
504                                 gint            column,
505                                 GdkEventButton *bevent,
506                                 gpointer        data)
507 {
508   GtkFontSelection *fontsel;
509   gchar *family_name;
510   gint index;
511   
512   fontsel = GTK_FONT_SELECTION (data);
513
514   if (GTK_CLIST (fontsel->font_clist)->selection)
515     {
516       index = GPOINTER_TO_INT (GTK_CLIST (fontsel->font_clist)->selection->data);
517
518       if (gtk_clist_get_text (GTK_CLIST (fontsel->font_clist), index, 0, &family_name) &&
519           strcasecmp (fontsel->font_desc->family_name, family_name) != 0)
520         {
521           g_free (fontsel->font_desc->family_name);
522           fontsel->font_desc->family_name  = g_strdup (family_name);
523
524           gtk_entry_set_text (GTK_ENTRY (fontsel->font_entry), family_name);
525           
526           gtk_font_selection_show_available_styles (fontsel);
527           gtk_font_selection_select_best_style (fontsel, TRUE);
528         }
529     }
530 }
531
532 static int
533 cmp_strings (const void *a, const void *b)
534 {
535   return strcasecmp (*(const char **)a, *(const char **)b);
536 }
537
538 static void
539 gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel)
540 {
541   gchar **families;
542   int n_families, i;
543
544   pango_context_list_families (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)),
545                                &families, &n_families);
546   qsort (families, n_families, sizeof(char *), cmp_strings);
547
548   gtk_clist_freeze (GTK_CLIST (fontsel->font_clist));
549   gtk_clist_clear (GTK_CLIST (fontsel->font_clist));
550
551   for (i=0; i<n_families; i++)
552     {
553       gtk_clist_append (GTK_CLIST (fontsel->font_clist), &families[i]);
554
555       if (!strcasecmp (families[i], fontsel->font_desc->family_name))
556         {
557           gtk_clist_select_row (GTK_CLIST(fontsel->font_clist), i, 0);
558           gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), families[i]);
559         }
560     }
561   
562   gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
563
564   pango_font_map_free_families (families, n_families);
565 }
566
567 static int
568 compare_font_descriptions (const PangoFontDescription *a, const PangoFontDescription *b)
569 {
570   int val = strcasecmp (a->family_name, b->family_name);
571   if (val != 0)
572     return val;
573
574   if (a->weight != b->weight)
575     return a->weight - b->weight;
576
577   if (a->style != b->style)
578     return a->style - b->style;
579   
580   if (a->stretch != b->stretch)
581     return a->stretch - b->stretch;
582
583   if (a->variant != b->variant)
584     return a->variant - b->variant;
585
586   return 0;
587 }
588
589 static int
590 font_description_sort_func (const void *a, const void *b)
591 {
592   return compare_font_descriptions (*(PangoFontDescription **)a, *(PangoFontDescription **)b);
593 }
594
595 /* This fills the font style clist with all the possible style combinations
596    for the current font family. */
597 static void
598 gtk_font_selection_show_available_styles (GtkFontSelection *fontsel)
599 {
600   PangoFontDescription **descs;
601   int n_descs, i;
602   gint match_row = -1;
603   gchar *str;
604   
605   pango_context_list_fonts (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)),
606                             fontsel->font_desc->family_name, &descs, &n_descs);
607   qsort (descs, n_descs, sizeof(PangoFontDescription *), font_description_sort_func);
608
609   gtk_clist_freeze (GTK_CLIST (fontsel->font_style_clist));
610   gtk_clist_clear (GTK_CLIST (fontsel->font_style_clist));
611
612   for (i=0; i<n_descs; i++)
613     {
614       PangoFontDescription tmp_desc;
615
616       tmp_desc = *descs[i];
617       tmp_desc.family_name = NULL;
618       tmp_desc.size = 0;
619
620       str = pango_font_description_to_string (&tmp_desc);
621       gtk_clist_append (GTK_CLIST (fontsel->font_style_clist), &str);
622
623       if (descs[i]->weight == fontsel->font_desc->weight &&
624           descs[i]->style == fontsel->font_desc->style &&
625           descs[i]->stretch == fontsel->font_desc->stretch &&
626           descs[i]->variant == fontsel->font_desc->variant)
627         match_row = i;
628       
629       g_free (str);
630     }
631
632   gtk_clist_select_row (GTK_CLIST (fontsel->font_style_clist), match_row, 0);
633   if (match_row >= 0)
634     {
635       gtk_clist_get_text (GTK_CLIST (fontsel->font_style_clist), match_row, 0, &str);
636       gtk_entry_set_text (GTK_ENTRY (fontsel->font_style_entry), str);
637     }
638   
639   gtk_clist_thaw (GTK_CLIST(fontsel->font_style_clist));
640
641   pango_font_descriptions_free (descs, n_descs);
642 }
643
644
645 /* This selects a style when the user selects a font. It just uses the first
646    available style at present. I was thinking of trying to maintain the
647    selected style, e.g. bold italic, when the user selects different fonts.
648    However, the interface is so easy to use now I'm not sure it's worth it.
649    Note: This will load a font. */
650 static void
651 gtk_font_selection_select_best_style(GtkFontSelection *fontsel,
652                                      gboolean          use_first)
653 {
654   gint best_row = 0;
655   
656   gtk_clist_select_row(GTK_CLIST(fontsel->font_style_clist), best_row, 0);
657   if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist), best_row)
658       != GTK_VISIBILITY_FULL)
659     gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), best_row, -1,
660                      0.5, 0);
661   gtk_font_selection_show_available_sizes (fontsel);
662   gtk_font_selection_select_best_size (fontsel);
663 }
664
665
666 /* This is called when a style is selected in the list. */
667 static void
668 gtk_font_selection_select_style (GtkWidget      *w,
669                                  gint           row,
670                                  gint           column,
671                                  GdkEventButton *bevent,
672                                  gpointer        data)
673 {
674   GtkFontSelection *fontsel = GTK_FONT_SELECTION (data);
675   PangoFontDescription *tmp_desc;
676   gchar *text;
677   gint index;
678   
679   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
680     gtk_widget_grab_focus (w);
681   
682   if (GTK_CLIST (fontsel->font_style_clist)->selection)
683     {
684       index = GPOINTER_TO_INT (GTK_CLIST (fontsel->font_style_clist)->selection->data);
685
686       if (gtk_clist_get_text (GTK_CLIST (fontsel->font_style_clist), index, 0, &text))
687         {
688           tmp_desc = pango_font_description_from_string (text);
689           
690           fontsel->font_desc->style = tmp_desc->style;
691           fontsel->font_desc->variant = tmp_desc->variant;
692           fontsel->font_desc->weight = tmp_desc->weight;
693           fontsel->font_desc->stretch = tmp_desc->stretch;
694           
695           pango_font_description_free (tmp_desc);
696         }
697     }
698
699   gtk_font_selection_show_available_sizes (fontsel);
700   gtk_font_selection_select_best_size (fontsel);
701 }
702
703 static void
704 gtk_font_selection_show_available_sizes (GtkFontSelection *fontsel)
705 {
706   gint i;
707   gchar buffer[128];
708   gchar *size;
709
710   /* Insert the standard font sizes */
711   gtk_clist_freeze (GTK_CLIST (fontsel->size_clist));
712   gtk_clist_clear (GTK_CLIST (fontsel->size_clist));
713
714   for (i = 0; i < G_N_ELEMENTS (font_sizes); i++)
715     {
716       sprintf(buffer, "%i", font_sizes[i]);
717       size = buffer;
718       gtk_clist_append (GTK_CLIST(fontsel->size_clist), &size);
719       if (font_sizes[i] * PANGO_SCALE == fontsel->font_desc->size)
720         gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), i, 0);
721     }
722   gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
723
724   sprintf (buffer, "%i", fontsel->font_desc->size / PANGO_SCALE);
725   gtk_entry_set_text (GTK_ENTRY(fontsel->size_entry), buffer);
726 }
727
728 static void
729 gtk_font_selection_select_best_size (GtkFontSelection *fontsel)
730 {
731   gtk_font_selection_load_font (fontsel);  
732 }
733
734 /* If the user hits return in the font size entry, we change to the new font
735    size. */
736 static void
737 gtk_font_selection_size_activate (GtkWidget   *w,
738                                   gpointer     data)
739 {
740   GtkFontSelection *fontsel;
741   gint new_size;
742   gchar *text;
743   
744   fontsel = GTK_FONT_SELECTION (data);
745
746   text = gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry));
747   new_size = atoi (text) * PANGO_SCALE;
748   
749   if (fontsel->font_desc->size != new_size)
750     {
751       fontsel->font_desc->size = new_size;
752       gtk_font_selection_load_font (fontsel);
753     }
754 }
755
756 /* This is called when a size is selected in the list. */
757 static void
758 gtk_font_selection_select_size (GtkWidget      *w,
759                                 gint            row,
760                                 gint            column,
761                                 GdkEventButton *bevent,
762                                 gpointer        data)
763 {
764   GtkFontSelection *fontsel;
765   gint new_size;
766   gchar *text;
767   
768   fontsel = GTK_FONT_SELECTION (data);
769   
770   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
771     gtk_widget_grab_focus (w);
772   
773   gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
774   new_size = atoi (text) * PANGO_SCALE;
775   
776   if (fontsel->font_desc->size != new_size)
777     {
778       /* If the size was selected by the user we set the selected_size. */
779       fontsel->font_desc->size = new_size;
780
781       gtk_font_selection_load_font (fontsel);
782     }
783 }
784
785 static void
786 gtk_font_selection_load_font (GtkFontSelection *fontsel)
787 {
788   if (fontsel->font)
789     gdk_font_unref (fontsel->font);
790   fontsel->font = NULL;
791
792   gtk_font_selection_update_preview (fontsel);
793 }
794
795 /* This sets the font in the preview entry to the selected font, and tries to
796    make sure that the preview entry is a reasonable size, i.e. so that the
797    text can be seen with a bit of space to spare. But it tries to avoid
798    resizing the entry every time the font changes.
799    This also used to shrink the preview if the font size was decreased, but
800    that made it awkward if the user wanted to resize the window themself. */
801 static void
802 gtk_font_selection_update_preview (GtkFontSelection *fontsel)
803 {
804   GtkRcStyle *rc_style;
805   gint new_height;
806   GtkRequisition old_requisition;
807   GtkWidget *preview_entry = fontsel->preview_entry;
808   gchar *text;
809
810   gtk_widget_get_child_requisition (preview_entry, &old_requisition);
811   
812   rc_style = gtk_rc_style_new ();
813   rc_style->font_desc = pango_font_description_copy (fontsel->font_desc);
814   gtk_widget_modify_style (preview_entry, rc_style);
815   gtk_rc_style_unref (rc_style);
816
817   gtk_widget_size_request (preview_entry, NULL);
818   
819   /* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
820   new_height = CLAMP (preview_entry->requisition.height, INITIAL_PREVIEW_HEIGHT, MAX_PREVIEW_HEIGHT);
821
822   if (new_height > old_requisition.height || new_height < old_requisition.height - 30)
823     gtk_widget_set_usize(preview_entry, -1, new_height);
824   
825   /* This sets the preview text, if it hasn't been set already. */
826   text = gtk_entry_get_text(GTK_ENTRY(preview_entry));
827   if (strlen(text) == 0)
828     gtk_entry_set_text(GTK_ENTRY(preview_entry), PREVIEW_TEXT);
829   gtk_entry_set_position(GTK_ENTRY(preview_entry), 0);
830 }
831
832 /*****************************************************************************
833  * These functions are the main public interface for getting/setting the font.
834  *****************************************************************************/
835
836 GdkFont*
837 gtk_font_selection_get_font (GtkFontSelection *fontsel)
838 {
839   if (!fontsel->font)
840     fontsel->font = gdk_font_from_description (fontsel->font_desc);
841   
842   return fontsel->font;
843 }
844
845
846 gchar *
847 gtk_font_selection_get_font_name (GtkFontSelection *fontsel)
848 {
849   return pango_font_description_to_string (fontsel->font_desc);
850 }
851
852
853 /* This sets the current font, selecting the appropriate clist rows.
854    First we check the fontname is valid and try to find the font family
855    - i.e. the name in the main list. If we can't find that, then just return.
856    Next we try to set each of the properties according to the fontname.
857    Finally we select the font family & style in the clists. */
858 gboolean
859 gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
860                                   const gchar      *fontname)
861 {
862   PangoFontDescription *new_desc;
863   PangoFontDescription **descs;
864   int n_descs, i;
865   gboolean found = FALSE;
866
867   g_return_val_if_fail (fontsel != NULL, FALSE);
868   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), FALSE);
869   
870   new_desc = pango_font_description_from_string (fontname);
871
872   /* Check to make sure that this is in the list of allowed fonts */
873
874   pango_context_list_fonts (gtk_widget_get_pango_context (GTK_WIDGET (fontsel)),
875                             new_desc->family_name, &descs, &n_descs);
876
877   for (i=0; i<n_descs; i++)
878     {
879       if (descs[i]->weight == new_desc->weight &&
880           descs[i]->style == new_desc->style &&
881           descs[i]->stretch == new_desc->stretch &&
882           descs[i]->variant == new_desc->variant)
883         {
884           found = TRUE;
885           break;
886         }
887     }
888
889   pango_font_descriptions_free (descs, n_descs);
890
891   if (!found)
892     return FALSE;
893
894   pango_font_description_free (fontsel->font_desc);
895   fontsel->font_desc = new_desc;
896
897   g_object_notify (G_OBJECT (fontsel), "font_name");
898   g_object_notify (G_OBJECT (fontsel), "font");
899   return TRUE;
900 }
901
902
903 /* This returns the text in the preview entry. You should copy the returned
904    text if you need it. */
905 gchar*
906 gtk_font_selection_get_preview_text  (GtkFontSelection *fontsel)
907 {
908   return gtk_entry_get_text (GTK_ENTRY (fontsel->preview_entry));
909 }
910
911
912 /* This sets the text in the preview entry. */
913 void
914 gtk_font_selection_set_preview_text  (GtkFontSelection *fontsel,
915                                       const gchar         *text)
916 {
917   gtk_entry_set_text (GTK_ENTRY (fontsel->preview_entry), text);
918 }
919
920 /*****************************************************************************
921  * GtkFontSelectionDialog
922  *****************************************************************************/
923
924 GtkType
925 gtk_font_selection_dialog_get_type (void)
926 {
927   static guint font_selection_dialog_type = 0;
928   
929   if (!font_selection_dialog_type)
930     {
931       GtkTypeInfo fontsel_diag_info =
932       {
933         "GtkFontSelectionDialog",
934         sizeof (GtkFontSelectionDialog),
935         sizeof (GtkFontSelectionDialogClass),
936         (GtkClassInitFunc) gtk_font_selection_dialog_class_init,
937         (GtkObjectInitFunc) gtk_font_selection_dialog_init,
938         /* reserved_1 */ NULL,
939         /* reserved_2 */ NULL,
940         (GtkClassInitFunc) NULL,
941       };
942       
943       font_selection_dialog_type = gtk_type_unique (GTK_TYPE_DIALOG,
944                                                     &fontsel_diag_info);
945     }
946   
947   return font_selection_dialog_type;
948 }
949
950 static void
951 gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass)
952 {
953   GtkObjectClass *object_class;
954   
955   object_class = (GtkObjectClass*) klass;
956   
957   font_selection_dialog_parent_class = gtk_type_class (GTK_TYPE_DIALOG);
958 }
959
960 static void
961 gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag)
962 {
963   GtkDialog *dialog;
964
965   dialog = GTK_DIALOG (fontseldiag);
966   
967   fontseldiag->dialog_width = -1;
968   fontseldiag->auto_resize = TRUE;
969   
970   gtk_widget_set_events(GTK_WIDGET(fontseldiag), GDK_STRUCTURE_MASK);
971   gtk_signal_connect (GTK_OBJECT (fontseldiag), "configure_event",
972                       (GtkSignalFunc) gtk_font_selection_dialog_on_configure,
973                       fontseldiag);
974   
975   gtk_container_set_border_width (GTK_CONTAINER (fontseldiag), 4);
976   gtk_window_set_policy(GTK_WINDOW(fontseldiag), FALSE, TRUE, TRUE);
977   
978   fontseldiag->main_vbox = dialog->vbox;
979   
980   fontseldiag->fontsel = gtk_font_selection_new();
981   gtk_container_set_border_width (GTK_CONTAINER (fontseldiag->fontsel), 4);
982   gtk_widget_show (fontseldiag->fontsel);
983   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
984                       fontseldiag->fontsel, TRUE, TRUE, 0);
985   
986   /* Create the action area */
987   fontseldiag->action_area = dialog->action_area;
988   
989   fontseldiag->ok_button = gtk_dialog_add_button (dialog,
990                                                   GTK_STOCK_BUTTON_OK,
991                                                   GTK_RESPONSE_OK);
992   gtk_widget_grab_default (fontseldiag->ok_button);
993   
994   fontseldiag->apply_button = gtk_dialog_add_button (dialog,
995                                                      GTK_STOCK_BUTTON_APPLY,
996                                                      GTK_RESPONSE_APPLY);
997   gtk_widget_hide (fontseldiag->apply_button);
998
999   
1000   fontseldiag->cancel_button = gtk_dialog_add_button (dialog,
1001                                                       GTK_STOCK_BUTTON_CANCEL,
1002                                                       GTK_RESPONSE_CANCEL);
1003
1004   gtk_window_set_title (GTK_WINDOW (fontseldiag),
1005                         _("Font Selection"));
1006
1007 }
1008
1009 GtkWidget*
1010 gtk_font_selection_dialog_new   (const gchar      *title)
1011 {
1012   GtkFontSelectionDialog *fontseldiag;
1013   
1014   fontseldiag = gtk_type_new (GTK_TYPE_FONT_SELECTION_DIALOG);
1015
1016   if (title)
1017     gtk_window_set_title (GTK_WINDOW (fontseldiag), title);
1018   
1019   return GTK_WIDGET (fontseldiag);
1020 }
1021
1022 gchar*
1023 gtk_font_selection_dialog_get_font_name (GtkFontSelectionDialog *fsd)
1024 {
1025   return gtk_font_selection_get_font_name(GTK_FONT_SELECTION(fsd->fontsel));
1026 }
1027
1028 GdkFont*
1029 gtk_font_selection_dialog_get_font      (GtkFontSelectionDialog *fsd)
1030 {
1031   return gtk_font_selection_get_font(GTK_FONT_SELECTION(fsd->fontsel));
1032 }
1033
1034 gboolean
1035 gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
1036                                          const gchar      *fontname)
1037 {
1038   return gtk_font_selection_set_font_name(GTK_FONT_SELECTION(fsd->fontsel),
1039                                           fontname);
1040 }
1041
1042 gchar*
1043 gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd)
1044 {
1045   return gtk_font_selection_get_preview_text(GTK_FONT_SELECTION(fsd->fontsel));
1046 }
1047
1048 void
1049 gtk_font_selection_dialog_set_preview_text (GtkFontSelectionDialog *fsd,
1050                                             const gchar   *text)
1051 {
1052   gtk_font_selection_set_preview_text(GTK_FONT_SELECTION(fsd->fontsel), text);
1053 }
1054
1055
1056 /* This turns auto-shrink off if the user resizes the width of the dialog.
1057    It also turns it back on again if the user resizes it back to its normal
1058    width. */
1059 static gint
1060 gtk_font_selection_dialog_on_configure (GtkWidget         *widget,
1061                                         GdkEventConfigure *event,
1062                                         GtkFontSelectionDialog *fsd)
1063 {
1064   /* This sets the initial width. */
1065   if (fsd->dialog_width == -1)
1066     fsd->dialog_width = event->width;
1067   else if (fsd->auto_resize && fsd->dialog_width != event->width)
1068     {
1069       fsd->auto_resize = FALSE;
1070       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, FALSE);
1071     }
1072   else if (!fsd->auto_resize && fsd->dialog_width == event->width)
1073     {
1074       fsd->auto_resize = TRUE;
1075       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, TRUE);
1076     }
1077   
1078   return FALSE;
1079 }