[gtk/wip/fanc999/fontchooser.win32.gtk3: 8/11] gtk/gtkfontchooserwidget.c: Split out font feature items
- From: Chun-wei Fan <fanchunwei src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/fanc999/fontchooser.win32.gtk3: 8/11] gtk/gtkfontchooserwidget.c: Split out font feature items
- Date: Thu, 3 Jan 2019 16:54:41 +0000 (UTC)
commit ef3bc7f75cc6d0a52efd187de45b31a9827c64d7
Author: Chun-wei Fan <fanc999 yahoo com tw>
Date: Tue Dec 25 21:19:11 2018 +0800
gtk/gtkfontchooserwidget.c: Split out font feature items
This is to prepare for working on the version of this code that uses
PangoWin32Fonts, rather than the PangoFcFont, which is the default
PangoFont type/backend used on Windows.
gtk/Makefile.am | 2 +
gtk/gtkfontchooserwidget.c | 938 +------------------------------------------
gtk/gtkfontfeatures.c | 921 ++++++++++++++++++++++++++++++++++++++++++
gtk/gtkfontfeaturesprivate.h | 92 +++++
4 files changed, 1021 insertions(+), 932 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 559442963e..d6c9e8a788 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -492,6 +492,7 @@ gtk_private_h_sources = \
gtkfontchooserprivate.h \
gtkfontchooserutils.h \
gtkfontchooserwidgetprivate.h \
+ gtkfontfeaturesprivate.h \
gtkgestureprivate.h \
gtkgesturedragprivate.h \
gtkgesturelongpressprivate.h \
@@ -793,6 +794,7 @@ gtk_base_c_sources = \
gtkfontchooserdialog.c \
gtkfontchooserutils.c \
gtkfontchooserwidget.c \
+ gtkfontfeatures.c \
gtkframe.c \
gtkgladecatalog.c \
gtkgesture.c \
diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c
index b858d92c29..20d6380af9 100644
--- a/gtk/gtkfontchooserwidget.c
+++ b/gtk/gtkfontchooserwidget.c
@@ -53,18 +53,7 @@
#include "gtkcombobox.h"
#include "gtkgesturemultipress.h"
-#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
-#include <pango/pangofc-font.h>
-#include <hb.h>
-#include <hb-ot.h>
-#include <hb-ft.h>
-#include <freetype/freetype.h>
-#include <freetype/ftmm.h>
-#include "language-names.h"
-#include "script-names.h"
-#endif
-
-#include "open-type-layout.h"
+#include "gtkfontfeaturesprivate.h"
/**
* SECTION:gtkfontchooserwidget
@@ -94,55 +83,6 @@
*/
-struct _GtkFontChooserWidgetPrivate
-{
- GtkWidget *stack;
- GtkWidget *search_entry;
- GtkWidget *family_face_list;
- GtkTreeViewColumn *family_face_column;
- GtkCellRenderer *family_face_cell;
- GtkWidget *list_scrolled_window;
- GtkWidget *list_stack;
- GtkTreeModel *model;
- GtkTreeModel *filter_model;
-
- GtkWidget *preview;
- GtkWidget *preview2;
- GtkWidget *font_name_label;
- gchar *preview_text;
- gboolean show_preview_entry;
-
- GtkWidget *size_spin;
- GtkWidget *size_slider;
- GtkWidget *size_slider2;
-
- GtkWidget *axis_grid;
- GtkWidget *feature_box;
-
- PangoFontMap *font_map;
-
- PangoFontDescription *font_desc;
- char *font_features;
- PangoLanguage *language;
- GtkTreeIter font_iter; /* invalid if font not available or pointer into model
- (not filter_model) to the row containing font */
- GtkFontFilterFunc filter_func;
- gpointer filter_data;
- GDestroyNotify filter_data_destroy;
-
- guint last_fontconfig_timestamp;
-
- GtkFontChooserLevel level;
-
- GHashTable *axes;
- gboolean updating_variations;
-
- GList *feature_items;
-
- GAction *tweak_action;
-};
-
-
/* This is the initial fixed height and the top padding of the preview entry */
#define PREVIEW_HEIGHT 72
#define PREVIEW_TOP_PADDING 6
@@ -192,9 +132,6 @@ static PangoFontDescription *gtk_font_chooser_widget_get_font_desc (GtkFontChoo
static void gtk_font_chooser_widget_merge_font_desc(GtkFontChooserWidget *fontchooser,
const PangoFontDescription *font_desc,
GtkTreeIter *iter);
-static void gtk_font_chooser_widget_take_font_desc (GtkFontChooserWidget *fontchooser,
- PangoFontDescription *font_desc);
-
static const gchar *gtk_font_chooser_widget_get_preview_text (GtkFontChooserWidget *fontchooser);
static void gtk_font_chooser_widget_set_preview_text (GtkFontChooserWidget *fontchooser,
@@ -208,7 +145,6 @@ static void gtk_font_chooser_widget_set_cell_size (GtkFontChooserWi
static void gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
gboolean force);
-static void gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser);
static gboolean visible_func (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data);
@@ -220,7 +156,6 @@ static void gtk_font_chooser_widget_cell_data_func (GtkTreeViewColum
static void selection_changed (GtkTreeSelection *selection,
GtkFontChooserWidget *fontchooser);
-static void update_font_features (GtkFontChooserWidget *fontchooser);
static void gtk_font_chooser_widget_set_level (GtkFontChooserWidget *fontchooser,
@@ -230,7 +165,6 @@ static void gtk_font_chooser_widget_set_language (GtkFontChooserW
const char *language);
static void selection_changed (GtkTreeSelection *selection,
GtkFontChooserWidget *fontchooser);
-static void update_font_features (GtkFontChooserWidget *fontchooser);
static void gtk_font_chooser_widget_iface_init (GtkFontChooserIface *iface);
@@ -422,23 +356,6 @@ size_change_cb (GtkAdjustment *adjustment,
gtk_font_chooser_widget_take_font_desc (fontchooser, font_desc);
}
-static gboolean
-output_cb (GtkSpinButton *spin,
- gpointer data)
-{
- GtkAdjustment *adjustment;
- gchar *text;
- gdouble value;
-
- adjustment = gtk_spin_button_get_adjustment (spin);
- value = gtk_adjustment_get_value (adjustment);
- text = g_strdup_printf ("%2.4g", value);
- gtk_entry_set_text (GTK_ENTRY (spin), text);
- g_free (text);
-
- return TRUE;
-}
-
static void
gtk_font_chooser_widget_update_marks (GtkFontChooserWidget *fontchooser)
{
@@ -604,7 +521,7 @@ resize_by_scroll_cb (GtkWidget *scrolled_window,
return TRUE;
}
-static void
+void
gtk_font_chooser_widget_update_preview_attributes (GtkFontChooserWidget *fontchooser)
{
GtkFontChooserWidgetPrivate *priv;
@@ -761,17 +678,6 @@ change_tweak (GSimpleAction *action,
g_simple_action_set_state (action, state);
}
-#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
-
-typedef struct {
- guint32 tag;
- GtkAdjustment *adjustment;
- GtkWidget *label;
- GtkWidget *scale;
- GtkWidget *spin;
- GtkWidget *fontchooser;
-} Axis;
-
static guint
axis_hash (gconstpointer v)
{
@@ -789,18 +695,6 @@ axis_equal (gconstpointer v1, gconstpointer v2)
return a1->tag == a2->tag;
}
-static void
-axis_remove (gpointer key,
- gpointer value,
- gpointer data)
-{
- Axis *a = value;
-
- gtk_widget_destroy (a->label);
- gtk_widget_destroy (a->scale);
- gtk_widget_destroy (a->spin);
-}
-
static void
axis_free (gpointer v)
{
@@ -809,8 +703,6 @@ axis_free (gpointer v)
g_free (a);
}
-#endif
-
static void
gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
{
@@ -821,7 +713,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
gtk_widget_init_template (GTK_WIDGET (fontchooser));
-#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+#if defined(HAVE_HARFBUZZ) && defined (HAVE_PANGOFT)
priv->axes = g_hash_table_new_full (axis_hash, axis_equal, NULL, axis_free);
#endif
@@ -862,7 +754,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
/* Load data and set initial style-dependent parameters */
gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
-#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+#if defined(HAVE_HARFBUZZ) && defined (HAVE_PANGOFT)
gtk_font_chooser_widget_populate_features (fontchooser);
#endif
gtk_font_chooser_widget_set_cell_size (fontchooser);
@@ -1442,824 +1334,6 @@ gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser)
}
}
-#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
-
-/* OpenType variations */
-
-#define FixedToFloat(f) (((float)(f))/65536.0)
-
-static void
-add_font_variations (GtkFontChooserWidget *fontchooser,
- GString *s)
-{
- GHashTableIter iter;
- Axis *axis;
- const char *sep = "";
- char buf[G_ASCII_DTOSTR_BUF_SIZE];
-
- g_hash_table_iter_init (&iter, fontchooser->priv->axes);
- while (g_hash_table_iter_next (&iter, (gpointer *)NULL, (gpointer *)&axis))
- {
- char tag[5];
- double value;
-
- tag[0] = (axis->tag >> 24) & 0xff;
- tag[1] = (axis->tag >> 16) & 0xff;
- tag[2] = (axis->tag >> 8) & 0xff;
- tag[3] = (axis->tag >> 0) & 0xff;
- tag[4] = '\0';
- value = gtk_adjustment_get_value (axis->adjustment);
- g_string_append_printf (s, "%s%s=%s", sep, tag, g_ascii_dtostr (buf, sizeof(buf), value));
- sep = ",";
- }
-}
-
-static void
-adjustment_changed (GtkAdjustment *adjustment,
- Axis *axis)
-{
- GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (axis->fontchooser);
- GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
- PangoFontDescription *font_desc;
- GString *s;
-
- priv->updating_variations = TRUE;
-
- s = g_string_new ("");
- add_font_variations (fontchooser, s);
-
- if (s->len > 0)
- {
- font_desc = pango_font_description_new ();
- pango_font_description_set_variations (font_desc, s->str);
- gtk_font_chooser_widget_take_font_desc (fontchooser, font_desc);
- }
-
- g_string_free (s, TRUE);
-
- priv->updating_variations = FALSE;
-}
-
-static gboolean
-should_show_axis (FT_Var_Axis *ax)
-{
- /* FIXME use FT_Get_Var_Axis_Flags */
- if (ax->tag == FT_MAKE_TAG ('o', 'p', 's', 'z'))
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-is_named_instance (FT_Face face)
-{
- return (face->face_index >> 16) > 0;
-}
-
-static struct {
- guint32 tag;
- const char *name;
-} axis_names[] = {
- { FT_MAKE_TAG ('w', 'd', 't', 'h'), N_("Width") },
- { FT_MAKE_TAG ('w', 'g', 'h', 't'), N_("Weight") },
- { FT_MAKE_TAG ('i', 't', 'a', 'l'), N_("Italic") },
- { FT_MAKE_TAG ('s', 'l', 'n', 't'), N_("Slant") },
- { FT_MAKE_TAG ('o', 'p', 's', 'z'), N_("Optical Size") },
-};
-
-static gboolean
-add_axis (GtkFontChooserWidget *fontchooser,
- FT_Face face,
- FT_Var_Axis *ax,
- FT_Fixed value,
- int row)
-{
- GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
- Axis *axis;
- const char *name;
- int i;
-
- axis = g_new (Axis, 1);
- axis->tag = ax->tag;
- axis->fontchooser = GTK_WIDGET (fontchooser);
-
- name = ax->name;
- for (i = 0; i < G_N_ELEMENTS (axis_names); i++)
- {
- if (axis_names[i].tag == ax->tag)
- {
- name = _(axis_names[i].name);
- break;
- }
- }
- axis->label = gtk_label_new (name);
- gtk_widget_show (axis->label);
- gtk_widget_set_halign (axis->label, GTK_ALIGN_START);
- gtk_widget_set_valign (axis->label, GTK_ALIGN_BASELINE);
- gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->label, 0, row, 1, 1);
- axis->adjustment = gtk_adjustment_new ((double)FixedToFloat(value),
- (double)FixedToFloat(ax->minimum),
- (double)FixedToFloat(ax->maximum),
- 1.0, 10.0, 0.0);
- axis->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, axis->adjustment);
- gtk_widget_show (axis->scale);
- gtk_scale_add_mark (GTK_SCALE (axis->scale), (double)FixedToFloat(ax->def), GTK_POS_TOP, NULL);
- gtk_widget_set_valign (axis->scale, GTK_ALIGN_BASELINE);
- gtk_widget_set_hexpand (axis->scale, TRUE);
- gtk_widget_set_size_request (axis->scale, 100, -1);
- gtk_scale_set_draw_value (GTK_SCALE (axis->scale), FALSE);
- gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->scale, 1, row, 1, 1);
- axis->spin = gtk_spin_button_new (axis->adjustment, 0, 0);
- gtk_widget_show (axis->spin);
- g_signal_connect (axis->spin, "output", G_CALLBACK (output_cb), fontchooser);
- gtk_widget_set_valign (axis->spin, GTK_ALIGN_BASELINE);
- gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->spin, 2, row, 1, 1);
-
- g_hash_table_add (priv->axes, axis);
-
- adjustment_changed (axis->adjustment, axis);
- g_signal_connect (axis->adjustment, "value-changed", G_CALLBACK (adjustment_changed), axis);
- if (is_named_instance (face) || !should_show_axis (ax))
- {
- gtk_widget_hide (axis->label);
- gtk_widget_hide (axis->scale);
- gtk_widget_hide (axis->spin);
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchooser)
-{
- GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
- PangoFont *pango_font;
- FT_Face ft_face;
- FT_MM_Var *ft_mm_var;
- FT_Error ret;
- gboolean has_axis = FALSE;
-
- if (priv->updating_variations)
- return FALSE;
-
- g_hash_table_foreach (priv->axes, axis_remove, NULL);
- g_hash_table_remove_all (priv->axes);
-
- if ((priv->level & GTK_FONT_CHOOSER_LEVEL_VARIATIONS) == 0)
- return FALSE;
-
- pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)),
- priv->font_desc);
- ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font));
-
- ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
- if (ret == 0)
- {
- int i;
- FT_Fixed *coords;
-
- coords = g_new (FT_Fixed, ft_mm_var->num_axis);
- for (i = 0; i < ft_mm_var->num_axis; i++)
- coords[i] = ft_mm_var->axis[i].def;
-
- if (ft_face->face_index > 0)
- {
- int instance_id = ft_face->face_index >> 16;
- if (instance_id && instance_id <= ft_mm_var->num_namedstyles)
- {
- FT_Var_Named_Style *instance = &ft_mm_var->namedstyle[instance_id - 1];
- memcpy (coords, instance->coords, ft_mm_var->num_axis * sizeof (*coords));
- }
- }
-
- for (i = 0; i < ft_mm_var->num_axis; i++)
- {
- if (add_axis (fontchooser, ft_face, &ft_mm_var->axis[i], coords[i], i + 4))
- has_axis = TRUE;
- }
-
- g_free (coords);
- free (ft_mm_var);
- }
-
- pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
- g_object_unref (pango_font);
-
- return has_axis;
-}
-
-/* OpenType features */
-
-/* look for a lang / script combination that matches the
- * language property and is supported by the hb_face. If
- * none is found, return the default lang / script tags.
- */
-static void
-find_language_and_script (GtkFontChooserWidget *fontchooser,
- hb_face_t *hb_face,
- hb_tag_t *lang_tag,
- hb_tag_t *script_tag)
-{
- GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
- gint i, j, k;
- hb_tag_t scripts[80];
- unsigned int n_scripts;
- unsigned int count;
- hb_tag_t table[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS };
- hb_language_t lang;
- const char *langname, *p;
-
- langname = pango_language_to_string (priv->language);
- p = strchr (langname, '-');
- lang = hb_language_from_string (langname, p ? p - langname : -1);
-
- n_scripts = 0;
- for (i = 0; i < 2; i++)
- {
- count = G_N_ELEMENTS (scripts);
- hb_ot_layout_table_get_script_tags (hb_face, table[i], n_scripts, &count, scripts);
- n_scripts += count;
- }
-
- for (j = 0; j < n_scripts; j++)
- {
- hb_tag_t languages[80];
- unsigned int n_languages;
-
- n_languages = 0;
- for (i = 0; i < 2; i++)
- {
- count = G_N_ELEMENTS (languages);
- hb_ot_layout_script_get_language_tags (hb_face, table[i], j, n_languages, &count, languages);
- n_languages += count;
- }
-
- for (k = 0; k < n_languages; k++)
- {
- if (lang == hb_ot_tag_to_language (languages[k]))
- {
- *script_tag = scripts[j];
- *lang_tag = languages[k];
- return;
- }
- }
- }
-
- *lang_tag = HB_OT_TAG_DEFAULT_LANGUAGE;
- *script_tag = HB_OT_TAG_DEFAULT_SCRIPT;
-}
-
-typedef struct {
- hb_tag_t tag;
- const char *name;
- GtkWidget *top;
- GtkWidget *feat;
- GtkWidget *example;
-} FeatureItem;
-
-static const char *
-get_feature_display_name (hb_tag_t tag)
-{
- int i;
-
- for (i = 0; i < G_N_ELEMENTS (open_type_layout_features); i++)
- {
- if (tag == open_type_layout_features[i].tag)
- return g_dpgettext2 (NULL, "OpenType layout", open_type_layout_features[i].name);
- }
-
- return NULL;
-}
-
-static void
-set_inconsistent (GtkCheckButton *button,
- gboolean inconsistent)
-{
- if (inconsistent)
- gtk_widget_set_state_flags (GTK_WIDGET (button), GTK_STATE_FLAG_INCONSISTENT, FALSE);
- else
- gtk_widget_unset_state_flags (GTK_WIDGET (button), GTK_STATE_FLAG_INCONSISTENT);
-// gtk_widget_set_opacity (gtk_widget_get_first_child (GTK_WIDGET (button)), inconsistent ? 0.0 : 1.0);
-}
-
-static void
-feat_clicked (GtkWidget *feat,
- gpointer data)
-{
- g_signal_handlers_block_by_func (feat, feat_clicked, NULL);
-
- if (gtk_widget_get_state_flags (feat) & GTK_STATE_FLAG_INCONSISTENT)
- {
- set_inconsistent (GTK_CHECK_BUTTON (feat), FALSE);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (feat), TRUE);
- }
-
- g_signal_handlers_unblock_by_func (feat, feat_clicked, NULL);
-}
-
-static void
-feat_pressed (GtkGesture *gesture,
- int n_press,
- double x,
- double y,
- GtkWidget *feat)
-{
- gboolean inconsistent;
-
- inconsistent = (gtk_widget_get_state_flags (feat) & GTK_STATE_FLAG_INCONSISTENT) != 0;
- set_inconsistent (GTK_CHECK_BUTTON (feat), !inconsistent);
-}
-
-static char *
-find_affected_text (hb_tag_t feature_tag,
- hb_face_t *hb_face,
- hb_tag_t script_tag,
- hb_tag_t lang_tag,
- int max_chars)
-{
- unsigned int script_index = 0;
- unsigned int lang_index = 0;
- unsigned int feature_index = 0;
- GString *chars;
-
- chars = g_string_new ("");
-
- hb_ot_layout_table_find_script (hb_face, HB_OT_TAG_GSUB, script_tag, &script_index);
- hb_ot_layout_script_find_language (hb_face, HB_OT_TAG_GSUB, script_index, lang_tag, &lang_index);
- if (hb_ot_layout_language_find_feature (hb_face, HB_OT_TAG_GSUB, script_index, lang_index, feature_tag,
&feature_index))
- {
- unsigned int lookup_indexes[32];
- unsigned int lookup_count = 32;
- int count;
- int n_chars = 0;
-
- count = hb_ot_layout_feature_get_lookups (hb_face,
- HB_OT_TAG_GSUB,
- feature_index,
- 0,
- &lookup_count,
- lookup_indexes);
- if (count > 0)
- {
- hb_set_t* glyphs_before = NULL;
- hb_set_t* glyphs_input = NULL;
- hb_set_t* glyphs_after = NULL;
- hb_set_t* glyphs_output = NULL;
- hb_font_t *hb_font = NULL;
- hb_codepoint_t gid;
-
- glyphs_input = hb_set_create ();
-
- // XXX For now, just look at first index
- hb_ot_layout_lookup_collect_glyphs (hb_face,
- HB_OT_TAG_GSUB,
- lookup_indexes[0],
- glyphs_before,
- glyphs_input,
- glyphs_after,
- glyphs_output);
-
- hb_font = hb_font_create (hb_face);
- hb_ft_font_set_funcs (hb_font);
-
- gid = -1;
- while (hb_set_next (glyphs_input, &gid)) {
- hb_codepoint_t ch;
- if (n_chars == max_chars)
- {
- g_string_append (chars, "…");
- break;
- }
- for (ch = 0; ch < 0xffff; ch++) {
- hb_codepoint_t glyph = 0;
- hb_font_get_nominal_glyph (hb_font, ch, &glyph);
- if (glyph == gid) {
- g_string_append_unichar (chars, (gunichar)ch);
- n_chars++;
- break;
- }
- }
- }
- hb_set_destroy (glyphs_input);
- hb_font_destroy (hb_font);
- }
- }
-
- return g_string_free (chars, FALSE);
-}
-
-static void
-update_feature_example (FeatureItem *item,
- hb_face_t *hb_face,
- hb_tag_t script_tag,
- hb_tag_t lang_tag,
- PangoFontDescription *font_desc)
-{
- const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL };
- const char *number_case[] = { "xxxx", "lnum", "onum", NULL };
- const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL };
- const char *number_formatting[] = { "zero", "nalt", NULL };
- const char *char_variants[] = {
- "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand",
- "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
- "ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20",
- NULL };
-
- if (g_strv_contains (number_case, item->name) ||
- g_strv_contains (number_spacing, item->name))
- {
- PangoAttrList *attrs;
- PangoAttribute *attr;
- PangoFontDescription *desc;
- char *str;
-
- attrs = pango_attr_list_new ();
-
- desc = pango_font_description_copy (font_desc);
- pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
- pango_attr_list_insert (attrs, pango_attr_font_desc_new (desc));
- pango_font_description_free (desc);
- str = g_strconcat (item->name, " 1", NULL);
- attr = pango_attr_font_features_new (str);
- pango_attr_list_insert (attrs, attr);
-
- gtk_label_set_text (GTK_LABEL (item->example), "0123456789");
- gtk_label_set_attributes (GTK_LABEL (item->example), attrs);
-
- pango_attr_list_unref (attrs);
- }
- else if (g_strv_contains (letter_case, item->name) ||
- g_strv_contains (number_formatting, item->name) ||
- g_strv_contains (char_variants, item->name))
- {
- char *input = NULL;
- char *text;
-
- if (strcmp (item->name, "case") == 0)
- input = g_strdup ("A-B[Cq]");
- else if (g_strv_contains (letter_case, item->name))
- input = g_strdup ("AaBbCc…");
- else if (strcmp (item->name, "zero") == 0)
- input = g_strdup ("0");
- else if (strcmp (item->name, "nalt") == 0)
- input = find_affected_text (item->tag, hb_face, script_tag, lang_tag, 3);
- else
- input = find_affected_text (item->tag, hb_face, script_tag, lang_tag, 10);
-
- if (input[0] != '\0')
- {
- PangoAttrList *attrs;
- PangoAttribute *attr;
- PangoFontDescription *desc;
- char *str;
-
- text = g_strconcat (input, " ⟶ ", input, NULL);
-
- attrs = pango_attr_list_new ();
-
- desc = pango_font_description_copy (font_desc);
- pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
- pango_attr_list_insert (attrs, pango_attr_font_desc_new (desc));
- pango_font_description_free (desc);
- str = g_strconcat (item->name, " 0", NULL);
- attr = pango_attr_font_features_new (str);
- attr->start_index = 0;
- attr->end_index = strlen (input);
- pango_attr_list_insert (attrs, attr);
- str = g_strconcat (item->name, " 1", NULL);
- attr = pango_attr_font_features_new (str);
- attr->start_index = strlen (input) + strlen (" ⟶ ");
- attr->end_index = attr->start_index + strlen (input);
- pango_attr_list_insert (attrs, attr);
-
- gtk_label_set_text (GTK_LABEL (item->example), text);
- gtk_label_set_attributes (GTK_LABEL (item->example), attrs);
-
- g_free (text);
- pango_attr_list_unref (attrs);
- }
- else
- gtk_label_set_markup (GTK_LABEL (item->example), "");
- g_free (input);
- }
-}
-
-static void
-add_check_group (GtkFontChooserWidget *fontchooser,
- const char *title,
- const char **tags)
-{
- GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
- GtkWidget *label;
- GtkWidget *group;
- PangoAttrList *attrs;
- int i;
-
- group = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_widget_show (group);
- gtk_widget_set_halign (group, GTK_ALIGN_FILL);
-
- label = gtk_label_new (title);
- gtk_widget_show (label);
- gtk_label_set_xalign (GTK_LABEL (label), 0.0);
- gtk_widget_set_halign (label, GTK_ALIGN_START);
- g_object_set (label, "margin-top", 10, "margin-bottom", 10, NULL);
- attrs = pango_attr_list_new ();
- pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
- gtk_label_set_attributes (GTK_LABEL (label), attrs);
- pango_attr_list_unref (attrs);
- gtk_container_add (GTK_CONTAINER (group), label);
-
- for (i = 0; tags[i]; i++)
- {
- hb_tag_t tag;
- GtkWidget *feat;
- FeatureItem *item;
- GtkGesture *gesture;
- GtkWidget *box;
- GtkWidget *example;
-
- tag = hb_tag_from_string (tags[i], -1);
-
- feat = gtk_check_button_new_with_label (get_feature_display_name (tag));
- gtk_widget_show (feat);
- set_inconsistent (GTK_CHECK_BUTTON (feat), TRUE);
- g_signal_connect_swapped (feat, "notify::active", G_CALLBACK (update_font_features), fontchooser);
- g_signal_connect_swapped (feat, "notify::inconsistent", G_CALLBACK (update_font_features),
fontchooser);
- g_signal_connect (feat, "clicked", G_CALLBACK (feat_clicked), NULL);
-
- gesture = gtk_gesture_multi_press_new (feat);
- g_object_set_data_full (G_OBJECT (feat), "press", gesture, g_object_unref);
-
- gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
- g_signal_connect (gesture, "pressed", G_CALLBACK (feat_pressed), feat);
-
- example = gtk_label_new ("");
- gtk_widget_show (example);
- gtk_label_set_selectable (GTK_LABEL (example), TRUE);
- gtk_widget_set_halign (example, GTK_ALIGN_START);
-
- box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
- gtk_widget_show (box);
- gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
- gtk_container_add (GTK_CONTAINER (box), feat);
- gtk_container_add (GTK_CONTAINER (box), example);
- gtk_container_add (GTK_CONTAINER (group), box);
-
- item = g_new (FeatureItem, 1);
- item->name = tags[i];
- item->tag = tag;
- item->top = box;
- item->feat = feat;
- item->example = example;
-
- priv->feature_items = g_list_prepend (priv->feature_items, item);
- }
-
- gtk_container_add (GTK_CONTAINER (priv->feature_box), group);
-}
-
-static void
-add_radio_group (GtkFontChooserWidget *fontchooser,
- const char *title,
- const char **tags)
-{
- GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
- GtkWidget *label;
- GtkWidget *group;
- int i;
- GtkWidget *group_button = NULL;
- PangoAttrList *attrs;
-
- group = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_widget_show (group);
- gtk_widget_set_halign (group, GTK_ALIGN_FILL);
-
- label = gtk_label_new (title);
- gtk_widget_show (label);
- gtk_label_set_xalign (GTK_LABEL (label), 0.0);
- gtk_widget_set_halign (label, GTK_ALIGN_START);
- g_object_set (label, "margin-top", 10, "margin-bottom", 10, NULL);
- attrs = pango_attr_list_new ();
- pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
- gtk_label_set_attributes (GTK_LABEL (label), attrs);
- pango_attr_list_unref (attrs);
- gtk_container_add (GTK_CONTAINER (group), label);
-
- for (i = 0; tags[i]; i++)
- {
- hb_tag_t tag;
- GtkWidget *feat;
- FeatureItem *item;
- const char *name;
- GtkWidget *box;
- GtkWidget *example;
-
- tag = hb_tag_from_string (tags[i], -1);
- name = get_feature_display_name (tag);
-
- feat = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (group_button),
- name ? name : _("Default"));
- gtk_widget_show (feat);
- if (group_button == NULL)
- group_button = feat;
-
- g_signal_connect_swapped (feat, "notify::active", G_CALLBACK (update_font_features), fontchooser);
- g_object_set_data (G_OBJECT (feat), "default", group_button);
-
- example = gtk_label_new ("");
- gtk_widget_show (example);
- gtk_label_set_selectable (GTK_LABEL (example), TRUE);
- gtk_widget_set_halign (example, GTK_ALIGN_START);
-
- box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
- gtk_widget_show (box);
- gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
- gtk_container_add (GTK_CONTAINER (box), feat);
- gtk_container_add (GTK_CONTAINER (box), example);
- gtk_container_add (GTK_CONTAINER (group), box);
-
- item = g_new (FeatureItem, 1);
- item->name = tags[i];
- item->tag = tag;
- item->top = box;
- item->feat = feat;
- item->example = example;
-
- priv->feature_items = g_list_prepend (priv->feature_items, item);
- }
-
- gtk_container_add (GTK_CONTAINER (priv->feature_box), group);
-}
-
-static void
-gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser)
-{
- const char *ligatures[] = { "liga", "dlig", "hlig", "clig", NULL };
- const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL };
- const char *number_case[] = { "xxxx", "lnum", "onum", NULL };
- const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL };
- const char *number_formatting[] = { "zero", "nalt", NULL };
- const char *char_variants[] = {
- "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand",
- "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
- "ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20",
- NULL };
-
- add_check_group (fontchooser, _("Ligatures"), ligatures);
- add_check_group (fontchooser, _("Letter Case"), letter_case);
- add_radio_group (fontchooser, _("Number Case"), number_case);
- add_radio_group (fontchooser, _("Number Spacing"), number_spacing);
- add_check_group (fontchooser, _("Number Formatting"), number_formatting);
- add_check_group (fontchooser, _("Character Variants"), char_variants);
-
- update_font_features (fontchooser);
-}
-
-static gboolean
-gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
-{
- GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
- PangoFont *pango_font;
- FT_Face ft_face;
- hb_font_t *hb_font;
- hb_tag_t script_tag;
- hb_tag_t lang_tag;
- guint script_index = 0;
- guint lang_index = 0;
- int i, j;
- GList *l;
- gboolean has_feature = FALSE;
-
- for (l = priv->feature_items; l; l = l->next)
- {
- FeatureItem *item = l->data;
- gtk_widget_hide (item->top);
- gtk_widget_hide (gtk_widget_get_parent (item->top));
- }
-
- if ((priv->level & GTK_FONT_CHOOSER_LEVEL_FEATURES) == 0)
- return FALSE;
-
- pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)),
- priv->font_desc);
- ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font)),
- hb_font = hb_ft_font_create (ft_face, NULL);
-
- if (hb_font)
- {
- hb_tag_t table[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS };
- hb_face_t *hb_face;
- hb_tag_t features[80];
- unsigned int count;
- unsigned int n_features;
-
- hb_face = hb_font_get_face (hb_font);
-
- find_language_and_script (fontchooser, hb_face, &lang_tag, &script_tag);
-
- n_features = 0;
- for (i = 0; i < 2; i++)
- {
- hb_ot_layout_table_find_script (hb_face, table[i], script_tag, &script_index);
- hb_ot_layout_script_find_language (hb_face, table[i], script_index, lang_tag, &lang_index);
- count = G_N_ELEMENTS (features);
- hb_ot_layout_language_get_feature_tags (hb_face,
- table[i],
- script_index,
- lang_index,
- n_features,
- &count,
- features);
- n_features += count;
- }
-
- for (j = 0; j < n_features; j++)
- {
- for (l = priv->feature_items; l; l = l->next)
- {
- FeatureItem *item = l->data;
- if (item->tag != features[j])
- continue;
-
- has_feature = TRUE;
- gtk_widget_show (item->top);
- gtk_widget_show (gtk_widget_get_parent (item->top));
-
- update_feature_example (item, hb_face, script_tag, lang_tag, priv->font_desc);
-
- if (GTK_IS_RADIO_BUTTON (item->feat))
- {
- GtkWidget *def = GTK_WIDGET (g_object_get_data (G_OBJECT (item->feat), "default"));
- gtk_widget_show (gtk_widget_get_parent (def));
- }
- else if (GTK_IS_CHECK_BUTTON (item->feat))
- {
- set_inconsistent (GTK_CHECK_BUTTON (item->feat), TRUE);
- }
- }
- }
-
- hb_face_destroy (hb_face);
- }
-
- pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
- g_object_unref (pango_font);
-
- return has_feature;
-}
-
-static void
-update_font_features (GtkFontChooserWidget *fontchooser)
-{
- GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
- GString *s;
- GList *l;
-
- s = g_string_new ("");
-
- for (l = priv->feature_items; l; l = l->next)
- {
- FeatureItem *item = l->data;
-
- if (!gtk_widget_is_sensitive (item->feat))
- continue;
-
- if (GTK_IS_RADIO_BUTTON (item->feat))
- {
- if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item->feat)) &&
- strcmp (item->name, "xxxx") != 0)
- {
- g_string_append_printf (s, "%s\"%s\" %d", s->len > 0 ? ", " : "", item->name, 1);
- }
- }
- else if (GTK_IS_CHECK_BUTTON (item->feat))
- {
- if (gtk_widget_get_state_flags (item->feat) & GTK_STATE_FLAG_INCONSISTENT)
- continue;
-
- g_string_append_printf (s, "%s\"%s\" %d",
- s->len > 0 ? ", " : "", item->name,
- gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item->feat)));
- }
- }
-
- if (g_strcmp0 (priv->font_features, s->str) != 0)
- {
- g_free (priv->font_features);
- priv->font_features = g_string_free (s, FALSE);
- g_object_notify (G_OBJECT (fontchooser), "font-features");
- }
- else
- g_string_free (s, TRUE);
-
- gtk_font_chooser_widget_update_preview_attributes (fontchooser);
-}
-
-#endif
-
static void
gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser,
const PangoFontDescription *font_desc,
@@ -2305,7 +1379,7 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser
gtk_font_chooser_widget_update_marks (fontchooser);
-#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
+#if defined(HAVE_HARFBUZZ) && defined (HAVE_PANGOFT)
if (gtk_font_chooser_widget_update_font_features (fontchooser))
has_tweak = TRUE;
if (gtk_font_chooser_widget_update_font_variations (fontchooser))
@@ -2321,7 +1395,7 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser
g_object_notify (G_OBJECT (fontchooser), "font-desc");
}
-static void
+void
gtk_font_chooser_widget_take_font_desc (GtkFontChooserWidget *fontchooser,
PangoFontDescription *font_desc)
{
diff --git a/gtk/gtkfontfeatures.c b/gtk/gtkfontfeatures.c
new file mode 100644
index 0000000000..168cc26756
--- /dev/null
+++ b/gtk/gtkfontfeatures.c
@@ -0,0 +1,921 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 Alberto Ruiz <aruiz gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkfontchooserwidget.h"
+#include "gtkfontchooserwidgetprivate.h"
+#include "gtkfontfeaturesprivate.h"
+
+#include "gtkadjustment.h"
+#include "gtkcheckbutton.h"
+#include "gtkgrid.h"
+#include "gtkintl.h"
+#include "gtklabel.h"
+#include "gtkscale.h"
+#include "gtkspinbutton.h"
+#include "gtkwidget.h"
+
+#include "gtkradiobutton.h"
+#include "gtkgesturemultipress.h"
+
+#if defined (HAVE_HARFBUZZ) && defined (HAVE_PANGOFT)
+#include <pango/pangofc-font.h>
+#include <hb.h>
+#include <hb-ot.h>
+#include <hb-ft.h>
+#include <freetype/freetype.h>
+#include <freetype/ftmm.h>
+#include "language-names.h"
+#include "script-names.h"
+#include "open-type-layout.h"
+
+static void update_font_features (GtkFontChooserWidget *fontchooser);
+
+guint
+axis_hash (gconstpointer v)
+{
+ const Axis *a = v;
+
+ return a->tag;
+}
+
+gboolean
+axis_equal (gconstpointer v1, gconstpointer v2)
+{
+ const Axis *a1 = v1;
+ const Axis *a2 = v2;
+
+ return a1->tag == a2->tag;
+}
+
+void
+axis_free (gpointer v)
+{
+ Axis *a = v;
+
+ g_free (a);
+}
+
+static void
+axis_remove (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ Axis *a = value;
+
+ gtk_widget_destroy (a->label);
+ gtk_widget_destroy (a->scale);
+ gtk_widget_destroy (a->spin);
+}
+
+/* OpenType variations */
+
+#define FixedToFloat(f) (((float)(f))/65536.0)
+
+static void
+add_font_variations (GtkFontChooserWidget *fontchooser,
+ GString *s)
+{
+ GHashTableIter iter;
+ Axis *axis;
+ const char *sep = "";
+ char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_hash_table_iter_init (&iter, fontchooser->priv->axes);
+ while (g_hash_table_iter_next (&iter, (gpointer *)NULL, (gpointer *)&axis))
+ {
+ char tag[5];
+ double value;
+
+ tag[0] = (axis->tag >> 24) & 0xff;
+ tag[1] = (axis->tag >> 16) & 0xff;
+ tag[2] = (axis->tag >> 8) & 0xff;
+ tag[3] = (axis->tag >> 0) & 0xff;
+ tag[4] = '\0';
+ value = gtk_adjustment_get_value (axis->adjustment);
+ g_string_append_printf (s, "%s%s=%s", sep, tag, g_ascii_dtostr (buf, sizeof(buf), value));
+ sep = ",";
+ }
+}
+
+static void
+adjustment_changed (GtkAdjustment *adjustment,
+ Axis *axis)
+{
+ GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (axis->fontchooser);
+ GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+ PangoFontDescription *font_desc;
+ GString *s;
+
+ priv->updating_variations = TRUE;
+
+ s = g_string_new ("");
+ add_font_variations (fontchooser, s);
+
+ if (s->len > 0)
+ {
+ font_desc = pango_font_description_new ();
+ pango_font_description_set_variations (font_desc, s->str);
+ gtk_font_chooser_widget_take_font_desc (fontchooser, font_desc);
+ }
+
+ g_string_free (s, TRUE);
+
+ priv->updating_variations = FALSE;
+}
+
+static gboolean
+should_show_axis (FT_Var_Axis *ax)
+{
+ /* FIXME use FT_Get_Var_Axis_Flags */
+ if (ax->tag == FT_MAKE_TAG ('o', 'p', 's', 'z'))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+is_named_instance (FT_Face face)
+{
+ return (face->face_index >> 16) > 0;
+}
+
+static struct {
+ guint32 tag;
+ const char *name;
+} axis_names[] = {
+ { FT_MAKE_TAG ('w', 'd', 't', 'h'), N_("Width") },
+ { FT_MAKE_TAG ('w', 'g', 'h', 't'), N_("Weight") },
+ { FT_MAKE_TAG ('i', 't', 'a', 'l'), N_("Italic") },
+ { FT_MAKE_TAG ('s', 'l', 'n', 't'), N_("Slant") },
+ { FT_MAKE_TAG ('o', 'p', 's', 'z'), N_("Optical Size") },
+};
+
+static gboolean
+add_axis (GtkFontChooserWidget *fontchooser,
+ FT_Face face,
+ FT_Var_Axis *ax,
+ FT_Fixed value,
+ int row)
+{
+ GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+ Axis *axis;
+ const char *name;
+ int i;
+
+ axis = g_new (Axis, 1);
+ axis->tag = ax->tag;
+ axis->fontchooser = GTK_WIDGET (fontchooser);
+
+ name = ax->name;
+ for (i = 0; i < G_N_ELEMENTS (axis_names); i++)
+ {
+ if (axis_names[i].tag == ax->tag)
+ {
+ name = _(axis_names[i].name);
+ break;
+ }
+ }
+ axis->label = gtk_label_new (name);
+ gtk_widget_show (axis->label);
+ gtk_widget_set_halign (axis->label, GTK_ALIGN_START);
+ gtk_widget_set_valign (axis->label, GTK_ALIGN_BASELINE);
+ gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->label, 0, row, 1, 1);
+ axis->adjustment = gtk_adjustment_new ((double)FixedToFloat(value),
+ (double)FixedToFloat(ax->minimum),
+ (double)FixedToFloat(ax->maximum),
+ 1.0, 10.0, 0.0);
+ axis->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, axis->adjustment);
+ gtk_widget_show (axis->scale);
+ gtk_scale_add_mark (GTK_SCALE (axis->scale), (double)FixedToFloat(ax->def), GTK_POS_TOP, NULL);
+ gtk_widget_set_valign (axis->scale, GTK_ALIGN_BASELINE);
+ gtk_widget_set_hexpand (axis->scale, TRUE);
+ gtk_widget_set_size_request (axis->scale, 100, -1);
+ gtk_scale_set_draw_value (GTK_SCALE (axis->scale), FALSE);
+ gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->scale, 1, row, 1, 1);
+ axis->spin = gtk_spin_button_new (axis->adjustment, 0, 0);
+ gtk_widget_show (axis->spin);
+ g_signal_connect (axis->spin, "output", G_CALLBACK (output_cb), fontchooser);
+ gtk_widget_set_valign (axis->spin, GTK_ALIGN_BASELINE);
+ gtk_grid_attach (GTK_GRID (priv->axis_grid), axis->spin, 2, row, 1, 1);
+
+ g_hash_table_add (priv->axes, axis);
+
+ adjustment_changed (axis->adjustment, axis);
+ g_signal_connect (axis->adjustment, "value-changed", G_CALLBACK (adjustment_changed), axis);
+ if (is_named_instance (face) || !should_show_axis (ax))
+ {
+ gtk_widget_hide (axis->label);
+ gtk_widget_hide (axis->scale);
+ gtk_widget_hide (axis->spin);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchooser)
+{
+ GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+ PangoFont *pango_font;
+ FT_Face ft_face;
+ FT_MM_Var *ft_mm_var;
+ FT_Error ret;
+ gboolean has_axis = FALSE;
+
+ if (priv->updating_variations)
+ return FALSE;
+
+ g_hash_table_foreach (priv->axes, axis_remove, NULL);
+ g_hash_table_remove_all (priv->axes);
+
+ if ((priv->level & GTK_FONT_CHOOSER_LEVEL_VARIATIONS) == 0)
+ return FALSE;
+
+ pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)),
+ priv->font_desc);
+
+ ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font));
+
+ ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
+ if (ret == 0)
+ {
+ int i;
+ FT_Fixed *coords;
+
+ coords = g_new (FT_Fixed, ft_mm_var->num_axis);
+ for (i = 0; i < ft_mm_var->num_axis; i++)
+ coords[i] = ft_mm_var->axis[i].def;
+
+ if (ft_face->face_index > 0)
+ {
+ int instance_id = ft_face->face_index >> 16;
+ if (instance_id && instance_id <= ft_mm_var->num_namedstyles)
+ {
+ FT_Var_Named_Style *instance = &ft_mm_var->namedstyle[instance_id - 1];
+ memcpy (coords, instance->coords, ft_mm_var->num_axis * sizeof (*coords));
+ }
+ }
+
+ for (i = 0; i < ft_mm_var->num_axis; i++)
+ {
+ if (add_axis (fontchooser, ft_face, &ft_mm_var->axis[i], coords[i], i + 4))
+ has_axis = TRUE;
+ }
+
+ g_free (coords);
+ free (ft_mm_var);
+ }
+
+ pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
+
+ g_object_unref (pango_font);
+
+ return has_axis;
+}
+
+/* OpenType features */
+
+/* look for a lang / script combination that matches the
+ * language property and is supported by the hb_face. If
+ * none is found, return the default lang / script tags.
+ */
+static void
+find_language_and_script (GtkFontChooserWidget *fontchooser,
+ hb_face_t *hb_face,
+ hb_tag_t *lang_tag,
+ hb_tag_t *script_tag)
+{
+ GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+ gint i, j, k;
+ hb_tag_t scripts[80];
+ unsigned int n_scripts;
+ unsigned int count;
+ hb_tag_t table[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS };
+ hb_language_t lang;
+ const char *langname, *p;
+
+ langname = pango_language_to_string (priv->language);
+ p = strchr (langname, '-');
+ lang = hb_language_from_string (langname, p ? p - langname : -1);
+
+ n_scripts = 0;
+ for (i = 0; i < 2; i++)
+ {
+ count = G_N_ELEMENTS (scripts);
+ hb_ot_layout_table_get_script_tags (hb_face, table[i], n_scripts, &count, scripts);
+ n_scripts += count;
+ }
+
+ for (j = 0; j < n_scripts; j++)
+ {
+ hb_tag_t languages[80];
+ unsigned int n_languages;
+
+ n_languages = 0;
+ for (i = 0; i < 2; i++)
+ {
+ count = G_N_ELEMENTS (languages);
+ hb_ot_layout_script_get_language_tags (hb_face, table[i], j, n_languages, &count, languages);
+ n_languages += count;
+ }
+
+ for (k = 0; k < n_languages; k++)
+ {
+ if (lang == hb_ot_tag_to_language (languages[k]))
+ {
+ *script_tag = scripts[j];
+ *lang_tag = languages[k];
+ return;
+ }
+ }
+ }
+
+ *lang_tag = HB_OT_TAG_DEFAULT_LANGUAGE;
+ *script_tag = HB_OT_TAG_DEFAULT_SCRIPT;
+}
+
+typedef struct {
+ hb_tag_t tag;
+ const char *name;
+ GtkWidget *top;
+ GtkWidget *feat;
+ GtkWidget *example;
+} FeatureItem;
+
+static const char *
+get_feature_display_name (hb_tag_t tag)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (open_type_layout_features); i++)
+ {
+ if (tag == open_type_layout_features[i].tag)
+ return g_dpgettext2 (NULL, "OpenType layout", open_type_layout_features[i].name);
+ }
+
+ return NULL;
+}
+
+static void
+set_inconsistent (GtkCheckButton *button,
+ gboolean inconsistent)
+{
+ if (inconsistent)
+ gtk_widget_set_state_flags (GTK_WIDGET (button), GTK_STATE_FLAG_INCONSISTENT, FALSE);
+ else
+ gtk_widget_unset_state_flags (GTK_WIDGET (button), GTK_STATE_FLAG_INCONSISTENT);
+// gtk_widget_set_opacity (gtk_widget_get_first_child (GTK_WIDGET (button)), inconsistent ? 0.0 : 1.0);
+}
+
+static void
+feat_clicked (GtkWidget *feat,
+ gpointer data)
+{
+ g_signal_handlers_block_by_func (feat, feat_clicked, NULL);
+
+ if (gtk_widget_get_state_flags (feat) & GTK_STATE_FLAG_INCONSISTENT)
+ {
+ set_inconsistent (GTK_CHECK_BUTTON (feat), FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (feat), TRUE);
+ }
+
+ g_signal_handlers_unblock_by_func (feat, feat_clicked, NULL);
+}
+
+static void
+feat_pressed (GtkGesture *gesture,
+ int n_press,
+ double x,
+ double y,
+ GtkWidget *feat)
+{
+ gboolean inconsistent;
+
+ inconsistent = (gtk_widget_get_state_flags (feat) & GTK_STATE_FLAG_INCONSISTENT) != 0;
+ set_inconsistent (GTK_CHECK_BUTTON (feat), !inconsistent);
+}
+
+static char *
+find_affected_text (hb_tag_t feature_tag,
+ hb_face_t *hb_face,
+ hb_tag_t script_tag,
+ hb_tag_t lang_tag,
+ int max_chars)
+{
+ unsigned int script_index = 0;
+ unsigned int lang_index = 0;
+ unsigned int feature_index = 0;
+ GString *chars;
+
+ chars = g_string_new ("");
+
+ hb_ot_layout_table_find_script (hb_face, HB_OT_TAG_GSUB, script_tag, &script_index);
+ hb_ot_layout_script_find_language (hb_face, HB_OT_TAG_GSUB, script_index, lang_tag, &lang_index);
+ if (hb_ot_layout_language_find_feature (hb_face, HB_OT_TAG_GSUB, script_index, lang_index, feature_tag,
&feature_index))
+ {
+ unsigned int lookup_indexes[32];
+ unsigned int lookup_count = 32;
+ int count;
+ int n_chars = 0;
+
+ count = hb_ot_layout_feature_get_lookups (hb_face,
+ HB_OT_TAG_GSUB,
+ feature_index,
+ 0,
+ &lookup_count,
+ lookup_indexes);
+ if (count > 0)
+ {
+ hb_set_t* glyphs_before = NULL;
+ hb_set_t* glyphs_input = NULL;
+ hb_set_t* glyphs_after = NULL;
+ hb_set_t* glyphs_output = NULL;
+ hb_font_t *hb_font = NULL;
+ hb_codepoint_t gid;
+
+ glyphs_input = hb_set_create ();
+
+ // XXX For now, just look at first index
+ hb_ot_layout_lookup_collect_glyphs (hb_face,
+ HB_OT_TAG_GSUB,
+ lookup_indexes[0],
+ glyphs_before,
+ glyphs_input,
+ glyphs_after,
+ glyphs_output);
+
+ hb_font = hb_font_create (hb_face);
+ hb_ft_font_set_funcs (hb_font);
+
+ gid = -1;
+ while (hb_set_next (glyphs_input, &gid)) {
+ hb_codepoint_t ch;
+ if (n_chars == max_chars)
+ {
+ g_string_append (chars, "…");
+ break;
+ }
+ for (ch = 0; ch < 0xffff; ch++) {
+ hb_codepoint_t glyph = 0;
+ hb_font_get_nominal_glyph (hb_font, ch, &glyph);
+ if (glyph == gid) {
+ g_string_append_unichar (chars, (gunichar)ch);
+ n_chars++;
+ break;
+ }
+ }
+ }
+ hb_set_destroy (glyphs_input);
+ hb_font_destroy (hb_font);
+ }
+ }
+
+ return g_string_free (chars, FALSE);
+}
+
+static void
+update_feature_example (FeatureItem *item,
+ hb_face_t *hb_face,
+ hb_tag_t script_tag,
+ hb_tag_t lang_tag,
+ PangoFontDescription *font_desc)
+{
+ const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL };
+ const char *number_case[] = { "xxxx", "lnum", "onum", NULL };
+ const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL };
+ const char *number_formatting[] = { "zero", "nalt", NULL };
+ const char *char_variants[] = {
+ "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand",
+ "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
+ "ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20",
+ NULL };
+
+ if (g_strv_contains (number_case, item->name) ||
+ g_strv_contains (number_spacing, item->name))
+ {
+ PangoAttrList *attrs;
+ PangoAttribute *attr;
+ PangoFontDescription *desc;
+ char *str;
+
+ attrs = pango_attr_list_new ();
+
+ desc = pango_font_description_copy (font_desc);
+ pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
+ pango_attr_list_insert (attrs, pango_attr_font_desc_new (desc));
+ pango_font_description_free (desc);
+ str = g_strconcat (item->name, " 1", NULL);
+ attr = pango_attr_font_features_new (str);
+ pango_attr_list_insert (attrs, attr);
+
+ gtk_label_set_text (GTK_LABEL (item->example), "0123456789");
+ gtk_label_set_attributes (GTK_LABEL (item->example), attrs);
+
+ pango_attr_list_unref (attrs);
+ }
+ else if (g_strv_contains (letter_case, item->name) ||
+ g_strv_contains (number_formatting, item->name) ||
+ g_strv_contains (char_variants, item->name))
+ {
+ char *input = NULL;
+ char *text;
+
+ if (strcmp (item->name, "case") == 0)
+ input = g_strdup ("A-B[Cq]");
+ else if (g_strv_contains (letter_case, item->name))
+ input = g_strdup ("AaBbCc…");
+ else if (strcmp (item->name, "zero") == 0)
+ input = g_strdup ("0");
+ else if (strcmp (item->name, "nalt") == 0)
+ input = find_affected_text (item->tag, hb_face, script_tag, lang_tag, 3);
+ else
+ input = find_affected_text (item->tag, hb_face, script_tag, lang_tag, 10);
+
+ if (input[0] != '\0')
+ {
+ PangoAttrList *attrs;
+ PangoAttribute *attr;
+ PangoFontDescription *desc;
+ char *str;
+
+ text = g_strconcat (input, " ⟶ ", input, NULL);
+
+ attrs = pango_attr_list_new ();
+
+ desc = pango_font_description_copy (font_desc);
+ pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
+ pango_attr_list_insert (attrs, pango_attr_font_desc_new (desc));
+ pango_font_description_free (desc);
+ str = g_strconcat (item->name, " 0", NULL);
+ attr = pango_attr_font_features_new (str);
+ attr->start_index = 0;
+ attr->end_index = strlen (input);
+ pango_attr_list_insert (attrs, attr);
+ str = g_strconcat (item->name, " 1", NULL);
+ attr = pango_attr_font_features_new (str);
+ attr->start_index = strlen (input) + strlen (" ⟶ ");
+ attr->end_index = attr->start_index + strlen (input);
+ pango_attr_list_insert (attrs, attr);
+
+ gtk_label_set_text (GTK_LABEL (item->example), text);
+ gtk_label_set_attributes (GTK_LABEL (item->example), attrs);
+
+ g_free (text);
+ pango_attr_list_unref (attrs);
+ }
+ else
+ gtk_label_set_markup (GTK_LABEL (item->example), "");
+ g_free (input);
+ }
+}
+
+static void
+add_check_group (GtkFontChooserWidget *fontchooser,
+ const char *title,
+ const char **tags)
+{
+ GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+ GtkWidget *label;
+ GtkWidget *group;
+ PangoAttrList *attrs;
+ int i;
+
+ group = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_widget_show (group);
+ gtk_widget_set_halign (group, GTK_ALIGN_FILL);
+
+ label = gtk_label_new (title);
+ gtk_widget_show (label);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_set_halign (label, GTK_ALIGN_START);
+ g_object_set (label, "margin-top", 10, "margin-bottom", 10, NULL);
+ attrs = pango_attr_list_new ();
+ pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+ gtk_label_set_attributes (GTK_LABEL (label), attrs);
+ pango_attr_list_unref (attrs);
+ gtk_container_add (GTK_CONTAINER (group), label);
+
+ for (i = 0; tags[i]; i++)
+ {
+ hb_tag_t tag;
+ GtkWidget *feat;
+ FeatureItem *item;
+ GtkGesture *gesture;
+ GtkWidget *box;
+ GtkWidget *example;
+
+ tag = hb_tag_from_string (tags[i], -1);
+
+ feat = gtk_check_button_new_with_label (get_feature_display_name (tag));
+ gtk_widget_show (feat);
+ set_inconsistent (GTK_CHECK_BUTTON (feat), TRUE);
+ g_signal_connect_swapped (feat, "notify::active", G_CALLBACK (update_font_features), fontchooser);
+ g_signal_connect_swapped (feat, "notify::inconsistent", G_CALLBACK (update_font_features),
fontchooser);
+ g_signal_connect (feat, "clicked", G_CALLBACK (feat_clicked), NULL);
+
+ gesture = gtk_gesture_multi_press_new (feat);
+ g_object_set_data_full (G_OBJECT (feat), "press", gesture, g_object_unref);
+
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
+ g_signal_connect (gesture, "pressed", G_CALLBACK (feat_pressed), feat);
+
+ example = gtk_label_new ("");
+ gtk_widget_show (example);
+ gtk_label_set_selectable (GTK_LABEL (example), TRUE);
+ gtk_widget_set_halign (example, GTK_ALIGN_START);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+ gtk_widget_show (box);
+ gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
+ gtk_container_add (GTK_CONTAINER (box), feat);
+ gtk_container_add (GTK_CONTAINER (box), example);
+ gtk_container_add (GTK_CONTAINER (group), box);
+
+ item = g_new (FeatureItem, 1);
+ item->name = tags[i];
+ item->tag = tag;
+ item->top = box;
+ item->feat = feat;
+ item->example = example;
+
+ priv->feature_items = g_list_prepend (priv->feature_items, item);
+ }
+
+ gtk_container_add (GTK_CONTAINER (priv->feature_box), group);
+}
+
+static void
+add_radio_group (GtkFontChooserWidget *fontchooser,
+ const char *title,
+ const char **tags)
+{
+ GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+ GtkWidget *label;
+ GtkWidget *group;
+ int i;
+ GtkWidget *group_button = NULL;
+ PangoAttrList *attrs;
+
+ group = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_widget_show (group);
+ gtk_widget_set_halign (group, GTK_ALIGN_FILL);
+
+ label = gtk_label_new (title);
+ gtk_widget_show (label);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_set_halign (label, GTK_ALIGN_START);
+ g_object_set (label, "margin-top", 10, "margin-bottom", 10, NULL);
+ attrs = pango_attr_list_new ();
+ pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+ gtk_label_set_attributes (GTK_LABEL (label), attrs);
+ pango_attr_list_unref (attrs);
+ gtk_container_add (GTK_CONTAINER (group), label);
+
+ for (i = 0; tags[i]; i++)
+ {
+ hb_tag_t tag;
+ GtkWidget *feat;
+ FeatureItem *item;
+ const char *name;
+ GtkWidget *box;
+ GtkWidget *example;
+
+ tag = hb_tag_from_string (tags[i], -1);
+ name = get_feature_display_name (tag);
+
+ feat = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (group_button),
+ name ? name : _("Default"));
+ gtk_widget_show (feat);
+ if (group_button == NULL)
+ group_button = feat;
+
+ g_signal_connect_swapped (feat, "notify::active", G_CALLBACK (update_font_features), fontchooser);
+ g_object_set_data (G_OBJECT (feat), "default", group_button);
+
+ example = gtk_label_new ("");
+ gtk_widget_show (example);
+ gtk_label_set_selectable (GTK_LABEL (example), TRUE);
+ gtk_widget_set_halign (example, GTK_ALIGN_START);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+ gtk_widget_show (box);
+ gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
+ gtk_container_add (GTK_CONTAINER (box), feat);
+ gtk_container_add (GTK_CONTAINER (box), example);
+ gtk_container_add (GTK_CONTAINER (group), box);
+
+ item = g_new (FeatureItem, 1);
+ item->name = tags[i];
+ item->tag = tag;
+ item->top = box;
+ item->feat = feat;
+ item->example = example;
+
+ priv->feature_items = g_list_prepend (priv->feature_items, item);
+ }
+
+ gtk_container_add (GTK_CONTAINER (priv->feature_box), group);
+}
+
+void
+gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser)
+{
+ const char *ligatures[] = { "liga", "dlig", "hlig", "clig", NULL };
+ const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL };
+ const char *number_case[] = { "xxxx", "lnum", "onum", NULL };
+ const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL };
+ const char *number_formatting[] = { "zero", "nalt", NULL };
+ const char *char_variants[] = {
+ "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand",
+ "ss01", "ss02", "ss03", "ss04", "ss05", "ss06", "ss07", "ss08", "ss09", "ss10",
+ "ss11", "ss12", "ss13", "ss14", "ss15", "ss16", "ss17", "ss18", "ss19", "ss20",
+ NULL };
+
+ add_check_group (fontchooser, _("Ligatures"), ligatures);
+ add_check_group (fontchooser, _("Letter Case"), letter_case);
+ add_radio_group (fontchooser, _("Number Case"), number_case);
+ add_radio_group (fontchooser, _("Number Spacing"), number_spacing);
+ add_check_group (fontchooser, _("Number Formatting"), number_formatting);
+ add_check_group (fontchooser, _("Character Variants"), char_variants);
+
+ update_font_features (fontchooser);
+}
+
+gboolean
+gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser)
+{
+ GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+ PangoFont *pango_font;
+ FT_Face ft_face;
+ hb_font_t *hb_font;
+ hb_tag_t script_tag;
+ hb_tag_t lang_tag;
+ guint script_index = 0;
+ guint lang_index = 0;
+ int i, j;
+ GList *l;
+ gboolean has_feature = FALSE;
+
+ for (l = priv->feature_items; l; l = l->next)
+ {
+ FeatureItem *item = l->data;
+ gtk_widget_hide (item->top);
+ gtk_widget_hide (gtk_widget_get_parent (item->top));
+ }
+
+ if ((priv->level & GTK_FONT_CHOOSER_LEVEL_FEATURES) == 0)
+ return FALSE;
+
+ pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)),
+ priv->font_desc);
+
+ ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font));
+
+ hb_font = hb_ft_font_create (ft_face, NULL);
+
+ if (hb_font)
+ {
+ hb_tag_t table[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS };
+ hb_face_t *hb_face;
+ hb_tag_t features[80];
+ unsigned int count;
+ unsigned int n_features;
+
+ hb_face = hb_font_get_face (hb_font);
+
+ find_language_and_script (fontchooser, hb_face, &lang_tag, &script_tag);
+
+ n_features = 0;
+ for (i = 0; i < 2; i++)
+ {
+ hb_ot_layout_table_find_script (hb_face, table[i], script_tag, &script_index);
+ hb_ot_layout_script_find_language (hb_face, table[i], script_index, lang_tag, &lang_index);
+ count = G_N_ELEMENTS (features);
+ hb_ot_layout_language_get_feature_tags (hb_face,
+ table[i],
+ script_index,
+ lang_index,
+ n_features,
+ &count,
+ features);
+ n_features += count;
+ }
+
+ for (j = 0; j < n_features; j++)
+ {
+ for (l = priv->feature_items; l; l = l->next)
+ {
+ FeatureItem *item = l->data;
+ if (item->tag != features[j])
+ continue;
+
+ has_feature = TRUE;
+ gtk_widget_show (item->top);
+ gtk_widget_show (gtk_widget_get_parent (item->top));
+
+ update_feature_example (item, hb_face, script_tag, lang_tag, priv->font_desc);
+
+ if (GTK_IS_RADIO_BUTTON (item->feat))
+ {
+ GtkWidget *def = GTK_WIDGET (g_object_get_data (G_OBJECT (item->feat), "default"));
+ gtk_widget_show (gtk_widget_get_parent (def));
+ }
+ else if (GTK_IS_CHECK_BUTTON (item->feat))
+ {
+ set_inconsistent (GTK_CHECK_BUTTON (item->feat), TRUE);
+ }
+ }
+ }
+
+ hb_face_destroy (hb_face);
+ }
+
+ pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
+
+ g_object_unref (pango_font);
+
+ return has_feature;
+}
+
+static void
+update_font_features (GtkFontChooserWidget *fontchooser)
+{
+ GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
+ GString *s;
+ GList *l;
+
+ s = g_string_new ("");
+
+ for (l = priv->feature_items; l; l = l->next)
+ {
+ FeatureItem *item = l->data;
+
+ if (!gtk_widget_is_sensitive (item->feat))
+ continue;
+
+ if (GTK_IS_RADIO_BUTTON (item->feat))
+ {
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item->feat)) &&
+ strcmp (item->name, "xxxx") != 0)
+ {
+ g_string_append_printf (s, "%s\"%s\" %d", s->len > 0 ? ", " : "", item->name, 1);
+ }
+ }
+ else if (GTK_IS_CHECK_BUTTON (item->feat))
+ {
+ if (gtk_widget_get_state_flags (item->feat) & GTK_STATE_FLAG_INCONSISTENT)
+ continue;
+
+ g_string_append_printf (s, "%s\"%s\" %d",
+ s->len > 0 ? ", " : "", item->name,
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item->feat)));
+ }
+ }
+
+ if (g_strcmp0 (priv->font_features, s->str) != 0)
+ {
+ g_free (priv->font_features);
+ priv->font_features = g_string_free (s, FALSE);
+ g_object_notify (G_OBJECT (fontchooser), "font-features");
+ }
+ else
+ g_string_free (s, TRUE);
+
+ gtk_font_chooser_widget_update_preview_attributes (fontchooser);
+}
+#endif
+
+gboolean
+output_cb (GtkSpinButton *spin,
+ gpointer data)
+{
+ GtkAdjustment *adjustment;
+ gchar *text;
+ gdouble value;
+
+ adjustment = gtk_spin_button_get_adjustment (spin);
+ value = gtk_adjustment_get_value (adjustment);
+ text = g_strdup_printf ("%2.4g", value);
+ gtk_entry_set_text (GTK_ENTRY (spin), text);
+ g_free (text);
+
+ return TRUE;
+}
diff --git a/gtk/gtkfontfeaturesprivate.h b/gtk/gtkfontfeaturesprivate.h
new file mode 100644
index 0000000000..d24c0ba31b
--- /dev/null
+++ b/gtk/gtkfontfeaturesprivate.h
@@ -0,0 +1,92 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 Alberto Ruiz <aruiz gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtkfontchooserwidget.h"
+#include "gtkfontchooserwidgetprivate.h"
+
+#include "gtkadjustment.h"
+#include "gtkfontchooser.h"
+#include "gtkwidget.h"
+#include "gtkspinbutton.h"
+#include "gtktreeview.h"
+
+struct _GtkFontChooserWidgetPrivate
+{
+ GtkWidget *stack;
+ GtkWidget *search_entry;
+ GtkWidget *family_face_list;
+ GtkTreeViewColumn *family_face_column;
+ GtkCellRenderer *family_face_cell;
+ GtkWidget *list_scrolled_window;
+ GtkWidget *list_stack;
+ GtkTreeModel *model;
+ GtkTreeModel *filter_model;
+
+ GtkWidget *preview;
+ GtkWidget *preview2;
+ GtkWidget *font_name_label;
+ gchar *preview_text;
+ gboolean show_preview_entry;
+
+ GtkWidget *size_spin;
+ GtkWidget *size_slider;
+ GtkWidget *size_slider2;
+
+ GtkWidget *axis_grid;
+ GtkWidget *feature_box;
+
+ PangoFontMap *font_map;
+
+ PangoFontDescription *font_desc;
+ char *font_features;
+ PangoLanguage *language;
+ GtkTreeIter font_iter; /* invalid if font not available or pointer into model
+ (not filter_model) to the row containing font */
+ GtkFontFilterFunc filter_func;
+ gpointer filter_data;
+ GDestroyNotify filter_data_destroy;
+
+ guint last_fontconfig_timestamp;
+
+ GtkFontChooserLevel level;
+
+ GHashTable *axes;
+ gboolean updating_variations;
+
+ GList *feature_items;
+
+ GAction *tweak_action;
+};
+
+typedef struct {
+ guint32 tag;
+ GtkAdjustment *adjustment;
+ GtkWidget *label;
+ GtkWidget *scale;
+ GtkWidget *spin;
+ GtkWidget *fontchooser;
+} Axis;
+
+void gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser);
+void gtk_font_chooser_widget_take_font_desc (GtkFontChooserWidget *fontchooser,
+ PangoFontDescription *font_desc);
+gboolean gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser);
+gboolean gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchooser);
+void gtk_font_chooser_widget_update_preview_attributes (GtkFontChooserWidget *fontchooser);
+
+gboolean output_cb (GtkSpinButton *spin,
+ gpointer data);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]