/* stylish.c
 * 
 * Written by David Allen
 * s2mdalle@titan.vcu.edu http://opop.nols.com/
 *
 * Styles/Themes/Colors/Fonts relating to GTKeyboard.  Manipulation,
 * creation, destruction, and all that other good stuff.
 */
/* GTKeyboard - A Graphical Keyboard For X
 * Copyright (C) 1999, 2000 David Allen  
 *
 * 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 Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 */

/* Typedefs and external prototypes are included through master.h */
#define STYLISH_C

#ifndef MASTER_H
#  include "master.h"
#endif /* MASTER_H */

#ifndef STYLISH_H
#  include "include/stylish.h"
#endif /* STYLISH_H */

/* Static prototypes for this file */
static void GetDialogColor           (GdkColor *color);
static void color_ok_button          (GtkWidget *w, color_info *colorInfo);
static void CloseDialog              (GtkWidget *w, color_info *di);
static GtkStyle *CreateBGStyle       (GdkColor background);
static GtkStyle *new_style           (GdkColor c);
static void grabfont                 (GtkWidget *emitter, GtkWidget *fontsel);
static void keyboardgrabfont         (GtkWidget *emitter, GtkWidget *fontsel);

void parse_and_apply_color(char *colorname)
{
     GdkColor resulting_color;
     GTKeyboardStyle stylish;
     register int x = 0;

     if(!colorname)
     {
          fprintf(stderr,
                  "GTKeyboard internal error (parse_and_apply_color): ");
          fprintf(stderr,"NULL colorname!\n");
          fflush(stderr);
          return;
     } /* End if */

     for(x=0; x<4; x++)
          GUI.saved_colors[x] = -1;      /* Illegal value for checking later */

     /* Grab the gdoubles from the string passed to us */
     sscanf(colorname,"%lf %lf %lf %lf",
            &GUI.saved_colors[0],
            &GUI.saved_colors[1],
            &GUI.saved_colors[2],
            &GUI.saved_colors[3]);

     /* Do some error checking and reporting on what we just read in
      */
     for(x=0; x<4; x++)
     {
          if(GUI.saved_colors[x] == -1)
          {
               fprintf(stderr,"parse_and_apply_color() error:  ");
               fprintf(stderr,"Illegal color %d value:  %f.  Resetting.\n",
                       x, (double)GUI.saved_colors[x]);
               
               GUI.saved_colors[x] = 0.8888;
          } /* End if */
     } /* End for */

     /* Place the gdoubles into a GdkColor structure */
     resulting_color.red   = GUI.saved_colors[0] * 0xffff;
     resulting_color.green = GUI.saved_colors[1] * 0xffff;
     resulting_color.blue  = GUI.saved_colors[2] * 0xffff;

     /* Create our new style with our color */
     GUI.style = new_style(resulting_color);
     
     /* Set up the GTKeyboardStyle structure specifying
      * GTKeyboardFontMask so we don't clobber whatever Font is there
      */
     stylish.style = GUI.style;
     stylish.mask  = GTKeyboardFontMask;

     /* Set the style into the application-wide window */
     SetStyleRecursively(window, (gpointer)&stylish);
} /* End parse_and_apply_color() */

/* switch_cursor
 * Sets the window cursor to something that the user chooses
 * check out include/ui.h for a list of possible cursors in the
 * options->looks->cursors menu.  GDK provides enums for different 
 * cursors, which end up as the gint CURSOR param
 */
void switch_cursor(GtkWidget *emitter, gint CURSOR)
{
     GUI.cursor = gdk_cursor_new(CURSOR);
     if(!window->window)
	  gtkeyboard_error(2,"Internal GTKeyboard error: GDK window == NULL\n",
			   "Cannot change cursors.\n");
     else
	  gdk_window_set_cursor(window->window, GUI.cursor);
} /* End switch_cursor */

/*
 * CreateBGStyle 
 *
 * Creates a style using the colors passed in.  
 * Set the foreground color, the text color and the
 * background color.
 * Makes all states the same color
 */
