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
{
int keyval ; /* GDK keynumber */
char * keyname ; /* String with the name of the function */
int action ; /* Index into key_actions */
int mod ; /* Flags of STATE_* above */
char * data1 , * data2 ; /* Pointers to strings, these must be freed */
struct key_binding * next ;
} ;
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 ;
} ;
static int key_load_kbs ( char * ) ;
static void key_load_defaults ( ) ;
static void key_save_kbs ( char * ) ;
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 ) ;
static GtkWidget * key_dialog ;
static struct key_binding * keys_root = NULL ;
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 " ) } ,
} ;
void
key_init ( )
{
keys_root = NULL ;
if ( key_load_kbs ( NULL ) = = 1 )
{
key_load_defaults ( ) ;
if ( key_load_kbs ( NULL ) = = 1 )
fe_message ( _ ( " There was an error loading key "
" bindings configuration " ) , FE_MSG_ERROR ) ;
}
}
static char *
key_get_key_name ( int keyval )
{
return gdk_keyval_name ( gdk_keyval_to_lower ( keyval ) ) ;
}
/* 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 {
int keyval ; GDK keynumber
char * keyname ; String with the name of the function
int action ; Index into key_actions
int mod ; Flags of STATE_ * above
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
*/
gboolean
key_handle_key_press ( GtkWidget * wid , GdkEventKey * evt , session * sess )
{
struct key_binding * kb , * last = NULL ;
int keyval = evt - > keyval ;
int mod , n ;
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 ;
if ( plugin_emit_keypress ( sess , evt - > state , evt - > keyval , evt - > length , evt - > string ) )
return 1 ;
/* maybe the plugin closed this tab? */
if ( ! is_session ( sess ) )
return 1 ;
mod = evt - > state & ( STATE_CTRL | STATE_ALT | STATE_SHIFT ) ;
kb = keys_root ;
while ( kb )
{
if ( kb - > keyval = = keyval & & kb - > mod = = mod )
{
if ( kb - > action < 0 | | kb - > action > KEY_MAX_ACTIONS )
return 0 ;
/* Bump this binding to the top of the list */
if ( last ! = NULL )
{
last - > next = kb - > next ;
kb - > next = keys_root ;
keys_root = kb ;
}
/* 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 ;
}
}
last = kb ;
kb = kb - > next ;
}
switch ( keyval )
{
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 ;
2011-12-11 11:34:02 -05:00
# if defined(USE_GTKSPELL) /* && !defined(WIN32) */
2011-02-23 22:14:30 -05:00
/* gtktextview has no 'activate' event, so we trap ENTER here */
2013-09-15 03:21:56 -04:00
case GDK_KEY_Return :
case GDK_KEY_KP_Enter :
2012-10-20 21:03:38 -04:00
if ( ! ( evt - > state & STATE_CTRL ) )
2011-02-23 22:14:30 -05:00
{
g_signal_stop_emission_by_name ( G_OBJECT ( wid ) , " key_press_event " ) ;
mg_inputbox_cb ( wid , sess - > gui ) ;
}
# endif
}
return 0 ;
}
/* Walks keys_root and free()'s everything */
/*static void
key_free_all ( )
{
struct key_binding * cur , * next ;
cur = keys_root ;
while ( cur )
{
next = cur - > next ;
if ( cur - > data1 )
free ( cur - > data1 ) ;
if ( cur - > data2 )
free ( cur - > data2 ) ;
free ( cur ) ;
cur = next ;
}
keys_root = NULL ;
} */
/* Turns mod flags into a C-A-S string */
static char *
key_make_mod_str ( int mod , char * buf )
{
int i = 0 ;
if ( mod & STATE_CTRL )
{
if ( i )
buf [ i + + ] = ' - ' ;
buf [ i + + ] = ' C ' ;
}
if ( mod & STATE_ALT )
{
if ( i )
buf [ i + + ] = ' - ' ;
buf [ i + + ] = ' A ' ;
}
if ( mod & STATE_SHIFT )
{
if ( i )
buf [ i + + ] = ' - ' ;
buf [ i + + ] = ' S ' ;
}
buf [ i ] = 0 ;
return buf ;
}
/* ***** GUI code here ******************* */
/* NOTE: The key_dialog defin is above --AGL */
static GtkWidget * key_dialog_act_menu , * key_dialog_kb_clist ;
static GtkWidget * key_dialog_tog_c , * key_dialog_tog_s , * key_dialog_tog_a ;
static GtkWidget * key_dialog_ent_key , * key_dialog_ent_d1 , * key_dialog_ent_d2 ;
static GtkWidget * key_dialog_text ;
static void
key_load_defaults ( )
{
/* This is the default config */
# define defcfg \
" C \n Prior \n Change Page \n D1:-1 \n D2:Relative \n \n " \
" C \n Next \n Change Page \n D1:1 \n D2:Relative \n \n " \
" A \n 9 \n Change Page \n D1:9 \n D2! \n \n " \
" A \n 8 \n Change Page \n D1:8 \n D2! \n \n " \
" A \n 7 \n Change Page \n D1:7 \n D2! \n \n " \
" A \n 6 \n Change Page \n D1:6 \n D2! \n \n " \
" A \n 5 \n Change Page \n D1:5 \n D2! \n \n " \
" A \n 4 \n Change Page \n D1:4 \n D2! \n \n " \
" A \n 3 \n Change Page \n D1:3 \n D2! \n \n " \
" A \n 2 \n Change Page \n D1:2 \n D2! \n \n " \
" A \n 1 \n Change Page \n D1:1 \n D2! \n \n " \
2012-12-25 00:33:57 -05:00
" A \n grave \n Change Page \n D1:auto \n D2! \n \n " \
2011-02-23 22:14:30 -05:00
" C \n o \n Insert in Buffer \n D1: \n D2! \n \n " \
" C \n b \n Insert in Buffer \n D1: \n D2! \n \n " \
" C \n k \n Insert in Buffer \n D1: \n D2! \n \n " \
" S \n Next \n Change Selected Nick \n D1! \n D2! \n \n " \
" S \n Prior \n Change Selected Nick \n D1:Up \n D2! \n \n " \
" None \n Next \n Scroll Page \n D1:Down \n D2! \n \n " \
2013-07-30 16:11:07 -04:00
" C \n Home \n Scroll Page \n D1:Top \n D2! \n \n " \
" C \n End \n Scroll Page \n D1:Bottom \n D2! \n \n " \
2011-02-23 22:14:30 -05:00
" None \n Prior \n Scroll Page \n D1:Up \n D2! \n \n " \
" S \n Down \n Scroll Page \n D1:+1 \n D2! \n \n " \
" S \n Up \n Scroll Page \n D1:-1 \n D2! \n \n " \
" None \n Down \n Next Command \n D1! \n D2! \n \n " \
" None \n Up \n Last Command \n D1! \n D2! \n \n " \
" None \n Tab \n Complete nick/command \n D1! \n D2! \n \n " \
" None \n space \n Check For Replace \n D1! \n D2! \n \n " \
" None \n Return \n Check For Replace \n D1! \n D2! \n \n " \
" None \n KP_Enter \n Check For Replace \n D1! \n D2! \n \n " \
" C \n Tab \n Complete nick/command \n D1:Up \n D2! \n \n " \
" A \n Left \n Move front tab left \n D1! \n D2! \n \n " \
" A \n Right \n Move front tab right \n D1! \n D2! \n \n " \
" CS \n Prior \n Move tab family left \n D1! \n D2! \n \n " \
" CS \n Next \n Move tab family right \n D1! \n D2! \n \n " \
" None \n F9 \n Run Command \n D1:/GUI MENU TOGGLE \n D2! \n \n "
int fd ;
2012-10-30 06:35:39 -04:00
fd = hexchat_open_file ( " keybindings.conf " , O_CREAT | O_TRUNC | O_WRONLY , 0x180 , XOF_DOMODE ) ;
2011-02-23 22:14:30 -05:00
if ( fd < 0 )
/* ???!!! */
return ;
write ( fd , defcfg , strlen ( defcfg ) ) ;
close ( fd ) ;
}
static void
key_dialog_close ( )
{
key_dialog = NULL ;
key_save_kbs ( NULL ) ;
}
static void
key_dialog_add_new ( GtkWidget * button , GtkCList * list )
{
gchar * strs [ ] = { " " , NULL , NULL , NULL , NULL } ;
struct key_binding * kb ;
strs [ 1 ] = _ ( " <none> " ) ;
strs [ 2 ] = _ ( " <none> " ) ;
strs [ 3 ] = _ ( " <none> " ) ;
strs [ 4 ] = _ ( " <none> " ) ;
kb = malloc ( sizeof ( struct key_binding ) ) ;
kb - > keyval = 0 ;
kb - > keyname = NULL ;
kb - > action = - 1 ;
kb - > mod = 0 ;
kb - > data1 = kb - > data2 = NULL ;
kb - > next = keys_root ;
keys_root = kb ;
gtk_clist_set_row_data ( GTK_CLIST ( list ) ,
gtk_clist_append ( GTK_CLIST ( list ) , strs ) , kb ) ;
}
static void
key_dialog_delete ( GtkWidget * button , GtkCList * list )
{
struct key_binding * kb , * cur , * last ;
int row = gtkutil_clist_selection ( ( GtkWidget * ) list ) ;
if ( row ! = - 1 )
{
kb = gtk_clist_get_row_data ( list , row ) ;
cur = keys_root ;
last = NULL ;
while ( cur )
{
if ( cur = = kb )
{
if ( last )
last - > next = kb - > next ;
else
keys_root = kb - > next ;
if ( kb - > data1 )
free ( kb - > data1 ) ;
if ( kb - > data2 )
free ( kb - > data2 ) ;
free ( kb ) ;
gtk_clist_remove ( list , row ) ;
return ;
}
last = cur ;
cur = cur - > next ;
}
printf ( " *** key_dialog_delete: couldn't find kb in list! \n " ) ;
2012-10-30 06:35:39 -04:00
/*if (getenv ("HEXCHAT_DEBUG"))
2011-02-23 22:14:30 -05:00
abort ( ) ; */
}
}
static void
key_print_text ( GtkXText * xtext , char * text )
{
2012-10-22 09:55:43 -04:00
unsigned int old = prefs . hex_stamp_text ;
prefs . hex_stamp_text = 0 ; /* temporarily disable stamps */
2011-02-23 22:14:30 -05:00
gtk_xtext_clear ( GTK_XTEXT ( xtext ) - > buffer , 0 ) ;
PrintTextRaw ( GTK_XTEXT ( xtext ) - > buffer , text , 0 , 0 ) ;
2012-10-22 09:55:43 -04:00
prefs . hex_stamp_text = old ;
2011-02-23 22:14:30 -05:00
}
static void
key_dialog_sel_act ( GtkWidget * un , int num )
{
int row = gtkutil_clist_selection ( key_dialog_kb_clist ) ;
struct key_binding * kb ;
if ( row ! = - 1 )
{
kb = gtk_clist_get_row_data ( GTK_CLIST ( key_dialog_kb_clist ) , row ) ;
kb - > action = num ;
gtk_clist_set_text ( GTK_CLIST ( key_dialog_kb_clist ) , row , 2 ,
_ ( key_actions [ num ] . name ) ) ;
if ( key_actions [ num ] . help )
{
key_print_text ( GTK_XTEXT ( key_dialog_text ) , _ ( key_actions [ num ] . help ) ) ;
}
}
}
static void
key_dialog_sel_row ( GtkWidget * clist , gint row , gint column ,
GdkEventButton * evt , gpointer data )
{
struct key_binding * kb = gtk_clist_get_row_data ( GTK_CLIST ( clist ) , row ) ;
if ( kb = = NULL )
{
printf ( " *** key_dialog_sel_row: kb == NULL \n " ) ;
abort ( ) ;
}
if ( kb - > action > - 1 & & kb - > action < = KEY_MAX_ACTIONS )
{
gtk_option_menu_set_history ( GTK_OPTION_MENU ( key_dialog_act_menu ) ,
kb - > action ) ;
if ( key_actions [ kb - > action ] . help )
{
key_print_text ( GTK_XTEXT ( key_dialog_text ) , _ ( key_actions [ kb - > action ] . help ) ) ;
}
}
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( key_dialog_tog_c ) ,
( kb - > mod & STATE_CTRL ) = = STATE_CTRL ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( key_dialog_tog_s ) ,
( kb - > mod & STATE_SHIFT ) = = STATE_SHIFT ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( key_dialog_tog_a ) ,
( kb - > mod & STATE_ALT ) = = STATE_ALT ) ;
if ( kb - > data1 )
gtk_entry_set_text ( GTK_ENTRY ( key_dialog_ent_d1 ) , kb - > data1 ) ;
else
gtk_entry_set_text ( GTK_ENTRY ( key_dialog_ent_d1 ) , " " ) ;
if ( kb - > data2 )
gtk_entry_set_text ( GTK_ENTRY ( key_dialog_ent_d2 ) , kb - > data2 ) ;
else
gtk_entry_set_text ( GTK_ENTRY ( key_dialog_ent_d2 ) , " " ) ;
if ( kb - > keyname )
gtk_entry_set_text ( GTK_ENTRY ( key_dialog_ent_key ) , kb - > keyname ) ;
else
gtk_entry_set_text ( GTK_ENTRY ( key_dialog_ent_key ) , " " ) ;
}
static void
key_dialog_tog_key ( GtkWidget * tog , int kstate )
{
int state = GTK_TOGGLE_BUTTON ( tog ) - > active ;
int row = gtkutil_clist_selection ( key_dialog_kb_clist ) ;
struct key_binding * kb ;
char buf [ 32 ] ;
if ( row = = - 1 )
return ;
kb = gtk_clist_get_row_data ( GTK_CLIST ( key_dialog_kb_clist ) , row ) ;
if ( state )
kb - > mod | = kstate ;
else
kb - > mod & = ~ kstate ;
gtk_clist_set_text ( GTK_CLIST ( key_dialog_kb_clist ) , row , 0 ,
key_make_mod_str ( kb - > mod , buf ) ) ;
}
static GtkWidget *
key_dialog_make_toggle ( char * label , void * callback , void * option ,
GtkWidget * box )
{
GtkWidget * wid ;
wid = gtk_check_button_new_with_label ( label ) ;
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( wid ) , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( wid ) , " toggled " ,
GTK_SIGNAL_FUNC ( callback ) , option ) ;
gtk_box_pack_end ( GTK_BOX ( box ) , wid , 0 , 0 , 0 ) ;
gtk_widget_show ( wid ) ;
return wid ;
}
static GtkWidget *
key_dialog_make_entry ( char * label , char * act , void * callback , void * option ,
GtkWidget * box )
{
GtkWidget * wid , * hbox ; ;
hbox = gtk_hbox_new ( 0 , 2 ) ;
if ( label )
{
wid = gtk_label_new ( label ) ;
gtk_widget_show ( wid ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , wid , 0 , 0 , 0 ) ;
}
wid = gtk_entry_new ( ) ;
if ( act )
{
gtk_signal_connect ( GTK_OBJECT ( wid ) , act , GTK_SIGNAL_FUNC ( callback ) ,
option ) ;
}
gtk_box_pack_start ( GTK_BOX ( hbox ) , wid , 0 , 0 , 0 ) ;
gtk_widget_show ( wid ) ;
gtk_widget_show ( hbox ) ;
gtk_box_pack_start ( GTK_BOX ( box ) , hbox , 0 , 0 , 0 ) ;
return wid ;
}
static void
key_dialog_set_key ( GtkWidget * entry , GdkEventKey * evt , void * none )
{
int row = gtkutil_clist_selection ( key_dialog_kb_clist ) ;
struct key_binding * kb ;
gtk_entry_set_text ( GTK_ENTRY ( entry ) , " " ) ;
if ( row = = - 1 )
return ;
kb = gtk_clist_get_row_data ( GTK_CLIST ( key_dialog_kb_clist ) , row ) ;
kb - > keyval = evt - > keyval ;
kb - > keyname = key_get_key_name ( kb - > keyval ) ;
gtk_clist_set_text ( GTK_CLIST ( key_dialog_kb_clist ) , row , 1 , kb - > keyname ) ;
gtk_entry_set_text ( GTK_ENTRY ( entry ) , kb - > keyname ) ;
g_signal_stop_emission_by_name ( G_OBJECT ( entry ) , " key_press_event " ) ;
}
static void
key_dialog_set_data ( GtkWidget * entry , int d )
{
const char * text = gtk_entry_get_text ( GTK_ENTRY ( entry ) ) ;
int row = gtkutil_clist_selection ( key_dialog_kb_clist ) ;
struct key_binding * kb ;
char * buf ;
int len = strlen ( text ) ;
len + + ;
if ( row = = - 1 )
return ;
kb = gtk_clist_get_row_data ( GTK_CLIST ( key_dialog_kb_clist ) , row ) ;
if ( d = = 0 )
{ /* using data1 */
if ( kb - > data1 )
free ( kb - > data1 ) ;
buf = ( char * ) malloc ( len ) ;
memcpy ( buf , text , len ) ;
kb - > data1 = buf ;
gtk_clist_set_text ( GTK_CLIST ( key_dialog_kb_clist ) , row , 3 , text ) ;
} else
{
if ( kb - > data2 )
free ( kb - > data2 ) ;
buf = ( char * ) malloc ( len ) ;
memcpy ( buf , text , len ) ;
kb - > data2 = buf ;
gtk_clist_set_text ( GTK_CLIST ( key_dialog_kb_clist ) , row , 4 , text ) ;
}
}
void
key_dialog_show ( )
{
GtkWidget * vbox , * hbox , * list , * vbox2 , * wid , * wid2 , * wid3 , * hbox2 ;
struct key_binding * kb ;
gchar * titles [ ] = { NULL , NULL , NULL , " 1 " , " 2 " } ;
char temp [ 32 ] ;
int i ;
titles [ 0 ] = _ ( " Mod " ) ;
titles [ 1 ] = _ ( " Key " ) ;
titles [ 2 ] = _ ( " Action " ) ;
if ( key_dialog )
{
mg_bring_tofront ( key_dialog ) ;
return ;
}
key_dialog =
2012-07-12 11:49:26 -04:00
mg_create_generic_tab ( " editkeys " , _ ( DISPLAY_NAME " : Keyboard Shortcuts " ) ,
2011-02-23 22:14:30 -05:00
TRUE , FALSE , key_dialog_close , NULL , 560 , 330 , & vbox , 0 ) ;
hbox = gtk_hbox_new ( 0 , 2 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , hbox , 1 , 1 , 0 ) ;
list = gtkutil_clist_new ( 5 , titles , hbox , 0 , key_dialog_sel_row , 0 , NULL ,
0 , GTK_SELECTION_SINGLE ) ;
gtk_widget_set_usize ( list , 400 , 0 ) ;
key_dialog_kb_clist = list ;
gtk_widget_show ( hbox ) ;
kb = keys_root ;
gtk_clist_set_column_width ( GTK_CLIST ( list ) , 1 , 50 ) ;
gtk_clist_set_column_width ( GTK_CLIST ( list ) , 2 , 120 ) ;
gtk_clist_set_column_width ( GTK_CLIST ( list ) , 3 , 50 ) ;
gtk_clist_set_column_width ( GTK_CLIST ( list ) , 4 , 50 ) ;
while ( kb )
{
titles [ 0 ] = key_make_mod_str ( kb - > mod , temp ) ;
titles [ 1 ] = kb - > keyname ;
if ( kb - > action < 0 | | kb - > action > KEY_MAX_ACTIONS )
titles [ 2 ] = _ ( " <none> " ) ;
else
titles [ 2 ] = key_actions [ kb - > action ] . name ;
if ( kb - > data1 )
titles [ 3 ] = kb - > data1 ;
else
titles [ 3 ] = _ ( " <none> " ) ;
if ( kb - > data2 )
titles [ 4 ] = kb - > data2 ;
else
titles [ 4 ] = _ ( " <none> " ) ;
gtk_clist_set_row_data ( GTK_CLIST ( list ) ,
gtk_clist_append ( GTK_CLIST ( list ) , titles ) ,
kb ) ;
kb = kb - > next ;
}
vbox2 = gtk_vbox_new ( 0 , 2 ) ;
gtk_box_pack_end ( GTK_BOX ( hbox ) , vbox2 , 1 , 1 , 0 ) ;
wid = gtk_button_new_with_label ( _ ( " Add New " ) ) ;
gtk_box_pack_start ( GTK_BOX ( vbox2 ) , wid , 0 , 0 , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( wid ) , " clicked " ,
GTK_SIGNAL_FUNC ( key_dialog_add_new ) , list ) ;
gtk_widget_show ( wid ) ;
wid = gtk_button_new_with_label ( _ ( " Delete " ) ) ;
gtk_box_pack_start ( GTK_BOX ( vbox2 ) , wid , 0 , 0 , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( wid ) , " clicked " ,
GTK_SIGNAL_FUNC ( key_dialog_delete ) , list ) ;
gtk_widget_show ( wid ) ;
gtk_widget_show ( vbox2 ) ;
wid = gtk_option_menu_new ( ) ;
wid2 = gtk_menu_new ( ) ;
for ( i = 0 ; i < = KEY_MAX_ACTIONS ; i + + )
{
wid3 = gtk_menu_item_new_with_label ( _ ( key_actions [ i ] . name ) ) ;
gtk_widget_show ( wid3 ) ;
gtk_menu_shell_append ( GTK_MENU_SHELL ( wid2 ) , wid3 ) ;
gtk_signal_connect ( GTK_OBJECT ( wid3 ) , " activate " ,
GTK_SIGNAL_FUNC ( key_dialog_sel_act ) ,
GINT_TO_POINTER ( i ) ) ;
}
gtk_option_menu_set_menu ( GTK_OPTION_MENU ( wid ) , wid2 ) ;
gtk_option_menu_set_history ( GTK_OPTION_MENU ( wid ) , 0 ) ;
gtk_box_pack_end ( GTK_BOX ( vbox2 ) , wid , 0 , 0 , 0 ) ;
gtk_widget_show ( wid ) ;
key_dialog_act_menu = wid ;
key_dialog_tog_s = key_dialog_make_toggle ( _ ( " Shift " ) , key_dialog_tog_key ,
( void * ) STATE_SHIFT , vbox2 ) ;
key_dialog_tog_a = key_dialog_make_toggle ( _ ( " Alt " ) , key_dialog_tog_key ,
( void * ) STATE_ALT , vbox2 ) ;
key_dialog_tog_c = key_dialog_make_toggle ( _ ( " Ctrl " ) , key_dialog_tog_key ,
( void * ) STATE_CTRL , vbox2 ) ;
key_dialog_ent_key = key_dialog_make_entry ( _ ( " Key " ) , " key_press_event " ,
key_dialog_set_key , NULL ,
vbox2 ) ;
key_dialog_ent_d1 = key_dialog_make_entry ( _ ( " Data 1 " ) , " activate " ,
key_dialog_set_data , NULL ,
vbox2 ) ;
key_dialog_ent_d2 = key_dialog_make_entry ( _ ( " Data 2 " ) , " activate " ,
key_dialog_set_data ,
( void * ) 1 , vbox2 ) ;
hbox2 = gtk_hbox_new ( 0 , 2 ) ;
gtk_box_pack_end ( GTK_BOX ( vbox ) , hbox2 , 0 , 0 , 1 ) ;
wid = gtk_xtext_new ( colors , 0 ) ;
2012-10-22 09:55:43 -04:00
gtk_xtext_set_tint ( GTK_XTEXT ( wid ) , prefs . hex_text_tint_red , prefs . hex_text_tint_green , prefs . hex_text_tint_blue ) ;
2011-02-23 22:14:30 -05:00
gtk_xtext_set_background ( GTK_XTEXT ( wid ) ,
channelwin_pix ,
2012-10-22 09:55:43 -04:00
prefs . hex_text_transparent ) ;
2011-02-23 22:14:30 -05:00
gtk_widget_set_usize ( wid , 0 , 75 ) ;
gtk_box_pack_start ( GTK_BOX ( hbox2 ) , wid , 1 , 1 , 1 ) ;
2012-10-22 09:55:43 -04:00
gtk_xtext_set_font ( GTK_XTEXT ( wid ) , prefs . hex_text_font ) ;
2011-02-23 22:14:30 -05:00
gtk_widget_show ( wid ) ;
wid2 = gtk_vscrollbar_new ( GTK_XTEXT ( wid ) - > adj ) ;
gtk_box_pack_start ( GTK_BOX ( hbox2 ) , wid2 , 0 , 0 , 0 ) ;
gtk_widget_show ( wid2 ) ;
gtk_widget_show ( hbox2 ) ;
key_dialog_text = wid ;
gtk_widget_show_all ( key_dialog ) ;
}
static void
key_save_kbs ( char * fn )
{
int fd , i ;
char buf [ 512 ] ;
struct key_binding * kb ;
if ( ! fn )
2012-10-30 06:35:39 -04:00
fd = hexchat_open_file ( " keybindings.conf " , O_CREAT | O_TRUNC | O_WRONLY ,
2011-02-23 22:14:30 -05:00
0x180 , XOF_DOMODE ) ;
else
2012-10-30 06:35:39 -04:00
fd = hexchat_open_file ( fn , O_CREAT | O_TRUNC | O_WRONLY ,
2011-02-23 22:14:30 -05:00
0x180 , XOF_DOMODE | XOF_FULLPATH ) ;
if ( fd < 0 )
{
fe_message ( _ ( " Error opening keys config file \n " ) , FE_MSG_ERROR ) ;
return ;
}
write ( fd , buf ,
2012-10-30 06:35:39 -04:00
snprintf ( buf , 510 , " # HexChat key bindings config file \n \n " ) ) ;
2011-02-23 22:14:30 -05:00
kb = keys_root ;
i = 0 ;
while ( kb )
{
if ( kb - > keyval = = - 1 | | kb - > keyname = = NULL | | kb - > action < 0 )
{
kb = kb - > next ;
continue ;
}
i = 0 ;
if ( kb - > mod & STATE_CTRL )
{
i + + ;
write ( fd , " C " , 1 ) ;
}
if ( kb - > mod & STATE_ALT )
{
i + + ;
write ( fd , " A " , 1 ) ;
}
if ( kb - > mod & STATE_SHIFT )
{
i + + ;
write ( fd , " S " , 1 ) ;
}
if ( i = = 0 )
write ( fd , " None \n " , 5 ) ;
else
write ( fd , " \n " , 1 ) ;
write ( fd , buf , snprintf ( buf , 510 , " %s \n %s \n " , kb - > keyname ,
key_actions [ kb - > action ] . name ) ) ;
if ( kb - > data1 & & kb - > data1 [ 0 ] )
write ( fd , buf , snprintf ( buf , 510 , " D1:%s \n " , kb - > data1 ) ) ;
else
write ( fd , " D1! \n " , 4 ) ;
if ( kb - > data2 & & kb - > data2 [ 0 ] )
write ( fd , buf , snprintf ( buf , 510 , " D2:%s \n " , kb - > data2 ) ) ;
else
write ( fd , " D2! \n " , 4 ) ;
write ( fd , " \n " , 1 ) ;
kb = kb - > next ;
}
close ( fd ) ;
}
/* I just know this is going to be a nasty parse, if you think it's bugged
2012-10-30 06:35:39 -04:00
it almost certainly is so contact the HexChat dev team - - AGL */
2011-02-23 22:14:30 -05:00
static inline int
key_load_kbs_helper_mod ( char * in , int * out )
{
int n , len , mod = 0 ;
/* First strip off the fluff */
while ( in [ 0 ] = = ' ' | | in [ 0 ] = = ' \t ' )
in + + ;
len = strlen ( in ) ;
while ( in [ len ] = = ' ' | | in [ len ] = = ' \t ' )
{
in [ len ] = 0 ;
len - - ;
}
if ( strcmp ( in , " None " ) = = 0 )
{
* out = 0 ;
return 0 ;
}
for ( n = 0 ; n < len ; n + + )
{
switch ( in [ n ] )
{
case ' C ' :
mod | = STATE_CTRL ;
break ;
case ' A ' :
mod | = STATE_ALT ;
break ;
case ' S ' :
mod | = STATE_SHIFT ;
break ;
default :
return 1 ;
}
}
* out = mod ;
return 0 ;
}
/* These are just local defines to keep me sane --AGL */
# define KBSTATE_MOD 0
# define KBSTATE_KEY 1
# define KBSTATE_ACT 2
# define KBSTATE_DT1 3
# define KBSTATE_DT2 4
/* *** Warning, Warning! - massive function ahead! --AGL */
static int
key_load_kbs ( char * filename )
{
char * buf , * ibuf ;
struct stat st ;
struct key_binding * kb = NULL , * last = NULL ;
int fd , len , pnt = 0 , state = 0 , n ;
if ( filename = = NULL )
2012-10-30 06:35:39 -04:00
fd = hexchat_open_file ( " keybindings.conf " , O_RDONLY , 0 , 0 ) ;
2011-02-23 22:14:30 -05:00
else
2012-10-30 06:35:39 -04:00
fd = hexchat_open_file ( filename , O_RDONLY , 0 , XOF_FULLPATH ) ;
2011-02-23 22:14:30 -05:00
if ( fd < 0 )
return 1 ;
if ( fstat ( fd , & st ) ! = 0 )
return 1 ;
ibuf = malloc ( st . st_size ) ;
read ( fd , ibuf , st . st_size ) ;
close ( fd ) ;
while ( buf_get_line ( ibuf , & buf , & pnt , st . st_size ) )
{
if ( buf [ 0 ] = = ' # ' )
continue ;
if ( strlen ( buf ) = = 0 )
continue ;
switch ( state )
{
case KBSTATE_MOD :
kb = ( struct key_binding * ) malloc ( sizeof ( struct key_binding ) ) ;
if ( key_load_kbs_helper_mod ( buf , & kb - > mod ) )
goto corrupt_file ;
state = KBSTATE_KEY ;
continue ;
case KBSTATE_KEY :
/* First strip off the fluff */
while ( buf [ 0 ] = = ' ' | | buf [ 0 ] = = ' \t ' )
buf + + ;
len = strlen ( buf ) ;
while ( buf [ len ] = = ' ' | | buf [ len ] = = ' \t ' )
{
buf [ len ] = 0 ;
len - - ;
}
n = gdk_keyval_from_name ( buf ) ;
if ( n = = 0 )
{
/* Unknown keyname, abort */
if ( last )
last - > next = NULL ;
free ( ibuf ) ;
ibuf = malloc ( 1024 ) ;
snprintf ( ibuf , 1024 ,
2012-11-03 13:24:25 -04:00
_ ( " Unknown keyname %s in key bindings config file \n Load aborted, please fix %s " G_DIR_SEPARATOR_S " keybindings.conf \n " ) ,
buf , get_xdir ( ) ) ;
2011-02-23 22:14:30 -05:00
fe_message ( ibuf , FE_MSG_ERROR ) ;
free ( ibuf ) ;
return 2 ;
}
kb - > keyname = gdk_keyval_name ( n ) ;
kb - > keyval = n ;
state = KBSTATE_ACT ;
continue ;
case KBSTATE_ACT :
/* First strip off the fluff */
while ( buf [ 0 ] = = ' ' | | buf [ 0 ] = = ' \t ' )
buf + + ;
len = strlen ( buf ) ;
while ( buf [ len ] = = ' ' | | buf [ len ] = = ' \t ' )
{
buf [ len ] = 0 ;
len - - ;
}
for ( n = 0 ; n < KEY_MAX_ACTIONS + 1 ; n + + )
{
if ( strcmp ( key_actions [ n ] . name , buf ) = = 0 )
{
kb - > action = n ;
break ;
}
}
if ( n = = KEY_MAX_ACTIONS + 1 )
{
if ( last )
last - > next = NULL ;
free ( ibuf ) ;
ibuf = malloc ( 1024 ) ;
snprintf ( ibuf , 1024 ,
2012-11-03 13:24:25 -04:00
_ ( " Unknown action %s in key bindings config file \n Load aborted, Please fix %s " G_DIR_SEPARATOR_S " keybindings \n " ) ,
buf , get_xdir ( ) ) ;
2011-02-23 22:14:30 -05:00
fe_message ( ibuf , FE_MSG_ERROR ) ;
free ( ibuf ) ;
return 3 ;
}
state = KBSTATE_DT1 ;
continue ;
case KBSTATE_DT1 :
case KBSTATE_DT2 :
if ( state = = KBSTATE_DT1 )
kb - > data1 = kb - > data2 = NULL ;
while ( buf [ 0 ] = = ' ' | | buf [ 0 ] = = ' \t ' )
buf + + ;
if ( buf [ 0 ] ! = ' D ' )
{
free ( ibuf ) ;
ibuf = malloc ( 1024 ) ;
snprintf ( ibuf , 1024 ,
2012-11-03 13:24:25 -04:00
_ ( " Expecting Data line (beginning Dx{:|!}) but got: \n %s \n \n Load aborted, Please fix %s " G_DIR_SEPARATOR_S " keybindings \n " ) ,
buf , get_xdir ( ) ) ;
2011-02-23 22:14:30 -05:00
fe_message ( ibuf , FE_MSG_ERROR ) ;
free ( ibuf ) ;
return 4 ;
}
switch ( buf [ 1 ] )
{
case ' 1 ' :
if ( state ! = KBSTATE_DT1 )
goto corrupt_file ;
break ;
case ' 2 ' :
if ( state ! = KBSTATE_DT2 )
goto corrupt_file ;
break ;
default :
goto corrupt_file ;
}
if ( buf [ 2 ] = = ' : ' )
{
len = strlen ( buf ) ;
/* Add one for the NULL, subtract 3 for the "Dx:" */
len + + ;
len - = 3 ;
if ( state = = KBSTATE_DT1 )
{
kb - > data1 = malloc ( len ) ;
memcpy ( kb - > data1 , & buf [ 3 ] , len ) ;
} else
{
kb - > data2 = malloc ( len ) ;
memcpy ( kb - > data2 , & buf [ 3 ] , len ) ;
}
} else if ( buf [ 2 ] = = ' ! ' )
{
if ( state = = KBSTATE_DT1 )
kb - > data1 = NULL ;
else
kb - > data2 = NULL ;
}
if ( state = = KBSTATE_DT1 )
{
state = KBSTATE_DT2 ;
continue ;
} else
{
if ( last )
last - > next = kb ;
else
keys_root = kb ;
last = kb ;
state = KBSTATE_MOD ;
}
continue ;
}
}
if ( last )
last - > next = NULL ;
free ( ibuf ) ;
return 0 ;
corrupt_file :
2012-10-30 06:35:39 -04:00
/*if (getenv ("HEXCHAT_DEBUG"))
2011-02-23 22:14:30 -05:00
abort ( ) ; */
snprintf ( ibuf , 1024 ,
_ ( " Key bindings config file is corrupt, load aborted \n "
2012-11-03 13:24:25 -04:00
" Please fix %s " G_DIR_SEPARATOR_S " keybindings.conf \n " ) ,
get_xdir ( ) ) ;
2011-02-23 22:14:30 -05:00
fe_message ( ibuf , FE_MSG_ERROR ) ;
free ( ibuf ) ;
2013-01-09 17:37:57 -05:00
free ( kb ) ;
2011-02-23 22:14:30 -05:00
return 5 ;
}
/* ***** Key actions start here *********** */
/* See the NOTES above --AGL */
/* "Run command" */
static int
key_action_handle_command ( GtkWidget * wid , GdkEventKey * evt , char * d1 ,
char * d2 , struct session * sess )
{
int ii , oi , len ;
char out [ 2048 ] , d = 0 ;
if ( ! d1 )
return 0 ;
len = strlen ( d1 ) ;
/* Replace each "\n" substring with '\n' */
for ( ii = oi = 0 ; ii < len ; ii + + )
{
d = d1 [ ii ] ;
if ( d = = ' \\ ' )
{
ii + + ;
d = d1 [ ii ] ;
if ( d = = ' n ' )
out [ oi + + ] = ' \n ' ;
else if ( d = = ' \\ ' )
out [ oi + + ] = ' \\ ' ;
else
{
out [ oi + + ] = ' \\ ' ;
out [ oi + + ] = d ;
}
continue ;
}
out [ oi + + ] = d ;
}
out [ oi ] = 0 ;
handle_multiline ( sess , out , 0 , 0 ) ;
return 0 ;
}
2012-12-25 00:33:57 -05:00
/*
* Check if the given session is inside the main window . This predicate
2013-01-07 14:50:54 -05:00
* is passed to lastact_getfirst ( ) as a way to filter out detached sessions .
2012-12-25 00:33:57 -05:00
* XXX : Consider moving this in a different file ?
*/
static int
session_check_is_tab ( session * sess )
{
if ( ! sess | | ! sess - > gui )
return FALSE ;
return ( sess - > gui - > is_tab ) ;
}
2011-02-23 22:14:30 -05:00
static int
key_action_page_switch ( GtkWidget * wid , GdkEventKey * evt , char * d1 ,
char * d2 , struct session * sess )
{
2012-12-25 00:33:57 -05:00
session * newsess ;
2011-02-23 22:14:30 -05:00
int len , i , num ;
if ( ! d1 )
return 1 ;
len = strlen ( d1 ) ;
if ( ! len )
return 1 ;
2012-12-25 00:33:57 -05:00
if ( strcasecmp ( d1 , " auto " ) = = 0 )
{
/* Auto switch makes no sense in detached sessions */
if ( ! sess - > gui - > is_tab )
return 1 ;
/* Obtain a session with recent activity */
newsess = lastact_getfirst ( session_check_is_tab ) ;
if ( newsess )
{
/*
* Only sessions in the current window should be considered ( i . e .
* we don ' t want to move the focus on a different window ) . This
* call could , in theory , do this , but we checked before that
* newsess - > gui - > is_tab and sess - > gui - > is_tab .
*/
mg_bring_tofront_sess ( newsess ) ;
return 0 ;
}
else
return 1 ;
}
2011-02-23 22:14:30 -05:00
for ( i = 0 ; i < len ; i + + )
{
if ( d1 [ i ] < ' 0 ' | | d1 [ i ] > ' 9 ' )
{
if ( i = = 0 & & ( d1 [ i ] = = ' + ' | | d1 [ i ] = = ' - ' ) )
continue ;
else
return 1 ;
}
}
num = atoi ( d1 ) ;
if ( ! d2 )
num - - ;
if ( ! d2 | | d2 [ 0 ] = = 0 )
mg_switch_page ( FALSE , num ) ;
else
mg_switch_page ( TRUE , num ) ;
return 0 ;
}
int
key_action_insert ( GtkWidget * wid , GdkEventKey * evt , char * d1 , char * d2 ,
struct session * sess )
{
int tmp_pos ;
if ( ! d1 )
return 1 ;
tmp_pos = SPELL_ENTRY_GET_POS ( wid ) ;
SPELL_ENTRY_INSERT ( wid , d1 , strlen ( d1 ) , & tmp_pos ) ;
SPELL_ENTRY_SET_POS ( wid , tmp_pos ) ;
return 2 ;
}
/* handles PageUp/Down keys */
static int
key_action_scroll_page ( GtkWidget * wid , GdkEventKey * evt , char * d1 ,
char * d2 , struct session * sess )
{
int value , end ;
GtkAdjustment * adj ;
2013-07-30 16:11:07 -04:00
enum scroll_type { PAGE_TOP , PAGE_BOTTOM , PAGE_UP , PAGE_DOWN , LINE_UP , LINE_DOWN } ;
2011-02-23 22:14:30 -05:00
int type = PAGE_DOWN ;
if ( d1 )
{
2013-07-30 16:11:07 -04:00
if ( ! g_ascii_strcasecmp ( d1 , " top " ) )
type = PAGE_TOP ;
else if ( ! g_ascii_strcasecmp ( d1 , " bottom " ) )
type = PAGE_BOTTOM ;
else if ( ! g_ascii_strcasecmp ( d1 , " up " ) )
2011-02-23 22:14:30 -05:00
type = PAGE_UP ;
2013-07-30 16:11:07 -04:00
else if ( ! g_ascii_strcasecmp ( d1 , " down " ) )
type = PAGE_DOWN ;
2012-06-16 07:01:47 -04:00
else if ( ! g_ascii_strcasecmp ( d1 , " +1 " ) )
2011-02-23 22:14:30 -05:00
type = LINE_DOWN ;
2012-06-16 07:01:47 -04:00
else if ( ! g_ascii_strcasecmp ( d1 , " -1 " ) )
2011-02-23 22:14:30 -05:00
type = LINE_UP ;
}
if ( ! sess )
return 0 ;
adj = GTK_RANGE ( sess - > gui - > vscrollbar ) - > adjustment ;
end = adj - > upper - adj - > lower - adj - > page_size ;
switch ( type )
{
2013-07-30 16:11:07 -04:00
case PAGE_TOP :
value = 0 ;
2011-02-23 22:14:30 -05:00
break ;
2013-07-30 16:11:07 -04:00
case PAGE_BOTTOM :
value = end ;
2011-02-23 22:14:30 -05:00
break ;
case PAGE_UP :
value = adj - > value - ( adj - > page_size - 1 ) ;
break ;
2013-07-30 16:11:07 -04:00
case PAGE_DOWN :
2011-02-23 22:14:30 -05:00
value = adj - > value + ( adj - > page_size - 1 ) ;
break ;
2013-07-30 16:11:07 -04:00
case LINE_UP :
value = adj - > value - 1.0 ;
break ;
case LINE_DOWN :
value = adj - > value + 1.0 ;
break ;
2011-02-23 22:14:30 -05:00
}
if ( value < 0 )
value = 0 ;
if ( value > end )
value = end ;
gtk_adjustment_set_value ( adj , value ) ;
return 0 ;
}
static int
key_action_set_buffer ( GtkWidget * wid , GdkEventKey * evt , char * d1 , char * d2 ,
struct session * sess )
{
if ( ! d1 )
return 1 ;
if ( d1 [ 0 ] = = 0 )
return 1 ;
SPELL_ENTRY_SET_TEXT ( wid , d1 ) ;
SPELL_ENTRY_SET_POS ( wid , - 1 ) ;
return 2 ;
}
static int
key_action_history_up ( GtkWidget * wid , GdkEventKey * ent , char * d1 , char * d2 ,
struct session * sess )
{
char * new_line ;
new_line = history_up ( & sess - > history , SPELL_ENTRY_GET_TEXT ( wid ) ) ;
if ( new_line )
{
SPELL_ENTRY_SET_TEXT ( wid , new_line ) ;
SPELL_ENTRY_SET_POS ( wid , - 1 ) ;
}
return 2 ;
}
static int
key_action_history_down ( GtkWidget * wid , GdkEventKey * ent , char * d1 ,
char * d2 , struct session * sess )
{
char * new_line ;
new_line = history_down ( & sess - > history ) ;
if ( new_line )
{
SPELL_ENTRY_SET_TEXT ( wid , new_line ) ;
SPELL_ENTRY_SET_POS ( wid , - 1 ) ;
}
return 2 ;
}
/* old data that we reuse */
static struct gcomp_data old_gcomp ;
/* work on the data, ie return only channels */
static int
double_chan_cb ( session * lsess , GList * * list )
{
if ( lsess - > type = = SESS_CHANNEL )
* list = g_list_prepend ( * list , lsess - > channel ) ;
return TRUE ;
}
/* convert a slist -> list. */
static GList *
chanlist_double_list ( GSList * inlist )
{
GList * list = NULL ;
g_slist_foreach ( inlist , ( GFunc ) double_chan_cb , & list ) ;
return list ;
}
/* handle commands */
static int
double_cmd_cb ( struct popup * pop , GList * * list )
{
* list = g_list_prepend ( * list , pop - > name ) ;
return TRUE ;
}
/* convert a slist -> list. */
static GList *
cmdlist_double_list ( GSList * inlist )
{
GList * list = NULL ;
g_slist_foreach ( inlist , ( GFunc ) double_cmd_cb , & list ) ;
return list ;
}
static char *
gcomp_nick_func ( char * data )
{
if ( data )
return ( ( struct User * ) data ) - > nick ;
return " " ;
}
void
key_action_tab_clean ( void )
{
if ( old_gcomp . elen )
{
old_gcomp . data [ 0 ] = 0 ;
old_gcomp . elen = 0 ;
}
}
/* Used in the followig completers */
# define COMP_BUF 2048
/* For use in sorting the user list for completion */
static int
talked_recent_cmp ( struct User * a , struct User * b )
{
if ( a - > lasttalk < b - > lasttalk )
return - 1 ;
if ( a - > lasttalk > b - > lasttalk )
return 1 ;
return 0 ;
}
static int
key_action_tab_comp ( GtkWidget * t , GdkEventKey * entry , char * d1 , char * d2 ,
struct session * sess )
{
int len = 0 , elen = 0 , i = 0 , cursor_pos , ent_start = 0 , comp = 0 , found = 0 ,
prefix_len , skip_len = 0 , is_nick , is_cmd = 0 ;
char buf [ COMP_BUF ] , ent [ CHANLEN ] , * postfix = NULL , * result , * ch ;
GList * list = NULL , * tmp_list = NULL ;
const char * text ;
GCompletion * gcomp = NULL ;
/* force the IM Context to reset */
SPELL_ENTRY_SET_EDITABLE ( t , FALSE ) ;
SPELL_ENTRY_SET_EDITABLE ( t , TRUE ) ;
text = SPELL_ENTRY_GET_TEXT ( t ) ;
if ( text [ 0 ] = = 0 )
return 1 ;
len = g_utf8_strlen ( text , - 1 ) ; /* must be null terminated */
cursor_pos = SPELL_ENTRY_GET_POS ( t ) ;
buf [ 0 ] = 0 ; /* make sure we don't get garbage in the buffer */
/* handle "nick: " or "nick " or "#channel "*/
ch = g_utf8_find_prev_char ( text , g_utf8_offset_to_pointer ( text , cursor_pos ) ) ;
if ( ch & & ch [ 0 ] = = ' ' )
{
skip_len + + ;
ch = g_utf8_find_prev_char ( text , ch ) ;
if ( ! ch )
return 2 ;
cursor_pos = g_utf8_pointer_to_offset ( text , ch ) ;
if ( cursor_pos & & ( g_utf8_get_char_validated ( ch , - 1 ) = = ' : ' | |
g_utf8_get_char_validated ( ch , - 1 ) = = ' , ' | |
2012-10-22 04:10:19 -04:00
g_utf8_get_char_validated ( ch , - 1 ) = = prefs . hex_completion_suffix [ 0 ] ) )
2011-02-23 22:14:30 -05:00
{
skip_len + + ;
}
else
cursor_pos = g_utf8_pointer_to_offset ( text , g_utf8_offset_to_pointer ( ch , 1 ) ) ;
}
comp = skip_len ;
/* store the text following the cursor for reinsertion later */
if ( ( cursor_pos + skip_len ) < len )
postfix = g_utf8_offset_to_pointer ( text , cursor_pos + skip_len ) ;
for ( ent_start = cursor_pos ; ; - - ent_start )
{
if ( ent_start = = 0 )
break ;
ch = g_utf8_offset_to_pointer ( text , ent_start - 1 ) ;
if ( ch & & ch [ 0 ] = = ' ' )
break ;
}
2012-10-22 08:18:20 -04:00
if ( ent_start = = 0 & & text [ 0 ] = = prefs . hex_input_command_char [ 0 ] )
2011-02-23 22:14:30 -05:00
{
ent_start + + ;
is_cmd = 1 ;
}
prefix_len = ent_start ;
elen = cursor_pos - ent_start ;
g_utf8_strncpy ( ent , g_utf8_offset_to_pointer ( text , prefix_len ) , elen ) ;
is_nick = ( ent [ 0 ] = = ' # ' | | ent [ 0 ] = = ' & ' | | is_cmd ) ? 0 : 1 ;
if ( sess - > type = = SESS_DIALOG & & is_nick )
{
/* tab in a dialog completes the other person's name */
if ( rfc_ncasecmp ( sess - > channel , ent , elen ) = = 0 )
{
result = sess - > channel ;
is_nick = 0 ;
}
else
return 2 ;
}
else
{
if ( is_nick )
{
gcomp = g_completion_new ( ( GCompletionFunc ) gcomp_nick_func ) ;
tmp_list = userlist_double_list ( sess ) ; /* create a temp list so we can free the memory */
2012-10-22 04:10:19 -04:00
if ( prefs . hex_completion_sort = = 1 ) /* sort in last-talk order? */
2011-02-23 22:14:30 -05:00
tmp_list = g_list_sort ( tmp_list , ( void * ) talked_recent_cmp ) ;
}
else
{
gcomp = g_completion_new ( NULL ) ;
if ( is_cmd )
{
tmp_list = cmdlist_double_list ( command_list ) ;
for ( i = 0 ; xc_cmds [ i ] . name ! = NULL ; i + + )
{
tmp_list = g_list_prepend ( tmp_list , xc_cmds [ i ] . name ) ;
}
tmp_list = plugin_command_list ( tmp_list ) ;
}
else
tmp_list = chanlist_double_list ( sess_list ) ;
}
tmp_list = g_list_reverse ( tmp_list ) ; /* make the comp entries turn up in the right order */
g_completion_set_compare ( gcomp , ( GCompletionStrncmpFunc ) rfc_ncasecmp ) ;
if ( tmp_list )
{
g_completion_add_items ( gcomp , tmp_list ) ;
g_list_free ( tmp_list ) ;
}
if ( comp & & ! ( rfc_ncasecmp ( old_gcomp . data , ent , old_gcomp . elen ) = = 0 ) )
{
key_action_tab_clean ( ) ;
comp = 0 ;
}
list = g_completion_complete_utf8 ( gcomp , comp ? old_gcomp . data : ent , & result ) ;
if ( result = = NULL ) /* No matches found */
{
g_completion_free ( gcomp ) ;
return 2 ;
}
if ( comp ) /* existing completion */
{
while ( list ) /* find the current entry */
{
if ( rfc_ncasecmp ( list - > data , ent , elen ) = = 0 )
{
found = 1 ;
break ;
}
list = list - > next ;
}
if ( found )
{
if ( ! ( d1 & & d1 [ 0 ] ) ) /* not holding down shift */
{
if ( g_list_next ( list ) = = NULL )
list = g_list_first ( list ) ;
else
list = g_list_next ( list ) ;
}
else
{
if ( g_list_previous ( list ) = = NULL )
list = g_list_last ( list ) ;
else
list = g_list_previous ( list ) ;
}
g_free ( result ) ;
result = ( char * ) list - > data ;
}
else
{
g_free ( result ) ;
g_completion_free ( gcomp ) ;
return 2 ;
}
}
else
{
strcpy ( old_gcomp . data , ent ) ;
old_gcomp . elen = elen ;
/* Get the first nick and put out the data for future nickcompletes */
2012-10-22 04:10:19 -04:00
if ( prefs . hex_completion_amount & & g_list_length ( list ) < = prefs . hex_completion_amount )
2011-02-23 22:14:30 -05:00
{
g_free ( result ) ;
result = ( char * ) list - > data ;
}
else
{
/* bash style completion */
if ( g_list_next ( list ) ! = NULL )
{
if ( strlen ( result ) > elen ) /* the largest common prefix is larger than nick, change the data */
{
if ( prefix_len )
g_utf8_strncpy ( buf , text , prefix_len ) ;
strncat ( buf , result , COMP_BUF - prefix_len ) ;
cursor_pos = strlen ( buf ) ;
g_free ( result ) ;
if ( postfix )
{
strcat ( buf , " " ) ;
strncat ( buf , postfix , COMP_BUF - cursor_pos - 1 ) ;
}
SPELL_ENTRY_SET_TEXT ( t , buf ) ;
SPELL_ENTRY_SET_POS ( t , g_utf8_pointer_to_offset ( buf , buf + cursor_pos ) ) ;
buf [ 0 ] = 0 ;
}
else
g_free ( result ) ;
while ( list )
{
len = strlen ( buf ) ; /* current buffer */
elen = strlen ( list - > data ) ; /* next item to add */
if ( len + elen + 2 > = COMP_BUF ) /* +2 is space + null */
{
PrintText ( sess , buf ) ;
buf [ 0 ] = 0 ;
len = 0 ;
}
strcpy ( buf + len , ( char * ) list - > data ) ;
strcpy ( buf + len + elen , " " ) ;
list = list - > next ;
}
PrintText ( sess , buf ) ;
g_completion_free ( gcomp ) ;
return 2 ;
}
/* Only one matching entry */
g_free ( result ) ;
result = list - > data ;
}
}
}
if ( result )
{
if ( prefix_len )
g_utf8_strncpy ( buf , text , prefix_len ) ;
strncat ( buf , result , COMP_BUF - ( prefix_len + 3 ) ) ; /* make sure nicksuffix and space fits */
if ( ! prefix_len & & is_nick )
2012-10-22 04:10:19 -04:00
strcat ( buf , & prefs . hex_completion_suffix [ 0 ] ) ;
2011-02-23 22:14:30 -05:00
strcat ( buf , " " ) ;
cursor_pos = strlen ( buf ) ;
if ( postfix )
strncat ( buf , postfix , COMP_BUF - cursor_pos - 2 ) ;
SPELL_ENTRY_SET_TEXT ( t , buf ) ;
SPELL_ENTRY_SET_POS ( t , g_utf8_pointer_to_offset ( buf , buf + cursor_pos ) ) ;
}
if ( gcomp )
g_completion_free ( gcomp ) ;
return 2 ;
}
# undef COMP_BUF
static int
key_action_comp_chng ( GtkWidget * wid , GdkEventKey * ent , char * d1 , char * d2 ,
struct session * sess )
{
key_action_tab_comp ( wid , ent , d1 , d2 , sess ) ;
return 2 ;
}
static int
key_action_replace ( GtkWidget * wid , GdkEventKey * ent , char * d1 , char * d2 ,
struct session * sess )
{
replace_handle ( wid ) ;
return 1 ;
}
static int
key_action_move_tab_left ( GtkWidget * wid , GdkEventKey * ent , char * d1 ,
char * d2 , struct session * sess )
{
mg_move_tab ( sess , + 1 ) ;
return 2 ; /* don't allow default action */
}
static int
key_action_move_tab_right ( GtkWidget * wid , GdkEventKey * ent , char * d1 ,
char * d2 , struct session * sess )
{
mg_move_tab ( sess , - 1 ) ;
return 2 ; /* -''- */
}
static int
key_action_move_tab_family_left ( GtkWidget * wid , GdkEventKey * ent , char * d1 ,
char * d2 , struct session * sess )
{
mg_move_tab_family ( sess , + 1 ) ;
return 2 ; /* don't allow default action */
}
static int
key_action_move_tab_family_right ( GtkWidget * wid , GdkEventKey * ent , char * d1 ,
char * d2 , struct session * sess )
{
mg_move_tab_family ( sess , - 1 ) ;
return 2 ; /* -''- */
}
static int
key_action_put_history ( GtkWidget * wid , GdkEventKey * ent , char * d1 ,
char * d2 , struct session * sess )
{
history_add ( & sess - > history , SPELL_ENTRY_GET_TEXT ( wid ) ) ;
SPELL_ENTRY_SET_TEXT ( wid , " " ) ;
return 2 ; /* -''- */
}
/* -------- */
static void
replace_handle ( GtkWidget * t )
{
const char * text , * postfix_pnt ;
struct popup * pop ;
GSList * list = replace_list ;
char word [ 256 ] ;
char postfix [ 256 ] ;
char outbuf [ 4096 ] ;
int c , len , xlen ;
text = SPELL_ENTRY_GET_TEXT ( t ) ;
len = strlen ( text ) ;
if ( len < 1 )
return ;
for ( c = len - 1 ; c > 0 ; c - - )
{
if ( text [ c ] = = ' ' )
break ;
}
if ( text [ c ] = = ' ' )
c + + ;
xlen = c ;
if ( len - c > = ( sizeof ( word ) - 12 ) )
return ;
if ( len - c < 1 )
return ;
memcpy ( word , & text [ c ] , len - c ) ;
word [ len - c ] = 0 ;
len = strlen ( word ) ;
if ( word [ 0 ] = = ' \' ' & & word [ len ] = = ' \' ' )
return ;
postfix_pnt = NULL ;
for ( c = 0 ; c < len ; c + + )
{
if ( word [ c ] = = ' \' ' )
{
postfix_pnt = & word [ c + 1 ] ;
word [ c ] = 0 ;
break ;
}
}
if ( postfix_pnt ! = NULL )
{
if ( strlen ( postfix_pnt ) > sizeof ( postfix ) - 12 )
return ;
strcpy ( postfix , postfix_pnt ) ;
}
while ( list )
{
pop = ( struct popup * ) list - > data ;
if ( strcmp ( pop - > name , word ) = = 0 )
{
memcpy ( outbuf , text , xlen ) ;
outbuf [ xlen ] = 0 ;
if ( postfix_pnt = = NULL )
snprintf ( word , sizeof ( word ) , " %s " , pop - > cmd ) ;
else
snprintf ( word , sizeof ( word ) , " %s%s " , pop - > cmd , postfix ) ;
strcat ( outbuf , word ) ;
SPELL_ENTRY_SET_TEXT ( t , outbuf ) ;
SPELL_ENTRY_SET_POS ( t , - 1 ) ;
return ;
}
list = list - > next ;
}
}