From bb80007da4edcebd0d7d90d9b303429b4619043d Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 27 Mar 2008 19:41:08 +0000 Subject: [PATCH] More consistent handling of X11 font aliases: we now don't resolve them automatically. If the user selects an alias in the font selector, they get that alias copied literally into the output font name string; when they return to the font selector, the alias is still selected. We still _can_ resolve aliases, but we only do it on demand: double-clicking one in the list box will do the job. [originally from svn r7944] --- unix/gtkfont.c | 209 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 184 insertions(+), 25 deletions(-) diff --git a/unix/gtkfont.c b/unix/gtkfont.c index 654dd141..1718cbee 100644 --- a/unix/gtkfont.c +++ b/unix/gtkfont.c @@ -27,25 +27,30 @@ * 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? + * - preview pane fixes: + * + better centring of text? (Garuda is a particularly oddly + * behaved font in this respect; are its metrics going weird + * for some reason?) + * + pick a better few lines of preview text. Should take care + * to use wide letters (MWmw) and narrow ones (ij). + * + resize to fit? * * - 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 * + * - consistency and updating issues: + * + the preview pane is empty when the dialog comes up + * + we should attempt to preserve font size when switching + * font family and style (it's currently OK as long as we're + * moving between scalable fonts but falls down badly + * otherwise) + * + * - generalised style and padding polish + * + gtk_scrolled_window_set_shadow_type(foo, GTK_SHADOW_IN); + * might be worth considering + * * - big testing and shakedown! */ @@ -103,7 +108,8 @@ struct unifont_vtable { 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 resolve_aliases); char *(*scale_fontname)(GtkWidget *widget, const char *name, int size); /* @@ -126,7 +132,7 @@ static void x11font_destroy(unifont *font); 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 resolve_aliases); static char *x11font_scale_fontname(GtkWidget *widget, const char *name, int size); @@ -593,14 +599,18 @@ static void x11font_enum_fonts(GtkWidget *widget, } static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, - int *size) + int *size, 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; @@ -618,15 +628,16 @@ static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, 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); + return dupstr(name[0] == '-' || resolve_aliases ? + newname : name); } } } @@ -655,7 +666,7 @@ static void pangofont_destroy(unifont *font); 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 resolve_aliases); static char *pangofont_scale_fontname(GtkWidget *widget, const char *name, int size); @@ -978,7 +989,7 @@ static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, } static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name, - int *size) + int *size, int resolve_aliases) { /* * When given a Pango font name to try to make sense of for a @@ -1160,6 +1171,10 @@ typedef struct unifontsel_internal { 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; @@ -1550,6 +1565,38 @@ static void unifontsel_select_font(unifontsel_internal *fs, gtk_entry_set_editable(GTK_ENTRY(fs->size_entry), fs->selected->size == 0); gtk_widget_set_sensitive(fs->size_entry, fs->selected->size == 0); + /* + * Instantiate the font and draw some preview text. + */ + { + unifont *font; + char *sizename; + + 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); + if (font && 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); + info->fontclass->draw_text(fs->preview_pixmap, gc, font, + font->width, font->height, + "hello, world", 12, + FALSE, FALSE, font->width); + gdk_gc_unref(gc); + gdk_window_invalidate_rect(fs->preview_area->window, NULL, FALSE); + } + + info->fontclass->destroy(font); + + sfree(sizename); + } + fs->inhibit_response = FALSE; } @@ -1715,13 +1762,99 @@ static void size_entry_changed(GtkEditable *ed, gpointer data) } } +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) { + newname = info->fontclass->canonify_fontname + (GTK_WIDGET(fs->u.window), info->realname, &newsize, TRUE); + newinfo = find234(fs->fonts_by_realname, (char *)newname, + 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); + } +} + +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; + GdkGC *gc; + 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) { + GdkPixmap *newpm; + + nx = (x > ox ? x : ox); + ny = (y > oy ? y : oy); + newpm = gdk_pixmap_new(widget->window, nx, ny, -1); + + gc = gdk_gc_new(newpm); + gdk_gc_set_foreground(gc, &fs->preview_bg); + gdk_draw_rectangle(newpm, gc, 1, 0, 0, nx, ny); + if (fs->preview_pixmap) { + gdk_draw_pixmap(newpm, gc, fs->preview_pixmap, 0, 0, 0, 0, ox, oy); + gdk_pixmap_unref(fs->preview_pixmap); + } + gdk_gc_unref(gc); + + fs->preview_pixmap = newpm; + fs->preview_width = nx; + fs->preview_height = ny; + } + + 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; 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; @@ -1735,6 +1868,7 @@ unifontsel *unifontsel_new(const char *wintitle) 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; @@ -1794,6 +1928,8 @@ unifontsel *unifontsel_new(const char *wintitle) 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_container_add(GTK_CONTAINER(scroll), w); @@ -1879,6 +2015,26 @@ unifontsel *unifontsel_new(const char *wintitle) fs->size_model = model; fs->size_list = w; + 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); + gtk_table_attach(GTK_TABLE(table), fs->preview_area, 0, 3, 3, 4, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + /* * FIXME: preview widget */ @@ -1945,6 +2101,9 @@ void unifontsel_destroy(unifontsel *fontsel) 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); @@ -1973,7 +2132,7 @@ void unifontsel_set_name(unifontsel *fontsel, const char *fontname) 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, FALSE); if (fontname2) break; }