/* ui.c
 * 
 * This file contains some of the most important UI components of the program.
 * build_keyboard.c contains all of the code for building the keyboard.
 */
/* 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.
 */

/* NOTE!!!!  If you are using the remote control with GTKeyboard and the
 * arrow keys in different directions make it go to strange places you hadn't
 * intended, change the bottom define to #define PIXEL_MAGIC_NUMBER 0
 * This is necessary!!!  But it works as is on my machine.  I expect some 
 * people are going to have a problem with this.
 */
#define PIXEL_MAGIC_NUMBER         0

#define UI_C         /* File module */
#ifndef MASTER_H
#  include "master.h"
#endif /* MASTER_H */
#ifndef UI_H
#  include "include/ui.h"
#endif /* UI_H */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <errno.h>
#include <signal.h>
#include <ctype.h>

#include "xpm/ulc.h"
#include "xpm/urc.h"
#include "xpm/blc.h"
#include "xpm/brc.h"
#include "xpm/center.h"

#ifndef INCLUDE_HTML
#  define INCLUDE_HTML
#endif /* INCLUDE_HTML */

/* Holds toolbar xpm's */
#include "xpm/toolbarxpm.h"
#include "xpm/ds.h"          /* Define shortcuts pixmap */
#include "xpm/s1.h"          /* User shortcuts */
#include "xpm/s2.h"
#include "xpm/s3.h"
#include "xpm/s4.h"
#include "xpm/s5.h"
#include "xpm/s6.h"
#include "xpm/winignore.h"
#include "xpm/wingrab.h"
#include "xpm/sr.h"
#include "xpm/color.h"
#include "xpm/uncolor.h"
#include "xpm/keyboard.h"
#include "xpm/fontk.h"
#include "xpm/target.h"
#include "share/up-small.xpm"
#include "share/down-small.xpm"

#include "include/build_keyboard.h"

#define BOXES              10
#define KEYS_PER_LINE      10
#define TEXT_WIDTH         630
#define TEXT_HEIGHT        150

/* This chunk of code here determines whether or not gtk_widget_set_usize()
 * will be called on GUI.main_output_text or not.  Sometimes people have
 * strange problems with resizing the keyboard depending on whether or not
 * the widgets size has been set explicitly, although it works fine on my
 * machine.
 *
 * If the main output text window looks weird normally, or if it looks weird
 * after changing the size of the keyboard/other widgets, comment out the
 * line #define SIZING_POLICY and recompile this module.  The text widget
 * will then just have its default size.
 */
#define SIZING_POLICY

#ifdef SIZING_POLICY
#define OUTPUT_HEIGHT             250
#define OUTPUT_WIDTH              350
#endif /* SIZING_POLICY */

/* Static methods - this file only */
static GtkWidget *create_toolbar              (GtkWidget *emitter, 
                                               gpointer data);
static GtkWidget *textframe                   (void);
static GtkWidget *build_bottom_row_buttons    (void);
static GtkWidget *build_status                (void);
static void gtkeyboard_default_window_opts    (void);

static void widget_edited(GtkWidget *emitter, gpointer data)
{
     options.document_modified = ON;
} /* End widget_edited() */

