/* kmanip.c
 * Written by David Allen <s2mdalle@titan.vcu.edu>
 * http://opop.nols.com/
 *
 * This file is distributed under the terms of the GNU General Public
 * License.  See COPYING or http://www.gnu.org/ for more details.
 *
 * Routines for manipulation of keysyms, keycodes, keyboard remapping
 * and such.  Here is where the vast majority of keyboard remapping stuff is
 * done.
 */
/* 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.
 */

#define KMANIP_C

#include "master.h"

#ifndef PRINT_STATUS_MESSAGES
#  define PRINT_STATUS_MESSAGES
#endif /* PRINT_STATUS_MESSAGES */

/*************** FUNCTIONS ****************************/
#if 0
void remap_at_exit_dialog(GtkWidget *emitter, gpointer data)
{

} /* End remap_at_exit_dialog() */

void remap_default_at_exit(GtkWidget *emitter, gpointer data)
{

} /* End remap_default_at_exit */
#endif

/* Wrapper for gtkeyboard_remap_keyboard() that destroys popup windows
 * requesting the action.
 */
void remap_keyboard_wrapper(GtkWidget *emitter, gpointer data)
{
     if(GTK_IS_DIALOG(emitter->parent->parent))
	  gtk_widget_destroy(emitter->parent->parent);
     else if(GTK_IS_DIALOG(emitter->parent->parent->parent))
	  gtk_widget_destroy(emitter->parent->parent->parent);
     
     return;
} /* End remap_keyboard_wrapper() */

