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