void CreateMainWindow(void)
{
     /* Of the widgets that are needed, s_table, window are global in 
      * master.h and KEYBOARD
      */

     GtkWidget *box1;
     GtkWidget *the_rest_widget;
     GtkWidget *handlebox;
     GdkPixmap *pixmap;
     char *tptr = (char *)NULL;
     char window_name[1024];
     int x, boxcounter=0;           /* Boxcounter is an index for miscbox */

     /* Get the ELEMENTS structure in, to figure out what we should and
      * shouldn't show.  But only if it's OK to read the file.  If 
      * ignore_layout_file is set, then don't read it.
      */
     if(!options.IGNORE_LAYOUT_FILE)
          load_layout_settings((GtkWidget *)NULL, (gpointer)NULL);

     /* Set up MAIN output text widget (initialize it, anyway) */
     GUI.main_output_text = gtk_text_new(NULL, NULL);

     gtk_signal_connect(GTK_OBJECT(GUI.main_output_text), 
			"changed", GTK_SIGNAL_FUNC(widget_edited), 
			GUI.main_output_text);
     gtk_signal_connect(GTK_OBJECT(GUI.main_output_text),
			"insert-text", GTK_SIGNAL_FUNC(widget_edited),
			GUI.main_output_text);
     gtk_signal_connect(GTK_OBJECT(GUI.main_output_text),
			"delete-text", GTK_SIGNAL_FUNC(widget_edited),
			GUI.main_output_text);

     /* Set up the status window, too.  It will be manipulated later. */
     GUI.status = gtk_text_new(NULL, NULL);

     /* This needs to be done right away */
     GUI.deflt = gtk_widget_get_default_style();

     if(options.BORDERLESS)
	  window = gtk_window_new(GTK_WINDOW_POPUP);
     else
	  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

     gtk_widget_realize(window);  /* Create the GdkWindow for xpms */

     /* Set up app exiting signals - quit properly and destroy window */
     gtk_signal_connect(GTK_OBJECT(window), "delete_event",
			GTK_SIGNAL_FUNC(final_check), (gpointer)NULL);

     /* Initialize the popup menus */
     GUI.popup_menu = create_context_popup_menu(NO_WIDGET, NULL);
     gtk_widget_show_all(GUI.popup_menu);

     /* Set title of app */
     sprintf(window_name, "%s Version %s",APPNAME, VERSION);
     gtk_window_set_title(GTK_WINDOW(window), window_name);
     
     GUI.xwindow = GDK_WINDOW_XWINDOW(GTK_WIDGET(window)->window);
     
     if(GUI.xwindow == (Window)NULL)
     {
	  fprintf(Q,"Error in finding our windowname.\n");
	  fflush(Q);
     } /* End if */

     gtk_container_border_width(GTK_CONTAINER(window), 0);
     
     box1 = gtk_vbox_new(FALSE, 0);
     gtk_container_add(GTK_CONTAINER(window), box1);
     
     GUI.mainbox = gtk_vbox_new(FALSE, 0);

     GUI.toolbar = create_toolbar((GtkWidget *)NULL, (gpointer)NULL);

     /* Create toolbar and pack it into the main vbox */
     gtk_box_pack_start(GTK_BOX(GUI.mainbox),
			GUI.toolbar,
			TRUE, FALSE, 0);

     /* Pack the ENTIRE keyboard into the widget GUI.mainbox */
     if(options.keyboard_file && file_exists(options.keyboard_file))
	  GUI.KEYBOARD = build_an_entire_keyboard((GtkWidget *)NULL, 
						  options.keyboard_file);
     else 
     {
	  tptr = expand_relative_pathname("QWERTY.key");
	  GUI.KEYBOARD = build_an_entire_keyboard((GtkWidget *)NULL, tptr);
	  g_free_(tptr);
     } /* End else */
     
     GUI.keyboard_socket = gtk_hbox_new(FALSE, 0);
     
     /* Pack the keyboard into the socket, then the socket into mainbox.
      * that way we can screw around with the keyboard widget and pack it
      * back into the socket without having to worry about window placement.
      */
     gtk_container_add(GTK_CONTAINER(GUI.keyboard_socket), GUI.KEYBOARD);
     
     gtk_widget_show(GUI.keyboard_socket);
     gtk_widget_show(GUI.KEYBOARD);

     /* Build the status widget with sliders */
     GUI.s_table = build_status();

     gtk_box_pack_start(GTK_BOX(GUI.mainbox), GUI.keyboard_socket,
			TRUE, FALSE, 0);

     /* s_table packed here into mainbox true false 0 */

     x=0;
     boxcounter=0;
     
     if(GUI.use_handleboxes)
     {
          handlebox = gtk_handle_box_new();
          set_handle_box_opts(handlebox);
     } /* End if */ 
     else
     {
          handlebox = gtk_hbox_new(FALSE, 0);
     } /* End else */

     gtk_container_add(GTK_CONTAINER(handlebox),
                       create_menus(NO_WIDGET, NULL));

     GUI.menubar = handlebox;

     gtk_box_pack_start(GTK_BOX(box1), handlebox, FALSE, FALSE, 0);
     
     /* If we are supposed to show everything, then pack a semi-massive
      * container returned by the_rest into the main box, box1
      */
     GUI.master_text = textframe(); /* Main table holding master_output_text */

     gtk_box_pack_start(GTK_BOX(GUI.mainbox), GUI.s_table, TRUE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(GUI.mainbox), GUI.master_text,
			FALSE, FALSE, 0);

     the_rest_widget = build_bottom_row_buttons();

     gtk_box_pack_start(GTK_BOX(GUI.mainbox), the_rest_widget, 
			TRUE, FALSE, 0);
     
     /* Pack the main box in */
     gtk_box_pack_start(GTK_BOX(box1),
			GUI.mainbox, TRUE, FALSE, 0);

     /* Load the file that we've specified.  Everything is created. */
     load_file(options.workingfile, GUI.main_output_text);

     /* Set up iconized pixmap */
     pixmap = gdk_pixmap_create_from_xpm_d(window->window, NULL, NULL, 
					   (gchar **)xpm_keyboard);
     gdk_window_set_icon(window->window, NULL, pixmap, NULL);

     /* Open and load the active shortcuts from disk */
     gtkeyboard_load_shortcuts();

     /* This is going to set up window appearance options. */
     gtkeyboard_default_window_opts();
     gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE);

     /* Set the window position if needed */
     if(options.xpos >= 0 && options.ypos >= 0)
	  gtk_widget_set_uposition(window, options.xpos, options.ypos);

     /* Showtime */
     do_keyboard_sublayout();
     
     gtk_widget_show_all(handlebox);
     gtk_widget_show(GUI.toolbar);
     gtk_widget_show(box1);
     gtk_widget_show(GUI.mainbox);

     /* Show and hide the appropriate screen elements */
     do_layout();
     do_keyboard_sublayout();

     if(!options.SHOW_TOOLTIPS)
     {
	  vanilla("Disabling Tooltips on startup.\n");
	  disable_tooltips();
     } /* End if */

     /* If we know which color to use, load it up. */
     if(GUI.colorname)
          parse_and_apply_color(GUI.colorname);

     /* Set up default fonts if specified */
     if(GUI.kfontname)
     {
          if(GUI.kfont) gdk_font_unref(GUI.kfont);
          GUI.kfont = gdk_font_load(GUI.kfontname);
	  load_font_to_widget(GUI.KEYBOARD, GUI.kfontname);
     } /* End if */
     if(GUI.fontname)
     {
          if(GUI.font) gdk_font_unref(GUI.font);
          GUI.font = gdk_font_load(GUI.fontname);
	  load_font_to_widget(GUI.main_output_text, GUI.fontname);
     } /* End if */

     /* Show the toplevel widget (which will show everything else) */
     gtk_widget_show(window);

     /* start the timeout for track_focus to be called every ~1/4 sec. */
     if(options.REDIRECT_POLICY_IMPLICIT)
	  options.handlerid = gtk_timeout_add(256, (GtkFunction)track_focus, 
					      NULL);
} /* End CreateMainWindow */

