]> Pileus Git - ~andy/gtk/blob - gtk/gtkfontsel.c
Update gcc build instructions. Mention gettext is GPL.
[~andy/gtk] / gtk / gtkfontsel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkFontSelection widget for Gtk+, by Damon Chaplin, May 1998.
5  * Based on the GnomeFontSelector widget, by Elliot Lee, but major changes.
6  * The GnomeFontSelector was derived from app/text_tool.c in the GIMP.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /*
25  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
26  * file for a list of people on the GTK+ Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
29  */
30
31 /*
32  * Limits:
33  *
34  *  Fontnames    - A maximum of MAX_FONTS (32767) fontnames will be retrieved
35  *                 from X Windows with XListFonts(). Any more are ignored.
36  *                 I think this limit may have been set because of a limit in
37  *                 GtkList. It could possibly be increased since we are using
38  *                 GtkClists now, but I'd be surprised if it was reached.
39  *  Field length - XLFD_MAX_FIELD_LEN is the maximum length that any field of a
40  *                 fontname can be for it to be considered valid. Others are
41  *                 ignored.
42  *  Properties   - Maximum of 65535 choices for each font property - guint16's
43  *                 are used as indices, e.g. in the FontInfo struct.
44  *  Combinations - Maximum of 65535 combinations of properties for each font
45  *                 family - a guint16 is used in the FontInfo struct.
46  *  Font size    - Minimum font size of 2 pixels/points, since trying to load
47  *                 some fonts with a size of 1 can cause X to hang.
48  *                 (e.g. the Misc Fixed fonts).
49  */
50
51 /*
52  * Possible Improvements:
53  *
54  *  Font Styles  - could sort the styles into a reasonable order - regular
55  *                 first, then bold, bold italic etc.
56  *
57  *  I18N         - the default preview text is not useful for international
58  *                 fonts. Maybe the first few characters of the font could be
59  *                 displayed instead.
60  *               - fontsets? should these be handled by the font dialog?
61  */
62
63 /*
64  * Debugging: compile with -DFONTSEL_DEBUG for lots of debugging output.
65  */
66
67 #include <stdlib.h>
68 #include <stdio.h>
69 #include <string.h>
70 #include <ctype.h>
71
72 #include "gdk/gdk.h"
73 /* Protect against the CHARSET struct in Win32 */
74 #if GDK_WINDOWING == GDK_WINDOWING_WIN32
75 # define CHARSET CHARSETstruct
76 #endif
77 #include "gdkx.h"
78 #if GDK_WINDOWING == GDK_WINDOWING_WIN32
79 # undef CHARSET
80 #endif
81 #include "gdk/gdkkeysyms.h"
82
83 #include "gtkbutton.h"
84 #include "gtkcheckbutton.h"
85 #include "gtkclist.h"
86 #include "gtkentry.h"
87 #include "gtkfontsel.h"
88 #include "gtkframe.h"
89 #include "gtkhbbox.h"
90 #include "gtkhbox.h"
91 #include "gtklabel.h"
92 #include "gtknotebook.h"
93 #include "gtkradiobutton.h"
94 #include "gtksignal.h"
95 #include "gtktable.h"
96 #include "gtkvbox.h"
97 #include "gtkscrolledwindow.h"
98 #include "gtkintl.h"
99
100 /* The maximum number of fontnames requested with XListFonts(). */
101 #define MAX_FONTS 32767
102
103 /* This is the largest field length we will accept. If a fontname has a field
104    larger than this we will skip it. */
105 #define XLFD_MAX_FIELD_LEN 64
106
107 /* These are what we use as the standard font sizes, for the size clist.
108    Note that when using points we still show these integer point values but
109    we work internally in decipoints (and decipoint values can be typed in). */
110 static const guint16 font_sizes[] = {
111   8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28,
112   32, 36, 40, 48, 56, 64, 72
113 };
114
115 /* Initial font metric & size (Remember point sizes are in decipoints).
116    The font size should match one of those in the font_sizes array. */
117 #define INITIAL_METRIC            GTK_FONT_METRIC_POINTS
118 #define INITIAL_FONT_SIZE         140
119
120 /* This is the default text shown in the preview entry, though the user
121    can set it. Remember that some fonts only have capital letters. */
122 #define PREVIEW_TEXT "abcdefghijk ABCDEFGHIJK"
123
124 /* This is the initial and maximum height of the preview entry (it expands
125    when large font sizes are selected). Initial height is also the minimum. */
126 #define INITIAL_PREVIEW_HEIGHT 44
127 #define MAX_PREVIEW_HEIGHT 300
128
129 /* These are the sizes of the font, style & size clists. */
130 #define FONT_LIST_HEIGHT        136
131 #define FONT_LIST_WIDTH         190
132 #define FONT_STYLE_LIST_WIDTH   170
133 #define FONT_SIZE_LIST_WIDTH    60
134
135 /* This is the number of fields in an X Logical Font Description font name.
136    Note that we count the registry & encoding as 1. */
137 #define GTK_XLFD_NUM_FIELDS 13
138
139 typedef struct _GtkFontSelInfo GtkFontSelInfo;
140 typedef struct _FontInfo FontInfo;
141 typedef struct _FontStyle FontStyle;
142
143 /* This struct represents one family of fonts (with one foundry), e.g. adobe
144    courier or sony fixed. It stores the family name, the index of the foundry
145    name, and the index of and number of available styles. */
146 struct _FontInfo
147 {
148   gchar   *family;
149   guint16  foundry;
150   gint     style_index;
151   guint16  nstyles;
152 };
153
154 /* This represents one style, as displayed in the Font Style clist. It can
155    have a number of available pixel sizes and point sizes. The indexes point
156    into the two big fontsel_info->pixel_sizes & fontsel_info->point_sizes
157    arrays. The displayed flag is used when displaying styles to remember which
158    styles have already been displayed. Note that it is combined with the
159    GtkFontType in the flags field. */
160 #define  GTK_FONT_DISPLAYED     (1 << 7)
161 struct _FontStyle
162 {
163   guint16  properties[GTK_NUM_STYLE_PROPERTIES];
164   gint     pixel_sizes_index;
165   guint16  npixel_sizes;
166   gint     point_sizes_index;
167   guint16  npoint_sizes;
168   guint8   flags;
169 };
170
171 struct _GtkFontSelInfo {
172   
173   /* This is a table with each FontInfo representing one font family+foundry */
174   FontInfo *font_info;
175   gint nfonts;
176   
177   /* This stores all the valid combinations of properties for every family.
178      Each FontInfo holds an index into its own space in this one big array. */
179   FontStyle *font_styles;
180   gint nstyles;
181   
182   /* This stores all the font sizes available for every style.
183      Each style holds an index into these arrays. */
184   guint16 *pixel_sizes;
185   guint16 *point_sizes;
186   
187   /* These are the arrays of strings of all possible weights, slants, 
188      set widths, spacings, charsets & foundries, and the amount of space
189      allocated for each array. */
190   gchar **properties[GTK_NUM_FONT_PROPERTIES];
191   guint16 nproperties[GTK_NUM_FONT_PROPERTIES];
192   guint16 space_allocated[GTK_NUM_FONT_PROPERTIES];
193 };
194
195 /* These are the field numbers in the X Logical Font Description fontnames,
196    e.g. -adobe-courier-bold-o-normal--25-180-100-100-m-150-iso8859-1 */
197 typedef enum
198 {
199   XLFD_FOUNDRY          = 0,
200   XLFD_FAMILY           = 1,
201   XLFD_WEIGHT           = 2,
202   XLFD_SLANT            = 3,
203   XLFD_SET_WIDTH        = 4,
204   XLFD_ADD_STYLE        = 5,
205   XLFD_PIXELS           = 6,
206   XLFD_POINTS           = 7,
207   XLFD_RESOLUTION_X     = 8,
208   XLFD_RESOLUTION_Y     = 9,
209   XLFD_SPACING          = 10,
210   XLFD_AVERAGE_WIDTH    = 11,
211   XLFD_CHARSET          = 12
212 } FontField;
213
214 /* These are the names of the fields, used on the info & filter page. */
215 static const gchar* xlfd_field_names[GTK_XLFD_NUM_FIELDS] = {
216   N_("Foundry:"),
217   N_("Family:"),
218   N_("Weight:"),
219   N_("Slant:"),
220   N_("Set Width:"),
221   N_("Add Style:"),
222   N_("Pixel Size:"),
223   N_("Point Size:"),
224   N_("Resolution X:"),
225   N_("Resolution Y:"),
226   N_("Spacing:"),
227   N_("Average Width:"),
228   N_("Charset:"),
229 };
230
231 /* These are the array indices of the font properties used in several arrays,
232    and should match the xlfd_index array below. */
233 typedef enum
234 {
235   WEIGHT        = 0,
236   SLANT         = 1,
237   SET_WIDTH     = 2,
238   SPACING       = 3,
239   CHARSET       = 4,
240   FOUNDRY       = 5
241 } PropertyIndexType;
242
243 /* This is used to look up a field in a fontname given one of the above
244    property indices. */
245 static const FontField xlfd_index[GTK_NUM_FONT_PROPERTIES] = {
246   XLFD_WEIGHT,
247   XLFD_SLANT,
248   XLFD_SET_WIDTH,
249   XLFD_SPACING,
250   XLFD_CHARSET,
251   XLFD_FOUNDRY
252 };
253
254 /* These are the positions of the properties in the filter table - x, y. */
255 static const gint filter_positions[GTK_NUM_FONT_PROPERTIES][2] = {
256   { 1, 0 }, { 0, 2 }, { 1, 2 }, { 2, 2 }, { 2, 0 }, { 0, 0 }
257 };
258 static const gint filter_heights[GTK_NUM_FONT_PROPERTIES] = {
259   100, 70, 70, 40, 100, 100
260 };
261
262 /* This is returned by gtk_font_selection_filter_state to describe if a
263    property value is filtered. e.g. if 'bold' has been selected on the filter
264    page, then that will return 'FILTERED' and 'black' will be 'NOT_FILTERED'.
265    If none of the weight values are selected, they all return 'NOT_SET'. */
266 typedef enum
267 {
268   FILTERED,
269   NOT_FILTERED,
270   NOT_SET
271 } GtkFontPropertyFilterState;
272
273 static GtkFontSelInfo *fontsel_info = NULL;
274
275 /* The initial size and increment of each of the arrays of property values. */
276 #define PROPERTY_ARRAY_INCREMENT        16
277
278 static void    gtk_font_selection_class_init         (GtkFontSelectionClass *klass);
279 static void    gtk_font_selection_init               (GtkFontSelection *fontsel);
280 static void    gtk_font_selection_destroy            (GtkObject      *object);
281
282 /* These are all used for class initialization - loading in the fonts etc. */
283 static void    gtk_font_selection_get_fonts          (void);
284 static void    gtk_font_selection_insert_font        (GSList         *fontnames[],
285                                                       gint           *ntable,
286                                                       gchar          *fontname);
287 static gint    gtk_font_selection_insert_field       (gchar          *fontname,
288                                                       gint            prop);
289
290 /* These are the callbacks & related functions. */
291 static void    gtk_font_selection_select_font        (GtkWidget      *w,
292                                                       gint            row,
293                                                       gint            column,
294                                                       GdkEventButton *bevent,
295                                                       gpointer        data);
296 static gint    gtk_font_selection_on_clist_key_press (GtkWidget      *clist,
297                                                       GdkEventKey    *event,
298                                                       GtkFontSelection *fs);
299 static gboolean gtk_font_selection_select_next       (GtkFontSelection *fs,
300                                                       GtkWidget        *clist,
301                                                       gint              step);
302 static void    gtk_font_selection_show_available_styles
303 (GtkFontSelection *fs);
304 static void    gtk_font_selection_select_best_style  (GtkFontSelection *fs,
305                                                       gboolean         use_first);
306
307 static void    gtk_font_selection_select_style       (GtkWidget      *w,
308                                                       gint            row,
309                                                       gint            column,
310                                                       GdkEventButton *bevent,
311                                                       gpointer        data);
312 static void    gtk_font_selection_show_available_sizes
313 (GtkFontSelection *fs);
314 static gint    gtk_font_selection_size_key_press     (GtkWidget      *w,
315                                                       GdkEventKey    *event,
316                                                       gpointer        data);
317 static void    gtk_font_selection_select_best_size   (GtkFontSelection *fs);
318 static void    gtk_font_selection_select_size        (GtkWidget      *w,
319                                                       gint            row,
320                                                       gint            column,
321                                                       GdkEventButton *bevent,
322                                                       gpointer        data);
323
324 static void    gtk_font_selection_metric_callback    (GtkWidget      *w,
325                                                       gpointer        data);
326 static void    gtk_font_selection_expose_list        (GtkWidget      *w,
327                                                       GdkEventExpose *event,
328                                                       gpointer        data);
329 static void    gtk_font_selection_realize_list       (GtkWidget      *widget,
330                                                       gpointer        data);
331
332 static void    gtk_font_selection_switch_page        (GtkWidget      *w,
333                                                       GtkNotebookPage *page,
334                                                       gint             page_num,
335                                                       gpointer         data);
336 static void    gtk_font_selection_show_font_info     (GtkFontSelection *fs);
337
338 static void    gtk_font_selection_select_filter      (GtkWidget      *w,
339                                                       gint            row,
340                                                       gint            column,
341                                                       GdkEventButton *bevent,
342                                                       GtkFontSelection *fs);
343 static void    gtk_font_selection_unselect_filter    (GtkWidget      *w,
344                                                       gint            row,
345                                                       gint            column,
346                                                       GdkEventButton *bevent,
347                                                       GtkFontSelection *fs);
348 static void    gtk_font_selection_update_filter      (GtkFontSelection *fs);
349 static gboolean gtk_font_selection_style_visible     (GtkFontSelection *fs,
350                                                       FontInfo       *font,
351                                                       gint            style);
352 static void    gtk_font_selection_reset_filter       (GtkWidget      *w,
353                                                       GtkFontSelection *fs);
354 static void    gtk_font_selection_on_clear_filter    (GtkWidget      *w,
355                                                       GtkFontSelection *fs);
356 static void    gtk_font_selection_show_available_fonts
357                                                      (GtkFontSelection *fs);
358 static void    gtk_font_selection_clear_filter       (GtkFontSelection *fs);
359 static void    gtk_font_selection_update_filter_lists(GtkFontSelection *fs);
360 static GtkFontPropertyFilterState gtk_font_selection_filter_state
361                                                      (GtkFontSelection *fs,
362                                                       GtkFontFilterType filter_type,
363                                                       gint              property,
364                                                       gint              index);
365
366 /* Misc. utility functions. */
367 static gboolean gtk_font_selection_load_font         (GtkFontSelection *fs);
368 static void    gtk_font_selection_update_preview     (GtkFontSelection *fs);
369
370 static gint    gtk_font_selection_find_font          (GtkFontSelection *fs,
371                                                       gchar          *family,
372                                                       guint16         foundry);
373 static guint16 gtk_font_selection_field_to_index     (gchar         **table,
374                                                       gint            ntable,
375                                                       gchar          *field);
376
377 static gchar*  gtk_font_selection_expand_slant_code  (gchar          *slant);
378 static gchar*  gtk_font_selection_expand_spacing_code(gchar          *spacing);
379
380 /* Functions for handling X Logical Font Description fontnames. */
381 static gboolean gtk_font_selection_is_xlfd_font_name (const gchar    *fontname);
382 static char*   gtk_font_selection_get_xlfd_field     (const gchar    *fontname,
383                                                       FontField       field_num,
384                                                       gchar          *buffer);
385 static gchar * gtk_font_selection_create_xlfd        (gint            size,
386                                                       GtkFontMetricType metric,
387                                                       gchar          *foundry,
388                                                       gchar          *family,
389                                                       gchar          *weight,
390                                                       gchar          *slant,
391                                                       gchar          *set_width,
392                                                       gchar          *spacing,
393                                                       gchar          *charset);
394
395
396 /* FontSelectionDialog */
397 static void    gtk_font_selection_dialog_class_init  (GtkFontSelectionDialogClass *klass);
398 static void    gtk_font_selection_dialog_init        (GtkFontSelectionDialog *fontseldiag);
399
400 static gint    gtk_font_selection_dialog_on_configure(GtkWidget      *widget,
401                                                       GdkEventConfigure *event,
402                                                       GtkFontSelectionDialog *fsd);
403
404 #if GDK_WINDOWING == GDK_WINDOWING_WIN32
405 static char *logfont_to_xlfd (const LOGFONT *lfp,
406                               int size,
407                               int res,
408                               int avg_width);
409 #endif
410
411 static GtkWindowClass *font_selection_parent_class = NULL;
412 static GtkNotebookClass *font_selection_dialog_parent_class = NULL;
413
414 GtkType
415 gtk_font_selection_get_type()
416 {
417   static GtkType font_selection_type = 0;
418   
419   if(!font_selection_type)
420     {
421       static const GtkTypeInfo fontsel_type_info =
422       {
423         "GtkFontSelection",
424         sizeof (GtkFontSelection),
425         sizeof (GtkFontSelectionClass),
426         (GtkClassInitFunc) gtk_font_selection_class_init,
427         (GtkObjectInitFunc) gtk_font_selection_init,
428         /* reserved_1 */ NULL,
429         /* reserved_2 */ NULL,
430         (GtkClassInitFunc) NULL,
431       };
432       
433       font_selection_type = gtk_type_unique (GTK_TYPE_NOTEBOOK,
434                                              &fontsel_type_info);
435     }
436   
437   return font_selection_type;
438 }
439
440 static void
441 gtk_font_selection_class_init(GtkFontSelectionClass *klass)
442 {
443   GtkObjectClass *object_class;
444   
445   object_class = (GtkObjectClass *) klass;
446   
447   font_selection_parent_class = gtk_type_class (GTK_TYPE_NOTEBOOK);
448   
449   object_class->destroy = gtk_font_selection_destroy;
450   
451   gtk_font_selection_get_fonts ();
452 }
453
454 static void
455 gtk_font_selection_init(GtkFontSelection *fontsel)
456 {
457   GtkWidget *scrolled_win;
458   GtkWidget *text_frame;
459   GtkWidget *text_box, *frame;
460   GtkWidget *table, *label, *hbox, *hbox2, *clist, *button, *vbox, *alignment;
461   gint i, prop, row;
462   gchar *titles[] = { NULL, NULL, NULL };
463   gchar buffer[128];
464   gchar *size;
465   gint size_to_match;
466   gchar *row_text[3];
467   gchar *property, *text;
468   gboolean inserted;
469   
470   /* Number of internationalized titles here must match number
471      of NULL initializers above */
472   titles[0] = _("Font Property");
473   titles[1] = _("Requested Value");
474   titles[2] = _("Actual Value");
475
476   /* Initialize the GtkFontSelection struct. We do this here in case any
477      callbacks are triggered while creating the interface. */
478   fontsel->font = NULL;
479   fontsel->font_index = -1;
480   fontsel->style = -1;
481   fontsel->metric = INITIAL_METRIC;
482   fontsel->size = INITIAL_FONT_SIZE;
483   fontsel->selected_size = INITIAL_FONT_SIZE;
484
485   fontsel->filters[GTK_FONT_FILTER_BASE].font_type = GTK_FONT_ALL;
486   fontsel->filters[GTK_FONT_FILTER_USER].font_type = GTK_FONT_BITMAP
487     | GTK_FONT_SCALABLE;
488
489   
490   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
491     {
492       fontsel->filters[GTK_FONT_FILTER_BASE].property_filters[prop] = NULL;
493       fontsel->filters[GTK_FONT_FILTER_BASE].property_nfilters[prop] = 0;
494       fontsel->filters[GTK_FONT_FILTER_USER].property_filters[prop] = NULL;
495       fontsel->filters[GTK_FONT_FILTER_USER].property_nfilters[prop] = 0;
496     }
497   
498   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
499     fontsel->property_values[prop] = 0;
500   
501   /* Create the main notebook page. */
502   gtk_notebook_set_homogeneous_tabs (GTK_NOTEBOOK (fontsel), TRUE);
503   gtk_notebook_set_tab_hborder (GTK_NOTEBOOK (fontsel), 8);
504   fontsel->main_vbox = gtk_vbox_new (FALSE, 4);
505   gtk_widget_show (fontsel->main_vbox);
506   gtk_container_set_border_width (GTK_CONTAINER (fontsel->main_vbox), 6);
507   label = gtk_label_new(_("Font"));
508   gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
509                             fontsel->main_vbox, label);
510   
511   /* Create the table of font, style & size. */
512   table = gtk_table_new (3, 3, FALSE);
513   gtk_widget_show (table);
514   gtk_table_set_col_spacings(GTK_TABLE(table), 8);
515   gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), table, TRUE, TRUE, 0);
516   
517   fontsel->font_label = gtk_label_new(_("Font:"));
518   gtk_misc_set_alignment (GTK_MISC (fontsel->font_label), 0.0, 0.5);
519   gtk_widget_show (fontsel->font_label);
520   gtk_table_attach (GTK_TABLE (table), fontsel->font_label, 0, 1, 0, 1,
521                     GTK_FILL, 0, 0, 0);
522   label = gtk_label_new(_("Font Style:"));
523   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
524   gtk_widget_show (label);
525   gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
526                     GTK_FILL, 0, 0, 0);
527   label = gtk_label_new(_("Size:"));
528   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
529   gtk_widget_show (label);
530   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
531                     GTK_FILL, 0, 0, 0);
532   
533   fontsel->font_entry = gtk_entry_new();
534   gtk_entry_set_editable(GTK_ENTRY(fontsel->font_entry), FALSE);
535   gtk_widget_set_usize (fontsel->font_entry, 20, -1);
536   gtk_widget_show (fontsel->font_entry);
537   gtk_table_attach (GTK_TABLE (table), fontsel->font_entry, 0, 1, 1, 2,
538                     GTK_FILL, 0, 0, 0);
539   fontsel->font_style_entry = gtk_entry_new();
540   gtk_entry_set_editable(GTK_ENTRY(fontsel->font_style_entry), FALSE);
541   gtk_widget_set_usize (fontsel->font_style_entry, 20, -1);
542   gtk_widget_show (fontsel->font_style_entry);
543   gtk_table_attach (GTK_TABLE (table), fontsel->font_style_entry, 1, 2, 1, 2,
544                     GTK_FILL, 0, 0, 0);
545   fontsel->size_entry = gtk_entry_new();
546   gtk_widget_set_usize (fontsel->size_entry, 20, -1);
547   gtk_widget_show (fontsel->size_entry);
548   gtk_table_attach (GTK_TABLE (table), fontsel->size_entry, 2, 3, 1, 2,
549                     GTK_FILL, 0, 0, 0);
550   gtk_signal_connect (GTK_OBJECT (fontsel->size_entry), "key_press_event",
551                       (GtkSignalFunc) gtk_font_selection_size_key_press,
552                       fontsel);
553   
554   /* Create the clists  */
555   fontsel->font_clist = gtk_clist_new(1);
556   gtk_clist_column_titles_hide (GTK_CLIST(fontsel->font_clist));
557   gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_clist), 0, TRUE);
558   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
559   gtk_widget_set_usize (scrolled_win, FONT_LIST_WIDTH, FONT_LIST_HEIGHT);
560   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->font_clist);
561   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
562                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
563   gtk_widget_show(fontsel->font_clist);
564   gtk_widget_show(scrolled_win);
565
566   gtk_table_attach (GTK_TABLE (table), scrolled_win, 0, 1, 2, 3,
567                     GTK_EXPAND | GTK_FILL,
568                     GTK_EXPAND | GTK_FILL, 0, 0);
569   
570   fontsel->font_style_clist = gtk_clist_new(1);
571   gtk_clist_column_titles_hide (GTK_CLIST(fontsel->font_style_clist));
572   gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_style_clist),
573                                     0, TRUE);
574   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
575   gtk_widget_set_usize (scrolled_win, FONT_STYLE_LIST_WIDTH, -1);
576   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->font_style_clist);
577   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
578                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
579   gtk_widget_show(fontsel->font_style_clist);
580   gtk_widget_show(scrolled_win);
581   gtk_table_attach (GTK_TABLE (table), scrolled_win, 1, 2, 2, 3,
582                     GTK_EXPAND | GTK_FILL,
583                     GTK_EXPAND | GTK_FILL, 0, 0);
584   
585   fontsel->size_clist = gtk_clist_new(1);
586   gtk_clist_column_titles_hide (GTK_CLIST(fontsel->size_clist));
587   gtk_clist_set_column_width (GTK_CLIST(fontsel->size_clist), 0, 20);
588   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
589   gtk_widget_set_usize (scrolled_win, FONT_SIZE_LIST_WIDTH, -1);
590   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->size_clist);
591   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
592                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
593   gtk_widget_show(fontsel->size_clist);
594   gtk_widget_show(scrolled_win);
595   gtk_table_attach (GTK_TABLE (table), scrolled_win, 2, 3, 2, 3,
596                     GTK_FILL, GTK_FILL, 0, 0);
597   
598   
599   /* Insert the fonts. If there exist fonts with the same family but
600      different foundries, then the foundry name is appended in brackets. */
601   gtk_font_selection_show_available_fonts(fontsel);
602   
603   gtk_signal_connect (GTK_OBJECT (fontsel->font_clist), "select_row",
604                       GTK_SIGNAL_FUNC(gtk_font_selection_select_font),
605                       fontsel);
606   GTK_WIDGET_SET_FLAGS (fontsel->font_clist, GTK_CAN_FOCUS);
607   gtk_signal_connect (GTK_OBJECT (fontsel->font_clist), "key_press_event",
608                       GTK_SIGNAL_FUNC(gtk_font_selection_on_clist_key_press),
609                       fontsel);
610   gtk_signal_connect_after (GTK_OBJECT (fontsel->font_clist), "expose_event",
611                             GTK_SIGNAL_FUNC(gtk_font_selection_expose_list),
612                             fontsel);
613   
614   gtk_signal_connect (GTK_OBJECT (fontsel->font_style_clist), "select_row",
615                       GTK_SIGNAL_FUNC(gtk_font_selection_select_style),
616                       fontsel);
617   GTK_WIDGET_SET_FLAGS (fontsel->font_style_clist, GTK_CAN_FOCUS);
618   gtk_signal_connect (GTK_OBJECT (fontsel->font_style_clist),
619                       "key_press_event",
620                       GTK_SIGNAL_FUNC(gtk_font_selection_on_clist_key_press),
621                       fontsel);
622   gtk_signal_connect_after (GTK_OBJECT (fontsel->font_style_clist),
623                             "realize",
624                             GTK_SIGNAL_FUNC(gtk_font_selection_realize_list),
625                             fontsel);
626   
627   /* Insert the standard font sizes */
628   gtk_clist_freeze (GTK_CLIST(fontsel->size_clist));
629   size_to_match = INITIAL_FONT_SIZE;
630   if (INITIAL_METRIC == GTK_FONT_METRIC_POINTS)
631     size_to_match = size_to_match / 10;
632   for (i = 0; i < sizeof(font_sizes) / sizeof(font_sizes[0]); i++)
633     {
634       sprintf(buffer, "%i", font_sizes[i]);
635       size = buffer;
636       gtk_clist_append(GTK_CLIST(fontsel->size_clist), &size);
637       if (font_sizes[i] == size_to_match)
638         {
639           gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), i, 0);
640           gtk_entry_set_text(GTK_ENTRY(fontsel->size_entry), buffer);
641         }
642     }
643   gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
644   
645   gtk_signal_connect (GTK_OBJECT (fontsel->size_clist), "select_row",
646                       GTK_SIGNAL_FUNC(gtk_font_selection_select_size),
647                       fontsel);
648   GTK_WIDGET_SET_FLAGS (fontsel->size_clist, GTK_CAN_FOCUS);
649   gtk_signal_connect (GTK_OBJECT (fontsel->size_clist), "key_press_event",
650                       GTK_SIGNAL_FUNC(gtk_font_selection_on_clist_key_press),
651                       fontsel);
652   
653   
654   /* create the Reset Filter & Metric buttons */
655   hbox = gtk_hbox_new(FALSE, 8);
656   gtk_widget_show (hbox);
657   gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), hbox, FALSE, TRUE, 0);
658   
659   fontsel->filter_button = gtk_button_new_with_label(_("Reset Filter"));
660   gtk_misc_set_padding (GTK_MISC (GTK_BIN (fontsel->filter_button)->child),
661                         16, 0);
662   gtk_widget_show(fontsel->filter_button);
663   gtk_box_pack_start (GTK_BOX (hbox), fontsel->filter_button, FALSE, FALSE, 0);
664   gtk_widget_set_sensitive (fontsel->filter_button, FALSE);
665   gtk_signal_connect (GTK_OBJECT (fontsel->filter_button), "clicked",
666                       GTK_SIGNAL_FUNC(gtk_font_selection_on_clear_filter),
667                       fontsel);
668   
669   hbox2 = gtk_hbox_new(FALSE, 0);
670   gtk_widget_show (hbox2);
671   gtk_box_pack_end (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
672   
673   label = gtk_label_new(_("Metric:"));
674   gtk_widget_show (label);
675   gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, TRUE, 8);
676   
677   fontsel->points_button = gtk_radio_button_new_with_label(NULL, _("Points"));
678   gtk_widget_show (fontsel->points_button);
679   gtk_box_pack_start (GTK_BOX (hbox2), fontsel->points_button, FALSE, TRUE, 0);
680   if (INITIAL_METRIC == GTK_FONT_METRIC_POINTS)
681     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->points_button),
682                                 TRUE);
683   
684   fontsel->pixels_button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(fontsel->points_button), _("Pixels"));
685   gtk_widget_show (fontsel->pixels_button);
686   gtk_box_pack_start (GTK_BOX (hbox2), fontsel->pixels_button, FALSE, TRUE, 0);
687   if (INITIAL_METRIC == GTK_FONT_METRIC_PIXELS)
688     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->pixels_button),
689                                 TRUE);
690   
691   gtk_signal_connect(GTK_OBJECT(fontsel->points_button), "toggled",
692                      (GtkSignalFunc) gtk_font_selection_metric_callback,
693                      fontsel);
694   gtk_signal_connect(GTK_OBJECT(fontsel->pixels_button), "toggled",
695                      (GtkSignalFunc) gtk_font_selection_metric_callback,
696                      fontsel);
697   
698   
699   /* create the text entry widget */
700   text_frame = gtk_frame_new (_("Preview:"));
701   gtk_widget_show (text_frame);
702   gtk_frame_set_shadow_type(GTK_FRAME(text_frame), GTK_SHADOW_ETCHED_IN);
703   gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), text_frame,
704                       FALSE, TRUE, 0);
705   
706   /* This is just used to get a 4-pixel space around the preview entry. */
707   text_box = gtk_hbox_new (FALSE, 0);
708   gtk_widget_show (text_box);
709   gtk_container_add (GTK_CONTAINER (text_frame), text_box);
710   gtk_container_set_border_width (GTK_CONTAINER (text_box), 4);
711   
712   fontsel->preview_entry = gtk_entry_new ();
713   gtk_widget_show (fontsel->preview_entry);
714   gtk_widget_set_usize (fontsel->preview_entry, -1, INITIAL_PREVIEW_HEIGHT);
715   gtk_box_pack_start (GTK_BOX (text_box), fontsel->preview_entry,
716                       TRUE, TRUE, 0);
717   
718   /* Create the message area */
719   fontsel->message_label = gtk_label_new("");
720   gtk_widget_show (fontsel->message_label);
721   gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), fontsel->message_label, 
722                       FALSE, FALSE, 0);
723   
724   
725   /* Create the font info page */
726   fontsel->info_vbox = gtk_vbox_new (FALSE, 4);
727   gtk_widget_show (fontsel->info_vbox);
728   gtk_container_set_border_width (GTK_CONTAINER (fontsel->info_vbox), 2);
729   label = gtk_label_new(_("Font Information"));
730   gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
731                             fontsel->info_vbox, label);
732   
733   fontsel->info_clist = gtk_clist_new_with_titles (3, titles);
734   gtk_widget_set_usize (fontsel->info_clist, 390, 150);
735   gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 0, 130);
736   gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 1, 130);
737   gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 2, 130);
738   gtk_clist_column_titles_passive(GTK_CLIST(fontsel->info_clist));
739   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
740   gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->info_clist);
741   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
742                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
743   gtk_widget_show(fontsel->info_clist);
744   gtk_widget_show(scrolled_win);
745   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), scrolled_win,
746                       TRUE, TRUE, 0);
747   
748   /* Insert the property names */
749   gtk_clist_freeze (GTK_CLIST(fontsel->info_clist));
750   row_text[1] = "";
751   row_text[2] = "";
752   for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
753     {
754       row_text[0] = _(xlfd_field_names[i]);
755       gtk_clist_append(GTK_CLIST(fontsel->info_clist), row_text);
756       gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 0, 0, 4);
757       gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 1, 0, 4);
758       gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 2, 0, 4);
759     }
760   gtk_clist_thaw (GTK_CLIST(fontsel->info_clist));
761   
762   label = gtk_label_new(_("Requested Font Name:"));
763   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
764   gtk_widget_show (label);
765   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, TRUE, 0);
766   
767   fontsel->requested_font_name = gtk_entry_new();
768   gtk_entry_set_editable(GTK_ENTRY(fontsel->requested_font_name), FALSE);
769   gtk_widget_show (fontsel->requested_font_name);
770   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox),
771                       fontsel->requested_font_name, FALSE, TRUE, 0);
772   
773   label = gtk_label_new(_("Actual Font Name:"));
774   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
775   gtk_widget_show (label);
776   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, TRUE, 0);
777   
778   fontsel->actual_font_name = gtk_entry_new();
779   gtk_entry_set_editable(GTK_ENTRY(fontsel->actual_font_name), FALSE);
780   gtk_widget_show (fontsel->actual_font_name);
781   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox),
782                       fontsel->actual_font_name, FALSE, TRUE, 0);
783   
784   sprintf(buffer, _("%i fonts available with a total of %i styles."),
785           fontsel_info->nfonts, fontsel_info->nstyles);
786   label = gtk_label_new(buffer);
787   gtk_widget_show (label);
788   gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, FALSE, 0);
789   
790   gtk_signal_connect (GTK_OBJECT (fontsel), "switch_page",
791                       GTK_SIGNAL_FUNC(gtk_font_selection_switch_page),
792                       fontsel);
793   
794   
795   /* Create the Filter page. */
796   fontsel->filter_vbox = gtk_vbox_new (FALSE, 4);
797   gtk_widget_show (fontsel->filter_vbox);
798   gtk_container_set_border_width (GTK_CONTAINER (fontsel->filter_vbox), 2);
799   label = gtk_label_new(_("Filter"));
800   gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
801                             fontsel->filter_vbox, label);
802   
803   /* Create the font type checkbuttons. */
804   frame = gtk_frame_new (NULL);
805   gtk_widget_show (frame);
806   gtk_box_pack_start (GTK_BOX (fontsel->filter_vbox), frame, FALSE, TRUE, 0);
807
808   hbox = gtk_hbox_new (FALSE, 20);
809   gtk_widget_show (hbox);
810   gtk_container_add (GTK_CONTAINER (frame), hbox);
811
812   label = gtk_label_new(_("Font Types:"));
813   gtk_widget_show (label);
814   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 10);
815
816   hbox2 = gtk_hbox_new (TRUE, 0);
817   gtk_widget_show (hbox2);
818   gtk_box_pack_start (GTK_BOX (hbox), hbox2, FALSE, TRUE, 0);
819
820   fontsel->type_bitmaps_button = gtk_check_button_new_with_label (_("Bitmap"));
821   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), TRUE);
822   gtk_widget_show (fontsel->type_bitmaps_button);
823   gtk_box_pack_start (GTK_BOX (hbox2), fontsel->type_bitmaps_button,
824                       FALSE, TRUE, 0);
825
826   fontsel->type_scalable_button = gtk_check_button_new_with_label (_("Scalable"));
827   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), TRUE);
828   gtk_widget_show (fontsel->type_scalable_button);
829   gtk_box_pack_start (GTK_BOX (hbox2), fontsel->type_scalable_button,
830                       FALSE, TRUE, 0);
831
832   fontsel->type_scaled_bitmaps_button = gtk_check_button_new_with_label (_("Scaled Bitmap"));
833   gtk_widget_show (fontsel->type_scaled_bitmaps_button);
834   gtk_box_pack_start (GTK_BOX (hbox2), fontsel->type_scaled_bitmaps_button,
835                       FALSE, TRUE, 0);
836
837   table = gtk_table_new (4, 3, FALSE);
838   gtk_table_set_col_spacings(GTK_TABLE(table), 2);
839   gtk_widget_show (table);
840   gtk_box_pack_start (GTK_BOX (fontsel->filter_vbox), table, TRUE, TRUE, 0);
841   
842   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
843     {
844       gint left = filter_positions[prop][0];
845       gint top = filter_positions[prop][1];
846       
847       label = gtk_label_new(_(xlfd_field_names[xlfd_index[prop]]));
848       gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
849       gtk_misc_set_padding (GTK_MISC (label), 0, 2);
850       gtk_widget_show(label);
851       gtk_table_attach (GTK_TABLE (table), label, left, left + 1,
852                         top, top + 1, GTK_FILL, GTK_FILL, 0, 0);
853       
854       clist = gtk_clist_new(1);
855       gtk_widget_set_usize (clist, 100, filter_heights[prop]);
856       gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_MULTIPLE);
857       gtk_clist_column_titles_hide(GTK_CLIST(clist));
858       gtk_clist_set_column_auto_resize (GTK_CLIST (clist), 0, TRUE);
859       scrolled_win = gtk_scrolled_window_new (NULL, NULL);
860       gtk_container_add (GTK_CONTAINER (scrolled_win), clist);
861       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
862                                       GTK_POLICY_AUTOMATIC,
863                                       GTK_POLICY_AUTOMATIC);
864       gtk_widget_show(clist);
865       gtk_widget_show(scrolled_win);
866       
867       /* For the bottom-right cell we add the 'Reset Filter' button. */
868       if (top == 2 && left == 2)
869         {
870           vbox = gtk_vbox_new(FALSE, 0);
871           gtk_widget_show(vbox);
872           gtk_table_attach (GTK_TABLE (table), vbox, left, left + 1,
873                             top + 1, top + 2, GTK_FILL, GTK_FILL, 0, 0);
874           
875           gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
876           
877           alignment = gtk_alignment_new(0.5, 0.0, 0.8, 0.0);
878           gtk_widget_show(alignment);
879           gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, TRUE, 4);
880           
881           button = gtk_button_new_with_label(_("Reset Filter"));
882           gtk_widget_show(button);
883           gtk_container_add(GTK_CONTAINER(alignment), button);
884           gtk_signal_connect (GTK_OBJECT (button), "clicked",
885                               GTK_SIGNAL_FUNC(gtk_font_selection_reset_filter),
886                               fontsel);
887         }
888       else
889         gtk_table_attach (GTK_TABLE (table), scrolled_win,
890                           left, left + 1, top + 1, top + 2,
891                           GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
892       
893       gtk_signal_connect (GTK_OBJECT (clist), "select_row",
894                           GTK_SIGNAL_FUNC(gtk_font_selection_select_filter),
895                           fontsel);
896       gtk_signal_connect (GTK_OBJECT (clist), "unselect_row",
897                           GTK_SIGNAL_FUNC(gtk_font_selection_unselect_filter),
898                           fontsel);
899       
900       /* Insert the property names, expanded, and in sorted order.
901          But we make sure that the wildcard '*' is first. */
902       gtk_clist_freeze (GTK_CLIST(clist));
903       property = N_("*");
904       gtk_clist_append(GTK_CLIST(clist), &property);
905       
906       for (i = 1; i < fontsel_info->nproperties[prop]; i++) {
907         property = _(fontsel_info->properties[prop][i]);
908         if (prop == SLANT)
909           property = gtk_font_selection_expand_slant_code(property);
910         else if (prop == SPACING)
911           property = gtk_font_selection_expand_spacing_code(property);
912
913         inserted = FALSE;
914         for (row = 1; row < GTK_CLIST(clist)->rows; row++)
915           {
916             gtk_clist_get_text(GTK_CLIST(clist), row, 0, &text);
917             if (strcmp(property, text) < 0)
918               {
919                 inserted = TRUE;
920                 gtk_clist_insert(GTK_CLIST(clist), row, &property);
921                 break;
922               }
923           }
924         if (!inserted)
925           row = gtk_clist_append(GTK_CLIST(clist), &property);
926         gtk_clist_set_row_data(GTK_CLIST(clist), row, GINT_TO_POINTER (i));
927       }
928       gtk_clist_select_row(GTK_CLIST(clist), 0, 0);
929       gtk_clist_thaw (GTK_CLIST(clist));
930       fontsel->filter_clists[prop] = clist;
931     }
932 }
933
934 GtkWidget *
935 gtk_font_selection_new()
936 {
937   GtkFontSelection *fontsel;
938   
939   fontsel = gtk_type_new (GTK_TYPE_FONT_SELECTION);
940   
941   return GTK_WIDGET (fontsel);
942 }
943
944 static void
945 gtk_font_selection_destroy (GtkObject *object)
946 {
947   GtkFontSelection *fontsel;
948   
949   g_return_if_fail (object != NULL);
950   g_return_if_fail (GTK_IS_FONT_SELECTION (object));
951   
952   fontsel = GTK_FONT_SELECTION (object);
953   
954   /* All we have to do is unref the font, if we have one. */
955   if (fontsel->font)
956     gdk_font_unref (fontsel->font);
957   
958   if (GTK_OBJECT_CLASS (font_selection_parent_class)->destroy)
959     (* GTK_OBJECT_CLASS (font_selection_parent_class)->destroy) (object);
960 }
961
962
963 /* This is called when the clist is exposed. Here we scroll to the current
964    font if necessary. */
965 static void
966 gtk_font_selection_expose_list (GtkWidget               *widget,
967                                 GdkEventExpose          *event,
968                                 gpointer                 data)
969 {
970   GtkFontSelection *fontsel;
971   FontInfo *font_info;
972   GList *selection;
973   gint index;
974   
975 #ifdef FONTSEL_DEBUG
976   g_message("In expose_list\n");
977 #endif
978   fontsel = GTK_FONT_SELECTION(data);
979   
980   font_info = fontsel_info->font_info;
981       
982   /* Try to scroll the font family clist to the selected item */
983   selection = GTK_CLIST(fontsel->font_clist)->selection;
984   if (selection)
985     {
986       index = GPOINTER_TO_INT (selection->data);
987       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_clist), index)
988           != GTK_VISIBILITY_FULL)
989         gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
990     }
991       
992   /* Try to scroll the font style clist to the selected item */
993   selection = GTK_CLIST(fontsel->font_style_clist)->selection;
994   if (selection)
995     {
996       index = GPOINTER_TO_INT (selection->data);
997       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist), index)
998           != GTK_VISIBILITY_FULL)
999         gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), index, -1,
1000                          0.5, 0);
1001     }
1002       
1003   /* Try to scroll the font size clist to the selected item */
1004   selection = GTK_CLIST(fontsel->size_clist)->selection;
1005   if (selection)
1006     {
1007       index = GPOINTER_TO_INT (selection->data);
1008       if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->size_clist), index)
1009           != GTK_VISIBILITY_FULL)
1010       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), index, -1, 0.5, 0);
1011     }
1012 }
1013
1014
1015 /* This is called when the style clist is realized. We need to set any
1016    charset rows to insensitive colours. */
1017 static void
1018 gtk_font_selection_realize_list (GtkWidget              *widget,
1019                                  gpointer                data)
1020 {
1021   GtkFontSelection *fontsel;
1022   gint row;
1023   GdkColor *inactive_fg, *inactive_bg;
1024
1025 #ifdef FONTSEL_DEBUG
1026   g_message("In realize_list\n");
1027 #endif
1028   fontsel = GTK_FONT_SELECTION (data);
1029
1030   /* Set the colours for any charset rows to insensitive. */
1031   inactive_fg = &fontsel->font_style_clist->style->fg[GTK_STATE_INSENSITIVE];
1032   inactive_bg = &fontsel->font_style_clist->style->bg[GTK_STATE_INSENSITIVE];
1033
1034   for (row = 0; row < GTK_CLIST (fontsel->font_style_clist)->rows; row++)
1035     {
1036       if (GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (fontsel->font_style_clist), row)) == -1)
1037         {
1038           gtk_clist_set_foreground (GTK_CLIST (fontsel->font_style_clist),
1039                                     row, inactive_fg);
1040           gtk_clist_set_background (GTK_CLIST (fontsel->font_style_clist),
1041                                     row, inactive_bg);
1042         }
1043     }
1044 }
1045
1046
1047 /* This is called when a family is selected in the list. */
1048 static void
1049 gtk_font_selection_select_font (GtkWidget      *w,
1050                                 gint            row,
1051                                 gint            column,
1052                                 GdkEventButton *bevent,
1053                                 gpointer        data)
1054 {
1055   GtkFontSelection *fontsel;
1056   FontInfo *font_info;
1057   FontInfo *font;
1058   
1059 #ifdef FONTSEL_DEBUG
1060   g_message("In select_font\n");
1061 #endif
1062   fontsel = GTK_FONT_SELECTION(data);
1063   font_info = fontsel_info->font_info;
1064   
1065   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
1066     gtk_widget_grab_focus (w);
1067   
1068   row = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (fontsel->font_clist), row));
1069   font = &font_info[row];
1070   gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), font->family);
1071   
1072   /* If it is already the current font, just return. */
1073   if (fontsel->font_index == row)
1074     return;
1075   
1076   fontsel->font_index = row;
1077   gtk_font_selection_show_available_styles (fontsel);
1078   gtk_font_selection_select_best_style (fontsel, TRUE);
1079 }
1080
1081
1082 static gint
1083 gtk_font_selection_on_clist_key_press (GtkWidget        *clist,
1084                                        GdkEventKey      *event,
1085                                        GtkFontSelection *fontsel)
1086 {
1087 #ifdef FONTSEL_DEBUG
1088   g_message("In on_clist_key_press\n");
1089 #endif
1090   if (event->keyval == GDK_Up)
1091     return gtk_font_selection_select_next (fontsel, clist, -1);
1092   else if (event->keyval == GDK_Down)
1093     return gtk_font_selection_select_next (fontsel, clist, 1);
1094   else
1095     return FALSE;
1096 }
1097
1098
1099 static gboolean
1100 gtk_font_selection_select_next (GtkFontSelection *fontsel,
1101                                 GtkWidget        *clist,
1102                                 gint              step)
1103 {
1104   GList *selection;
1105   gint current_row, row;
1106   
1107   selection = GTK_CLIST(clist)->selection;
1108   if (!selection)
1109     return FALSE;
1110   current_row = GPOINTER_TO_INT (selection->data);
1111   
1112   /* Stop the normal clist key handler from being run. */
1113   gtk_signal_emit_stop_by_name (GTK_OBJECT (clist), "key_press_event");
1114
1115   for (row = current_row + step;
1116        row >= 0 && row < GTK_CLIST(clist)->rows;
1117        row += step)
1118     {
1119       /* If this is the style clist, make sure that the item is not a charset
1120          entry. */
1121       if (clist == fontsel->font_style_clist)
1122         if (GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(clist), row)) == -1)
1123           continue;
1124       
1125       /* Now we've found the row to select. */
1126       if (gtk_clist_row_is_visible(GTK_CLIST(clist), row)
1127           != GTK_VISIBILITY_FULL)
1128         gtk_clist_moveto(GTK_CLIST(clist), row, -1, (step < 0) ? 0 : 1, 0);
1129       gtk_clist_select_row(GTK_CLIST(clist), row, 0);
1130       break;
1131     }
1132   return TRUE;
1133 }
1134
1135
1136 /* This fills the font style clist with all the possible style combinations
1137    for the current font family. */
1138 static void
1139 gtk_font_selection_show_available_styles (GtkFontSelection *fontsel)
1140 {
1141   FontInfo *font;
1142   FontStyle *styles;
1143   gint style, tmpstyle, row;
1144   gint weight_index, slant_index, set_width_index, spacing_index;
1145   gint charset_index;
1146   gchar *weight, *slant, *set_width, *spacing;
1147   gchar *charset = NULL;
1148   gchar *new_item;
1149   gchar buffer[XLFD_MAX_FIELD_LEN * 6 + 2];
1150   GdkColor *inactive_fg, *inactive_bg;
1151   gboolean show_charset;
1152   
1153 #ifdef FONTSEL_DEBUG
1154   g_message("In show_available_styles\n");
1155 #endif
1156   font = &fontsel_info->font_info[fontsel->font_index];
1157   styles = &fontsel_info->font_styles[font->style_index];
1158   
1159   gtk_clist_freeze (GTK_CLIST(fontsel->font_style_clist));
1160   gtk_clist_clear (GTK_CLIST(fontsel->font_style_clist));
1161   
1162   /* First we mark all visible styles as not having been displayed yet,
1163      and check if every style has the same charset. If not then we will
1164      display the charset in the list before the styles. */
1165   show_charset = FALSE;
1166   charset_index = -1;
1167   for (style = 0; style < font->nstyles; style++)
1168     {
1169       if (gtk_font_selection_style_visible(fontsel, font, style))
1170         {
1171           styles[style].flags &= ~GTK_FONT_DISPLAYED;
1172           
1173           if (charset_index == -1)
1174             charset_index  = styles[style].properties[CHARSET];
1175           else if (charset_index != styles[style].properties[CHARSET])
1176             show_charset = TRUE;
1177         }
1178       else
1179         styles[style].flags |= GTK_FONT_DISPLAYED;
1180     }
1181   
1182   /* Step through the undisplayed styles, finding the next charset which
1183      hasn't been displayed yet. Then display the charset on one line, if
1184      necessary, and the visible styles indented beneath it. */
1185   inactive_fg = &fontsel->font_style_clist->style->fg[GTK_STATE_INSENSITIVE];
1186   inactive_bg = &fontsel->font_style_clist->style->bg[GTK_STATE_INSENSITIVE];
1187   
1188   for (style = 0; style < font->nstyles; style++)
1189     {
1190       if (styles[style].flags & GTK_FONT_DISPLAYED)
1191         continue;
1192       
1193       if (show_charset)
1194         {
1195           charset_index  = styles[style].properties[CHARSET];
1196           charset  = fontsel_info->properties[CHARSET] [charset_index];
1197           row = gtk_clist_append(GTK_CLIST(fontsel->font_style_clist),
1198                                  &charset);
1199           gtk_clist_set_row_data(GTK_CLIST(fontsel->font_style_clist), row,
1200                                  (gpointer) -1);
1201           if (GTK_WIDGET_REALIZED (fontsel->font_style_clist))
1202             {
1203               gtk_clist_set_foreground(GTK_CLIST(fontsel->font_style_clist),
1204                                        row, inactive_fg);
1205               gtk_clist_set_background(GTK_CLIST(fontsel->font_style_clist),
1206                                        row, inactive_bg);
1207             }
1208         }
1209       
1210       for (tmpstyle = style; tmpstyle < font->nstyles; tmpstyle++)
1211         {
1212           if (styles[tmpstyle].flags & GTK_FONT_DISPLAYED
1213               || charset_index != styles[tmpstyle].properties[CHARSET])
1214             continue;
1215           
1216           styles[tmpstyle].flags |= GTK_FONT_DISPLAYED;
1217           
1218           weight_index    = styles[tmpstyle].properties[WEIGHT];
1219           slant_index     = styles[tmpstyle].properties[SLANT];
1220           set_width_index = styles[tmpstyle].properties[SET_WIDTH];
1221           spacing_index   = styles[tmpstyle].properties[SPACING];
1222           weight    = fontsel_info->properties[WEIGHT]   [weight_index];
1223           slant     = fontsel_info->properties[SLANT]    [slant_index];
1224           set_width = fontsel_info->properties[SET_WIDTH][set_width_index];
1225           spacing   = fontsel_info->properties[SPACING]  [spacing_index];
1226           
1227           /* Convert '(nil)' weights to 'regular', since it looks nicer. */
1228           if      (!g_strcasecmp(weight, N_("(nil)")))  weight = N_("regular");
1229           
1230           /* We don't show default values or (nil) in the other properties. */
1231           if      (!g_strcasecmp(slant, "r"))        slant = NULL;
1232           else if (!g_strcasecmp(slant, "(nil)"))    slant = NULL;
1233           else if (!g_strcasecmp(slant, "i"))        slant = N_("italic");
1234           else if (!g_strcasecmp(slant, "o"))        slant = N_("oblique");
1235           else if (!g_strcasecmp(slant, "ri"))       slant = N_("reverse italic");
1236           else if (!g_strcasecmp(slant, "ro"))       slant = N_("reverse oblique");
1237           else if (!g_strcasecmp(slant, "ot"))       slant = N_("other");
1238           
1239           if      (!g_strcasecmp(set_width, "normal")) set_width = NULL;
1240           else if (!g_strcasecmp(set_width, "(nil)"))  set_width = NULL;
1241           
1242           if      (!g_strcasecmp(spacing, "p"))        spacing = NULL;
1243           else if (!g_strcasecmp(spacing, "(nil)"))    spacing = NULL;
1244           else if (!g_strcasecmp(spacing, "m"))        spacing = N_("[M]");
1245           else if (!g_strcasecmp(spacing, "c"))        spacing = N_("[C]");
1246           
1247           /* Add the strings together, making sure there is 1 space between
1248              them */
1249           strcpy(buffer, _(weight));
1250           if (slant)
1251             {
1252               strcat(buffer, " ");
1253               strcat(buffer, _(slant));
1254             }
1255           if (set_width)
1256             {
1257               strcat(buffer, " ");
1258               strcat(buffer, _(set_width));
1259             }
1260           if (spacing)
1261             {
1262               strcat(buffer, " ");
1263               strcat(buffer, _(spacing));
1264             }
1265           
1266           new_item = buffer;
1267           row = gtk_clist_append(GTK_CLIST(fontsel->font_style_clist),
1268                                  &new_item);
1269           if (show_charset)
1270             gtk_clist_set_shift(GTK_CLIST(fontsel->font_style_clist), row, 0,
1271                                 0, 4);
1272           gtk_clist_set_row_data(GTK_CLIST(fontsel->font_style_clist), row,
1273                                  GINT_TO_POINTER (tmpstyle));
1274         }
1275     }
1276   
1277   gtk_clist_thaw (GTK_CLIST(fontsel->font_style_clist));
1278 }
1279
1280
1281 /* This selects a style when the user selects a font. It just uses the first
1282    available style at present. I was thinking of trying to maintain the
1283    selected style, e.g. bold italic, when the user selects different fonts.
1284    However, the interface is so easy to use now I'm not sure it's worth it.
1285    Note: This will load a font. */
1286 static void
1287 gtk_font_selection_select_best_style(GtkFontSelection *fontsel,
1288                                      gboolean          use_first)
1289 {
1290   FontInfo *font;
1291   FontStyle *styles;
1292   gint row, prop, style, matched;
1293   gint best_matched = -1, best_style = -1, best_row = -1;
1294   
1295 #ifdef FONTSEL_DEBUG
1296   g_message("In select_best_style\n");
1297 #endif
1298   font = &fontsel_info->font_info[fontsel->font_index];
1299   styles = &fontsel_info->font_styles[font->style_index];
1300   
1301   for (row = 0; row < GTK_CLIST(fontsel->font_style_clist)->rows; row++)
1302     {
1303       style = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (fontsel->font_style_clist), row));
1304       /* Skip charset rows. */
1305       if (style == -1)
1306         continue;
1307
1308       /* If we just want the first style, we've got it. */
1309       if (use_first)
1310         {
1311           best_style = style;
1312           best_row = row;
1313           break;
1314         }
1315
1316       matched = 0;
1317       for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
1318         {
1319           if (fontsel->property_values[prop] == styles[style].properties[prop])
1320             matched++;
1321         }
1322       if (matched > best_matched)
1323         {
1324           best_matched = matched;
1325           best_style = style;
1326           best_row = row;
1327         }
1328     }
1329   g_return_if_fail (best_style != -1);
1330   g_return_if_fail (best_row != -1);
1331
1332   fontsel->style = best_style;
1333
1334   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
1335     fontsel->property_values[prop] = styles[fontsel->style].properties[prop];
1336
1337   gtk_clist_select_row(GTK_CLIST(fontsel->font_style_clist), best_row, 0);
1338   if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist), best_row)
1339       != GTK_VISIBILITY_FULL)
1340     gtk_clist_moveto(GTK_CLIST(fontsel->font_style_clist), best_row, -1,
1341                      0.5, 0);
1342   gtk_font_selection_show_available_sizes (fontsel);
1343   gtk_font_selection_select_best_size (fontsel);
1344 }
1345
1346
1347 /* This is called when a style is selected in the list. */
1348 static void
1349 gtk_font_selection_select_style (GtkWidget      *w,
1350                                  gint           row,
1351                                  gint           column,
1352                                  GdkEventButton *bevent,
1353                                  gpointer        data)
1354 {
1355   GtkFontSelection *fontsel;
1356   FontInfo *font_info;
1357   FontInfo *font;
1358   FontStyle *styles;
1359   gint style, prop;
1360   gchar *text;
1361   
1362 #ifdef FONTSEL_DEBUG
1363   g_message("In select_style\n");
1364 #endif
1365   fontsel = GTK_FONT_SELECTION(data);
1366   font_info = fontsel_info->font_info;
1367   font = &font_info[fontsel->font_index];
1368   styles = &fontsel_info->font_styles[font->style_index];
1369   
1370   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
1371     gtk_widget_grab_focus (w);
1372   
1373   /* The style index is stored in the row data, so we just need to copy
1374      the style values into the fontsel and reload the font. */
1375   style = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(fontsel->font_style_clist), row));
1376   
1377   /* Don't allow selection of charset rows. */
1378   if (style == -1)
1379     {
1380       gtk_clist_unselect_row(GTK_CLIST(fontsel->font_style_clist), row, 0);
1381       return;
1382     }
1383   
1384   gtk_clist_get_text(GTK_CLIST(fontsel->font_style_clist), row, 0, &text);
1385   gtk_entry_set_text(GTK_ENTRY(fontsel->font_style_entry), text);
1386   
1387   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
1388     fontsel->property_values[prop] = styles[style].properties[prop];
1389   
1390   if (fontsel->style == style)
1391     return;
1392   
1393   fontsel->style = style;
1394   gtk_font_selection_show_available_sizes (fontsel);
1395   gtk_font_selection_select_best_size (fontsel);
1396 }
1397
1398
1399 /* This shows all the available sizes in the size clist, according to the
1400    current metric and the current font & style. */
1401 static void
1402 gtk_font_selection_show_available_sizes (GtkFontSelection *fontsel)
1403 {
1404   FontInfo *font;
1405   FontStyle *styles, *style;
1406   const guint16 *standard_sizes;
1407   guint16 *bitmapped_sizes;
1408   gint nstandard_sizes, nbitmapped_sizes;
1409   gchar buffer[16], *size;
1410   gfloat bitmap_size_float = 0.;
1411   guint16 bitmap_size = 0;
1412   gboolean can_match;
1413   gint type_filter;
1414   
1415 #ifdef FONTSEL_DEBUG
1416   g_message("In show_available_sizes\n");
1417 #endif
1418   font = &fontsel_info->font_info[fontsel->font_index];
1419   styles = &fontsel_info->font_styles[font->style_index];
1420   style = &styles[fontsel->style];
1421   
1422   standard_sizes = font_sizes;
1423   nstandard_sizes = sizeof(font_sizes) / sizeof(font_sizes[0]);
1424   if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1425     {
1426       bitmapped_sizes = &fontsel_info->point_sizes[style->point_sizes_index];
1427       nbitmapped_sizes = style->npoint_sizes;
1428     }
1429   else
1430     {
1431       bitmapped_sizes = &fontsel_info->pixel_sizes[style->pixel_sizes_index];
1432       nbitmapped_sizes = style->npixel_sizes;
1433     }
1434   
1435   /* Only show the standard sizes if a scalable font is available. */
1436   type_filter = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
1437     & fontsel->filters[GTK_FONT_FILTER_USER].font_type;
1438
1439   if (!((style->flags & GTK_FONT_SCALABLE_BITMAP
1440          && type_filter & GTK_FONT_SCALABLE_BITMAP)
1441         || (style->flags & GTK_FONT_SCALABLE
1442             && type_filter & GTK_FONT_SCALABLE)))
1443     nstandard_sizes = 0;
1444   
1445   gtk_clist_freeze (GTK_CLIST(fontsel->size_clist));
1446   gtk_clist_clear (GTK_CLIST(fontsel->size_clist));
1447   
1448   /* Interleave the standard sizes with the bitmapped sizes so we get a list
1449      of ascending sizes. If the metric is points, we have to convert the
1450      decipoints to points. */
1451   while (nstandard_sizes || nbitmapped_sizes)
1452     {
1453       can_match = TRUE;
1454
1455       if (nbitmapped_sizes)
1456         if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1457           {
1458             if (*bitmapped_sizes % 10 != 0)
1459               can_match = FALSE;
1460             bitmap_size = *bitmapped_sizes / 10;
1461             bitmap_size_float = *bitmapped_sizes / 10;
1462           }
1463         else
1464           {
1465             bitmap_size = *bitmapped_sizes;
1466             bitmap_size_float = *bitmapped_sizes;
1467           }
1468       
1469       if (can_match && nstandard_sizes && nbitmapped_sizes
1470           && *standard_sizes == bitmap_size)
1471         {
1472           sprintf(buffer, "%i *", *standard_sizes);
1473           standard_sizes++;
1474           nstandard_sizes--;
1475           bitmapped_sizes++;
1476           nbitmapped_sizes--;
1477         }
1478       else if (nstandard_sizes
1479                && (!nbitmapped_sizes
1480                    || (gfloat)*standard_sizes < bitmap_size_float))
1481         {
1482           sprintf(buffer, "%i", *standard_sizes);
1483           standard_sizes++;
1484           nstandard_sizes--;
1485         }
1486       else
1487         {
1488           if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1489             {
1490               if (*bitmapped_sizes % 10 == 0)
1491                 sprintf(buffer, "%i *", *bitmapped_sizes / 10);
1492               else
1493                 sprintf(buffer, "%i.%i *", *bitmapped_sizes / 10,
1494                         *bitmapped_sizes % 10);
1495             }
1496           else
1497             {
1498               sprintf(buffer, "%i *", *bitmapped_sizes);
1499             }
1500           bitmapped_sizes++;
1501           nbitmapped_sizes--;
1502         }
1503       size = buffer;
1504       gtk_clist_append(GTK_CLIST(fontsel->size_clist), &size);
1505     }
1506   gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
1507 }
1508
1509
1510 /* If the user hits return in the font size entry, we change to the new font
1511    size. */
1512 static gint
1513 gtk_font_selection_size_key_press (GtkWidget   *w,
1514                                    GdkEventKey *event,
1515                                    gpointer     data)
1516 {
1517   GtkFontSelection *fontsel;
1518   gint new_size;
1519   gfloat new_size_float;
1520   gchar *text;
1521   
1522 #ifdef FONTSEL_DEBUG
1523   g_message("In size_key_press\n");
1524 #endif
1525   fontsel = GTK_FONT_SELECTION(data);
1526   
1527   if (event->keyval == GDK_Return)
1528     {
1529       text = gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry));
1530       if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
1531         {
1532           new_size = atoi (text);
1533           if (new_size < 2)
1534             new_size = 2;
1535         }
1536       else
1537         {
1538           new_size_float = atof (text) * 10;
1539           new_size = (gint) new_size_float;
1540           if (new_size < 20)
1541             new_size = 20;
1542         }
1543       
1544       /* Remember that this size was set explicitly. */
1545       fontsel->selected_size = new_size;
1546       
1547       /* Check if the font size has changed, and return if it hasn't. */
1548       if (fontsel->size == new_size)
1549         return TRUE;
1550       
1551       fontsel->size = new_size;
1552       gtk_font_selection_select_best_size (fontsel);
1553       return TRUE;
1554     }
1555   
1556   return FALSE;
1557 }
1558
1559
1560 /* This tries to select the closest size to the current size, though it
1561    may have to change the size if only unscaled bitmaps are available.
1562    Note: this will load a font. */
1563 static void
1564 gtk_font_selection_select_best_size(GtkFontSelection *fontsel)
1565 {
1566   FontInfo *font;
1567   FontStyle *styles, *style;
1568   gchar *text;
1569   gint row, best_row = 0, size, size_fraction, best_size = 0, nmatched;
1570   gboolean found = FALSE;
1571   gchar buffer[32];
1572   GList *selection;
1573   gint type_filter;
1574   
1575 #ifdef FONTSEL_DEBUG
1576   g_message("In select_best_size\n");
1577 #endif
1578   font = &fontsel_info->font_info[fontsel->font_index];
1579   styles = &fontsel_info->font_styles[font->style_index];
1580   style = &styles[fontsel->style];
1581   
1582   /* Find the closest size available in the size clist. If the exact size is
1583      in the list set found to TRUE. */
1584   for (row = 0; row < GTK_CLIST(fontsel->size_clist)->rows; row++)
1585     {
1586       gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
1587       nmatched = sscanf(text, "%i.%i", &size, &size_fraction);
1588       if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1589         {
1590           size *= 10;
1591           if (nmatched == 2)
1592             size += size_fraction;
1593         }
1594       
1595       if (size == fontsel->selected_size)
1596         {
1597           found = TRUE;
1598           best_size = size;
1599           best_row = row;
1600           break;
1601         }
1602       else if (best_size == 0
1603                || abs(size - fontsel->selected_size)
1604                < (abs(best_size - fontsel->selected_size)))
1605         {
1606           best_size = size;
1607           best_row = row;
1608         }
1609     }
1610   
1611   /* If we aren't scaling bitmapped fonts and this is a bitmapped font, we
1612      need to use the closest size found. */
1613   type_filter = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
1614     & fontsel->filters[GTK_FONT_FILTER_USER].font_type;
1615
1616   if (!((style->flags & GTK_FONT_SCALABLE_BITMAP
1617          && type_filter & GTK_FONT_SCALABLE_BITMAP)
1618         || (style->flags & GTK_FONT_SCALABLE
1619             && type_filter & GTK_FONT_SCALABLE)))
1620     found = TRUE;
1621   
1622   if (found)
1623     {
1624       fontsel->size = best_size;
1625       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
1626       gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), best_row, 0);
1627     }
1628   else
1629     {
1630       fontsel->size = fontsel->selected_size;
1631       selection = GTK_CLIST(fontsel->size_clist)->selection;
1632       if (selection)
1633         gtk_clist_unselect_row(GTK_CLIST(fontsel->size_clist),
1634                                GPOINTER_TO_INT (selection->data), 0);
1635       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
1636       
1637       /* Show the size in the size entry. */
1638       if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
1639         sprintf(buffer, "%i", fontsel->size);
1640       else
1641         {
1642           if (fontsel->size % 10 == 0)
1643             sprintf(buffer, "%i", fontsel->size / 10);
1644           else
1645             sprintf(buffer, "%i.%i", fontsel->size / 10, fontsel->size % 10);
1646         }
1647       gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
1648     }
1649   gtk_font_selection_load_font (fontsel);
1650 }
1651
1652
1653 /* This is called when a size is selected in the list. */
1654 static void
1655 gtk_font_selection_select_size (GtkWidget      *w,
1656                                 gint            row,
1657                                 gint            column,
1658                                 GdkEventButton *bevent,
1659                                 gpointer        data)
1660 {
1661   GtkFontSelection *fontsel;
1662   gint new_size;
1663   gchar *text;
1664   gchar buffer[16];
1665   gint i;
1666   
1667 #ifdef FONTSEL_DEBUG
1668   g_message("In select_size\n");
1669 #endif
1670   fontsel = GTK_FONT_SELECTION(data);
1671   
1672   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
1673     gtk_widget_grab_focus (w);
1674   
1675   /* Copy the size from the clist to the size entry, but without the bitmapped
1676      marker ('*'). */
1677   gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
1678   i = 0;
1679   while (i < 15 && (text[i] == '.' || (text[i] >= '0' && text[i] <= '9')))
1680     {
1681       buffer[i] = text[i];
1682       i++;
1683     }
1684   buffer[i] = '\0';
1685   gtk_entry_set_text(GTK_ENTRY(fontsel->size_entry), buffer);
1686   
1687   /* Check if the font size has changed, and return if it hasn't. */
1688   new_size = atoi(text);
1689   if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1690     new_size *= 10;
1691   
1692   if (fontsel->size == new_size)
1693     return;
1694   
1695   /* If the size was selected by the user we set the selected_size. */
1696   fontsel->selected_size = new_size;
1697   
1698   fontsel->size = new_size;
1699   gtk_font_selection_load_font (fontsel);
1700 }
1701
1702
1703 /* This is called when the pixels or points radio buttons are pressed. */
1704 static void
1705 gtk_font_selection_metric_callback (GtkWidget *w,
1706                                     gpointer   data)
1707 {
1708   GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
1709   
1710 #ifdef FONTSEL_DEBUG
1711   g_message("In metric_callback\n");
1712 #endif
1713   if (GTK_TOGGLE_BUTTON(fontsel->pixels_button)->active)
1714     {
1715       if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
1716         return;
1717       fontsel->metric = GTK_FONT_METRIC_PIXELS;
1718       fontsel->size = (fontsel->size + 5) / 10;
1719       fontsel->selected_size = (fontsel->selected_size + 5) / 10;
1720     }
1721   else
1722     {
1723       if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1724         return;
1725       fontsel->metric = GTK_FONT_METRIC_POINTS;
1726       fontsel->size *= 10;
1727       fontsel->selected_size *= 10;
1728     }
1729   if (fontsel->font_index != -1)
1730     {
1731       gtk_font_selection_show_available_sizes (fontsel);
1732       gtk_font_selection_select_best_size (fontsel);
1733     }
1734 }
1735
1736
1737 /* This searches the given property table and returns the index of the given
1738    string, or 0, which is the wildcard '*' index, if it's not found. */
1739 static guint16
1740 gtk_font_selection_field_to_index (gchar **table,
1741                                    gint    ntable,
1742                                    gchar  *field)
1743 {
1744   gint i;
1745   
1746   for (i = 0; i < ntable; i++)
1747     if (strcmp (field, table[i]) == 0)
1748       return i;
1749   
1750   return 0;
1751 }
1752
1753
1754
1755 /* This attempts to load the current font, and returns TRUE if it succeeds. */
1756 static gboolean
1757 gtk_font_selection_load_font (GtkFontSelection *fontsel)
1758 {
1759   GdkFont *font;
1760   gchar *fontname, *label_text;
1761   
1762   if (fontsel->font)
1763     gdk_font_unref (fontsel->font);
1764   fontsel->font = NULL;
1765   
1766   /* If no family has been selected yet, just return FALSE. */
1767   if (fontsel->font_index == -1)
1768     return FALSE;
1769   
1770   fontname = gtk_font_selection_get_font_name (fontsel);
1771   if (fontname)
1772     {
1773 #ifdef FONTSEL_DEBUG
1774       g_message("Loading: %s\n", fontname);
1775 #endif
1776       font = gdk_font_load (fontname);
1777       g_free(fontname);
1778       
1779       if (font)
1780         {
1781           fontsel->font = font;
1782           /* Make sure the message label is empty, but don't change it unless
1783              it's necessary as it results in a resize of the whole window! */
1784           gtk_label_get(GTK_LABEL(fontsel->message_label), &label_text);
1785           if (strcmp(label_text, ""))
1786             gtk_label_set_text(GTK_LABEL(fontsel->message_label), "");
1787           gtk_font_selection_update_preview (fontsel);
1788           return TRUE;
1789         }
1790       else 
1791         {
1792           gtk_label_set_text(GTK_LABEL(fontsel->message_label),
1793                              _("The selected font is not available."));
1794         }
1795     }
1796   else
1797     {
1798       gtk_label_set_text(GTK_LABEL(fontsel->message_label),
1799                          _("The selected font is not a valid font."));
1800     }
1801   
1802   return FALSE;
1803 }
1804
1805
1806 /* This sets the font in the preview entry to the selected font, and tries to
1807    make sure that the preview entry is a reasonable size, i.e. so that the
1808    text can be seen with a bit of space to spare. But it tries to avoid
1809    resizing the entry every time the font changes.
1810    This also used to shrink the preview if the font size was decreased, but
1811    that made it awkward if the user wanted to resize the window themself. */
1812 static void
1813 gtk_font_selection_update_preview (GtkFontSelection *fontsel)
1814 {
1815   GtkWidget *preview_entry;
1816   GtkStyle *style;
1817   gint text_height, new_height;
1818   gchar *text;
1819 #if GDK_WINDOWING == GDK_WINDOWING_X11
1820   XFontStruct *xfs;
1821 #endif
1822
1823 #ifdef FONTSEL_DEBUG
1824   g_message("In update_preview\n");
1825 #endif
1826   style = gtk_style_new ();
1827   gdk_font_unref (style->font);
1828   style->font = fontsel->font;
1829   gdk_font_ref (style->font);
1830   
1831   preview_entry = fontsel->preview_entry;
1832   gtk_widget_set_style (preview_entry, style);
1833   gtk_style_unref(style);
1834   
1835   text_height = preview_entry->style->font->ascent
1836     + preview_entry->style->font->descent;
1837   /* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
1838   new_height = text_height + 20;
1839   if (new_height < INITIAL_PREVIEW_HEIGHT)
1840     new_height = INITIAL_PREVIEW_HEIGHT;
1841   if (new_height > MAX_PREVIEW_HEIGHT)
1842     new_height = MAX_PREVIEW_HEIGHT;
1843   
1844   if ((preview_entry->requisition.height < text_height + 10)
1845       || (preview_entry->requisition.height > text_height + 40))
1846     gtk_widget_set_usize(preview_entry, -1, new_height);
1847   
1848   /* This sets the preview text, if it hasn't been set already. */
1849   text = gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
1850   if (strlen(text) == 0)
1851     gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), PREVIEW_TEXT);
1852   gtk_entry_set_position(GTK_ENTRY(fontsel->preview_entry), 0);
1853   
1854 #if GDK_WINDOWING == GDK_WINDOWING_X11
1855   /* If this is a 2-byte font display a message to say it may not be
1856      displayed properly. */
1857   xfs = GDK_FONT_XFONT(fontsel->font);
1858   if (xfs->min_byte1 != 0 || xfs->max_byte1 != 0)
1859     gtk_label_set_text(GTK_LABEL(fontsel->message_label),
1860                        _("This is a 2-byte font and may not be displayed correctly."));
1861 #endif
1862 }
1863
1864
1865 static void
1866 gtk_font_selection_switch_page (GtkWidget       *w,
1867                                 GtkNotebookPage *page,
1868                                 gint             page_num,
1869                                 gpointer         data)
1870 {
1871   GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
1872   
1873   /* This function strangely gets called when the window is destroyed,
1874      so we check here to see if the notebook is visible. */
1875   if (!GTK_WIDGET_VISIBLE(w))
1876     return;
1877   
1878   if (page_num == 0)
1879     gtk_font_selection_update_filter(fontsel);
1880   else if (page_num == 1)
1881     gtk_font_selection_show_font_info(fontsel);
1882 }
1883
1884
1885 static void
1886 gtk_font_selection_show_font_info (GtkFontSelection *fontsel)
1887 {
1888 #if GDK_WINDOWING == GDK_WINDOWING_X11
1889   Atom font_atom, atom;
1890   Bool status;
1891 #endif
1892   char *name;
1893   gchar *fontname;
1894   gchar field_buffer[XLFD_MAX_FIELD_LEN];
1895   gchar *field;
1896   gint i;
1897   gboolean shown_actual_fields = FALSE;
1898   
1899   fontname = gtk_font_selection_get_font_name(fontsel);
1900   gtk_entry_set_text(GTK_ENTRY(fontsel->requested_font_name),
1901                      fontname ? fontname : "");
1902   
1903   gtk_clist_freeze (GTK_CLIST(fontsel->info_clist));
1904   for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1905     {
1906       if (fontname)
1907         field = gtk_font_selection_get_xlfd_field (fontname, i, field_buffer);
1908       else
1909         field = NULL;
1910       if (field)
1911         {
1912           if (i == XLFD_SLANT)
1913             field = gtk_font_selection_expand_slant_code(field);
1914           else if (i == XLFD_SPACING)
1915             field = gtk_font_selection_expand_spacing_code(field);
1916         }
1917       gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 1,
1918                          field ? field : "");
1919     }
1920 #if GDK_WINDOWING == GDK_WINDOWING_X11
1921   if (fontsel->font)
1922     {
1923       font_atom = XInternAtom(GDK_DISPLAY(), "FONT", True);
1924       if (font_atom != None)
1925         {
1926           status = XGetFontProperty(GDK_FONT_XFONT(fontsel->font), font_atom,
1927                                     &atom);
1928           if (status == True)
1929             {
1930               name = XGetAtomName(GDK_DISPLAY(), atom);
1931               gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), name);
1932               
1933               for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1934                 {
1935                   field = gtk_font_selection_get_xlfd_field (name, i,
1936                                                              field_buffer);
1937                   if (i == XLFD_SLANT)
1938                     field = gtk_font_selection_expand_slant_code(field);
1939                   else if (i == XLFD_SPACING)
1940                     field = gtk_font_selection_expand_spacing_code(field);
1941                   gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1942                                      field ? field : "");
1943                 }
1944               shown_actual_fields = TRUE;
1945               XFree(name);
1946             }
1947         }
1948     }
1949 #elif GDK_WINDOWING == GDK_WINDOWING_WIN32
1950   if (fontsel->font)
1951     {
1952       LOGFONT logfont;
1953
1954       if (GetObject (GDK_FONT_XFONT (fontsel->font),
1955                      sizeof (LOGFONT), &logfont) > 0)
1956         {
1957           name = logfont_to_xlfd (&logfont, logfont.lfHeight, -1, 0);
1958           gtk_entry_set_text (GTK_ENTRY(fontsel->actual_font_name),name);
1959           for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1960             {
1961               field = gtk_font_selection_get_xlfd_field (name, i,
1962                                                          field_buffer);
1963               if (i == XLFD_SLANT)
1964                 field = gtk_font_selection_expand_slant_code(field);
1965               else if (i == XLFD_SPACING)
1966                 field = gtk_font_selection_expand_spacing_code(field);
1967               gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1968                                  field ? field : "");
1969             }
1970           shown_actual_fields = TRUE;
1971           g_free (name);
1972         }
1973     }
1974   
1975 #endif
1976   if (!shown_actual_fields)
1977     {
1978       gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), "");
1979       for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1980         {
1981           gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1982                              fontname ? _("(unknown)") : "");
1983         }
1984     }
1985   gtk_clist_thaw (GTK_CLIST(fontsel->info_clist));
1986   g_free(fontname);
1987 }
1988
1989
1990 static gchar*
1991 gtk_font_selection_expand_slant_code(gchar *slant)
1992 {
1993   if      (!g_strcasecmp(slant, "r"))   return(_("roman"));
1994   else if (!g_strcasecmp(slant, "i"))   return(_("italic"));
1995   else if (!g_strcasecmp(slant, "o"))   return(_("oblique"));
1996   else if (!g_strcasecmp(slant, "ri"))  return(_("reverse italic"));
1997   else if (!g_strcasecmp(slant, "ro"))  return(_("reverse oblique"));
1998   else if (!g_strcasecmp(slant, "ot"))  return(_("other"));
1999   return slant;
2000 }
2001
2002 static gchar*
2003 gtk_font_selection_expand_spacing_code(gchar *spacing)
2004 {
2005   if      (!g_strcasecmp(spacing, "p")) return(_("proportional"));
2006   else if (!g_strcasecmp(spacing, "m")) return(_("monospaced"));
2007   else if (!g_strcasecmp(spacing, "c")) return(_("char cell"));
2008   return spacing;
2009 }
2010
2011
2012 /*****************************************************************************
2013  * These functions all deal with the Filter page and filtering the fonts.
2014  *****************************************************************************/
2015
2016 /* This is called when an item is selected in one of the filter clists.
2017    We make sure that the first row of the clist, i.e. the wildcard '*', is
2018    selected if and only if none of the other items are selected.
2019    Also doesn't allow selections of values filtered out by base filter.
2020    We may need to be careful about triggering other signals. */
2021 static void
2022 gtk_font_selection_select_filter             (GtkWidget      *w,
2023                                               gint            row,
2024                                               gint            column,
2025                                               GdkEventButton *bevent,
2026                                               GtkFontSelection *fontsel)
2027 {
2028   gint i, prop, index;
2029   
2030   if (row == 0)
2031     {
2032       for (i = 1; i < GTK_CLIST(w)->rows; i++)
2033         gtk_clist_unselect_row(GTK_CLIST(w), i, 0);
2034     }
2035   else
2036     {
2037       /* Find out which property this is. */
2038       for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2039         if (fontsel->filter_clists[prop] == w)
2040           break;
2041       index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(w), row));
2042       if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_BASE,
2043                                            prop, index) == NOT_FILTERED)
2044         gtk_clist_unselect_row(GTK_CLIST(w), row, 0);
2045       else
2046         gtk_clist_unselect_row(GTK_CLIST(w), 0, 0);
2047     }
2048 }
2049
2050
2051 /* Here a filter item is being deselected. If there are now no items selected
2052    we select the first '*' item, unless that it is the item being deselected,
2053    in which case we select all of the other items. This makes it easy to
2054    select all items in the list except one or two. */
2055 static void
2056 gtk_font_selection_unselect_filter           (GtkWidget      *w,
2057                                               gint            row,
2058                                               gint            column,
2059                                               GdkEventButton *bevent,
2060                                               GtkFontSelection *fontsel)
2061 {
2062   gint i, prop, index;
2063
2064   if (!GTK_CLIST(w)->selection)
2065     {
2066       if (row == 0)
2067         {
2068           /* Find out which property this is. */
2069           for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2070             if (fontsel->filter_clists[prop] == w)
2071               break;
2072
2073           for (i = 1; i < GTK_CLIST(w)->rows; i++)
2074             {
2075               index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(w),
2076                                                               i));
2077               if (gtk_font_selection_filter_state (fontsel,
2078                                                    GTK_FONT_FILTER_BASE,
2079                                                    prop, index)
2080                   != NOT_FILTERED)
2081                 gtk_clist_select_row(GTK_CLIST(w), i, 0);
2082             }
2083         }
2084       else
2085         {
2086           gtk_clist_select_row(GTK_CLIST(w), 0, 0);
2087         }
2088     }
2089 }
2090
2091
2092 /* This is called when the main notebook page is selected. It checks if the
2093    filter has changed, an if so it creates the filter settings, and filters the
2094    fonts shown. If an empty filter (all '*'s) is applied, then filtering is
2095    turned off. */
2096 static void
2097 gtk_font_selection_update_filter     (GtkFontSelection *fontsel)
2098 {
2099   GtkWidget *clist;
2100   GList *selection;
2101   gboolean default_filter = TRUE, filter_changed = FALSE;
2102   gint prop, nselected, i, row, index;
2103   GtkFontFilter *filter = &fontsel->filters[GTK_FONT_FILTER_USER];
2104   gint base_font_type, user_font_type, new_font_type;
2105   
2106 #ifdef FONTSEL_DEBUG
2107   g_message("In update_filter\n");
2108 #endif
2109   
2110   /* Check if the user filter has changed, and also if it is the default
2111      filter, i.e. bitmap & scalable fonts and all '*'s selected.
2112      We only look at the bits which are not already filtered out by the base
2113      filter, since that overrides the user filter. */
2114   base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
2115     & GTK_FONT_ALL;
2116   user_font_type = fontsel->filters[GTK_FONT_FILTER_USER].font_type
2117     & GTK_FONT_ALL;
2118   new_font_type = GTK_TOGGLE_BUTTON(fontsel->type_bitmaps_button)->active
2119     ? GTK_FONT_BITMAP : 0;
2120   new_font_type |= (GTK_TOGGLE_BUTTON(fontsel->type_scalable_button)->active
2121     ? GTK_FONT_SCALABLE : 0);
2122   new_font_type |= (GTK_TOGGLE_BUTTON(fontsel->type_scaled_bitmaps_button)->active ? GTK_FONT_SCALABLE_BITMAP : 0);
2123   new_font_type &= base_font_type;
2124   new_font_type |= (~base_font_type & user_font_type);
2125   if (new_font_type != (GTK_FONT_BITMAP | GTK_FONT_SCALABLE))
2126     default_filter = FALSE;
2127
2128   if (new_font_type != user_font_type)
2129     filter_changed = TRUE;
2130   fontsel->filters[GTK_FONT_FILTER_USER].font_type = new_font_type;
2131
2132   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2133     {
2134       clist = fontsel->filter_clists[prop];
2135       selection = GTK_CLIST(clist)->selection;
2136       nselected = g_list_length(selection);
2137       if (nselected != 1 || GPOINTER_TO_INT (selection->data) != 0)
2138         {
2139           default_filter = FALSE;
2140           
2141           if (filter->property_nfilters[prop] != nselected)
2142             filter_changed = TRUE;
2143           else
2144             {
2145               for (i = 0; i < nselected; i++)
2146                 {
2147                   row = GPOINTER_TO_INT (selection->data);
2148                   index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
2149                   if (filter->property_filters[prop][i] != index)
2150                     filter_changed = TRUE;
2151                   selection = selection->next;
2152                 }
2153             }
2154         }
2155       else
2156         {
2157           if (filter->property_nfilters[prop] != 0)
2158             filter_changed = TRUE;
2159         }
2160     }
2161   
2162   /* If the filter hasn't changed we just return. */
2163   if (!filter_changed)
2164     return;
2165   
2166 #ifdef FONTSEL_DEBUG
2167   g_message("   update_fonts: filter has changed\n");
2168 #endif
2169   
2170   /* Free the old filter data and create the new arrays. */
2171   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2172     {
2173       g_free(filter->property_filters[prop]);
2174
2175       clist = fontsel->filter_clists[prop];
2176       selection = GTK_CLIST(clist)->selection;
2177       nselected = g_list_length(selection);
2178       if (nselected == 1 && GPOINTER_TO_INT (selection->data) == 0)
2179         {
2180           filter->property_filters[prop] = NULL;
2181           filter->property_nfilters[prop] = 0;
2182         }
2183       else
2184         {
2185           filter->property_filters[prop] = g_new(guint16, nselected);
2186           filter->property_nfilters[prop] = nselected;
2187           for (i = 0; i < nselected; i++)
2188             {
2189               row = GPOINTER_TO_INT (selection->data);
2190               index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
2191               filter->property_filters[prop][i] = index;
2192               selection = selection->next;
2193             }
2194         }
2195     }
2196
2197   /* Set the 'Reset Filter' button sensitive if a filter is in effect, and
2198      also set the label above the font list to show this as well. */
2199   if (default_filter)
2200     {
2201       gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
2202       gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font:"));
2203     }
2204   else
2205     {
2206       gtk_widget_set_sensitive(fontsel->filter_button, TRUE);
2207       gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font: (Filter Applied)"));
2208     }
2209   gtk_font_selection_show_available_fonts(fontsel);
2210 }  
2211
2212
2213 /* This shows all the available fonts in the font clist. */
2214 static void
2215 gtk_font_selection_show_available_fonts     (GtkFontSelection *fontsel)
2216 {
2217   FontInfo *font_info, *font;
2218   GtkFontFilter *filter;
2219   gint nfonts, i, j, k, row, style, font_row = -1;
2220   gchar font_buffer[XLFD_MAX_FIELD_LEN * 2 + 4];
2221   gchar *font_item;
2222   gboolean matched, matched_style;
2223   
2224 #ifdef FONTSEL_DEBUG
2225   g_message("In show_available_fonts\n");
2226 #endif
2227   font_info = fontsel_info->font_info;
2228   nfonts = fontsel_info->nfonts;
2229   
2230   /* Filter the list of fonts. */
2231   gtk_clist_freeze (GTK_CLIST(fontsel->font_clist));
2232   gtk_clist_clear (GTK_CLIST(fontsel->font_clist));
2233   for (i = 0; i < nfonts; i++)
2234     {
2235       font = &font_info[i];
2236       
2237       /* Check if the foundry passes through all filters. */
2238       matched = TRUE;
2239       for (k = 0; k < GTK_NUM_FONT_FILTERS; k++)
2240         {
2241           filter = &fontsel->filters[k];
2242
2243           if (filter->property_nfilters[FOUNDRY] != 0)
2244             {
2245               matched = FALSE;
2246               for (j = 0; j < filter->property_nfilters[FOUNDRY]; j++)
2247                 {
2248                   if (font->foundry == filter->property_filters[FOUNDRY][j])
2249                     {
2250                       matched = TRUE;
2251                       break;
2252                     }
2253                 }
2254               if (!matched)
2255                 break;
2256             }
2257         }
2258       
2259       if (!matched)
2260         continue;
2261
2262
2263       /* Now check if the other properties are matched in at least one style.*/
2264       matched_style = FALSE;
2265       for (style = 0; style < font->nstyles; style++)
2266         {
2267           if (gtk_font_selection_style_visible(fontsel, font, style))
2268             {
2269               matched_style = TRUE;
2270               break;
2271             }
2272         }
2273       if (!matched_style)
2274         continue;
2275       
2276       /* Insert the font in the clist. */
2277       if ((i > 0 && font->family == font_info[i-1].family)
2278           || (i < nfonts - 1 && font->family == font_info[i+1].family))
2279         {
2280           sprintf(font_buffer, "%s (%s)", font->family,
2281                   fontsel_info->properties[FOUNDRY][font->foundry]);
2282           font_item = font_buffer;
2283           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist), &font_item);
2284         }
2285       else
2286         {
2287           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist),
2288                                  &font->family);
2289         }
2290       gtk_clist_set_row_data(GTK_CLIST(fontsel->font_clist), row,
2291                              GINT_TO_POINTER (i));
2292       if (fontsel->font_index == i)
2293         font_row = row;
2294     }
2295   gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
2296   
2297   /* If the currently-selected font isn't in the new list, reset the
2298      selection. */
2299   if (font_row == -1)
2300     {
2301       fontsel->font_index = -1;
2302       if (fontsel->font)
2303         gdk_font_unref(fontsel->font);
2304       fontsel->font = NULL;
2305       gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), "");
2306       gtk_clist_clear (GTK_CLIST(fontsel->font_style_clist));
2307       gtk_entry_set_text(GTK_ENTRY(fontsel->font_style_entry), "");
2308       return;
2309     }
2310
2311   gtk_clist_select_row(GTK_CLIST(fontsel->font_clist), font_row, 0);
2312   if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_clist), font_row)
2313       != GTK_VISIBILITY_FULL)
2314     gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), font_row, -1, 0.5, 0);
2315
2316   gtk_font_selection_show_available_styles (fontsel);
2317   gtk_font_selection_select_best_style (fontsel, FALSE);
2318 }
2319
2320
2321 /* Returns TRUE if the style is not currently filtered out. */
2322 static gboolean
2323 gtk_font_selection_style_visible(GtkFontSelection *fontsel,
2324                                  FontInfo         *font,
2325                                  gint              style_index)
2326 {
2327   FontStyle *styles, *style;
2328   GtkFontFilter *filter;
2329   guint16 value;
2330   gint prop, i, j;
2331   gboolean matched;
2332   gint type_filter;
2333
2334   styles = &fontsel_info->font_styles[font->style_index];
2335   style = &styles[style_index];
2336
2337   /* Check if font_type of style is filtered. */
2338   type_filter = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
2339     & fontsel->filters[GTK_FONT_FILTER_USER].font_type;
2340   if (!(style->flags & type_filter))
2341     return FALSE;
2342   
2343   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2344     {
2345       value = style->properties[prop];
2346       
2347       /* Check each filter. */
2348       for (i = 0; i < GTK_NUM_FONT_FILTERS; i++)
2349         {
2350           filter = &fontsel->filters[i];
2351
2352           if (filter->property_nfilters[prop] != 0)
2353             {
2354               matched = FALSE;
2355               for (j = 0; j < filter->property_nfilters[prop]; j++)
2356                 {
2357                   if (value == filter->property_filters[prop][j])
2358                     {
2359                       matched = TRUE;
2360                       break;
2361                     }
2362                 }
2363               if (!matched)
2364                 return FALSE;
2365             }
2366         }
2367     }
2368   return TRUE;
2369 }
2370
2371
2372 /* This resets the font type to bitmap or scalable, and sets all the filter
2373    clists to the wildcard '*' options. */
2374 static void
2375 gtk_font_selection_reset_filter      (GtkWidget      *w,
2376                                       GtkFontSelection *fontsel)
2377 {
2378   gint prop, base_font_type;
2379   
2380   fontsel->filters[GTK_FONT_FILTER_USER].font_type = GTK_FONT_BITMAP
2381     | GTK_FONT_SCALABLE;
2382
2383   base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type;
2384   if (base_font_type & GTK_FONT_BITMAP)
2385     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), TRUE);
2386   if (base_font_type & GTK_FONT_SCALABLE)
2387     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), TRUE);
2388   if (base_font_type & GTK_FONT_SCALABLE_BITMAP)
2389     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), FALSE);
2390   
2391   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2392     gtk_clist_select_row(GTK_CLIST(fontsel->filter_clists[prop]), 0, 0);
2393 }
2394
2395
2396 /* This clears the filter, showing all fonts and styles again. */
2397 static void
2398 gtk_font_selection_on_clear_filter     (GtkWidget      *w,
2399                                         GtkFontSelection *fontsel)
2400 {
2401   gtk_font_selection_clear_filter(fontsel);
2402 }
2403
2404
2405 /* This resets the user filter, showing all fonts and styles which pass the
2406    base filter again. Note that the font type is set to bitmaps and scalable
2407    fonts - scaled bitmaps are not shown. */
2408 static void
2409 gtk_font_selection_clear_filter     (GtkFontSelection *fontsel)
2410 {
2411   GtkFontFilter *filter;
2412   gint prop;
2413   
2414 #ifdef FONTSEL_DEBUG
2415   g_message("In clear_filter\n");
2416 #endif
2417   /* Clear the filter data. */
2418   filter = &fontsel->filters[GTK_FONT_FILTER_USER];
2419   filter->font_type = GTK_FONT_BITMAP | GTK_FONT_SCALABLE;
2420   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2421     {
2422       g_free(filter->property_filters[prop]);
2423       filter->property_filters[prop] = NULL;
2424       filter->property_nfilters[prop] = 0;
2425     }
2426   
2427   /* Select all the '*'s on the filter page. */
2428   gtk_font_selection_reset_filter(NULL, fontsel);
2429   
2430   /* Update the main notebook page. */
2431   gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
2432   gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font:"));
2433   
2434   gtk_font_selection_show_available_fonts(fontsel);
2435 }
2436   
2437   
2438 void
2439 gtk_font_selection_set_filter   (GtkFontSelection *fontsel,
2440                                  GtkFontFilterType filter_type,
2441                                  GtkFontType       font_type,
2442                                  gchar           **foundries,
2443                                  gchar           **weights,
2444                                  gchar           **slants,
2445                                  gchar           **setwidths,
2446                                  gchar           **spacings,
2447                                  gchar           **charsets)
2448 {
2449   GtkFontFilter *filter;
2450   gchar **filter_strings [GTK_NUM_FONT_PROPERTIES];
2451   gchar *filter_string;
2452   gchar *property, *property_alt;
2453   gint prop, nfilters, i, j, num_found;
2454   gint base_font_type, user_font_type;
2455   gboolean filter_set;
2456
2457   /* Put them into an array so we can use a simple loop. */
2458   filter_strings[FOUNDRY]   = foundries;
2459   filter_strings[WEIGHT]    = weights;
2460   filter_strings[SLANT]     = slants;
2461   filter_strings[SET_WIDTH] = setwidths;
2462   filter_strings[SPACING]   = spacings;
2463   filter_strings[CHARSET]   = charsets;
2464
2465   filter = &fontsel->filters[filter_type];
2466   filter->font_type = font_type;
2467       
2468   /* Free the old filter data, and insert the new. */
2469   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2470     {
2471       g_free(filter->property_filters[prop]);
2472       filter->property_filters[prop] = NULL;
2473       filter->property_nfilters[prop] = 0;
2474       
2475       if (filter_strings[prop])
2476         {
2477           /* Count how many items in the new array. */
2478           nfilters = 0;
2479           while (filter_strings[prop][nfilters])
2480             nfilters++;
2481
2482           filter->property_filters[prop] = g_new(guint16, nfilters);
2483           filter->property_nfilters[prop] = 0;
2484
2485           /* Now convert the strings to property indices. */
2486           num_found = 0;
2487           for (i = 0; i < nfilters; i++)
2488             {
2489               filter_string = filter_strings[prop][i];
2490               for (j = 0; j < fontsel_info->nproperties[prop]; j++)
2491                 {
2492                   property = _(fontsel_info->properties[prop][j]);
2493                   property_alt = NULL;
2494                   if (prop == SLANT)
2495                     property_alt = gtk_font_selection_expand_slant_code(property);
2496                   else if (prop == SPACING)
2497                     property_alt = gtk_font_selection_expand_spacing_code(property);
2498                   if (!strcmp (filter_string, property)
2499                       || (property_alt && !strcmp (filter_string, property_alt)))
2500                     {
2501                       filter->property_filters[prop][num_found] = j;
2502                       num_found++;
2503                       break;
2504                     }
2505                 }
2506             }
2507           filter->property_nfilters[prop] = num_found;
2508         }
2509     }
2510
2511   /* Now set the clists on the filter page according to the new filter. */
2512   gtk_font_selection_update_filter_lists (fontsel);
2513
2514   if (filter_type == GTK_FONT_FILTER_BASE)
2515     {
2516       user_font_type = fontsel->filters[GTK_FONT_FILTER_USER].font_type;
2517       if (font_type & GTK_FONT_BITMAP)
2518         {
2519           gtk_widget_set_sensitive (fontsel->type_bitmaps_button, TRUE);
2520           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), user_font_type & GTK_FONT_BITMAP);
2521         }
2522       else
2523         {
2524           gtk_widget_set_sensitive (fontsel->type_bitmaps_button, FALSE);
2525           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), FALSE);
2526         }
2527       
2528       if (font_type & GTK_FONT_SCALABLE)
2529         {
2530           gtk_widget_set_sensitive (fontsel->type_scalable_button, TRUE);
2531           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), user_font_type & GTK_FONT_SCALABLE);
2532         }
2533       else
2534         {
2535           gtk_widget_set_sensitive (fontsel->type_scalable_button, FALSE);
2536           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), FALSE);
2537         }
2538
2539       if (font_type & GTK_FONT_SCALABLE_BITMAP)
2540         {
2541           gtk_widget_set_sensitive (fontsel->type_scaled_bitmaps_button, TRUE);
2542           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), user_font_type & GTK_FONT_SCALABLE_BITMAP);
2543         }
2544       else
2545         {
2546           gtk_widget_set_sensitive (fontsel->type_scaled_bitmaps_button, FALSE);
2547           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), FALSE);
2548         }
2549     }
2550   else
2551     {
2552       base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type;
2553       if (base_font_type & GTK_FONT_BITMAP)
2554         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), font_type & GTK_FONT_BITMAP);
2555
2556       if (base_font_type & GTK_FONT_SCALABLE)
2557         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), font_type & GTK_FONT_SCALABLE);
2558
2559       if (base_font_type & GTK_FONT_SCALABLE_BITMAP)
2560         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), font_type & GTK_FONT_SCALABLE_BITMAP);
2561
2562       /* If the user filter is not the default, make the 'Reset Filter' button
2563          sensitive. */
2564       filter_set = FALSE;
2565       if (font_type != (GTK_FONT_BITMAP | GTK_FONT_SCALABLE))
2566         filter_set = TRUE;
2567       for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2568         {
2569           if (filter->property_nfilters[prop] != 0)
2570             filter_set = TRUE;
2571         }
2572       if (filter_set)
2573         gtk_widget_set_sensitive (fontsel->filter_button, TRUE);
2574     }
2575
2576   gtk_font_selection_show_available_fonts (fontsel);
2577 }
2578
2579
2580 /* This sets the colour of each property in the filter clists according to
2581    the base filter. i.e. Filtered properties are shown as insensitive. */
2582 static void
2583 gtk_font_selection_update_filter_lists (GtkFontSelection *fontsel)
2584 {
2585   GtkWidget *clist;
2586   GdkColor *inactive_fg, *inactive_bg, *fg, *bg;
2587   gint prop, row, index;
2588
2589   /* We have to make sure the clist is realized to use the colours. */
2590   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2591     {
2592       clist = fontsel->filter_clists[prop];
2593       gtk_widget_realize (clist);
2594       inactive_fg = &clist->style->fg[GTK_STATE_INSENSITIVE];
2595       inactive_bg = &clist->style->bg[GTK_STATE_INSENSITIVE];
2596       for (row = 1; row < GTK_CLIST(clist)->rows; row++)
2597         {
2598           index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(clist),
2599                                                            row));
2600           /* Set the colour according to the base filter. */
2601           if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_BASE,
2602                                                prop, index) == NOT_FILTERED)
2603             {
2604               fg = inactive_fg;
2605               bg = inactive_bg;
2606             }
2607           else
2608             {
2609               fg = NULL;
2610               bg = NULL;
2611             }
2612           gtk_clist_set_foreground(GTK_CLIST(clist), row, fg);
2613           gtk_clist_set_background(GTK_CLIST(clist), row, bg);
2614
2615           /* Set the selection state according to the user filter. */
2616           if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_USER,
2617                                                prop, index) == FILTERED
2618               && fg == NULL)
2619             gtk_clist_select_row (GTK_CLIST (clist), row, 0);
2620           else
2621             gtk_clist_unselect_row (GTK_CLIST (clist), row, 0);
2622         }
2623     }
2624 }
2625
2626
2627 /* Returns whether a property value is in the filter or not, or if the
2628    property has no filter set. */
2629 static GtkFontPropertyFilterState
2630 gtk_font_selection_filter_state (GtkFontSelection *fontsel,
2631                                  GtkFontFilterType filter_type,
2632                                  gint             property,
2633                                  gint             index)
2634 {
2635   GtkFontFilter *filter;
2636   gint i;
2637
2638   filter = &fontsel->filters[filter_type];
2639   if (filter->property_nfilters[property] == 0)
2640     return NOT_SET;
2641
2642   for (i = 0; i < filter->property_nfilters[property]; i++)
2643     {
2644       if (filter->property_filters[property][i] == index)
2645         return FILTERED;
2646     }
2647   return NOT_FILTERED;
2648 }
2649
2650
2651 #if GDK_WINDOWING == GDK_WINDOWING_WIN32
2652
2653 static gint num_fonts;
2654 static gint font_names_size;
2655 static gchar **xfontnames;
2656 static HDC hdc;
2657
2658 static char *
2659 logfont_to_xlfd (const LOGFONT *lfp,
2660                  int size,
2661                  int res,
2662                  int avg_width)
2663 {
2664   const gchar *weight;
2665   const gchar *registry, *encoding;
2666   int point_size;
2667   static int logpixelsy = 0;
2668   gchar facename[LF_FACESIZE*3];
2669   gchar *p, *q;
2670
2671   if (logpixelsy == 0)
2672     {
2673       HDC hdc = GetDC (NULL);
2674       logpixelsy = GetDeviceCaps (hdc, LOGPIXELSY);
2675       ReleaseDC (NULL, hdc);
2676     }
2677
2678   /* Don't use _() here, only N_(), the actual translation is done elsewhere */   
2679   if (lfp->lfWeight >= FW_HEAVY)
2680     weight = N_("heavy");
2681   else if (lfp->lfWeight >= FW_EXTRABOLD)
2682     weight = N_("extrabold");
2683   else if (lfp->lfWeight >= FW_BOLD)
2684     weight = N_("bold");
2685 #ifdef FW_DEMIBOLD
2686   else if (lfp->lfWeight >= FW_DEMIBOLD)
2687     weight = N_("demibold");
2688 #endif
2689   else if (lfp->lfWeight >= FW_MEDIUM)
2690     weight = N_("medium");
2691   else if (lfp->lfWeight >= FW_NORMAL)
2692     weight = N_("normal");
2693   else if (lfp->lfWeight >= FW_LIGHT)
2694     weight = N_("light");
2695   else if (lfp->lfWeight >= FW_EXTRALIGHT)
2696     weight = N_("extralight");
2697   else if (lfp->lfWeight >= FW_THIN)
2698     weight = N_("thin");
2699   else
2700     weight = N_("regular");
2701
2702   if (lfp->lfCharSet == ANSI_CHARSET)
2703     {
2704       registry = "iso8859";
2705       encoding = "1";
2706     }
2707   else
2708     {
2709       registry = "windows";
2710       if (lfp->lfCharSet == DEFAULT_CHARSET)
2711         encoding = "default";
2712       else if (lfp->lfCharSet == SYMBOL_CHARSET)
2713         encoding = "symbol";
2714       else if (lfp->lfCharSet == SHIFTJIS_CHARSET)
2715         encoding = "shiftjis";
2716       else if (lfp->lfCharSet == GB2312_CHARSET)
2717         encoding = "gb2312";
2718       else if (lfp->lfCharSet == HANGEUL_CHARSET)
2719         encoding = "hangeul";
2720       else if (lfp->lfCharSet == CHINESEBIG5_CHARSET)
2721         encoding = "chinesebig5";
2722       else if (lfp->lfCharSet == OEM_CHARSET)
2723         encoding = "oem";
2724 #ifdef JOHAB_CHARSET
2725       else if (lfp->lfCharSet == JOHAB_CHARSET)
2726         encoding = "johab";
2727 #endif
2728       else if (lfp->lfCharSet == HEBREW_CHARSET)
2729         encoding = "hebrew";
2730       else if (lfp->lfCharSet == ARABIC_CHARSET)
2731         encoding = "arabic";
2732       else if (lfp->lfCharSet == GREEK_CHARSET)
2733         encoding = "greek";
2734       else if (lfp->lfCharSet == TURKISH_CHARSET)
2735         encoding = "turkish";
2736       else if (lfp->lfCharSet == THAI_CHARSET)
2737         encoding = "thai";
2738       else if (lfp->lfCharSet == EASTEUROPE_CHARSET)
2739         encoding = "easteurope";
2740       else if (lfp->lfCharSet == RUSSIAN_CHARSET)
2741         encoding = "russian";
2742       else if (lfp->lfCharSet == MAC_CHARSET)
2743         encoding = "mac";
2744       else if (lfp->lfCharSet == BALTIC_CHARSET)
2745         encoding = "baltic";
2746       else
2747         encoding = "unknown";
2748     }
2749   
2750   point_size = (int) (((double) size/logpixelsy) * 720.);
2751
2752   if (res == -1)
2753     res = logpixelsy;
2754
2755   /* Replace illegal characters with hex escapes. */
2756   p = facename;
2757   q = lfp->lfFaceName;
2758   while (*q)
2759     {
2760       if (*q == '-' || *q == '*' || *q == '?' || *q == '%')
2761         p += sprintf (p, "%%%.02x", *q);
2762       else
2763         *p++ = *q;
2764       q++;
2765     }
2766   *p = '\0';
2767
2768   return  g_strdup_printf
2769     ("-%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-%d-%s-%s",
2770      "unknown", 
2771      facename,
2772      weight,
2773      (lfp->lfItalic ?
2774       ((lfp->lfPitchAndFamily & 0xF0) == FF_ROMAN
2775        || (lfp->lfPitchAndFamily & 0xF0) == FF_SCRIPT ?
2776        "i" : "o") : "r"),
2777      "normal",
2778      "",
2779      size,
2780      point_size,
2781      res,
2782      res,
2783      ((lfp->lfPitchAndFamily & 0x03) == FIXED_PITCH ? "m" : "p"),
2784      avg_width,
2785      registry, encoding);
2786 }
2787
2788 int CALLBACK
2789 InnerEnumFontFamExProc (const LOGFONT *lfp,
2790                         const TEXTMETRIC *metrics,
2791                         DWORD fontType,
2792                         LPARAM lParam)
2793 {
2794   int size;
2795
2796   if (fontType == TRUETYPE_FONTTYPE)
2797     {
2798       size = 0;
2799     }
2800   else
2801     {
2802       size = lfp->lfHeight;
2803     }
2804
2805   num_fonts++;
2806   if (num_fonts == font_names_size)
2807     {
2808       font_names_size *= 2;
2809       xfontnames = g_realloc (xfontnames, font_names_size * sizeof (gchar *));
2810     }
2811   xfontnames[num_fonts-1] =
2812     logfont_to_xlfd (lfp, size, 0, 0);
2813   return 1;
2814 }
2815
2816 int CALLBACK
2817 EnumFontFamExProc (const LOGFONT *lfp,
2818                    const TEXTMETRIC *metrics,
2819                    DWORD fontType,
2820                    LPARAM lParam)
2821 {
2822   if (fontType == TRUETYPE_FONTTYPE)
2823     {
2824       LOGFONT lf = *lfp;
2825       lf.lfPitchAndFamily = 0;
2826       EnumFontFamiliesEx (hdc, &lf, InnerEnumFontFamExProc, 0, 0);
2827     }
2828   else
2829     InnerEnumFontFamExProc (lfp, metrics, fontType, lParam);
2830   return 1;
2831 }
2832
2833 #endif
2834
2835 /*****************************************************************************
2836  * These functions all deal with creating the main class arrays containing
2837  * the data about all available fonts.
2838  *****************************************************************************/
2839 static void
2840 gtk_font_selection_get_fonts (void)
2841 {
2842 #if GDK_WINDOWING == GDK_WINDOWING_X11
2843   gchar **xfontnames;
2844   gint num_fonts;
2845 #elif GDK_WINDOWING == GDK_WINDOWING_WIN32
2846   LOGFONT logfont;
2847 #else
2848   gint num_fonts = 0;
2849   gchar **xfontnames = NULL;
2850 #endif
2851   GSList **fontnames;
2852   gchar *fontname;
2853   GSList * temp_list;
2854   gint i, prop, style, size;
2855   gint npixel_sizes = 0, npoint_sizes = 0;
2856   FontInfo *font;
2857   FontStyle *current_style, *prev_style, *tmp_style;
2858   gboolean matched_style, found_size;
2859   gint pixels, points, res_x, res_y;
2860   gchar field_buffer[XLFD_MAX_FIELD_LEN];
2861   gchar *field;
2862   guint8 flags;
2863   guint16 *pixel_sizes, *point_sizes, *tmp_sizes;
2864   
2865   fontsel_info = g_new (GtkFontSelInfo, 1);
2866
2867 #if GDK_WINDOWING == GDK_WINDOWING_X11
2868   /* Get a maximum of MAX_FONTS fontnames from the X server.
2869      Use "-*" as the pattern rather than "-*-*-*-*-*-*-*-*-*-*-*-*-*-*" since
2870      the latter may result in fonts being returned which don't actually exist.
2871      xlsfonts also uses "*" so I think it's OK. "-*" gets rid of aliases. */
2872   xfontnames = XListFonts (GDK_DISPLAY(), "-*", MAX_FONTS, &num_fonts);
2873   /* Output a warning if we actually get MAX_FONTS fonts. */
2874   if (num_fonts == MAX_FONTS)
2875     g_warning(_("MAX_FONTS exceeded. Some fonts may be missing."));
2876   
2877 #elif GDK_WINDOWING == GDK_WINDOWING_WIN32
2878   num_fonts = 0;
2879   hdc = GetDC (NULL);
2880   font_names_size = 100;
2881   xfontnames = g_new (gchar *, font_names_size);
2882   logfont.lfCharSet = DEFAULT_CHARSET;
2883   logfont.lfFaceName[0] = '\0';
2884   logfont.lfPitchAndFamily = 0;
2885   EnumFontFamiliesEx (hdc, &logfont, EnumFontFamExProc, 0, 0);
2886   ReleaseDC (NULL, hdc);
2887 #endif
2888
2889   /* The maximum size of all these tables is the number of font names
2890      returned. We realloc them later when we know exactly how many
2891      unique entries there are. */
2892   fontsel_info->font_info = g_new (FontInfo, num_fonts);
2893   fontsel_info->font_styles = g_new (FontStyle, num_fonts);
2894   fontsel_info->pixel_sizes = g_new (guint16, num_fonts);
2895   fontsel_info->point_sizes = g_new (guint16, num_fonts);
2896   
2897   fontnames = g_new (GSList*, num_fonts);
2898   
2899   /* Create the initial arrays for the property value strings, though they
2900      may be realloc'ed later. Put the wildcard '*' in the first elements. */
2901   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2902     {
2903       fontsel_info->properties[prop] = g_new(gchar*, PROPERTY_ARRAY_INCREMENT);
2904       fontsel_info->space_allocated[prop] = PROPERTY_ARRAY_INCREMENT;
2905       fontsel_info->nproperties[prop] = 1;
2906       fontsel_info->properties[prop][0] = "*";
2907     }
2908   
2909   
2910   /* Insert the font families into the main table, sorted by family and
2911      foundry (fonts with different foundries are placed in seaparate FontInfos.
2912      All fontnames in each family + foundry are placed into the fontnames
2913      array of lists. */
2914   fontsel_info->nfonts = 0;
2915   for (i = 0; i < num_fonts; i++)
2916     {
2917 #ifdef FONTSEL_DEBUG
2918       g_message("%s\n", xfontnames[i]);
2919 #endif
2920       if (gtk_font_selection_is_xlfd_font_name (xfontnames[i]))
2921         gtk_font_selection_insert_font (fontnames, &fontsel_info->nfonts, xfontnames[i]);
2922       else
2923         {
2924 #ifdef FONTSEL_DEBUG
2925           g_warning("Skipping invalid font: %s", xfontnames[i]);
2926 #endif
2927         }
2928     }
2929   
2930   
2931   /* Since many font names will be in the same FontInfo not all of the
2932      allocated FontInfo table will be used, so we will now reallocate it
2933      with the real size. */
2934   fontsel_info->font_info = g_realloc(fontsel_info->font_info,
2935                                       sizeof(FontInfo) * fontsel_info->nfonts);
2936   
2937   
2938   /* Now we work out which choices of weight/slant etc. are valid for each
2939      font. */
2940   fontsel_info->nstyles = 0;
2941   current_style = fontsel_info->font_styles;
2942   for (i = 0; i < fontsel_info->nfonts; i++)
2943     {
2944       font = &fontsel_info->font_info[i];
2945       
2946       /* Use the next free position in the styles array. */
2947       font->style_index = fontsel_info->nstyles;
2948       
2949       /* Now step through each of the fontnames with this family, and create
2950          a style for each fontname. Each style contains the index into the
2951          weights/slants etc. arrays, and a number of pixel/point sizes. */
2952       style = 0;
2953       temp_list = fontnames[i];
2954       while (temp_list)
2955         {
2956           fontname = temp_list->data;
2957           temp_list = temp_list->next;
2958           
2959           for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2960             {
2961               current_style->properties[prop]
2962                 = gtk_font_selection_insert_field (fontname, prop);
2963             }
2964           current_style->pixel_sizes_index = npixel_sizes;
2965           current_style->npixel_sizes = 0;
2966           current_style->point_sizes_index = npoint_sizes;
2967           current_style->npoint_sizes = 0;
2968           current_style->flags = 0;
2969           
2970           
2971           field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
2972                                                      field_buffer);
2973           pixels = atoi(field);
2974           
2975           field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
2976                                                      field_buffer);
2977           points = atoi(field);
2978           
2979           field = gtk_font_selection_get_xlfd_field (fontname,
2980                                                      XLFD_RESOLUTION_X,
2981                                                      field_buffer);
2982           res_x = atoi(field);
2983           
2984           field = gtk_font_selection_get_xlfd_field (fontname,
2985                                                      XLFD_RESOLUTION_Y,
2986                                                      field_buffer);
2987           res_y = atoi(field);
2988           
2989           if (pixels == 0 && points == 0)
2990             {
2991               if (res_x == 0 && res_y == 0)
2992                 flags = GTK_FONT_SCALABLE;
2993               else
2994                 flags = GTK_FONT_SCALABLE_BITMAP;
2995             }
2996           else
2997             flags = GTK_FONT_BITMAP;
2998           
2999           /* Now we check to make sure that the style is unique. If it isn't
3000              we forget it. */
3001           prev_style = fontsel_info->font_styles + font->style_index;
3002           matched_style = FALSE;
3003           while (prev_style < current_style)
3004             {
3005               matched_style = TRUE;
3006               for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
3007                 {
3008                   if (prev_style->properties[prop]
3009                       != current_style->properties[prop])
3010                     {
3011                       matched_style = FALSE;
3012                       break;
3013                     }
3014                 }
3015               if (matched_style)
3016                 break;
3017               prev_style++;
3018             }
3019           
3020           /* If we matched an existing style, we need to add the pixels &
3021              point sizes to the style. If not, we insert the pixel & point
3022              sizes into our new style. Note that we don't add sizes for
3023              scalable fonts. */
3024           if (matched_style)
3025             {
3026               prev_style->flags |= flags;
3027               if (flags == GTK_FONT_BITMAP)
3028                 {
3029                   pixel_sizes = fontsel_info->pixel_sizes
3030                     + prev_style->pixel_sizes_index;
3031                   found_size = FALSE;
3032                   for (size = 0; size < prev_style->npixel_sizes; size++)
3033                     {
3034                       if (pixels == *pixel_sizes)
3035                         {
3036                           found_size = TRUE;
3037                           break;
3038                         }
3039                       else if (pixels < *pixel_sizes)
3040                         break;
3041                       pixel_sizes++;
3042                     }
3043                   /* We need to move all the following pixel sizes up, and also
3044                      update the indexes of any following styles. */
3045                   if (!found_size)
3046                     {
3047                       for (tmp_sizes = fontsel_info->pixel_sizes + npixel_sizes;
3048                            tmp_sizes > pixel_sizes; tmp_sizes--)
3049                         *tmp_sizes = *(tmp_sizes - 1);
3050                       
3051                       *pixel_sizes = pixels;
3052                       npixel_sizes++;
3053                       prev_style->npixel_sizes++;
3054                       
3055                       tmp_style = prev_style + 1;
3056                       while (tmp_style < current_style)
3057                         {
3058                           tmp_style->pixel_sizes_index++;
3059                           tmp_style++;
3060                         }
3061                     }
3062                   
3063                   point_sizes = fontsel_info->point_sizes
3064                     + prev_style->point_sizes_index;
3065                   found_size = FALSE;
3066                   for (size = 0; size < prev_style->npoint_sizes; size++)
3067                     {
3068                       if (points == *point_sizes)
3069                         {
3070                           found_size = TRUE;
3071                           break;
3072                         }
3073                       else if (points < *point_sizes)
3074                         break;
3075                       point_sizes++;
3076                     }
3077                   /* We need to move all the following point sizes up, and also
3078                      update the indexes of any following styles. */
3079                   if (!found_size)
3080                     {
3081                       for (tmp_sizes = fontsel_info->point_sizes + npoint_sizes;
3082                            tmp_sizes > point_sizes; tmp_sizes--)
3083                         *tmp_sizes = *(tmp_sizes - 1);
3084                       
3085                       *point_sizes = points;
3086                       npoint_sizes++;
3087                       prev_style->npoint_sizes++;
3088                       
3089                       tmp_style = prev_style + 1;
3090                       while (tmp_style < current_style)
3091                         {
3092                           tmp_style->point_sizes_index++;
3093                           tmp_style++;
3094                         }
3095                     }
3096                 }
3097             }
3098           else
3099             {
3100               current_style->flags = flags;
3101               if (flags == GTK_FONT_BITMAP)
3102                 {
3103                   fontsel_info->pixel_sizes[npixel_sizes++] = pixels;
3104                   current_style->npixel_sizes = 1;
3105                   fontsel_info->point_sizes[npoint_sizes++] = points;
3106                   current_style->npoint_sizes = 1;
3107                 }
3108               style++;
3109               fontsel_info->nstyles++;
3110               current_style++;
3111             }
3112         }
3113       g_slist_free(fontnames[i]);
3114       
3115       /* Set nstyles to the real value, minus duplicated fontnames.
3116          Note that we aren't using all the allocated memory if fontnames are
3117          duplicated. */
3118       font->nstyles = style;
3119     }
3120   
3121   /* Since some repeated styles may be skipped we won't have used all the
3122      allocated space, so we will now reallocate it with the real size. */
3123   fontsel_info->font_styles = g_realloc(fontsel_info->font_styles,
3124                                         sizeof(FontStyle) * fontsel_info->nstyles);
3125   fontsel_info->pixel_sizes = g_realloc(fontsel_info->pixel_sizes,
3126                                         sizeof(guint16) * npixel_sizes);
3127   fontsel_info->point_sizes = g_realloc(fontsel_info->point_sizes,
3128                                         sizeof(guint16) * npoint_sizes);
3129   g_free(fontnames);
3130
3131 #if GDK_WINDOWING == GDK_WINDOWING_X11
3132   XFreeFontNames (xfontnames);
3133 #elif GDK_WINDOWING == GDK_WINDOWING_WIN32
3134   for (i = 0; i < num_fonts; i++)
3135     g_free (xfontnames[i]);
3136   g_free (xfontnames);
3137 #endif
3138   
3139   /* Debugging Output */
3140   /* This outputs all FontInfos. */
3141 #ifdef FONTSEL_DEBUG
3142   g_message("\n\n Font Family           Weight    Slant     Set Width Spacing   Charset\n\n");
3143   for (i = 0; i < fontsel_info->nfonts; i++)
3144     {
3145       FontInfo *font = &fontsel_info->font_info[i];
3146       FontStyle *styles = fontsel_info->font_styles + font->style_index;
3147       for (style = 0; style < font->nstyles; style++)
3148         {
3149           g_message("%5i %-16.16s ", i, font->family);
3150           for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
3151             g_message("%-9.9s ",
3152                       fontsel_info->properties[prop][styles->properties[prop]]);
3153           g_message("\n      ");
3154           
3155           if (styles->flags & GTK_FONT_BITMAP)
3156             g_message("Bitmapped font  ");
3157           if (styles->flags & GTK_FONT_SCALABLE)
3158             g_message("Scalable font  ");
3159           if (styles->flags & GTK_FONT_SCALABLE_BITMAP)
3160             g_message("Scalable-Bitmapped font  ");
3161           g_message("\n");
3162           
3163           if (styles->npixel_sizes)
3164             {
3165               g_message("      Pixel sizes: ");
3166               tmp_sizes = fontsel_info->pixel_sizes + styles->pixel_sizes_index;
3167               for (size = 0; size < styles->npixel_sizes; size++)
3168                 g_message("%i ", *tmp_sizes++);
3169               g_message("\n");
3170             }
3171           
3172           if (styles->npoint_sizes)
3173             {
3174               g_message("      Point sizes: ");
3175               tmp_sizes = fontsel_info->point_sizes + styles->point_sizes_index;
3176               for (size = 0; size < styles->npoint_sizes; size++)
3177                 g_message("%i ", *tmp_sizes++);
3178               g_message("\n");
3179             }
3180           
3181           g_message("\n");
3182           styles++;
3183         }
3184     }
3185   /* This outputs all available properties. */
3186   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
3187     {
3188       g_message("Property: %s\n", xlfd_field_names[xlfd_index[prop]]);
3189       for (i = 0; i < fontsel_info->nproperties[prop]; i++)
3190         g_message("  %s\n", fontsel_info->properties[prop][i]);
3191     }
3192 #endif
3193 }
3194
3195 /* This inserts the given fontname into the FontInfo table.
3196    If a FontInfo already exists with the same family and foundry, then the
3197    fontname is added to the FontInfos list of fontnames, else a new FontInfo
3198    is created and inserted in alphabetical order in the table. */
3199 static void
3200 gtk_font_selection_insert_font (GSList                *fontnames[],
3201                                 gint                  *ntable,
3202                                 gchar                 *fontname)
3203 {
3204   FontInfo *table;
3205   FontInfo temp_info;
3206   GSList *temp_fontname;
3207   gchar *family;
3208   gboolean family_exists = FALSE;
3209   gint foundry;
3210   gint lower, upper;
3211   gint middle, cmp;
3212   gchar family_buffer[XLFD_MAX_FIELD_LEN];
3213   
3214   table = fontsel_info->font_info;
3215   
3216   /* insert a fontname into a table */
3217   family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
3218                                               family_buffer);
3219   if (!family)
3220     return;
3221   
3222   foundry = gtk_font_selection_insert_field (fontname, FOUNDRY);
3223   
3224   lower = 0;
3225   if (*ntable > 0)
3226     {
3227       /* Do a binary search to determine if we have already encountered
3228        *  a font with this family & foundry. */
3229       upper = *ntable;
3230       while (lower < upper)
3231         {
3232           middle = (lower + upper) >> 1;
3233           
3234           cmp = strcmp (family, table[middle].family);
3235           /* If the family matches we sort by the foundry. */
3236           if (cmp == 0)
3237             {
3238               family_exists = TRUE;
3239               family = table[middle].family;
3240               cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
3241                            fontsel_info->properties[FOUNDRY][table[middle].foundry]);
3242             }
3243           
3244           if (cmp == 0)
3245             {
3246               fontnames[middle] = g_slist_prepend (fontnames[middle],
3247                                                    fontname);
3248               return;
3249             }
3250           else if (cmp < 0)
3251             upper = middle;
3252           else
3253             lower = middle+1;
3254         }
3255     }
3256   
3257   /* Add another entry to the table for this new font family */
3258   temp_info.family = family_exists ? family : g_strdup(family);
3259   temp_info.foundry = foundry;
3260   temp_fontname = g_slist_prepend (NULL, fontname);
3261   
3262   (*ntable)++;
3263   
3264   /* Quickly insert the entry into the table in sorted order
3265    *  using a modification of insertion sort and the knowledge
3266    *  that the entries proper position in the table was determined
3267    *  above in the binary search and is contained in the "lower"
3268    *  variable. */
3269   if (*ntable > 1)
3270     {
3271       upper = *ntable - 1;
3272       while (lower != upper)
3273         {
3274           table[upper] = table[upper-1];
3275           fontnames[upper] = fontnames[upper-1];
3276           upper--;
3277         }
3278     }
3279   table[lower] = temp_info;
3280   fontnames[lower] = temp_fontname;
3281 }
3282
3283
3284 /* This checks that the specified field of the given fontname is in the
3285    appropriate properties array. If not it is added. Thus eventually we get
3286    arrays of all possible weights/slants etc. It returns the array index. */
3287 static gint
3288 gtk_font_selection_insert_field (gchar                 *fontname,
3289                                  gint                   prop)
3290 {
3291   gchar field_buffer[XLFD_MAX_FIELD_LEN];
3292   gchar *field;
3293   guint16 index;
3294   
3295   field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
3296                                              field_buffer);
3297   if (!field)
3298     return 0;
3299   
3300   /* If the field is already in the array just return its index. */
3301   for (index = 0; index < fontsel_info->nproperties[prop]; index++)
3302     if (!strcmp(field, fontsel_info->properties[prop][index]))
3303       return index;
3304   
3305   /* Make sure we have enough space to add the field. */
3306   if (fontsel_info->nproperties[prop] == fontsel_info->space_allocated[prop])
3307     {
3308       fontsel_info->space_allocated[prop] += PROPERTY_ARRAY_INCREMENT;
3309       fontsel_info->properties[prop] = g_realloc(fontsel_info->properties[prop],
3310                                                  sizeof(gchar*)
3311                                                  * fontsel_info->space_allocated[prop]);
3312     }
3313   
3314   /* Add the new field. */
3315   index = fontsel_info->nproperties[prop];
3316   fontsel_info->properties[prop][index] = g_strdup(field);
3317   fontsel_info->nproperties[prop]++;
3318   return index;
3319 }
3320
3321
3322 /*****************************************************************************
3323  * These functions are the main public interface for getting/setting the font.
3324  *****************************************************************************/
3325
3326 GdkFont*
3327 gtk_font_selection_get_font (GtkFontSelection *fontsel)
3328 {
3329   g_return_val_if_fail (fontsel != NULL, NULL);
3330   return fontsel->font;
3331 }
3332
3333
3334 gchar *
3335 gtk_font_selection_get_font_name (GtkFontSelection *fontsel)
3336 {
3337   FontInfo *font;
3338   gchar *family_str, *foundry_str;
3339   gchar *property_str[GTK_NUM_STYLE_PROPERTIES];
3340   gint prop;
3341   
3342   g_return_val_if_fail (fontsel != NULL, NULL);
3343   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), NULL);
3344   
3345   /* If no family has been selected return NULL. */
3346   if (fontsel->font_index == -1)
3347     return NULL;
3348   
3349   font = &fontsel_info->font_info[fontsel->font_index];
3350   family_str = font->family;
3351   foundry_str = fontsel_info->properties[FOUNDRY][font->foundry];
3352   
3353   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
3354     {
3355       property_str[prop]
3356         = fontsel_info->properties[prop][fontsel->property_values[prop]];
3357       if (strcmp (property_str[prop], "(nil)") == 0)
3358         property_str[prop] = "";
3359     }
3360   
3361   return gtk_font_selection_create_xlfd (fontsel->size,
3362                                          fontsel->metric,
3363                                          foundry_str,
3364                                          family_str,
3365                                          property_str[WEIGHT],
3366                                          property_str[SLANT],
3367                                          property_str[SET_WIDTH],
3368                                          property_str[SPACING],
3369                                          property_str[CHARSET]);
3370 }
3371
3372
3373 /* This sets the current font, selecting the appropriate clist rows.
3374    First we check the fontname is valid and try to find the font family
3375    - i.e. the name in the main list. If we can't find that, then just return.
3376    Next we try to set each of the properties according to the fontname.
3377    Finally we select the font family & style in the clists. */
3378 gboolean
3379 gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
3380                                   const gchar      *fontname)
3381 {
3382   gchar *family, *field;
3383   gint index, prop, size;
3384   guint16 foundry, value;
3385   gchar family_buffer[XLFD_MAX_FIELD_LEN];
3386   gchar field_buffer[XLFD_MAX_FIELD_LEN];
3387   gchar buffer[16];
3388   
3389   g_return_val_if_fail (fontsel != NULL, FALSE);
3390   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), FALSE);
3391   g_return_val_if_fail (fontname != NULL, FALSE);
3392   
3393   /* Check it is a valid fontname. */
3394   if (!gtk_font_selection_is_xlfd_font_name(fontname))
3395     return FALSE;
3396   
3397   family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
3398                                               family_buffer);
3399   if (!family)
3400     return FALSE;
3401   
3402   field = gtk_font_selection_get_xlfd_field (fontname, XLFD_FOUNDRY,
3403                                              field_buffer);
3404   foundry = gtk_font_selection_field_to_index (fontsel_info->properties[FOUNDRY],
3405                                                fontsel_info->nproperties[FOUNDRY],
3406                                                field);
3407   
3408   index = gtk_font_selection_find_font(fontsel, family, foundry);
3409   if (index == -1)
3410     return FALSE;
3411   
3412   /* Convert the property fields into indices and set them. */
3413   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++) 
3414     {
3415       field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
3416                                                  field_buffer);
3417       value = gtk_font_selection_field_to_index (fontsel_info->properties[prop],
3418                                                  fontsel_info->nproperties[prop],
3419                                                  field);
3420       fontsel->property_values[prop] = value;
3421     }
3422   
3423   field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
3424                                              field_buffer);
3425   size = atoi(field);
3426   if (size > 0)
3427     {
3428       if (size < 20)
3429         size = 20;
3430       fontsel->size = fontsel->selected_size = size;
3431       fontsel->metric = GTK_FONT_METRIC_POINTS;
3432       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->points_button),
3433                                   TRUE);
3434       if (size % 10 == 0)
3435         sprintf (buffer, "%i", size / 10);
3436       else
3437         sprintf (buffer, "%i.%i", size / 10, size % 10);
3438     }
3439   else
3440     {
3441       field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
3442                                                  field_buffer);
3443       size = atoi(field);
3444       if (size < 2)
3445         size = 2;
3446       fontsel->size = fontsel->selected_size = size;
3447       fontsel->metric = GTK_FONT_METRIC_PIXELS;
3448       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->pixels_button),
3449                                   TRUE);
3450       sprintf (buffer, "%i", size);
3451     }
3452   gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
3453   
3454   /* Clear any current filter. */
3455   gtk_font_selection_clear_filter(fontsel);
3456   
3457   /* Now find the best style match. */
3458   fontsel->font_index = index;
3459   gtk_clist_select_row(GTK_CLIST(fontsel->font_clist), index, 0);
3460   if (GTK_WIDGET_MAPPED (fontsel->font_clist))
3461     gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
3462   
3463   gtk_font_selection_show_available_styles (fontsel);
3464   /* This will load the font. */
3465   gtk_font_selection_select_best_style (fontsel, FALSE);
3466   
3467   return TRUE;
3468 }
3469
3470
3471 /* Returns the index of the given family, or -1 if not found */
3472 static gint
3473 gtk_font_selection_find_font (GtkFontSelection *fontsel,
3474                               gchar            *family,
3475                               guint16           foundry)
3476 {
3477   FontInfo *font_info;
3478   gint lower, upper, middle = -1, cmp, nfonts;
3479   gint found_family = -1;
3480   
3481   font_info = fontsel_info->font_info;
3482   nfonts = fontsel_info->nfonts;
3483   if (nfonts == 0)
3484     return -1;
3485   
3486   /* Do a binary search to find the font family. */
3487   lower = 0;
3488   upper = nfonts;
3489   while (lower < upper)
3490     {
3491       middle = (lower + upper) >> 1;
3492       
3493       cmp = strcmp (family, font_info[middle].family);
3494       if (cmp == 0)
3495         {
3496           found_family = middle;
3497           cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
3498                        fontsel_info->properties[FOUNDRY][font_info[middle].foundry]);
3499         }
3500
3501       if (cmp == 0)
3502         return middle;
3503       else if (cmp < 0)
3504         upper = middle;
3505       else if (cmp > 0)
3506         lower = middle+1;
3507     }
3508   
3509   /* We couldn't find the family and foundry, but we may have just found the
3510      family, so we return that. */
3511   return found_family;
3512 }
3513
3514
3515 /* This returns the text in the preview entry. You should copy the returned
3516    text if you need it. */
3517 gchar*
3518 gtk_font_selection_get_preview_text  (GtkFontSelection *fontsel)
3519 {
3520   return gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
3521 }
3522
3523
3524 /* This sets the text in the preview entry. */
3525 void
3526 gtk_font_selection_set_preview_text  (GtkFontSelection *fontsel,
3527                                       const gchar         *text)
3528 {
3529   gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), text);
3530 }
3531
3532
3533 /*****************************************************************************
3534  * These functions all deal with X Logical Font Description (XLFD) fontnames.
3535  * See the freely available documentation about this.
3536  *****************************************************************************/
3537
3538 /*
3539  * Returns TRUE if the fontname is a valid XLFD.
3540  * (It just checks if the number of dashes is 14, and that each
3541  * field < XLFD_MAX_FIELD_LEN  characters long - that's not in the XLFD but it
3542  * makes it easier for me).
3543  */
3544 static gboolean
3545 gtk_font_selection_is_xlfd_font_name (const gchar *fontname)
3546 {
3547   gint i = 0;
3548   gint field_len = 0;
3549   
3550   while (*fontname)
3551     {
3552       if (*fontname++ == '-')
3553         {
3554           if (field_len > XLFD_MAX_FIELD_LEN) return FALSE;
3555           field_len = 0;
3556           i++;
3557         }
3558       else
3559         field_len++;
3560     }
3561   
3562   return (i == 14) ? TRUE : FALSE;
3563 }
3564
3565 /*
3566  * This fills the buffer with the specified field from the X Logical Font
3567  * Description name, and returns it. If fontname is NULL or the field is
3568  * longer than XFLD_MAX_FIELD_LEN it returns NULL.
3569  * Note: For the charset field, we also return the encoding, e.g. 'iso8859-1'.
3570  */
3571 static gchar*
3572 gtk_font_selection_get_xlfd_field (const gchar *fontname,
3573                                    FontField    field_num,
3574                                    gchar       *buffer)
3575 {
3576   const gchar *t1, *t2;
3577   gint countdown, len, num_dashes;
3578   
3579   if (!fontname)
3580     return NULL;
3581   
3582   /* we assume this is a valid fontname...that is, it has 14 fields */
3583   
3584   countdown = field_num;
3585   t1 = fontname;
3586   while (*t1 && (countdown >= 0))
3587     if (*t1++ == '-')
3588       countdown--;
3589   
3590   num_dashes = (field_num == XLFD_CHARSET) ? 2 : 1;
3591   for (t2 = t1; *t2; t2++)
3592     { 
3593       if (*t2 == '-' && --num_dashes == 0)
3594         break;
3595     }
3596   
3597   if (t1 != t2)
3598     {
3599       /* Check we don't overflow the buffer */
3600       len = (long) t2 - (long) t1;
3601       if (len > XLFD_MAX_FIELD_LEN - 1)
3602         return NULL;
3603       strncpy (buffer, t1, len);
3604       buffer[len] = 0;
3605
3606       /* Convert to lower case. */
3607       g_strdown (buffer);
3608     }
3609   else
3610     strcpy(buffer, "(nil)");
3611   
3612   return buffer;
3613 }
3614
3615 /*
3616  * This returns a X Logical Font Description font name, given all the pieces.
3617  * Note: this retval must be freed by the caller.
3618  */
3619 static gchar *
3620 gtk_font_selection_create_xlfd (gint              size,
3621                                 GtkFontMetricType metric,
3622                                 gchar             *foundry,
3623                                 gchar             *family,
3624                                 gchar             *weight,
3625                                 gchar             *slant,
3626                                 gchar             *set_width,
3627                                 gchar             *spacing,
3628                                 gchar             *charset)
3629 {
3630   gchar buffer[16];
3631   gchar *pixel_size = "*", *point_size = "*", *fontname;
3632   
3633   if (size <= 0)
3634     return NULL;
3635   
3636   sprintf (buffer, "%d", (int) size);
3637   if (metric == GTK_FONT_METRIC_PIXELS)
3638     pixel_size = buffer;
3639   else
3640     point_size = buffer;
3641   
3642   fontname = g_strdup_printf ("-%s-%s-%s-%s-%s-*-%s-%s-*-*-%s-*-%s",
3643                               foundry, family, weight, slant,
3644                               set_width, pixel_size, point_size,
3645                               spacing, charset);
3646   return fontname;
3647 }
3648
3649
3650
3651 /*****************************************************************************
3652  * GtkFontSelectionDialog
3653  *****************************************************************************/
3654
3655 guint
3656 gtk_font_selection_dialog_get_type (void)
3657 {
3658   static guint font_selection_dialog_type = 0;
3659   
3660   if (!font_selection_dialog_type)
3661     {
3662       GtkTypeInfo fontsel_diag_info =
3663       {
3664         "GtkFontSelectionDialog",
3665         sizeof (GtkFontSelectionDialog),
3666         sizeof (GtkFontSelectionDialogClass),
3667         (GtkClassInitFunc) gtk_font_selection_dialog_class_init,
3668         (GtkObjectInitFunc) gtk_font_selection_dialog_init,
3669         /* reserved_1 */ NULL,
3670         /* reserved_2 */ NULL,
3671         (GtkClassInitFunc) NULL,
3672       };
3673       
3674       font_selection_dialog_type = gtk_type_unique (GTK_TYPE_WINDOW, &fontsel_diag_info);
3675     }
3676   
3677   return font_selection_dialog_type;
3678 }
3679
3680 static void
3681 gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass)
3682 {
3683   GtkObjectClass *object_class;
3684   
3685   object_class = (GtkObjectClass*) klass;
3686   
3687   font_selection_dialog_parent_class = gtk_type_class (GTK_TYPE_WINDOW);
3688 }
3689
3690 static void
3691 gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag)
3692 {
3693   fontseldiag->dialog_width = -1;
3694   fontseldiag->auto_resize = TRUE;
3695   
3696   gtk_widget_set_events(GTK_WIDGET(fontseldiag), GDK_STRUCTURE_MASK);
3697   gtk_signal_connect (GTK_OBJECT (fontseldiag), "configure_event",
3698                       (GtkSignalFunc) gtk_font_selection_dialog_on_configure,
3699                       fontseldiag);
3700   
3701   gtk_container_set_border_width (GTK_CONTAINER (fontseldiag), 4);
3702   gtk_window_set_policy(GTK_WINDOW(fontseldiag), FALSE, TRUE, TRUE);
3703   
3704   fontseldiag->main_vbox = gtk_vbox_new (FALSE, 4);
3705   gtk_widget_show (fontseldiag->main_vbox);
3706   gtk_container_add (GTK_CONTAINER (fontseldiag), fontseldiag->main_vbox);
3707   
3708   fontseldiag->fontsel = gtk_font_selection_new();
3709   gtk_widget_show (fontseldiag->fontsel);
3710   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
3711                       fontseldiag->fontsel, TRUE, TRUE, 0);
3712   
3713   /* Create the action area */
3714   fontseldiag->action_area = gtk_hbutton_box_new ();
3715   gtk_button_box_set_layout(GTK_BUTTON_BOX(fontseldiag->action_area),
3716                             GTK_BUTTONBOX_END);
3717   gtk_button_box_set_spacing(GTK_BUTTON_BOX(fontseldiag->action_area), 5);
3718   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
3719                       fontseldiag->action_area, FALSE, FALSE, 0);
3720   gtk_widget_show (fontseldiag->action_area);
3721   
3722   fontseldiag->ok_button = gtk_button_new_with_label(_("OK"));
3723   GTK_WIDGET_SET_FLAGS (fontseldiag->ok_button, GTK_CAN_DEFAULT);
3724   gtk_widget_show(fontseldiag->ok_button);
3725   gtk_box_pack_start (GTK_BOX (fontseldiag->action_area),
3726                       fontseldiag->ok_button, TRUE, TRUE, 0);
3727   gtk_widget_grab_default (fontseldiag->ok_button);
3728   
3729   fontseldiag->apply_button = gtk_button_new_with_label(_("Apply"));
3730   GTK_WIDGET_SET_FLAGS (fontseldiag->apply_button, GTK_CAN_DEFAULT);
3731   /*gtk_widget_show(fontseldiag->apply_button);*/
3732   gtk_box_pack_start (GTK_BOX(fontseldiag->action_area),
3733                       fontseldiag->apply_button, TRUE, TRUE, 0);
3734   
3735   fontseldiag->cancel_button = gtk_button_new_with_label(_("Cancel"));
3736   GTK_WIDGET_SET_FLAGS (fontseldiag->cancel_button, GTK_CAN_DEFAULT);
3737   gtk_widget_show(fontseldiag->cancel_button);
3738   gtk_box_pack_start (GTK_BOX(fontseldiag->action_area),
3739                       fontseldiag->cancel_button, TRUE, TRUE, 0);
3740   
3741   
3742 }
3743
3744 GtkWidget*
3745 gtk_font_selection_dialog_new   (const gchar      *title)
3746 {
3747   GtkFontSelectionDialog *fontseldiag;
3748   
3749   fontseldiag = gtk_type_new (GTK_TYPE_FONT_SELECTION_DIALOG);
3750   gtk_window_set_title (GTK_WINDOW (fontseldiag),
3751                         title ? title : _("Font Selection"));
3752   
3753   return GTK_WIDGET (fontseldiag);
3754 }
3755
3756 gchar*
3757 gtk_font_selection_dialog_get_font_name (GtkFontSelectionDialog *fsd)
3758 {
3759   return gtk_font_selection_get_font_name(GTK_FONT_SELECTION(fsd->fontsel));
3760 }
3761
3762 GdkFont*
3763 gtk_font_selection_dialog_get_font      (GtkFontSelectionDialog *fsd)
3764 {
3765   return gtk_font_selection_get_font(GTK_FONT_SELECTION(fsd->fontsel));
3766 }
3767
3768 gboolean
3769 gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
3770                                          const gchar      *fontname)
3771 {
3772   return gtk_font_selection_set_font_name(GTK_FONT_SELECTION(fsd->fontsel),
3773                                           fontname);
3774 }
3775
3776 void
3777 gtk_font_selection_dialog_set_filter    (GtkFontSelectionDialog *fsd,
3778                                          GtkFontFilterType filter_type,
3779                                          GtkFontType       font_type,
3780                                          gchar           **foundries,
3781                                          gchar           **weights,
3782                                          gchar           **slants,
3783                                          gchar           **setwidths,
3784                                          gchar           **spacings,
3785                                          gchar           **charsets)
3786 {
3787   gtk_font_selection_set_filter (GTK_FONT_SELECTION (fsd->fontsel),
3788                                  filter_type, font_type,
3789                                  foundries, weights, slants, setwidths,
3790                                  spacings, charsets);
3791 }
3792
3793 gchar*
3794 gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd)
3795 {
3796   return gtk_font_selection_get_preview_text(GTK_FONT_SELECTION(fsd->fontsel));
3797 }
3798
3799 void
3800 gtk_font_selection_dialog_set_preview_text (GtkFontSelectionDialog *fsd,
3801                                             const gchar   *text)
3802 {
3803   gtk_font_selection_set_preview_text(GTK_FONT_SELECTION(fsd->fontsel), text);
3804 }
3805
3806
3807 /* This turns auto-shrink off if the user resizes the width of the dialog.
3808    It also turns it back on again if the user resizes it back to its normal
3809    width. */
3810 static gint
3811 gtk_font_selection_dialog_on_configure (GtkWidget         *widget,
3812                                         GdkEventConfigure *event,
3813                                         GtkFontSelectionDialog *fsd)
3814 {
3815   /* This sets the initial width. */
3816   if (fsd->dialog_width == -1)
3817     fsd->dialog_width = event->width;
3818   else if (fsd->auto_resize && fsd->dialog_width != event->width)
3819     {
3820       fsd->auto_resize = FALSE;
3821       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, FALSE);
3822     }
3823   else if (!fsd->auto_resize && fsd->dialog_width == event->width)
3824     {
3825       fsd->auto_resize = TRUE;
3826       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, TRUE);
3827     }
3828   
3829   return FALSE;
3830 }