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