/*--------------------------------------------------------------*/
/* keybindings.c:  List of key bindings				*/
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University	*/
/*--------------------------------------------------------------*/

/*----------------------------------------------------------------------*/
/*      written by Tim Edwards, 2/27/01                                 */
/*----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>	 /* for tolower(), toupper() */
#include <math.h>

#ifndef XC_WIN32
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#endif

#if !defined(XC_WIN32) || defined(TCL_WRAPPER)
#define  XK_MISCELLANY
#define  XK_LATIN1
#define  XK_XKB_KEYS
#include <X11/keysymdef.h>
#endif

/*----------------------------------------------------------------------*/
/* Local includes							*/
/*----------------------------------------------------------------------*/

#ifdef TCL_WRAPPER 
#include <tk.h>
#endif

#include "xcircuit.h"

/*----------------------------------------------------------------------*/
/* Function prototypes							*/
/*----------------------------------------------------------------------*/

#include "prototypes.h"

/*----------------------------------------------------------------------*/
/* Global variables							*/
/*----------------------------------------------------------------------*/

keybinding *keylist = NULL;

extern Display *dpy;
extern char  _STR[150], _STR2[250];
extern short eventmode;
extern Boolean pressmode;

/*--------------------------------------------------------------*/
/* Key modifiers (convenience definitions)			*/
/* Use Mod2Mask for a "hold" ("press") definition.		*/
/*--------------------------------------------------------------*/

#define ALT (Mod1Mask << 16)
#define CTRL (ControlMask << 16)
#define CAPSLOCK (LockMask << 16)
#define SHIFT (ShiftMask << 16)
#define BUTTON1 (Button1Mask << 16)
#define BUTTON2 (Button2Mask << 16)
#define BUTTON3 (Button3Mask << 16)
#define BUTTON4 (Button4Mask << 16)
#define BUTTON5 (Button5Mask << 16)
#define HOLD (Mod2Mask << 16)

/*--------------------------------------------------------------*/
/* This list declares all the functions which can be bound to   */
/* keys.  It must match the order of the enumeration listed in	*/
/* xcircuit.h!							*/
/*--------------------------------------------------------------*/

static char *function_names[NUM_FUNCTIONS + 1] = {
   "Page", "Justify", "Superscript", "Subscript", "Normalscript",
   "Nextfont", "Boldfont", "Italicfont", "Normalfont", "Underline",
   "Overline", "ISO Encoding", "Halfspace", "Quarterspace",
   "Special", "Tab Stop", "Tab Forward", "Tab Backward",
   "Text Return", "Text Delete", "Text Right", "Text Left",
   "Text Up", "Text Down", "Text Split", 
   "Text Home", "Text End", "Linebreak", "Parameter",
   "Parameterize Point", "Break at Point", "Delete Point", "Insert Point",
   "Append Point", "Next Point", "Attach", "Next Library", "Library Directory",
   "Library Move", "Library Copy", "Library Edit", "Library Delete",
   "Library Duplicate", "Library Hide", "Library Virtual Copy",
   "Page Directory", "Library Pop", "Virtual Copy",
   "Help", "Redraw", "View", "Zoom In", "Zoom Out", "Pan",
   "Double Snap", "Halve Snap", "Write", "Rotate", "Flip X",
   "Flip Y", "Snap", "Snap To",
   "Pop", "Push", "Delete", "Select", "Box", "Arc", "Text",
   "Exchange", "Copy", "Move", "Join", "Unjoin", "Spline", "Edit",
   "Undo", "Redo", "Select Save", "Unselect", "Dashed", "Dotted",
   "Solid", "Prompt", "Dot", "Wire", "Cancel", "Nothing", "Exit",
   "Netlist", "Swap", "Pin Label", "Pin Global", "Info Label", "Graphic",
   "Rescale", "Select Box", "Connectivity", "Continue Copy",
   "Continue Element", "Finish Copy", "Finish Element", "Finish",
   "Cancel Segment", "Sim", "SPICE", "PCB", "SPICE Flat", "Text Delete Param",
   NULL			/* sentinel */
};

/*--------------------------------------------------------------*/
/* Return TRUE if the indicated key (keysym + bit-shifted state)*/
/* is bound to some function.					*/
/*--------------------------------------------------------------*/

