/*
roxterm - GTK+ 2.0 terminal emulator with tabs
Copyright (C) 2004 Tony Houghton <h@realh.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "defns.h"
#include <string.h>
#include <glade/glade-xml.h>
#include "capplet.h"
#include "colourgui.h"
#include "colourscheme.h"
#include "configlet.h"
#include "dlg.h"
#include "dynopts.h"
#define CG_CONNECT(cg, h) \
glade_xml_signal_connect_data ((cg)->glade, #h, (GCallback) (h), (cg))
#define DEFAULT_PALETTE_SIZE 16
struct ColourGUI {
GladeXML *glade;
GtkWidget *widget;
char *scheme_name;
Options *colour_scheme;
Options *orig_scheme;
gboolean ignore_destroy;
};
static GHashTable *colourgui_being_edited = NULL;
static void colourgui_set_colour_widget(GladeXML *glade,
const char *widget_name, GdkColor *colour, gboolean ignore)
{
static GdkColormap *colour_map = NULL;
GtkWidget *widget;
if (!colour)
return;
if (!colour_map)
colour_map = gdk_colormap_get_system();
g_return_if_fail(gdk_colormap_alloc_color(colour_map, colour,
TRUE, TRUE));
widget = glade_xml_get_widget(glade, widget_name);
/* gtk_color_button_set_color doesn't seem to cause a change signal so
* explicitly raise one if necessary */
capplet_ignore_changes = TRUE;
gtk_color_button_set_color(
GTK_COLOR_BUTTON(widget),
colour);
capplet_ignore_changes = FALSE;
if (!ignore)
g_signal_emit_by_name(widget, "color-set");
}
static void colourgui_set_fgbg_colour_widgets(GladeXML *glade,
Options *colour_scheme, gboolean ignore)
{
colourgui_set_colour_widget(glade, "foreground_colour",
colour_scheme_get_foreground_colour(colour_scheme, FALSE), ignore);
colourgui_set_colour_widget(glade, "background_colour",
colour_scheme_get_background_colour(colour_scheme, FALSE), ignore);
}
static void colourgui_set_cursor_colour_widget(GladeXML *glade,
Options *colour_scheme, gboolean ignore)
{
colourgui_set_colour_widget(glade, "cursor_colour",
colour_scheme_get_cursor_colour(colour_scheme, FALSE), ignore);
}
static void colourgui_set_all_palette_widgets(GladeXML *glade,
Options *colour_scheme, gboolean ignore)
{
int n;
GdkColor *palette = colour_scheme_get_palette(colour_scheme);
g_return_if_fail(palette);
for (n = 0; n < 24; ++n)
{
char widget_name[20];
sprintf(widget_name, "palette%d_%d", n / 8, n % 8);
colourgui_set_colour_widget(glade, widget_name, &palette[n], ignore);
}
}
static void colourgui_shade_row(GladeXML *glade, int row, gboolean sensitive)
{
char widget_name[20];
int column;
for (column = 0; column < 8; ++column)
{
sprintf(widget_name, "palette%d_%d", row, column);
gtk_widget_set_sensitive(glade_xml_get_widget(glade,
widget_name), sensitive);
}
}
static void colourgui_set_palette_shading(GladeXML *glade)
{
int size_radio = -1;
gboolean use_custom = gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade,
"use_custom_colours")));
int row;
char widget_name[20];
if (use_custom)
{
size_radio = capplet_which_radio_is_selected(
glade_xml_get_widget(glade, "palette_size0"));
}
colourgui_shade_row(glade, 2, size_radio == 2);
colourgui_shade_row(glade, 1, size_radio >= 1);
colourgui_shade_row(glade, 0, size_radio >= 0);
for (row = 0; row < 3; ++row)
{
sprintf(widget_name, "palette_size%d", row);
gtk_widget_set_sensitive(glade_xml_get_widget(glade,
widget_name), use_custom);
}
}
static void colourgui_set_fgbg_shading(GladeXML *glade)
{
gboolean sensitive = gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, "set_fgbg")))
&& !gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade,
"fgbg_track_palette")));
gtk_widget_set_sensitive(glade_xml_get_widget(glade,
"foreground_colour"), sensitive);
gtk_widget_set_sensitive(glade_xml_get_widget(glade,
"background_colour"), sensitive);
}
static void colourgui_set_fgbg_track_shading(GladeXML *glade)
{
gtk_widget_set_sensitive(glade_xml_get_widget(glade, "fgbg_track_palette"),
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
glade_xml_get_widget(glade, "set_fgbg")))
&& gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
glade_xml_get_widget(glade, "use_custom_colours"))));
}
static void colourgui_set_cursor_shading(GladeXML *glade)
{
gtk_widget_set_sensitive(glade_xml_get_widget(glade, "cursor_colour"),
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
glade_xml_get_widget(glade, "set_cursor_colour"))));
}
static void colourgui_set_fgbg_toggle(GladeXML *glade, Options *colour_scheme,
gboolean ignore)
{
char *fg = options_lookup_string(colour_scheme, "foreground");
char *bg = options_lookup_string(colour_scheme, "background");
capplet_ignore_changes = ignore;
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, "set_fgbg")),
fg || bg);
capplet_ignore_changes = FALSE;
if (bg)
g_free(bg);
if (fg)
g_free(fg);
}
static void colourgui_set_fgbg_track_toggle(GladeXML *glade,
Options *colour_scheme, gboolean ignore)
{
gboolean track = FALSE;
if (gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade,
"set_fgbg")))
&& colour_scheme_get_palette_size(colour_scheme))
{
GdkColor *palette = colour_scheme_get_palette(colour_scheme);
GdkColor *fg = colour_scheme_get_foreground_colour(colour_scheme, TRUE);
GdkColor *bg = colour_scheme_get_background_colour(colour_scheme, TRUE);
if (fg && bg
&& gdk_color_equal(&palette[0], fg)
&& gdk_color_equal(&palette[7], bg))
{
track = TRUE;
}
}
capplet_ignore_changes = ignore;
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, "fgbg_track_palette")),
track);
capplet_ignore_changes = FALSE;
}
static void colourgui_set_palette_toggles(GladeXML *glade,
Options *colour_scheme, gboolean ignore)
{
int ps = colour_scheme_get_palette_size(colour_scheme);
GtkToggleButton *ucp =
GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, "use_custom_colours"));
capplet_ignore_changes = ignore;
switch (ps)
{
case 0:
gtk_toggle_button_set_active(ucp, FALSE);
/* Leave radios in whatever state they were in before */
break;
case 8:
case 16:
case 24:
/* Treat as 0 */
gtk_toggle_button_set_active(ucp, TRUE);
capplet_set_radio_by_index(glade, "palette_size", ps / 8 - 1);
/* This will clear ignore_changes, but doesn't matter */
break;
default:
g_critical(_("Illegal palette size %d"),
colour_scheme_get_palette_size(colour_scheme));
gtk_toggle_button_set_active(ucp, FALSE);
break;
}
capplet_ignore_changes = FALSE;
}
static void colourgui_set_cursor_toggle(GladeXML *glade,
Options *colour_scheme, gboolean ignore)
{
char *cc = options_lookup_string(colour_scheme, "cursor");
capplet_ignore_changes = ignore;
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, "set_cursor_colour")),
cc != NULL);
capplet_ignore_changes = FALSE;
g_free(cc);
}
static void colourgui_set_all_toggles(GladeXML *glade, Options *colour_scheme,
gboolean ignore)
{
colourgui_set_fgbg_toggle(glade, colour_scheme, ignore);
colourgui_set_fgbg_track_toggle(glade, colour_scheme, ignore);
colourgui_set_palette_toggles(glade, colour_scheme, ignore);
colourgui_set_cursor_toggle(glade, colour_scheme, ignore);
}
static void colourgui_set_all_shading(GladeXML *glade)
{
colourgui_set_palette_shading(glade);
colourgui_set_fgbg_shading(glade);
colourgui_set_fgbg_track_shading(glade);
colourgui_set_cursor_shading(glade);
}
static void colourgui_fill_in_dialog(ColourGUI * cg, gboolean ignore)
{
GladeXML *glade = cg->glade;
Options *colour_scheme = cg->colour_scheme;
colourgui_set_all_toggles(glade, colour_scheme, ignore);
colourgui_set_all_shading(glade);
colourgui_set_all_palette_widgets(glade, colour_scheme, ignore);
colourgui_set_fgbg_colour_widgets(glade, colour_scheme, ignore);
colourgui_set_cursor_colour_widget(glade, colour_scheme, ignore);
}
static void colourgui_revert(ColourGUI *cg)
{
options_copy_keyfile(cg->colour_scheme, cg->orig_scheme);
colour_scheme_reset_cached_data(cg->colour_scheme);
colourgui_fill_in_dialog(cg, FALSE);
}
/***********************************************************/
/* Handlers */
static void on_Colour_Editor_destroy(GtkWidget *widget, ColourGUI * cg)
{
if (cg->ignore_destroy)
{
return;
}
else
{
cg->ignore_destroy = TRUE;
colourgui_delete(cg);
}
}
static void on_Colour_Editor_response(GtkWidget *widget, gint response,
ColourGUI * cg)
{
if (response == 1)
colourgui_revert(cg);
else
colourgui_delete(cg);
}
static void on_Colour_Editor_close(GtkWidget *widget, ColourGUI * cg)
{
colourgui_delete(cg);
}
static const char *colourgui_name_colour(const GdkColor *colour)
{
static char colour_name[16];
sprintf(colour_name, "#%04hx%04hx%04hx",
colour->red, colour->green, colour->blue);
return colour_name;
}
static const char *colourgui_read_colour_widget(ColourGUI *cg,
const char *widget_name)
{
GtkColorButton *button = GTK_COLOR_BUTTON(glade_xml_get_widget(cg->glade,
widget_name));
GdkColor colour;
g_return_val_if_fail(button, NULL);
gtk_color_button_get_color(button, &colour);
return colourgui_name_colour(&colour);
}
static void colourgui_set_colour_option(Options *colour_scheme,
const char *opt_name, const char *colour_name,
void (*set_fn)(Options *, const char *))
{
set_fn(colour_scheme, colour_name);
capplet_set_string(colour_scheme, opt_name, colour_name);
}
#define COLOURGUI_SET_COLOUR_OPTION(cs, opt_name, colour_name) \
colourgui_set_colour_option(cs, \
#opt_name, colour_name, colour_scheme_set_ ## opt_name ## _colour)
static void colourgui_set_colour_option_from_widget(ColourGUI *cg,
const char *widget_name, const char *opt_name,
void (*set_fn)(Options *, const char *))
{
colourgui_set_colour_option(cg->colour_scheme, opt_name,
colourgui_read_colour_widget(cg, widget_name), set_fn);
}
#define COLOURGUI_SET_COLOUR_OPTION_FROM_WIDGET(cg, name) \
colourgui_set_colour_option_from_widget(cg, #name "_colour", \
#name, colour_scheme_set_ ## name ## _colour)
static void on_color_set(GtkColorButton *button, ColourGUI * cg)
{
const char *widget_name;
GdkColor colour;
const char *colour_name;
const char *option_name = NULL;
char dyn_opt_nm[8];
Options *opts = cg->colour_scheme;
if (capplet_ignore_changes)
{
return;
}
widget_name = glade_get_widget_name(GTK_WIDGET(button));
g_return_if_fail(widget_name);
gtk_color_button_get_color(button, &colour);
colour_name = colourgui_name_colour(&colour);
if (!strcmp(widget_name, "foreground_colour"))
{
option_name = "foreground";
colour_scheme_set_foreground_colour(opts, colour_name);
}
else if (!strcmp(widget_name, "background_colour"))
{
option_name = "background";
colour_scheme_set_background_colour(opts, colour_name);
}
else if (!strcmp(widget_name, "cursor_colour"))
{
option_name = "cursor";
colour_scheme_set_cursor_colour(opts, colour_name);
}
else if (!strncmp(widget_name, "palette", 7) && strlen(widget_name) == 10)
{
int row = widget_name[7] - '0';
int column = widget_name[9] - '0';
colour_scheme_set_palette_entry(opts, row * 8 + column, colour_name);
sprintf(dyn_opt_nm, "%d", row * 8 + column);
option_name = dyn_opt_nm;
if (row == 0 && gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(cg->glade,
"fgbg_track_palette"))))
{
if (column == 7)
{
colourgui_set_colour_widget(cg->glade,
"background_colour", &colour, TRUE);
capplet_set_string(cg->colour_scheme,
"background", colour_name);
}
if (column == 0)
{
colourgui_set_colour_widget(cg->glade,
"foreground_colour", &colour, TRUE);
capplet_set_string(cg->colour_scheme,
"foreground", colour_name);
}
}
}
if (option_name)
{
capplet_set_string(cg->colour_scheme, option_name, colour_name);
}
else
{
g_critical(_("Unable to get colour option name from widget name '%s'"),
widget_name);
}
}
static void on_set_fgbg_toggled(GtkToggleButton *button, ColourGUI * cg)
{
gboolean state;
if (capplet_ignore_changes)
return;
colourgui_set_fgbg_shading(cg->glade);
colourgui_set_fgbg_track_shading(cg->glade);
colourgui_set_fgbg_track_toggle(cg->glade, cg->colour_scheme, FALSE);
state = gtk_toggle_button_get_active(button);
if (state)
{
COLOURGUI_SET_COLOUR_OPTION_FROM_WIDGET(cg, foreground);
COLOURGUI_SET_COLOUR_OPTION_FROM_WIDGET(cg, background);
}
else
{
COLOURGUI_SET_COLOUR_OPTION(cg->colour_scheme, foreground, NULL);
COLOURGUI_SET_COLOUR_OPTION(cg->colour_scheme, background, NULL);
}
}
static void mirror_colour(ColourGUI *cg, const char *source_widget_name,
const char *target_widget, const char *target_option)
{
GdkColor colour;
const char *colour_name;
gtk_color_button_get_color(
GTK_COLOR_BUTTON(glade_xml_get_widget(cg->glade, source_widget_name)),
&colour);
colour_name = colourgui_name_colour(&colour);
colourgui_set_colour_widget(cg->glade, target_widget, &colour, TRUE);
capplet_set_string(cg->colour_scheme, target_option, colour_name);
}
static void on_fgbg_track_palette_toggled(GtkToggleButton *button,
ColourGUI * cg)
{
gboolean state;
if (capplet_ignore_changes)
return;
colourgui_set_fgbg_shading(cg->glade);
state = gtk_toggle_button_get_active(button);
if (state)
{
mirror_colour(cg, "palette0_7", "background_colour", "background");
mirror_colour(cg, "palette0_0", "foreground_colour", "foreground");
}
}
static void on_use_custom_colours_toggled(GtkToggleButton *button,
ColourGUI *cg)
{
gboolean state;
int old_size;
int palette_size;
if (capplet_ignore_changes)
{
return;
}
state = gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(cg->glade,
"use_custom_colours")));
old_size = colour_scheme_get_palette_size(cg->colour_scheme);
if (state)
{
palette_size = capplet_which_radio_is_selected(
glade_xml_get_widget(cg->glade, "palette_size0")) * 8 + 8;
}
else
{
palette_size = 0;
}
if (palette_size != old_size)
{
int n;
colour_scheme_set_palette_size(cg->colour_scheme, palette_size);
if (!palette_size)
{
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(glade_xml_get_widget(cg->glade,
"fgbg_track_palette")), FALSE);
}
colourgui_set_fgbg_track_shading(cg->glade);
colourgui_set_palette_shading(cg->glade);
for (n = old_size; n < palette_size; ++n)
{
char key[8];
char *val;
sprintf(key, "%d", n);
val = options_lookup_string(cg->colour_scheme, key);
if (!val)
{
char widget_name[20];
const char *colour_name;
sprintf(widget_name, "palette%d_%d", n / 8, n % 8);
colour_name = colourgui_read_colour_widget(cg, widget_name);
colour_scheme_set_palette_entry(cg->colour_scheme, n,
colour_name);
capplet_set_string(cg->colour_scheme, key, colour_name);
}
else
{
g_free(val);
}
}
capplet_set_int(cg->colour_scheme, "palette_size", palette_size);
}
}
static void on_set_cursor_colour_toggled(GtkToggleButton *button, ColourGUI *cg)
{
int state;
if (capplet_ignore_changes)
return;
colourgui_set_cursor_shading(cg->glade);
state = gtk_toggle_button_get_active(button);
if (state)
COLOURGUI_SET_COLOUR_OPTION_FROM_WIDGET(cg, cursor);
else
COLOURGUI_SET_COLOUR_OPTION(cg->colour_scheme, cursor, NULL);
}
static void on_palette_size_toggled(GtkToggleButton *button,
ColourGUI *cg)
{
/* We can ignore a radio button being switched off because we'll get
* another signal for the one being switched on */
if (gtk_toggle_button_get_active(button))
on_use_custom_colours_toggled(button, cg);
}
/***********************************************************/
static void colourgui_connect_handlers(ColourGUI * cg)
{
/* Misc */
CG_CONNECT(cg, on_Colour_Editor_destroy);
CG_CONNECT(cg, on_Colour_Editor_close);
CG_CONNECT(cg, on_Colour_Editor_response);
/* Generic colour selection handler*/
CG_CONNECT(cg, on_color_set);
/* Widgets */
CG_CONNECT(cg, on_set_fgbg_toggled);
CG_CONNECT(cg, on_palette_size_toggled);
CG_CONNECT(cg, on_fgbg_track_palette_toggled);
CG_CONNECT(cg, on_set_cursor_colour_toggled);
CG_CONNECT(cg, on_use_custom_colours_toggled);
}
ColourGUI *colourgui_open(const char *scheme_name, GdkScreen *scrn)
{
ColourGUI *cg;
char *title;
if (!colourgui_being_edited)
{
colourgui_being_edited = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
}
cg = g_hash_table_lookup(colourgui_being_edited, scheme_name);
if (cg)
{
if (scrn)
gtk_window_set_screen(GTK_WINDOW(cg->widget), scrn);
gtk_window_present(GTK_WINDOW(cg->widget));
return cg;
}
cg = g_new0(ColourGUI, 1);
cg->scheme_name = g_strdup(scheme_name);
cg->colour_scheme = colour_scheme_lookup_and_ref(scheme_name);
cg->orig_scheme = options_copy(cg->colour_scheme);
cg->glade = glade_xml_new(capplet_get_glade_filename(),
"Colour_Scheme_Editor", NULL);
g_assert(cg->glade);
cg->widget = glade_xml_get_widget(cg->glade, "Colour_Scheme_Editor");
if (scrn)
gtk_window_set_screen(GTK_WINDOW(cg->widget), scrn);
title = g_strdup_printf(_("ROXTerm Colour Scheme \"%s\""), scheme_name);
gtk_window_set_title(GTK_WINDOW(cg->widget), title);
g_free(title);
if (!cg->orig_scheme)
{
dlg_warning(GTK_WINDOW(cg->widget),
_("Unable to back up colour scheme to revert"));
gtk_widget_set_sensitive(glade_xml_get_widget(cg->glade, "revert"),
FALSE);
}
g_hash_table_insert(colourgui_being_edited, g_strdup(scheme_name), cg);
colourgui_fill_in_dialog(cg, TRUE);
colourgui_connect_handlers(cg);
gtk_widget_show(cg->widget);
capplet_inc_windows();
configlet_lock_colour_schemes();
return cg;
}
void colourgui_delete(ColourGUI * cg)
{
if (!g_hash_table_remove(colourgui_being_edited, cg->scheme_name))
{
g_critical(_("Deleting edited colour scheme that wasn't in list of "
"schemes being edited"));
}
if (!cg->ignore_destroy)
{
cg->ignore_destroy = TRUE;
gtk_widget_destroy(cg->widget);
}
UNREF_LOG(g_object_unref(cg->glade));
g_free(cg->scheme_name);
if (cg->orig_scheme)
options_delete(cg->orig_scheme);
UNREF_LOG(colour_scheme_unref(cg->colour_scheme));
g_free(cg);
capplet_dec_windows();
configlet_unlock_colour_schemes();
}
/* vi:set sw=4 ts=4 noet cindent cino= */