void redirect_implicit(GtkWidget *emitter, gpointer data)
{
     if(options.handlerid != NO_HANDLER)
     {
	  chocolate("Redirection mode is already implicit.\n");
	  return;
     } /* End if */
     
     options.handlerid = gtk_timeout_add(256, (GtkFunction)track_focus, NULL);
     chocolate("Redirection mode is now implicit.\n");
} /* End redirect_implicit */

void redirect_explicit(GtkWidget *emitter, gpointer data)
{
     /* check redundant */
     if(options.handlerid == NO_HANDLER)
     {
	  chocolate("Redirection mode is already explicit.\n");
	  return;
     } /* End if */
     
     /* Remove the follow_focus handler */
     gtk_timeout_remove(options.handlerid);

     /* Ignore whatever window we were working with */
     ignore_window();

     /* Set it to be explicit */
     options.handlerid = NO_HANDLER;
     chocolate("Redirection mode is now explicit.\n");
} /* End redirect_explicit */

void set_handle_box_opts(GtkWidget *handlebox)
{
     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(handlebox), 
				    GUI.SHADOW_TYPE);
     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(handlebox),
					GUI.PULLOFFS_SIDE);
     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(handlebox), 
				  GTK_POS_LEFT);
} /* End set_handle_box_opts */

/* Returns a structure packed with info on a specific window.
 * really all it is right now is xpos, ypos, height, width
 * with modifications made for border_width as returned by 
 * Xlib.
 */
window_dimensions gtkeyboard_query_window(Window w)
{
     XWindowAttributes attribs;
     window_dimensions dimensions;

     /* Error.  This ain't good, yo */
     if(!GUI.xwindow || GUI.xwindow == (Window)NULL)
     {
	  gtkeyboard_error(1,"ERROR: Window_With_Name in window_dimensions\n");
	  dimensions.x      = -1;
	  dimensions.y      = -1;
	  dimensions.width  = -1;
	  dimensions.height = -1;
	  return(dimensions);
     } /* End if */

     XGetWindowAttributes(GDK_DISPLAY(), w, &attribs);
     dimensions.x      = attribs.x;
     dimensions.y      = attribs.y;
     dimensions.width  = attribs.width  + attribs.border_width;
     dimensions.height = attribs.height + attribs.border_width;
     
     return(dimensions);
} /* End gtkeyboard_query_window */

/* Just displays a message to the screen on window location, height/width */
void gtkeyboard_print_coords(GtkWidget *emitter, gpointer data)
{
     window_dimensions our_window;
     char buffer[1024];

     our_window = gtkeyboard_query_window(GUI.xwindow);

     sprintf(buffer,
	     "Our window is located at (%d,%d) and is %d X %d\n",
	     our_window.x, our_window.y, our_window.width, our_window.height);

     gtkeyboard_message(1,buffer);
} /* End gtkeyboard_print_coords */

GtkWidget *make_browse_button(GtkWidget *insert_into)
{
     GtkWidget *button = gtk_button_new_with_label("  Browse  ");
     gtk_signal_connect(GTK_OBJECT(button), "clicked",
                        GTK_SIGNAL_FUNC(browse), insert_into);
     return(button);
} /* End make_browse_button */

void browse(GtkWidget *emitter, gpointer data)
{
     /* Set up file selection dialog */
     GtkWidget *fileselection;

     fileselection = gtk_file_selection_new("Choose a File:");
     
     /* Hook up the action of clicking the OK button */
     gtk_signal_connect(GTK_OBJECT(
          GTK_FILE_SELECTION(fileselection)->ok_button),
                        "clicked", GTK_SIGNAL_FUNC(grab_smack),
                        (GtkWidget *)data);
     gtk_signal_connect(GTK_OBJECT(
          GTK_FILE_SELECTION(fileselection)->cancel_button),
                        "clicked", GTK_SIGNAL_FUNC(smack),
                        fileselection);

     /* Set up the filename selection dialog */
     gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection), 
				     options.extrafiles);

     gtkeyboard_window_common_setup(fileselection);
     gtk_widget_show_all(fileselection);
} /* End browse */