Boolean ismacro(int keywstate)
{
   keybinding *ksearch;

   for (ksearch = keylist; ksearch != NULL; ksearch = ksearch->nextbinding)
      if (keywstate == ksearch->keywstate)
	 return True;

   return False;
}

/*--------------------------------------------------------------*/
/* Return the first key binding for the indicated function	*/
/*--------------------------------------------------------------*/

int firstbinding(int function)
{
   keybinding *ksearch;

   for (ksearch = keylist; ksearch != NULL; ksearch = ksearch->nextbinding)
      if (function == ksearch->function)
	 return ksearch->keywstate;

   return -1;
}

/*--------------------------------------------------------------*/
/* Return the nth function bound to the indicated keywstate.	*/
/* Return -1 if no function is bound.				*/
/*--------------------------------------------------------------*/

int boundfunction(int keywstate, int n)
{
   int j;
   keybinding *ksearch;

   j = 0;
   for (ksearch = keylist; ksearch != NULL; ksearch = ksearch->nextbinding)
      if (keywstate == ksearch->keywstate)
	 if (j++ == n)
	    return ksearch->function;

   return -1;
}

/*--------------------------------------------------------------*/
/* Return TRUE if the indicated key (keysym + bit-shifted state)*/
/* is bound to the indicated function (called by name)		*/
/*--------------------------------------------------------------*/

Boolean isbound(int keywstate, int function)
{
   keybinding *ksearch;

   for (ksearch = keylist; ksearch != NULL; ksearch = ksearch->nextbinding)
      if ((keywstate == ksearch->keywstate) && (function == ksearch->function))
	 return True;

   return False;
}

/*--------------------------------------------------------------*/
/* Return TRUE if the key binding (keysym + bit-shifted state)  */
/* is bound to a function name which matches the indicated	*/
/* function name to "length" characters.  If the function name	*/
/* includes a numerical suffix (e.g., "Page 1"), the suffix is	*/
/* converted to an integer and passed back in "retnum".		*/
/*--------------------------------------------------------------*/

Boolean isnbound(int keywstate, int function, short *retnum)
{
   keybinding *ksearch;

   for (ksearch = keylist; ksearch != NULL; ksearch = ksearch->nextbinding) {
      if ((keywstate == ksearch->keywstate) && (function == ksearch->function)) {
	 if (retnum != NULL)
	    *retnum = (ksearch->value);
	 return True;
      }
   }
   return False;
}

/*--------------------------------------------------------------*/
/* Return the string associated with a function, or NULL if the	*/
/* function value is out-of-bounds.				*/
/*--------------------------------------------------------------*/

char *func_to_string(int function)
{
   if ((function < 0) || (function >= NUM_FUNCTIONS)) return NULL;
   return function_names[function];
}

/*--------------------------------------------------------------*/
/* Identify a function with the string version of its name	*/
/*--------------------------------------------------------------*/

int string_to_func(const char *funcstring, short *value)
{
   int i;

   for (i = 0; i < NUM_FUNCTIONS; i++)
   {
      if (function_names[i] == NULL) {
	 Fprintf(stderr, "Error: resolve bindings and function strings!\n");
	 return -1;	  /* should not happen? */
      }
      if (!strcmp(funcstring, function_names[i]))
	 return i;
   }

   /* Check if this string might have a value attached */

   if (value != NULL)
      for (i = 0; i < NUM_FUNCTIONS; i++)
         if (!strncmp(funcstring, function_names[i], strlen(function_names[i]))) {
	    sscanf(funcstring + strlen(function_names[i]), "%hd", value);
	    return i;
	 }

   return -1;
}

/*--------------------------------------------------------------*/
/* Make a key sym from a string representing the key state 	*/
/*--------------------------------------------------------------*/

