mirror of https://github.com/Gnucash/gnucash
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
530 lines
14 KiB
530 lines
14 KiB
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/*
|
|
* A font selector widget. This is a simplified version of the
|
|
* GnomePrint font selector widget.
|
|
*
|
|
* Authors:
|
|
* Jody Goldberg (jody@gnome.org)
|
|
* Miguel de Icaza (miguel@gnu.org)
|
|
* Almer S. Tigelaar (almer@gnome.org)
|
|
*/
|
|
#include <goffice/goffice-config.h>
|
|
#include "go-font-sel.h"
|
|
#include <goffice/utils/go-font.h>
|
|
#include <goffice/gui-utils/go-gui-utils.h>
|
|
#include <goffice/utils/go-color.h>
|
|
|
|
#include <gtk/gtkhbox.h>
|
|
#include <gtk/gtkscrolledwindow.h>
|
|
#include <gtk/gtktreeview.h>
|
|
#include <gtk/gtktreeselection.h>
|
|
#include <gtk/gtkliststore.h>
|
|
#include <gtk/gtkcellrenderertext.h>
|
|
#include <gtk/gtkentry.h>
|
|
|
|
#include <libfoocanvas/foo-canvas.h>
|
|
#include <libfoocanvas/foo-canvas-text.h>
|
|
|
|
#include <gsf/gsf-impl-utils.h>
|
|
#include <glib/gi18n.h>
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
|
|
struct _GOFontSel {
|
|
GtkHBox box;
|
|
GladeXML *gui;
|
|
|
|
GtkWidget *font_name_entry;
|
|
GtkWidget *font_style_entry;
|
|
GtkWidget *font_size_entry;
|
|
GtkTreeView *font_name_list;
|
|
GtkTreeView *font_style_list;
|
|
GtkTreeView *font_size_list;
|
|
|
|
FooCanvas *font_preview_canvas;
|
|
FooCanvasItem *font_preview_text;
|
|
|
|
GOFont *base, *current;
|
|
PangoAttrList *modifications;
|
|
};
|
|
|
|
typedef struct {
|
|
GtkHBoxClass parent_class;
|
|
|
|
void (* font_changed) (GOFontSel *gfs, PangoAttrList *modfications);
|
|
} GOFontSelClass;
|
|
|
|
enum {
|
|
FONT_CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint gfs_signals[LAST_SIGNAL] = { 0 };
|
|
static GtkObjectClass *gfs_parent_class;
|
|
|
|
static void
|
|
go_font_sel_add_attr (GOFontSel *gfs, PangoAttribute *attr0, PangoAttribute *attr1)
|
|
{
|
|
attr0->start_index = 0;
|
|
attr0->end_index = -1;
|
|
pango_attr_list_change (gfs->modifications, attr0);
|
|
if (attr1 != NULL) {
|
|
attr1->start_index = 0;
|
|
attr1->end_index = -1;
|
|
pango_attr_list_change (gfs->modifications, attr1);
|
|
}
|
|
|
|
}
|
|
static void
|
|
go_dont_sel_emit_changed (GOFontSel *gfs)
|
|
{
|
|
g_signal_emit (G_OBJECT (gfs),
|
|
gfs_signals [FONT_CHANGED], 0, gfs->modifications);
|
|
foo_canvas_item_set (gfs->font_preview_text,
|
|
"attributes", gfs->modifications,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
cb_list_adjust (GtkTreeView* view)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
GtkTreePath *path;
|
|
GtkScrolledWindow* scroll;
|
|
GdkRectangle rect;
|
|
GtkAdjustment *adj;
|
|
int pos, height, child_height;
|
|
|
|
if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (view), &model, &iter)) {
|
|
path = gtk_tree_model_get_path (model, &iter);
|
|
scroll = GTK_SCROLLED_WINDOW (gtk_widget_get_parent (GTK_WIDGET (view)));
|
|
height = GTK_WIDGET (view)->allocation.height;
|
|
child_height = GTK_WIDGET (view)->requisition.height;
|
|
if (height < child_height) {
|
|
gtk_tree_view_get_cell_area (view, path, NULL, &rect);
|
|
adj = gtk_scrolled_window_get_vadjustment (scroll);
|
|
pos = gtk_adjustment_get_value (adj);
|
|
if (rect.y < 0)
|
|
pos += rect.y;
|
|
else if (rect.y + rect.height > height)
|
|
pos += rect.y + rect.height - height;
|
|
gtk_adjustment_set_value (adj, pos);
|
|
gtk_scrolled_window_set_vadjustment (scroll, adj);
|
|
}
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
|
|
static void
|
|
list_init (GtkTreeView* view)
|
|
{
|
|
GtkCellRenderer *renderer;
|
|
GtkListStore *store;
|
|
GtkTreeViewColumn *column;
|
|
|
|
gtk_tree_view_set_headers_visible (view, FALSE);
|
|
store = gtk_list_store_new (1, G_TYPE_STRING);
|
|
gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
column = gtk_tree_view_column_new_with_attributes (
|
|
NULL, renderer, "text", 0, NULL);
|
|
gtk_tree_view_column_set_expand (column, TRUE);
|
|
gtk_tree_view_append_column (view, column);
|
|
g_signal_connect (view, "realize", G_CALLBACK (cb_list_adjust), NULL);
|
|
}
|
|
|
|
static void
|
|
font_selected (GtkTreeSelection *selection, GOFontSel *gfs)
|
|
{
|
|
gchar *text;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
|
|
gtk_tree_model_get (model, &iter, 0, &text, -1);
|
|
gtk_entry_set_text (GTK_ENTRY (gfs->font_name_entry), text);
|
|
go_font_sel_add_attr (gfs, pango_attr_family_new (text), NULL);
|
|
go_dont_sel_emit_changed (gfs);
|
|
g_free (text);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gfs_fill_font_name_list (GOFontSel *gfs)
|
|
{
|
|
GSList *ptr;
|
|
GtkListStore *store;
|
|
GtkTreeIter iter;
|
|
|
|
list_init (gfs->font_name_list);
|
|
store = GTK_LIST_STORE (gtk_tree_view_get_model (gfs->font_name_list));
|
|
for (ptr = go_font_family_list; ptr != NULL; ptr = ptr->next) {
|
|
gtk_list_store_append (store, &iter);
|
|
gtk_list_store_set (store, &iter, 0, ptr->data, -1);
|
|
}
|
|
|
|
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (gfs->font_name_list)),
|
|
"changed",
|
|
G_CALLBACK (font_selected), gfs);
|
|
}
|
|
|
|
static char const *styles[] = {
|
|
N_("Normal"),
|
|
N_("Bold"),
|
|
N_("Bold italic"),
|
|
N_("Italic"),
|
|
NULL
|
|
};
|
|
|
|
static void
|
|
style_selected (GtkTreeSelection *selection,
|
|
GOFontSel *gfs)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
GtkTreePath *path;
|
|
int row;
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
|
|
path = gtk_tree_model_get_path (model, &iter);
|
|
row = *gtk_tree_path_get_indices (path);
|
|
gtk_tree_path_free (path);
|
|
gtk_entry_set_text (GTK_ENTRY (gfs->font_style_entry), _(styles[row]));
|
|
go_font_sel_add_attr (gfs,
|
|
pango_attr_weight_new ((row == 0 || row == 3)
|
|
? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL),
|
|
pango_attr_style_new ((row == 1 || row == 3)
|
|
? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
|
|
go_dont_sel_emit_changed (gfs);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gfs_fill_font_style_list (GOFontSel *gfs)
|
|
{
|
|
int i;
|
|
GtkListStore *store;
|
|
GtkTreeIter iter;
|
|
|
|
list_init (gfs->font_style_list);
|
|
store = GTK_LIST_STORE (gtk_tree_view_get_model (gfs->font_style_list));
|
|
for (i = 0; styles[i] != NULL; i++) {
|
|
gtk_list_store_append (store, &iter);
|
|
gtk_list_store_set (store, &iter, 0, _(styles[i]), -1);
|
|
}
|
|
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (gfs->font_style_list)),
|
|
"changed",
|
|
G_CALLBACK (style_selected), gfs);
|
|
}
|
|
|
|
static void
|
|
select_row (GtkTreeView *list, int row)
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
if (row < 0)
|
|
gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (list));
|
|
else {
|
|
path = gtk_tree_path_new_from_indices (row, -1);
|
|
|
|
gtk_tree_selection_select_path (gtk_tree_view_get_selection (list), path);
|
|
if (GTK_WIDGET_REALIZED (list))
|
|
cb_list_adjust (list);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
|
|
static float
|
|
size_set_text (GOFontSel *gfs, char const *size_text)
|
|
{
|
|
char *end;
|
|
float size;
|
|
errno = 0; /* strtol sets errno, but does not clear it. */
|
|
size = strtod (size_text, &end);
|
|
size = ((int)floor ((size * 20.) + .5)) / 20.; /* round .05 */
|
|
|
|
if (size_text != end && errno != ERANGE && 1. <= size && size <= 400.) {
|
|
gtk_entry_set_text (GTK_ENTRY (gfs->font_size_entry), size_text);
|
|
go_font_sel_add_attr (gfs,
|
|
pango_attr_size_new (size * PANGO_SCALE), NULL);
|
|
go_dont_sel_emit_changed (gfs);
|
|
return size;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
size_selected (GtkTreeSelection *selection,
|
|
GOFontSel *gfs)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
char *size_text;
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
|
|
gtk_tree_model_get (model, &iter, 0, &size_text, -1);
|
|
size_set_text (gfs, size_text);
|
|
g_free (size_text);
|
|
}
|
|
}
|
|
|
|
static void
|
|
size_changed (GtkEntry *entry, GOFontSel *gfs)
|
|
{
|
|
int i;
|
|
float size = size_set_text (gfs, gtk_entry_get_text (entry));
|
|
|
|
if (size > 0) {
|
|
for (i = 0; go_font_sizes [i] != 0; i++)
|
|
if (go_font_sizes [i] == size)
|
|
break;
|
|
g_signal_handlers_block_by_func (
|
|
gtk_tree_view_get_selection (gfs->font_size_list),
|
|
size_selected, gfs);
|
|
select_row (gfs->font_size_list, (go_font_sizes [i] != 0) ? i : -1);
|
|
g_signal_handlers_unblock_by_func (
|
|
gtk_tree_view_get_selection (gfs->font_size_list),
|
|
size_selected, gfs);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gfs_fill_font_size_list (GOFontSel *gfs)
|
|
{
|
|
int i;
|
|
GtkListStore *store;
|
|
GtkTreeIter iter;
|
|
|
|
list_init (gfs->font_size_list);
|
|
store = GTK_LIST_STORE (gtk_tree_view_get_model (gfs->font_size_list));
|
|
for (i = 0; go_font_sizes [i] != 0; i++) {
|
|
char buffer[4 * sizeof (int)];
|
|
sprintf (buffer, "%d", go_font_sizes [i]);
|
|
gtk_list_store_append (store, &iter);
|
|
gtk_list_store_set (store, &iter, 0, buffer, -1);
|
|
}
|
|
g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (gfs->font_size_list)),
|
|
"changed",
|
|
G_CALLBACK (size_selected), gfs);
|
|
g_signal_connect (G_OBJECT (gfs->font_size_entry),
|
|
"changed",
|
|
G_CALLBACK (size_changed), gfs);
|
|
}
|
|
|
|
static void
|
|
canvas_size_changed (G_GNUC_UNUSED GtkWidget *widget,
|
|
GtkAllocation *allocation, GOFontSel *gfs)
|
|
{
|
|
int width = allocation->width - 1;
|
|
int height = allocation->height - 1;
|
|
|
|
foo_canvas_item_set (gfs->font_preview_text,
|
|
"default-col-width", width,
|
|
"default-row-height", height,
|
|
NULL);
|
|
|
|
foo_canvas_set_scroll_region (gfs->font_preview_canvas, 0, 0,
|
|
width, height);
|
|
}
|
|
|
|
static void
|
|
gfs_init (GOFontSel *gfs)
|
|
{
|
|
GtkWidget *w;
|
|
|
|
gfs->gui = go_libglade_new ("go-font-sel.glade", "toplevel-table", NULL, NULL);
|
|
if (gfs->gui == NULL)
|
|
return;
|
|
|
|
gfs->modifications = pango_attr_list_new ();
|
|
|
|
gtk_box_pack_start_defaults (GTK_BOX (gfs),
|
|
glade_xml_get_widget (gfs->gui, "toplevel-table"));
|
|
gfs->font_name_entry = glade_xml_get_widget (gfs->gui, "font-name-entry");
|
|
gfs->font_style_entry = glade_xml_get_widget (gfs->gui, "font-style-entry");
|
|
gfs->font_size_entry = glade_xml_get_widget (gfs->gui, "font-size-entry");
|
|
gfs->font_name_list = GTK_TREE_VIEW (glade_xml_get_widget (gfs->gui, "font-name-list"));
|
|
gfs->font_style_list = GTK_TREE_VIEW (glade_xml_get_widget (gfs->gui, "font-style-list"));
|
|
gfs->font_size_list = GTK_TREE_VIEW (glade_xml_get_widget (gfs->gui, "font-size-list"));
|
|
|
|
w = foo_canvas_new ();
|
|
gfs->font_preview_canvas = FOO_CANVAS (w);
|
|
foo_canvas_set_scroll_region (gfs->font_preview_canvas, -1, -1, INT_MAX/2, INT_MAX/2);
|
|
foo_canvas_scroll_to (gfs->font_preview_canvas, 0, 0);
|
|
gtk_widget_show_all (w);
|
|
w = glade_xml_get_widget (gfs->gui, "font-preview-frame");
|
|
gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (gfs->font_preview_canvas));
|
|
|
|
gfs->font_preview_text = FOO_CANVAS_ITEM (foo_canvas_item_new (
|
|
foo_canvas_root (gfs->font_preview_canvas),
|
|
FOO_TYPE_CANVAS_TEXT,
|
|
NULL));
|
|
go_font_sel_set_sample_text (gfs, NULL); /* init to default */
|
|
|
|
g_signal_connect (G_OBJECT (gfs->font_preview_canvas),
|
|
"size-allocate",
|
|
G_CALLBACK (canvas_size_changed), gfs);
|
|
|
|
gfs_fill_font_name_list (gfs);
|
|
gfs_fill_font_style_list (gfs);
|
|
gfs_fill_font_size_list (gfs);
|
|
}
|
|
|
|
static void
|
|
gfs_destroy (GtkObject *object)
|
|
{
|
|
GOFontSel *gfs = GO_FONT_SEL (object);
|
|
|
|
if (gfs->gui) {
|
|
g_object_unref (G_OBJECT (gfs->gui));
|
|
gfs->gui = NULL;
|
|
}
|
|
if (gfs->base != NULL) {
|
|
go_font_unref (gfs->base);
|
|
gfs->base = NULL;
|
|
}
|
|
if (gfs->current != NULL) {
|
|
go_font_unref (gfs->current);
|
|
gfs->current = NULL;
|
|
}
|
|
if (gfs->modifications != NULL) {
|
|
pango_attr_list_unref (gfs->modifications);
|
|
gfs->modifications = NULL;
|
|
}
|
|
|
|
gfs_parent_class->destroy (object);
|
|
}
|
|
|
|
static void
|
|
gfs_class_init (GObjectClass *klass)
|
|
{
|
|
GtkObjectClass *gto_class = (GtkObjectClass *) klass;
|
|
|
|
gto_class->destroy = gfs_destroy;
|
|
|
|
gfs_parent_class = g_type_class_peek (gtk_hbox_get_type ());
|
|
|
|
gfs_signals [FONT_CHANGED] =
|
|
g_signal_new (
|
|
"font_changed",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GOFontSelClass, font_changed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__POINTER,
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
|
}
|
|
|
|
GSF_CLASS (GOFontSel, go_font_sel,
|
|
gfs_class_init, gfs_init, GTK_TYPE_HBOX)
|
|
|
|
GtkWidget *
|
|
go_font_sel_new (void)
|
|
{
|
|
return g_object_new (GO_FONT_SEL_TYPE, NULL);
|
|
}
|
|
|
|
void
|
|
go_font_sel_editable_enters (GOFontSel *gfs, GtkWindow *dialog)
|
|
{
|
|
go_editable_enters (dialog,
|
|
GTK_WIDGET (gfs->font_name_entry));
|
|
go_editable_enters (dialog,
|
|
GTK_WIDGET (gfs->font_style_entry));
|
|
go_editable_enters (dialog,
|
|
GTK_WIDGET (gfs->font_size_entry));
|
|
}
|
|
|
|
void
|
|
go_font_sel_set_sample_text (GOFontSel *gfs, char const *text)
|
|
{
|
|
g_return_if_fail (IS_GO_FONT_SEL (gfs));
|
|
foo_canvas_item_set (gfs->font_preview_text,
|
|
/* xgettext: This text is used as a sample when selecting a font
|
|
* please choose a translation that would produce common
|
|
* characters specific to the target alphabet. */
|
|
"text", ((text == NULL) ? _("AaBbCcDdEe12345") : text),
|
|
NULL);
|
|
}
|
|
|
|
GOFont const *
|
|
go_font_sel_get_font (GOFontSel const *gfs)
|
|
{
|
|
g_return_val_if_fail (IS_GO_FONT_SEL (gfs), NULL);
|
|
return gfs->current;
|
|
}
|
|
|
|
static void
|
|
go_font_sel_set_name (GOFontSel *gfs, char const *font_name)
|
|
{
|
|
GSList *ptr;
|
|
int row;
|
|
|
|
for (row = 0, ptr = go_font_family_list; ptr != NULL; ptr = ptr->next, row++)
|
|
if (g_ascii_strcasecmp (font_name, ptr->data) == 0)
|
|
break;
|
|
select_row (gfs->font_name_list, (ptr != NULL) ? row : -1);
|
|
}
|
|
|
|
static void
|
|
go_font_sel_set_style (GOFontSel *gfs, gboolean is_bold, gboolean is_italic)
|
|
{
|
|
int n;
|
|
|
|
if (is_bold) {
|
|
if (is_italic)
|
|
n = 2;
|
|
else
|
|
n = 1;
|
|
} else {
|
|
if (is_italic)
|
|
n = 3;
|
|
else
|
|
n = 0;
|
|
}
|
|
select_row (gfs->font_style_list, n);
|
|
|
|
go_font_sel_add_attr (gfs,
|
|
pango_attr_weight_new (is_bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL),
|
|
pango_attr_style_new (is_italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
|
|
go_dont_sel_emit_changed (gfs);
|
|
}
|
|
|
|
static void
|
|
go_font_sel_set_points (GOFontSel *gfs,
|
|
double point_size)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; go_font_sizes [i] != 0; i++)
|
|
if (go_font_sizes [i] == point_size) {
|
|
select_row (gfs->font_size_list, i);
|
|
break;
|
|
}
|
|
|
|
if (go_font_sizes [i] == 0) {
|
|
char *buffer;
|
|
buffer = g_strdup_printf ("%g", point_size);
|
|
gtk_entry_set_text (GTK_ENTRY (gfs->font_size_entry), buffer);
|
|
g_free (buffer);
|
|
}
|
|
}
|
|
|
|
void
|
|
go_font_sel_set_font (GOFontSel *gfs, GOFont const *font)
|
|
{
|
|
g_return_if_fail (IS_GO_FONT_SEL (gfs));
|
|
|
|
go_font_sel_set_name (gfs, pango_font_description_get_family (font->desc));
|
|
go_font_sel_set_style (gfs,
|
|
pango_font_description_get_weight (font->desc) >= PANGO_WEIGHT_BOLD,
|
|
pango_font_description_get_style (font->desc) != PANGO_STYLE_NORMAL);
|
|
go_font_sel_set_points (gfs,
|
|
pango_font_description_get_size (font->desc) / PANGO_SCALE);
|
|
go_font_sel_set_strike (gfs, font->has_strike);
|
|
go_font_sel_set_uline (gfs, font->underline);
|
|
go_font_sel_set_color (gfs, font->color);
|
|
}
|