/* Callback for some buttons - moves the window to a predetermined spot */
void move_window(GtkWidget *emitter, int POSITION)
{
     /* Data to hold window info in. */
     window_dimensions our_window, root_window;
     int newx = 0, newy = 0;  /* New coordinates to move to */
     char damnbuffer[512];        /* Ending message */

     GTKEYBOARD_MESSAGE_SEPARATOR;

     /* Get the positions of various windows */
     our_window = gtkeyboard_query_window(GUI.xwindow);
     
     root_window.width = gdk_screen_width();
     root_window.height = gdk_screen_height();
     root_window.x = 0;
     root_window.y = 0;
     
     if(DIMENSIONS_ERROR(root_window) || DIMENSIONS_ERROR(our_window))
     {
	  gtkeyboard_error(1,"ERROR:  move_window() invalid return.\n");
	  return;
     } /* End if */

     /* Now catch some cases that would cause us to do really ugly things
      * and return instead of doing the ugly thing in question.
      */
     if(our_window.width > root_window.width)
     {
	  gtkeyboard_error(2,"GTKeyboard width > root window width.\n",
			   "Can't move window.\n");
	  gtkeyboard_message(1,"Perhaps you should resize the keyboard.\n");
	  return;
     } /* End if */
     else if(our_window.height > root_window.height)
     {
	  gtkeyboard_error(2,"GTKeyboard height > root window height.\n",
			   "Can't move window.\n");
	  gtkeyboard_message(1,"Perhaps you should resize the keyboard.\n");
	  return;
     } /* End else if */

     switch(POSITION)
     {
     case ORIGIN:
	  newx = 0;
	  newy = 0;
	  break;
     case WIN_CENTER:  /* Center the window */
	  newx = (int)(root_window.width/2) - (int)(our_window.width/2);
	  newy = (int)(root_window.height/2) - (int)(our_window.height/2);
	  break;
     case B_CENTER:     /* Bottom center of the screen */
	  newx = (int)(root_window.width/2) - (int)(our_window.width/2);
	  newy = root_window.height - our_window.height;
	  break;
     case T_CENTER:    /* Top center of screen */
	  newx = (int)(root_window.width/2) - (int)(our_window.width/2);
	  newy = 0;
	  break;
     case L_CENTER:    /* Left centered */
	  newx = 0;
	  newy = (int)(root_window.height/2) - (int)(our_window.height/2);
	  break;
     case R_CENTER:    /* Right centered */
	  newx = root_window.width - our_window.width;
	  newy = (int)(root_window.height/2) - (int)(our_window.height/2);
	  break;
     case UL_CORNER:  /* Upper left, same as origin */
	  newx = 0;
	  newy = 0;
	  break;
     case UR_CORNER:  /* Upper righthand corner */
	  newy = 0;
	  newx = root_window.width - our_window.width;
	  break;
     case BR_CORNER:  /* Bottom right corner */
	  newx = root_window.width - our_window.width;
	  newy = root_window.height - our_window.height;
	  break;
     case BL_CORNER:  /* Bottom left corner */
	  newx = 0;
	  newy = root_window.height - our_window.height;
	  break;
     default:
	  /* Whatever is in there is ok with me. :) */
	  break;
     } /* End switch */

     if(newx > root_window.width || newy > root_window.height ||
	newx < -1 || newy < -1)
     {
	  sprintf(damnbuffer,"(%d,%d)",newx,newy);
	  gtkeyboard_error(4,"Invalid coordinates ",damnbuffer,
			   " for GTKeyboard or ",
			   "root window.\n", 
			   "Please report this with FULL STATUS OUTPUT ",
			   "to the maintainer of GTKeyboard.\n");
	  gtkeyboard_message(1,"Well damn, I just can't do that.\n");
	  return;
     } /* End if */

     move_gtkeyboard(newx,newy);

     return;
} /* End move_window */

/* A simple call to whatever move_window call I'm favoring at the moment. */
void move_gtkeyboard(const int x, const int y)
{
     char buffer[512];
     sprintf(buffer,"Moved GTKeyboard to (%d,%d)\n",x,y);
     gtkeyboard_message(1,buffer);
     gdk_window_move(window->window, x, y);
     /* XMoveWindow(GDK_DISPLAY(), GUI.xwindow, x, y); */
} /* End move_gtkeyboard */

/* Adds x to the x location of our window, adds y to the y location of our
 * window, and moves the window.  X and y are negative or positive offsets
 * from our current position.  Often they are going to be BUMP_AMOUNT
 * (remote_control.c)
 */