/* Remaps the keyboard completely to whatever is in options.keyboard */
void gtkeyboard_remap_keyboard(GtkWidget *emitter, gpointer data)
{
     GtkWidget *e = emitter;
     KEY *key;
     int x, y;
     char error_buffer[1024 * 5];
     KeySym symarr[3];
     int symbols_to_remap;
     XModifierKeymap *mmap = (XModifierKeymap *)NULL;

     if(emitter)
     {
	  while(e && GTK_IS_WIDGET(e) && !GTK_IS_WINDOW(e))
	       e = e->parent;
     } /* End if */

     if(!options.keyboard)
     {
	  sprintf(error_buffer,
		  "Error:  Can't remap keyboard:\n");
	  strcat(error_buffer,
		 "No keyboard definition loaded.\n");
	  annoying_popup(error_buffer);
	  return;
     } /* End if */

     gtkeyboard_message(3, "REMAPPING KEYBOARD...\n",
                        "Checking to see if your keyboard definition is ",
                        "complete...\n");

     if(ModmapTable_is_empty(options.keyboard->modmap))
     {
          char errbuf[1024];
          sprintf(errbuf, "%s%s%s%s%s%s%s",
                  "This keyboard definition doesn't seem to specify which\n",
                  "modifier keys exist, and what definitions they should\n",
                  "use.  Since your keyboard may be in a very bad state\n",
                  "if I remap the modifiers to nothing, I'm not going to\n",
                  "do that.  PLEASE NOTE:  some keys may not work properly\n",
                  "in combination with others.  Please correct the lack of\n",
                  "modifiers present in the keyboard definition file.\n");

          /* Destroy the dialog window */
          if(e && GTK_IS_WIDGET(e))
               gtk_widget_destroy(e);
          
          annoying_popup(errbuf);
          return;
     } /* End if */

     /* We have to do a dry run through all of the keys to check and see if
      * they have valid KeyCodes specified.  We don't want to find out half
      * way through the mapping that we can't finish the mapping.
      * Report the error and return if there is even one key without a valid
      * KeyCode.
      */
     for(x=0; x<MAXIMUM_ROWS; x++)
     {
	  for(y=0; y<options.keyboard->row_values[x]; y++)
	  {
	       key = gtkeyboard_keyboard_get_key(options.keyboard, x, y);
	       
	       if(!key->code)
	       {
		    sprintf(error_buffer,"Error:  Cannot remap keyboard:\n%s",
			    options.keyboard->name); 
		    strcat(error_buffer,"Some keycodes are missing.\n");

		    if(e && GTK_IS_WIDGET(e))
			 gtk_widget_destroy(e);

                    gtkeyboard_destroy_key(key);

		    annoying_popup(error_buffer);
		    return;
	       } /* End if */

	       key = gtkeyboard_destroy_key(key);
	  } /* End for */
     } /* End for */

     gtkeyboard_message(1, "Unmapping old keyset...\n");

     /* Loop through all of the keys again.  We couldn't do this in the
      * last loop, because we didn't know if it was going to choke halfway
      * through, leaving us with a totally hosed keyboard layout.  Also, 
      * the keyboards may not have the same number of keys.
      * 
      * This go around, you just want to map each key mentioned by the *OLD*
      * layout setting to { NoSymbol, NoSymbol, NoSymol } so when we're
      * done, we don't leave any spare mappings lying around.  (These can
      * really fsck up X and make the right keys generate the wrong sequences)
      * Oh, and don't error message out of this, since it MIGHT be needed,
      * but not necessarily.
      */
     if(!options.old_keyboard || options.old_keyboard == NO_KEYBOARD)
     {
          /* Oh, well, nevermind then.  :) There isn't an old keyboard
           * definition.
           */
     } /* End if */
     else 
     {
          /* Loop through each row, and each key on each row. */
          for(x=0; x<MAXIMUM_ROWS; x++)
          {
               for(y=0; y<options.old_keyboard->row_values[x]; y++)
               {
                    key = gtkeyboard_keyboard_get_key(options.old_keyboard, 
                                                      x, y);
                    
                    /* gtkeyboard_print_key(key); */

                    if(!key || !key->code)
                    {
                         /* Oh well...this should be reported though because
                          * it's a hole in the keyboard definition file. 
                          */
                         fprintf(stderr,"GTKeyboard Warning: ");
                         fprintf(stderr,"Undefined keycode in old key %d %d\n",
                                 x, y);
                         fflush(stderr);
                         continue;
                    } /* End if */
                    else
                    {
                         symarr[0] = NoSymbol;
                         symarr[1] = NoSymbol;
                         symarr[2] = NoSymbol;
                         
                         /* Remap the old keycode in this slot to all 
                          * NoSymbols
                          */
                         XChangeKeyboardMapping(GDK_DISPLAY(), key->code, 
                                                3,
                                                &symarr[0], 1);
                    } /* End else */

                    /* Dispose of the current key */
                    key = gtkeyboard_destroy_key(key);
               } /* End for */
          } /* End for */
     } /* End else */

     gtkeyboard_message(1, "Mapping keys...\n");

     /* Now do the mapping for real.  Just map each keycode as the keyboard
      * specifies to the set: lower_case, upper_case, alt_gr
      */
     for(x=0; x<MAXIMUM_ROWS; x++)
     {
	  for(y=0; y<options.keyboard->row_values[x]; y++)
	  {
	       /* Grab our current key */
	       key = gtkeyboard_keyboard_get_key(options.keyboard, x, y);

	       symarr[0] = key->lower_case;
	       symarr[1] = key->upper_case;
	       symarr[2] = key->alt_gr;

	       if(symarr[2] == NoSymbol)
		    symbols_to_remap = 2;
	       else symbols_to_remap = 3;

	       /* Remap one KeyCode using symarr, map it to symbols_to_remap
		* different symbols.
		*/
	       XChangeKeyboardMapping(GDK_DISPLAY(), key->code, 
				      symbols_to_remap, &symarr[0], 1);  
	  } /* End for */
     } /* End for */



     gtkeyboard_message(1, "Mapping modifiers...\n");

     /* FIXME:  These should have permanent homes, not be inserted as
      * an afterthought.
      */
     ModmapTable_insert_first_empty_slot(options.keyboard->modmap,
                                         XKeysymToKeycode(GDK_DISPLAY(),
                                                          XK_Num_Lock));
     ModmapTable_insert_first_empty_slot(options.keyboard->modmap,
                                         XKeysymToKeycode(GDK_DISPLAY(),
                                                          XK_Scroll_Lock));
     
     mmap = ModmapTable_to_XModifierKeymap(options.keyboard->modmap);

     if(set_modmap(mmap))
     {
          gtkeyboard_message(1, "Modifier mapping successful!\n");
     } /* End if */
     else
     {
          gtkeyboard_message(1, "Modifier mapping failed!\n");
          gtkeyboard_message(1, "WARNING: Your keyboard may be in a\n");
          gtkeyboard_message(1, "bad state until you remap to another\n");
          gtkeyboard_message(1, "configuration.\n");
     } /* End else */

     /* Update before exit */
     XFlush(GDK_DISPLAY());

     if(e && GTK_IS_WIDGET(e))
	  gtk_widget_destroy(e);

     if(options.keyboard->name)
	  sprintf(error_buffer,"Remapping to\n%s\nSuccessful!\n",
		  options.keyboard->name);
     else
	  sprintf(error_buffer,"Remapping successful!\n");

     gtkeyboard_message(1, error_buffer);
} /* End gtkeyboard_remap_keyboard() */