static GtkStyle *CreateBGStyle(GdkColor background)
{
     GtkStyle *defstyle;
     GtkStyle *style;
     register int i;
     
     defstyle = gtk_widget_get_default_style ();
     style    = gtk_style_copy(defstyle);

     /* Set the style in different places */
     for (i=0; i<5; i++) 
	  style->bg[i] = background;

     return(style);
} /* End CreateBGStyle */

/* Sets all of the windows and subwidgets back to the default style
 * which is stored in GUI.deflt.  Should be a macro...damn itemfactories.
 */
void reset_application_style(GtkWidget *emitter, gpointer data)
{
     GTKeyboardStyle stylin = { NULL, 0 };
     char buf[1024];

     CONDFREE(GUI.kfontname);
     CONDFREE(GUI.fontname);
     CONDFREE(GUI.colorname);

     GUI.kfont = (GdkFont *)NULL;
     GUI.font  = (GdkFont *)NULL;

     GUI.deflt = gtk_widget_get_default_style();

     GUI.style     = gtk_style_copy(GUI.deflt);
     GUI.kstyle    = gtk_style_copy(GUI.deflt);
     GUI.textstyle = gtk_style_copy(GUI.deflt);

     GUI.saved_colors[0] = ((double)GUI.deflt->bg[0].red / 0xffff);
     GUI.saved_colors[1] = ((double)GUI.deflt->bg[0].green / 0xffff);
     GUI.saved_colors[2] = ((double)GUI.deflt->bg[0].blue / 0xffff);
     GUI.saved_colors[3] = ((double)GUI.deflt->bg[0].pixel / 0xffff);

     /* Grab the gdoubles from the string passed to us */
     sprintf(buf,"%f %f %f %f",
             GUI.saved_colors[0],
             GUI.saved_colors[1],
             GUI.saved_colors[2],
             GUI.saved_colors[3]);
     
     GUI.colorname = g_strdup_(buf);

     stylin.style = GUI.deflt;
     stylin.mask  = 0;
     /* Set the toplevel window and everything under it to the default
      * style 
      */
     SetStyleRecursively(window, (gpointer)&stylin);

     gtkeyboard_message(1,"Application default fonts/colors restored.\n");
} /* End reset_application_style */

/*
 * Creates a random style using the list of colors
 * above.  There is no checking to see if the colors 
 * would be a bad choice.
 */
static GtkStyle *new_style(GdkColor c)
{
    GtkStyle *style;
    
    /* Create new style */
    style = CreateBGStyle (c);
    
    return(style);
} /* End new_style */

/*
 * SetStyleRecursively
 *
 * Set the style on the current widget and on any 
 * children widgets.  This takes a GTKeyboardStyle structure as the data
 * argument.  What that does is to specify a style to apply to the widget,
 * and also whether or not to take the widgets current color/font with it.
 * i.e. some style changes are only intended to change color, and not font.
 * in that case, you would specify GTKeyboardFontMask in the mask portion of
 * the GTkeyboardStyle structure, and whatever font the widget was using would
 * be preserved rather than overriding it.
 */
void SetStyleRecursively (GtkWidget *widget, gpointer data)
{
     GTKeyboardStyle *stylin = (GTKeyboardStyle *)data;
     GtkStyle *oldstyle;
     int x;

     if(widget && GTK_IS_WIDGET(widget))
     {
          oldstyle = gtk_widget_get_style(widget);

          if((stylin->mask == GTKeyboardFontMask) ||
             (stylin->mask == GTKeyboardFontMask + GTKeyboardColorMask))
          {
               /* Copy font over */
               stylin->style->font = oldstyle->font;
          } /* End if */

          /* If the GTKeyboardColorMask is specified,
           * copy colors from the widget in question with us
           */
          if((stylin->mask == GTKeyboardColorMask) ||
             (stylin->mask == GTKeyboardColorMask + GTKeyboardFontMask))
          {
               /* Copy colors over */
               for(x=0; x<5; x++)
               {
                    stylin->style->bg[x]    = oldstyle->bg[x];
                    stylin->style->fg[x]    = oldstyle->fg[x];
                    stylin->style->light[x] = oldstyle->light[x]; 
                    stylin->style->dark[x]  = oldstyle->dark[x];
                    stylin->style->mid[x]   = oldstyle->mid[x];
                    stylin->style->text[x]  = oldstyle->text[x];
                    stylin->style->base[x]  = oldstyle->base[x];
               } /* End for */
          } /* End if */

          /* Set the new style */
	  gtk_widget_set_style(widget, stylin->style);

	  if(GTK_IS_CONTAINER(widget)) 
	  {
	       gtk_container_foreach(GTK_CONTAINER(widget), 
				     SetStyleRecursively, stylin);
	  } /* End if */
     } /* End if */
     else
	  gtkeyboard_error(2,"GTKeyboard error: in SetStyleRecursively,\n",
			   "NULL or NON-widget argument.\n");
} /* End SetStyleRecursively */

