#include "gtkfont.h"
#include "tree234.h"
-/*
- * TODO on fontsel
- * ---------------
- *
- * - implement the preview pane
- *
- * - extend the font style language for X11 fonts so that we
- * never get unexplained double size elements? Or, at least, so
- * that _my_ font collection never produces them; that'd be a
- * decent start.
- *
- * - decide what _should_ happen about font aliases. Should we
- * resolve them as soon as they're clicked? Or be able to
- * resolve them on demand, er, somehow? Or resolve them on exit
- * from the function? Or what? If we resolve on demand, should
- * we stop canonifying them on input, on the basis that we'd
- * prefer to let the user _tell_ us when to canonify them?
- *
- * - think about points versus pixels, harder than I already have
- *
- * - work out why the list boxes don't go all the way to the RHS
- * of the dialog box
- *
- * - big testing and shakedown!
- */
-
/*
* Future work:
*
+ * - it would be nice to have a display of the current font name,
+ * and in particular whether it's client- or server-side,
+ * during the progress of the font selector.
+ *
* - all the GDK font functions used in the x11font subclass are
* deprecated, so one day they may go away. When this happens -
* or before, if I'm feeling proactive - it oughtn't to be too
#define FONTFLAG_SERVERALIAS 0x0004
#define FONTFLAG_NONMONOSPACED 0x0008
+#define FONTFLAG_SORT_MASK 0x0007 /* used to disambiguate font families */
+
typedef void (*fontsel_add_entry)(void *ctx, const char *realfontname,
const char *family, const char *charset,
const char *style, const char *stylekey,
int bold, int cellwidth);
void (*enum_fonts)(GtkWidget *widget,
fontsel_add_entry callback, void *callback_ctx);
- char *(*canonify_fontname)(GtkWidget *widget, const char *name, int *size);
+ char *(*canonify_fontname)(GtkWidget *widget, const char *name, int *size,
+ int *flags, int resolve_aliases);
char *(*scale_fontname)(GtkWidget *widget, const char *name, int size);
/*
static void x11font_enum_fonts(GtkWidget *widget,
fontsel_add_entry callback, void *callback_ctx);
static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
- int *size);
+ int *size, int *flags,
+ int resolve_aliases);
static char *x11font_scale_fontname(GtkWidget *widget, const char *name,
int size);
* whether we use gdk_draw_text_wc() or gdk_draw_text().
*/
int sixteen_bit;
+ /*
+ * `variable' is true iff the font is non-fixed-pitch. This
+ * enables some code which takes greater care over character
+ * positioning during text drawing.
+ */
+ int variable;
/*
* Data passed in to unifont_create().
*/
x11font_enum_fonts,
x11font_canonify_fontname,
x11font_scale_fontname,
- "server"
+ "server",
};
char *x11_guess_derived_font_name(GdkFont *font, int bold, int wide)
if (sixteen_bit) {
XChar2b space;
space.byte1 = 0;
- space.byte2 = ' ';
+ space.byte2 = '0';
return gdk_text_width(font, (const gchar *)&space, 2);
} else {
- return gdk_char_width(font, ' ');
+ return gdk_char_width(font, '0');
}
}
GdkFont *font;
XFontStruct *xfs;
Display *disp;
- Atom charset_registry, charset_encoding;
- unsigned long registry_ret, encoding_ret;
- int pubcs, realcs, sixteen_bit;
+ Atom charset_registry, charset_encoding, spacing;
+ unsigned long registry_ret, encoding_ret, spacing_ret;
+ int pubcs, realcs, sixteen_bit, variable;
int i;
font = gdk_font_load(name);
pubcs = realcs = CS_NONE;
sixteen_bit = FALSE;
+ variable = TRUE;
if (XGetFontProperty(xfs, charset_registry, ®istry_ret) &&
XGetFontProperty(xfs, charset_encoding, &encoding_ret)) {
}
}
+ spacing = XInternAtom(disp, "SPACING", False);
+ if (XGetFontProperty(xfs, spacing, &spacing_ret)) {
+ char *spc;
+ spc = XGetAtomName(disp, (Atom)spacing_ret);
+
+ if (spc && strchr("CcMm", spc[0]))
+ variable = FALSE;
+ }
+
xfont = snew(struct x11font);
xfont->u.vt = &x11font_vtable;
xfont->u.width = x11_font_width(font, sixteen_bit);
xfont->fonts[0] = font;
xfont->allocated[0] = TRUE;
xfont->sixteen_bit = sixteen_bit;
+ xfont->variable = variable;
xfont->wide = wide;
xfont->bold = bold;
xfont->shadowoffset = shadowoffset;
sfree(derived_name);
}
+static void x11font_really_draw_text(GdkDrawable *target, GdkFont *font,
+ GdkGC *gc, int x, int y,
+ const gchar *string, int clen, int nchars,
+ int shadowbold, int shadowoffset,
+ int fontvariable, int cellwidth)
+{
+ int step = clen * nchars, nsteps = 1, centre = FALSE;
+
+ if (fontvariable) {
+ /*
+ * In a variable-pitch font, we draw one character at a
+ * time, and centre it in the character cell.
+ */
+ step = clen;
+ nsteps = nchars;
+ centre = TRUE;
+ }
+
+ while (nsteps-- > 0) {
+ int X = x;
+ if (centre)
+ X += (cellwidth - gdk_text_width(font, string, step)) / 2;
+
+ gdk_draw_text(target, font, gc, X, y, string, step);
+ if (shadowbold)
+ gdk_draw_text(target, font, gc, X + shadowoffset, y, string, step);
+
+ x += cellwidth;
+ string += step;
+ }
+}
+
static void x11font_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
int x, int y, const char *string, int len,
int wide, int bold, int cellwidth)
struct x11font *xfont = (struct x11font *)font;
int sfid;
int shadowbold = FALSE;
+ int mult = (wide ? 2 : 1);
wide -= xfont->wide;
bold -= xfont->bold;
xcs[i].byte2 = wcs[i];
}
- gdk_draw_text(target, xfont->fonts[sfid], gc,
- x, y, (gchar *)xcs, nchars*2);
- if (shadowbold)
- gdk_draw_text(target, xfont->fonts[sfid], gc,
- x + xfont->shadowoffset, y, (gchar *)xcs, nchars*2);
+ x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y,
+ (gchar *)xcs, 2, nchars,
+ shadowbold, xfont->shadowoffset,
+ xfont->variable, cellwidth * mult);
sfree(xcs);
sfree(wcs);
} else {
- gdk_draw_text(target, xfont->fonts[sfid], gc, x, y, string, len);
- if (shadowbold)
- gdk_draw_text(target, xfont->fonts[sfid], gc,
- x + xfont->shadowoffset, y, string, len);
+ x11font_really_draw_text(target, xfont->fonts[sfid], gc, x, y,
+ string, 1, len,
+ shadowbold, xfont->shadowoffset,
+ xfont->variable, cellwidth * mult);
}
}
p += sprintf(p, "%04d%04d%s%04d%04d%s%04d%04d%s%04d%s%04d%s",
weightkey,
- strlen(components[2]), components[2],
+ (int)strlen(components[2]), components[2],
slantkey,
- strlen(components[3]), components[3],
+ (int)strlen(components[3]), components[3],
setwidthkey,
- strlen(components[4]), components[4],
- strlen(components[10]), components[10],
- strlen(components[5]), components[5]);
+ (int)strlen(components[4]), components[4],
+ (int)strlen(components[10]), components[10],
+ (int)strlen(components[5]), components[5]);
assert(p - tmp < thistmpsize);
}
static char *x11font_canonify_fontname(GtkWidget *widget, const char *name,
- int *size)
+ int *size, int *flags,
+ int resolve_aliases)
{
/*
* When given an X11 font name to try to make sense of for a
* font selector, we must attempt to load it (to see if it
* exists), and then canonify it by extracting its FONT
* property, which should give its full XLFD even if what we
- * originally had was an alias.
+ * originally had was a wildcard.
+ *
+ * However, we must carefully avoid canonifying font
+ * _aliases_, unless specifically asked to, because the font
+ * selector treats them as worthwhile in their own right.
*/
GdkFont *font = gdk_font_load(name);
XFontStruct *xfs;
fontprop = XInternAtom(disp, "FONT", False);
if (XGetFontProperty(xfs, fontprop, &ret)) {
- char *name = XGetAtomName(disp, (Atom)ret);
- if (name) {
+ char *newname = XGetAtomName(disp, (Atom)ret);
+ if (newname) {
unsigned long fsize = 12;
fontprop2 = XInternAtom(disp, "PIXEL_SIZE", False);
- if (XGetFontProperty(xfs, fontprop2, &fsize)) {
+ if (XGetFontProperty(xfs, fontprop2, &fsize) && fsize > 0) {
*size = fsize;
gdk_font_unref(font);
- return dupstr(name);
+ if (flags) {
+ if (name[0] == '-' || resolve_aliases)
+ *flags = FONTFLAG_SERVERSIDE;
+ else
+ *flags = FONTFLAG_SERVERALIAS;
+ }
+ return dupstr(name[0] == '-' || resolve_aliases ?
+ newname : name);
}
}
}
return NULL; /* shan't */
}
+#if GTK_CHECK_VERSION(2,0,0)
+
/* ----------------------------------------------------------------------
- * Pango font implementation.
+ * Pango font implementation (for GTK 2 only).
*/
+#if defined PANGO_PRE_1POINT4 && !defined PANGO_PRE_1POINT6
+#define PANGO_PRE_1POINT6 /* make life easier for pre-1.4 folk */
+#endif
+
static void pangofont_draw_text(GdkDrawable *target, GdkGC *gc, unifont *font,
int x, int y, const char *string, int len,
int wide, int bold, int cellwidth);
static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback,
void *callback_ctx);
static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name,
- int *size);
+ int *size, int *flags,
+ int resolve_aliases);
static char *pangofont_scale_fontname(GtkWidget *widget, const char *name,
int size);
pangofont_enum_fonts,
pangofont_canonify_fontname,
pangofont_scale_fontname,
- "client"
+ "client",
};
+/*
+ * This function is used to rigorously validate a
+ * PangoFontDescription. Later versions of Pango have a nasty
+ * habit of accepting _any_ old string as input to
+ * pango_font_description_from_string and returning a font
+ * description which can actually be used to display text, even if
+ * they have to do it by falling back to their most default font.
+ * This is doubtless helpful in some situations, but not here,
+ * because we need to know if a Pango font string actually _makes
+ * sense_ in order to fall back to treating it as an X font name
+ * if it doesn't. So we check that the font family is actually one
+ * supported by Pango.
+ */
+static int pangofont_check_desc_makes_sense(PangoContext *ctx,
+ PangoFontDescription *desc)
+{
+#ifndef PANGO_PRE_1POINT6
+ PangoFontMap *map;
+#endif
+ PangoFontFamily **families;
+ int i, nfamilies, matched;
+
+ /*
+ * Ask Pango for a list of font families, and iterate through
+ * them to see if one of them matches the family in the
+ * PangoFontDescription.
+ */
+#ifndef PANGO_PRE_1POINT6
+ map = pango_context_get_font_map(ctx);
+ if (!map)
+ return FALSE;
+ pango_font_map_list_families(map, &families, &nfamilies);
+#else
+ pango_context_list_families(ctx, &families, &nfamilies);
+#endif
+
+ matched = FALSE;
+ for (i = 0; i < nfamilies; i++) {
+ if (!g_strcasecmp(pango_font_family_get_name(families[i]),
+ pango_font_description_get_family(desc))) {
+ matched = TRUE;
+ break;
+ }
+ }
+ g_free(families);
+
+ return matched;
+}
+
static unifont *pangofont_create(GtkWidget *widget, const char *name,
int wide, int bold,
int shadowoffset, int shadowalways)
pango_font_description_free(desc);
return NULL;
}
+ if (!pangofont_check_desc_makes_sense(ctx, desc)) {
+ pango_font_description_free(desc);
+ return NULL;
+ }
#ifndef PANGO_PRE_1POINT6
map = pango_context_get_font_map(ctx);
if (!map) {
}
static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name,
- int *size)
+ int *size, int *flags,
+ int resolve_aliases)
{
/*
* When given a Pango font name to try to make sense of for a
pango_font_description_free(desc);
return NULL;
}
+ if (!pangofont_check_desc_makes_sense(ctx, desc)) {
+ pango_font_description_free(desc);
+ return NULL;
+ }
#ifndef PANGO_PRE_1POINT6
map = pango_context_get_font_map(ctx);
if (!map) {
}
*size = PANGO_PIXELS(pango_font_description_get_size(desc));
+ *flags = FONTFLAG_CLIENTSIDE;
pango_font_description_set_size(desc, PANGO_DUMMY_SIZE * PANGO_SCALE);
newname = pango_font_description_to_string(desc);
retname = dupstr(newname);
return retname;
}
+#endif /* GTK_CHECK_VERSION(2,0,0) */
+
/* ----------------------------------------------------------------------
* Outermost functions which do the vtable dispatch.
*/
* of an explicit type-disambiguating prefix.)
*/
static const struct unifont_vtable *unifont_types[] = {
+#if GTK_CHECK_VERSION(2,0,0)
&pangofont_vtable,
+#endif
&x11font_vtable,
};
wide, bold, cellwidth);
}
+#if GTK_CHECK_VERSION(2,0,0)
+
/* ----------------------------------------------------------------------
- * Implementation of a unified font selector.
+ * Implementation of a unified font selector. Used on GTK 2 only;
+ * for GTK 1 we still use the standard font selector.
*/
typedef struct fontinfo fontinfo;
GtkListStore *family_model, *style_model, *size_model;
GtkWidget *family_list, *style_list, *size_entry, *size_list;
GtkWidget *filter_buttons[4];
+ GtkWidget *preview_area;
+ GdkPixmap *preview_pixmap;
+ int preview_width, preview_height;
+ GdkColor preview_fg, preview_bg;
int filter_flags;
tree234 *fonts_by_realname, *fonts_by_selorder;
fontinfo *selected;
- int selsize;
+ int selsize, intendedsize;
int inhibit_response; /* inhibit callbacks when we change GUI controls */
} unifontsel_internal;
const struct unifont_vtable *fontclass;
};
-static int fontinfo_realname_compare(void *av, void *bv)
-{
- fontinfo *a = (fontinfo *)av;
- fontinfo *b = (fontinfo *)bv;
- return g_strcasecmp(a->realname, b->realname);
-}
-
-static int fontinfo_realname_find(void *av, void *bv)
-{
- const char *a = (const char *)av;
- fontinfo *b = (fontinfo *)bv;
- return g_strcasecmp(a, b->realname);
-}
+struct fontinfo_realname_find {
+ const char *realname;
+ int flags;
+};
static int strnullcasecmp(const char *a, const char *b)
{
return g_strcasecmp(a, b);
}
+static int fontinfo_realname_compare(void *av, void *bv)
+{
+ fontinfo *a = (fontinfo *)av;
+ fontinfo *b = (fontinfo *)bv;
+ int i;
+
+ if ((i = strnullcasecmp(a->realname, b->realname)) != 0)
+ return i;
+ if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
+ return ((a->flags & FONTFLAG_SORT_MASK) <
+ (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
+ return 0;
+}
+
+static int fontinfo_realname_find(void *av, void *bv)
+{
+ struct fontinfo_realname_find *a = (struct fontinfo_realname_find *)av;
+ fontinfo *b = (fontinfo *)bv;
+ int i;
+
+ if ((i = strnullcasecmp(a->realname, b->realname)) != 0)
+ return i;
+ if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
+ return ((a->flags & FONTFLAG_SORT_MASK) <
+ (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
+ return 0;
+}
+
static int fontinfo_selorder_compare(void *av, void *bv)
{
fontinfo *a = (fontinfo *)av;
int i;
if ((i = strnullcasecmp(a->family, b->family)) != 0)
return i;
+ /*
+ * Font class comes immediately after family, so that fonts
+ * from different classes with the same family
+ */
+ if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK))
+ return ((a->flags & FONTFLAG_SORT_MASK) <
+ (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1);
if ((i = strnullcasecmp(a->charset, b->charset)) != 0)
return i;
if ((i = strnullcasecmp(a->stylekey, b->stylekey)) != 0)
return 0;
}
+static void unifontsel_deselect(unifontsel_internal *fs)
+{
+ fs->selected = NULL;
+ gtk_list_store_clear(fs->style_model);
+ gtk_list_store_clear(fs->size_model);
+ gtk_widget_set_sensitive(fs->u.ok_button, FALSE);
+ gtk_widget_set_sensitive(fs->size_entry, FALSE);
+}
+
static void unifontsel_setup_familylist(unifontsel_internal *fs)
{
GtkTreeIter iter;
int i, listindex, minpos = -1, maxpos = -1;
char *currfamily = NULL;
+ int currflags = -1;
fontinfo *info;
gtk_list_store_clear(fs->family_model);
info->familyindex = -1;
continue; /* we're filtering out this font */
}
- if (!info || strnullcasecmp(currfamily, info->family)) {
+ if (!info || strnullcasecmp(currfamily, info->family) ||
+ currflags != (info->flags & FONTFLAG_SORT_MASK)) {
/*
* We've either finished a family, or started a new
* one, or both.
if (info) {
minpos = i;
currfamily = info->family;
+ currflags = info->flags & FONTFLAG_SORT_MASK;
}
}
if (!info)
info->familyindex = listindex;
maxpos = i;
}
+
+ /*
+ * If we've just filtered out the previously selected font,
+ * deselect it thoroughly.
+ */
+ if (fs->selected && fs->selected->familyindex < 0)
+ unifontsel_deselect(fs);
}
static void unifontsel_setup_stylelist(unifontsel_internal *fs,
}
}
+static void unifontsel_draw_preview_text(unifontsel_internal *fs)
+{
+ unifont *font;
+ char *sizename = NULL;
+ fontinfo *info = fs->selected;
+
+ if (info) {
+ sizename = info->fontclass->scale_fontname
+ (GTK_WIDGET(fs->u.window), info->realname, fs->selsize);
+ font = info->fontclass->create(GTK_WIDGET(fs->u.window),
+ sizename ? sizename : info->realname,
+ FALSE, FALSE, 0, 0);
+ } else
+ font = NULL;
+
+ if (fs->preview_pixmap) {
+ GdkGC *gc = gdk_gc_new(fs->preview_pixmap);
+ gdk_gc_set_foreground(gc, &fs->preview_bg);
+ gdk_draw_rectangle(fs->preview_pixmap, gc, 1, 0, 0,
+ fs->preview_width, fs->preview_height);
+ gdk_gc_set_foreground(gc, &fs->preview_fg);
+ if (font) {
+ /*
+ * The pangram used here is rather carefully
+ * constructed: it contains a sequence of very narrow
+ * letters (`jil') and a pair of adjacent very wide
+ * letters (`wm').
+ *
+ * If the user selects a proportional font, it will be
+ * coerced into fixed-width character cells when used
+ * in the actual terminal window. We therefore display
+ * it the same way in the preview pane, so as to show
+ * it the way it will actually be displayed - and we
+ * deliberately pick a pangram which will show the
+ * resulting miskerning at its worst.
+ *
+ * We aren't trying to sell people these fonts; we're
+ * trying to let them make an informed choice. Better
+ * that they find out the problems with using
+ * proportional fonts in terminal windows here than
+ * that they go to the effort of selecting their font
+ * and _then_ realise it was a mistake.
+ */
+ info->fontclass->draw_text(fs->preview_pixmap, gc, font,
+ 0, font->ascent,
+ "bankrupt jilted showmen quiz convex fogey",
+ 41, FALSE, FALSE, font->width);
+ info->fontclass->draw_text(fs->preview_pixmap, gc, font,
+ 0, font->ascent + font->height,
+ "BANKRUPT JILTED SHOWMEN QUIZ CONVEX FOGEY",
+ 41, FALSE, FALSE, font->width);
+ /*
+ * The ordering of punctuation here is also selected
+ * with some specific aims in mind. I put ` and '
+ * together because some software (and people) still
+ * use them as matched quotes no matter what Unicode
+ * might say on the matter, so people can quickly
+ * check whether they look silly in a candidate font.
+ * The sequence #_@ is there to let people judge the
+ * suitability of the underscore as an effectively
+ * alphabetic character (since that's how it's often
+ * used in practice, at least by programmers).
+ */
+ info->fontclass->draw_text(fs->preview_pixmap, gc, font,
+ 0, font->ascent + font->height * 2,
+ "0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$",
+ 42, FALSE, FALSE, font->width);
+ }
+ gdk_gc_unref(gc);
+ gdk_window_invalidate_rect(fs->preview_area->window, NULL, FALSE);
+ }
+ if (font)
+ info->fontclass->destroy(font);
+
+ sfree(sizename);
+}
+
static void unifontsel_select_font(unifontsel_internal *fs,
- fontinfo *info, int size, int leftlist)
+ fontinfo *info, int size, int leftlist,
+ int size_is_explicit)
{
int index;
int minval, maxval;
fs->selected = info;
fs->selsize = size;
+ if (size_is_explicit)
+ fs->intendedsize = size;
+
+ gtk_widget_set_sensitive(fs->u.ok_button, TRUE);
/*
* Find the index of this fontinfo in the selorder list.
gtk_entry_set_editable(GTK_ENTRY(fs->size_entry), fs->selected->size == 0);
gtk_widget_set_sensitive(fs->size_entry, fs->selected->size == 0);
+ unifontsel_draw_preview_text(fs);
+
fs->inhibit_response = FALSE;
}
add234(fs->fonts_by_selorder, info);
}
+static fontinfo *update_for_intended_size(unifontsel_internal *fs,
+ fontinfo *info)
+{
+ fontinfo info2, *below, *above;
+ int pos;
+
+ /*
+ * Copy the info structure. This doesn't copy its dynamic
+ * string fields, but that's unimportant because all we're
+ * going to do is to adjust the size field and use it in one
+ * tree search.
+ */
+ info2 = *info;
+ info2.size = fs->intendedsize;
+
+ /*
+ * Search in the tree to find the fontinfo structure which
+ * best approximates the size the user last requested.
+ */
+ below = findrelpos234(fs->fonts_by_selorder, &info2, NULL,
+ REL234_LE, &pos);
+ above = index234(fs->fonts_by_selorder, pos+1);
+
+ /*
+ * See if we've found it exactly, which is an easy special
+ * case. If we have, it'll be in `below' and not `above',
+ * because we did a REL234_LE rather than REL234_LT search.
+ */
+ if (!fontinfo_selorder_compare(&info2, below))
+ return below;
+
+ /*
+ * Now we've either found two suitable fonts, one smaller and
+ * one larger, or we're at one or other extreme end of the
+ * scale. Find out which, by NULLing out either of below and
+ * above if it differs from this one in any respect but size
+ * (and the disambiguating index field). Bear in mind, also,
+ * that either one might _already_ be NULL if we're at the
+ * extreme ends of the font list.
+ */
+ if (below) {
+ info2.size = below->size;
+ info2.index = below->index;
+ if (fontinfo_selorder_compare(&info2, below))
+ below = NULL;
+ }
+ if (above) {
+ info2.size = above->size;
+ info2.index = above->index;
+ if (fontinfo_selorder_compare(&info2, above))
+ above = NULL;
+ }
+
+ /*
+ * Now return whichever of above and below is non-NULL, if
+ * that's unambiguous.
+ */
+ if (!above)
+ return below;
+ if (!below)
+ return above;
+
+ /*
+ * And now we really do have to make a choice about whether to
+ * round up or down. We'll do it by rounding to nearest,
+ * breaking ties by rounding up.
+ */
+ if (above->size - fs->intendedsize <= fs->intendedsize - below->size)
+ return above;
+ else
+ return below;
+}
+
static void family_changed(GtkTreeSelection *treeselection, gpointer data)
{
unifontsel_internal *fs = (unifontsel_internal *)data;
gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1);
info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
- unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize, 1);
+ info = update_for_intended_size(fs, info);
+ if (!info)
+ return; /* _shouldn't_ happen unless font list is completely funted */
+ if (!info->size)
+ fs->selsize = fs->intendedsize; /* font is scalable */
+ unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize,
+ 1, FALSE);
}
static void style_changed(GtkTreeSelection *treeselection, gpointer data)
if (minval < 0)
return; /* somehow a charset heading got clicked */
info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
- unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize, 2);
+ info = update_for_intended_size(fs, info);
+ if (!info)
+ return; /* _shouldn't_ happen unless font list is completely funted */
+ if (!info->size)
+ fs->selsize = fs->intendedsize; /* font is scalable */
+ unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize,
+ 2, FALSE);
}
static void size_changed(GtkTreeSelection *treeselection, gpointer data)
gtk_tree_model_get(treemodel, &treeiter, 1, &minval, 2, &size, -1);
info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
- unifontsel_select_font(fs, info, info->size ? info->size : size, 3);
+ unifontsel_select_font(fs, info, info->size ? info->size : size, 3, TRUE);
}
static void size_entry_changed(GtkEditable *ed, gpointer data)
if (size > 0) {
assert(fs->selected->size == 0);
- unifontsel_select_font(fs, fs->selected, size, 3);
+ unifontsel_select_font(fs, fs->selected, size, 3, TRUE);
+ }
+}
+
+static void alias_resolve(GtkTreeView *treeview, GtkTreePath *path,
+ GtkTreeViewColumn *column, gpointer data)
+{
+ unifontsel_internal *fs = (unifontsel_internal *)data;
+ GtkTreeIter iter;
+ int minval, newsize;
+ fontinfo *info, *newinfo;
+ char *newname;
+
+ if (fs->inhibit_response) /* we made this change ourselves */
+ return;
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, path);
+ gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter, 1,&minval, -1);
+ info = (fontinfo *)index234(fs->fonts_by_selorder, minval);
+ if (info) {
+ int flags;
+ struct fontinfo_realname_find f;
+
+ newname = info->fontclass->canonify_fontname
+ (GTK_WIDGET(fs->u.window), info->realname, &newsize, &flags, TRUE);
+
+ f.realname = newname;
+ f.flags = flags;
+ newinfo = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
+
+ sfree(newname);
+ if (!newinfo)
+ return; /* font name not in our index */
+ if (newinfo == info)
+ return; /* didn't change under canonification => not an alias */
+ unifontsel_select_font(fs, newinfo,
+ newinfo->size ? newinfo->size : newsize,
+ 1, TRUE);
+ }
+}
+
+static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event,
+ gpointer data)
+{
+ unifontsel_internal *fs = (unifontsel_internal *)data;
+
+ if (fs->preview_pixmap) {
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+ fs->preview_pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
}
+ return TRUE;
+}
+
+static gint unifontsel_configure_area(GtkWidget *widget,
+ GdkEventConfigure *event, gpointer data)
+{
+ unifontsel_internal *fs = (unifontsel_internal *)data;
+ int ox, oy, nx, ny, x, y;
+
+ /*
+ * Enlarge the pixmap, but never shrink it.
+ */
+ ox = fs->preview_width;
+ oy = fs->preview_height;
+ x = event->width;
+ y = event->height;
+ if (x > ox || y > oy) {
+ if (fs->preview_pixmap)
+ gdk_pixmap_unref(fs->preview_pixmap);
+
+ nx = (x > ox ? x : ox);
+ ny = (y > oy ? y : oy);
+ fs->preview_pixmap = gdk_pixmap_new(widget->window, nx, ny, -1);
+ fs->preview_width = nx;
+ fs->preview_height = ny;
+
+ unifontsel_draw_preview_text(fs);
+ }
+
+ gdk_window_invalidate_rect(widget->window, NULL, FALSE);
+
+ return TRUE;
}
unifontsel *unifontsel_new(const char *wintitle)
{
unifontsel_internal *fs = snew(unifontsel_internal);
- GtkWidget *table, *label, *w, *scroll;
+ GtkWidget *table, *label, *w, *ww, *scroll;
GtkListStore *model;
GtkTreeViewColumn *column;
- int lists_height, font_width, style_width, size_width;
+ int lists_height, preview_height, font_width, style_width, size_width;
int i;
fs->inhibit_response = FALSE;
+ fs->selected = NULL;
{
/*
gtk_widget_size_request(label, &req);
font_width = req.width;
lists_height = 14 * req.height;
+ preview_height = 5 * req.height;
gtk_label_set_text(GTK_LABEL(label), "Italic Extra Condensed");
gtk_widget_size_request(label, &req);
style_width = req.width;
* Now set up the internal fields, including in particular all
* the controls that actually allow the user to select fonts.
*/
- table = gtk_table_new(3, 8, FALSE);
+ table = gtk_table_new(8, 3, FALSE);
gtk_widget_show(table);
gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+#if GTK_CHECK_VERSION(2,4,0)
+ /* GtkAlignment seems to be the simplest way to put padding round things */
+ w = gtk_alignment_new(0, 0, 1, 1);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);
+ gtk_container_add(GTK_CONTAINER(w), table);
+ gtk_widget_show(w);
+#else
+ w = table;
+#endif
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(fs->u.window)->vbox),
- table, TRUE, TRUE, 0);
+ w, TRUE, TRUE, 0);
label = gtk_label_new_with_mnemonic("_Font:");
gtk_widget_show(label);
gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))),
"changed", G_CALLBACK(family_changed), fs);
+ g_signal_connect(G_OBJECT(w), "row-activated",
+ G_CALLBACK(alias_resolve), fs);
scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
+ GTK_SHADOW_IN);
gtk_container_add(GTK_CONTAINER(scroll), w);
gtk_widget_show(scroll);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_widget_set_size_request(scroll, font_width, lists_height);
- gtk_table_attach(GTK_TABLE(table), scroll, 0, 1, 1, 3, GTK_FILL, 0, 0, 0);
+ gtk_table_attach(GTK_TABLE(table), scroll, 0, 1, 1, 3, GTK_FILL,
+ GTK_EXPAND | GTK_FILL, 0, 0);
fs->family_model = model;
fs->family_list = w;
"changed", G_CALLBACK(style_changed), fs);
scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
+ GTK_SHADOW_IN);
gtk_container_add(GTK_CONTAINER(scroll), w);
gtk_widget_show(scroll);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_widget_set_size_request(scroll, style_width, lists_height);
- gtk_table_attach(GTK_TABLE(table), scroll, 1, 2, 1, 3, GTK_FILL, 0, 0, 0);
+ gtk_table_attach(GTK_TABLE(table), scroll, 1, 2, 1, 3, GTK_FILL,
+ GTK_EXPAND | GTK_FILL, 0, 0);
fs->style_model = model;
fs->style_list = w;
"changed", G_CALLBACK(size_changed), fs);
scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
+ GTK_SHADOW_IN);
gtk_container_add(GTK_CONTAINER(scroll), w);
gtk_widget_show(scroll);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
fs->size_list = w;
/*
- * FIXME: preview widget
+ * Preview widget.
*/
+ fs->preview_area = gtk_drawing_area_new();
+ fs->preview_pixmap = NULL;
+ fs->preview_width = 0;
+ fs->preview_height = 0;
+ fs->preview_fg.pixel = fs->preview_bg.pixel = 0;
+ fs->preview_fg.red = fs->preview_fg.green = fs->preview_fg.blue = 0x0000;
+ fs->preview_bg.red = fs->preview_bg.green = fs->preview_bg.blue = 0xFFFF;
+ gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_fg,
+ FALSE, FALSE);
+ gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_bg,
+ FALSE, FALSE);
+ gtk_signal_connect(GTK_OBJECT(fs->preview_area), "expose_event",
+ GTK_SIGNAL_FUNC(unifontsel_expose_area), fs);
+ gtk_signal_connect(GTK_OBJECT(fs->preview_area), "configure_event",
+ GTK_SIGNAL_FUNC(unifontsel_configure_area), fs);
+ gtk_widget_set_size_request(fs->preview_area, 1, preview_height);
+ gtk_widget_show(fs->preview_area);
+ ww = fs->preview_area;
+ w = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(w), ww);
+ gtk_widget_show(w);
+#if GTK_CHECK_VERSION(2,4,0)
+ ww = w;
+ /* GtkAlignment seems to be the simplest way to put padding round things */
+ w = gtk_alignment_new(0, 0, 1, 1);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8);
+ gtk_container_add(GTK_CONTAINER(w), ww);
+ gtk_widget_show(w);
+#endif
+ ww = w;
+ w = gtk_frame_new("Preview of font");
+ gtk_container_add(GTK_CONTAINER(w), ww);
+ gtk_widget_show(w);
+ gtk_table_attach(GTK_TABLE(table), w, 0, 3, 3, 4,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 8);
+
i = 0;
w = gtk_check_button_new_with_label("Show client-side fonts");
gtk_object_set_data(GTK_OBJECT(w), "user-data",
gtk_table_attach(GTK_TABLE(table), w, 0, 3, 7, 8, GTK_FILL, 0, 0, 0);
assert(i == lenof(fs->filter_buttons));
- fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE;
+ fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE |
+ FONTFLAG_SERVERALIAS;
unifontsel_set_filter_buttons(fs);
/*
*/
unifontsel_setup_familylist(fs);
- fs->selected = NULL;
+ fs->selsize = fs->intendedsize = 13; /* random default */
+ gtk_widget_set_sensitive(fs->u.ok_button, FALSE);
return (unifontsel *)fs;
}
unifontsel_internal *fs = (unifontsel_internal *)fontsel;
fontinfo *info;
+ if (fs->preview_pixmap)
+ gdk_pixmap_unref(fs->preview_pixmap);
+
freetree234(fs->fonts_by_selorder);
while ((info = delpos234(fs->fonts_by_realname, 0)) != NULL)
sfree(info);
void unifontsel_set_name(unifontsel *fontsel, const char *fontname)
{
unifontsel_internal *fs = (unifontsel_internal *)fontsel;
- int i, start, end, size;
+ int i, start, end, size, flags;
const char *fontname2 = NULL;
fontinfo *info;
* Provide a default if given an empty or null font name.
*/
if (!fontname || !*fontname)
- fontname = "fixed"; /* Pango zealots might prefer "Monospace 12" */
+ fontname = "server:fixed";
/*
* Call the canonify_fontname function.
fontname = unifont_do_prefix(fontname, &start, &end);
for (i = start; i < end; i++) {
fontname2 = unifont_types[i]->canonify_fontname
- (GTK_WIDGET(fs->u.window), fontname, &size);
+ (GTK_WIDGET(fs->u.window), fontname, &size, &flags, FALSE);
if (fontname2)
break;
}
/*
* Now look up the canonified font name in our index.
*/
- info = find234(fs->fonts_by_realname, (char *)fontname2,
- fontinfo_realname_find);
+ {
+ struct fontinfo_realname_find f;
+ f.realname = fontname2;
+ f.flags = flags;
+ info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
+ }
/*
* If we've found the font, and its size field is either
* font name instead.
*/
if (!info || (info->size != size && info->size != 0)) {
- info = find234(fs->fonts_by_realname, (char *)fontname,
- fontinfo_realname_find);
+ struct fontinfo_realname_find f;
+ f.realname = fontname;
+ f.flags = flags;
+
+ info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find);
if (!info || info->size != size)
return; /* font name not in our index */
}
* know everything we need to fill in all the fields in the
* dialog.
*/
- unifontsel_select_font(fs, info, size, 0);
+ unifontsel_select_font(fs, info, size, 0, TRUE);
}
char *unifontsel_get_name(unifontsel *fontsel)
unifontsel_internal *fs = (unifontsel_internal *)fontsel;
char *name;
- assert(fs->selected);
+ if (!fs->selected)
+ return NULL;
if (fs->selected->size == 0) {
name = fs->selected->fontclass->scale_fontname
(GTK_WIDGET(fs->u.window), fs->selected->realname, fs->selsize);
- if (name)
- return name;
+ if (name) {
+ char *ret = dupcat(fs->selected->fontclass->prefix, ":",
+ name, NULL);
+ sfree(name);
+ return ret;
+ }
}
- return dupstr(fs->selected->realname);
+ return dupcat(fs->selected->fontclass->prefix, ":",
+ fs->selected->realname, NULL);
}
+
+#endif /* GTK_CHECK_VERSION(2,0,0) */