2011-02-23 22:14:30 -05:00
/* X-Chat
* Copyright ( C ) 1998 Peter Zelezny .
*
* 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
2012-12-23 14:36:54 -05:00
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA
2011-02-23 22:14:30 -05:00
*/
# include <stdio.h>
# include <stdlib.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <string.h>
# include <fcntl.h>
# include <ctype.h>
2012-07-21 08:26:19 -04:00
# ifdef WIN32
# include <io.h>
# else
2011-12-11 11:34:02 -05:00
# include <unistd.h>
# endif
2013-04-23 15:46:32 -04:00
# define GLIB_DISABLE_DEPRECATION_WARNINGS
2011-02-23 22:14:30 -05:00
# include "fe-gtk.h"
2012-10-24 15:33:02 -04:00
# include "../common/hexchat.h"
# include "../common/hexchatc.h"
2011-02-23 22:14:30 -05:00
# include "../common/cfgfiles.h"
# include "../common/fe.h"
# include "../common/userlist.h"
# include "../common/outbound.h"
# include "../common/util.h"
# include "../common/text.h"
# include "../common/plugin.h"
2012-10-30 17:13:40 -04:00
# include "../common/typedef.h"
2011-02-23 22:14:30 -05:00
# include <gdk/gdkkeysyms.h>
# include "gtkutil.h"
# include "menu.h"
# include "xtext.h"
# include "palette.h"
# include "maingui.h"
# include "textgui.h"
# include "fkeys.h"
static void replace_handle ( GtkWidget * wid ) ;
void key_action_tab_clean ( void ) ;
/***************** Key Binding Code ******************/
/* NOTES:
To add a new action :
1 ) inc KEY_MAX_ACTIONS
2 ) write the function at the bottom of this file ( with all the others )
FIXME : Write about calling and returning
3 ) Add it to key_actions
- - AGL
*/
/* Remember that the *number* of actions is this *plus* 1 --AGL */
# define KEY_MAX_ACTIONS 14
struct key_binding
{
2013-10-10 14:49:37 -04:00
guint keyval ; /* keyval from gdk */
GdkModifierType mod ; /* Modifier, always ran through key_modifier_get_valid() */
2011-02-23 22:14:30 -05:00
int action ; /* Index into key_actions */
char * data1 , * data2 ; /* Pointers to strings, these must be freed */
} ;
struct key_action
{
int ( * handler ) ( GtkWidget * wid , GdkEventKey * evt , char * d1 , char * d2 ,
struct session * sess ) ;
char * name ;
char * help ;
} ;
struct gcomp_data
{
char data [ CHANLEN ] ;
int elen ;
} ;
2014-01-26 06:30:33 -05:00
static int key_load_kbs ( ) ;
2013-10-10 14:49:37 -04:00
static int key_save_kbs ( ) ;
2011-02-23 22:14:30 -05:00
static int key_action_handle_command ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 ,
struct session * sess ) ;
static int key_action_page_switch ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 , struct session * sess ) ;
int key_action_insert ( GtkWidget * wid , GdkEventKey * evt , char * d1 , char * d2 ,
struct session * sess ) ;
static int key_action_scroll_page ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 , struct session * sess ) ;
static int key_action_set_buffer ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 , struct session * sess ) ;
static int key_action_history_up ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 , struct session * sess ) ;
static int key_action_history_down ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 , struct session * sess ) ;
static int key_action_tab_comp ( GtkWidget * wid , GdkEventKey * evt , char * d1 ,
char * d2 , struct session * sess ) ;
static int key_action_comp_chng ( GtkWidget * wid , GdkEventKey * evt , char * d1 ,
char * d2 , struct session * sess ) ;
static int key_action_replace ( GtkWidget * wid , GdkEventKey * evt , char * d1 ,
char * d2 , struct session * sess ) ;
static int key_action_move_tab_left ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 ,
struct session * sess ) ;
static int key_action_move_tab_right ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 ,
struct session * sess ) ;
static int key_action_move_tab_family_left ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 ,
struct session * sess ) ;
static int key_action_move_tab_family_right ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 ,
struct session * sess ) ;
static int key_action_put_history ( GtkWidget * wid , GdkEventKey * evt ,
char * d1 , char * d2 ,
struct session * sess ) ;
2013-10-10 14:49:37 -04:00
static GSList * keybind_list = NULL ;
2011-02-23 22:14:30 -05:00
static const struct key_action key_actions [ KEY_MAX_ACTIONS + 1 ] = {
{ key_action_handle_command , " Run Command " ,
2013-04-04 11:11:05 -04:00
N_ ( " The \002 Run Command \002 action runs the data in Data 1 as if it had been typed into the entry box where you pressed the key sequence. Thus it can contain text (which will be sent to the channel/person), commands or user commands. When run all \002 \\ n \002 characters in Data 1 are used to deliminate separate commands so it is possible to run more than one command. If you want a \002 \\ \002 in the actual text run then enter \002 \\ \\ \002 " ) } ,
2011-02-23 22:14:30 -05:00
{ key_action_page_switch , " Change Page " ,
2012-12-25 00:33:57 -05:00
N_ ( " The \002 Change Page \002 command switches between pages in the notebook. Set Data 1 to the page you want to switch to. If Data 2 is set to anything then the switch will be relative to the current position. Set Data 1 to auto to switch to the page with the most recent and important activity (queries first, then channels with hilight, channels with dialogue, channels with other data) " ) } ,
2011-02-23 22:14:30 -05:00
{ key_action_insert , " Insert in Buffer " ,
N_ ( " The \002 Insert in Buffer \002 command will insert the contents of Data 1 into the entry where the key sequence was pressed at the current cursor position " ) } ,
{ key_action_scroll_page , " Scroll Page " ,
2013-08-01 14:55:22 -04:00
N_ ( " The \002 Scroll Page \002 command scrolls the text widget up or down one page or one line. Set Data 1 to either Top, Bottom, Up, Down, +1 or -1. " ) } ,
2011-02-23 22:14:30 -05:00
{ key_action_set_buffer , " Set Buffer " ,
N_ ( " The \002 Set Buffer \002 command sets the entry where the key sequence was entered to the contents of Data 1 " ) } ,
{ key_action_history_up , " Last Command " ,
N_ ( " The \002 Last Command \002 command sets the entry to contain the last command entered - the same as pressing up in a shell " ) } ,
{ key_action_history_down , " Next Command " ,
N_ ( " The \002 Next Command \002 command sets the entry to contain the next command entered - the same as pressing down in a shell " ) } ,
{ key_action_tab_comp , " Complete nick/command " ,
N_ ( " This command changes the text in the entry to finish an incomplete nickname or command. If Data 1 is set then double-tabbing in a string will select the last nick, not the next " ) } ,
{ key_action_comp_chng , " Change Selected Nick " ,
N_ ( " This command scrolls up and down through the list of nicks. If Data 1 is set to anything it will scroll up, else it scrolls down " ) } ,
{ key_action_replace , " Check For Replace " ,
N_ ( " This command checks the last word entered in the entry against the replace list and replaces it if it finds a match " ) } ,
{ key_action_move_tab_left , " Move front tab left " ,
N_ ( " This command moves the front tab left by one " ) } ,
{ key_action_move_tab_right , " Move front tab right " ,
N_ ( " This command moves the front tab right by one " ) } ,
{ key_action_move_tab_family_left , " Move tab family left " ,
N_ ( " This command moves the current tab family to the left " ) } ,
{ key_action_move_tab_family_right , " Move tab family right " ,
N_ ( " This command moves the current tab family to the right " ) } ,
{ key_action_put_history , " Push input line into history " ,
N_ ( " Push input line into history but doesn't send to server " ) } ,
} ;
2014-01-26 06:30:33 -05:00
# define default_kb_cfg \
2014-05-28 01:39:45 -04:00
" ACCEL=<Primary>Page_Up \n Change Page \n D1:-1 \n D2:Relative \n \n " \
" ACCEL=<Primary>Page_Down \n Change Page \n D1:1 \n D2:Relative \n \n " \
" ACCEL=<Alt>9 \n Change Page \n D1:9 \n D2! \n \n " \
" ACCEL=<Alt>8 \n Change Page \n D1:8 \n D2! \n \n " \
" ACCEL=<Alt>7 \n Change Page \n D1:7 \n D2! \n \n " \
" ACCEL=<Alt>6 \n Change Page \n D1:6 \n D2! \n \n " \
" ACCEL=<Alt>5 \n Change Page \n D1:5 \n D2! \n \n " \
" ACCEL=<Alt>4 \n Change Page \n D1:4 \n D2! \n \n " \
" ACCEL=<Alt>3 \n Change Page \n D1:3 \n D2! \n \n " \
" ACCEL=<Alt>2 \n Change Page \n D1:2 \n D2! \n \n " \
" ACCEL=<Alt>1 \n Change Page \n D1:1 \n D2! \n \n " \
" ACCEL=<Alt>grave \n Change Page \n D1:auto \n D2! \n \n " \
" ACCEL=<Primary>o \n Insert in Buffer \n D1: \n D2! \n \n " \
" ACCEL=<Primary>b \n Insert in Buffer \n D1: \n D2! \n \n " \
" ACCEL=<Primary>k \n Insert in Buffer \n D1: \n D2! \n \n " \
" ACCEL=<Primary>i \n Insert in Buffer \n D1: \n D2! \n \n " \
" ACCEL=<Primary>u \n Insert in Buffer \n D1: \n D2! \n \n " \
" ACCEL=<Shift>Page_Down \n Change Selected Nick \n D1! \n D2! \n \n " \
" ACCEL=<Shift>Page_Up \n Change Selected Nick \n D1:Up \n D2! \n \n " \
" ACCEL=Page_Down \n Scroll Page \n D1:Down \n D2! \n \n " \
" ACCEL=<Primary>Home \n Scroll Page \n D1:Top \n D2! \n \n " \
" ACCEL=<Primary>End \n Scroll Page \n D1:Bottom \n D2! \n \n " \
" ACCEL=Page_Up \n Scroll Page \n D1:Up \n D2! \n \n " \
" ACCEL=<Shift>Down \n Scroll Page \n D1:+1 \n D2! \n \n " \
" ACCEL=<Shift>Up \n Scroll Page \n D1:-1 \n D2! \n \n " \
" ACCEL=Down \n Next Command \n D1! \n D2! \n \n " \
" ACCEL=Up \n Last Command \n D1! \n D2! \n \n " \
" ACCEL=Tab \n Complete nick/command \n D1! \n D2! \n \n " \
2015-01-26 17:53:33 -05:00
" ACCEL=<Shift>ISO_Left_Tab \n Complete nick/command \n D1:Previous \n D2! \n \n " \
2014-05-28 01:39:45 -04:00
" ACCEL=space \n Check For Replace \n D1! \n D2! \n \n " \
" ACCEL=Return \n Check For Replace \n D1! \n D2! \n \n " \
" ACCEL=KP_Enter \n Check For Replace \n D1! \n D2! \n \n " \
" ACCEL=<Primary>Tab \n Complete nick/command \n D1:Up \n D2! \n \n " \
" ACCEL=<Alt>Left \n Move front tab left \n D1! \n D2! \n \n " \
" ACCEL=<Alt>Right \n Move front tab right \n D1! \n D2! \n \n " \
" ACCEL=<Primary><Shift>Page_Up \n Move tab family left \n D1! \n D2! \n \n " \
" ACCEL=<Primary><Shift>Page_Down \n Move tab family right \n D1! \n D2! \n \n " \
" ACCEL=F9 \n Run Command \n D1:/GUI MENU TOGGLE \n D2! \n \n "
2014-01-26 06:30:33 -05:00
2011-02-23 22:14:30 -05:00
void
key_init ( )
{
2014-01-26 06:30:33 -05:00
if ( key_load_kbs ( ) = = 1 )
2011-02-23 22:14:30 -05:00
{
2014-01-26 06:30:33 -05:00
fe_message ( _ ( " There was an error loading key "
2011-02-23 22:14:30 -05:00
" bindings configuration " ) , FE_MSG_ERROR ) ;
}
}
2013-10-10 14:49:37 -04:00
static inline int
key_get_action_from_string ( char * text )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
int i ;
for ( i = 0 ; i < KEY_MAX_ACTIONS + 1 ; i + + )
{
if ( strcmp ( key_actions [ i ] . name , text ) = = 0 )
{
return i ;
}
}
return 0 ;
}
static void
key_free ( gpointer data )
{
struct key_binding * kb = ( struct key_binding * ) data ;
g_return_if_fail ( kb ! = NULL ) ;
2014-12-09 00:24:59 -05:00
g_free ( kb - > data1 ) ;
g_free ( kb - > data2 ) ;
2013-10-10 14:49:37 -04:00
g_free ( kb ) ;
2011-02-23 22:14:30 -05:00
}
/* Ok, here are the NOTES
key_handle_key_press now handles all the key presses and history_keypress is
now defunct . It goes thru the linked list keys_root and finds a matching
key . It runs the action func and switches on these values :
0 ) Return
1 ) Find next
2 ) stop signal and return
* history_keypress is now dead ( and gone )
* key_handle_key_press now takes its role
* All the possible actions are in a struct called key_actions ( in fkeys . c )
* it is made of { function , name , desc }
* key bindings can pass 2 * text * strings to the handler . If more options are nee
ded a format can be put on one of these options
* key actions are passed {
the entry widget
the Gdk event
data 1
data 2
session struct
}
* key bindings are stored in a linked list of key_binding structs
* which looks like {
2013-10-10 14:49:37 -04:00
guint keyval ; GDK keynumber
2011-02-23 22:14:30 -05:00
int action ; Index into key_actions
2013-10-10 14:49:37 -04:00
GdkModiferType mod ; modifier , only ones from key_modifer_get_valid ( )
2011-02-23 22:14:30 -05:00
char * data1 , * data2 ; Pointers to strings , these must be freed
struct key_binding * next ;
}
* remember that is ( data1 | | data2 ) ! = NULL then they need to be free ( ) ' ed
- - AGL
*/
2013-10-10 14:49:37 -04:00
static inline GdkModifierType
key_modifier_get_valid ( GdkModifierType mod )
{
2014-05-11 04:29:06 -04:00
GdkModifierType ret ;
# ifdef __APPLE__
ret = mod & ( GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK ) ;
# else
2013-10-10 14:49:37 -04:00
/* These masks work on both Windows and Unix */
2014-05-11 04:29:06 -04:00
ret = mod & ( GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK ) ;
# endif
return ret ;
2013-10-10 14:49:37 -04:00
}
2011-02-23 22:14:30 -05:00
gboolean
key_handle_key_press ( GtkWidget * wid , GdkEventKey * evt , session * sess )
{
2013-10-10 14:49:37 -04:00
struct key_binding * kb ;
int n ;
2011-02-23 22:14:30 -05:00
GSList * list ;
/* where did this event come from? */
list = sess_list ;
while ( list )
{
sess = list - > data ;
if ( sess - > gui - > input_box = = wid )
{
if ( sess - > gui - > is_tab )
sess = current_tab ;
break ;
}
list = list - > next ;
}
if ( ! list )
return FALSE ;
current_sess = sess ;
2015-02-01 16:37:27 -05:00
if ( plugin_emit_keypress ( sess , evt - > state , evt - > keyval , gdk_keyval_to_unicode ( evt - > keyval ) ) )
2011-02-23 22:14:30 -05:00
return 1 ;
/* maybe the plugin closed this tab? */
if ( ! is_session ( sess ) )
return 1 ;
2013-10-10 14:49:37 -04:00
list = keybind_list ;
while ( list )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
kb = ( struct key_binding * ) list - > data ;
if ( kb - > keyval = = evt - > keyval & & kb - > mod = = key_modifier_get_valid ( evt - > state ) )
2011-02-23 22:14:30 -05:00
{
if ( kb - > action < 0 | | kb - > action > KEY_MAX_ACTIONS )
return 0 ;
/* Run the function */
n = key_actions [ kb - > action ] . handler ( wid , evt , kb - > data1 ,
kb - > data2 , sess ) ;
switch ( n )
{
case 0 :
return 1 ;
case 2 :
g_signal_stop_emission_by_name ( G_OBJECT ( wid ) ,
" key_press_event " ) ;
return 1 ;
}
}
2013-10-10 14:49:37 -04:00
list = g_slist_next ( list ) ;
2011-02-23 22:14:30 -05:00
}
2013-10-10 14:49:37 -04:00
switch ( evt - > keyval )
2011-02-23 22:14:30 -05:00
{
2013-09-15 03:21:56 -04:00
case GDK_KEY_space :
2011-02-23 22:14:30 -05:00
key_action_tab_clean ( ) ;
break ;
}
return 0 ;
}
2013-10-10 14:49:37 -04:00
/* ***** GUI code here ******************* */
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
enum
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
KEY_COLUMN ,
2014-02-16 11:08:32 -05:00
ACCEL_COLUMN ,
2013-10-10 14:49:37 -04:00
ACTION_COLUMN ,
D1_COLUMN ,
D2_COLUMN ,
N_COLUMNS
} ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
static GtkWidget * key_dialog = NULL ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
static inline GtkTreeModel *
get_store ( void )
{
return gtk_tree_view_get_model ( g_object_get_data ( G_OBJECT ( key_dialog ) , " view " ) ) ;
}
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
static void
key_dialog_print_text ( GtkXText * xtext , char * text )
{
unsigned int old = prefs . hex_stamp_text ;
prefs . hex_stamp_text = 0 ; /* temporarily disable stamps */
gtk_xtext_clear ( GTK_XTEXT ( xtext ) - > buffer , 0 ) ;
PrintTextRaw ( GTK_XTEXT ( xtext ) - > buffer , text , 0 , 0 ) ;
prefs . hex_stamp_text = old ;
}
2011-02-23 22:14:30 -05:00
static void
2013-10-10 14:49:37 -04:00
key_dialog_set_key ( GtkCellRendererAccel * accel , gchar * pathstr , guint accel_key ,
GdkModifierType accel_mods , guint hardware_keycode , gpointer userdata )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
GtkTreeModel * model = get_store ( ) ;
GtkTreePath * path = gtk_tree_path_new_from_string ( pathstr ) ;
GtkTreeIter iter ;
2014-02-16 11:08:32 -05:00
gchar * label_name , * accel_name ;
2013-10-10 14:49:37 -04:00
2014-02-16 14:48:17 -05:00
/* Shift tab requires an exception, hopefully that list ends here.. */
if ( accel_key = = GDK_KEY_Tab & & accel_mods & GDK_SHIFT_MASK )
accel_key = GDK_KEY_ISO_Left_Tab ;
2014-02-16 11:08:32 -05:00
label_name = gtk_accelerator_get_label ( accel_key , key_modifier_get_valid ( accel_mods ) ) ;
accel_name = gtk_accelerator_name ( accel_key , key_modifier_get_valid ( accel_mods ) ) ;
2013-10-10 14:49:37 -04:00
gtk_tree_model_get_iter ( model , & iter , path ) ;
2014-02-16 11:08:32 -05:00
gtk_list_store_set ( GTK_LIST_STORE ( model ) , & iter , KEY_COLUMN , label_name ,
ACCEL_COLUMN , accel_name , - 1 ) ;
2013-10-10 14:49:37 -04:00
gtk_tree_path_free ( path ) ;
2014-02-16 11:08:32 -05:00
g_free ( label_name ) ;
g_free ( accel_name ) ;
2011-02-23 22:14:30 -05:00
}
static void
2013-10-10 14:49:37 -04:00
key_dialog_combo_changed ( GtkCellRendererCombo * combo , gchar * pathstr ,
GtkTreeIter * new_iter , gpointer data )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
GtkTreeModel * model ;
GtkXText * xtext ;
gchar * actiontext = NULL ;
gint action ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
xtext = GTK_XTEXT ( g_object_get_data ( G_OBJECT ( key_dialog ) , " xtext " ) ) ;
model = GTK_TREE_MODEL ( data ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
gtk_tree_model_get ( model , new_iter , 0 , & actiontext , - 1 ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
if ( actiontext )
{
# ifdef WIN32
/* We need to manually update the store */
GtkTreePath * path ;
GtkTreeIter iter ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
path = gtk_tree_path_new_from_string ( pathstr ) ;
model = get_store ( ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
gtk_tree_model_get_iter ( model , & iter , path ) ;
gtk_list_store_set ( GTK_LIST_STORE ( model ) , & iter , ACTION_COLUMN , actiontext , - 1 ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
gtk_tree_path_free ( path ) ;
# endif
action = key_get_action_from_string ( actiontext ) ;
key_dialog_print_text ( xtext , key_actions [ action ] . help ) ;
g_free ( actiontext ) ;
}
2011-02-23 22:14:30 -05:00
}
static void
2013-10-10 14:49:37 -04:00
key_dialog_entry_edited ( GtkCellRendererText * render , gchar * pathstr , gchar * new_text , gpointer data )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
GtkTreeModel * model = get_store ( ) ;
GtkTreePath * path = gtk_tree_path_new_from_string ( pathstr ) ;
GtkTreeIter iter ;
gint column = GPOINTER_TO_INT ( data ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
gtk_tree_model_get_iter ( model , & iter , path ) ;
gtk_list_store_set ( GTK_LIST_STORE ( model ) , & iter , column , new_text , - 1 ) ;
gtk_tree_path_free ( path ) ;
}
static gboolean
key_dialog_keypress ( GtkWidget * wid , GdkEventKey * evt , gpointer userdata )
{
GtkTreeView * view = g_object_get_data ( G_OBJECT ( key_dialog ) , " view " ) ;
GtkTreeModel * store ;
GtkTreeIter iter1 , iter2 ;
GtkTreeSelection * sel ;
GtkTreePath * path ;
gboolean handled = FALSE ;
int delta ;
if ( evt - > state & GDK_SHIFT_MASK )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
if ( evt - > keyval = = GDK_KEY_Up )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
handled = TRUE ;
delta = - 1 ;
}
else if ( evt - > keyval = = GDK_KEY_Down )
{
handled = TRUE ;
delta = 1 ;
2011-02-23 22:14:30 -05:00
}
}
2013-10-10 14:49:37 -04:00
if ( handled )
{
sel = gtk_tree_view_get_selection ( view ) ;
gtk_tree_selection_get_selected ( sel , & store , & iter1 ) ;
path = gtk_tree_model_get_path ( store , & iter1 ) ;
if ( delta = = 1 )
gtk_tree_path_next ( path ) ;
else
gtk_tree_path_prev ( path ) ;
gtk_tree_model_get_iter ( store , & iter2 , path ) ;
gtk_tree_path_free ( path ) ;
gtk_list_store_swap ( GTK_LIST_STORE ( store ) , & iter1 , & iter2 ) ;
}
return handled ;
2011-02-23 22:14:30 -05:00
}
static void
2013-10-10 14:49:37 -04:00
key_dialog_selection_changed ( GtkTreeSelection * sel , gpointer userdata )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
GtkTreeModel * model ;
GtkTreeIter iter ;
GtkXText * xtext ;
char * actiontext ;
int action ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
if ( ! gtk_tree_selection_get_selected ( sel , & model , & iter ) | | model = = NULL )
return ;
xtext = GTK_XTEXT ( g_object_get_data ( G_OBJECT ( key_dialog ) , " xtext " ) ) ;
gtk_tree_model_get ( model , & iter , ACTION_COLUMN , & actiontext , - 1 ) ;
if ( actiontext )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
action = key_get_action_from_string ( actiontext ) ;
key_dialog_print_text ( xtext , key_actions [ action ] . help ) ;
g_free ( actiontext ) ;
2011-02-23 22:14:30 -05:00
}
2013-10-10 14:49:37 -04:00
else
key_dialog_print_text ( xtext , _ ( " Select a row to get help information on its Action. " ) ) ;
2011-02-23 22:14:30 -05:00
}
static void
2013-10-10 14:49:37 -04:00
key_dialog_close ( GtkWidget * wid , gpointer userdata )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
gtk_widget_destroy ( key_dialog ) ;
key_dialog = NULL ;
}
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
static void
key_dialog_save ( GtkWidget * wid , gpointer userdata )
{
GtkTreeModel * store = get_store ( ) ;
GtkTreeIter iter ;
struct key_binding * kb ;
char * data1 , * data2 , * accel , * actiontext ;
guint keyval ;
GdkModifierType mod ;
if ( keybind_list )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
g_slist_free_full ( keybind_list , key_free ) ;
keybind_list = NULL ;
2011-02-23 22:14:30 -05:00
}
2013-10-10 14:49:37 -04:00
if ( gtk_tree_model_get_iter_first ( GTK_TREE_MODEL ( store ) , & iter ) )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
do
2011-02-23 22:14:30 -05:00
{
2014-12-28 06:37:25 -05:00
kb = g_new0 ( struct key_binding , 1 ) ;
2011-02-23 22:14:30 -05:00
2014-02-16 11:08:32 -05:00
gtk_tree_model_get ( GTK_TREE_MODEL ( store ) , & iter , ACCEL_COLUMN , & accel ,
2013-10-10 14:49:37 -04:00
ACTION_COLUMN , & actiontext ,
D1_COLUMN , & data1 ,
D2_COLUMN , & data2 ,
- 1 ) ;
kb - > data1 = data1 ;
kb - > data2 = data2 ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
if ( accel )
{
gtk_accelerator_parse ( accel , & keyval , & mod ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
kb - > keyval = keyval ;
kb - > mod = key_modifier_get_valid ( mod ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
g_free ( accel ) ;
}
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
if ( actiontext )
{
kb - > action = key_get_action_from_string ( actiontext ) ;
g_free ( actiontext ) ;
}
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
if ( ! accel | | ! actiontext )
key_free ( kb ) ;
else
keybind_list = g_slist_append ( keybind_list , kb ) ;
2014-12-09 00:24:59 -05:00
}
while ( gtk_tree_model_iter_next ( GTK_TREE_MODEL ( store ) , & iter ) ) ;
2013-10-10 14:49:37 -04:00
}
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
if ( key_save_kbs ( ) = = 0 )
key_dialog_close ( wid , NULL ) ;
2011-02-23 22:14:30 -05:00
}
2013-10-10 14:49:37 -04:00
static void
key_dialog_add ( GtkWidget * wid , gpointer userdata )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
GtkTreeView * view = g_object_get_data ( G_OBJECT ( key_dialog ) , " view " ) ;
GtkTreeViewColumn * col ;
GtkListStore * store = GTK_LIST_STORE ( get_store ( ) ) ;
GtkTreeIter iter ;
GtkTreePath * path ;
gtk_list_store_append ( store , & iter ) ;
/* make sure the new row is visible and selected */
path = gtk_tree_model_get_path ( GTK_TREE_MODEL ( store ) , & iter ) ;
col = gtk_tree_view_get_column ( view , ACTION_COLUMN ) ;
gtk_tree_view_scroll_to_cell ( view , path , NULL , FALSE , 0.0 , 0.0 ) ;
gtk_tree_view_set_cursor ( view , path , col , TRUE ) ;
gtk_tree_path_free ( path ) ;
}
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
static void
key_dialog_delete ( GtkWidget * wid , gpointer userdata )
{
GtkTreeView * view = g_object_get_data ( G_OBJECT ( key_dialog ) , " view " ) ;
GtkListStore * store = GTK_LIST_STORE ( gtk_tree_view_get_model ( view ) ) ;
GtkTreeIter iter ;
GtkTreePath * path ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
if ( gtkutil_treeview_get_selected ( view , & iter , - 1 ) )
{
/* delete this row, select next one */
if ( gtk_list_store_remove ( store , & iter ) )
{
path = gtk_tree_model_get_path ( GTK_TREE_MODEL ( store ) , & iter ) ;
gtk_tree_view_scroll_to_cell ( view , path , NULL , TRUE , 1.0 , 0.0 ) ;
gtk_tree_view_set_cursor ( view , path , NULL , FALSE ) ;
gtk_tree_path_free ( path ) ;
}
}
2011-02-23 22:14:30 -05:00
}
static GtkWidget *
2013-10-10 14:49:37 -04:00
key_dialog_treeview_new ( GtkWidget * box )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
GtkWidget * scroll ;
GtkListStore * store , * combostore ;
GtkTreeViewColumn * col ;
GtkWidget * view ;
GtkCellRenderer * render ;
int i ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
scroll = gtk_scrolled_window_new ( NULL , NULL ) ;
gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW ( scroll ) , GTK_POLICY_AUTOMATIC , GTK_POLICY_AUTOMATIC ) ;
gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW ( scroll ) , GTK_SHADOW_IN ) ;
2011-02-23 22:14:30 -05:00
2014-02-16 11:08:32 -05:00
store = gtk_list_store_new ( N_COLUMNS , G_TYPE_STRING , G_TYPE_STRING , G_TYPE_STRING ,
2013-10-10 14:49:37 -04:00
G_TYPE_STRING , G_TYPE_STRING ) ;
g_return_val_if_fail ( store ! = NULL , NULL ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
view = gtk_tree_view_new_with_model ( GTK_TREE_MODEL ( store ) ) ;
gtk_tree_view_set_fixed_height_mode ( GTK_TREE_VIEW ( view ) , TRUE ) ;
gtk_tree_view_set_enable_search ( GTK_TREE_VIEW ( view ) , FALSE ) ;
2015-02-08 01:51:22 -05:00
gtk_tree_view_set_reorderable ( GTK_TREE_VIEW ( view ) , TRUE ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
g_signal_connect ( G_OBJECT ( view ) , " key-press-event " ,
G_CALLBACK ( key_dialog_keypress ) , NULL ) ;
g_signal_connect ( G_OBJECT ( gtk_tree_view_get_selection ( GTK_TREE_VIEW ( view ) ) ) ,
" changed " , G_CALLBACK ( key_dialog_selection_changed ) , NULL ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
gtk_tree_view_set_rules_hint ( GTK_TREE_VIEW ( view ) , TRUE ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
render = gtk_cell_renderer_accel_new ( ) ;
g_object_set ( render , " editable " , TRUE ,
# ifndef WIN32
" accel-mode " , GTK_CELL_RENDERER_ACCEL_MODE_OTHER ,
# endif
NULL ) ;
g_signal_connect ( G_OBJECT ( render ) , " accel-edited " ,
G_CALLBACK ( key_dialog_set_key ) , NULL ) ;
gtk_tree_view_insert_column_with_attributes ( GTK_TREE_VIEW ( view ) , KEY_COLUMN ,
" Key " , render ,
" text " , KEY_COLUMN ,
NULL ) ;
2014-02-16 11:08:32 -05:00
render = gtk_cell_renderer_text_new ( ) ;
gtk_tree_view_insert_column_with_attributes (
GTK_TREE_VIEW ( view ) , ACCEL_COLUMN ,
" Accel " , render ,
" text " , ACCEL_COLUMN ,
NULL ) ;
2013-10-10 14:49:37 -04:00
combostore = gtk_list_store_new ( 1 , G_TYPE_STRING ) ;
for ( i = 0 ; i < = KEY_MAX_ACTIONS ; i + + )
{
GtkTreeIter iter ;
if ( key_actions [ i ] . name [ 0 ] )
{
gtk_list_store_append ( combostore , & iter ) ;
gtk_list_store_set ( combostore , & iter , 0 , key_actions [ i ] . name , - 1 ) ;
}
}
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
render = gtk_cell_renderer_combo_new ( ) ;
g_object_set ( G_OBJECT ( render ) , " model " , combostore ,
" has-entry " , FALSE ,
" editable " , TRUE ,
" text-column " , 0 ,
NULL ) ;
g_signal_connect ( G_OBJECT ( render ) , " edited " ,
G_CALLBACK ( key_dialog_entry_edited ) , GINT_TO_POINTER ( ACTION_COLUMN ) ) ;
g_signal_connect ( G_OBJECT ( render ) , " changed " ,
G_CALLBACK ( key_dialog_combo_changed ) , combostore ) ;
gtk_tree_view_insert_column_with_attributes ( GTK_TREE_VIEW ( view ) , ACTION_COLUMN ,
" Action " , render ,
" text " , ACTION_COLUMN ,
NULL ) ;
render = gtk_cell_renderer_text_new ( ) ;
g_object_set ( render , " editable " , TRUE , NULL ) ;
g_signal_connect ( G_OBJECT ( render ) , " edited " ,
G_CALLBACK ( key_dialog_entry_edited ) , GINT_TO_POINTER ( D1_COLUMN ) ) ;
gtk_tree_view_insert_column_with_attributes (
GTK_TREE_VIEW ( view ) , D1_COLUMN ,
" Data1 " , render ,
" text " , D1_COLUMN ,
NULL ) ;
render = gtk_cell_renderer_text_new ( ) ;
g_object_set ( render , " editable " , TRUE , NULL ) ;
g_signal_connect ( G_OBJECT ( render ) , " edited " ,
G_CALLBACK ( key_dialog_entry_edited ) , GINT_TO_POINTER ( D2_COLUMN ) ) ;
gtk_tree_view_insert_column_with_attributes (
GTK_TREE_VIEW ( view ) , D2_COLUMN ,
" Data2 " , render ,
" text " , D2_COLUMN ,
NULL ) ;
col = gtk_tree_view_get_column ( GTK_TREE_VIEW ( view ) , KEY_COLUMN ) ;
gtk_tree_view_column_set_fixed_width ( col , 200 ) ;
gtk_tree_view_column_set_resizable ( col , TRUE ) ;
2014-02-16 11:08:32 -05:00
col = gtk_tree_view_get_column ( GTK_TREE_VIEW ( view ) , ACCEL_COLUMN ) ;
gtk_tree_view_column_set_visible ( col , FALSE ) ;
2013-10-10 14:49:37 -04:00
col = gtk_tree_view_get_column ( GTK_TREE_VIEW ( view ) , ACTION_COLUMN ) ;
gtk_tree_view_column_set_fixed_width ( col , 160 ) ;
col = gtk_tree_view_get_column ( GTK_TREE_VIEW ( view ) , D1_COLUMN ) ;
gtk_tree_view_column_set_sizing ( col , GTK_TREE_VIEW_COLUMN_AUTOSIZE ) ;
gtk_tree_view_column_set_min_width ( col , 80 ) ;
gtk_tree_view_column_set_resizable ( col , TRUE ) ;
col = gtk_tree_view_get_column ( GTK_TREE_VIEW ( view ) , D2_COLUMN ) ;
gtk_tree_view_column_set_sizing ( col , GTK_TREE_VIEW_COLUMN_AUTOSIZE ) ;
gtk_tree_view_column_set_min_width ( col , 80 ) ;
gtk_tree_view_column_set_resizable ( col , TRUE ) ;
gtk_container_add ( GTK_CONTAINER ( scroll ) , view ) ;
gtk_container_add ( GTK_CONTAINER ( box ) , scroll ) ;
return view ;
2011-02-23 22:14:30 -05:00
}
static void
2013-10-10 14:49:37 -04:00
key_dialog_load ( GtkListStore * store )
2011-02-23 22:14:30 -05:00
{
2013-10-10 14:49:37 -04:00
struct key_binding * kb = NULL ;
2014-02-16 11:08:32 -05:00
char * label_text , * accel_text ;
2013-10-10 14:49:37 -04:00
GtkTreeIter iter ;
GSList * list = keybind_list ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
while ( list )
{
kb = ( struct key_binding * ) list - > data ;
2011-02-23 22:14:30 -05:00
2014-02-16 11:08:32 -05:00
label_text = gtk_accelerator_get_label ( kb - > keyval , kb - > mod ) ;
2013-10-10 14:49:37 -04:00
accel_text = gtk_accelerator_name ( kb - > keyval , kb - > mod ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
gtk_list_store_append ( store , & iter ) ;
gtk_list_store_set ( store , & iter ,
2014-02-16 11:08:32 -05:00
KEY_COLUMN , label_text ,
ACCEL_COLUMN , accel_text ,
2013-10-10 14:49:37 -04:00
ACTION_COLUMN , key_actions [ kb - > action ] . name ,
D1_COLUMN , kb - > data1 ,
D2_COLUMN , kb - > data2 , - 1 ) ;
g_free ( accel_text ) ;
2014-02-16 11:08:32 -05:00
g_free ( label_text ) ;
2013-10-10 14:49:37 -04:00
list = g_slist_next ( list ) ;
2011-02-23 22:14:30 -05:00
}
}
void
key_dialog_show ( )
{
2013-10-10 14:49:37 -04:00
GtkWidget * vbox , * box ;
GtkWidget * view , * xtext ;
GtkListStore * store ;
2011-02-23 22:14:30 -05:00
if ( key_dialog )
{
mg_bring_tofront ( key_dialog ) ;
return ;
}
2013-10-10 14:49:37 -04:00
key_dialog = mg_create_generic_tab ( " editkeys " , _ ( DISPLAY_NAME " : Keyboard Shortcuts " ) ,
TRUE , FALSE , key_dialog_close , NULL , 600 , 360 , & vbox , 0 ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
view = key_dialog_treeview_new ( vbox ) ;
xtext = gtk_xtext_new ( colors , 0 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , xtext , FALSE , TRUE , 2 ) ;
gtk_xtext_set_font ( GTK_XTEXT ( xtext ) , prefs . hex_text_font ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
g_object_set_data ( G_OBJECT ( key_dialog ) , " view " , view ) ;
g_object_set_data ( G_OBJECT ( key_dialog ) , " xtext " , xtext ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
box = gtk_hbutton_box_new ( ) ;
gtk_button_box_set_layout ( GTK_BUTTON_BOX ( box ) , GTK_BUTTONBOX_SPREAD ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , box , FALSE , FALSE , 2 ) ;
gtk_container_set_border_width ( GTK_CONTAINER ( box ) , 5 ) ;
2011-02-23 22:14:30 -05:00
2013-10-10 14:49:37 -04:00
gtkutil_button ( box , GTK_STOCK_NEW , NULL , key