2007-03-21 Federico Mena Quintero <federico@novell.com> Fix the gnome-settings-daemon part of https://bugzilla.novell.com/show_bug.cgi?id=217790 and http://bugzilla.gnome.org/show_bug.cgi?id=378338: try to figure out the DPI value from the X server or the user's GConf settings. Should also fix https://bugzilla.novell.com/show_bug.cgi?id=240246. * gnome-settings-daemon/gnome-settings-xsettings.c (gnome_xft_settings_get): Call get_dpi_from_gconf_or_server() to figure out a reasonable DPI value; don't unconditionally get it from GConf. (get_dpi_from_gconf_or_server): New function. If the user has ever set the /desktop/gnome/font_rendering/dpi value in GConf, we use its value. Otherwise, we ask the X server. We constrain the X server's response to a range of reasonable DPI values, since some servers lie about the screen's phisical dimensions --- the user would get unusably huge or tiny fonts otherwise. * capplets/font/main.c (dpi_load): First, see if the DPI value is actually set in GConf. If it is, it means that the user has changed it at least once. In that case, just use the value. Otherwise, find the value from the X server in a similar way to what we do in gnome-settings-daemon. svn path=/trunk/; revision=7409
1020 lines
27 KiB
C
1020 lines
27 KiB
C
/* This program was written with lots of love under the GPL by Jonathan
|
|
* Blandford <jrb@gnome.org>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <string.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gconf/gconf-client.h>
|
|
#include <glade/glade.h>
|
|
#include <stdarg.h>
|
|
#include <math.h>
|
|
|
|
#ifdef HAVE_XFT2
|
|
#include <gdk/gdkx.h>
|
|
#include <X11/Xft/Xft.h>
|
|
#endif /* HAVE_XFT2 */
|
|
|
|
#include "capplet-util.h"
|
|
#include "activate-settings-daemon.h"
|
|
#include "gconf-property-editor.h"
|
|
|
|
#ifdef HAVE_XFT2
|
|
static void cb_show_details (GtkWidget *button,
|
|
GtkWindow *parent);
|
|
#endif /* HAVE_XFT2 */
|
|
|
|
#define GTK_FONT_KEY "/desktop/gnome/interface/font_name"
|
|
#define DESKTOP_FONT_KEY "/apps/nautilus/preferences/desktop_font"
|
|
|
|
#define METACITY_DIR "/apps/metacity/general"
|
|
#define WINDOW_TITLE_FONT_KEY METACITY_DIR "/titlebar_font"
|
|
#define WINDOW_TITLE_USES_SYSTEM_KEY METACITY_DIR "/titlebar_uses_system_font"
|
|
#define MONOSPACE_FONT_KEY "/desktop/gnome/interface/monospace_font_name"
|
|
#define DOCUMENT_FONT_KEY "/desktop/gnome/interface/document_font_name"
|
|
|
|
#ifdef HAVE_XFT2
|
|
#define FONT_RENDER_DIR "/desktop/gnome/font_rendering"
|
|
#define FONT_ANTIALIASING_KEY FONT_RENDER_DIR "/antialiasing"
|
|
#define FONT_HINTING_KEY FONT_RENDER_DIR "/hinting"
|
|
#define FONT_RGBA_ORDER_KEY FONT_RENDER_DIR "/rgba_order"
|
|
#define FONT_DPI_KEY FONT_RENDER_DIR "/dpi"
|
|
|
|
/* X servers sometimes lie about the screen's physical dimensions, so we cannot
|
|
* compute an accurate DPI value. When this happens, the user gets fonts that
|
|
* are too huge or too tiny. So, we see what the server returns: if it reports
|
|
* something outside of the range [DPI_LOW_REASONABLE_VALUE,
|
|
* DPI_HIGH_REASONABLE_VALUE], then we assume that it is lying and we use
|
|
* DPI_FALLBACK instead.
|
|
*
|
|
* See get_dpi_from_gconf_or_server() below, and also
|
|
* https://bugzilla.novell.com/show_bug.cgi?id=217790
|
|
*/
|
|
#define DPI_FALLBACK 96
|
|
#define DPI_LOW_REASONABLE_VALUE 50
|
|
#define DPI_HIGH_REASONABLE_VALUE 500
|
|
#endif /* HAVE_XFT2 */
|
|
static gboolean in_change = FALSE;
|
|
static gchar *old_font = NULL;
|
|
|
|
#define MAX_FONT_POINT_WITHOUT_WARNING 32
|
|
#define MAX_FONT_SIZE_WITHOUT_WARNING MAX_FONT_POINT_WITHOUT_WARNING*1024
|
|
|
|
static void
|
|
cb_dialog_response (GtkDialog *dialog, gint response_id)
|
|
{
|
|
if (response_id == GTK_RESPONSE_HELP)
|
|
capplet_help (GTK_WINDOW (dialog),
|
|
"user-guide.xml",
|
|
"goscustdesk-38");
|
|
else
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
#ifdef HAVE_XFT2
|
|
|
|
/*
|
|
* Code for displaying previews of font rendering with various Xft options
|
|
*/
|
|
|
|
static void
|
|
sample_size_request (GtkWidget *darea,
|
|
GtkRequisition *requisition)
|
|
{
|
|
GdkPixbuf *pixbuf = g_object_get_data (G_OBJECT (darea), "sample-pixbuf");
|
|
|
|
requisition->width = gdk_pixbuf_get_width (pixbuf) + 2;
|
|
requisition->height = gdk_pixbuf_get_height (pixbuf) + 2;
|
|
}
|
|
|
|
static void
|
|
sample_expose (GtkWidget *darea,
|
|
GdkEventExpose *expose)
|
|
{
|
|
GdkPixbuf *pixbuf = g_object_get_data (G_OBJECT (darea), "sample-pixbuf");
|
|
int width = gdk_pixbuf_get_width (pixbuf);
|
|
int height = gdk_pixbuf_get_height (pixbuf);
|
|
|
|
int x = (darea->allocation.width - width) / 2;
|
|
int y = (darea->allocation.height - height) / 2;
|
|
|
|
gdk_draw_rectangle (darea->window, darea->style->white_gc, TRUE,
|
|
0, 0,
|
|
darea->allocation.width, darea->allocation.height);
|
|
gdk_draw_rectangle (darea->window, darea->style->black_gc, FALSE,
|
|
0, 0,
|
|
darea->allocation.width - 1, darea->allocation.height - 1);
|
|
|
|
gdk_pixbuf_render_to_drawable (pixbuf, darea->window, NULL,
|
|
0, 0, x, y, width, height,
|
|
GDK_RGB_DITHER_NORMAL, 0, 0);
|
|
}
|
|
|
|
typedef enum {
|
|
ANTIALIAS_NONE,
|
|
ANTIALIAS_GRAYSCALE,
|
|
ANTIALIAS_RGBA
|
|
} Antialiasing;
|
|
|
|
static GConfEnumStringPair antialias_enums[] = {
|
|
{ ANTIALIAS_NONE, "none" },
|
|
{ ANTIALIAS_GRAYSCALE, "grayscale" },
|
|
{ ANTIALIAS_RGBA, "rgba" },
|
|
{ -1, NULL }
|
|
};
|
|
|
|
typedef enum {
|
|
HINT_NONE,
|
|
HINT_SLIGHT,
|
|
HINT_MEDIUM,
|
|
HINT_FULL
|
|
} Hinting;
|
|
|
|
static GConfEnumStringPair hint_enums[] = {
|
|
{ HINT_NONE, "none" },
|
|
{ HINT_SLIGHT, "slight" },
|
|
{ HINT_MEDIUM, "medium" },
|
|
{ HINT_FULL, "full" },
|
|
{ -1, NULL }
|
|
};
|
|
|
|
typedef enum {
|
|
RGBA_RGB,
|
|
RGBA_BGR,
|
|
RGBA_VRGB,
|
|
RGBA_VBGR
|
|
} RgbaOrder;
|
|
|
|
static GConfEnumStringPair rgba_order_enums[] = {
|
|
{ RGBA_RGB, "rgb" },
|
|
{ RGBA_BGR, "bgr" },
|
|
{ RGBA_VRGB, "vrgb" },
|
|
{ RGBA_VBGR, "vbgr" },
|
|
{ -1, NULL }
|
|
};
|
|
|
|
static XftFont *
|
|
open_pattern (FcPattern *pattern,
|
|
Antialiasing antialiasing,
|
|
Hinting hinting)
|
|
{
|
|
#ifdef FC_HINT_STYLE
|
|
static const int hintstyles[] = { FC_HINT_NONE, FC_HINT_SLIGHT, FC_HINT_MEDIUM, FC_HINT_FULL };
|
|
#endif /* FC_HINT_STYLE */
|
|
|
|
FcPattern *res_pattern;
|
|
FcResult result;
|
|
XftFont *font;
|
|
|
|
Display *xdisplay = gdk_x11_get_default_xdisplay ();
|
|
int screen = gdk_x11_get_default_screen ();
|
|
|
|
res_pattern = XftFontMatch (xdisplay, screen, pattern, &result);
|
|
if (res_pattern == NULL)
|
|
return NULL;
|
|
|
|
FcPatternDel (res_pattern, FC_HINTING);
|
|
FcPatternAddBool (res_pattern, FC_HINTING, hinting != HINT_NONE);
|
|
|
|
#ifdef FC_HINT_STYLE
|
|
FcPatternDel (res_pattern, FC_HINT_STYLE);
|
|
FcPatternAddInteger (res_pattern, FC_HINT_STYLE, hintstyles[hinting]);
|
|
#endif /* FC_HINT_STYLE */
|
|
|
|
FcPatternDel (res_pattern, FC_ANTIALIAS);
|
|
FcPatternAddBool (res_pattern, FC_ANTIALIAS, antialiasing != ANTIALIAS_NONE);
|
|
|
|
FcPatternDel (res_pattern, FC_RGBA);
|
|
FcPatternAddInteger (res_pattern, FC_RGBA,
|
|
antialiasing == ANTIALIAS_RGBA ? FC_RGBA_RGB : FC_RGBA_NONE);
|
|
|
|
FcPatternDel (res_pattern, FC_DPI);
|
|
FcPatternAddInteger (res_pattern, FC_DPI, 96);
|
|
|
|
font = XftFontOpenPattern (xdisplay, res_pattern);
|
|
if (!font)
|
|
FcPatternDestroy (res_pattern);
|
|
|
|
return font;
|
|
}
|
|
|
|
static void
|
|
setup_font_sample (GtkWidget *darea,
|
|
Antialiasing antialiasing,
|
|
Hinting hinting)
|
|
{
|
|
const char *string1 = "abcfgop AO ";
|
|
const char *string2 = "abcfgop";
|
|
|
|
XftColor black, white;
|
|
XRenderColor rendcolor;
|
|
|
|
Display *xdisplay = gdk_x11_get_default_xdisplay ();
|
|
|
|
GdkColormap *colormap = gdk_rgb_get_colormap ();
|
|
Colormap xcolormap = GDK_COLORMAP_XCOLORMAP (colormap);
|
|
|
|
GdkVisual *visual = gdk_colormap_get_visual (colormap);
|
|
Visual *xvisual = GDK_VISUAL_XVISUAL (visual);
|
|
|
|
FcPattern *pattern;
|
|
XftFont *font1, *font2;
|
|
XGlyphInfo extents1 = { 0 };
|
|
XGlyphInfo extents2 = { 0 };
|
|
GdkPixmap *pixmap;
|
|
XftDraw *draw;
|
|
GdkPixbuf *tmp_pixbuf, *pixbuf;
|
|
|
|
int width, height;
|
|
int ascent, descent;
|
|
|
|
pattern = FcPatternBuild (NULL,
|
|
FC_FAMILY, FcTypeString, "Serif",
|
|
FC_SLANT, FcTypeInteger, FC_SLANT_ROMAN,
|
|
FC_SIZE, FcTypeDouble, 18.,
|
|
NULL);
|
|
font1 = open_pattern (pattern, antialiasing, hinting);
|
|
FcPatternDestroy (pattern);
|
|
|
|
pattern = FcPatternBuild (NULL,
|
|
FC_FAMILY, FcTypeString, "Serif",
|
|
FC_SLANT, FcTypeInteger, FC_SLANT_ITALIC,
|
|
FC_SIZE, FcTypeDouble, 20.,
|
|
NULL);
|
|
font2 = open_pattern (pattern, antialiasing, hinting);
|
|
FcPatternDestroy (pattern);
|
|
|
|
if (font1)
|
|
XftTextExtentsUtf8 (xdisplay, font1, (unsigned char *)string1, strlen (string1), &extents1);
|
|
if (font2)
|
|
XftTextExtentsUtf8 (xdisplay, font2, (unsigned char *)string2, strlen (string2), &extents2);
|
|
|
|
ascent = 0;
|
|
if (font1)
|
|
ascent = MAX (ascent, font1->ascent);
|
|
if (font2)
|
|
ascent = MAX (ascent, font2->ascent);
|
|
|
|
descent = 0;
|
|
if (font1)
|
|
descent = MAX (descent, font1->descent);
|
|
if (font2)
|
|
descent = MAX (descent, font2->descent);
|
|
|
|
width = extents1.xOff + extents2.xOff + 4;
|
|
|
|
height = ascent + descent + 2;
|
|
|
|
pixmap = gdk_pixmap_new (NULL, width, height, visual->depth);
|
|
|
|
draw = XftDrawCreate (xdisplay, GDK_DRAWABLE_XID (pixmap), xvisual, xcolormap);
|
|
|
|
rendcolor.red = 0;
|
|
rendcolor.green = 0;
|
|
rendcolor.blue = 0;
|
|
rendcolor.alpha = 0xffff;
|
|
XftColorAllocValue (xdisplay, xvisual, xcolormap, &rendcolor, &black);
|
|
|
|
rendcolor.red = 0xffff;
|
|
rendcolor.green = 0xffff;
|
|
rendcolor.blue = 0xffff;
|
|
rendcolor.alpha = 0xffff;
|
|
XftColorAllocValue (xdisplay, xvisual, xcolormap, &rendcolor, &white);
|
|
XftDrawRect (draw, &white, 0, 0, width, height);
|
|
if (font1)
|
|
XftDrawStringUtf8 (draw, &black, font1,
|
|
2, 2 + ascent,
|
|
(unsigned char *)string1, strlen (string1));
|
|
if (font2)
|
|
XftDrawStringUtf8 (draw, &black, font2,
|
|
2 + extents1.xOff, 2 + ascent,
|
|
(unsigned char *)string2, strlen (string2));
|
|
|
|
XftDrawDestroy (draw);
|
|
|
|
if (font1)
|
|
XftFontClose (xdisplay, font1);
|
|
if (font2)
|
|
XftFontClose (xdisplay, font2);
|
|
|
|
tmp_pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, colormap, 0, 0, 0, 0, width, height);
|
|
pixbuf = gdk_pixbuf_scale_simple (tmp_pixbuf, 1 * width, 1 * height, GDK_INTERP_TILES);
|
|
|
|
g_object_unref (pixmap);
|
|
g_object_unref (tmp_pixbuf);
|
|
|
|
g_object_set_data_full (G_OBJECT (darea), "sample-pixbuf",
|
|
pixbuf, (GDestroyNotify)g_object_unref);
|
|
|
|
g_signal_connect (darea, "size_request", G_CALLBACK (sample_size_request), NULL);
|
|
g_signal_connect (darea, "expose_event", G_CALLBACK (sample_expose), NULL);
|
|
}
|
|
|
|
/*
|
|
* Code implementing a group of radio buttons with different Xft option combinations.
|
|
* If one of the buttons is matched by the GConf key, we pick it. Otherwise we
|
|
* show the group as inconsistent.
|
|
*/
|
|
static void
|
|
font_render_get_gconf (Antialiasing *antialiasing,
|
|
Hinting *hinting)
|
|
{
|
|
GConfClient *client = gconf_client_get_default ();
|
|
char *antialias_str = gconf_client_get_string (client, FONT_ANTIALIASING_KEY, NULL);
|
|
char *hint_str = gconf_client_get_string (client, FONT_HINTING_KEY, NULL);
|
|
int val;
|
|
|
|
val = ANTIALIAS_GRAYSCALE;
|
|
if (antialias_str) {
|
|
gconf_string_to_enum (antialias_enums, antialias_str, &val);
|
|
g_free (antialias_str);
|
|
}
|
|
*antialiasing = val;
|
|
|
|
val = HINT_FULL;
|
|
if (hint_str) {
|
|
gconf_string_to_enum (hint_enums, hint_str, &val);
|
|
g_free (hint_str);
|
|
}
|
|
*hinting = val;
|
|
|
|
g_object_unref (client);
|
|
}
|
|
|
|
typedef struct {
|
|
Antialiasing antialiasing;
|
|
Hinting hinting;
|
|
GtkWidget *radio;
|
|
} FontPair;
|
|
|
|
static GSList *font_pairs = NULL;
|
|
|
|
static void
|
|
font_render_load (void)
|
|
{
|
|
Antialiasing antialiasing;
|
|
Hinting hinting;
|
|
gboolean inconsistent = TRUE;
|
|
GSList *tmp_list;
|
|
|
|
font_render_get_gconf (&antialiasing, &hinting);
|
|
|
|
in_change = TRUE;
|
|
|
|
for (tmp_list = font_pairs; tmp_list; tmp_list = tmp_list->next) {
|
|
FontPair *pair = tmp_list->data;
|
|
|
|
if (antialiasing == pair->antialiasing && hinting == pair->hinting) {
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pair->radio), TRUE);
|
|
inconsistent = FALSE;
|
|
}
|
|
}
|
|
|
|
for (tmp_list = font_pairs; tmp_list; tmp_list = tmp_list->next) {
|
|
FontPair *pair = tmp_list->data;
|
|
|
|
gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (pair->radio), inconsistent);
|
|
}
|
|
|
|
in_change = FALSE;
|
|
}
|
|
|
|
static void
|
|
font_render_changed (GConfClient *client,
|
|
guint cnxn_id,
|
|
GConfEntry *entry,
|
|
gpointer user_data)
|
|
{
|
|
font_render_load ();
|
|
}
|
|
|
|
static void
|
|
font_radio_toggled (GtkToggleButton *toggle_button,
|
|
FontPair *pair)
|
|
{
|
|
if (!in_change) {
|
|
GConfClient *client = gconf_client_get_default ();
|
|
|
|
gconf_client_set_string (client, FONT_ANTIALIASING_KEY,
|
|
gconf_enum_to_string (antialias_enums, pair->antialiasing),
|
|
NULL);
|
|
gconf_client_set_string (client, FONT_HINTING_KEY,
|
|
gconf_enum_to_string (hint_enums, pair->hinting),
|
|
NULL);
|
|
|
|
g_object_unref (client);
|
|
}
|
|
|
|
/* Restore back to the previous state until we get notification
|
|
*/
|
|
font_render_load ();
|
|
}
|
|
|
|
static void
|
|
setup_font_pair (GtkWidget *radio,
|
|
GtkWidget *darea,
|
|
Antialiasing antialiasing,
|
|
Hinting hinting)
|
|
{
|
|
FontPair *pair = g_new (FontPair, 1);
|
|
|
|
pair->antialiasing = antialiasing;
|
|
pair->hinting = hinting;
|
|
pair->radio = radio;
|
|
|
|
setup_font_sample (darea, antialiasing, hinting);
|
|
font_pairs = g_slist_prepend (font_pairs, pair);
|
|
|
|
g_signal_connect (radio, "toggled",
|
|
G_CALLBACK (font_radio_toggled), pair);
|
|
}
|
|
#endif /* HAVE_XFT2 */
|
|
|
|
static void
|
|
metacity_titlebar_load_sensitivity (GConfClient *client,
|
|
GladeXML *dialog)
|
|
{
|
|
gtk_widget_set_sensitive (WID ("window_title_font"),
|
|
!gconf_client_get_bool (client,
|
|
WINDOW_TITLE_USES_SYSTEM_KEY,
|
|
NULL));
|
|
}
|
|
|
|
static void
|
|
metacity_changed (GConfClient *client,
|
|
guint cnxn_id,
|
|
GConfEntry *entry,
|
|
gpointer user_data)
|
|
{
|
|
if (strcmp (entry->key, WINDOW_TITLE_USES_SYSTEM_KEY) == 0)
|
|
metacity_titlebar_load_sensitivity (client, user_data);
|
|
}
|
|
|
|
/* returns 0 if the font is safe, otherwise returns the size in points. */
|
|
static gint
|
|
new_font_dangerous (const char *new_font)
|
|
{
|
|
PangoFontDescription *pfd;
|
|
gboolean retval = 0;
|
|
|
|
pfd = pango_font_description_from_string (new_font);
|
|
if (pfd == NULL)
|
|
/* an invalid font was passed in. This isn't our problem. */
|
|
return 0;
|
|
|
|
if (pango_font_description_get_set_fields (pfd) & PANGO_FONT_MASK_SIZE) {
|
|
if (pango_font_description_get_size (pfd) >= MAX_FONT_SIZE_WITHOUT_WARNING) {
|
|
retval = pango_font_description_get_size (pfd)/1024;
|
|
}
|
|
}
|
|
pango_font_description_free (pfd);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static GConfValue *
|
|
application_font_to_gconf (GConfPropertyEditor *peditor,
|
|
GConfValue *value)
|
|
{
|
|
GConfValue *new_value;
|
|
const char *new_font;
|
|
GtkWidget *font_button;
|
|
gint danger_level;
|
|
|
|
font_button = GTK_WIDGET (gconf_property_editor_get_ui_control (peditor));
|
|
g_return_val_if_fail (font_button != NULL, NULL);
|
|
|
|
new_value = gconf_value_new (GCONF_VALUE_STRING);
|
|
new_font = gconf_value_get_string (value);
|
|
if (new_font_dangerous (old_font)) {
|
|
/* If we're already too large, we don't warn again. */
|
|
gconf_value_set_string (new_value, new_font);
|
|
return new_value;
|
|
}
|
|
|
|
danger_level = new_font_dangerous (new_font);
|
|
if (danger_level) {
|
|
GtkWidget *warning_dialog, *apply_button;
|
|
gchar *warning_label;
|
|
gchar *warning_label2;
|
|
|
|
warning_label = g_strdup (_("Font may be too large"));
|
|
|
|
if (danger_level > MAX_FONT_POINT_WITHOUT_WARNING) {
|
|
warning_label2 = g_strdup_printf (ngettext (
|
|
"The font selected is %d point large, "
|
|
"and may make it difficult to effectively "
|
|
"use the computer. It is recommended that "
|
|
"you select a size smaller than %d.",
|
|
"The font selected is %d points large, "
|
|
"and may make it difficult to effectively "
|
|
"use the computer. It is recommended that "
|
|
"you select a size smaller than %d.",
|
|
danger_level),
|
|
danger_level,
|
|
MAX_FONT_POINT_WITHOUT_WARNING);
|
|
} else {
|
|
warning_label2 = g_strdup_printf (ngettext (
|
|
"The font selected is %d point large, "
|
|
"and may make it difficult to effectively "
|
|
"use the computer. It is recommended that "
|
|
"you select a smaller sized font.",
|
|
"The font selected is %d points large, "
|
|
"and may make it difficult to effectively "
|
|
"use the computer. It is recommended that "
|
|
"you select a smaller sized font.",
|
|
danger_level),
|
|
danger_level);
|
|
}
|
|
|
|
warning_dialog = gtk_message_dialog_new (NULL,
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_NONE,
|
|
warning_label);
|
|
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (warning_dialog),
|
|
warning_label2);
|
|
|
|
gtk_dialog_add_button (GTK_DIALOG (warning_dialog), _("Use previous font"), GTK_RESPONSE_CLOSE);
|
|
|
|
apply_button = gtk_button_new_with_label (_("Use selected font"));
|
|
|
|
gtk_button_set_image (GTK_BUTTON (apply_button), gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON));
|
|
gtk_dialog_add_action_widget (GTK_DIALOG (warning_dialog), apply_button, GTK_RESPONSE_APPLY);
|
|
GTK_WIDGET_SET_FLAGS (apply_button, GTK_CAN_DEFAULT);
|
|
gtk_widget_show (apply_button);
|
|
|
|
gtk_dialog_set_default_response (GTK_DIALOG (warning_dialog), GTK_RESPONSE_CLOSE);
|
|
|
|
g_free (warning_label);
|
|
g_free (warning_label2);
|
|
|
|
if (gtk_dialog_run (GTK_DIALOG (warning_dialog)) == GTK_RESPONSE_APPLY) {
|
|
gconf_value_set_string (new_value, new_font);
|
|
} else {
|
|
gconf_value_set_string (new_value, old_font);
|
|
gtk_font_button_set_font_name (GTK_FONT_BUTTON (font_button), old_font);
|
|
}
|
|
|
|
gtk_widget_destroy (warning_dialog);
|
|
} else {
|
|
gconf_value_set_string (new_value, new_font);
|
|
}
|
|
|
|
return new_value;
|
|
}
|
|
|
|
static void
|
|
application_font_changed (GtkWidget *font_button)
|
|
{
|
|
const gchar *font;
|
|
|
|
font = gtk_font_button_get_font_name (GTK_FONT_BUTTON (font_button));
|
|
g_free (old_font);
|
|
old_font = g_strdup (font);
|
|
}
|
|
|
|
static void
|
|
setup_dialog (void)
|
|
{
|
|
GladeXML *dialog;
|
|
GConfClient *client;
|
|
GtkWidget *widget;
|
|
GObject *peditor;
|
|
|
|
dialog = glade_xml_new (GLADEDIR "/font-properties.glade", "font_dialog", NULL);
|
|
if (!dialog) {
|
|
g_warning ("could not load font-properties.glade");
|
|
return;
|
|
}
|
|
|
|
client = gconf_client_get_default ();
|
|
|
|
gconf_client_add_dir (client, "/desktop/gnome/interface", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
|
gconf_client_add_dir (client, "/apps/nautilus/preferences", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
|
gconf_client_add_dir (client, METACITY_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
|
#ifdef HAVE_XFT2
|
|
gconf_client_add_dir (client, FONT_RENDER_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
|
#endif /* HAVE_XFT2 */
|
|
|
|
peditor = gconf_peditor_new_font (NULL, GTK_FONT_KEY,
|
|
WID ("application_font"),
|
|
"conv-from-widget-cb", application_font_to_gconf,
|
|
NULL);
|
|
g_signal_connect_swapped (peditor, "value-changed",
|
|
G_CALLBACK (application_font_changed), WID ("application_font"));
|
|
application_font_changed (WID ("application_font"));
|
|
|
|
peditor = gconf_peditor_new_font (NULL, DOCUMENT_FONT_KEY,
|
|
WID ("document_font"),
|
|
NULL);
|
|
|
|
peditor = gconf_peditor_new_font (NULL, DESKTOP_FONT_KEY,
|
|
WID ("desktop_font"),
|
|
NULL);
|
|
|
|
peditor = gconf_peditor_new_font (NULL, WINDOW_TITLE_FONT_KEY,
|
|
WID ("window_title_font"),
|
|
NULL);
|
|
|
|
peditor = gconf_peditor_new_font (NULL, MONOSPACE_FONT_KEY,
|
|
WID ("monospace_font"),
|
|
NULL);
|
|
|
|
gconf_client_notify_add (client, METACITY_DIR,
|
|
metacity_changed,
|
|
dialog, NULL, NULL);
|
|
|
|
metacity_titlebar_load_sensitivity (client, dialog);
|
|
|
|
widget = WID ("font_dialog");
|
|
capplet_set_icon (widget, "gnome-settings-font");
|
|
|
|
#ifdef HAVE_XFT2
|
|
setup_font_pair (WID ("monochrome_radio"), WID ("monochrome_sample"), ANTIALIAS_NONE, HINT_FULL);
|
|
setup_font_pair (WID ("best_shapes_radio"), WID ("best_shapes_sample"), ANTIALIAS_GRAYSCALE, HINT_MEDIUM);
|
|
setup_font_pair (WID ("best_contrast_radio"), WID ("best_contrast_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL);
|
|
setup_font_pair (WID ("subpixel_radio"), WID ("subpixel_sample"), ANTIALIAS_RGBA, HINT_FULL);
|
|
|
|
font_render_load ();
|
|
|
|
gconf_client_notify_add (client, FONT_RENDER_DIR,
|
|
font_render_changed,
|
|
NULL, NULL, NULL);
|
|
|
|
g_signal_connect (WID ("details_button"), "clicked",
|
|
G_CALLBACK (cb_show_details), widget);
|
|
#else /* !HAVE_XFT2 */
|
|
gtk_widget_hide (WID ("font_render_frame"));
|
|
#endif /* HAVE_XFT2 */
|
|
|
|
g_signal_connect (widget, "response",
|
|
G_CALLBACK (cb_dialog_response), NULL);
|
|
|
|
gtk_widget_show (widget);
|
|
|
|
g_object_unref (client);
|
|
}
|
|
|
|
#ifdef HAVE_XFT2
|
|
/*
|
|
* EnumGroup - a group of radio buttons tied to a string enumeration
|
|
* value. We add this here because the gconf peditor
|
|
* equivalent of this is both painful to use (you have
|
|
* to supply functions to convert from enums to indices)
|
|
* and conceptually broken (the order of radio buttons
|
|
* in a group when using Glade is not predictable.
|
|
*/
|
|
typedef struct
|
|
{
|
|
GConfClient *client;
|
|
GSList *items;
|
|
const gchar *gconf_key;
|
|
GConfEnumStringPair *enums;
|
|
int default_value;
|
|
} EnumGroup;
|
|
|
|
typedef struct
|
|
{
|
|
EnumGroup *group;
|
|
GtkWidget *widget;
|
|
int value;
|
|
} EnumItem;
|
|
|
|
static void
|
|
enum_group_load (EnumGroup *group)
|
|
{
|
|
char *str = gconf_client_get_string (group->client, group->gconf_key, NULL);
|
|
int val = group->default_value;
|
|
GSList *tmp_list;
|
|
|
|
if (str)
|
|
gconf_string_to_enum (group->enums, str, &val);
|
|
|
|
g_free (str);
|
|
|
|
in_change = TRUE;
|
|
|
|
for (tmp_list = group->items; tmp_list; tmp_list = tmp_list->next) {
|
|
EnumItem *item = tmp_list->data;
|
|
|
|
if (val == item->value)
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->widget), TRUE);
|
|
}
|
|
|
|
|
|
|
|
in_change = FALSE;
|
|
}
|
|
|
|
static void
|
|
enum_group_changed (GConfClient *client,
|
|
guint cnxn_id,
|
|
GConfEntry *entry,
|
|
gpointer user_data)
|
|
{
|
|
enum_group_load (user_data);
|
|
}
|
|
|
|
static void
|
|
enum_item_toggled (GtkToggleButton *toggle_button,
|
|
EnumItem *item)
|
|
{
|
|
EnumGroup *group = item->group;
|
|
|
|
if (!in_change) {
|
|
gconf_client_set_string (group->client, group->gconf_key,
|
|
gconf_enum_to_string (group->enums, item->value),
|
|
NULL);
|
|
}
|
|
|
|
/* Restore back to the previous state until we get notification
|
|
*/
|
|
enum_group_load (group);
|
|
}
|
|
|
|
static EnumGroup *
|
|
enum_group_create (const gchar *gconf_key,
|
|
GConfEnumStringPair *enums,
|
|
int default_value,
|
|
GtkWidget *first_widget,
|
|
...)
|
|
{
|
|
EnumGroup *group;
|
|
GtkWidget *widget;
|
|
va_list args;
|
|
|
|
group = g_new (EnumGroup, 1);
|
|
|
|
group->client = gconf_client_get_default ();
|
|
group->gconf_key = g_strdup (gconf_key);
|
|
group->enums = enums;
|
|
group->default_value = default_value;
|
|
group->items = NULL;
|
|
|
|
va_start (args, first_widget);
|
|
|
|
widget = first_widget;
|
|
while (widget)
|
|
{
|
|
EnumItem *item;
|
|
|
|
item = g_new (EnumItem, 1);
|
|
item->group = group;
|
|
item->widget = widget;
|
|
item->value = va_arg (args, int);
|
|
|
|
g_signal_connect (item->widget, "toggled",
|
|
G_CALLBACK (enum_item_toggled), item);
|
|
|
|
group->items = g_slist_prepend (group->items, item);
|
|
|
|
widget = va_arg (args, GtkWidget *);
|
|
}
|
|
|
|
va_end (args);
|
|
|
|
enum_group_load (group);
|
|
|
|
gconf_client_notify_add (group->client, gconf_key,
|
|
enum_group_changed,
|
|
group, NULL, NULL);
|
|
|
|
return group;
|
|
}
|
|
|
|
static double
|
|
dpi_from_pixels_and_mm (int pixels, int mm)
|
|
{
|
|
double dpi;
|
|
|
|
if (mm >= 1)
|
|
dpi = pixels / (mm / 25.4);
|
|
else
|
|
dpi = 0;
|
|
|
|
return dpi;
|
|
}
|
|
|
|
static double
|
|
get_dpi_from_x_server (void)
|
|
{
|
|
GdkScreen *screen;
|
|
double dpi;
|
|
|
|
screen = gdk_screen_get_default ();
|
|
if (screen)
|
|
{
|
|
double width_dpi, height_dpi;
|
|
|
|
width_dpi = dpi_from_pixels_and_mm (gdk_screen_get_width (screen), gdk_screen_get_width_mm (screen));
|
|
height_dpi = dpi_from_pixels_and_mm (gdk_screen_get_height (screen), gdk_screen_get_height_mm (screen));
|
|
|
|
if (width_dpi < DPI_LOW_REASONABLE_VALUE || width_dpi > DPI_HIGH_REASONABLE_VALUE
|
|
|| height_dpi < DPI_LOW_REASONABLE_VALUE || height_dpi > DPI_HIGH_REASONABLE_VALUE)
|
|
dpi = DPI_FALLBACK;
|
|
else
|
|
dpi = (width_dpi + height_dpi) / 2.0;
|
|
}
|
|
else
|
|
{
|
|
/* Huh!? No screen? */
|
|
|
|
dpi = DPI_FALLBACK;
|
|
}
|
|
|
|
return dpi;
|
|
}
|
|
|
|
/*
|
|
* The font rendering details dialog
|
|
*/
|
|
static void
|
|
dpi_load (GConfClient *client,
|
|
GtkSpinButton *spinner)
|
|
{
|
|
GConfValue *value;
|
|
gdouble dpi;
|
|
|
|
value = gconf_client_get_without_default (client, FONT_DPI_KEY, NULL);
|
|
|
|
if (value) {
|
|
dpi = gconf_value_get_float (value);
|
|
gconf_value_free (value);
|
|
} else
|
|
dpi = get_dpi_from_x_server ();
|
|
|
|
if (dpi < 50.)
|
|
dpi = 50.;
|
|
|
|
in_change = TRUE;
|
|
gtk_spin_button_set_value (spinner, dpi);
|
|
in_change = FALSE;
|
|
}
|
|
|
|
static void
|
|
dpi_changed (GConfClient *client,
|
|
guint cnxn_id,
|
|
GConfEntry *entry,
|
|
gpointer user_data)
|
|
{
|
|
dpi_load (client, user_data);
|
|
}
|
|
|
|
static void
|
|
dpi_value_changed (GtkSpinButton *spinner,
|
|
GConfClient *client)
|
|
{
|
|
/* Like any time when using a spin button with GConf, there is
|
|
* a race condition here. When we change, we send the new
|
|
* value to GCOnf, then restore to the old value until
|
|
* we get a response to emulate the proper model/view behavior.
|
|
*
|
|
* If the user changes the value faster than responses are
|
|
* received from GConf, this may cause mild strange effects.
|
|
*/
|
|
gdouble new_dpi = gtk_spin_button_get_value (spinner);
|
|
|
|
gconf_client_set_float (client, FONT_DPI_KEY, new_dpi, NULL);
|
|
|
|
dpi_load (client, spinner);
|
|
}
|
|
|
|
static void
|
|
cb_details_response (GtkDialog *dialog, gint response_id)
|
|
{
|
|
if (response_id == GTK_RESPONSE_HELP)
|
|
capplet_help (GTK_WINDOW (dialog),
|
|
"user-guide.xml",
|
|
"goscustdesk-38");
|
|
else if (response_id == 1) {
|
|
/* "Go to font folder" was clicked */
|
|
g_spawn_command_line_async ("nautilus --no-desktop fonts:///", NULL);
|
|
} else
|
|
gtk_widget_hide (GTK_WIDGET (dialog));
|
|
}
|
|
|
|
static void
|
|
cb_show_details (GtkWidget *button,
|
|
GtkWindow *parent)
|
|
{
|
|
static GtkWidget *details_dialog = NULL;
|
|
|
|
if (!details_dialog) {
|
|
GConfClient *client = gconf_client_get_default ();
|
|
GladeXML *dialog;
|
|
GtkWidget *dpi_spinner;
|
|
GnomeVFSURI *uri;
|
|
int dpi;
|
|
GtkAdjustment *adjustment;
|
|
|
|
dialog = glade_xml_new (GLADEDIR "/font-properties.glade", "render_details", NULL);
|
|
if (!dialog) {
|
|
g_warning ("could not load font-properties.glade");
|
|
return;
|
|
}
|
|
|
|
details_dialog = WID ("render_details");
|
|
uri = gnome_vfs_uri_new ("fonts:///");
|
|
if (uri == NULL) {
|
|
gtk_widget_hide (WID ("go_to_font_button"));
|
|
} else {
|
|
gnome_vfs_uri_unref (uri);
|
|
gtk_widget_show (WID ("go_to_font_button"));
|
|
}
|
|
|
|
gtk_window_set_transient_for (GTK_WINDOW (details_dialog), parent);
|
|
|
|
dpi_spinner = WID ("dpi_spinner");
|
|
|
|
/* pick a sensible maximum dpi */
|
|
dpi = floor ((gdk_screen_width () / gdk_screen_width_mm () +
|
|
gdk_screen_height () / gdk_screen_height_mm ()) * 25.4 / 2. + .5);
|
|
if (dpi < 50)
|
|
dpi = 50; /* be extra careful */
|
|
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dpi_spinner));
|
|
adjustment->upper = dpi * 3;
|
|
|
|
dpi_load (client, GTK_SPIN_BUTTON (dpi_spinner));
|
|
g_signal_connect (dpi_spinner, "value_changed",
|
|
G_CALLBACK (dpi_value_changed), client);
|
|
|
|
gconf_client_notify_add (client, FONT_DPI_KEY,
|
|
dpi_changed,
|
|
dpi_spinner, NULL, NULL);
|
|
|
|
setup_font_sample (WID ("antialias_none_sample"), ANTIALIAS_NONE, HINT_FULL);
|
|
setup_font_sample (WID ("antialias_grayscale_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL);
|
|
setup_font_sample (WID ("antialias_subpixel_sample"), ANTIALIAS_RGBA, HINT_FULL);
|
|
|
|
enum_group_create (FONT_ANTIALIASING_KEY, antialias_enums, ANTIALIAS_GRAYSCALE,
|
|
WID ("antialias_none_radio"), ANTIALIAS_NONE,
|
|
WID ("antialias_grayscale_radio"), ANTIALIAS_GRAYSCALE,
|
|
WID ("antialias_subpixel_radio"), ANTIALIAS_RGBA,
|
|
NULL);
|
|
|
|
setup_font_sample (WID ("hint_none_sample"), ANTIALIAS_GRAYSCALE, HINT_NONE);
|
|
setup_font_sample (WID ("hint_slight_sample"), ANTIALIAS_GRAYSCALE, HINT_SLIGHT);
|
|
setup_font_sample (WID ("hint_medium_sample"), ANTIALIAS_GRAYSCALE, HINT_MEDIUM);
|
|
setup_font_sample (WID ("hint_full_sample"), ANTIALIAS_GRAYSCALE, HINT_FULL);
|
|
|
|
enum_group_create (FONT_HINTING_KEY, hint_enums, HINT_FULL,
|
|
WID ("hint_none_radio"), HINT_NONE,
|
|
WID ("hint_slight_radio"), HINT_SLIGHT,
|
|
WID ("hint_medium_radio"), HINT_MEDIUM,
|
|
WID ("hint_full_radio"), HINT_FULL,
|
|
NULL);
|
|
|
|
gtk_image_set_from_file (GTK_IMAGE (WID ("subpixel_rgb_image")),
|
|
PIXMAPDIR "/subpixel-rgb.png");
|
|
gtk_image_set_from_file (GTK_IMAGE (WID ("subpixel_bgr_image")),
|
|
PIXMAPDIR "/subpixel-bgr.png");
|
|
gtk_image_set_from_file (GTK_IMAGE (WID ("subpixel_vrgb_image")),
|
|
PIXMAPDIR "/subpixel-vrgb.png");
|
|
gtk_image_set_from_file (GTK_IMAGE (WID ("subpixel_vbgr_image")),
|
|
PIXMAPDIR "/subpixel-vbgr.png");
|
|
|
|
enum_group_create (FONT_RGBA_ORDER_KEY, rgba_order_enums, RGBA_RGB,
|
|
WID ("subpixel_rgb_radio"), RGBA_RGB,
|
|
WID ("subpixel_bgr_radio"), RGBA_BGR,
|
|
WID ("subpixel_vrgb_radio"), RGBA_VRGB,
|
|
WID ("subpixel_vbgr_radio"), RGBA_VBGR,
|
|
NULL);
|
|
|
|
g_signal_connect (G_OBJECT (details_dialog),
|
|
"response",
|
|
G_CALLBACK (cb_details_response), NULL);
|
|
g_signal_connect (G_OBJECT (details_dialog),
|
|
"delete_event",
|
|
G_CALLBACK (gtk_true), NULL);
|
|
|
|
g_object_unref (dialog);
|
|
g_object_unref (client);
|
|
}
|
|
|
|
gtk_window_present (GTK_WINDOW (details_dialog));
|
|
}
|
|
#endif /* HAVE_XFT2 */
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
|
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
textdomain (GETTEXT_PACKAGE);
|
|
|
|
gnome_program_init ("gnome-font-properties", VERSION,
|
|
LIBGNOMEUI_MODULE, argc, argv,
|
|
GNOME_PARAM_APP_DATADIR, GNOMECC_DATA_DIR,
|
|
NULL);
|
|
|
|
activate_settings_daemon ();
|
|
|
|
setup_dialog ();
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|