forked from AuroraMiddleware/gtk
15f8b2d0ae
Add a button the dialog's header bar that lets us switch to a second page where we can customize the selected font. Make the font chooser widget export an action that the dialog can use for the button. This has some advantages: - we can export not just the toggle state, but also enabled - we can reuse the same enabled state to make the select button insensitive when no font is selected To determine whether a font is selected, listen to changes of the list selection. And ensure that the font chooser is in an initial state when mapped, even if we close the dialog from the tweak page.
1603 lines
54 KiB
C
1603 lines
54 KiB
C
/* 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 <stdlib.h>
|
|
#include <glib/gprintf.h>
|
|
#include <string.h>
|
|
|
|
#include <atk/atk.h>
|
|
|
|
#include "gtkfontchooserwidget.h"
|
|
#include "gtkfontchooserwidgetprivate.h"
|
|
|
|
#include "gtkadjustment.h"
|
|
#include "gtkbuildable.h"
|
|
#include "gtkbox.h"
|
|
#include "gtkcellrenderertext.h"
|
|
#include "gtkcssnumbervalueprivate.h"
|
|
#include "gtkentry.h"
|
|
#include "gtksearchentry.h"
|
|
#include "gtkgrid.h"
|
|
#include "gtkfontchooser.h"
|
|
#include "gtkfontchooserutils.h"
|
|
#include "gtkintl.h"
|
|
#include "gtklabel.h"
|
|
#include "gtkliststore.h"
|
|
#include "gtkstack.h"
|
|
#include "gtkprivate.h"
|
|
#include "gtkscale.h"
|
|
#include "gtkscrolledwindow.h"
|
|
#include "gtkspinbutton.h"
|
|
#include "gtkstylecontextprivate.h"
|
|
#include "gtktextview.h"
|
|
#include "gtktreeselection.h"
|
|
#include "gtktreeview.h"
|
|
#include "gtkwidget.h"
|
|
#include "gtksettings.h"
|
|
#include "gtkdialog.h"
|
|
|
|
/**
|
|
* SECTION:gtkfontchooserwidget
|
|
* @Short_description: A widget for selecting fonts
|
|
* @Title: GtkFontChooserWidget
|
|
* @See_also: #GtkFontChooserDialog
|
|
*
|
|
* The #GtkFontChooserWidget widget lists the available fonts,
|
|
* styles and sizes, allowing the user to select a font. It is
|
|
* used in the #GtkFontChooserDialog widget to provide a
|
|
* dialog box for selecting fonts.
|
|
*
|
|
* To set the font which is initially selected, use
|
|
* gtk_font_chooser_set_font() or gtk_font_chooser_set_font_desc().
|
|
*
|
|
* To get the selected font use gtk_font_chooser_get_font() or
|
|
* gtk_font_chooser_get_font_desc().
|
|
*
|
|
* To change the text which is shown in the preview area, use
|
|
* gtk_font_chooser_set_preview_text().
|
|
*
|
|
* # CSS nodes
|
|
*
|
|
* GtkFontChooserWidget has a single CSS node with name fontchooser.
|
|
*
|
|
* Since: 3.2
|
|
*/
|
|
|
|
struct _GtkFontChooserWidgetPrivate
|
|
{
|
|
GtkWidget *stack;
|
|
GtkWidget *grid;
|
|
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_label;
|
|
GtkWidget *size_spin;
|
|
GtkWidget *size_slider;
|
|
GtkWidget *size_slider2;
|
|
|
|
PangoFontMap *font_map;
|
|
|
|
PangoFontDescription *font_desc;
|
|
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;
|
|
|
|
GAction *tweak_action;
|
|
};
|
|
|
|
/* Keep in line with GtkTreeStore defined in gtkfontchooserwidget.ui */
|
|
enum {
|
|
FAMILY_COLUMN,
|
|
FACE_COLUMN,
|
|
FONT_DESC_COLUMN,
|
|
PREVIEW_TITLE_COLUMN
|
|
};
|
|
|
|
static void gtk_font_chooser_widget_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gtk_font_chooser_widget_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gtk_font_chooser_widget_finalize (GObject *object);
|
|
|
|
static void gtk_font_chooser_widget_display_changed (GtkWidget *widget,
|
|
GdkDisplay *previous_display);
|
|
|
|
static gboolean gtk_font_chooser_widget_find_font (GtkFontChooserWidget *fontchooser,
|
|
const PangoFontDescription *font_desc,
|
|
GtkTreeIter *iter);
|
|
static void gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser);
|
|
|
|
static gchar *gtk_font_chooser_widget_get_font (GtkFontChooserWidget *fontchooser);
|
|
static void gtk_font_chooser_widget_set_font (GtkFontChooserWidget *fontchooser,
|
|
const gchar *fontname);
|
|
|
|
static PangoFontDescription *gtk_font_chooser_widget_get_font_desc (GtkFontChooserWidget *fontchooser);
|
|
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,
|
|
const gchar *text);
|
|
|
|
static gboolean gtk_font_chooser_widget_get_show_preview_entry (GtkFontChooserWidget *fontchooser);
|
|
static void gtk_font_chooser_widget_set_show_preview_entry (GtkFontChooserWidget *fontchooser,
|
|
gboolean show_preview_entry);
|
|
|
|
static void gtk_font_chooser_widget_set_cell_size (GtkFontChooserWidget *fontchooser);
|
|
static void gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
|
|
gboolean force);
|
|
static gboolean visible_func (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
gpointer user_data);
|
|
static void gtk_font_chooser_widget_cell_data_func (GtkTreeViewColumn *column,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gpointer user_data);
|
|
static void gtk_font_chooser_widget_set_level (GtkFontChooserWidget *fontchooser,
|
|
GtkFontChooserLevel level);
|
|
static GtkFontChooserLevel gtk_font_chooser_widget_get_level (GtkFontChooserWidget *fontchooser);
|
|
static void selection_changed (GtkTreeSelection *selection,
|
|
GtkFontChooserWidget *fontchooser);
|
|
|
|
static void gtk_font_chooser_widget_iface_init (GtkFontChooserIface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GtkFontChooserWidget, gtk_font_chooser_widget, GTK_TYPE_WIDGET,
|
|
G_ADD_PRIVATE (GtkFontChooserWidget)
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_FONT_CHOOSER,
|
|
gtk_font_chooser_widget_iface_init))
|
|
|
|
typedef struct _GtkDelayedFontDescription GtkDelayedFontDescription;
|
|
struct _GtkDelayedFontDescription {
|
|
PangoFontFace *face;
|
|
PangoFontDescription *desc;
|
|
guint ref_count;
|
|
};
|
|
|
|
static GtkDelayedFontDescription *
|
|
gtk_delayed_font_description_new (PangoFontFace *face)
|
|
{
|
|
GtkDelayedFontDescription *result;
|
|
|
|
result = g_slice_new0 (GtkDelayedFontDescription);
|
|
|
|
result->face = g_object_ref (face);
|
|
result->desc = NULL;
|
|
result->ref_count = 1;
|
|
|
|
return result;
|
|
}
|
|
|
|
static GtkDelayedFontDescription *
|
|
gtk_delayed_font_description_ref (GtkDelayedFontDescription *desc)
|
|
{
|
|
desc->ref_count++;
|
|
|
|
return desc;
|
|
}
|
|
|
|
static void
|
|
gtk_delayed_font_description_unref (GtkDelayedFontDescription *desc)
|
|
{
|
|
desc->ref_count--;
|
|
|
|
if (desc->ref_count > 0)
|
|
return;
|
|
|
|
g_object_unref (desc->face);
|
|
if (desc->desc)
|
|
pango_font_description_free (desc->desc);
|
|
|
|
g_slice_free (GtkDelayedFontDescription, desc);
|
|
}
|
|
|
|
static const PangoFontDescription *
|
|
gtk_delayed_font_description_get (GtkDelayedFontDescription *desc)
|
|
{
|
|
if (desc->desc == NULL)
|
|
desc->desc = pango_font_face_describe (desc->face);
|
|
|
|
return desc->desc;
|
|
}
|
|
|
|
#define GTK_TYPE_DELAYED_FONT_DESCRIPTION (gtk_delayed_font_description_get_type ())
|
|
GType gtk_delayed_font_description_get_type (void);
|
|
|
|
G_DEFINE_BOXED_TYPE (GtkDelayedFontDescription, gtk_delayed_font_description,
|
|
gtk_delayed_font_description_ref,
|
|
gtk_delayed_font_description_unref)
|
|
static void
|
|
gtk_font_chooser_widget_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case GTK_FONT_CHOOSER_PROP_FONT:
|
|
gtk_font_chooser_widget_set_font (fontchooser, g_value_get_string (value));
|
|
break;
|
|
case GTK_FONT_CHOOSER_PROP_FONT_DESC:
|
|
gtk_font_chooser_widget_take_font_desc (fontchooser, g_value_dup_boxed (value));
|
|
break;
|
|
case GTK_FONT_CHOOSER_PROP_PREVIEW_TEXT:
|
|
gtk_font_chooser_widget_set_preview_text (fontchooser, g_value_get_string (value));
|
|
break;
|
|
case GTK_FONT_CHOOSER_PROP_SHOW_PREVIEW_ENTRY:
|
|
gtk_font_chooser_widget_set_show_preview_entry (fontchooser, g_value_get_boolean (value));
|
|
break;
|
|
case GTK_FONT_CHOOSER_PROP_LEVEL:
|
|
gtk_font_chooser_widget_set_level (fontchooser, g_value_get_enum (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case GTK_FONT_CHOOSER_PROP_FONT:
|
|
g_value_take_string (value, gtk_font_chooser_widget_get_font (fontchooser));
|
|
break;
|
|
case GTK_FONT_CHOOSER_PROP_FONT_DESC:
|
|
g_value_set_boxed (value, gtk_font_chooser_widget_get_font_desc (fontchooser));
|
|
break;
|
|
case GTK_FONT_CHOOSER_PROP_PREVIEW_TEXT:
|
|
g_value_set_string (value, gtk_font_chooser_widget_get_preview_text (fontchooser));
|
|
break;
|
|
case GTK_FONT_CHOOSER_PROP_SHOW_PREVIEW_ENTRY:
|
|
g_value_set_boolean (value, gtk_font_chooser_widget_get_show_preview_entry (fontchooser));
|
|
break;
|
|
case GTK_FONT_CHOOSER_PROP_LEVEL:
|
|
g_value_set_enum (value, gtk_font_chooser_widget_get_level (fontchooser));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_refilter_font_list (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (fontchooser->priv->filter_model));
|
|
gtk_font_chooser_widget_ensure_selection (fontchooser);
|
|
}
|
|
|
|
static void
|
|
text_changed_cb (GtkEntry *entry,
|
|
GtkFontChooserWidget *fc)
|
|
{
|
|
gtk_font_chooser_widget_refilter_font_list (fc);
|
|
}
|
|
|
|
static void
|
|
stop_search_cb (GtkEntry *entry,
|
|
GtkFontChooserWidget *fc)
|
|
{
|
|
if (gtk_entry_get_text (entry)[0] != 0)
|
|
gtk_entry_set_text (entry, "");
|
|
else
|
|
{
|
|
GtkWidget *dialog;
|
|
GtkWidget *button = NULL;
|
|
|
|
dialog = gtk_widget_get_ancestor (GTK_WIDGET (fc), GTK_TYPE_DIALOG);
|
|
if (dialog)
|
|
button = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
|
|
|
|
if (button)
|
|
gtk_widget_activate (button);
|
|
}
|
|
}
|
|
|
|
static void
|
|
size_change_cb (GtkAdjustment *adjustment,
|
|
gpointer user_data)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = user_data;
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
PangoFontDescription *font_desc;
|
|
gdouble size = gtk_adjustment_get_value (adjustment);
|
|
|
|
font_desc = pango_font_description_new ();
|
|
if (pango_font_description_get_size_is_absolute (priv->font_desc))
|
|
pango_font_description_set_absolute_size (font_desc, size * PANGO_SCALE);
|
|
else
|
|
pango_font_description_set_size (font_desc, size * PANGO_SCALE);
|
|
|
|
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_spin_button_set_text (spin, text);
|
|
g_free (text);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_update_marks (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
GtkAdjustment *adj, *spin_adj;
|
|
const int *sizes;
|
|
gint *font_sizes;
|
|
gint i, n_sizes;
|
|
gdouble value, spin_value;
|
|
|
|
if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (priv->model), &priv->font_iter))
|
|
{
|
|
PangoFontFace *face;
|
|
|
|
gtk_tree_model_get (priv->model, &priv->font_iter,
|
|
FACE_COLUMN, &face,
|
|
-1);
|
|
|
|
pango_font_face_list_sizes (face, &font_sizes, &n_sizes);
|
|
|
|
/* It seems not many fonts actually have a sane set of sizes */
|
|
for (i = 0; i < n_sizes; i++)
|
|
font_sizes[i] = font_sizes[i] / PANGO_SCALE;
|
|
|
|
g_object_unref (face);
|
|
}
|
|
else
|
|
{
|
|
font_sizes = NULL;
|
|
n_sizes = 0;
|
|
}
|
|
|
|
if (n_sizes < 2)
|
|
{
|
|
static const gint fallback_sizes[] = {
|
|
6, 8, 9, 10, 11, 12, 13, 14, 16, 20, 24, 36, 48, 72
|
|
};
|
|
|
|
sizes = fallback_sizes;
|
|
n_sizes = G_N_ELEMENTS (fallback_sizes);
|
|
}
|
|
else
|
|
{
|
|
sizes = font_sizes;
|
|
}
|
|
|
|
gtk_scale_clear_marks (GTK_SCALE (priv->size_slider));
|
|
gtk_scale_clear_marks (GTK_SCALE (priv->size_slider2));
|
|
|
|
adj = gtk_range_get_adjustment (GTK_RANGE (priv->size_slider));
|
|
spin_adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->size_spin));
|
|
spin_value = gtk_adjustment_get_value (spin_adj);
|
|
|
|
if (spin_value < sizes[0])
|
|
value = (gdouble) sizes[0];
|
|
else if (spin_value > sizes[n_sizes - 1])
|
|
value = (gdouble)sizes[n_sizes - 1];
|
|
else
|
|
value = (gdouble)spin_value;
|
|
|
|
/* ensure clamping doesn't callback into font resizing code */
|
|
g_signal_handlers_block_by_func (adj, size_change_cb, fontchooser);
|
|
gtk_adjustment_configure (adj,
|
|
value,
|
|
sizes[0],
|
|
sizes[n_sizes - 1],
|
|
gtk_adjustment_get_step_increment (adj),
|
|
gtk_adjustment_get_page_increment (adj),
|
|
gtk_adjustment_get_page_size (adj));
|
|
g_signal_handlers_unblock_by_func (adj, size_change_cb, fontchooser);
|
|
|
|
for (i = 0; i < n_sizes; i++)
|
|
{
|
|
gtk_scale_add_mark (GTK_SCALE (priv->size_slider),
|
|
sizes[i],
|
|
GTK_POS_BOTTOM, NULL);
|
|
gtk_scale_add_mark (GTK_SCALE (priv->size_slider2),
|
|
sizes[i],
|
|
GTK_POS_BOTTOM, NULL);
|
|
}
|
|
|
|
g_free (font_sizes);
|
|
}
|
|
|
|
static void
|
|
row_activated_cb (GtkTreeView *view,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *column,
|
|
gpointer user_data)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = user_data;
|
|
gchar *fontname;
|
|
|
|
fontname = gtk_font_chooser_widget_get_font (fontchooser);
|
|
_gtk_font_chooser_font_activated (GTK_FONT_CHOOSER (fontchooser), fontname);
|
|
g_free (fontname);
|
|
}
|
|
|
|
static void
|
|
cursor_changed_cb (GtkTreeView *treeview,
|
|
gpointer user_data)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = user_data;
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
GtkDelayedFontDescription *desc;
|
|
GtkTreeIter filter_iter, iter;
|
|
GtkTreePath *path = NULL;
|
|
|
|
gtk_tree_view_get_cursor (treeview, &path, NULL);
|
|
|
|
if (!path)
|
|
return;
|
|
|
|
if (!gtk_tree_model_get_iter (priv->filter_model, &filter_iter, path))
|
|
{
|
|
gtk_tree_path_free (path);
|
|
return;
|
|
}
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
|
|
&iter,
|
|
&filter_iter);
|
|
gtk_tree_model_get (priv->model, &iter,
|
|
FONT_DESC_COLUMN, &desc,
|
|
-1);
|
|
|
|
gtk_font_chooser_widget_merge_font_desc (fontchooser,
|
|
gtk_delayed_font_description_get (desc),
|
|
&iter);
|
|
|
|
gtk_delayed_font_description_unref (desc);
|
|
}
|
|
|
|
static gboolean
|
|
resize_by_scroll_cb (GtkWidget *scrolled_window,
|
|
GdkEventScroll *event,
|
|
gpointer user_data)
|
|
{
|
|
GtkFontChooserWidget *fc = user_data;
|
|
GtkFontChooserWidgetPrivate *priv = fc->priv;
|
|
GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (priv->size_spin));
|
|
GdkScrollDirection direction;
|
|
gdouble delta_x, delta_y;
|
|
|
|
if (!gdk_event_get_scroll_direction ((GdkEvent *) event, &direction))
|
|
return GDK_EVENT_PROPAGATE;
|
|
|
|
gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y);
|
|
|
|
if (direction == GDK_SCROLL_UP || direction == GDK_SCROLL_RIGHT)
|
|
gtk_adjustment_set_value (adj,
|
|
gtk_adjustment_get_value (adj) +
|
|
gtk_adjustment_get_step_increment (adj));
|
|
else if (direction == GDK_SCROLL_DOWN || direction == GDK_SCROLL_LEFT)
|
|
gtk_adjustment_set_value (adj,
|
|
gtk_adjustment_get_value (adj) -
|
|
gtk_adjustment_get_step_increment (adj));
|
|
else if (direction == GDK_SCROLL_SMOOTH && delta_x != 0.0)
|
|
gtk_adjustment_set_value (adj,
|
|
gtk_adjustment_get_value (adj) +
|
|
gtk_adjustment_get_step_increment (adj) * delta_x);
|
|
else if (direction == GDK_SCROLL_SMOOTH && delta_y != 0.0)
|
|
gtk_adjustment_set_value (adj,
|
|
gtk_adjustment_get_value (adj) -
|
|
gtk_adjustment_get_step_increment (adj) * delta_y);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_update_preview_attributes (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv;
|
|
PangoAttrList *attrs;
|
|
|
|
priv = fontchooser->priv;
|
|
|
|
attrs = pango_attr_list_new ();
|
|
|
|
/* Prevent font fallback */
|
|
pango_attr_list_insert (attrs, pango_attr_fallback_new (FALSE));
|
|
|
|
/* Force current font */
|
|
pango_attr_list_insert (attrs, pango_attr_font_desc_new (priv->font_desc));
|
|
|
|
gtk_entry_set_attributes (GTK_ENTRY (priv->preview), attrs);
|
|
|
|
pango_attr_list_unref (attrs);
|
|
}
|
|
|
|
static void
|
|
rows_changed_cb (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
const char *page;
|
|
|
|
if (gtk_tree_model_iter_n_children (priv->filter_model, NULL) == 0)
|
|
page = "empty";
|
|
else
|
|
page = "list";
|
|
|
|
if (strcmp (gtk_stack_get_visible_child_name (GTK_STACK (priv->list_stack)), page) != 0)
|
|
gtk_stack_set_visible_child_name (GTK_STACK (priv->list_stack), page);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_map (GtkWidget *widget)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (widget);
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
gtk_entry_set_text (GTK_ENTRY (priv->search_entry), "");
|
|
gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "list");
|
|
g_simple_action_set_state (G_SIMPLE_ACTION (priv->tweak_action), g_variant_new_boolean (FALSE));
|
|
|
|
GTK_WIDGET_CLASS (gtk_font_chooser_widget_parent_class)->map (widget);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_measure (GtkWidget *widget,
|
|
GtkOrientation orientation,
|
|
int for_size,
|
|
int *minimum,
|
|
int *natural,
|
|
int *minimum_baseline,
|
|
int *natural_baseline)
|
|
{
|
|
GtkFontChooserWidget *self = GTK_FONT_CHOOSER_WIDGET (widget);
|
|
GtkFontChooserWidgetPrivate *priv = gtk_font_chooser_widget_get_instance_private (self);
|
|
|
|
gtk_widget_measure (priv->stack, orientation, for_size,
|
|
minimum, natural,
|
|
minimum_baseline, natural_baseline);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_snapshot (GtkWidget *widget,
|
|
GtkSnapshot *snapshot)
|
|
{
|
|
GtkFontChooserWidget *self = GTK_FONT_CHOOSER_WIDGET (widget);
|
|
GtkFontChooserWidgetPrivate *priv = gtk_font_chooser_widget_get_instance_private (self);
|
|
|
|
gtk_widget_snapshot_child (widget, priv->stack, snapshot);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_size_allocate (GtkWidget *widget,
|
|
const GtkAllocation *allocation,
|
|
int baseline,
|
|
GtkAllocation *out_clip)
|
|
{
|
|
GtkFontChooserWidget *self = GTK_FONT_CHOOSER_WIDGET (widget);
|
|
GtkFontChooserWidgetPrivate *priv = gtk_font_chooser_widget_get_instance_private (self);
|
|
|
|
GTK_WIDGET_CLASS (gtk_font_chooser_widget_parent_class)->size_allocate (widget, allocation, -1, out_clip);
|
|
|
|
gtk_widget_size_allocate (priv->stack, allocation, -1, out_clip);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_dispose (GObject *object)
|
|
{
|
|
GtkFontChooserWidget *self = GTK_FONT_CHOOSER_WIDGET (object);
|
|
GtkFontChooserWidgetPrivate *priv = gtk_font_chooser_widget_get_instance_private (self);
|
|
|
|
if (priv->stack)
|
|
{
|
|
gtk_widget_unparent (priv->stack);
|
|
priv->stack = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
g_type_ensure (GTK_TYPE_DELAYED_FONT_DESCRIPTION);
|
|
g_type_ensure (G_TYPE_THEMED_ICON);
|
|
|
|
widget_class->display_changed = gtk_font_chooser_widget_display_changed;
|
|
widget_class->measure = gtk_font_chooser_widget_measure;
|
|
widget_class->size_allocate = gtk_font_chooser_widget_size_allocate;
|
|
widget_class->snapshot = gtk_font_chooser_widget_snapshot;
|
|
widget_class->map = gtk_font_chooser_widget_map;
|
|
|
|
gobject_class->finalize = gtk_font_chooser_widget_finalize;
|
|
gobject_class->dispose = gtk_font_chooser_widget_dispose;
|
|
gobject_class->set_property = gtk_font_chooser_widget_set_property;
|
|
gobject_class->get_property = gtk_font_chooser_widget_get_property;
|
|
|
|
_gtk_font_chooser_install_properties (gobject_class);
|
|
|
|
/* Bind class to template */
|
|
gtk_widget_class_set_template_from_resource (widget_class,
|
|
"/org/gtk/libgtk/ui/gtkfontchooserwidget.ui");
|
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, search_entry);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_list);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_column);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_cell);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, list_scrolled_window);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, list_stack);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, model);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, filter_model);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, preview);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, preview2);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, size_label);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, size_spin);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, size_slider);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, size_slider2);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, stack);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, grid);
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, font_name_label);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, text_changed_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, stop_search_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, cursor_changed_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, row_activated_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, gtk_font_chooser_widget_set_cell_size);
|
|
gtk_widget_class_bind_template_callback (widget_class, resize_by_scroll_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, rows_changed_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, size_change_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, output_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, selection_changed);
|
|
|
|
gtk_widget_class_set_css_name (widget_class, I_("fontchooser"));
|
|
}
|
|
|
|
static void
|
|
change_tweak (GSimpleAction *action,
|
|
GVariant *state,
|
|
gpointer data)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = data;
|
|
gboolean tweak = g_variant_get_boolean (state);
|
|
|
|
if (tweak)
|
|
{
|
|
gtk_entry_grab_focus_without_selecting (GTK_ENTRY (fontchooser->priv->preview2));
|
|
gtk_stack_set_visible_child_name (GTK_STACK (fontchooser->priv->stack), "tweaks");
|
|
}
|
|
else
|
|
{
|
|
gtk_entry_grab_focus_without_selecting (GTK_ENTRY (fontchooser->priv->search_entry));
|
|
gtk_stack_set_visible_child_name (GTK_STACK (fontchooser->priv->stack), "list");
|
|
}
|
|
|
|
g_simple_action_set_state (action, state);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv;
|
|
|
|
fontchooser->priv = gtk_font_chooser_widget_get_instance_private (fontchooser);
|
|
priv = fontchooser->priv;
|
|
|
|
gtk_widget_set_has_window (GTK_WIDGET (fontchooser), FALSE);
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (fontchooser));
|
|
|
|
/* Default preview string */
|
|
priv->preview_text = g_strdup (pango_language_get_sample_string (NULL));
|
|
priv->show_preview_entry = TRUE;
|
|
priv->font_desc = pango_font_description_new ();
|
|
|
|
/* Set default preview text */
|
|
gtk_entry_set_text (GTK_ENTRY (priv->preview), priv->preview_text);
|
|
|
|
gtk_font_chooser_widget_update_preview_attributes (fontchooser);
|
|
|
|
/* Set the upper values of the spin/scale with G_MAXINT / PANGO_SCALE */
|
|
gtk_spin_button_set_range (GTK_SPIN_BUTTON (priv->size_spin),
|
|
1.0, (gdouble)(G_MAXINT / PANGO_SCALE));
|
|
gtk_adjustment_set_upper (gtk_range_get_adjustment (GTK_RANGE (priv->size_slider)),
|
|
(gdouble)(G_MAXINT / PANGO_SCALE));
|
|
|
|
/* Setup treeview/model auxilary functions */
|
|
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
|
|
visible_func, (gpointer)priv, NULL);
|
|
|
|
gtk_tree_view_column_set_cell_data_func (priv->family_face_column,
|
|
priv->family_face_cell,
|
|
gtk_font_chooser_widget_cell_data_func,
|
|
fontchooser,
|
|
NULL);
|
|
|
|
priv->tweak_action = G_ACTION (g_simple_action_new_stateful ("tweak", NULL, g_variant_new_boolean (FALSE)));
|
|
g_signal_connect (priv->tweak_action, "change-state", G_CALLBACK (change_tweak), fontchooser);
|
|
|
|
/* Load data and set initial style-dependent parameters */
|
|
gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
|
|
gtk_font_chooser_widget_set_cell_size (fontchooser);
|
|
gtk_font_chooser_widget_take_font_desc (fontchooser, NULL);
|
|
}
|
|
|
|
/**
|
|
* gtk_font_chooser_widget_new:
|
|
*
|
|
* Creates a new #GtkFontChooserWidget.
|
|
*
|
|
* Returns: a new #GtkFontChooserWidget
|
|
*
|
|
* Since: 3.2
|
|
*/
|
|
GtkWidget *
|
|
gtk_font_chooser_widget_new (void)
|
|
{
|
|
return g_object_new (GTK_TYPE_FONT_CHOOSER_WIDGET, NULL);
|
|
}
|
|
|
|
static int
|
|
cmp_families (const void *a,
|
|
const void *b)
|
|
{
|
|
const char *a_name = pango_font_family_get_name (*(PangoFontFamily **)a);
|
|
const char *b_name = pango_font_family_get_name (*(PangoFontFamily **)b);
|
|
|
|
return g_utf8_collate (a_name, b_name);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
|
|
gboolean force)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
GtkListStore *list_store;
|
|
gint n_families, i;
|
|
PangoFontFamily **families;
|
|
guint fontconfig_timestamp;
|
|
gboolean need_reload;
|
|
PangoFontMap *font_map;
|
|
|
|
g_object_get (gtk_widget_get_settings (GTK_WIDGET (fontchooser)),
|
|
"gtk-fontconfig-timestamp", &fontconfig_timestamp,
|
|
NULL);
|
|
|
|
/* The fontconfig timestamp is only set on systems with fontconfig; every
|
|
* other platform will set it to 0. For those systems, we fall back to
|
|
* reloading the fonts every time.
|
|
*/
|
|
need_reload = fontconfig_timestamp == 0 ||
|
|
fontconfig_timestamp != priv->last_fontconfig_timestamp;
|
|
|
|
priv->last_fontconfig_timestamp = fontconfig_timestamp;
|
|
|
|
if (!need_reload && !force)
|
|
return;
|
|
|
|
list_store = GTK_LIST_STORE (priv->model);
|
|
|
|
if (priv->font_map)
|
|
font_map = priv->font_map;
|
|
else
|
|
font_map = pango_cairo_font_map_get_default ();
|
|
pango_font_map_list_families (font_map, &families, &n_families);
|
|
|
|
qsort (families, n_families, sizeof (PangoFontFamily *), cmp_families);
|
|
|
|
g_signal_handlers_block_by_func (priv->family_face_list, cursor_changed_cb, fontchooser);
|
|
g_signal_handlers_block_by_func (priv->filter_model, rows_changed_cb, fontchooser);
|
|
gtk_list_store_clear (list_store);
|
|
|
|
/* Iterate over families and faces */
|
|
for (i = 0; i < n_families; i++)
|
|
{
|
|
GtkTreeIter iter;
|
|
PangoFontFace **faces;
|
|
int j, n_faces;
|
|
const gchar *fam_name = pango_font_family_get_name (families[i]);
|
|
|
|
pango_font_family_list_faces (families[i], &faces, &n_faces);
|
|
|
|
for (j = 0; j < n_faces; j++)
|
|
{
|
|
GtkDelayedFontDescription *desc;
|
|
const gchar *face_name;
|
|
char *title;
|
|
|
|
face_name = pango_font_face_get_face_name (faces[j]);
|
|
|
|
if (priv->level == GTK_FONT_CHOOSER_LEVEL_FAMILY)
|
|
title = g_strdup (fam_name);
|
|
else
|
|
title = g_strconcat (fam_name, " ", face_name, NULL);
|
|
|
|
desc = gtk_delayed_font_description_new (faces[j]);
|
|
|
|
gtk_list_store_insert_with_values (list_store, &iter, -1,
|
|
FAMILY_COLUMN, families[i],
|
|
FACE_COLUMN, faces[j],
|
|
FONT_DESC_COLUMN, desc,
|
|
PREVIEW_TITLE_COLUMN, title,
|
|
-1);
|
|
|
|
g_free (title);
|
|
gtk_delayed_font_description_unref (desc);
|
|
|
|
if (priv->level == GTK_FONT_CHOOSER_LEVEL_FAMILY)
|
|
break;
|
|
}
|
|
|
|
g_free (faces);
|
|
}
|
|
|
|
g_free (families);
|
|
|
|
rows_changed_cb (fontchooser);
|
|
|
|
g_signal_handlers_unblock_by_func (priv->filter_model, rows_changed_cb, fontchooser);
|
|
g_signal_handlers_unblock_by_func (priv->family_face_list, cursor_changed_cb, fontchooser);
|
|
|
|
/* now make sure the font list looks right */
|
|
if (!gtk_font_chooser_widget_find_font (fontchooser, priv->font_desc, &priv->font_iter))
|
|
memset (&priv->font_iter, 0, sizeof (GtkTreeIter));
|
|
|
|
gtk_font_chooser_widget_ensure_selection (fontchooser);
|
|
}
|
|
|
|
static gboolean
|
|
visible_func (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
gpointer user_data)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = user_data;
|
|
gboolean result = TRUE;
|
|
const gchar *search_text;
|
|
gchar **split_terms;
|
|
gchar *font_name, *font_name_casefold;
|
|
guint i;
|
|
|
|
if (priv->filter_func != NULL)
|
|
{
|
|
PangoFontFamily *family;
|
|
PangoFontFace *face;
|
|
|
|
gtk_tree_model_get (model, iter,
|
|
FAMILY_COLUMN, &family,
|
|
FACE_COLUMN, &face,
|
|
-1);
|
|
|
|
result = priv->filter_func (family, face, priv->filter_data);
|
|
|
|
g_object_unref (family);
|
|
g_object_unref (face);
|
|
|
|
if (!result)
|
|
return FALSE;
|
|
}
|
|
|
|
/* If there's no filter string we show the item */
|
|
search_text = gtk_entry_get_text (GTK_ENTRY (priv->search_entry));
|
|
if (strlen (search_text) == 0)
|
|
return TRUE;
|
|
|
|
gtk_tree_model_get (model, iter,
|
|
PREVIEW_TITLE_COLUMN, &font_name,
|
|
-1);
|
|
|
|
if (font_name == NULL)
|
|
return FALSE;
|
|
|
|
split_terms = g_strsplit (search_text, " ", 0);
|
|
font_name_casefold = g_utf8_casefold (font_name, -1);
|
|
|
|
for (i = 0; split_terms[i] && result; i++)
|
|
{
|
|
gchar* term_casefold = g_utf8_casefold (split_terms[i], -1);
|
|
|
|
if (!strstr (font_name_casefold, term_casefold))
|
|
result = FALSE;
|
|
|
|
g_free (term_casefold);
|
|
}
|
|
|
|
g_free (font_name_casefold);
|
|
g_free (font_name);
|
|
g_strfreev (split_terms);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* in pango units */
|
|
static int
|
|
gtk_font_chooser_widget_get_preview_text_height (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkWidget *treeview = fontchooser->priv->family_face_list;
|
|
GtkStyleContext *context;
|
|
double dpi, font_size;
|
|
|
|
context = gtk_widget_get_style_context (treeview);
|
|
dpi = _gtk_css_number_value_get (_gtk_style_context_peek_property (context,
|
|
GTK_CSS_PROPERTY_DPI),
|
|
100);
|
|
gtk_style_context_get (context,
|
|
"font-size", &font_size,
|
|
NULL);
|
|
|
|
return (dpi < 0.0 ? 96.0 : dpi) / 72.0 * PANGO_SCALE_X_LARGE * font_size * PANGO_SCALE;
|
|
}
|
|
|
|
static PangoAttrList *
|
|
gtk_font_chooser_widget_get_preview_attributes (GtkFontChooserWidget *fontchooser,
|
|
const PangoFontDescription *font_desc)
|
|
{
|
|
PangoAttribute *attribute;
|
|
PangoAttrList *attrs;
|
|
|
|
attrs = pango_attr_list_new ();
|
|
|
|
if (font_desc)
|
|
{
|
|
attribute = pango_attr_font_desc_new (font_desc);
|
|
pango_attr_list_insert (attrs, attribute);
|
|
}
|
|
|
|
attribute = pango_attr_size_new_absolute (gtk_font_chooser_widget_get_preview_text_height (fontchooser));
|
|
pango_attr_list_insert (attrs, attribute);
|
|
|
|
return attrs;
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_cell_data_func (GtkTreeViewColumn *column,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gpointer user_data)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = user_data;
|
|
GtkDelayedFontDescription *desc;
|
|
PangoAttrList *attrs;
|
|
char *preview_title;
|
|
|
|
gtk_tree_model_get (tree_model, iter,
|
|
PREVIEW_TITLE_COLUMN, &preview_title,
|
|
FONT_DESC_COLUMN, &desc,
|
|
-1);
|
|
|
|
attrs = gtk_font_chooser_widget_get_preview_attributes (fontchooser,
|
|
gtk_delayed_font_description_get (desc));
|
|
|
|
g_object_set (cell,
|
|
"xpad", 20,
|
|
"ypad", 10,
|
|
"attributes", attrs,
|
|
"text", preview_title,
|
|
NULL);
|
|
|
|
gtk_delayed_font_description_unref (desc);
|
|
pango_attr_list_unref (attrs);
|
|
g_free (preview_title);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_set_cell_size (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
PangoAttrList *attrs;
|
|
GtkRequisition size;
|
|
|
|
gtk_cell_renderer_set_fixed_size (priv->family_face_cell, -1, -1);
|
|
|
|
attrs = gtk_font_chooser_widget_get_preview_attributes (fontchooser, NULL);
|
|
|
|
g_object_set (priv->family_face_cell,
|
|
"xpad", 20,
|
|
"ypad", 10,
|
|
"attributes", attrs,
|
|
"text", "x",
|
|
NULL);
|
|
|
|
pango_attr_list_unref (attrs);
|
|
|
|
gtk_cell_renderer_get_preferred_size (priv->family_face_cell,
|
|
priv->family_face_list,
|
|
&size,
|
|
NULL);
|
|
gtk_cell_renderer_set_fixed_size (priv->family_face_cell, size.width, size.height);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_finalize (GObject *object)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (object);
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
if (priv->font_desc)
|
|
pango_font_description_free (priv->font_desc);
|
|
|
|
if (priv->filter_data_destroy)
|
|
priv->filter_data_destroy (priv->filter_data);
|
|
|
|
g_free (priv->preview_text);
|
|
|
|
g_clear_object (&priv->font_map);
|
|
|
|
g_object_unref (priv->tweak_action);
|
|
|
|
G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
my_pango_font_family_equal (const char *familya,
|
|
const char *familyb)
|
|
{
|
|
return g_ascii_strcasecmp (familya, familyb) == 0;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_font_chooser_widget_find_font (GtkFontChooserWidget *fontchooser,
|
|
const PangoFontDescription *font_desc,
|
|
/* out arguments */
|
|
GtkTreeIter *iter)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
gboolean valid;
|
|
|
|
if (pango_font_description_get_family (font_desc) == NULL)
|
|
return FALSE;
|
|
|
|
for (valid = gtk_tree_model_get_iter_first (priv->model, iter);
|
|
valid;
|
|
valid = gtk_tree_model_iter_next (priv->model, iter))
|
|
{
|
|
GtkDelayedFontDescription *desc;
|
|
PangoFontDescription *merged;
|
|
PangoFontFamily *family;
|
|
|
|
gtk_tree_model_get (priv->model, iter,
|
|
FAMILY_COLUMN, &family,
|
|
FONT_DESC_COLUMN, &desc,
|
|
-1);
|
|
|
|
if (!my_pango_font_family_equal (pango_font_description_get_family (font_desc),
|
|
pango_font_family_get_name (family)))
|
|
{
|
|
gtk_delayed_font_description_unref (desc);
|
|
g_object_unref (family);
|
|
continue;
|
|
}
|
|
|
|
merged = pango_font_description_copy_static (gtk_delayed_font_description_get (desc));
|
|
|
|
pango_font_description_merge_static (merged, font_desc, FALSE);
|
|
if (pango_font_description_equal (merged, font_desc))
|
|
{
|
|
gtk_delayed_font_description_unref (desc);
|
|
pango_font_description_free (merged);
|
|
g_object_unref (family);
|
|
break;
|
|
}
|
|
|
|
gtk_delayed_font_description_unref (desc);
|
|
pango_font_description_free (merged);
|
|
g_object_unref (family);
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
static void
|
|
fontconfig_changed (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_display_changed (GtkWidget *widget,
|
|
GdkDisplay *previous_display)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (widget);
|
|
GtkSettings *settings;
|
|
|
|
if (GTK_WIDGET_CLASS (gtk_font_chooser_widget_parent_class)->display_changed)
|
|
GTK_WIDGET_CLASS (gtk_font_chooser_widget_parent_class)->display_changed (widget, previous_display);
|
|
|
|
if (previous_display)
|
|
{
|
|
settings = gtk_settings_get_for_display (previous_display);
|
|
g_signal_handlers_disconnect_by_func (settings, fontconfig_changed, widget);
|
|
}
|
|
settings = gtk_widget_get_settings (widget);
|
|
g_signal_connect_object (settings, "notify::gtk-fontconfig-timestamp",
|
|
G_CALLBACK (fontconfig_changed), widget, G_CONNECT_SWAPPED);
|
|
|
|
if (previous_display == NULL)
|
|
previous_display = gdk_display_get_default ();
|
|
|
|
if (previous_display == gtk_widget_get_display (widget))
|
|
return;
|
|
|
|
gtk_font_chooser_widget_load_fonts (fontchooser, FALSE);
|
|
}
|
|
|
|
static PangoFontFamily *
|
|
gtk_font_chooser_widget_get_family (GtkFontChooser *chooser)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (chooser);
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
PangoFontFamily *family;
|
|
|
|
if (!gtk_list_store_iter_is_valid (GTK_LIST_STORE (priv->model), &priv->font_iter))
|
|
return NULL;
|
|
|
|
gtk_tree_model_get (priv->model, &priv->font_iter,
|
|
FAMILY_COLUMN, &family,
|
|
-1);
|
|
g_object_unref (family);
|
|
|
|
return family;
|
|
}
|
|
|
|
static PangoFontFace *
|
|
gtk_font_chooser_widget_get_face (GtkFontChooser *chooser)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (chooser);
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
PangoFontFace *face;
|
|
|
|
if (!gtk_list_store_iter_is_valid (GTK_LIST_STORE (priv->model), &priv->font_iter))
|
|
return NULL;
|
|
|
|
gtk_tree_model_get (priv->model, &priv->font_iter,
|
|
FACE_COLUMN, &face,
|
|
-1);
|
|
g_object_unref (face);
|
|
|
|
return face;
|
|
}
|
|
|
|
static gint
|
|
gtk_font_chooser_widget_get_size (GtkFontChooser *chooser)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (chooser);
|
|
PangoFontDescription *desc = gtk_font_chooser_widget_get_font_desc (fontchooser);
|
|
|
|
if (desc)
|
|
return pango_font_description_get_size (desc);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static gchar *
|
|
gtk_font_chooser_widget_get_font (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
PangoFontDescription *desc = gtk_font_chooser_widget_get_font_desc (fontchooser);
|
|
|
|
if (desc)
|
|
return pango_font_description_to_string (desc);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static PangoFontDescription *
|
|
gtk_font_chooser_widget_get_font_desc (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
GtkTreeSelection *selection;
|
|
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->family_face_list));
|
|
if (gtk_tree_selection_count_selected_rows (selection) > 0)
|
|
return fontchooser->priv->font_desc;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_set_font (GtkFontChooserWidget *fontchooser,
|
|
const gchar *fontname)
|
|
{
|
|
PangoFontDescription *font_desc;
|
|
|
|
font_desc = pango_font_description_from_string (fontname);
|
|
gtk_font_chooser_widget_take_font_desc (fontchooser, font_desc);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_update_font_name (GtkFontChooserWidget *fontchooser,
|
|
GtkTreeSelection *selection)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
PangoFontFamily *family;
|
|
PangoFontFace *face;
|
|
GtkDelayedFontDescription *desc;
|
|
const PangoFontDescription *font_desc;
|
|
PangoAttrList *attrs;
|
|
const char *fam_name;
|
|
const char *face_name;
|
|
char *title;
|
|
|
|
gtk_tree_selection_get_selected (selection, &model, &iter);
|
|
gtk_tree_model_get (model, &iter,
|
|
FAMILY_COLUMN, &family,
|
|
FACE_COLUMN, &face,
|
|
FONT_DESC_COLUMN, &desc,
|
|
-1);
|
|
|
|
fam_name = pango_font_family_get_name (family);
|
|
face_name = pango_font_face_get_face_name (face);
|
|
font_desc = gtk_delayed_font_description_get (desc);
|
|
|
|
g_object_unref (family);
|
|
g_object_unref (face);
|
|
|
|
if (priv->level == GTK_FONT_CHOOSER_LEVEL_FAMILY)
|
|
title = g_strdup (fam_name);
|
|
else
|
|
title = g_strconcat (fam_name, " ", face_name, NULL);
|
|
|
|
attrs = gtk_font_chooser_widget_get_preview_attributes (fontchooser, font_desc);
|
|
gtk_label_set_attributes (GTK_LABEL (priv->font_name_label), attrs);
|
|
pango_attr_list_unref (attrs);
|
|
|
|
gtk_label_set_label (GTK_LABEL (priv->font_name_label), title);
|
|
g_free (title);
|
|
}
|
|
|
|
static void
|
|
selection_changed (GtkTreeSelection *selection,
|
|
GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
g_object_notify (G_OBJECT (fontchooser), "font");
|
|
g_object_notify (G_OBJECT (fontchooser), "font-desc");
|
|
|
|
if (gtk_tree_selection_count_selected_rows (selection) > 0)
|
|
{
|
|
gtk_font_chooser_widget_update_font_name (fontchooser, selection);
|
|
g_simple_action_set_enabled (G_SIMPLE_ACTION (priv->tweak_action), TRUE);
|
|
}
|
|
else
|
|
{
|
|
g_simple_action_set_state (G_SIMPLE_ACTION (priv->tweak_action), g_variant_new_boolean (FALSE));
|
|
g_simple_action_set_enabled (G_SIMPLE_ACTION (priv->tweak_action), FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeIter filter_iter;
|
|
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->family_face_list));
|
|
|
|
if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (priv->model), &priv->font_iter) &&
|
|
gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
|
|
&filter_iter,
|
|
&priv->font_iter))
|
|
{
|
|
GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->filter_model),
|
|
&filter_iter);
|
|
|
|
gtk_tree_selection_select_iter (selection, &filter_iter);
|
|
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->family_face_list),
|
|
path, NULL, FALSE, 0.0, 0.0);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
else
|
|
{
|
|
gtk_tree_selection_unselect_all (selection);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser,
|
|
const PangoFontDescription *font_desc,
|
|
GtkTreeIter *iter)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
PangoFontMask mask;
|
|
|
|
g_assert (font_desc != NULL);
|
|
/* iter may be NULL if the font doesn't exist on the list */
|
|
|
|
mask = pango_font_description_get_set_fields (font_desc);
|
|
|
|
/* sucky test, because we can't restrict the comparison to
|
|
* only the parts that actually do get merged */
|
|
if (pango_font_description_equal (font_desc, priv->font_desc))
|
|
return;
|
|
|
|
pango_font_description_merge (priv->font_desc, font_desc, TRUE);
|
|
|
|
if (mask & PANGO_FONT_MASK_SIZE)
|
|
{
|
|
double font_size = (double) pango_font_description_get_size (priv->font_desc) / PANGO_SCALE;
|
|
/* XXX: This clamps, which can cause it to reloop into here, do we need
|
|
* to block its signal handler? */
|
|
gtk_range_set_value (GTK_RANGE (priv->size_slider), font_size);
|
|
gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->size_spin), font_size);
|
|
}
|
|
if (mask & (PANGO_FONT_MASK_FAMILY | PANGO_FONT_MASK_STYLE | PANGO_FONT_MASK_VARIANT |
|
|
PANGO_FONT_MASK_WEIGHT | PANGO_FONT_MASK_STRETCH))
|
|
{
|
|
if (&priv->font_iter != iter)
|
|
{
|
|
if (iter == NULL)
|
|
memset (&priv->font_iter, 0, sizeof (GtkTreeIter));
|
|
else
|
|
memcpy (&priv->font_iter, iter, sizeof (GtkTreeIter));
|
|
|
|
gtk_font_chooser_widget_ensure_selection (fontchooser);
|
|
}
|
|
|
|
gtk_font_chooser_widget_update_marks (fontchooser);
|
|
}
|
|
|
|
gtk_font_chooser_widget_update_preview_attributes (fontchooser);
|
|
|
|
g_object_notify (G_OBJECT (fontchooser), "font");
|
|
g_object_notify (G_OBJECT (fontchooser), "font-desc");
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_take_font_desc (GtkFontChooserWidget *fontchooser,
|
|
PangoFontDescription *font_desc)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
PangoFontMask mask;
|
|
|
|
if (font_desc == NULL)
|
|
font_desc = pango_font_description_from_string (GTK_FONT_CHOOSER_DEFAULT_FONT_NAME);
|
|
|
|
mask = pango_font_description_get_set_fields (font_desc);
|
|
if (mask & (PANGO_FONT_MASK_FAMILY | PANGO_FONT_MASK_STYLE | PANGO_FONT_MASK_VARIANT |
|
|
PANGO_FONT_MASK_WEIGHT | PANGO_FONT_MASK_STRETCH))
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
if (gtk_font_chooser_widget_find_font (fontchooser, font_desc, &iter))
|
|
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, &iter);
|
|
else
|
|
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, NULL);
|
|
}
|
|
else
|
|
{
|
|
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, &priv->font_iter);
|
|
}
|
|
|
|
pango_font_description_free (font_desc);
|
|
}
|
|
|
|
static const gchar*
|
|
gtk_font_chooser_widget_get_preview_text (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
return fontchooser->priv->preview_text;
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_set_preview_text (GtkFontChooserWidget *fontchooser,
|
|
const gchar *text)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
g_free (priv->preview_text);
|
|
priv->preview_text = g_strdup (text);
|
|
|
|
gtk_entry_set_text (GTK_ENTRY (priv->preview), text);
|
|
|
|
g_object_notify (G_OBJECT (fontchooser), "preview-text");
|
|
|
|
/* XXX: There's no API to tell the treeview that a column has changed,
|
|
* so we just */
|
|
gtk_widget_queue_draw (priv->family_face_list);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_font_chooser_widget_get_show_preview_entry (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
return fontchooser->priv->show_preview_entry;
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_set_show_preview_entry (GtkFontChooserWidget *fontchooser,
|
|
gboolean show_preview_entry)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
if (priv->show_preview_entry != show_preview_entry)
|
|
{
|
|
fontchooser->priv->show_preview_entry = show_preview_entry;
|
|
|
|
if (show_preview_entry)
|
|
gtk_widget_show (fontchooser->priv->preview);
|
|
else
|
|
gtk_widget_hide (fontchooser->priv->preview);
|
|
|
|
g_object_notify (G_OBJECT (fontchooser), "show-preview-entry");
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_set_font_map (GtkFontChooser *chooser,
|
|
PangoFontMap *fontmap)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (chooser);
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
if (g_set_object (&priv->font_map, fontmap))
|
|
{
|
|
PangoContext *context;
|
|
|
|
if (!fontmap)
|
|
fontmap = pango_cairo_font_map_get_default ();
|
|
|
|
context = gtk_widget_get_pango_context (priv->family_face_list);
|
|
pango_context_set_font_map (context, fontmap);
|
|
|
|
context = gtk_widget_get_pango_context (priv->preview);
|
|
pango_context_set_font_map (context, fontmap);
|
|
|
|
gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
|
|
}
|
|
}
|
|
|
|
static PangoFontMap *
|
|
gtk_font_chooser_widget_get_font_map (GtkFontChooser *chooser)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (chooser);
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
return priv->font_map;
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_set_filter_func (GtkFontChooser *chooser,
|
|
GtkFontFilterFunc filter,
|
|
gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (chooser);
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
if (priv->filter_data_destroy)
|
|
priv->filter_data_destroy (priv->filter_data);
|
|
|
|
priv->filter_func = filter;
|
|
priv->filter_data = data;
|
|
priv->filter_data_destroy = destroy;
|
|
|
|
gtk_font_chooser_widget_refilter_font_list (fontchooser);
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_set_level (GtkFontChooserWidget *fontchooser,
|
|
GtkFontChooserLevel level)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
if (priv->level == level)
|
|
return;
|
|
|
|
priv->level = level;
|
|
|
|
if (level == GTK_FONT_CHOOSER_LEVEL_FONT)
|
|
{
|
|
gtk_widget_show (priv->size_label);
|
|
gtk_widget_show (priv->size_slider);
|
|
gtk_widget_show (priv->size_spin);
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_hide (priv->size_label);
|
|
gtk_widget_hide (priv->size_slider);
|
|
gtk_widget_hide (priv->size_spin);
|
|
}
|
|
|
|
gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
|
|
|
|
g_object_notify (G_OBJECT (fontchooser), "level");
|
|
}
|
|
|
|
static GtkFontChooserLevel
|
|
gtk_font_chooser_widget_get_level (GtkFontChooserWidget *fontchooser)
|
|
{
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
|
|
return priv->level;
|
|
}
|
|
|
|
static void
|
|
gtk_font_chooser_widget_iface_init (GtkFontChooserIface *iface)
|
|
{
|
|
iface->get_font_family = gtk_font_chooser_widget_get_family;
|
|
iface->get_font_face = gtk_font_chooser_widget_get_face;
|
|
iface->get_font_size = gtk_font_chooser_widget_get_size;
|
|
iface->set_filter_func = gtk_font_chooser_widget_set_filter_func;
|
|
iface->set_font_map = gtk_font_chooser_widget_set_font_map;
|
|
iface->get_font_map = gtk_font_chooser_widget_get_font_map;
|
|
}
|
|
|
|
gboolean
|
|
gtk_font_chooser_widget_handle_event (GtkWidget *widget,
|
|
GdkEventKey *key_event)
|
|
{
|
|
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (widget);
|
|
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
|
GdkEvent *event = (GdkEvent *)key_event;
|
|
|
|
return gtk_search_entry_handle_event (GTK_SEARCH_ENTRY (priv->search_entry), event);
|
|
}
|
|
|
|
GAction *
|
|
gtk_font_chooser_widget_get_tweak_action (GtkWidget *widget)
|
|
{
|
|
return GTK_FONT_CHOOSER_WIDGET (widget)->priv->tweak_action;
|
|
}
|
|
|