]> Pileus Git - ~andy/gtk/blob - gtk/gtkfontsel.c
8790c65b7dc1640206a23938bcbca7006ac6dd4c
[~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 #ifdef GDK_WINDOWING_WIN32
75 # define CHARSET CHARSETstruct
76 #endif
77 #include "gdkx.h"
78 #ifdef 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 #ifdef 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         {
1457           if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1458             {
1459               if (*bitmapped_sizes % 10 != 0)
1460                 can_match = FALSE;
1461               bitmap_size = *bitmapped_sizes / 10;
1462               bitmap_size_float = *bitmapped_sizes / 10;
1463             }
1464           else
1465             {
1466               bitmap_size = *bitmapped_sizes;
1467               bitmap_size_float = *bitmapped_sizes;
1468             }
1469         }
1470       
1471       if (can_match && nstandard_sizes && nbitmapped_sizes
1472           && *standard_sizes == bitmap_size)
1473         {
1474           sprintf(buffer, "%i *", *standard_sizes);
1475           standard_sizes++;
1476           nstandard_sizes--;
1477           bitmapped_sizes++;
1478           nbitmapped_sizes--;
1479         }
1480       else if (nstandard_sizes
1481                && (!nbitmapped_sizes
1482                    || (gfloat)*standard_sizes < bitmap_size_float))
1483         {
1484           sprintf(buffer, "%i", *standard_sizes);
1485           standard_sizes++;
1486           nstandard_sizes--;
1487         }
1488       else
1489         {
1490           if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1491             {
1492               if (*bitmapped_sizes % 10 == 0)
1493                 sprintf(buffer, "%i *", *bitmapped_sizes / 10);
1494               else
1495                 sprintf(buffer, "%i.%i *", *bitmapped_sizes / 10,
1496                         *bitmapped_sizes % 10);
1497             }
1498           else
1499             {
1500               sprintf(buffer, "%i *", *bitmapped_sizes);
1501             }
1502           bitmapped_sizes++;
1503           nbitmapped_sizes--;
1504         }
1505       size = buffer;
1506       gtk_clist_append(GTK_CLIST(fontsel->size_clist), &size);
1507     }
1508   gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
1509 }
1510
1511
1512 /* If the user hits return in the font size entry, we change to the new font
1513    size. */
1514 static gint
1515 gtk_font_selection_size_key_press (GtkWidget   *w,
1516                                    GdkEventKey *event,
1517                                    gpointer     data)
1518 {
1519   GtkFontSelection *fontsel;
1520   gint new_size;
1521   gfloat new_size_float;
1522   gchar *text;
1523   
1524 #ifdef FONTSEL_DEBUG
1525   g_message("In size_key_press\n");
1526 #endif
1527   fontsel = GTK_FONT_SELECTION(data);
1528   
1529   if (event->keyval == GDK_Return)
1530     {
1531       text = gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry));
1532       if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
1533         {
1534           new_size = atoi (text);
1535           if (new_size < 2)
1536             new_size = 2;
1537         }
1538       else
1539         {
1540           new_size_float = atof (text) * 10;
1541           new_size = (gint) new_size_float;
1542           if (new_size < 20)
1543             new_size = 20;
1544         }
1545       
1546       /* Remember that this size was set explicitly. */
1547       fontsel->selected_size = new_size;
1548       
1549       /* Check if the font size has changed, and return if it hasn't. */
1550       if (fontsel->size == new_size)
1551         return TRUE;
1552       
1553       fontsel->size = new_size;
1554       gtk_font_selection_select_best_size (fontsel);
1555       return TRUE;
1556     }
1557   
1558   return FALSE;
1559 }
1560
1561
1562 /* This tries to select the closest size to the current size, though it
1563    may have to change the size if only unscaled bitmaps are available.
1564    Note: this will load a font. */
1565 static void
1566 gtk_font_selection_select_best_size(GtkFontSelection *fontsel)
1567 {
1568   FontInfo *font;
1569   FontStyle *styles, *style;
1570   gchar *text;
1571   gint row, best_row = 0, size, size_fraction, best_size = 0, nmatched;
1572   gboolean found = FALSE;
1573   gchar buffer[32];
1574   GList *selection;
1575   gint type_filter;
1576   
1577 #ifdef FONTSEL_DEBUG
1578   g_message("In select_best_size\n");
1579 #endif
1580   font = &fontsel_info->font_info[fontsel->font_index];
1581   styles = &fontsel_info->font_styles[font->style_index];
1582   style = &styles[fontsel->style];
1583   
1584   /* Find the closest size available in the size clist. If the exact size is
1585      in the list set found to TRUE. */
1586   for (row = 0; row < GTK_CLIST(fontsel->size_clist)->rows; row++)
1587     {
1588       gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
1589       nmatched = sscanf(text, "%i.%i", &size, &size_fraction);
1590       if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1591         {
1592           size *= 10;
1593           if (nmatched == 2)
1594             size += size_fraction;
1595         }
1596       
1597       if (size == fontsel->selected_size)
1598         {
1599           found = TRUE;
1600           best_size = size;
1601           best_row = row;
1602           break;
1603         }
1604       else if (best_size == 0
1605                || abs(size - fontsel->selected_size)
1606                < (abs(best_size - fontsel->selected_size)))
1607         {
1608           best_size = size;
1609           best_row = row;
1610         }
1611     }
1612   
1613   /* If we aren't scaling bitmapped fonts and this is a bitmapped font, we
1614      need to use the closest size found. */
1615   type_filter = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
1616     & fontsel->filters[GTK_FONT_FILTER_USER].font_type;
1617
1618   if (!((style->flags & GTK_FONT_SCALABLE_BITMAP
1619          && type_filter & GTK_FONT_SCALABLE_BITMAP)
1620         || (style->flags & GTK_FONT_SCALABLE
1621             && type_filter & GTK_FONT_SCALABLE)))
1622     found = TRUE;
1623   
1624   if (found)
1625     {
1626       fontsel->size = best_size;
1627       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
1628       gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), best_row, 0);
1629     }
1630   else
1631     {
1632       fontsel->size = fontsel->selected_size;
1633       selection = GTK_CLIST(fontsel->size_clist)->selection;
1634       if (selection)
1635         gtk_clist_unselect_row(GTK_CLIST(fontsel->size_clist),
1636                                GPOINTER_TO_INT (selection->data), 0);
1637       gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
1638       
1639       /* Show the size in the size entry. */
1640       if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
1641         sprintf(buffer, "%i", fontsel->size);
1642       else
1643         {
1644           if (fontsel->size % 10 == 0)
1645             sprintf(buffer, "%i", fontsel->size / 10);
1646           else
1647             sprintf(buffer, "%i.%i", fontsel->size / 10, fontsel->size % 10);
1648         }
1649       gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
1650     }
1651   gtk_font_selection_load_font (fontsel);
1652 }
1653
1654
1655 /* This is called when a size is selected in the list. */
1656 static void
1657 gtk_font_selection_select_size (GtkWidget      *w,
1658                                 gint            row,
1659                                 gint            column,
1660                                 GdkEventButton *bevent,
1661                                 gpointer        data)
1662 {
1663   GtkFontSelection *fontsel;
1664   gint new_size;
1665   gchar *text;
1666   gchar buffer[16];
1667   gint i;
1668   
1669 #ifdef FONTSEL_DEBUG
1670   g_message("In select_size\n");
1671 #endif
1672   fontsel = GTK_FONT_SELECTION(data);
1673   
1674   if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
1675     gtk_widget_grab_focus (w);
1676   
1677   /* Copy the size from the clist to the size entry, but without the bitmapped
1678      marker ('*'). */
1679   gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
1680   i = 0;
1681   while (i < 15 && (text[i] == '.' || (text[i] >= '0' && text[i] <= '9')))
1682     {
1683       buffer[i] = text[i];
1684       i++;
1685     }
1686   buffer[i] = '\0';
1687   gtk_entry_set_text(GTK_ENTRY(fontsel->size_entry), buffer);
1688   
1689   /* Check if the font size has changed, and return if it hasn't. */
1690   new_size = atoi(text);
1691   if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1692     new_size *= 10;
1693   
1694   if (fontsel->size == new_size)
1695     return;
1696   
1697   /* If the size was selected by the user we set the selected_size. */
1698   fontsel->selected_size = new_size;
1699   
1700   fontsel->size = new_size;
1701   gtk_font_selection_load_font (fontsel);
1702 }
1703
1704
1705 /* This is called when the pixels or points radio buttons are pressed. */
1706 static void
1707 gtk_font_selection_metric_callback (GtkWidget *w,
1708                                     gpointer   data)
1709 {
1710   GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
1711   
1712 #ifdef FONTSEL_DEBUG
1713   g_message("In metric_callback\n");
1714 #endif
1715   if (GTK_TOGGLE_BUTTON(fontsel->pixels_button)->active)
1716     {
1717       if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
1718         return;
1719       fontsel->metric = GTK_FONT_METRIC_PIXELS;
1720       fontsel->size = (fontsel->size + 5) / 10;
1721       fontsel->selected_size = (fontsel->selected_size + 5) / 10;
1722     }
1723   else
1724     {
1725       if (fontsel->metric == GTK_FONT_METRIC_POINTS)
1726         return;
1727       fontsel->metric = GTK_FONT_METRIC_POINTS;
1728       fontsel->size *= 10;
1729       fontsel->selected_size *= 10;
1730     }
1731   if (fontsel->font_index != -1)
1732     {
1733       gtk_font_selection_show_available_sizes (fontsel);
1734       gtk_font_selection_select_best_size (fontsel);
1735     }
1736 }
1737
1738
1739 /* This searches the given property table and returns the index of the given
1740    string, or 0, which is the wildcard '*' index, if it's not found. */
1741 static guint16
1742 gtk_font_selection_field_to_index (gchar **table,
1743                                    gint    ntable,
1744                                    gchar  *field)
1745 {
1746   gint i;
1747   
1748   for (i = 0; i < ntable; i++)
1749     if (strcmp (field, table[i]) == 0)
1750       return i;
1751   
1752   return 0;
1753 }
1754
1755
1756
1757 /* This attempts to load the current font, and returns TRUE if it succeeds. */
1758 static gboolean
1759 gtk_font_selection_load_font (GtkFontSelection *fontsel)
1760 {
1761   GdkFont *font;
1762   gchar *fontname, *label_text;
1763   
1764   if (fontsel->font)
1765     gdk_font_unref (fontsel->font);
1766   fontsel->font = NULL;
1767   
1768   /* If no family has been selected yet, just return FALSE. */
1769   if (fontsel->font_index == -1)
1770     return FALSE;
1771   
1772   fontname = gtk_font_selection_get_font_name (fontsel);
1773   if (fontname)
1774     {
1775 #ifdef FONTSEL_DEBUG
1776       g_message("Loading: %s\n", fontname);
1777 #endif
1778 #ifndef GDK_WINDOWING_WIN32
1779       font = gdk_font_load (fontname);
1780 #else
1781       /* Load as a fontset so that gtkentry uses wide chars for it */
1782       font = gdk_fontset_load (fontname);
1783 #endif
1784       g_free(fontname);
1785       
1786       if (font)
1787         {
1788           fontsel->font = font;
1789           /* Make sure the message label is empty, but don't change it unless
1790              it's necessary as it results in a resize of the whole window! */
1791           gtk_label_get(GTK_LABEL(fontsel->message_label), &label_text);
1792           if (strcmp(label_text, ""))
1793             gtk_label_set_text(GTK_LABEL(fontsel->message_label), "");
1794           gtk_font_selection_update_preview (fontsel);
1795           return TRUE;
1796         }
1797       else 
1798         {
1799           gtk_label_set_text(GTK_LABEL(fontsel->message_label),
1800                              _("The selected font is not available."));
1801         }
1802     }
1803   else
1804     {
1805       gtk_label_set_text(GTK_LABEL(fontsel->message_label),
1806                          _("The selected font is not a valid font."));
1807     }
1808   
1809   return FALSE;
1810 }
1811
1812
1813 /* This sets the font in the preview entry to the selected font, and tries to
1814    make sure that the preview entry is a reasonable size, i.e. so that the
1815    text can be seen with a bit of space to spare. But it tries to avoid
1816    resizing the entry every time the font changes.
1817    This also used to shrink the preview if the font size was decreased, but
1818    that made it awkward if the user wanted to resize the window themself. */
1819 static void
1820 gtk_font_selection_update_preview (GtkFontSelection *fontsel)
1821 {
1822   GtkWidget *preview_entry;
1823   GtkStyle *style;
1824   gint text_height, new_height;
1825   gchar *text;
1826 #ifdef GDK_WINDOWING_X11
1827   XFontStruct *xfs;
1828 #endif
1829
1830 #ifdef FONTSEL_DEBUG
1831   g_message("In update_preview\n");
1832 #endif
1833   style = gtk_style_new ();
1834   gdk_font_unref (style->font);
1835   style->font = fontsel->font;
1836   gdk_font_ref (style->font);
1837   
1838   preview_entry = fontsel->preview_entry;
1839   gtk_widget_set_style (preview_entry, style);
1840   gtk_style_unref(style);
1841   
1842   text_height = preview_entry->style->font->ascent
1843     + preview_entry->style->font->descent;
1844   /* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
1845   new_height = text_height + 20;
1846   if (new_height < INITIAL_PREVIEW_HEIGHT)
1847     new_height = INITIAL_PREVIEW_HEIGHT;
1848   if (new_height > MAX_PREVIEW_HEIGHT)
1849     new_height = MAX_PREVIEW_HEIGHT;
1850   
1851   if ((preview_entry->requisition.height < text_height + 10)
1852       || (preview_entry->requisition.height > text_height + 40))
1853     gtk_widget_set_usize(preview_entry, -1, new_height);
1854   
1855   /* This sets the preview text, if it hasn't been set already. */
1856   text = gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
1857   if (strlen(text) == 0)
1858     gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), PREVIEW_TEXT);
1859   gtk_entry_set_position(GTK_ENTRY(fontsel->preview_entry), 0);
1860   
1861 #ifdef GDK_WINDOWING_X11
1862   /* If this is a 2-byte font display a message to say it may not be
1863      displayed properly. */
1864   xfs = GDK_FONT_XFONT(fontsel->font);
1865   if (xfs->min_byte1 != 0 || xfs->max_byte1 != 0)
1866     gtk_label_set_text(GTK_LABEL(fontsel->message_label),
1867                        _("This is a 2-byte font and may not be displayed correctly."));
1868 #endif
1869 }
1870
1871
1872 static void
1873 gtk_font_selection_switch_page (GtkWidget       *w,
1874                                 GtkNotebookPage *page,
1875                                 gint             page_num,
1876                                 gpointer         data)
1877 {
1878   GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
1879   
1880   /* This function strangely gets called when the window is destroyed,
1881      so we check here to see if the notebook is visible. */
1882   if (!GTK_WIDGET_VISIBLE(w))
1883     return;
1884   
1885   if (page_num == 0)
1886     gtk_font_selection_update_filter(fontsel);
1887   else if (page_num == 1)
1888     gtk_font_selection_show_font_info(fontsel);
1889 }
1890
1891
1892 static void
1893 gtk_font_selection_show_font_info (GtkFontSelection *fontsel)
1894 {
1895 #ifdef GDK_WINDOWING_X11
1896   Atom font_atom, atom;
1897   Bool status;
1898 #endif
1899   char *name;
1900   gchar *fontname;
1901   gchar field_buffer[XLFD_MAX_FIELD_LEN];
1902   gchar *field;
1903   gint i;
1904   gboolean shown_actual_fields = FALSE;
1905   
1906   fontname = gtk_font_selection_get_font_name(fontsel);
1907   gtk_entry_set_text(GTK_ENTRY(fontsel->requested_font_name),
1908                      fontname ? fontname : "");
1909   
1910   gtk_clist_freeze (GTK_CLIST(fontsel->info_clist));
1911   for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1912     {
1913       if (fontname)
1914         field = gtk_font_selection_get_xlfd_field (fontname, i, field_buffer);
1915       else
1916         field = NULL;
1917       if (field)
1918         {
1919           if (i == XLFD_SLANT)
1920             field = gtk_font_selection_expand_slant_code(field);
1921           else if (i == XLFD_SPACING)
1922             field = gtk_font_selection_expand_spacing_code(field);
1923         }
1924       gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 1,
1925                          field ? field : "");
1926     }
1927 #ifdef GDK_WINDOWING_X11
1928   if (fontsel->font)
1929     {
1930       font_atom = XInternAtom(GDK_DISPLAY(), "FONT", True);
1931       if (font_atom != None)
1932         {
1933           status = XGetFontProperty(GDK_FONT_XFONT(fontsel->font), font_atom,
1934                                     &atom);
1935           if (status == True)
1936             {
1937               name = XGetAtomName(GDK_DISPLAY(), atom);
1938               gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), name);
1939               
1940               for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1941                 {
1942                   field = gtk_font_selection_get_xlfd_field (name, i,
1943                                                              field_buffer);
1944                   if (i == XLFD_SLANT)
1945                     field = gtk_font_selection_expand_slant_code(field);
1946                   else if (i == XLFD_SPACING)
1947                     field = gtk_font_selection_expand_spacing_code(field);
1948                   gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1949                                      field ? field : "");
1950                 }
1951               shown_actual_fields = TRUE;
1952               XFree(name);
1953             }
1954         }
1955     }
1956 #elif defined (GDK_WINDOWING_WIN32)
1957   if (fontsel->font)
1958     {
1959       LOGFONT logfont;
1960
1961       if (GetObject (GDK_FONT_XFONT (fontsel->font),
1962                      sizeof (LOGFONT), &logfont) > 0)
1963         {
1964           name = logfont_to_xlfd (&logfont, logfont.lfHeight, -1, 0);
1965           gtk_entry_set_text (GTK_ENTRY(fontsel->actual_font_name),name);
1966           for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1967             {
1968               field = gtk_font_selection_get_xlfd_field (name, i,
1969                                                          field_buffer);
1970               if (i == XLFD_SLANT)
1971                 field = gtk_font_selection_expand_slant_code(field);
1972               else if (i == XLFD_SPACING)
1973                 field = gtk_font_selection_expand_spacing_code(field);
1974               gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1975                                  field ? field : "");
1976             }
1977           shown_actual_fields = TRUE;
1978           g_free (name);
1979         }
1980     }
1981   
1982 #endif
1983   if (!shown_actual_fields)
1984     {
1985       gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), "");
1986       for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
1987         {
1988           gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
1989                              fontname ? _("(unknown)") : "");
1990         }
1991     }
1992   gtk_clist_thaw (GTK_CLIST(fontsel->info_clist));
1993   g_free(fontname);
1994 }
1995
1996
1997 static gchar*
1998 gtk_font_selection_expand_slant_code(gchar *slant)
1999 {
2000   if      (!g_strcasecmp(slant, "r"))   return(_("roman"));
2001   else if (!g_strcasecmp(slant, "i"))   return(_("italic"));
2002   else if (!g_strcasecmp(slant, "o"))   return(_("oblique"));
2003   else if (!g_strcasecmp(slant, "ri"))  return(_("reverse italic"));
2004   else if (!g_strcasecmp(slant, "ro"))  return(_("reverse oblique"));
2005   else if (!g_strcasecmp(slant, "ot"))  return(_("other"));
2006   return slant;
2007 }
2008
2009 static gchar*
2010 gtk_font_selection_expand_spacing_code(gchar *spacing)
2011 {
2012   if      (!g_strcasecmp(spacing, "p")) return(_("proportional"));
2013   else if (!g_strcasecmp(spacing, "m")) return(_("monospaced"));
2014   else if (!g_strcasecmp(spacing, "c")) return(_("char cell"));
2015   return spacing;
2016 }
2017
2018
2019 /*****************************************************************************
2020  * These functions all deal with the Filter page and filtering the fonts.
2021  *****************************************************************************/
2022
2023 /* This is called when an item is selected in one of the filter clists.
2024    We make sure that the first row of the clist, i.e. the wildcard '*', is
2025    selected if and only if none of the other items are selected.
2026    Also doesn't allow selections of values filtered out by base filter.
2027    We may need to be careful about triggering other signals. */
2028 static void
2029 gtk_font_selection_select_filter             (GtkWidget      *w,
2030                                               gint            row,
2031                                               gint            column,
2032                                               GdkEventButton *bevent,
2033                                               GtkFontSelection *fontsel)
2034 {
2035   gint i, prop, index;
2036   
2037   if (row == 0)
2038     {
2039       for (i = 1; i < GTK_CLIST(w)->rows; i++)
2040         gtk_clist_unselect_row(GTK_CLIST(w), i, 0);
2041     }
2042   else
2043     {
2044       /* Find out which property this is. */
2045       for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2046         if (fontsel->filter_clists[prop] == w)
2047           break;
2048       index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(w), row));
2049       if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_BASE,
2050                                            prop, index) == NOT_FILTERED)
2051         gtk_clist_unselect_row(GTK_CLIST(w), row, 0);
2052       else
2053         gtk_clist_unselect_row(GTK_CLIST(w), 0, 0);
2054     }
2055 }
2056
2057
2058 /* Here a filter item is being deselected. If there are now no items selected
2059    we select the first '*' item, unless that it is the item being deselected,
2060    in which case we select all of the other items. This makes it easy to
2061    select all items in the list except one or two. */
2062 static void
2063 gtk_font_selection_unselect_filter           (GtkWidget      *w,
2064                                               gint            row,
2065                                               gint            column,
2066                                               GdkEventButton *bevent,
2067                                               GtkFontSelection *fontsel)
2068 {
2069   gint i, prop, index;
2070
2071   if (!GTK_CLIST(w)->selection)
2072     {
2073       if (row == 0)
2074         {
2075           /* Find out which property this is. */
2076           for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2077             if (fontsel->filter_clists[prop] == w)
2078               break;
2079
2080           for (i = 1; i < GTK_CLIST(w)->rows; i++)
2081             {
2082               index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(w),
2083                                                               i));
2084               if (gtk_font_selection_filter_state (fontsel,
2085                                                    GTK_FONT_FILTER_BASE,
2086                                                    prop, index)
2087                   != NOT_FILTERED)
2088                 gtk_clist_select_row(GTK_CLIST(w), i, 0);
2089             }
2090         }
2091       else
2092         {
2093           gtk_clist_select_row(GTK_CLIST(w), 0, 0);
2094         }
2095     }
2096 }
2097
2098
2099 /* This is called when the main notebook page is selected. It checks if the
2100    filter has changed, an if so it creates the filter settings, and filters the
2101    fonts shown. If an empty filter (all '*'s) is applied, then filtering is
2102    turned off. */
2103 static void
2104 gtk_font_selection_update_filter     (GtkFontSelection *fontsel)
2105 {
2106   GtkWidget *clist;
2107   GList *selection;
2108   gboolean default_filter = TRUE, filter_changed = FALSE;
2109   gint prop, nselected, i, row, index;
2110   GtkFontFilter *filter = &fontsel->filters[GTK_FONT_FILTER_USER];
2111   gint base_font_type, user_font_type, new_font_type;
2112   
2113 #ifdef FONTSEL_DEBUG
2114   g_message("In update_filter\n");
2115 #endif
2116   
2117   /* Check if the user filter has changed, and also if it is the default
2118      filter, i.e. bitmap & scalable fonts and all '*'s selected.
2119      We only look at the bits which are not already filtered out by the base
2120      filter, since that overrides the user filter. */
2121   base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
2122     & GTK_FONT_ALL;
2123   user_font_type = fontsel->filters[GTK_FONT_FILTER_USER].font_type
2124     & GTK_FONT_ALL;
2125   new_font_type = GTK_TOGGLE_BUTTON(fontsel->type_bitmaps_button)->active
2126     ? GTK_FONT_BITMAP : 0;
2127   new_font_type |= (GTK_TOGGLE_BUTTON(fontsel->type_scalable_button)->active
2128     ? GTK_FONT_SCALABLE : 0);
2129   new_font_type |= (GTK_TOGGLE_BUTTON(fontsel->type_scaled_bitmaps_button)->active ? GTK_FONT_SCALABLE_BITMAP : 0);
2130   new_font_type &= base_font_type;
2131   new_font_type |= (~base_font_type & user_font_type);
2132   if (new_font_type != (GTK_FONT_BITMAP | GTK_FONT_SCALABLE))
2133     default_filter = FALSE;
2134
2135   if (new_font_type != user_font_type)
2136     filter_changed = TRUE;
2137   fontsel->filters[GTK_FONT_FILTER_USER].font_type = new_font_type;
2138
2139   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2140     {
2141       clist = fontsel->filter_clists[prop];
2142       selection = GTK_CLIST(clist)->selection;
2143       nselected = g_list_length(selection);
2144       if (nselected != 1 || GPOINTER_TO_INT (selection->data) != 0)
2145         {
2146           default_filter = FALSE;
2147           
2148           if (filter->property_nfilters[prop] != nselected)
2149             filter_changed = TRUE;
2150           else
2151             {
2152               for (i = 0; i < nselected; i++)
2153                 {
2154                   row = GPOINTER_TO_INT (selection->data);
2155                   index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
2156                   if (filter->property_filters[prop][i] != index)
2157                     filter_changed = TRUE;
2158                   selection = selection->next;
2159                 }
2160             }
2161         }
2162       else
2163         {
2164           if (filter->property_nfilters[prop] != 0)
2165             filter_changed = TRUE;
2166         }
2167     }
2168   
2169   /* If the filter hasn't changed we just return. */
2170   if (!filter_changed)
2171     return;
2172   
2173 #ifdef FONTSEL_DEBUG
2174   g_message("   update_fonts: filter has changed\n");
2175 #endif
2176   
2177   /* Free the old filter data and create the new arrays. */
2178   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2179     {
2180       g_free(filter->property_filters[prop]);
2181
2182       clist = fontsel->filter_clists[prop];
2183       selection = GTK_CLIST(clist)->selection;
2184       nselected = g_list_length(selection);
2185       if (nselected == 1 && GPOINTER_TO_INT (selection->data) == 0)
2186         {
2187           filter->property_filters[prop] = NULL;
2188           filter->property_nfilters[prop] = 0;
2189         }
2190       else
2191         {
2192           filter->property_filters[prop] = g_new(guint16, nselected);
2193           filter->property_nfilters[prop] = nselected;
2194           for (i = 0; i < nselected; i++)
2195             {
2196               row = GPOINTER_TO_INT (selection->data);
2197               index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
2198               filter->property_filters[prop][i] = index;
2199               selection = selection->next;
2200             }
2201         }
2202     }
2203
2204   /* Set the 'Reset Filter' button sensitive if a filter is in effect, and
2205      also set the label above the font list to show this as well. */
2206   if (default_filter)
2207     {
2208       gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
2209       gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font:"));
2210     }
2211   else
2212     {
2213       gtk_widget_set_sensitive(fontsel->filter_button, TRUE);
2214       gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font: (Filter Applied)"));
2215     }
2216   gtk_font_selection_show_available_fonts(fontsel);
2217 }  
2218
2219
2220 /* This shows all the available fonts in the font clist. */
2221 static void
2222 gtk_font_selection_show_available_fonts     (GtkFontSelection *fontsel)
2223 {
2224   FontInfo *font_info, *font;
2225   GtkFontFilter *filter;
2226   gint nfonts, i, j, k, row, style, font_row = -1;
2227   gchar font_buffer[XLFD_MAX_FIELD_LEN * 2 + 4];
2228   gchar *font_item;
2229   gboolean matched, matched_style;
2230   
2231 #ifdef FONTSEL_DEBUG
2232   g_message("In show_available_fonts\n");
2233 #endif
2234   font_info = fontsel_info->font_info;
2235   nfonts = fontsel_info->nfonts;
2236   
2237   /* Filter the list of fonts. */
2238   gtk_clist_freeze (GTK_CLIST(fontsel->font_clist));
2239   gtk_clist_clear (GTK_CLIST(fontsel->font_clist));
2240   for (i = 0; i < nfonts; i++)
2241     {
2242       font = &font_info[i];
2243       
2244       /* Check if the foundry passes through all filters. */
2245       matched = TRUE;
2246       for (k = 0; k < GTK_NUM_FONT_FILTERS; k++)
2247         {
2248           filter = &fontsel->filters[k];
2249
2250           if (filter->property_nfilters[FOUNDRY] != 0)
2251             {
2252               matched = FALSE;
2253               for (j = 0; j < filter->property_nfilters[FOUNDRY]; j++)
2254                 {
2255                   if (font->foundry == filter->property_filters[FOUNDRY][j])
2256                     {
2257                       matched = TRUE;
2258                       break;
2259                     }
2260                 }
2261               if (!matched)
2262                 break;
2263             }
2264         }
2265       
2266       if (!matched)
2267         continue;
2268
2269
2270       /* Now check if the other properties are matched in at least one style.*/
2271       matched_style = FALSE;
2272       for (style = 0; style < font->nstyles; style++)
2273         {
2274           if (gtk_font_selection_style_visible(fontsel, font, style))
2275             {
2276               matched_style = TRUE;
2277               break;
2278             }
2279         }
2280       if (!matched_style)
2281         continue;
2282       
2283       /* Insert the font in the clist. */
2284       if ((i > 0 && font->family == font_info[i-1].family)
2285           || (i < nfonts - 1 && font->family == font_info[i+1].family))
2286         {
2287           sprintf(font_buffer, "%s (%s)", font->family,
2288                   fontsel_info->properties[FOUNDRY][font->foundry]);
2289           font_item = font_buffer;
2290           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist), &font_item);
2291         }
2292       else
2293         {
2294           row = gtk_clist_append(GTK_CLIST(fontsel->font_clist),
2295                                  &font->family);
2296         }
2297       gtk_clist_set_row_data(GTK_CLIST(fontsel->font_clist), row,
2298                              GINT_TO_POINTER (i));
2299       if (fontsel->font_index == i)
2300         font_row = row;
2301     }
2302   gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
2303   
2304   /* If the currently-selected font isn't in the new list, reset the
2305      selection. */
2306   if (font_row == -1)
2307     {
2308       fontsel->font_index = -1;
2309       if (fontsel->font)
2310         gdk_font_unref(fontsel->font);
2311       fontsel->font = NULL;
2312       gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), "");
2313       gtk_clist_clear (GTK_CLIST(fontsel->font_style_clist));
2314       gtk_entry_set_text(GTK_ENTRY(fontsel->font_style_entry), "");
2315       return;
2316     }
2317
2318   gtk_clist_select_row(GTK_CLIST(fontsel->font_clist), font_row, 0);
2319   if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_clist), font_row)
2320       != GTK_VISIBILITY_FULL)
2321     gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), font_row, -1, 0.5, 0);
2322
2323   gtk_font_selection_show_available_styles (fontsel);
2324   gtk_font_selection_select_best_style (fontsel, FALSE);
2325 }
2326
2327
2328 /* Returns TRUE if the style is not currently filtered out. */
2329 static gboolean
2330 gtk_font_selection_style_visible(GtkFontSelection *fontsel,
2331                                  FontInfo         *font,
2332                                  gint              style_index)
2333 {
2334   FontStyle *styles, *style;
2335   GtkFontFilter *filter;
2336   guint16 value;
2337   gint prop, i, j;
2338   gboolean matched;
2339   gint type_filter;
2340
2341   styles = &fontsel_info->font_styles[font->style_index];
2342   style = &styles[style_index];
2343
2344   /* Check if font_type of style is filtered. */
2345   type_filter = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
2346     & fontsel->filters[GTK_FONT_FILTER_USER].font_type;
2347   if (!(style->flags & type_filter))
2348     return FALSE;
2349   
2350   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2351     {
2352       value = style->properties[prop];
2353       
2354       /* Check each filter. */
2355       for (i = 0; i < GTK_NUM_FONT_FILTERS; i++)
2356         {
2357           filter = &fontsel->filters[i];
2358
2359           if (filter->property_nfilters[prop] != 0)
2360             {
2361               matched = FALSE;
2362               for (j = 0; j < filter->property_nfilters[prop]; j++)
2363                 {
2364                   if (value == filter->property_filters[prop][j])
2365                     {
2366                       matched = TRUE;
2367                       break;
2368                     }
2369                 }
2370               if (!matched)
2371                 return FALSE;
2372             }
2373         }
2374     }
2375   return TRUE;
2376 }
2377
2378
2379 /* This resets the font type to bitmap or scalable, and sets all the filter
2380    clists to the wildcard '*' options. */
2381 static void
2382 gtk_font_selection_reset_filter      (GtkWidget      *w,
2383                                       GtkFontSelection *fontsel)
2384 {
2385   gint prop, base_font_type;
2386   
2387   fontsel->filters[GTK_FONT_FILTER_USER].font_type = GTK_FONT_BITMAP
2388     | GTK_FONT_SCALABLE;
2389
2390   base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type;
2391   if (base_font_type & GTK_FONT_BITMAP)
2392     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), TRUE);
2393   if (base_font_type & GTK_FONT_SCALABLE)
2394     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), TRUE);
2395   if (base_font_type & GTK_FONT_SCALABLE_BITMAP)
2396     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), FALSE);
2397   
2398   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2399     gtk_clist_select_row(GTK_CLIST(fontsel->filter_clists[prop]), 0, 0);
2400 }
2401
2402
2403 /* This clears the filter, showing all fonts and styles again. */
2404 static void
2405 gtk_font_selection_on_clear_filter     (GtkWidget      *w,
2406                                         GtkFontSelection *fontsel)
2407 {
2408   gtk_font_selection_clear_filter(fontsel);
2409 }
2410
2411
2412 /* This resets the user filter, showing all fonts and styles which pass the
2413    base filter again. Note that the font type is set to bitmaps and scalable
2414    fonts - scaled bitmaps are not shown. */
2415 static void
2416 gtk_font_selection_clear_filter     (GtkFontSelection *fontsel)
2417 {
2418   GtkFontFilter *filter;
2419   gint prop;
2420   
2421 #ifdef FONTSEL_DEBUG
2422   g_message("In clear_filter\n");
2423 #endif
2424   /* Clear the filter data. */
2425   filter = &fontsel->filters[GTK_FONT_FILTER_USER];
2426   filter->font_type = GTK_FONT_BITMAP | GTK_FONT_SCALABLE;
2427   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2428     {
2429       g_free(filter->property_filters[prop]);
2430       filter->property_filters[prop] = NULL;
2431       filter->property_nfilters[prop] = 0;
2432     }
2433   
2434   /* Select all the '*'s on the filter page. */
2435   gtk_font_selection_reset_filter(NULL, fontsel);
2436   
2437   /* Update the main notebook page. */
2438   gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
2439   gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font:"));
2440   
2441   gtk_font_selection_show_available_fonts(fontsel);
2442 }
2443   
2444   
2445 void
2446 gtk_font_selection_set_filter   (GtkFontSelection *fontsel,
2447                                  GtkFontFilterType filter_type,
2448                                  GtkFontType       font_type,
2449                                  gchar           **foundries,
2450                                  gchar           **weights,
2451                                  gchar           **slants,
2452                                  gchar           **setwidths,
2453                                  gchar           **spacings,
2454                                  gchar           **charsets)
2455 {
2456   GtkFontFilter *filter;
2457   gchar **filter_strings [GTK_NUM_FONT_PROPERTIES];
2458   gchar *filter_string;
2459   gchar *property, *property_alt;
2460   gint prop, nfilters, i, j, num_found;
2461   gint base_font_type, user_font_type;
2462   gboolean filter_set;
2463
2464   /* Put them into an array so we can use a simple loop. */
2465   filter_strings[FOUNDRY]   = foundries;
2466   filter_strings[WEIGHT]    = weights;
2467   filter_strings[SLANT]     = slants;
2468   filter_strings[SET_WIDTH] = setwidths;
2469   filter_strings[SPACING]   = spacings;
2470   filter_strings[CHARSET]   = charsets;
2471
2472   filter = &fontsel->filters[filter_type];
2473   filter->font_type = font_type;
2474       
2475   /* Free the old filter data, and insert the new. */
2476   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2477     {
2478       g_free(filter->property_filters[prop]);
2479       filter->property_filters[prop] = NULL;
2480       filter->property_nfilters[prop] = 0;
2481       
2482       if (filter_strings[prop])
2483         {
2484           /* Count how many items in the new array. */
2485           nfilters = 0;
2486           while (filter_strings[prop][nfilters])
2487             nfilters++;
2488
2489           filter->property_filters[prop] = g_new(guint16, nfilters);
2490           filter->property_nfilters[prop] = 0;
2491
2492           /* Now convert the strings to property indices. */
2493           num_found = 0;
2494           for (i = 0; i < nfilters; i++)
2495             {
2496               filter_string = filter_strings[prop][i];
2497               for (j = 0; j < fontsel_info->nproperties[prop]; j++)
2498                 {
2499                   property = _(fontsel_info->properties[prop][j]);
2500                   property_alt = NULL;
2501                   if (prop == SLANT)
2502                     property_alt = gtk_font_selection_expand_slant_code(property);
2503                   else if (prop == SPACING)
2504                     property_alt = gtk_font_selection_expand_spacing_code(property);
2505                   if (!strcmp (filter_string, property)
2506                       || (property_alt && !strcmp (filter_string, property_alt)))
2507                     {
2508                       filter->property_filters[prop][num_found] = j;
2509                       num_found++;
2510                       break;
2511                     }
2512                 }
2513             }
2514           filter->property_nfilters[prop] = num_found;
2515         }
2516     }
2517
2518   /* Now set the clists on the filter page according to the new filter. */
2519   gtk_font_selection_update_filter_lists (fontsel);
2520
2521   if (filter_type == GTK_FONT_FILTER_BASE)
2522     {
2523       user_font_type = fontsel->filters[GTK_FONT_FILTER_USER].font_type;
2524       if (font_type & GTK_FONT_BITMAP)
2525         {
2526           gtk_widget_set_sensitive (fontsel->type_bitmaps_button, TRUE);
2527           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), user_font_type & GTK_FONT_BITMAP);
2528         }
2529       else
2530         {
2531           gtk_widget_set_sensitive (fontsel->type_bitmaps_button, FALSE);
2532           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), FALSE);
2533         }
2534       
2535       if (font_type & GTK_FONT_SCALABLE)
2536         {
2537           gtk_widget_set_sensitive (fontsel->type_scalable_button, TRUE);
2538           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), user_font_type & GTK_FONT_SCALABLE);
2539         }
2540       else
2541         {
2542           gtk_widget_set_sensitive (fontsel->type_scalable_button, FALSE);
2543           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), FALSE);
2544         }
2545
2546       if (font_type & GTK_FONT_SCALABLE_BITMAP)
2547         {
2548           gtk_widget_set_sensitive (fontsel->type_scaled_bitmaps_button, TRUE);
2549           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), user_font_type & GTK_FONT_SCALABLE_BITMAP);
2550         }
2551       else
2552         {
2553           gtk_widget_set_sensitive (fontsel->type_scaled_bitmaps_button, FALSE);
2554           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), FALSE);
2555         }
2556     }
2557   else
2558     {
2559       base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type;
2560       if (base_font_type & GTK_FONT_BITMAP)
2561         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), font_type & GTK_FONT_BITMAP);
2562
2563       if (base_font_type & GTK_FONT_SCALABLE)
2564         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), font_type & GTK_FONT_SCALABLE);
2565
2566       if (base_font_type & GTK_FONT_SCALABLE_BITMAP)
2567         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), font_type & GTK_FONT_SCALABLE_BITMAP);
2568
2569       /* If the user filter is not the default, make the 'Reset Filter' button
2570          sensitive. */
2571       filter_set = FALSE;
2572       if (font_type != (GTK_FONT_BITMAP | GTK_FONT_SCALABLE))
2573         filter_set = TRUE;
2574       for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2575         {
2576           if (filter->property_nfilters[prop] != 0)
2577             filter_set = TRUE;
2578         }
2579       if (filter_set)
2580         gtk_widget_set_sensitive (fontsel->filter_button, TRUE);
2581     }
2582
2583   gtk_font_selection_show_available_fonts (fontsel);
2584 }
2585
2586
2587 /* This sets the colour of each property in the filter clists according to
2588    the base filter. i.e. Filtered properties are shown as insensitive. */
2589 static void
2590 gtk_font_selection_update_filter_lists (GtkFontSelection *fontsel)
2591 {
2592   GtkWidget *clist;
2593   GdkColor *inactive_fg, *inactive_bg, *fg, *bg;
2594   gint prop, row, index;
2595
2596   /* We have to make sure the clist is realized to use the colours. */
2597   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2598     {
2599       clist = fontsel->filter_clists[prop];
2600       gtk_widget_realize (clist);
2601       inactive_fg = &clist->style->fg[GTK_STATE_INSENSITIVE];
2602       inactive_bg = &clist->style->bg[GTK_STATE_INSENSITIVE];
2603       for (row = 1; row < GTK_CLIST(clist)->rows; row++)
2604         {
2605           index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(clist),
2606                                                            row));
2607           /* Set the colour according to the base filter. */
2608           if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_BASE,
2609                                                prop, index) == NOT_FILTERED)
2610             {
2611               fg = inactive_fg;
2612               bg = inactive_bg;
2613             }
2614           else
2615             {
2616               fg = NULL;
2617               bg = NULL;
2618             }
2619           gtk_clist_set_foreground(GTK_CLIST(clist), row, fg);
2620           gtk_clist_set_background(GTK_CLIST(clist), row, bg);
2621
2622           /* Set the selection state according to the user filter. */
2623           if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_USER,
2624                                                prop, index) == FILTERED
2625               && fg == NULL)
2626             gtk_clist_select_row (GTK_CLIST (clist), row, 0);
2627           else
2628             gtk_clist_unselect_row (GTK_CLIST (clist), row, 0);
2629         }
2630     }
2631 }
2632
2633
2634 /* Returns whether a property value is in the filter or not, or if the
2635    property has no filter set. */
2636 static GtkFontPropertyFilterState
2637 gtk_font_selection_filter_state (GtkFontSelection *fontsel,
2638                                  GtkFontFilterType filter_type,
2639                                  gint             property,
2640                                  gint             index)
2641 {
2642   GtkFontFilter *filter;
2643   gint i;
2644
2645   filter = &fontsel->filters[filter_type];
2646   if (filter->property_nfilters[property] == 0)
2647     return NOT_SET;
2648
2649   for (i = 0; i < filter->property_nfilters[property]; i++)
2650     {
2651       if (filter->property_filters[property][i] == index)
2652         return FILTERED;
2653     }
2654   return NOT_FILTERED;
2655 }
2656
2657
2658 #ifdef GDK_WINDOWING_WIN32
2659
2660 static gint num_fonts;
2661 static gint font_names_size;
2662 static gchar **xfontnames;
2663 static HDC hdc;
2664
2665 static char *
2666 logfont_to_xlfd (const LOGFONT *lfp,
2667                  int size,
2668                  int res,
2669                  int avg_width)
2670 {
2671   const gchar *weight;
2672   const gchar *registry, *encoding;
2673   int point_size;
2674   static int logpixelsy = 0;
2675   gchar facename[LF_FACESIZE*3];
2676   gchar *p;
2677   const gchar *q;
2678
2679   if (logpixelsy == 0)
2680     {
2681       HDC hdc = GetDC (NULL);
2682       logpixelsy = GetDeviceCaps (hdc, LOGPIXELSY);
2683       ReleaseDC (NULL, hdc);
2684     }
2685
2686   /* Don't use _() here, only N_(), the actual translation is done elsewhere */   
2687   if (lfp->lfWeight >= FW_HEAVY)
2688     weight = N_("heavy");
2689   else if (lfp->lfWeight >= FW_EXTRABOLD)
2690     weight = N_("extrabold");
2691   else if (lfp->lfWeight >= FW_BOLD)
2692     weight = N_("bold");
2693 #ifdef FW_DEMIBOLD
2694   else if (lfp->lfWeight >= FW_DEMIBOLD)
2695     weight = N_("demibold");
2696 #endif
2697   else if (lfp->lfWeight >= FW_MEDIUM)
2698     weight = N_("medium");
2699   else if (lfp->lfWeight >= FW_NORMAL)
2700     weight = N_("normal");
2701   else if (lfp->lfWeight >= FW_LIGHT)
2702     weight = N_("light");
2703   else if (lfp->lfWeight >= FW_EXTRALIGHT)
2704     weight = N_("extralight");
2705   else if (lfp->lfWeight >= FW_THIN)
2706     weight = N_("thin");
2707   else
2708     weight = N_("regular");
2709
2710   if (lfp->lfCharSet == ANSI_CHARSET)
2711     {
2712       registry = "iso8859";
2713       encoding = "1";
2714     }
2715   else
2716     {
2717       registry = "windows";
2718       if (lfp->lfCharSet == DEFAULT_CHARSET)
2719         encoding = "default";
2720       else if (lfp->lfCharSet == SYMBOL_CHARSET)
2721         encoding = "symbol";
2722       else if (lfp->lfCharSet == SHIFTJIS_CHARSET)
2723         encoding = "shiftjis";
2724       else if (lfp->lfCharSet == GB2312_CHARSET)
2725         encoding = "gb2312";
2726       else if (lfp->lfCharSet == HANGEUL_CHARSET)
2727         encoding = "hangeul";
2728       else if (lfp->lfCharSet == CHINESEBIG5_CHARSET)
2729         encoding = "chinesebig5";
2730       else if (lfp->lfCharSet == OEM_CHARSET)
2731         encoding = "oem";
2732 #ifdef JOHAB_CHARSET
2733       else if (lfp->lfCharSet == JOHAB_CHARSET)
2734         encoding = "johab";
2735 #endif
2736       else if (lfp->lfCharSet == HEBREW_CHARSET)
2737         encoding = "hebrew";
2738       else if (lfp->lfCharSet == ARABIC_CHARSET)
2739         encoding = "arabic";
2740       else if (lfp->lfCharSet == GREEK_CHARSET)
2741         encoding = "greek";
2742       else if (lfp->lfCharSet == TURKISH_CHARSET)
2743         encoding = "turkish";
2744       else if (lfp->lfCharSet == THAI_CHARSET)
2745         encoding = "thai";
2746       else if (lfp->lfCharSet == EASTEUROPE_CHARSET)
2747         encoding = "easteurope";
2748       else if (lfp->lfCharSet == RUSSIAN_CHARSET)
2749         encoding = "russian";
2750       else if (lfp->lfCharSet == MAC_CHARSET)
2751         encoding = "mac";
2752       else if (lfp->lfCharSet == BALTIC_CHARSET)
2753         encoding = "baltic";
2754       else
2755         encoding = "unknown";
2756     }
2757   
2758   point_size = (int) (((double) size/logpixelsy) * 720.);
2759
2760   if (res == -1)
2761     res = logpixelsy;
2762
2763   /* Replace illegal characters with hex escapes. */
2764   p = facename;
2765   q = lfp->lfFaceName;
2766   while (*q)
2767     {
2768       if (*q == '-' || *q == '*' || *q == '?' || *q == '%')
2769         p += sprintf (p, "%%%.02x", *q);
2770       else
2771         *p++ = *q;
2772       q++;
2773     }
2774   *p = '\0';
2775
2776   return  g_strdup_printf
2777     ("-%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-%d-%s-%s",
2778      "unknown", 
2779      facename,
2780      weight,
2781      (lfp->lfItalic ?
2782       ((lfp->lfPitchAndFamily & 0xF0) == FF_ROMAN
2783        || (lfp->lfPitchAndFamily & 0xF0) == FF_SCRIPT ?
2784        "i" : "o") : "r"),
2785      "normal",
2786      "",
2787      size,
2788      point_size,
2789      res,
2790      res,
2791      ((lfp->lfPitchAndFamily & 0x03) == FIXED_PITCH ? "m" : "p"),
2792      avg_width,
2793      registry, encoding);
2794 }
2795
2796 int CALLBACK
2797 InnerEnumFontFamExProc (const LOGFONT *lfp,
2798                         const TEXTMETRIC *metrics,
2799                         DWORD fontType,
2800                         LPARAM lParam)
2801 {
2802   int size;
2803
2804   if (fontType == TRUETYPE_FONTTYPE)
2805     {
2806       size = 0;
2807     }
2808   else
2809     {
2810       size = lfp->lfHeight;
2811     }
2812
2813   num_fonts++;
2814   if (num_fonts == font_names_size)
2815     {
2816       font_names_size *= 2;
2817       xfontnames = g_realloc (xfontnames, font_names_size * sizeof (gchar *));
2818     }
2819   xfontnames[num_fonts-1] =
2820     logfont_to_xlfd (lfp, size, 0, 0);
2821   return 1;
2822 }
2823
2824 int CALLBACK
2825 EnumFontFamExProc (const LOGFONT *lfp,
2826                    const TEXTMETRIC *metrics,
2827                    DWORD fontType,
2828                    LPARAM lParam)
2829 {
2830   if (fontType == TRUETYPE_FONTTYPE)
2831     {
2832       LOGFONT lf = *lfp;
2833       lf.lfPitchAndFamily = 0;
2834       EnumFontFamiliesEx (hdc, &lf, InnerEnumFontFamExProc, 0, 0);
2835     }
2836   else
2837     InnerEnumFontFamExProc (lfp, metrics, fontType, lParam);
2838   return 1;
2839 }
2840
2841 #endif
2842
2843 /*****************************************************************************
2844  * These functions all deal with creating the main class arrays containing
2845  * the data about all available fonts.
2846  *****************************************************************************/
2847 static void
2848 gtk_font_selection_get_fonts (void)
2849 {
2850 #ifdef GDK_WINDOWING_X11
2851   gchar **xfontnames;
2852   gint num_fonts;
2853 #elif defined (GDK_WINDOWING_WIN32)
2854   LOGFONT logfont;
2855 #else
2856   gint num_fonts = 0;
2857   gchar **xfontnames = NULL;
2858 #endif
2859   GSList **fontnames;
2860   gchar *fontname;
2861   GSList * temp_list;
2862   gint i, prop, style, size;
2863   gint npixel_sizes = 0, npoint_sizes = 0;
2864   FontInfo *font;
2865   FontStyle *current_style, *prev_style, *tmp_style;
2866   gboolean matched_style, found_size;
2867   gint pixels, points, res_x, res_y;
2868   gchar field_buffer[XLFD_MAX_FIELD_LEN];
2869   gchar *field;
2870   guint8 flags;
2871   guint16 *pixel_sizes, *point_sizes, *tmp_sizes;
2872   
2873   fontsel_info = g_new (GtkFontSelInfo, 1);
2874
2875 #ifdef GDK_WINDOWING_X11
2876   /* Get a maximum of MAX_FONTS fontnames from the X server.
2877      Use "-*" as the pattern rather than "-*-*-*-*-*-*-*-*-*-*-*-*-*-*" since
2878      the latter may result in fonts being returned which don't actually exist.
2879      xlsfonts also uses "*" so I think it's OK. "-*" gets rid of aliases. */
2880   xfontnames = XListFonts (GDK_DISPLAY(), "-*", MAX_FONTS, &num_fonts);
2881   /* Output a warning if we actually get MAX_FONTS fonts. */
2882   if (num_fonts == MAX_FONTS)
2883     g_warning(_("MAX_FONTS exceeded. Some fonts may be missing."));
2884   
2885 #elif defined (GDK_WINDOWING_WIN32)
2886   num_fonts = 0;
2887   hdc = GetDC (NULL);
2888   font_names_size = 100;
2889   xfontnames = g_new (gchar *, font_names_size);
2890   logfont.lfCharSet = DEFAULT_CHARSET;
2891   logfont.lfFaceName[0] = '\0';
2892   logfont.lfPitchAndFamily = 0;
2893   EnumFontFamiliesEx (hdc, &logfont, EnumFontFamExProc, 0, 0);
2894   ReleaseDC (NULL, hdc);
2895 #endif
2896
2897   /* The maximum size of all these tables is the number of font names
2898      returned. We realloc them later when we know exactly how many
2899      unique entries there are. */
2900   fontsel_info->font_info = g_new (FontInfo, num_fonts);
2901   fontsel_info->font_styles = g_new (FontStyle, num_fonts);
2902   fontsel_info->pixel_sizes = g_new (guint16, num_fonts);
2903   fontsel_info->point_sizes = g_new (guint16, num_fonts);
2904   
2905   fontnames = g_new (GSList*, num_fonts);
2906   
2907   /* Create the initial arrays for the property value strings, though they
2908      may be realloc'ed later. Put the wildcard '*' in the first elements. */
2909   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
2910     {
2911       fontsel_info->properties[prop] = g_new(gchar*, PROPERTY_ARRAY_INCREMENT);
2912       fontsel_info->space_allocated[prop] = PROPERTY_ARRAY_INCREMENT;
2913       fontsel_info->nproperties[prop] = 1;
2914       fontsel_info->properties[prop][0] = "*";
2915     }
2916   
2917   
2918   /* Insert the font families into the main table, sorted by family and
2919      foundry (fonts with different foundries are placed in seaparate FontInfos.
2920      All fontnames in each family + foundry are placed into the fontnames
2921      array of lists. */
2922   fontsel_info->nfonts = 0;
2923   for (i = 0; i < num_fonts; i++)
2924     {
2925       if (gtk_font_selection_is_xlfd_font_name (xfontnames[i]))
2926         gtk_font_selection_insert_font (fontnames, &fontsel_info->nfonts, xfontnames[i]);
2927     }
2928   
2929   
2930   /* Since many font names will be in the same FontInfo not all of the
2931      allocated FontInfo table will be used, so we will now reallocate it
2932      with the real size. */
2933   fontsel_info->font_info = g_realloc(fontsel_info->font_info,
2934                                       sizeof(FontInfo) * fontsel_info->nfonts);
2935   
2936   
2937   /* Now we work out which choices of weight/slant etc. are valid for each
2938      font. */
2939   fontsel_info->nstyles = 0;
2940   current_style = fontsel_info->font_styles;
2941   for (i = 0; i < fontsel_info->nfonts; i++)
2942     {
2943       font = &fontsel_info->font_info[i];
2944       
2945       /* Use the next free position in the styles array. */
2946       font->style_index = fontsel_info->nstyles;
2947       
2948       /* Now step through each of the fontnames with this family, and create
2949          a style for each fontname. Each style contains the index into the
2950          weights/slants etc. arrays, and a number of pixel/point sizes. */
2951       style = 0;
2952       temp_list = fontnames[i];
2953       while (temp_list)
2954         {
2955           fontname = temp_list->data;
2956           temp_list = temp_list->next;
2957           
2958           for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
2959             {
2960               current_style->properties[prop]
2961                 = gtk_font_selection_insert_field (fontname, prop);
2962             }
2963           current_style->pixel_sizes_index = npixel_sizes;
2964           current_style->npixel_sizes = 0;
2965           current_style->point_sizes_index = npoint_sizes;
2966           current_style->npoint_sizes = 0;
2967           current_style->flags = 0;
2968           
2969           
2970           field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
2971                                                      field_buffer);
2972           pixels = atoi(field);
2973           
2974           field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
2975                                                      field_buffer);
2976           points = atoi(field);
2977           
2978           field = gtk_font_selection_get_xlfd_field (fontname,
2979                                                      XLFD_RESOLUTION_X,
2980                                                      field_buffer);
2981           res_x = atoi(field);
2982           
2983           field = gtk_font_selection_get_xlfd_field (fontname,
2984                                                      XLFD_RESOLUTION_Y,
2985                                                      field_buffer);
2986           res_y = atoi(field);
2987           
2988           if (pixels == 0 && points == 0)
2989             {
2990               if (res_x == 0 && res_y == 0)
2991                 flags = GTK_FONT_SCALABLE;
2992               else
2993                 flags = GTK_FONT_SCALABLE_BITMAP;
2994             }
2995           else
2996             flags = GTK_FONT_BITMAP;
2997           
2998           /* Now we check to make sure that the style is unique. If it isn't
2999              we forget it. */
3000           prev_style = fontsel_info->font_styles + font->style_index;
3001           matched_style = FALSE;
3002           while (prev_style < current_style)
3003             {
3004               matched_style = TRUE;
3005               for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
3006                 {
3007                   if (prev_style->properties[prop]
3008                       != current_style->properties[prop])
3009                     {
3010                       matched_style = FALSE;
3011                       break;
3012                     }
3013                 }
3014               if (matched_style)
3015                 break;
3016               prev_style++;
3017             }
3018           
3019           /* If we matched an existing style, we need to add the pixels &
3020              point sizes to the style. If not, we insert the pixel & point
3021              sizes into our new style. Note that we don't add sizes for
3022              scalable fonts. */
3023           if (matched_style)
3024             {
3025               prev_style->flags |= flags;
3026               if (flags == GTK_FONT_BITMAP)
3027                 {
3028                   pixel_sizes = fontsel_info->pixel_sizes
3029                     + prev_style->pixel_sizes_index;
3030                   found_size = FALSE;
3031                   for (size = 0; size < prev_style->npixel_sizes; size++)
3032                     {
3033                       if (pixels == *pixel_sizes)
3034                         {
3035                           found_size = TRUE;
3036                           break;
3037                         }
3038                       else if (pixels < *pixel_sizes)
3039                         break;
3040                       pixel_sizes++;
3041                     }
3042                   /* We need to move all the following pixel sizes up, and also
3043                      update the indexes of any following styles. */
3044                   if (!found_size)
3045                     {
3046                       for (tmp_sizes = fontsel_info->pixel_sizes + npixel_sizes;
3047                            tmp_sizes > pixel_sizes; tmp_sizes--)
3048                         *tmp_sizes = *(tmp_sizes - 1);
3049                       
3050                       *pixel_sizes = pixels;
3051                       npixel_sizes++;
3052                       prev_style->npixel_sizes++;
3053                       
3054                       tmp_style = prev_style + 1;
3055                       while (tmp_style < current_style)
3056                         {
3057                           tmp_style->pixel_sizes_index++;
3058                           tmp_style++;
3059                         }
3060                     }
3061                   
3062                   point_sizes = fontsel_info->point_sizes
3063                     + prev_style->point_sizes_index;
3064                   found_size = FALSE;
3065                   for (size = 0; size < prev_style->npoint_sizes; size++)
3066                     {
3067                       if (points == *point_sizes)
3068                         {
3069                           found_size = TRUE;
3070                           break;
3071                         }
3072                       else if (points < *point_sizes)
3073                         break;
3074                       point_sizes++;
3075                     }
3076                   /* We need to move all the following point sizes up, and also
3077                      update the indexes of any following styles. */
3078                   if (!found_size)
3079                     {
3080                       for (tmp_sizes = fontsel_info->point_sizes + npoint_sizes;
3081                            tmp_sizes > point_sizes; tmp_sizes--)
3082                         *tmp_sizes = *(tmp_sizes - 1);
3083                       
3084                       *point_sizes = points;
3085                       npoint_sizes++;
3086                       prev_style->npoint_sizes++;
3087                       
3088                       tmp_style = prev_style + 1;
3089                       while (tmp_style < current_style)
3090                         {
3091                           tmp_style->point_sizes_index++;
3092                           tmp_style++;
3093                         }
3094                     }
3095                 }
3096             }
3097           else
3098             {
3099               current_style->flags = flags;
3100               if (flags == GTK_FONT_BITMAP)
3101                 {
3102                   fontsel_info->pixel_sizes[npixel_sizes++] = pixels;
3103                   current_style->npixel_sizes = 1;
3104                   fontsel_info->point_sizes[npoint_sizes++] = points;
3105                   current_style->npoint_sizes = 1;
3106                 }
3107               style++;
3108               fontsel_info->nstyles++;
3109               current_style++;
3110             }
3111         }
3112       g_slist_free(fontnames[i]);
3113       
3114       /* Set nstyles to the real value, minus duplicated fontnames.
3115          Note that we aren't using all the allocated memory if fontnames are
3116          duplicated. */
3117       font->nstyles = style;
3118     }
3119   
3120   /* Since some repeated styles may be skipped we won't have used all the
3121      allocated space, so we will now reallocate it with the real size. */
3122   fontsel_info->font_styles = g_realloc(fontsel_info->font_styles,
3123                                         sizeof(FontStyle) * fontsel_info->nstyles);
3124   fontsel_info->pixel_sizes = g_realloc(fontsel_info->pixel_sizes,
3125                                         sizeof(guint16) * npixel_sizes);
3126   fontsel_info->point_sizes = g_realloc(fontsel_info->point_sizes,
3127                                         sizeof(guint16) * npoint_sizes);
3128   g_free(fontnames);
3129
3130 #ifdef GDK_WINDOWING_X11
3131   XFreeFontNames (xfontnames);
3132 #elif defined (GDK_WINDOWING_WIN32)
3133   for (i = 0; i < num_fonts; i++)
3134     g_free (xfontnames[i]);
3135   g_free (xfontnames);
3136 #endif
3137   
3138   /* Debugging Output */
3139   /* This outputs all FontInfos. */
3140 #ifdef FONTSEL_DEBUG
3141   g_message("\n\n Font Family           Weight    Slant     Set Width Spacing   Charset\n\n");
3142   for (i = 0; i < fontsel_info->nfonts; i++)
3143     {
3144       FontInfo *font = &fontsel_info->font_info[i];
3145       FontStyle *styles = fontsel_info->font_styles + font->style_index;
3146       for (style = 0; style < font->nstyles; style++)
3147         {
3148           g_message("%5i %-16.16s ", i, font->family);
3149           for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
3150             g_message("%-9.9s ",
3151                       fontsel_info->properties[prop][styles->properties[prop]]);
3152           g_message("\n      ");
3153           
3154           if (styles->flags & GTK_FONT_BITMAP)
3155             g_message("Bitmapped font  ");
3156           if (styles->flags & GTK_FONT_SCALABLE)
3157             g_message("Scalable font  ");
3158           if (styles->flags & GTK_FONT_SCALABLE_BITMAP)
3159             g_message("Scalable-Bitmapped font  ");
3160           g_message("\n");
3161           
3162           if (styles->npixel_sizes)
3163             {
3164               g_message("      Pixel sizes: ");
3165               tmp_sizes = fontsel_info->pixel_sizes + styles->pixel_sizes_index;
3166               for (size = 0; size < styles->npixel_sizes; size++)
3167                 g_message("%i ", *tmp_sizes++);
3168               g_message("\n");
3169             }
3170           
3171           if (styles->npoint_sizes)
3172             {
3173               g_message("      Point sizes: ");
3174               tmp_sizes = fontsel_info->point_sizes + styles->point_sizes_index;
3175               for (size = 0; size < styles->npoint_sizes; size++)
3176                 g_message("%i ", *tmp_sizes++);
3177               g_message("\n");
3178             }
3179           
3180           g_message("\n");
3181           styles++;
3182         }
3183     }
3184   /* This outputs all available properties. */
3185   for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
3186     {
3187       g_message("Property: %s\n", xlfd_field_names[xlfd_index[prop]]);
3188       for (i = 0; i < fontsel_info->nproperties[prop]; i++)
3189         g_message("  %s\n", fontsel_info->properties[prop][i]);
3190     }
3191 #endif
3192 }
3193
3194 /* This inserts the given fontname into the FontInfo table.
3195    If a FontInfo already exists with the same family and foundry, then the
3196    fontname is added to the FontInfos list of fontnames, else a new FontInfo
3197    is created and inserted in alphabetical order in the table. */
3198 static void
3199 gtk_font_selection_insert_font (GSList                *fontnames[],
3200                                 gint                  *ntable,
3201                                 gchar                 *fontname)
3202 {
3203   FontInfo *table;
3204   FontInfo temp_info;
3205   GSList *temp_fontname;
3206   gchar *family;
3207   gboolean family_exists = FALSE;
3208   gint foundry;
3209   gint lower, upper;
3210   gint middle, cmp;
3211   gchar family_buffer[XLFD_MAX_FIELD_LEN];
3212   
3213   table = fontsel_info->font_info;
3214   
3215   /* insert a fontname into a table */
3216   family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
3217                                               family_buffer);
3218   if (!family)
3219     return;
3220   
3221   foundry = gtk_font_selection_insert_field (fontname, FOUNDRY);
3222   
3223   lower = 0;
3224   if (*ntable > 0)
3225     {
3226       /* Do a binary search to determine if we have already encountered
3227        *  a font with this family & foundry. */
3228       upper = *ntable;
3229       while (lower < upper)
3230         {
3231           middle = (lower + upper) >> 1;
3232           
3233           cmp = strcmp (family, table[middle].family);
3234           /* If the family matches we sort by the foundry. */
3235           if (cmp == 0)
3236             {
3237               family_exists = TRUE;
3238               family = table[middle].family;
3239               cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
3240                            fontsel_info->properties[FOUNDRY][table[middle].foundry]);
3241             }
3242           
3243           if (cmp == 0)
3244             {
3245               fontnames[middle] = g_slist_prepend (fontnames[middle],
3246                                                    fontname);
3247               return;
3248             }
3249           else if (cmp < 0)
3250             upper = middle;
3251           else
3252             lower = middle+1;
3253         }
3254     }
3255   
3256   /* Add another entry to the table for this new font family */
3257   temp_info.family = family_exists ? family : g_strdup(family);
3258   temp_info.foundry = foundry;
3259   temp_fontname = g_slist_prepend (NULL, fontname);
3260   
3261   (*ntable)++;
3262   
3263   /* Quickly insert the entry into the table in sorted order
3264    *  using a modification of insertion sort and the knowledge
3265    *  that the entries proper position in the table was determined
3266    *  above in the binary search and is contained in the "lower"
3267    *  variable. */
3268   if (*ntable > 1)
3269     {
3270       upper = *ntable - 1;
3271       while (lower != upper)
3272         {
3273           table[upper] = table[upper-1];
3274           fontnames[upper] = fontnames[upper-1];
3275           upper--;
3276         }
3277     }
3278   table[lower] = temp_info;
3279   fontnames[lower] = temp_fontname;
3280 }
3281
3282
3283 /* This checks that the specified field of the given fontname is in the
3284    appropriate properties array. If not it is added. Thus eventually we get
3285    arrays of all possible weights/slants etc. It returns the array index. */
3286 static gint
3287 gtk_font_selection_insert_field (gchar                 *fontname,
3288                                  gint                   prop)
3289 {
3290   gchar field_buffer[XLFD_MAX_FIELD_LEN];
3291   gchar *field;
3292   guint16 index;
3293   
3294   field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
3295                                              field_buffer);
3296   if (!field)
3297     return 0;
3298   
3299   /* If the field is already in the array just return its index. */
3300   for (index = 0; index < fontsel_info->nproperties[prop]; index++)
3301     if (!strcmp(field, fontsel_info->properties[prop][index]))
3302       return index;
3303   
3304   /* Make sure we have enough space to add the field. */
3305   if (fontsel_info->nproperties[prop] == fontsel_info->space_allocated[prop])
3306     {
3307       fontsel_info->space_allocated[prop] += PROPERTY_ARRAY_INCREMENT;
3308       fontsel_info->properties[prop] = g_realloc(fontsel_info->properties[prop],
3309                                                  sizeof(gchar*)
3310                                                  * fontsel_info->space_allocated[prop]);
3311     }
3312   
3313   /* Add the new field. */
3314   index = fontsel_info->nproperties[prop];
3315   fontsel_info->properties[prop][index] = g_strdup(field);
3316   fontsel_info->nproperties[prop]++;
3317   return index;
3318 }
3319
3320
3321 /*****************************************************************************
3322  * These functions are the main public interface for getting/setting the font.
3323  *****************************************************************************/
3324
3325 GdkFont*
3326 gtk_font_selection_get_font (GtkFontSelection *fontsel)
3327 {
3328   g_return_val_if_fail (fontsel != NULL, NULL);
3329   return fontsel->font;
3330 }
3331
3332
3333 gchar *
3334 gtk_font_selection_get_font_name (GtkFontSelection *fontsel)
3335 {
3336   FontInfo *font;
3337   gchar *family_str, *foundry_str;
3338   gchar *property_str[GTK_NUM_STYLE_PROPERTIES];
3339   gint prop;
3340   
3341   g_return_val_if_fail (fontsel != NULL, NULL);
3342   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), NULL);
3343   
3344   /* If no family has been selected return NULL. */
3345   if (fontsel->font_index == -1)
3346     return NULL;
3347   
3348   font = &fontsel_info->font_info[fontsel->font_index];
3349   family_str = font->family;
3350   foundry_str = fontsel_info->properties[FOUNDRY][font->foundry];
3351   
3352   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
3353     {
3354       property_str[prop]
3355         = fontsel_info->properties[prop][fontsel->property_values[prop]];
3356       if (strcmp (property_str[prop], "(nil)") == 0)
3357         property_str[prop] = "";
3358     }
3359   
3360   return gtk_font_selection_create_xlfd (fontsel->size,
3361                                          fontsel->metric,
3362                                          foundry_str,
3363                                          family_str,
3364                                          property_str[WEIGHT],
3365                                          property_str[SLANT],
3366                                          property_str[SET_WIDTH],
3367                                          property_str[SPACING],
3368                                          property_str[CHARSET]);
3369 }
3370
3371
3372 /* This sets the current font, selecting the appropriate clist rows.
3373    First we check the fontname is valid and try to find the font family
3374    - i.e. the name in the main list. If we can't find that, then just return.
3375    Next we try to set each of the properties according to the fontname.
3376    Finally we select the font family & style in the clists. */
3377 gboolean
3378 gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
3379                                   const gchar      *fontname)
3380 {
3381   gchar *family, *field;
3382   gint index, prop, size;
3383   guint16 foundry, value;
3384   gchar family_buffer[XLFD_MAX_FIELD_LEN];
3385   gchar field_buffer[XLFD_MAX_FIELD_LEN];
3386   gchar buffer[16];
3387   
3388   g_return_val_if_fail (fontsel != NULL, FALSE);
3389   g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), FALSE);
3390   g_return_val_if_fail (fontname != NULL, FALSE);
3391   
3392   /* Check it is a valid fontname. */
3393   if (!gtk_font_selection_is_xlfd_font_name(fontname))
3394     return FALSE;
3395   
3396   family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
3397                                               family_buffer);
3398   if (!family)
3399     return FALSE;
3400   
3401   field = gtk_font_selection_get_xlfd_field (fontname, XLFD_FOUNDRY,
3402                                              field_buffer);
3403   foundry = gtk_font_selection_field_to_index (fontsel_info->properties[FOUNDRY],
3404                                                fontsel_info->nproperties[FOUNDRY],
3405                                                field);
3406   
3407   index = gtk_font_selection_find_font(fontsel, family, foundry);
3408   if (index == -1)
3409     return FALSE;
3410   
3411   /* Convert the property fields into indices and set them. */
3412   for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++) 
3413     {
3414       field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
3415                                                  field_buffer);
3416       value = gtk_font_selection_field_to_index (fontsel_info->properties[prop],
3417                                                  fontsel_info->nproperties[prop],
3418                                                  field);
3419       fontsel->property_values[prop] = value;
3420     }
3421   
3422   field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
3423                                              field_buffer);
3424   size = atoi(field);
3425   if (size > 0)
3426     {
3427       if (size < 20)
3428         size = 20;
3429       fontsel->size = fontsel->selected_size = size;
3430       fontsel->metric = GTK_FONT_METRIC_POINTS;
3431       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->points_button),
3432                                   TRUE);
3433       if (size % 10 == 0)
3434         sprintf (buffer, "%i", size / 10);
3435       else
3436         sprintf (buffer, "%i.%i", size / 10, size % 10);
3437     }
3438   else
3439     {
3440       field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
3441                                                  field_buffer);
3442       size = atoi(field);
3443       if (size < 2)
3444         size = 2;
3445       fontsel->size = fontsel->selected_size = size;
3446       fontsel->metric = GTK_FONT_METRIC_PIXELS;
3447       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->pixels_button),
3448                                   TRUE);
3449       sprintf (buffer, "%i", size);
3450     }
3451   gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
3452   
3453   /* Clear any current filter. */
3454   gtk_font_selection_clear_filter(fontsel);
3455   
3456   /* Now find the best style match. */
3457   fontsel->font_index = index;
3458   gtk_clist_select_row(GTK_CLIST(fontsel->font_clist), index, 0);
3459   if (GTK_WIDGET_MAPPED (fontsel->font_clist))
3460     gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
3461   
3462   gtk_font_selection_show_available_styles (fontsel);
3463   /* This will load the font. */
3464   gtk_font_selection_select_best_style (fontsel, FALSE);
3465   
3466   return TRUE;
3467 }
3468
3469
3470 /* Returns the index of the given family, or -1 if not found */
3471 static gint
3472 gtk_font_selection_find_font (GtkFontSelection *fontsel,
3473                               gchar            *family,
3474                               guint16           foundry)
3475 {
3476   FontInfo *font_info;
3477   gint lower, upper, middle = -1, cmp, nfonts;
3478   gint found_family = -1;
3479   
3480   font_info = fontsel_info->font_info;
3481   nfonts = fontsel_info->nfonts;
3482   if (nfonts == 0)
3483     return -1;
3484   
3485   /* Do a binary search to find the font family. */
3486   lower = 0;
3487   upper = nfonts;
3488   while (lower < upper)
3489     {
3490       middle = (lower + upper) >> 1;
3491       
3492       cmp = strcmp (family, font_info[middle].family);
3493       if (cmp == 0)
3494         {
3495           found_family = middle;
3496           cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
3497                        fontsel_info->properties[FOUNDRY][font_info[middle].foundry]);
3498         }
3499
3500       if (cmp == 0)
3501         return middle;
3502       else if (cmp < 0)
3503         upper = middle;
3504       else if (cmp > 0)
3505         lower = middle+1;
3506     }
3507   
3508   /* We couldn't find the family and foundry, but we may have just found the
3509      family, so we return that. */
3510   return found_family;
3511 }
3512
3513
3514 /* This returns the text in the preview entry. You should copy the returned
3515    text if you need it. */
3516 gchar*
3517 gtk_font_selection_get_preview_text  (GtkFontSelection *fontsel)
3518 {
3519   return gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
3520 }
3521
3522
3523 /* This sets the text in the preview entry. */
3524 void
3525 gtk_font_selection_set_preview_text  (GtkFontSelection *fontsel,
3526                                       const gchar         *text)
3527 {
3528   gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), text);
3529 }
3530
3531
3532 /*****************************************************************************
3533  * These functions all deal with X Logical Font Description (XLFD) fontnames.
3534  * See the freely available documentation about this.
3535  *****************************************************************************/
3536
3537 /*
3538  * Returns TRUE if the fontname is a valid XLFD.
3539  * (It just checks if the number of dashes is 14, and that each
3540  * field < XLFD_MAX_FIELD_LEN  characters long - that's not in the XLFD but it
3541  * makes it easier for me).
3542  */
3543 static gboolean
3544 gtk_font_selection_is_xlfd_font_name (const gchar *fontname)
3545 {
3546   gint i = 0;
3547   gint field_len = 0;
3548   
3549   while (*fontname)
3550     {
3551       if (*fontname++ == '-')
3552         {
3553           if (field_len > XLFD_MAX_FIELD_LEN) return FALSE;
3554           field_len = 0;
3555           i++;
3556         }
3557       else
3558         field_len++;
3559     }
3560   
3561   return (i == 14) ? TRUE : FALSE;
3562 }
3563
3564 /*
3565  * This fills the buffer with the specified field from the X Logical Font
3566  * Description name, and returns it. If fontname is NULL or the field is
3567  * longer than XFLD_MAX_FIELD_LEN it returns NULL.
3568  * Note: For the charset field, we also return the encoding, e.g. 'iso8859-1'.
3569  */
3570 static gchar*
3571 gtk_font_selection_get_xlfd_field (const gchar *fontname,
3572                                    FontField    field_num,
3573                                    gchar       *buffer)
3574 {
3575   const gchar *t1, *t2;
3576   gint countdown, len, num_dashes;
3577 #ifdef GDK_WINDOWING_WIN32
3578   gchar *p;
3579 #endif
3580   
3581   if (!fontname)
3582     return NULL;
3583   
3584   /* we assume this is a valid fontname...that is, it has 14 fields */
3585   
3586   countdown = field_num;
3587   t1 = fontname;
3588   while (*t1 && (countdown >= 0))
3589     if (*t1++ == '-')
3590       countdown--;
3591   
3592   num_dashes = (field_num == XLFD_CHARSET) ? 2 : 1;
3593   for (t2 = t1; *t2; t2++)
3594     { 
3595       if (*t2 == '-' && --num_dashes == 0)
3596         break;
3597     }
3598   
3599   if (t1 != t2)
3600     {
3601       /* Check we don't overflow the buffer */
3602       len = (long) t2 - (long) t1;
3603       if (len > XLFD_MAX_FIELD_LEN - 1)
3604         return NULL;
3605       strncpy (buffer, t1, len);
3606       buffer[len] = 0;
3607 #ifdef GDK_WINDOWING_X11
3608       /* Convert to lower case. */
3609       g_strdown (buffer);
3610 #elif defined (GDK_WINDOWING_WIN32)
3611       /* Check for hex escapes in font family */
3612       if (field_num == XLFD_FAMILY)
3613         {
3614           p = buffer;
3615           while (*p)
3616             {
3617               if (*p == '%' && isxdigit (p[1]) && isxdigit (p[2]))
3618                 {
3619                   guint c;
3620                   sscanf (p+1, "%2x", &c);
3621                   *p = c;
3622                   strcpy (p+1, p+3);
3623                 }
3624               p++;
3625             }
3626         }
3627 #endif
3628     }
3629   else
3630     strcpy(buffer, "(nil)");
3631   
3632   return buffer;
3633 }
3634
3635 /*
3636  * This returns a X Logical Font Description font name, given all the pieces.
3637  * Note: this retval must be freed by the caller.
3638  */
3639 static gchar *
3640 gtk_font_selection_create_xlfd (gint              size,
3641                                 GtkFontMetricType metric,
3642                                 gchar             *foundry,
3643                                 gchar             *family,
3644                                 gchar             *weight,
3645                                 gchar             *slant,
3646                                 gchar             *set_width,
3647                                 gchar             *spacing,
3648                                 gchar             *charset)
3649 {
3650   gchar buffer[16];
3651   gchar *pixel_size = "*", *point_size = "*", *fontname;
3652   gchar *fam = family;
3653 #ifdef GDK_WINDOWING_WIN32 
3654   gchar *p, *q;
3655 #endif
3656
3657   if (size <= 0)
3658     return NULL;
3659   
3660   sprintf (buffer, "%d", (int) size);
3661   if (metric == GTK_FONT_METRIC_PIXELS)
3662     pixel_size = buffer;
3663   else
3664     point_size = buffer;
3665   
3666 #ifdef GDK_WINDOWING_WIN32
3667   fam = g_malloc (strlen (family) * 3 + 1);
3668   p = fam;
3669   q = family;
3670   while (*q)
3671     {
3672       if (*q == '-' || *q == '*' || *q == '?' || *q == '%')
3673         p += sprintf (p, "%%%.02x", *q);
3674       else
3675         *p++ = *q;
3676       q++;
3677     }
3678   *p = '\0';
3679 #endif
3680   fontname = g_strdup_printf ("-%s-%s-%s-%s-%s-*-%s-%s-*-*-%s-*-%s",
3681                               foundry, fam, weight, slant,
3682                               set_width, pixel_size, point_size,
3683                               spacing, charset);
3684 #ifdef GDK_WINDOWING_WIN32
3685   g_free (fam);
3686 #endif
3687   return fontname;
3688 }
3689
3690
3691
3692 /*****************************************************************************
3693  * GtkFontSelectionDialog
3694  *****************************************************************************/
3695
3696 GtkType
3697 gtk_font_selection_dialog_get_type (void)
3698 {
3699   static guint font_selection_dialog_type = 0;
3700   
3701   if (!font_selection_dialog_type)
3702     {
3703       GtkTypeInfo fontsel_diag_info =
3704       {
3705         "GtkFontSelectionDialog",
3706         sizeof (GtkFontSelectionDialog),
3707         sizeof (GtkFontSelectionDialogClass),
3708         (GtkClassInitFunc) gtk_font_selection_dialog_class_init,
3709         (GtkObjectInitFunc) gtk_font_selection_dialog_init,
3710         /* reserved_1 */ NULL,
3711         /* reserved_2 */ NULL,
3712         (GtkClassInitFunc) NULL,
3713       };
3714       
3715       font_selection_dialog_type = gtk_type_unique (GTK_TYPE_WINDOW, &fontsel_diag_info);
3716     }
3717   
3718   return font_selection_dialog_type;
3719 }
3720
3721 static void
3722 gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass)
3723 {
3724   GtkObjectClass *object_class;
3725   
3726   object_class = (GtkObjectClass*) klass;
3727   
3728   font_selection_dialog_parent_class = gtk_type_class (GTK_TYPE_WINDOW);
3729 }
3730
3731 static void
3732 gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag)
3733 {
3734   fontseldiag->dialog_width = -1;
3735   fontseldiag->auto_resize = TRUE;
3736   
3737   gtk_widget_set_events(GTK_WIDGET(fontseldiag), GDK_STRUCTURE_MASK);
3738   gtk_signal_connect (GTK_OBJECT (fontseldiag), "configure_event",
3739                       (GtkSignalFunc) gtk_font_selection_dialog_on_configure,
3740                       fontseldiag);
3741   
3742   gtk_container_set_border_width (GTK_CONTAINER (fontseldiag), 4);
3743   gtk_window_set_policy(GTK_WINDOW(fontseldiag), FALSE, TRUE, TRUE);
3744   
3745   fontseldiag->main_vbox = gtk_vbox_new (FALSE, 4);
3746   gtk_widget_show (fontseldiag->main_vbox);
3747   gtk_container_add (GTK_CONTAINER (fontseldiag), fontseldiag->main_vbox);
3748   
3749   fontseldiag->fontsel = gtk_font_selection_new();
3750   gtk_widget_show (fontseldiag->fontsel);
3751   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
3752                       fontseldiag->fontsel, TRUE, TRUE, 0);
3753   
3754   /* Create the action area */
3755   fontseldiag->action_area = gtk_hbutton_box_new ();
3756   gtk_button_box_set_layout(GTK_BUTTON_BOX(fontseldiag->action_area),
3757                             GTK_BUTTONBOX_END);
3758   gtk_button_box_set_spacing(GTK_BUTTON_BOX(fontseldiag->action_area), 5);
3759   gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
3760                       fontseldiag->action_area, FALSE, FALSE, 0);
3761   gtk_widget_show (fontseldiag->action_area);
3762   
3763   fontseldiag->ok_button = gtk_button_new_with_label(_("OK"));
3764   GTK_WIDGET_SET_FLAGS (fontseldiag->ok_button, GTK_CAN_DEFAULT);
3765   gtk_widget_show(fontseldiag->ok_button);
3766   gtk_box_pack_start (GTK_BOX (fontseldiag->action_area),
3767                       fontseldiag->ok_button, TRUE, TRUE, 0);
3768   gtk_widget_grab_default (fontseldiag->ok_button);
3769   
3770   fontseldiag->apply_button = gtk_button_new_with_label(_("Apply"));
3771   GTK_WIDGET_SET_FLAGS (fontseldiag->apply_button, GTK_CAN_DEFAULT);
3772   /*gtk_widget_show(fontseldiag->apply_button);*/
3773   gtk_box_pack_start (GTK_BOX(fontseldiag->action_area),
3774                       fontseldiag->apply_button, TRUE, TRUE, 0);
3775   
3776   fontseldiag->cancel_button = gtk_button_new_with_label(_("Cancel"));
3777   GTK_WIDGET_SET_FLAGS (fontseldiag->cancel_button, GTK_CAN_DEFAULT);
3778   gtk_widget_show(fontseldiag->cancel_button);
3779   gtk_box_pack_start (GTK_BOX(fontseldiag->action_area),
3780                       fontseldiag->cancel_button, TRUE, TRUE, 0);
3781   
3782   
3783 }
3784
3785 GtkWidget*
3786 gtk_font_selection_dialog_new   (const gchar      *title)
3787 {
3788   GtkFontSelectionDialog *fontseldiag;
3789   
3790   fontseldiag = gtk_type_new (GTK_TYPE_FONT_SELECTION_DIALOG);
3791   gtk_window_set_title (GTK_WINDOW (fontseldiag),
3792                         title ? title : _("Font Selection"));
3793   
3794   return GTK_WIDGET (fontseldiag);
3795 }
3796
3797 gchar*
3798 gtk_font_selection_dialog_get_font_name (GtkFontSelectionDialog *fsd)
3799 {
3800   return gtk_font_selection_get_font_name(GTK_FONT_SELECTION(fsd->fontsel));
3801 }
3802
3803 GdkFont*
3804 gtk_font_selection_dialog_get_font      (GtkFontSelectionDialog *fsd)
3805 {
3806   return gtk_font_selection_get_font(GTK_FONT_SELECTION(fsd->fontsel));
3807 }
3808
3809 gboolean
3810 gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
3811                                          const gchar      *fontname)
3812 {
3813   return gtk_font_selection_set_font_name(GTK_FONT_SELECTION(fsd->fontsel),
3814                                           fontname);
3815 }
3816
3817 void
3818 gtk_font_selection_dialog_set_filter    (GtkFontSelectionDialog *fsd,
3819                                          GtkFontFilterType filter_type,
3820                                          GtkFontType       font_type,
3821                                          gchar           **foundries,
3822                                          gchar           **weights,
3823                                          gchar           **slants,
3824                                          gchar           **setwidths,
3825                                          gchar           **spacings,
3826                                          gchar           **charsets)
3827 {
3828   gtk_font_selection_set_filter (GTK_FONT_SELECTION (fsd->fontsel),
3829                                  filter_type, font_type,
3830                                  foundries, weights, slants, setwidths,
3831                                  spacings, charsets);
3832 }
3833
3834 gchar*
3835 gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd)
3836 {
3837   return gtk_font_selection_get_preview_text(GTK_FONT_SELECTION(fsd->fontsel));
3838 }
3839
3840 void
3841 gtk_font_selection_dialog_set_preview_text (GtkFontSelectionDialog *fsd,
3842                                             const gchar   *text)
3843 {
3844   gtk_font_selection_set_preview_text(GTK_FONT_SELECTION(fsd->fontsel), text);
3845 }
3846
3847
3848 /* This turns auto-shrink off if the user resizes the width of the dialog.
3849    It also turns it back on again if the user resizes it back to its normal
3850    width. */
3851 static gint
3852 gtk_font_selection_dialog_on_configure (GtkWidget         *widget,
3853                                         GdkEventConfigure *event,
3854                                         GtkFontSelectionDialog *fsd)
3855 {
3856   /* This sets the initial width. */
3857   if (fsd->dialog_width == -1)
3858     fsd->dialog_width = event->width;
3859   else if (fsd->auto_resize && fsd->dialog_width != event->width)
3860     {
3861       fsd->auto_resize = FALSE;
3862       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, FALSE);
3863     }
3864   else if (!fsd->auto_resize && fsd->dialog_width == event->width)
3865     {
3866       fsd->auto_resize = TRUE;
3867       gtk_window_set_policy(GTK_WINDOW(fsd), FALSE, TRUE, TRUE);
3868     }
3869   
3870   return FALSE;
3871 }