static int COLOR_CHANGED = 0;

/* color_button_clicked()
 * This is what happens when you click "Ok" in the color
 * selection dialog
 */
void color_button_clicked(GtkWidget *widget, gpointer gdata)
{
    GtkStyle *style;
    GdkColor color;
    GTKeyboardStyle stylin = { NULL, 0 };

    /* Call the color dialog to get a color */
    GetDialogColor(&color);

    if(!COLOR_CHANGED) /* The OK button wasn't clicked, so don't apply style */
         return;
    else
    {
         /* COLOR_CHANGED was true, so we'll apply the style.
          * But let's set it back to 0 so the user has to click OK
          * again before a different style will be applied.
          */
         COLOR_CHANGED = 0;
    } /* End else */

    /* Create a style based on the color */
    style = new_style(color);

    /* Save this for posterity */
    GUI.style = gtk_style_copy(style);

    stylin.style = style;
    stylin.mask  = GTKeyboardFontMask;

    /* Set the style of the wiget based on that new style 
     * We're setting properties for the WHOLE MAIN WINDOW
     */
    SetStyleRecursively(window, (gpointer)&stylin);
    if(GUI.kfont)
    {
         stylin.style->font = GUI.kfont;
         stylin.mask = GTKeyboardColorMask;
         SetStyleRecursively(window, (gpointer)&stylin);
    } /* End if */
} /* End color_button_clicked */

static void GetDialogColor(GdkColor *color)
{
     static GtkWidget *windowl = NULL;
     color_info *colorInfo;
     
     windowl = gtk_color_selection_dialog_new("Color Selection Dialog");
     
     /* Allocate memory for the colorinfo and populate it */
     colorInfo = g_malloc(sizeof(color_info));
     colorInfo->dialog = windowl;
     colorInfo->color  = color;
     
     gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(
	  GTK_COLOR_SELECTION_DIALOG(windowl)->colorsel),
				     TRUE);
     
     gtk_color_selection_set_update_policy(GTK_COLOR_SELECTION(
	  GTK_COLOR_SELECTION_DIALOG(windowl)->colorsel),
					   GTK_UPDATE_CONTINUOUS);
     
     gtk_signal_connect(GTK_OBJECT(windowl), "destroy",
                        GTK_SIGNAL_FUNC(smack),
                        windowl);
     
     gtk_signal_connect(GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(
          windowl)->ok_button),
                        "clicked",
                        GTK_SIGNAL_FUNC(color_ok_button),
                        colorInfo);
     
     gtk_signal_connect(GTK_OBJECT(windowl), "destroy",
                        GTK_SIGNAL_FUNC(CloseDialog),
		       colorInfo);
     
     gtk_signal_connect_object(
          GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG (windowl)->cancel_button),
          "clicked",
          GTK_SIGNAL_FUNC(gtk_widget_destroy),
          GTK_OBJECT(windowl));
     
     gtkeyboard_window_common_setup(windowl);
     gtk_widget_show_all(windowl);
     
     gtk_grab_add(windowl);
     gtk_main();
} /* End GetDialogColor */