void move_gtkeyboard_offset(const int x, const int y)
{
     /* char buffer[512]; */
     int xnorm, ynorm;

     gdk_window_get_position(window->window, &xnorm, &ynorm);

     xnorm += x;
     ynorm += y;

     /* ynorm -= PIXEL_MAGIC_NUMBER; */

     gdk_window_move(window->window, xnorm, ynorm);
     /* move_gtkeyboard(xnorm, ynorm); */
} /* End move_gtkeyboard_offset */

/* This has to be a function instead of a macro so it can be used
 * with callbacks.  Bummer
 */
void gtkeyboard_hide(void)
{
     gdk_window_hide(window->window);
} /* End gtkeyboard_hide() */

void gtkeyboard_show(void)
{
     gdk_window_show(window->window);
} /* End gtkeyboard_show() */

void gtkeyboard_lower(void)
{
     /* Don't print a message, since the user won't see it anyway. */
     gdk_window_lower(window->window);
} /* End gtkeyboard_iconify */

void gtkeyboard_raise(void)
{
     gtkeyboard_message(1,"Raising GTKeyboard window...\n");
     gdk_window_raise(window->window);
} /* End gtkeyboard_raise */

static void gtkeyboard_default_window_opts(void)
{
     XWindowChanges fu;

     fu.stack_mode = Above;

     XConfigureWindow(GDK_DISPLAY(), GUI.xwindow, CWStackMode, &fu);
} /* End fuck_around */