int string_to_key(const char *keystring)
{
   int ct, keywstate = 0;
   const char *kptr = keystring;

   while(1) {
      if (*kptr == '\0') return -1;
      if (!strncmp(kptr, "XK_", 3))
	 kptr += 3;
      else if (!strncmp(kptr, "Shift_", 6)) {
	 keywstate |= SHIFT;
	 kptr += 6;
      }
      else if (!strncmp(kptr, "Capslock_", 9)) {
	 keywstate |= CAPSLOCK;
	 kptr += 9;
      }
      else if (!strncmp(kptr, "Control_", 8)) {
	 keywstate |= CTRL;
	 kptr += 8;
      }
      else if (!strncmp(kptr, "Alt_", 4)) {
	 keywstate |= ALT;
	 kptr += 4;
      }
      else if (!strncmp(kptr, "Meta_", 5)) {
	 keywstate |= ALT;
	 kptr += 5;
      }
      else if (!strncmp(kptr, "Hold_", 5)) {
	 keywstate |= HOLD;
	 kptr += 5;
      }
      else if (*kptr == '^') {
	 kptr++;
	 ct = (int)tolower(*kptr);
	 keywstate |= CTRL | ct;
	 break;
      }
      else if (*(kptr + 1) == '\0') {
	 if ((*kptr) < 32)
	    keywstate |= CTRL | (int)('A' + (*kptr) - 1);
	 else
	    keywstate |= (int)(*kptr);
	 break;
      }
      else {
         if (!strncmp(kptr, "Button", 6)) {
	    switch (*(kptr + 6)) {
	       case '1': keywstate = (Button1Mask << 16); break;
	       case '2': keywstate = (Button2Mask << 16); break;
	       case '3': keywstate = (Button3Mask << 16); break;
	       case '4': keywstate = (Button4Mask << 16); break;
	       case '5': keywstate = (Button5Mask << 16); break;
	    }
         }
	 else {
	    /* When any modifier keys are used, presence of SHIFT */
	    /* requires that the corresponding key be uppercase,  */
	    /* and lack of SHIFT requires lowercase.  Enforce it  */
	    /* here so that it is not necessary for the user to	  */
	    /* do so.						  */
	    if (*(kptr + 1) == '\0') {
	       if (keywstate & SHIFT)
	          ct = (int)toupper(*kptr);
	       else
	          ct = (int)tolower(*kptr);
	       keywstate |= ct;
	    }
	    else
	       keywstate |= XStringToKeysym(kptr);
	 }
	 break;
      }
   }
   return keywstate;
}

/*--------------------------------------------------------------*/
/* Convert a function into a string representing all of its	*/
/* key bindings, or convert a key into a string representing	*/
/* all of its function bindings.  One of the arguments is	*/
/* valid, and the other should be -1.				*/
/*--------------------------------------------------------------*/

char *binding_to_string(int keywstate, int function)
{
   keybinding *ksearch;
   char *retstr, *tmpstr;
   Bool first = True;

   retstr = (char *)malloc(1);
   retstr[0] = '\0';
   for (ksearch = keylist; ksearch != NULL; ksearch = ksearch->nextbinding) {
      if (function == ksearch->function || keywstate == ksearch->keywstate) {
	 if (function < 0)
	    tmpstr = function_names[ksearch->function];
	 else
	    tmpstr = key_to_string(ksearch->keywstate);
	 if (tmpstr != NULL) {
	    retstr = (char *)realloc(retstr, strlen(retstr) + strlen(tmpstr) + 
		((first) ? 1 : 3));
	    if (!first) strcat(retstr, ", ");
	    strcat(retstr, tmpstr);
	    if (function >= 0) free(tmpstr);
	 }
	 first = False;
      }
   }
   if (retstr[0] == '\0') {
      retstr = (char *)realloc(retstr, 10);
      strcat(retstr, "<unbound>");
   }
   return(retstr);
}

/*--------------------------------------------------------------*/
/* Convert a key sym into a string				*/
/*--------------------------------------------------------------*/

char *key_to_string(int keywstate)
{
   static char hex[17] = "0123456789ABCDEF";
   char *kptr, *str = NULL;
   KeySym ks = keywstate & 0xffff;
   int kmod = keywstate >> 16;

#if defined(XC_WIN32) && defined(TCL_WRAPPER)
   if (ks != NoSymbol) str = XKeysymToString_TkW32(ks);
#else
   if (ks != NoSymbol) str = XKeysymToString(ks);
#endif

   kptr = (char *)malloc(32);
   kptr[0] = '\0';
   if (kmod & Mod1Mask) strcat(kptr, "Alt_");
   if (kmod & Mod2Mask) strcat(kptr, "Hold_");
   if (kmod & ControlMask) strcat(kptr, "Control_");
   if (kmod & LockMask) strcat(kptr, "Capslock_");
   if (kmod & ShiftMask) strcat(kptr, "Shift_");

   if (str != NULL) {
      /* 33 is length of all modifiers concatenated + 1 */
      kptr = (char *)realloc(kptr, strlen(str) + 33);
      strcat(kptr, str);
   }
   else {
      kptr = (char *)realloc(kptr, 40);
      if (kmod & Button1Mask) strcat(kptr, "Button1");
      else if (kmod & Button2Mask) strcat(kptr, "Button2");
      else if (kmod & Button3Mask) strcat(kptr, "Button3");
      else if (kmod & Button4Mask) strcat(kptr, "Button4");
      else if (kmod & Button5Mask) strcat(kptr, "Button5");
      else {
	 kptr[0] = '0';
	 kptr[1] = 'x';
	 kptr[2] = hex[(kmod & 0xf)];
	 kptr[3] = hex[(keywstate & 0xf000) >> 12];
	 kptr[4] = hex[(keywstate & 0x0f00) >>  8];
	 kptr[5] = hex[(keywstate & 0x00f0) >>  4];
	 kptr[6] = hex[(keywstate & 0x000f)      ];
	 kptr[7] = '\0';
      }
   }
   return kptr;
}