/* User decided on a color */
static void color_ok_button(GtkWidget *w, color_info *colorInfo)
{
     GtkColorSelectionDialog *cs;
     GtkColorSelection *colorsel;
     gdouble color[4];
     int x = 0;
     
     cs       = (GtkColorSelectionDialog *) colorInfo->dialog;
     colorsel = GTK_COLOR_SELECTION(cs->colorsel);
     
     gtk_color_selection_get_color(colorsel, color);

     /* save our color values for posterity */
     for(x=0; x<4; x++)
          GUI.saved_colors[x] = color[x];
     
     /* Free up old colorname */
     CONDFREE(GUI.colorname);
     
     GUI.colorname = g_new0_(char, 1024);

     sprintf(GUI.colorname, "%f %f %f %f",
             (double)GUI.saved_colors[0],
             (double)GUI.saved_colors[1],
             (double)GUI.saved_colors[2],
             (double)GUI.saved_colors[3]);
     
     colorInfo->color->red   = color[0] * 0xffff;
     colorInfo->color->green = color[1] * 0xffff;
     colorInfo->color->blue  = color[2] * 0xffff;
     
     gtk_widget_destroy(GTK_WIDGET(cs));

     /* We did get a color via the OK button, so apply it.
      * Above methods will be looking for this value to be true 
      */
     COLOR_CHANGED = 1;
} /* End color_ok_button */

static void CloseDialog(GtkWidget *w, color_info *di)
{
     gtk_main_quit();
     gtk_grab_remove(w);
     g_free_(di);
     di = (color_info *)NULL;
} /* End close_dialog */

void load_font_to_widget(GtkWidget *widget, char *fontname)
{
     GdkFont *font;
     GtkStyle *style;
     GTKeyboardStyle stylin = { NULL, 0 };
     int x = 0;

     if(!fontname)
     {
	  gtkeyboard_error(1,"Cannot load specified font!\n");
	  return;
     } /* End if */

     font = gdk_font_load(fontname);

     if(!font)
     {
	  gtkeyboard_error(3,"Cannot load font named \"",
			   fontname, "\"!\n");
	  return;
     } /* End if */

     style = gtk_style_copy(GUI.deflt);

     stylin.style = style;
     stylin.mask  = GTKeyboardColorMask;  /* Preserve colors */

     /* This should solve the background color copying problem */
     for(x=0; x<5; x++)
     {
	  style->bg[x] = GUI.deflt->bg[x];
     } /* End for */

     style->font = font;

     SetStyleRecursively(widget, (gpointer)&stylin);     
} /* End load_font_to_widget() */

static void grabfont(GtkWidget *emitter, GtkWidget *fontsel)
{
     GtkFontSelectionDialog *fsd = GTK_FONT_SELECTION_DIALOG(fontsel);
     XFontStruct *fontOK = (XFontStruct *)NULL;
     char *fname = NULL;
     GTKeyboardStyle stylish;

     CONDFREE(GUI.fontname);

     fname = gtk_font_selection_dialog_get_font_name(fsd);
     
     if(fname)
     {
          GUI.fontname = g_strdup_(fname);
          g_free_(fname);
     } /* End if */
     else
     {
          fprintf(stderr,"Error:  No font specified!\n");
          fflush(stderr);
     } /* End else */

     gtk_widget_destroy (fontsel);

     chocolate("Got ");
     chocolate(GUI.fontname);
     chocolate(" as our font.\n");

     /* Test load the font.  If it fails, fontOK is null, and get on with
      * life after giving the user an error.
      * If it is successful, immediately unload the font, since we're using
      * GTK+ here, mmmk?
      */
     fontOK = XLoadQueryFont(GDK_DISPLAY(), GUI.fontname);
     
     if(fontOK == (XFontStruct *)NULL || (!fontOK))
     {
	  g_warning("Error loading font %s\n",GUI.fontname);
	  gtkeyboard_error(3,"Can't find fontname ",GUI.fontname,
			   "in the system list of available fonts.  ",
			   "Sorry - font not loaded.\n");

	  /* Danger!  Bail out while you still can! */
	  return;
     } /* End if */
     else
	  XFreeFont(GDK_DISPLAY(), fontOK);

     /* Else the font is available, load it and reset bg style color */

     GUI.font = gdk_font_load(GUI.fontname);

     chocolate("Updating widget...(this may take a while)...\n");

     GUI.textstyle = gtk_style_copy(GUI.deflt);
     GUI.textstyle->font = GUI.font;
     
     stylish.style = GUI.textstyle;
     stylish.mask  = GTKeyboardColorMask;

     SetStyleRecursively(GUI.main_output_text, (gpointer)&stylish);
} /* End grabfont */