GtkWidget *textframe(void)
{
     GtkWidget *container, *output_table, *output_vscroll, *output_label,
	  *pulloff;

     output_table              = gtk_table_new(2, 2, FALSE);
     container                 = gtk_vbox_new(FALSE, 0);

     if(GUI.use_handleboxes)
     {
          pulloff = gtk_handle_box_new();
          set_handle_box_opts(pulloff);
     } /* End if */
     else pulloff = gtk_hbox_new(FALSE, 0);
     gtk_text_set_editable(GTK_TEXT(GUI.main_output_text), TRUE);
     gtk_text_set_point(GTK_TEXT(GUI.main_output_text), 0);
     
     gtk_table_attach(GTK_TABLE(output_table), 
		      GUI.main_output_text, 0, 1, 0, 1,
		      GTK_EXPAND | GTK_SHRINK | GTK_FILL,
		      GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);

     output_vscroll = gtk_vscrollbar_new(GTK_TEXT(GUI.main_output_text)->vadj);
     gtk_table_attach(GTK_TABLE(output_table), output_vscroll, 1, 2, 0, 1, 
		      GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
     gtk_widget_show(output_vscroll);

     output_label = gtk_label_new("Text Editing Buffer");

     gtk_box_pack_start(GTK_BOX(container), output_label, 
			TRUE, FALSE, 0);

     gtk_box_pack_start(GTK_BOX(container), output_table, 
			TRUE, FALSE, 0);

     gtk_signal_connect_object(GTK_OBJECT(output_label),
			       "button_press_event",
			       GTK_SIGNAL_FUNC(button_press),
			       GTK_OBJECT(GUI.popup_menu));

     gtk_signal_connect_object(GTK_OBJECT(GUI.main_output_text), 
			       "button_press_event",
			       GTK_SIGNAL_FUNC(button_press), 
			       GTK_OBJECT(GUI.popup_menu));
     
     gtk_container_add(GTK_CONTAINER(pulloff), container);
     return(pulloff); 
} /* End textframe */

static GtkWidget *build_status(void)
{
     GtkWidget *vscroll;
     GtkWidget *table;
     GtkWidget *pulloff;

     /* Set up GUI.s_table, which contains a scrollbar and the status widget 
      * that tells the user what's going on with the program 
      */
     table = gtk_table_new(2, 2, FALSE);
     gtk_widget_show(table);

     if(GUI.use_handleboxes)
     {
          pulloff = gtk_handle_box_new();
          set_handle_box_opts(pulloff);
     } /* End if */
     else pulloff = gtk_hbox_new(FALSE, 0);

     gtk_text_set_editable(GTK_TEXT(GUI.status), FALSE);
     gtk_text_set_point(GTK_TEXT(GUI.status), 0);

     gtk_table_attach(GTK_TABLE(table), GUI.status, 0, 1, 0, 1,
		      GTK_EXPAND | GTK_SHRINK | GTK_FILL,
		      GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
     gtk_widget_show(GUI.status);

     vscroll = gtk_vscrollbar_new(GTK_TEXT(GUI.status)->vadj);
     gtk_table_attach(GTK_TABLE(table), vscroll, 1, 2, 0, 1, 
		      GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
     
     gtk_signal_connect_object (GTK_OBJECT(GUI.status), 
				"button_press_event",
				GTK_SIGNAL_FUNC(button_press), 
				GTK_OBJECT(GUI.popup_menu));

     gtk_container_add(GTK_CONTAINER(pulloff), table);

     gtk_widget_show(GUI.status);
     gtk_widget_show(table);
     gtk_widget_show(vscroll);
     gtk_widget_show(pulloff);

     return(pulloff);
} /* End build_status */

/* This function creates the main_output_text and puts it in a table with 
 * a scroll bar, and makes the buttons at the bottom of the application,
 * packages everything into a container and returns it to be packed into 
 * the main box
 */
GtkWidget *build_bottom_row_buttons(void)
{
     /* variables for the output section */
     GtkWidget *output_subcontainer;
     GtkWidget *output_savebutton, *output_aboutbutton, *output_licensebutton;
     GtkWidget *output_clearbutton, *output_clearstatusbutton, *output_button,
	  *output_openbutton, *output_sa, *output_choosewindow, 
	  *output_ignorewindow, *statusreport, *output_saveasbutton;
     GtkWidget *handlebox;
     GtkWidget *align = gtk_alignment_new(0.5, 0, 0.5, 0);

     if(GUI.use_handleboxes)
     {
          handlebox = gtk_handle_box_new();
          set_handle_box_opts(handlebox);
     } /* End if */
     else
          handlebox = gtk_hbox_new(FALSE, 0);

     output_container          = gtk_vbox_new(FALSE, 0);
     output_subcontainer       = gtk_hbox_new(FALSE, 0);
   
     /* New buttons galore */
     output_sa                 = gtk_button_new_with_label("Select All");
     output_choosewindow       = gtk_button_new_with_label("Redirect");
     output_ignorewindow       = gtk_button_new_with_label("Ignore");
     output_button             = gtk_button_new_with_label("Exit");
     output_savebutton         = gtk_button_new_with_label("Save");
     output_saveasbutton       = gtk_button_new_with_label("Save As");
     output_aboutbutton        = gtk_button_new_with_label("About");
     output_licensebutton      = gtk_button_new_with_label("License");
     output_clearbutton        = gtk_button_new_with_label("Clear All");
     output_clearstatusbutton  = gtk_button_new_with_label("Clear Status");
     output_openbutton         = gtk_button_new_with_label("Open");
     statusreport              = gtk_button_new_with_label("Status");

     /* Get us some tooltips running */
     add_tooltip(output_clearstatusbutton, "Clear the status text widget");
     add_tooltip(output_savebutton,        "Save this Document");
     add_tooltip(output_saveasbutton,      "Save File As");
     add_tooltip(output_button,            "Exit GTKeyboard");
     add_tooltip(output_sa,                "Select All Text");
     add_tooltip(output_ignorewindow,      "Ignore output window");
     add_tooltip(output_aboutbutton,       "About GTKeyboard");
     add_tooltip(output_licensebutton,     "Display the GTKeyboard license");
     add_tooltip(output_clearbutton,       "Clear all text from widget");
     add_tooltip(output_openbutton,        "Open a different file");
     add_tooltip(statusreport, "Print a status report to the status widget");
     add_tooltip(output_choosewindow,  "Choose a different window for output");

     /* Pack all of the base row buttons into subcontainer in the following
      * order:  =SAVE=OPEN=CLEAR=ABOUT=QUIT=
      */
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_savebutton, 
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_saveasbutton,
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_openbutton,
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_sa,
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_clearbutton,
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_clearstatusbutton,
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_aboutbutton, 
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_licensebutton,
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), statusreport,
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_button, 
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_choosewindow,
			FALSE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(output_subcontainer), output_ignorewindow,
			FALSE, FALSE, 0);

     /* Signal Mapping for bottom row buttons */
     
     /* Quit button mapped to CloseApp - gtk_main_quit and so on. */
     gtk_signal_connect(GTK_OBJECT(output_sa), "clicked",
			GTK_SIGNAL_FUNC(select_all), GUI.main_output_text);
     gtk_signal_connect(GTK_OBJECT(statusreport), "clicked",
			GTK_SIGNAL_FUNC(status_report), GUI.status);
     gtk_signal_connect(GTK_OBJECT(output_button), "clicked", 
			GTK_SIGNAL_FUNC(final_check), NULL);
     /* Save button mapped to save_main_output_text, text widget as arg */
     gtk_signal_connect(GTK_OBJECT(output_savebutton), "clicked", 
			GTK_SIGNAL_FUNC(save_output_text), 
			GUI.main_output_text);
     gtk_signal_connect(GTK_OBJECT(output_saveasbutton), "clicked",
			GTK_SIGNAL_FUNC(save_file_as), NULL);
     /* About button mapped to program_info and INFO_ABOUT */
     gtk_signal_connect(GTK_OBJECT(output_aboutbutton), "clicked", 
			GTK_SIGNAL_FUNC(program_info), 
                        GINT_TO_POINTER(INFO_ABOUT));
     /* Open button mapped to OpenFile, no args */
     gtk_signal_connect(GTK_OBJECT(output_openbutton), "clicked",
			GTK_SIGNAL_FUNC(OpenFile), NULL);
     /* License button mapped to LICENSE, no args */
     gtk_signal_connect(GTK_OBJECT(output_licensebutton), "clicked",
			GTK_SIGNAL_FUNC(program_info), 
                        GINT_TO_POINTER(INFO_LICENSE));
     /* Clear button mapped to output_clearbutton, (pop up dialog) */
     gtk_signal_connect(GTK_OBJECT(output_clearbutton), "clicked",
			GTK_SIGNAL_FUNC(clear_all_text), GUI.main_output_text);
     gtk_signal_connect(GTK_OBJECT(output_clearstatusbutton), "clicked",
			GTK_SIGNAL_FUNC(clear_all_text), GUI.status);
     gtk_signal_connect(GTK_OBJECT(output_ignorewindow), "clicked",
			GTK_SIGNAL_FUNC(ignore_window), NULL);
     gtk_signal_connect(GTK_OBJECT(output_choosewindow), "clicked",
			GTK_SIGNAL_FUNC(grab_a_window), NULL);

     /* IMPORTANT:  IT IS THE CALLERS RESPONSIBILITY TO SHOW THESE WIDGETS */
     gtk_container_add(GTK_CONTAINER(align), output_subcontainer);
     gtk_container_add(GTK_CONTAINER(handlebox), align);
     GUI.bottom_row_buttons = handlebox;
     gtk_widget_show_all(handlebox);
     return(handlebox);
} /* End the_rest */

/* Creates a toolbar and packs it into a removable widget.
 */
static GtkWidget *create_toolbar(GtkWidget *emitter, gpointer data)
{
     char label[512];
     GtkWidget *toolbar;
     register int x = 0;
     char *charptr;
     GtkWidget *handlebox;

     if(GUI.use_handleboxes)
     {
          handlebox = gtk_handle_box_new();
          set_handle_box_opts(handlebox);
     } /* End if */
     else handlebox = gtk_hbox_new(FALSE, 0);

     toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
			       GTK_TOOLBAR_ICONS);

     GUI.toolbar_gtktb = toolbar;

     if(Toolbar.TB_NEW)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Change Working Filename", NULL, 
				  xpm_to_widget(window, (gchar **)xpm_new),
				  GTK_SIGNAL_FUNC(NewFile), NULL);
     if(Toolbar.TB_OPEN)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Open a File", NULL,
				  xpm_to_widget(window, (gchar **)xpm_open),
				  GTK_SIGNAL_FUNC(OpenFile), NULL);
     if(Toolbar.TB_CUT)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL, 
				  "Cut Text", NULL,
				  xpm_to_widget(window, (gchar **)xpm_cut),
				  GTK_SIGNAL_FUNC(CutText), NULL);
     if(Toolbar.TB_COPY)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Copy Text", NULL, 
				  xpm_to_widget(window, (gchar **)xpm_copy),
				  GTK_SIGNAL_FUNC(CopyText), NULL);
     if(Toolbar.TB_PASTE)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Paste Text", NULL,
				  xpm_to_widget(window, (gchar **)xpm_paste),
				  GTK_SIGNAL_FUNC(PasteText), NULL);
     if(Toolbar.TB_FONT)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Choose Your Text Font", NULL, 
				  xpm_to_widget(window, (gchar **)xpm_font),
				  GTK_SIGNAL_FUNC(FontSelect), NULL);
     if(Toolbar.TB_KEYBOARD_FONT)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Choose the Keyboard Font", NULL, 
				  xpm_to_widget(window, (gchar **)xpm_fontk),
				  GTK_SIGNAL_FUNC(KeyboardFontSelect), NULL);
     if(Toolbar.TB_COLOR)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Choose Application Colors", NULL, 
				  xpm_to_widget(window, (gchar **)xpm_color),
				  GTK_SIGNAL_FUNC(color_button_clicked), NULL);
     if(Toolbar.TB_RESET_STYLE)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Restore Application Default Looks", NULL, 
				  xpm_to_widget(window, (gchar **)xpm_uncolor),
			     GTK_SIGNAL_FUNC(reset_application_style), NULL);
     if(Toolbar.TB_STATUS_REPORT)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Status Report", NULL, 
				  xpm_to_widget(window, (gchar **)xpm_sr),
				  GTK_SIGNAL_FUNC(status_report), GUI.status);
     
     if(Toolbar.TB_SHORTCUTS)
     {
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Define User Shortcuts", NULL,
				  xpm_to_widget(window, (gchar **)xpm_ds),
				  GTK_SIGNAL_FUNC(define_shortcuts), NULL);
	  
	  for(x=0; x<SHORTCUTS; x++)
	  {
	       sprintf(label,"User Defined Shortcut #%d",(x+1));

	       switch(x)
	       {
	       case 0:  charptr = (char *)xpm_s1; break;
	       case 1:  charptr = (char *)xpm_s2; break;
	       case 2:  charptr = (char *)xpm_s3; break;
	       case 3:  charptr = (char *)xpm_s4; break;
	       case 4:  charptr = (char *)xpm_s5; break;
	       case 5:  charptr = (char *)xpm_s6; break;
	       default: charptr = (char *)xpm_s1; break;
	       } /* End switch */
	       
	       gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				       label, NULL,
				       xpm_to_widget(window,(gchar **)charptr),
				       GTK_SIGNAL_FUNC(do_shortcut), 
				       (gpointer)x);
	  } /* End for */
     } /* End if toolbar.TB_SHORTCUTS */

     if(Toolbar.TB_WINDOW_GRAB)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Grab A Window", NULL,
				  xpm_to_widget(window, (gchar **)xpm_wingrab),
				  GTK_SIGNAL_FUNC(grab_a_window), NULL);
     if(Toolbar.TB_WINDOW_IGNORE)
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Ignore Window", NULL,
				  xpm_to_widget(window,(gchar**)xpm_winignore),
				  GTK_SIGNAL_FUNC(ignore_window), NULL);
     
     if(Toolbar.TB_POSITIONING)
     {
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Open the GTKeyboard Remote Control", NULL,
				  xpm_to_widget(window, (gchar **)xpm_target),
				  GTK_SIGNAL_FUNC(gtkeyboard_remote_control), 
				  (gpointer)NULL);
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Move to upper left corner", NULL,
				  xpm_to_widget(window, (gchar **)xpm_ulc),
				  GTK_SIGNAL_FUNC(move_window), 
				  (gpointer)UL_CORNER);
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Move to upper right corner", NULL,
				  xpm_to_widget(window, (gchar **)xpm_urc),
				  GTK_SIGNAL_FUNC(move_window),
				  (gpointer)UR_CORNER);
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Center on screen", NULL,
				  xpm_to_widget(window, (gchar **)xpm_center),
				  GTK_SIGNAL_FUNC(move_window),
				  (gpointer)WIN_CENTER);
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Move to lower left hand corner", NULL,
				  xpm_to_widget(window, (gchar **)xpm_blc),
				  GTK_SIGNAL_FUNC(move_window),
				  (gpointer)BL_CORNER);
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Move to lower right hand corner", NULL,
				  xpm_to_widget(window, (gchar **)xpm_brc),
				  GTK_SIGNAL_FUNC(move_window),
				  (gpointer)BR_CORNER);
				  
     } /* End if TB_POSITINING */

     if(Toolbar.TB_RAISE)
     {
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Stack GTKeyboard Upward", NULL,
				  xpm_to_widget(window,
						(gchar **)up_small_xpm),
				  GTK_SIGNAL_FUNC(gtkeyboard_raise), NULL);
     } /* End if raise */
     if(Toolbar.TB_LOWER)
     {
	  gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), NULL,
				  "Stack GTKeyboard Downward", NULL,
				  xpm_to_widget(window,
						(gchar **)down_small_xpm),
				  GTK_SIGNAL_FUNC(gtkeyboard_lower), NULL);
     } /* End if lower */

     gtk_container_add(GTK_CONTAINER(handlebox), toolbar);
     return(handlebox);
} /* End create_toolbar */