/*--------------------------------------------------------------*/
/* Print the bindings for the (polygon) edit functions		*/
/*--------------------------------------------------------------*/

void printeditbindings()
{
   char *tstr;

   _STR2[0] = '\0';

   tstr = key_to_string(firstbinding(XCF_Edit_Break));
   strcat(_STR2, tstr);
   strcat(_STR2, "=");
   strcat(_STR2, func_to_string(XCF_Edit_Break));
   strcat(_STR2, ", ");
   free(tstr);

   tstr = key_to_string(firstbinding(XCF_Edit_Delete));
   strcat(_STR2, tstr);
   strcat(_STR2, "=");
   strcat(_STR2, func_to_string(XCF_Edit_Delete));
   strcat(_STR2, ", ");
   free(tstr);

   tstr = key_to_string(firstbinding(XCF_Edit_Insert));
   strcat(_STR2, tstr);
   strcat(_STR2, "=");
   strcat(_STR2, func_to_string(XCF_Edit_Insert));
   strcat(_STR2, ", ");
   free(tstr);

   tstr = key_to_string(firstbinding(XCF_Edit_Param));
   strcat(_STR2, tstr);
   strcat(_STR2, "=");
   strcat(_STR2, func_to_string(XCF_Edit_Param));
   strcat(_STR2, ", ");
   free(tstr);

   tstr = key_to_string(firstbinding(XCF_Edit_Next));
   strcat(_STR2, tstr);
   strcat(_STR2, "=");
   strcat(_STR2, func_to_string(XCF_Edit_Next));
   free(tstr);

   Wprintf(_STR2);
}

/*--------------------------------------------------------------*/
/* Remove a key binding from the list				*/
/*--------------------------------------------------------------*/

int remove_binding(int keywstate, int function)
{
   keybinding *ksearch, *klast = NULL;

   for (ksearch = keylist; ksearch != NULL; ksearch = ksearch->nextbinding) {
      if ((function == ksearch->function)
		&& (keywstate == ksearch->keywstate)) {
	 if (klast == NULL)
	    keylist = ksearch->nextbinding;
	 else
	    klast->nextbinding = ksearch->nextbinding;
	 free(ksearch);
	 return 0;
      }
      klast = ksearch;
   }
   return -1;
}

/*--------------------------------------------------------------*/
/* Wrapper for remove_binding					*/
/*--------------------------------------------------------------*/

void remove_keybinding(const char *keystring, const char *fstring)
{
   int function = string_to_func(fstring, NULL);
   int keywstate = string_to_key(keystring);

   if ((function < 0) || (remove_binding(keywstate, function) < 0)) {
      sprintf(_STR2, "Key binding \'%s\' to \'%s\' does not exist in list.",
		keystring, fstring);
      Wprintf(_STR2);
   }
}

/*--------------------------------------------------------------*/
/* Add a key binding to the list				*/
/*--------------------------------------------------------------*/

int add_vbinding(int keywstate, int function, short value)
{
   keybinding *newbinding;

   /* If key is already bound to the function, ignore it */

   if (isbound(keywstate, function)) return 1;

   /* Add the new key binding */

   newbinding = (keybinding *)malloc(sizeof(keybinding));
   newbinding->keywstate = keywstate;
   newbinding->function = function;
   newbinding->value = value;
   newbinding->nextbinding = keylist;
   keylist = newbinding;
   return 0;
}

/*--------------------------------------------------------------*/
/* Wrapper function for key binding without any values		*/
/*--------------------------------------------------------------*/