static void keyboardgrabfont(GtkWidget *emitter, GtkWidget *fontsel)
{
     GtkFontSelectionDialog *fsd = GTK_FONT_SELECTION_DIALOG(fontsel);
     /* register int x=0; */
     XFontStruct *fontOK = (XFontStruct *)NULL;
     GTKeyboardStyle stylin = {NULL, 0};
     char *fname = NULL;

     CONDFREE(GUI.kfontname);

     fname = gtk_font_selection_dialog_get_font_name(fsd);

     if(fname)
     {
          GUI.kfontname = g_strdup_(fname);
          g_free_(fname);
     } /* End if */
     else
     {
          fprintf(stderr,"Error:  No font name found!\n");
          fflush(stderr);
     } /* End else */

     gtk_widget_destroy (fontsel);

     gtkeyboard_message(3,"(KEYBOARD) Got ", GUI.kfontname, 
			" as our font.\n");

     fontOK = XLoadQueryFont(GDK_DISPLAY(), GUI.kfontname);
     
     if(fontOK == (XFontStruct *)NULL || (!fontOK))
     {
	  g_warning("Error loading font %s\n",GUI.kfontname);
	  gtkeyboard_error(3,"Can't find fontname ",GUI.kfontname,
			   "in the system list of available fonts.  ",
			   "Sorry - font not loaded.\n");
	  return;
     } /* End if */
     else
	  XFreeFont(GDK_DISPLAY(), fontOK);

     /* Else the font is available, load it and reset bg style color */

     GUI.kfont = gdk_font_load(GUI.kfontname);

     chocolate("Updating widget...(this may take a while)...\n");

     GUI.kstyle = gtk_style_copy(GUI.deflt);
     GUI.kstyle->font = GUI.kfont;

     stylin.style = GUI.kstyle;
     stylin.mask  = GTKeyboardColorMask;

     /* Set the keyboard style recursively */
     SetStyleRecursively(GUI.KEYBOARD, (gpointer)&stylin);
} /* End keyboardgrabfont */

void FontSelect(GtkWidget *emitter, gpointer data)
{
     GtkWidget *widget;
     GtkFontSelectionDialog *fontsel;

     widget = gtk_font_selection_dialog_new ("Grab-A-Font (TM)");
     fontsel = GTK_FONT_SELECTION_DIALOG (widget);
     
     gtk_signal_connect(GTK_OBJECT(fontsel->ok_button), "clicked",
			GTK_SIGNAL_FUNC(grabfont), fontsel);
     
     gtk_signal_connect_object(GTK_OBJECT(fontsel->cancel_button), "clicked",
			       GTK_SIGNAL_FUNC(gtk_widget_destroy),
			       GTK_OBJECT(fontsel));

     gtkeyboard_window_common_setup(widget);
     gtk_widget_show_all(widget);
} /* End FontSelect */

void KeyboardFontSelect(GtkWidget *emitter, gpointer data)
{
     GtkWidget *widget;
     GtkFontSelectionDialog *fontsel;

     widget = gtk_font_selection_dialog_new ("Grab-A-Font (TM)");
     fontsel = GTK_FONT_SELECTION_DIALOG (widget);
     
     gtk_signal_connect(GTK_OBJECT(fontsel->ok_button), "clicked",
			GTK_SIGNAL_FUNC (keyboardgrabfont), fontsel);
     
     gtk_signal_connect_object(GTK_OBJECT(fontsel->cancel_button), "clicked",
			       GTK_SIGNAL_FUNC(gtk_widget_destroy),
			       GTK_OBJECT (fontsel));

     gtkeyboard_window_common_setup(widget);
     gtk_widget_show_all(widget);
} /* End KeyboardFontSelect */