void EasterEgg(GtkWidget *emitter, gpointer data)
{
     GtkWidget *popup = gtk_dialog_new();
     GtkWidget *button = gtk_button_new_with_label("Boy this is stupid.");

     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(popup)->vbox), 
			gtk_label_new("Congratulations!  You found a completely worthless Easter Egg!!!!!!"), 
			FALSE, FALSE, 0);

     gtk_signal_connect(GTK_OBJECT(button), "clicked",
			GTK_SIGNAL_FUNC(smack), popup);
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(popup)->action_area),
			button, FALSE, FALSE, 0);
     gtk_widget_show_all(popup);
} /* End EasterEgg */

/* END MAIN WINDOW CONSTRUCTION FUNCTION CREATEMAINWINDOW */

void select_all(GtkWidget *w, gpointer data)
{
     GtkWidget *foo = (GtkWidget *)data;
     gtk_editable_select_region(GTK_EDITABLE(foo), (gint)0, (gint)-1);
} /* End select_all */

void gtkeyboard_window_common_setup(GtkWidget *window)
{
     GTKeyboardStyle stylish;

     if(!window)
     {
          fprintf(stderr,"GTKeyboard internal error: ");
          fprintf(stderr,"(gtkeyboard_window_common_setup) ");
          fprintf(stderr,"Uninitialized widget window.\n"); 
          fflush(stderr);
          return;
     } /* End if */

     stylish.mask = 0;          /* Set font and color */

     if(GUI.style)
          stylish.style = GUI.style;
     else 
          stylish.style = gtk_widget_get_default_style();

     SetStyleRecursively(window, (gpointer)&stylish);
} /* End gtkeybard_window_common_setup() */

gint button_press(GtkWidget *widget, GdkEvent *event)
{
     if(event->type == GDK_BUTTON_PRESS)
     {
	  if (event->button.button == RIGHT_MOUSE_BUTTON) 
	  {
	       GdkEventButton *bevent = (GdkEventButton *) event;
               
	       gtk_menu_popup(GTK_MENU(GUI.popup_menu), NULL, NULL, 
			      NULL, NULL, bevent->button, bevent->time);
	       
	       /* Tell calling code that we have handled this event; the buck
		* stops here. 
		*/
	       return TRUE;
	  } /* End if */
	  else
	       return FALSE;
     } /* End if */
     
     /* Tell calling code that we have not handled this event; pass it on. */
     return FALSE;
} /* End button_press() */

/* EOF */