int add_binding(int keywstate, int function)
{
   return add_vbinding(keywstate, function, (short)-1);
}

/*--------------------------------------------------------------*/
/* Wrapper function for key binding with function as string	*/
/*--------------------------------------------------------------*/

int add_keybinding(const char *keystring, const char *fstring)
{
   short value = -1;
   int function = string_to_func(fstring, &value);
   int keywstate = string_to_key(keystring);

   if (function < 0)
      return -1;
   else
      return add_vbinding(keywstate, function, value);
}

/*--------------------------------------------------------------*/
/* Create list of default key bindings.				*/
/* These are conditional upon any bindings set in the startup	*/
/* file .xcircuitrc.						*/
/*--------------------------------------------------------------*/

void default_keybindings()
{
   add_vbinding(XK_1, XCF_Page, 1);
   add_vbinding(XK_2, XCF_Page, 2);
   add_vbinding(XK_3, XCF_Page, 3);
   add_vbinding(XK_4, XCF_Page, 4);
   add_vbinding(XK_5, XCF_Page, 5);
   add_vbinding(XK_6, XCF_Page, 6);
   add_vbinding(XK_7, XCF_Page, 7);
   add_vbinding(XK_8, XCF_Page, 8);
   add_vbinding(XK_9, XCF_Page, 9);
   add_vbinding(XK_0, XCF_Page, 10);

   add_vbinding(XK_KP_1, XCF_Justify, 0);
   add_vbinding(XK_KP_2, XCF_Justify, 1);
   add_vbinding(XK_KP_3, XCF_Justify, 2);
   add_vbinding(XK_KP_4, XCF_Justify, 3);
   add_vbinding(XK_KP_5, XCF_Justify, 4);
   add_vbinding(XK_KP_6, XCF_Justify, 5);
   add_vbinding(XK_KP_7, XCF_Justify, 6);
   add_vbinding(XK_KP_8, XCF_Justify, 7);
   add_vbinding(XK_KP_9, XCF_Justify, 8);

   add_vbinding(XK_KP_End, XCF_Justify, 0);
   add_vbinding(XK_KP_Down, XCF_Justify, 1);
   add_vbinding(XK_KP_Next, XCF_Justify, 2);
   add_vbinding(XK_KP_Left, XCF_Justify, 3);
   add_vbinding(XK_KP_Begin, XCF_Justify, 4);
   add_vbinding(XK_KP_Right, XCF_Justify, 5);
   add_vbinding(XK_KP_Home, XCF_Justify, 6);
   add_vbinding(XK_KP_Up, XCF_Justify, 7);
   add_vbinding(XK_KP_Prior, XCF_Justify, 8);

   add_binding(XK_Delete, XCF_Text_Delete);
   add_binding(XK_Return, XCF_Text_Return);
   add_binding(BUTTON1, XCF_Text_Return);
   add_binding(XK_BackSpace, XCF_Text_Delete);
   add_binding(XK_Left, XCF_Text_Left);
   add_binding(XK_Right, XCF_Text_Right);
   add_binding(XK_Up, XCF_Text_Up);
   add_binding(XK_Down, XCF_Text_Down);
   add_binding(ALT | XK_x, XCF_Text_Split);
   add_binding(XK_Home, XCF_Text_Home);
   add_binding(XK_End, XCF_Text_End);
   add_binding(XK_Tab, XCF_TabForward);
   add_binding(SHIFT | XK_Tab, XCF_TabBackward);
#ifdef XK_ISO_Left_Tab
   add_binding(SHIFT | XK_ISO_Left_Tab, XCF_TabBackward);
#endif
   add_binding(ALT | XK_Tab, XCF_TabStop);
   add_binding(XK_KP_Add, XCF_Superscript);
   add_binding(XK_KP_Subtract, XCF_Subscript);
   add_binding(XK_KP_Enter, XCF_Normalscript);
   add_binding(ALT | XK_f, XCF_Nextfont);
   add_binding(ALT | XK_b, XCF_Boldfont);
   add_binding(ALT | XK_i, XCF_Italicfont);
   add_binding(ALT | XK_n, XCF_Normalfont);
   add_binding(ALT | XK_u, XCF_Underline);
   add_binding(ALT | XK_o, XCF_Overline);
   add_binding(ALT | XK_e, XCF_ISO_Encoding);
   add_binding(ALT | XK_Return, XCF_Linebreak);
   add_binding(ALT | XK_h, XCF_Halfspace);
   add_binding(ALT | XK_q, XCF_Quarterspace);
   add_binding(XK_backslash, XCF_Special);
   add_binding(ALT | XK_c, XCF_Special);
   add_binding(ALT | XK_p, XCF_Parameter);
   add_binding(XK_p, XCF_Edit_Param);
   add_binding(XK_x, XCF_Edit_Break);
   add_binding(XK_d, XCF_Edit_Delete);
   add_binding(XK_Delete, XCF_Edit_Delete);
   add_binding(XK_i, XCF_Edit_Insert);
   add_binding(XK_Insert, XCF_Edit_Insert);
   add_binding(XK_e, XCF_Edit_Next);
   add_binding(BUTTON1, XCF_Edit_Next);
   add_binding(XK_A, XCF_Attach);
   add_binding(XK_V, XCF_Virtual);
   add_binding(XK_l, XCF_Next_Library);
   add_binding(XK_L, XCF_Library_Directory);
   add_binding(XK_c, XCF_Library_Copy);
   add_binding(XK_E, XCF_Library_Edit);
   add_binding(XK_D, XCF_Library_Delete);
   add_binding(XK_C, XCF_Library_Duplicate);
   add_binding(XK_H, XCF_Library_Hide);
   add_binding(XK_V, XCF_Library_Virtual);
   add_binding(XK_M, XCF_Library_Move);
   add_binding(XK_P, XCF_Page_Directory);
   add_binding(XK_less, XCF_Library_Pop);
   add_binding(HOLD | BUTTON1, XCF_Library_Pop);
   add_binding(XK_h, XCF_Help);
   add_binding(XK_question, XCF_Help);
   add_binding(XK_space, XCF_Redraw);
   add_binding(XK_Redo, XCF_Redraw);
   add_binding(XK_Undo, XCF_Redraw);
   add_binding(XK_Home, XCF_View);
   add_binding(XK_v, XCF_View);
   add_binding(XK_Z, XCF_Zoom_In);
   add_binding(XK_z, XCF_Zoom_Out);
   add_vbinding(XK_p, XCF_Pan, 0);
   add_binding(XK_plus, XCF_Double_Snap);
   add_binding(XK_minus, XCF_Halve_Snap);
   add_vbinding(XK_Left, XCF_Pan, 1);
   add_vbinding(XK_Right, XCF_Pan, 2);
   add_vbinding(XK_Up, XCF_Pan, 3);
   add_vbinding(XK_Down, XCF_Pan, 4);
   add_binding(XK_W, XCF_Write);
   add_vbinding(XK_O, XCF_Rotate, -5);
   add_vbinding(XK_o, XCF_Rotate, 5);
   add_vbinding(XK_R, XCF_Rotate, -15);
   add_vbinding(XK_r, XCF_Rotate, 15);
   add_binding(XK_f, XCF_Flip_X);
   add_binding(XK_F, XCF_Flip_Y);
   add_binding(XK_S, XCF_Snap);
   add_binding(XK_less, XCF_Pop);
   add_binding(XK_greater, XCF_Push);
   add_binding(XK_Delete, XCF_Delete);
   add_binding(XK_d, XCF_Delete);
   add_binding(XK_F19, XCF_Select);
   add_binding(XK_b, XCF_Box);
   add_binding(XK_a, XCF_Arc);
   add_binding(XK_t, XCF_Text);
   add_binding(XK_X, XCF_Exchange);
   add_binding(XK_c, XCF_Copy);
   add_binding(XK_j, XCF_Join);
   add_binding(XK_J, XCF_Unjoin);
   add_binding(XK_s, XCF_Spline);
   add_binding(XK_e, XCF_Edit);
   add_binding(XK_u, XCF_Undo);
   add_binding(XK_U, XCF_Redo);
   add_binding(XK_M, XCF_Select_Save);
   add_binding(XK_m, XCF_Select_Save);
   add_binding(XK_x, XCF_Unselect);
   add_binding(XK_bar, XCF_Dashed);
   add_binding(XK_colon, XCF_Dotted);
   add_binding(XK_underscore, XCF_Solid);
   add_binding(XK_percent, XCF_Prompt);
   add_binding(XK_period, XCF_Dot);
   add_binding(BUTTON1, XCF_Wire);
   add_binding(XK_w, XCF_Wire);
   add_binding(CTRL | ALT | XK_q, XCF_Exit);
   add_binding(HOLD | BUTTON1, XCF_Move);
   add_binding(BUTTON1, XCF_Continue_Element);
   add_binding(BUTTON1, XCF_Continue_Copy);
   add_binding(BUTTON1, XCF_Finish);
   add_binding(XK_Escape, XCF_Cancel);
   add_binding(ALT | XK_r, XCF_Rescale);
   add_binding(ALT | XK_s, XCF_SnapTo);
   add_binding(ALT | XK_q, XCF_Netlist);
   add_binding(XK_slash, XCF_Swap);
   add_binding(XK_T, XCF_Pin_Label);
   add_binding(XK_G, XCF_Pin_Global);
   add_binding(XK_I, XCF_Info_Label);
   add_binding(ALT | XK_w, XCF_Connectivity);

/* These are for test purposes only.  Menu selection is	*/
/* preferred.						*/

/* add_binding(ALT | XK_d, XCF_Sim);		*/
/* add_binding(ALT | XK_a, XCF_SPICE);		*/
/* add_binding(ALT | XK_f, XCF_SPICEflat);	*/
/* add_binding(ALT | XK_p, XCF_PCB);		*/

   /* 2-button vs. 3-button mouse bindings (set with -2	*/
   /* commandline option; 3-button bindings default)	*/

   if (pressmode) {
      add_binding(BUTTON3, XCF_Text_Return);
      add_binding(BUTTON3, XCF_Select);
      add_binding(HOLD | BUTTON3, XCF_SelectBox);
      add_binding(BUTTON3, XCF_Finish_Element);
      add_binding(BUTTON3, XCF_Finish_Copy);

      add_binding(XK_BackSpace, XCF_Cancel_Last);
      add_binding(XK_BackSpace, XCF_Cancel);
   }
   else {
      add_binding(BUTTON2, XCF_Text_Return);
      add_binding(SHIFT | BUTTON1, XCF_Text_Return);
      add_binding(BUTTON2, XCF_Select);
      add_binding(SHIFT | BUTTON1, XCF_Select);
      add_binding(HOLD | BUTTON2, XCF_SelectBox);
      add_binding(SHIFT | HOLD | BUTTON1, XCF_SelectBox);
      add_binding(BUTTON2, XCF_Finish_Element);
      add_binding(SHIFT | BUTTON1, XCF_Finish_Element);
      add_binding(BUTTON2, XCF_Finish_Copy);
      add_binding(SHIFT | BUTTON1, XCF_Finish_Copy);
      add_binding(BUTTON3, XCF_Cancel_Last);
      add_binding(BUTTON3, XCF_Cancel);
   }
}

#ifndef TCL_WRAPPER
/*----------------------------------------------*/
/* Mode-setting rebindings (non-Tcl version)	*/
/*----------------------------------------------*/

static int button1mode = XCF_Wire;

/*--------------------------------------------------------------*/
/* Re-bind BUTTON1 to the indicated function and optional value */
/*--------------------------------------------------------------*/

void mode_rebinding(int newmode, int newvalue)
{
   remove_binding(BUTTON1, button1mode);
   add_vbinding(BUTTON1, newmode, (short)newvalue);
   button1mode = newmode;
   toolcursor(newmode);
}

/*--------------------------------------------------------------*/
/* Execute the function associated with the indicated BUTTON1	*/
/* mode, but return the keybinding to its previous state.	*/
/*--------------------------------------------------------------*/

void mode_tempbinding(int newmode, int newvalue)
{
   short saveval;
   XPoint cpos;

   if (isnbound(BUTTON1, button1mode, &saveval)) {
      remove_binding(BUTTON1, button1mode);
      add_vbinding(BUTTON1, newmode, (short)newvalue);
      cpos = UGetCursor();
      eventdispatch(BUTTON1, (int)cpos.x, (int)cpos.y);
      remove_binding(BUTTON1, newmode);
      add_vbinding(BUTTON1, button1mode, saveval);
   }
   else
      fprintf(stderr, "Error: No such button1 binding %s\n",
		func_to_string(button1mode));
}

#endif /* TCL_WRAPPER */

#undef ALT
#undef CTRL
#undef CAPSLOCK
#undef SHIFT
#undef BUTTON1
#undef BUTTON2
#undef BUTTON3

/*--------------------------------------------------------------*/
